GRSISort "v4.0.0.5"
An extension of the ROOT analysis Framework
Loading...
Searching...
No Matches
TPulseAnalyzer.cxx
Go to the documentation of this file.
1#include <utility>
2
3#include "TPulseAnalyzer.h"
4#include "TGRSIFunctions.h"
5
10
11TPulseAnalyzer::TPulseAnalyzer(const TFragment& fragment, double noise_fac)
12{
13 Clear();
14 SetData(fragment, noise_fac);
15}
16
17TPulseAnalyzer::TPulseAnalyzer(const std::vector<Short_t>& wave, double noise_fac, std::string name)
18 : fName(std::move(name))
19{
20 Clear();
21 SetData(wave, noise_fac);
22}
23
25{
26 delete cWpar;
27 delete spar;
28 delete shpar;
29}
30
31void TPulseAnalyzer::Clear(Option_t*)
32{
33 SetCsI(false);
34 set = false;
35 cN = 0;
36 FILTER = 8;
37 T0RANGE = 8;
38 LARGECHISQ = 1E111;
39 EPS = 0.001;
40
41 lineq_dim = 0;
42 memset(lineq_matrix, 0, sizeof(lineq_matrix)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
43 memset(lineq_vector, 0, sizeof(lineq_vector)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
44 memset(lineq_solution, 0, sizeof(lineq_solution)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
45 memset(copy_matrix, 0, sizeof(copy_matrix)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
46}
47
48void TPulseAnalyzer::SetData(const TFragment& fragment, double noise_fac)
49{
50 if(fragment.HasWave()) {
51 SetData(*fragment.GetWaveform(), noise_fac);
52 }
53}
54
55// NOLINTBEGIN(cppcoreguidelines-narrowing-conversions)
56void TPulseAnalyzer::SetData(const std::vector<Short_t>& wave, double noise_fac)
57{
58 SetCsI(false);
59 cN = wave.size();
60 if(cN > 0) {
61 cWavebuffer = wave;
62 set = true;
63 if(noise_fac > 0) {
64 FILTER = 8 * noise_fac;
65 T0RANGE = 8 * noise_fac;
66 }
67 }
68}
69
70////////////////////////////////////////
71// Linear Equation Solver
72////////////////////////////////////////
73
74// "Very efficient" apparently, written by Kris S,
75// Solve the currently stored n dimentional linear eqaution
77{
78 memcpy(copy_matrix, lineq_matrix, sizeof(lineq_matrix)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
79 long double w = determinant(lineq_dim);
80 if(w == 0.) {
81 return 0;
82 }
83 for(int i = 0; i < lineq_dim; i++) {
84 memcpy(copy_matrix, lineq_matrix, sizeof(lineq_matrix)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
85 memcpy(copy_matrix[i], lineq_vector, sizeof(lineq_vector)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
87 }
88 return 1;
89}
90
91// solve the determinant of the currently stored copy_matrix for dimentions m
93{
94 if(m == 1) {
95 return copy_matrix[0][0];
96 }
97 int sign = 1;
98 if(copy_matrix[m - 1][m - 1] == 0.) {
99 int j = m - 1;
100 while(copy_matrix[m - 1][j] == 0 && j >= 0) {
101 j--;
102 }
103 if(j < 0) {
104 return 0.;
105 }
106 for(int i = 0; i < m; i++) {
107 long double s = copy_matrix[i][m - 1];
108 copy_matrix[i][m - 1] = copy_matrix[i][j];
109 copy_matrix[i][j] = s;
110 }
111 sign *= -1;
112 }
113 for(int j = m - 2; j >= 0; j--) {
114 for(int i = 0; i < m; i++) {
115 copy_matrix[i][j] -= copy_matrix[i][m - 1] / copy_matrix[m - 1][m - 1] * copy_matrix[m - 1][j];
116 }
117 }
118 return copy_matrix[m - 1][m - 1] * sign * determinant(m - 1);
119}
120
121////////////////////////////////////////
122// Waveform Fits Functions
123////////////////////////////////////////
124
125int TPulseAnalyzer::fit_smooth_parabola(int low, int high, double x0, ParPar* pp)
126{
127 memset(pp, 0, sizeof(ParPar));
128 memset(lineq_matrix, 0, sizeof(lineq_matrix)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
129 memset(lineq_vector, 0, sizeof(lineq_vector)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
130 lineq_dim = 2;
131 double chisq = 0.;
132 int ndf = 0;
133 int k = static_cast<int>(rint(x0));
134
135 for(int i = low; i < k; i++) {
136 lineq_matrix[0][0] += 1;
137 lineq_vector[0] += cWavebuffer[i];
138 ndf++;
139 chisq += cWavebuffer[i] * cWavebuffer[i];
140 }
141
142 for(int i = k; i < high; i++) {
143 double x = (i - x0) * (i - x0);
144 lineq_matrix[0][0] += 1;
145 lineq_matrix[0][1] += x;
146 lineq_matrix[1][1] += x * x;
147 lineq_vector[0] += cWavebuffer[i];
148 lineq_vector[1] += cWavebuffer[i] * x;
149 ndf++;
150 chisq += cWavebuffer[i] * cWavebuffer[i];
151 }
152 lineq_matrix[1][0] = lineq_matrix[0][1];
153
154 if(solve_lin_eq() == 0) {
155 pp->chisq = BADCHISQ_MAT;
156 return -1;
157 }
158 chisq -= lineq_vector[0] * lineq_solution[0];
159 chisq -= lineq_vector[1] * lineq_solution[1];
160
161 pp->constant = lineq_solution[0];
162 pp->linear = 0.;
163 pp->quadratic = lineq_solution[1];
164 pp->chisq = chisq;
165 pp->ndf = ndf;
166
167 return 1;
168}
169
170////////////////////////////////////////
171// RF Fit Run Functions
172////////////////////////////////////////
173
175{
176 if(!set || cN < 10) {
177 return -1;
178 }
179 delete spar;
180 spar = new SinPar;
181
182 spar->t0 = -1;
183
184 return 5 * get_sin_par(T);
185}
186
187////////////////////////////////////////
188// Waveform Time Fit Run Functions
189////////////////////////////////////////
190
191// Overall function which determins limits and fits the 3 trial functions
193{
194 if(!set || cN < 10) {
195 return -1;
196 }
197
198 delete cWpar;
199 cWpar = new WaveFormPar;
200 cWpar->t0 = -1;
201
202 std::array<double, 3> chisq;
203 std::array<WaveFormPar, 3> w;
204
205 cWpar->baseline_range = T0RANGE; // default only 8 samples!
206 get_baseline();
207 get_tmax();
208
209 if(good_baseline() == 0) {
210 return -1;
211 }
212
213 get_t30();
214 get_t50();
215 cWpar->thigh = cWpar->t50;
216
217 for(double& i : chisq) {
218 i = LARGECHISQ;
219 }
220
221 size_t swp = sizeof(WaveFormPar);
222 chisq[0] = get_smooth_T0();
223 memcpy(w.data(), cWpar, swp);
224 chisq[1] = get_parabolic_T0();
225 memcpy(&w[1], cWpar, swp);
226 chisq[2] = get_linear_T0();
227 memcpy(&w[2], cWpar, swp);
228
229 double chimin = LARGECHISQ;
230 int imin = 0;
231
232 for(int i = 0; i < 3; i++) {
233 if(chisq[i] < chimin && chisq[i] > 0) {
234 chimin = chisq[i];
235 imin = i;
236 }
237 }
238
239 if(imin < 2) {
240 memcpy(cWpar, &w[imin], swp);
241 }
242
244 return cWpar->t0;
245}
246/*================================================================*/
247
248/*================================================================*/
249int TPulseAnalyzer::fit_parabola(int low, int high, ParPar* pp)
250{
251 memset(pp, 0, sizeof(ParPar));
252 memset(lineq_matrix, 0, sizeof(lineq_matrix)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
253 memset(lineq_vector, 0, sizeof(lineq_vector)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
254 lineq_dim = 3;
255 double chisq = 0.;
256 int ndf = 0;
257 for(int i = low; i < high; i++) {
258 lineq_matrix[0][0] += 1;
259 lineq_matrix[0][1] += i;
260 lineq_matrix[0][2] += i * i;
261 lineq_matrix[1][2] += i * i * i;
262 lineq_matrix[2][2] += i * i * i * i;
263 lineq_vector[0] += cWavebuffer[i];
264 lineq_vector[1] += cWavebuffer[i] * i;
265 lineq_vector[2] += cWavebuffer[i] * i * i;
266 ndf++;
267 chisq += cWavebuffer[i] * cWavebuffer[i];
268 }
269 lineq_matrix[1][0] = lineq_matrix[0][1];
270 lineq_matrix[1][1] = lineq_matrix[0][2];
271 lineq_matrix[2][0] = lineq_matrix[0][2];
272 lineq_matrix[2][1] = lineq_matrix[1][2];
273
274 if(solve_lin_eq() == 0) {
275 pp->chisq = BADCHISQ_MAT;
276 return -1;
277 }
278 chisq -= lineq_vector[0] * lineq_solution[0];
279 chisq -= lineq_vector[1] * lineq_solution[1];
280 chisq -= lineq_vector[2] * lineq_solution[2];
281 pp->constant = lineq_solution[0];
282 pp->linear = lineq_solution[1];
283 pp->quadratic = lineq_solution[2];
284 pp->chisq = chisq;
285 pp->ndf = ndf;
286 return 1;
287}
288
289/*================================================================*/
290int TPulseAnalyzer::fit_line(int low, int high, LinePar* lp)
291{
292 memset(lp, 0, sizeof(LinePar));
293 memset(lineq_matrix, 0, sizeof(lineq_matrix)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
294 memset(lineq_vector, 0, sizeof(lineq_vector)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
295 lineq_dim = 2;
296 double chisq = 0.;
297 int ndf = 0;
298 for(int i = low; i < high; i++) {
299 lineq_matrix[0][0] += 1;
300 lineq_matrix[0][1] += i;
301 lineq_matrix[1][1] += i * i;
302 lineq_vector[0] += cWavebuffer[i];
303 lineq_vector[1] += cWavebuffer[i] * i;
304 ndf++;
305 chisq += cWavebuffer[i] * cWavebuffer[i];
306 }
307 lineq_matrix[1][0] = lineq_matrix[0][1];
308
309 if(solve_lin_eq() == 0) {
310 lp->chisq = BADCHISQ_MAT;
311 return -1;
312 }
313 chisq -= lineq_vector[0] * lineq_solution[0];
314 chisq -= lineq_vector[1] * lineq_solution[1];
315 lp->slope = lineq_solution[1];
316 lp->intercept = lineq_solution[0];
317 lp->chisq = chisq;
318 lp->ndf = ndf;
319 return 1;
320}
321/*================================================================*/
322
323/*======================================================*/
324
326{
327 LinePar lp = {};
328 LinePar lpl = {};
329 int k = 0;
330 double chit = 0.;
331 double chitmin = 0.;
332 double b = 0.;
333 double c = 0.;
334 double t = 0.;
335
336 chitmin = LARGECHISQ;
337
338 for(k = T0RANGE / 2; k < cWpar->thigh - T0RANGE / 8; k++) {
339 // fit line to the baseline
340 fit_line(0, k, &lp);
341
342 // fit line to the risetime
343 fit_line(k, cWpar->thigh, &lpl);
344
345 chit = lp.chisq + lpl.chisq;
346
347 if(chit < chitmin) {
348 chitmin = chit;
349 cWpar->b0 = lp.intercept;
350 cWpar->b1 = lp.slope;
351 cWpar->s0 = lpl.intercept;
352 cWpar->s1 = lpl.slope;
353 cWpar->s2 = 0.;
354 }
355 } // end of the loop over k
356 b = cWpar->s1 - cWpar->b1;
357 c = cWpar->s0 - cWpar->b0;
358 t = -c / b;
359
360 cWpar->t0 = -1;
361 cWpar->temin = 0;
363 if(t < cN && t > 0) {
364 cWpar->t0 = t;
365 cWpar->temin = static_cast<int>(rint(cWpar->t0)) - 2;
366 cWpar->temax = static_cast<int>(rint(cWpar->t0)) + 2;
367 return (chitmin / (cWpar->thigh - 5));
368 }
369
370 return BADCHISQ_LIN_T0;
371}
372/*================================================================*/
374{
375 ParPar pp;
376 ParPar ppmin;
377 int k = 0;
378 int kmin = 0;
379 double chit = 0.;
380 double chitmin = 0.;
381 double c = 0.;
382 double t = 0.;
383
384 memset(&ppmin, 0, sizeof(ParPar));
385
386 chitmin = LARGECHISQ;
387 kmin = 0;
388 // corse search first
389 for(k = T0RANGE / 2; k < cWpar->thigh - T0RANGE / 2; k++) {
390 fit_smooth_parabola(0, cWpar->thigh, static_cast<double>(k), &pp);
391
392 chit = pp.chisq;
393 if(chit < chitmin) {
394 chitmin = chit;
395 kmin = k;
396 }
397 } // end of the corse search loop over k
398 c = kmin;
399
400 chitmin = LARGECHISQ;
401 // fine search next
402 for(t = kmin - 1; t < kmin + 1; t += 0.1) {
403 fit_smooth_parabola(0, cWpar->thigh, t, &pp);
404 chit = pp.chisq;
405 if(chit < chitmin) {
406 memcpy(&ppmin, &pp, sizeof(ParPar));
407 chitmin = chit;
408 c = t;
409 }
410 } // end of the fine search loop over k
411
412 memcpy(&pp, &ppmin, sizeof(ParPar));
413 t = c;
414 cWpar->s0 = pp.constant + pp.quadratic * t * t;
415 cWpar->s1 = -2. * pp.quadratic * t;
416 cWpar->s2 = pp.quadratic;
417 cWpar->b0 = pp.constant;
418 cWpar->b1 = 0.;
419
420 cWpar->t0 = -1;
421 cWpar->temin = 0;
423 if(t < cN && t > 0) {
424 cWpar->t0 = t;
425 cWpar->temin = static_cast<int>(rint(cWpar->t0)) - 2;
426 cWpar->temax = static_cast<int>(rint(cWpar->t0)) + 2;
427 return (chitmin / (cWpar->thigh - 2));
428 }
429 return BADCHISQ_SMOOTH_T0;
430}
431/*================================================================*/
433{
434 LinePar lp;
435 ParPar pp;
436 double chit = 0.;
437 double chitmin = 0.;
438 double a = 0.;
439 double b = 0.;
440 double c = 0.;
441 double d = 0.;
442 double t = 0.;
443
444 chitmin = LARGECHISQ;
445 for(int k = T0RANGE / 2; k < cWpar->thigh - T0RANGE / 2; k++) {
446 // fit line to the baseline
447 fit_line(0, k, &lp);
448
449 // fit parabola to the risetime
450 fit_parabola(k, cWpar->thigh, &pp);
451
452 chit = lp.chisq + pp.chisq;
453
454 if(chit < chitmin) {
455 chitmin = chit;
456 cWpar->b0 = lp.intercept;
457 cWpar->b1 = lp.slope;
458 cWpar->s0 = pp.constant;
459 cWpar->s1 = pp.linear;
460 cWpar->s2 = pp.quadratic;
461 }
462 } // end loop through k
463
464 a = cWpar->s2;
465 b = cWpar->s1 - cWpar->b1;
466 c = cWpar->s0 - cWpar->b0;
467 d = b * b - 4 * a * c;
468
469 if(a == 0.) {
470 t = -c / b;
471 } else {
472 if(d >= 0) {
473 if(d == 0.) {
474 t = -0.5 * b / a;
475 } else {
476 d = sqrt(d);
477 t = 0.5 * (-b + d) / a;
478 }
479 } else {
480 return BADCHISQ_PAR_T0;
481 }
482 }
483
484 cWpar->t0 = -1;
485 cWpar->temin = 0;
487 if(t < cN && t > 0) {
488 cWpar->t0 = t;
489 cWpar->temin = static_cast<int>(rint(cWpar->t0)) - 2;
490 cWpar->temax = static_cast<int>(rint(cWpar->t0)) + 2;
491 return (chitmin / (cWpar->thigh - 5));
492 }
493 return BADCHISQ_PAR_T0;
494}
495
496// Measure the baseline and standard deviation of the waveform, over the tick range specified by
497// cWpar->baseline_range
499{
500 cWpar->baseline = 0.;
501 cWpar->baselineStDev = 0.;
502
503 // error if waveform length cN is shorter than baseline range
504 if(cN < cWpar->baseline_range) {
505 std::cout << "Baseline range (" << cWpar->baseline_range << ") larger than waveform length!" << std::endl;
506 std::cout << "Terminating program" << std::endl;
507 exit(0);
508 }
509
510 for(int i = 0; i < cWpar->baseline_range; i++) {
513 }
514
519 cWpar->bflag = true; // flag after establishing baseline
520}
521
522/*======================================================*/
523
524// Measure the baseline and standard deviation up to tick cWpar->t0 after a fit
526{
527 cWpar->baselinefin = 0.;
529 int tb = cWpar->t0; // t0 non integer, result always too small before.
530 if(tb > T0RANGE + 10) {
531 tb -= 10;
532 }
533
534 // error if waveform length cN is shorter than baseline range
535 if(cN > tb && tb > 0) {
536 for(int i = 0; i < tb; i++) {
539 }
540
541 cWpar->baselineStDevfin /= tb;
542 cWpar->baselinefin /= tb;
544 cWpar->baselineStDevfin = sqrt(std::abs(cWpar->baselineStDevfin));
545 }
546}
547
548/*======================================================*/
549
550// Find the maximum of the wavefunction, smoothed with a moving average filter
552{
553 int i = 0;
554 int j = 0;
555 int sum = 0;
556 int D = FILTER / 2;
557
558 cWpar->max = cWavebuffer[0];
559 cWpar->tmax = 0;
560
561 // applies the filter to the waveform
562 // cout<<" "<<cWpar->tmax<<" "<< cWpar->max<<flush;
563 for(i = D; i < cN - D; i++) {
564 sum = 0;
565 for(j = i - D; j < i + D; j++) {
566 sum += cWavebuffer[j];
567 }
568 sum /= FILTER; // the value of the filtered waveform at i
569 if(sum > cWpar->max) {
570 // if the value of the filtered waveform at i is larger than the current maximum, max=value and tmax = i
571 cWpar->max = sum;
572 cWpar->tmax = i;
573 }
574 }
575 cWpar->mflag = 1; // flag after finding tmax
576}
577
578/*===========================================================*/
579double TPulseAnalyzer::get_tfrac(double frac, double fraclow, double frachigh)
580{
581 if(!cWpar->bflag) {
582 std::cout << "Baseline not deterimned for the tfraction" << std::endl;
583 exit(1);
584 }
585
586 if(cWpar->mflag != 1) {
587 std::cout << "Maximum not deterimned for the tfraction" << std::endl;
588 exit(1);
589 }
590
591 int t = cWpar->tmax;
592
593 double f = cWpar->baseline + frac * (cWpar->max - cWpar->baseline);
594 double flow = cWpar->baseline + fraclow * (cWpar->max - cWpar->baseline);
595 double fhigh = cWpar->baseline + frachigh * (cWpar->max - cWpar->baseline);
596
597 while(cWavebuffer[t] > f) {
598 t--;
599 if(t <= 4) {
600 break;
601 }
602 }
603 int imin = t;
604 while(cWavebuffer[imin] > flow) {
605 imin--;
606 if(imin <= 1) {
607 break;
608 }
609 }
610
611 int imax = t;
612
613 while(cWavebuffer[imax] < fhigh) {
614 imax++;
615 if(imax >= cN - 1) {
616 break;
617 }
618 }
619
620 memset(lineq_matrix, 0, sizeof(lineq_matrix)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
621 memset(lineq_vector, 0, sizeof(lineq_vector)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
622 lineq_dim = 3;
623
624 int i = imax - imin;
625 int64_t a = i;
626 lineq_matrix[0][0] = a + 1;
627 lineq_matrix[0][1] = 0.5 * a;
628 lineq_matrix[2][0] = a / 6.;
629 lineq_matrix[2][2] = -a / 30.;
630 a *= i;
631 lineq_matrix[0][1] += 0.5 * a;
632 lineq_matrix[2][0] += 0.5 * a;
633 lineq_matrix[2][1] = 0.25 * a;
634 a *= i;
635 lineq_matrix[2][0] += a / 3.;
636 lineq_matrix[2][1] += 0.5 * a;
637 lineq_matrix[2][2] += a / 3.;
638 a *= i;
639 lineq_matrix[2][1] += 0.25 * a;
640 lineq_matrix[2][2] += 0.5 * a;
641 a *= i;
642 lineq_matrix[2][2] += 0.2 * a;
643
644 lineq_matrix[1][0] = lineq_matrix[0][1];
645 lineq_matrix[1][1] = lineq_matrix[2][0];
646 lineq_matrix[0][2] = lineq_matrix[2][0];
647 lineq_matrix[1][2] = lineq_matrix[2][1];
648
649 for(i = 0; i < lineq_dim; i++) {
650 lineq_vector[i] = 0;
651 }
652
653 for(i = imin; i < imax + 1; i++) {
654 a = i - imin;
655 lineq_vector[0] += cWavebuffer[i];
656 lineq_vector[1] += cWavebuffer[i] * a;
657 lineq_vector[2] += cWavebuffer[i] * a * a;
658 }
659
660 if(solve_lin_eq() == 0) {
661 return -4;
662 }
663 double p = lineq_solution[0] - f;
664 double q = lineq_solution[1];
665 double r = lineq_solution[2];
666
667 if(r != 0) {
668 double d = q * q - 4 * r * p;
669 if(d < 0) {
670 return -5;
671 }
672 f = -q + sqrt(d);
673 f *= 0.5;
674 f /= r;
675 f += imin;
676 return f;
677 }
678 if(q != 0) {
679 f = -p / q;
680 return f;
681 }
682 return -6;
683
684 return -7;
685}
686
687/* ==================================================== */
689{
690 int t = get_tfrac(0.5, 0.3, 0.8);
691 if((t > 0) && (t < MAX_SAMPLES)) {
692 cWpar->t50_flag = 1;
693 cWpar->t50 = t;
694 } else {
695 cWpar->t50_flag = -1;
696 cWpar->t50 = -1;
697 }
698}
699/* ==================================================== */
701{
702 int t = get_tfrac(0.9, 0.8, 0.98);
703
704 if((t > 0) && (t < MAX_SAMPLES)) {
705 cWpar->t90_flag = 1;
706 cWpar->t90 = t;
707 } else {
708 cWpar->t90_flag = -1;
709 cWpar->t90 = -1;
710 }
711}
712/*===========================================================*/
714{
715 int t = get_tfrac(0.1, 0.05, 0.2);
716
717 if((t > 0) && (t < MAX_SAMPLES)) {
718 cWpar->t10_flag = 1;
719 cWpar->t10 = t;
720 } else {
721 cWpar->t10_flag = -1;
722 cWpar->t10 = -1;
723 }
724}
725/*===========================================================*/
727{
728 int t = get_tfrac(0.3, 0.15, 0.45);
729 if((t > 0) && (t < MAX_SAMPLES)) {
730 cWpar->t30_flag = 1;
731 cWpar->t30 = t;
732 } else {
733 cWpar->t30_flag = -1;
734 cWpar->t30 = -1;
735 }
736}
737
739{
740 double s = 0.;
741 double sn = 0.;
742 double snm = 0.;
743 double s2 = 0.;
744 double s2n = 0.;
745 double s2nm = 0.;
746 double c = 0.;
747 double cn = 0.;
748 double cnm = 0.;
749 double c2 = 0.;
750 double c2n = 0.;
751 double c2nm = 0.;
752 double w = 0.;
753 memset(lineq_matrix, 0, sizeof(lineq_matrix)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
754 memset(lineq_vector, 0, sizeof(lineq_vector)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
755 lineq_dim = 3;
756
757 w = 2 * TMath::Pi() / T;
758
759 s = sin(w);
760 sn = sin(cN * w);
761 snm = sin((cN - 1) * w);
762 s2 = sin(2 * w);
763 s2n = sin(2 * cN * w);
764 s2nm = sin(2 * (cN - 1) * w);
765
766 c = cos(w);
767 cn = cos(cN * w);
768 cnm = cos((cN - 1) * w);
769 c2 = cos(2 * w);
770 c2n = cos(2 * cN * w);
771 c2nm = cos(2 * (cN - 1) * w);
772
773 lineq_matrix[0][0] = 0.5 * cN - 0.25 * (1 - c2 - c2n + c2nm) / (1 - c2);
774 lineq_matrix[0][1] = 0.25 * (s2 + s2nm - s2n) / (1 - c2);
775 lineq_matrix[1][0] = lineq_matrix[0][1];
776 lineq_matrix[0][2] = 0.5 * (s + snm - sn) / (1 - c);
777 lineq_matrix[2][0] = lineq_matrix[0][2];
778 lineq_matrix[1][1] = 0.5 * cN + 0.25 * (1 - c2 - c2n + c2nm) / (1 - c2);
779 lineq_matrix[1][2] = 0.5 * (1 - c - cn + cnm) / (1 - c);
780 lineq_matrix[2][1] = lineq_matrix[1][2];
781 lineq_matrix[2][2] = cN;
782
783 for(int i = 0; i < lineq_dim; i++) {
784 lineq_vector[i] = 0;
785 }
786
787 for(int i = 0; i < cN; i++) {
788 lineq_vector[0] += cWavebuffer[i] * sin(w * i);
789 lineq_vector[1] += cWavebuffer[i] * cos(w * i);
790 lineq_vector[2] += cWavebuffer[i];
791 }
792 if(solve_lin_eq() == 0) {
793 return 0;
794 }
796 spar->C = lineq_solution[2];
797
798 s = -lineq_solution[1] / spar->A;
799 c = lineq_solution[0] / spar->A;
800
801 if(s >= 0) {
802 spar->t0 = acos(c) * T / (2 * TMath::Pi());
803 } else {
804 spar->t0 = (1 - acos(c) / (2 * TMath::Pi())) * T;
805 }
806
807 return spar->t0;
808}
809
810/*======================================================*/
811// void TPulseAnalyzer::get_sig2noise(){ if(cN==0)return;
812// if(set){
813// get_baseline();
814// get_tmax();
815// cWpar->sig2noise=(cWpar->max-cWpar->baseline)/cWpar->baselineStDev;
816// }
817// return;
818// }
819/*======================================================*/
821{
822 if(set && (cWpar != nullptr)) {
823 if(cWpar->t0 > 0) {
825 }
826 }
827 return -1;
828}
829
831{
832 if(set && (cWpar != nullptr)) {
833 if(cWpar->tmax < T0RANGE) {
834 return 0;
835 }
836 if((cWpar->max - cWpar->baseline) < (cWpar->baseline - cWavebuffer[0]) * 10) {
837 return 0;
838 }
840 return 0;
841 }
842 return 1;
843 }
844 return 0;
845}
846
847//=====================================================//
848// CsI functions:
849//=====================================================//
850
852{
853 if(CsIIsSet()) {
854 return shpar->chisq;
855 }
856 return -1;
857}
858
860{
861 if(CsIIsSet()) {
862 return shpar->type;
863 }
864 return -1;
865}
866
868{
869
870 if(CsIIsSet()) {
871 return shpar->t[0];
872 }
873 if(!set || cN < 10) {
874 return -1.;
875 }
876
877 delete cWpar;
878 cWpar = new WaveFormPar;
879 delete shpar;
880 shpar = new ShapePar;
881
883 if(cWpar->teflag == 1) {
884 // good exclusion zone
885 int tmpchisq = GetCsIShape();
886 if(tmpchisq >= 0) {
887 SetCsI();
888 return shpar->t[0];
889 }
890 }
891
892 return -1.0;
893}
894
896{
897 // std::cout<<"Fitting CsI PID"<<std::endl;
898 if(CsIIsSet()) {
899 double f = shpar->am[2];
900 double s = shpar->am[3];
901 double r = s / f * 100;
902
903 return r;
904 }
905 if(!set || cN < 10) {
906 return -1.;
907 }
908
909 delete cWpar;
910 cWpar = new WaveFormPar;
911 delete shpar;
912 shpar = new ShapePar;
913
914 shpar->t[1] = 4510;
915 shpar->t[2] = 64.3;
916 shpar->t[3] = 380.0;
917
919
920 if(!cWpar->bflag) {
921 shpar->type = -2; // type for failed exclusion zone determination
922 return BAD_BASELINE_RANGE;
923 }
924 if(cWpar->mflag == 0) {
925 shpar->type = -2; // type for failed exclusion zone determination
926 return BAD_MAX;
927 }
928 if(cWpar->teflag == 0) {
929 shpar->type = -2; // type for failed exclusion zone determination
930 return BAD_EXCLUSION_ZONE;
931 }
932
933 int tmpchisq = GetCsIShape();
934 if(tmpchisq >= 0) {
935 double f = shpar->am[2];
936 double s = shpar->am[3];
937 double r = s / f * 100;
938
939 SetCsI();
940
941 return r;
942 }
943
944 return BAD_PID;
945}
946
948{
949
950 std::array<double, 4> chisq; // chisq array for fit types: 0=2 comp, 1=fast, 2=slow, 3=gamma on PIN
951
952 int imin = 0;
953
954 // calculate ndf assuming two component (4 parameter) fit
955 int ndf = -4;
956 for(int i = 0; i < cWpar->temin; i++) {
957 ndf++;
958 }
959 for(int i = cWpar->temax; i < cN; i++) {
960 ndf++;
961 }
962
963 // std::cout<<"ndf 4 parameters: "<<ndf<<std::endl;
964
965 // initialize chisq to large value and set up waveform parameters
966 for(int i = 0; i < 4; i++) {
967 chisq[i] = LARGECHISQ;
968 csiTestShpar[i] = new ShapePar;
969 csiTestWpar[i] = new WaveFormPar;
970 memcpy(csiTestShpar[i], shpar, sizeof(ShapePar));
971 memcpy(csiTestWpar[i], cWpar, sizeof(WaveFormPar));
972 }
973
974 // two component fit
976 chisq[0] = csiTestShpar[0]->chisq / ndf;
977
978 // for 3 parameter fits, ndf is one larger
979 ndf++;
980
981 // std::cout<<"ndf 3 parameters: "<<ndf<<std::endl;
982
983 // fast only for high Z recoils
985 chisq[1] = csiTestShpar[1]->chisq / ndf;
986
987 // slow only for gamma on CsI
988 csiTestShpar[2]->t[2] = shpar->t[3];
989 csiTestShpar[2]->t[3] = shpar->t[2];
991 chisq[2] = csiTestShpar[2]->chisq / ndf;
992
993 // gamma on PIN
994 csiTestShpar[3]->t[2] = shpar->t[4];
995 csiTestShpar[3]->t[4] = shpar->t[2];
997 chisq[3] = csiTestShpar[3]->chisq / ndf;
998
999 /*std::cout<<"0=two comp 1=fast 2=slow 3=gamma on PIN"<<std::endl;
1000 for(i = 0; i < 4; i++)
1001 std::cout<<"chisq "<<csiTestShpar[i]->chisq<<", chisq["<<i<<"]/ndf "<<chisq[i]<<std::endl;*/
1002
1003 // find minimum chisq
1004 imin = -1;
1005 double chimin = LARGECHISQ;
1006 for(int i = 0; i < 4; i++) {
1007 if((chisq[i] < chimin) && (chisq[i] > 0)) {
1008 chimin = chisq[i];
1009 imin = i;
1010 }
1011 }
1012
1013 // std::cout<<"minimum chisq["<<imin<<"]/ndf "<<chisq[imin]<<", t0min "<<csiTestShpar[imin]->t[0]<<std::endl;
1014
1015 switch(imin) {
1016 case 3:
1017 // gamma on PIN fit type
1018 memcpy(shpar, csiTestShpar[imin], sizeof(ShapePar));
1019 memcpy(cWpar, csiTestWpar[imin], sizeof(WaveFormPar));
1020 shpar->t[2] = csiTestShpar[imin]->t[4];
1021 shpar->am[2] = csiTestShpar[imin]->am[4];
1022 shpar->t[4] = csiTestShpar[imin]->t[2];
1023 shpar->am[4] = csiTestShpar[imin]->am[2];
1024 shpar->type = imin + 1; // gamma on PIN type
1025 break;
1026 case 2:
1027 // slow component only fit type
1028 memcpy(shpar, csiTestShpar[imin], sizeof(ShapePar));
1029 memcpy(cWpar, csiTestWpar[imin], sizeof(WaveFormPar));
1030 shpar->t[2] = csiTestShpar[imin]->t[3];
1031 shpar->am[2] = csiTestShpar[imin]->am[3];
1032 shpar->t[3] = csiTestShpar[imin]->t[2];
1033 shpar->am[3] = csiTestShpar[imin]->am[2];
1034 shpar->type = imin + 1; // slow only type
1035 break;
1036 case 1:
1037 // fast component only fit type
1038 memcpy(shpar, csiTestShpar[imin], sizeof(ShapePar));
1039 memcpy(cWpar, csiTestWpar[imin], sizeof(WaveFormPar));
1040 shpar->type = imin + 1; // fast only type
1041 break;
1042 case 0:
1043 // two component fit type
1044 memcpy(shpar, csiTestShpar[imin], sizeof(ShapePar));
1045 memcpy(cWpar, csiTestWpar[imin], sizeof(WaveFormPar));
1046 shpar->type = imin + 1; // two component type
1047 break;
1048 default:
1049 // failed fit
1050 shpar->type = -1; // fit failure type
1051 shpar->chisq = BADCHISQ_FAIL_DIRECT; // set it here so it still frees memory on bad fits
1052 break;
1053 }
1054
1055 // free memory allocated for the fit
1056 for(int i = 0; i < 4; i++) {
1057 delete csiTestShpar[i];
1058 delete csiTestWpar[i];
1059 }
1060
1061 return shpar->chisq; // generic chisq return statement for all types
1062}
1063
1065{
1066 long double sum = 0.;
1067 long double tau = 0.;
1068 long double tau_i = 0.;
1069 long double tau_j = 0.;
1070 int p = 0;
1071 int q = 0;
1072 int d = 0;
1073
1074 memset(lineq_matrix, 0, sizeof(lineq_matrix)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1075 memset(lineq_vector, 0, sizeof(lineq_vector)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1076 memset(lineq_solution, 0, sizeof(lineq_solution)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1077
1078 /* q is the low limit of the signal section */
1079 q = wpar->temax;
1080 if(q >= cN || q <= 0) {
1081 par->chisq = -1;
1082 return -1.;
1083 }
1084
1085 /* p is the high limit of the baseline section */
1086 p = wpar->temin;
1087 if(p >= cN || p <= 0) {
1088 par->chisq = -1;
1089 return -1.;
1090 }
1091 lineq_dim = dim;
1092
1093 // initialize amplitudes to 0
1094 for(long double& i : par->am) { // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1095 i = 0.;
1096 }
1097
1098 d = cN;
1099
1100 // initialize chi square 0 and ndf = n-k to -k where k=dim
1101 par->chisq = 0.;
1102 par->ndf = -lineq_dim;
1103
1104 /**************************************************************************
1105 linearized chi square fit is Mu = v where M is a data matrix
1106 u, v are vectors; u is the parameter vector (solution)
1107 note that in this formulation, chisq_min = y_i^2-sum(u_iv_i)
1108 **************************************************************************/
1109
1110 // create matrix for linearized fit
1111 for(int i = 1; i < lineq_dim; i++) {
1112 tau = GetCsITau(i, par);
1113 tau_i = tau;
1114 sum = -(static_cast<double>(q)) / tau + log(1. - exp(-(static_cast<double>(d - q)) / tau));
1115 sum -= log(1. - exp(-1. / tau));
1116 lineq_matrix[i][0] = exp(sum);
1117 lineq_matrix[0][i] = exp(sum);
1118
1119 tau /= 2.;
1120 sum = -(static_cast<double>(q)) / tau + log(1. - exp(-(static_cast<double>(d - q)) / tau));
1121 sum -= log(1. - exp(-1. / tau));
1122 lineq_matrix[i][i] = exp(sum);
1123
1124 for(int j = i + 1; j < lineq_dim; j++) {
1125 tau_j = GetCsITau(j, par);
1126 tau = (tau_i * tau_j) / (tau_i + tau_j);
1127 sum = -(static_cast<double>(q)) / tau + log(1. - exp(-(static_cast<double>(d - q)) / tau));
1128 sum -= log(1. - exp(-1. / tau));
1129 lineq_matrix[i][j] = exp(sum);
1130 lineq_matrix[j][i] = exp(sum);
1131 }
1132 }
1133
1134 lineq_vector[0] = 0;
1135 lineq_matrix[0][0] = 0;
1136
1137 for(int j = q; j < cN; j++) {
1138 lineq_vector[0] += cWavebuffer[j];
1139 lineq_matrix[0][0] += 1;
1140 par->chisq += cWavebuffer[j] * cWavebuffer[j];
1141 par->ndf += 1;
1142 }
1143
1144 if(lineq_dim >= cN) {
1145 par->chisq = -1;
1146 return -1.;
1147 }
1148
1149 for(int i = 1; i < lineq_dim; i++) {
1150 tau = GetCsITau(i, par);
1151 lineq_vector[i] = 0;
1152 for(int j = q; j < cN; j++) {
1153 lineq_vector[i] += cWavebuffer[j] * exp(-(static_cast<double>(j)) / tau);
1154 }
1155 }
1156
1157 for(int j = 0; j < p; j++) {
1158 lineq_vector[0] += cWavebuffer[j];
1159 lineq_matrix[0][0] += 1;
1160 par->chisq += cWavebuffer[j] * cWavebuffer[j];
1161 par->ndf += 1;
1162 }
1163
1164 // solve the matrix equation Mu = v -> u = M^(-1)v where M^(-1) is the inverse
1165 // of M. note this has no solution if M is not invertable!
1166
1167 // error if the matrix cannot be inverted
1168 if(solve_lin_eq() == 0) {
1169 par->chisq = BADCHISQ_MAT;
1170 par->ndf = 1;
1171
1172 return BADCHISQ_MAT;
1173 } // else try and find t0 and calculate amplitudes
1174
1175 // see the function comments for find_t0 for details
1176
1177 par->t[0] = GetCsIt0(par, wpar);
1178
1179 // if t0 is less than 0, return a T0FAIL
1180 if(par->t[0] <= 0) {
1181 par->chisq = BADCHISQ_T0;
1182 par->ndf = 1;
1183 return BADCHISQ_T0;
1184 }
1185
1186 // calculate amplitudes
1187 par->am[0] = lineq_solution[0];
1188
1189 for(int i = 1; i < lineq_dim; i++) {
1190 tau = GetCsITau(i, par);
1191 par->am[i] = lineq_solution[i] * exp(-par->t[0] / tau);
1192 }
1193 // done calculating amplitudes
1194
1195 for(int i = 0; i < lineq_dim; i++) {
1196 par->chisq -= lineq_solution[i] * lineq_vector[i];
1197 }
1198
1199 if(par->chisq < 0) {
1200 par->chisq = BADCHISQ_NEG;
1201 par->ndf = 1;
1202 return BADCHISQ_NEG;
1203 }
1204
1205 for(int i = 2; i < lineq_dim; i++) {
1206 par->am[i] *= -1;
1207 }
1208
1209 par->type = dim - 2;
1210
1211 // return BADCHISQ_AMPL if a component amplitude is less than 0
1212 //(apart from the baseline which can be negative)
1213 for(int i = 1; i < lineq_dim; i++) {
1214 if(par->am[i] < 0) {
1215 par->chisq = BADCHISQ_AMPL;
1216 par->ndf = 1;
1217 return BADCHISQ_AMPL;
1218 }
1219 }
1220
1221 // std::cout<<"chisq from FitCsIShape: "<<par->chisq<<std::endl;
1222
1223 return par->chisq;
1224}
1225
1227{
1228 int i = 0;
1229 int j = 0;
1230 int D = FILTER / 2; // filter half width
1231
1232 // initilize the fit parameters for the risetime to 0 for safety
1233 cWpar->afit = 0.;
1234 cWpar->bfit = 0.;
1235
1236 // make sure the baseline is established prior to finding the exclusion zone
1238 get_baseline();
1239
1240 // Here we determine the x position temax of the upper limit for the exclusion zone.
1241 // find tmax and define baselineMax
1242 get_tmax();
1243
1244 // If tmax is established, continue.
1245 if(cWpar->mflag == 1) {
1247
1248 // Starting at tmax and working backwards along the waveform get the value of the filtered waveform at i and
1249 // when
1250 // the value of the filtered waveform goes below baselineMax, set the upper limit of the exclusion zone temax =
1251 // i.
1252 // The exclusion zone cannot be defined in the area of the waveform used to calculate the baseline.
1253 for(i = cWpar->tmax; i > cWpar->baseline_range; i--) {
1254 double sum = 0.;
1255 for(j = i - D; j < i + D; j++) {
1256 sum += cWavebuffer[j];
1257 }
1258 sum /= FILTER;
1259 if(sum < cWpar->baselineMax) {
1260 cWpar->temax = i;
1261 cWpar->teflag = 1;
1262 break;
1263 }
1264 }
1265
1266 if(cWpar->temax > cWpar->tmax || cWpar->temax < cWpar->baseline_range || cWpar->temax < 0) {
1267 cWpar->teflag = 0;
1268 }
1269 // End of the determination of the upper limit of the exclusion zone.
1270
1271 // Here we determine the x position of the lower limit for the exclusion zone
1272 /***** Fitting the risetime *****/
1273 if(cWpar->teflag == 1) {
1274 // Set baselineMin
1276
1277 // Here we fit a line y=ax+b from temax to temax + 3*FILTER and find the intersection with baselineMin. The
1278 // x
1279 // coordinate of this intersection becomes temin.
1280 // Matrix for the fit
1281 memset(lineq_matrix, 0, sizeof(lineq_matrix)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1282 memset(lineq_vector, 0, sizeof(lineq_vector)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1283 memset(lineq_solution, 0, sizeof(lineq_solution)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1284
1285 lineq_dim = 2;
1286 for(i = cWpar->temax; i <= cWpar->temax + 3 * FILTER; i++) {
1287 lineq_matrix[0][0] += 1;
1288 lineq_matrix[0][1] += i;
1289 lineq_matrix[1][1] += i * i;
1290
1291 lineq_vector[0] += cWavebuffer[i];
1292
1293 lineq_vector[1] += cWavebuffer[i] * i;
1294 }
1295 lineq_matrix[1][0] = lineq_matrix[0][1];
1296
1297 // solve_lin_eq returns 0 if the determinant of the matrix is 0 the system is unsolvable. If there is no
1298 // solution, set temin to the upper limit of the baseline range.
1299 if(solve_lin_eq() == 0) {
1301 } else {
1304 // solve baselineMin = afit*x + bfit for x to find the crossing point. If the crossing point is outside
1305 // the
1306 // acceptable range, set temin to the upper limit of the baseline range.
1307 cWpar->temin = static_cast<int>(floor((cWpar->baselineMin - cWpar->bfit) / cWpar->afit));
1308 cWpar->temin = std::max(cWpar->temin, cWpar->baseline_range);
1309 if(cWpar->temin > cWpar->temax) {
1311 }
1312 }
1313 }
1314 // End of the determination of the lower limit of the exclusion zone.
1315 }
1316}
1317
1319{
1320
1321 if(i == 1) {
1322 return par->t[1];
1323 }
1324
1325 if(i >= 2) {
1326 if(i < NSHAPE) {
1327 return par->t[i] * par->t[1] / (par->t[i] + par->t[1]);
1328 }
1329 }
1330
1331 return -1.;
1332}
1333
1335{
1336 /*************************************************************************
1337 This function calculates t0 given the matrix solution from FitCsIShape.
1338 In this case, the fit function is written as follows:
1339
1340 f(t) = C + (Af+As)*exp(t0/tRC)*exp(-t/tRC) - Af*exp(t0/tF')*exp(-t/tF')
1341 - As*exp(t0/tS')*exp(-t/tS')
1342
1343 This can be re-written as:
1344
1345 f(t) = C' + alpha*exp(-t/tRC) + beta*exp(-t/tF') + gamma*exp(-t/tS')
1346
1347Where:
1348C = C'
1349alpha = (Af+As)*exp(t0/tRC)
1350beta = -Af*exp(t0/tF')
1351gamma = -As*exp(t0/tS')
1352
1353Ignoring the constant, we have:
1354
1355f'(t0) = alpha*exp(-t0/tRC) + beta*exp(-t0/tF') + gamma*exp(-t0/tS') = 0
1356
1357For t<t0, f'(t)< 0, and for t>t0, f'(t)>0. This function finds the
1358intersection of f'(t) and 0 by linear interpolation from these endpoints.
1359 *************************************************************************/
1360
1361 double ta = wpar->baseline_range;
1362 // ta=wpar->temin;
1363 double fa = 0.;
1364
1365 // t0 must be between the baseline and the max
1366 // calculates fit value (no constant) at the end of the baseline range
1367 // this is the t<t0 point
1368 for(int i = 1; i < lineq_dim; i++) {
1369 double tau = GetCsITau(i, par);
1370 // getc(stdin);
1371 fa += lineq_solution[i] * exp(-ta / tau);
1372 }
1373
1374 double tb = wpar->tmax;
1375 // tb=wpar->temax;
1376 double fb = 0.;
1377
1378 // calculates fit value (no constant) at tmax
1379 // this is the t>t0 point
1380 for(int i = 1; i < lineq_dim; i++) {
1381 double tau = GetCsITau(i, par);
1382 fb += lineq_solution[i] * exp(-tb / tau);
1383 }
1384
1385 double delta = 1.;
1386
1387 double tc = 0.; // need this later to set wpar->t0
1388
1389 if((fa < 0) && (fb > 0)) {
1390 // keep the interpolation going until you get below epsilon
1391 /* |f(t0) - 0| = |f(t0)|< epsilon */
1392 while(delta > EPS) {
1393 double slope = -fa / (fb - fa); // interpolation slope for dependent variable t
1394
1395 //"reasonable" interpolation slopes
1396 slope = std::min(slope, 0.99);
1397
1398 slope = std::max(slope, 0.01);
1399 // its pretty harmless computationally
1400
1401 // tc is the estimate for t0
1402 tc = ta + slope * (tb - ta);
1403 double fc = 0.;
1404 for(int i = 1; i < lineq_dim; i++) {
1405 double tau = GetCsITau(i, par);
1406 fc += lineq_solution[i] * exp(-tc / tau);
1407 }
1408
1409 // really should have this, just to be safe
1410 if(fc == 0) {
1411 return tc;
1412 }
1413
1414 if(fc > 0) {
1415 tb = tc;
1416 fb = fc;
1417 } else {
1418 ta = tc;
1419 fa = fc;
1420 }
1421 delta = std::abs(fc);
1422 }
1423 } else {
1424 return -1;
1425 }
1426
1427 // set wpar->t0 here
1428 wpar->t0 = tc;
1429
1430 return tc;
1431}
1432
1434{
1435 if(!IsSet()) { return; }
1436 delete cWpar;
1437 cWpar = new WaveFormPar;
1438 cWpar->baseline_range = T0RANGE; // default only 8 samples! but can be increased with a multiplier in TPulseAnalyzer constructor
1439 cWpar->t90_flag = 0;
1440 cWpar->t50_flag = 0;
1441 cWpar->t10_flag = 0;
1442 cWpar->mflag = 0;
1443 cWpar->bflag = false;
1444 cWpar->t0 = 0;
1445 cWpar->baselinefin = 0;
1446
1447 get_baseline(); // Takes a small sample baseline
1448 get_tmax(); // Does a filtered max search
1449
1450 if(cWpar->mflag != 1 || !cWpar->bflag) { return; }
1451
1452 if(cWpar->tmax > cN) { cWpar->tmax = cN - 1; }
1453
1454 double amp = cWpar->max - cWpar->baseline;
1455 double y9 = cWpar->baseline + amp * .9;
1456 double y5 = cWpar->baseline + amp * .5;
1457 double y1 = cWpar->baseline + amp * .1;
1458
1459 for(int t = cWpar->tmax; t > 0; t--) {
1460 if(cWavebuffer[t] < y5) {
1461 cWpar->t50_flag = 1;
1462 cWpar->t50 = t + 0.5;
1463 break;
1464 }
1465 }
1466
1467 if(cWpar->t50_flag != 1) { return; }
1468
1469 for(int t = cWpar->t50; t < cWpar->tmax; t++) {
1470 if(cWavebuffer[t] > y9) {
1471 cWpar->t90_flag = 1;
1472 cWpar->t90 = t - 0.5;
1473 break;
1474 }
1475 }
1476
1477 for(int t = cWpar->t50; t > 0; t--) {
1478 if(cWavebuffer[t] < y1) {
1479 cWpar->t10_flag = 1;
1480 cWpar->t10 = t + 0.5;
1481 break;
1482 }
1483 }
1484
1485 if(cWpar->t10_flag != 1) { return; }
1486
1487 double t0 = cWpar->t50 - ((cWpar->t50 - cWpar->t10) * 1.2);
1488 if(cWpar->t90_flag == 1) {
1489 t0 += cWpar->t90 - ((cWpar->t90 - cWpar->t10) * 1.125);
1490 t0 *= 0.5;
1491 }
1492 t0 = std::max(t0, 0.);
1493
1494 // std::cout<<std::endl<<t0<<std::flush;
1495 cWpar->t0 = t0;
1496 get_baseline_fin(); // baseline all the way up to t0
1497}
1498
1499bool TPulseAnalyzer::SiliShapePrepare(double tauDecay, double tauRise)
1500{
1501 if(IsSet()) {
1502 // double t0=fit_newT0();//fits the T0 in the SFU way with my added bit at the end for a nice baseline calc
1503 // double t0=cWpar->t0;
1504 // int exclusion=t0+3;
1505 // if(t0<1){//if the fit_newT0() failed
1506 // exclusion=10;
1507 // if(abs(baseline-cWavebuffer[0])>100)baseline=cWavebuffer[0];
1508 // }
1509
1510 // New simplified guesses because fit_newT0 was taking 1000% longer
1511 GetQuickPara();
1512 cWpar->amplitude = 0;
1513 cWpar->tauDecay = tauDecay;
1514 cWpar->tauRise = tauRise;
1515 cWpar->bflag = false; // baseline
1516 cWpar->baseamp = 0;
1517 cWpar->basefreq = 0;
1518 cWpar->basephase = 0;
1519 cWpar->osciflag = 0;
1520
1521 if(cWpar->t10_flag == 0) { return false; }
1522
1523 // GetQuickPara() Returns values that are spurious if the baseline is missing or <<T0RANGE
1524 if(cWpar->t0 < cWpar->baseline_range) { // Is there is no clear baseline baseline
1525 if(!(cWpar->baselineStDevfin / (cWpar->max - cWpar->baselinefin) < 0.035)) { // Strict (previously 0.05) limit determined from data
1526 return false;
1527 }
1528 }
1529 cWpar->bflag = true;
1530 return true;
1531 }
1532 return false;
1533}
1534
1535bool TPulseAnalyzer::GetSiliShape(double tauDecay, double tauRise)
1536{
1537 if(IsSet()) {
1538
1539 if(!SiliShapePrepare(tauDecay, tauRise)) { return false; }
1540
1541 int exclusion = cWpar->t10;
1542 double baseline = cWpar->baselinefin;
1543
1544 /**************************************************************************
1545 // Parametes for this function
1546 //fShpar->t0 t0 (time where signal starts, calculated)
1547 //fShpar->tau[0]) decay (provided)
1548 //fShpar->tau[1]) rise (provided)
1549 //fShpar->tau[2]) slow (not used)
1550 //fShpar->tau[3]) diode (not used)
1551 //fShpar->am[0]) baseline (provided)
1552 //fShpar->am[1]) fast (amplitude, calculated )
1553 //fShpar->am[2]) slow (not used)
1554 //fShpar->am[3]) diode (not used)
1555
1556 linearized chi square fit is Mu = v where M is a data matrix
1557 u, v are vectors; u is the parameter vector (solution)
1558 note that in this formulation, chisq_min = y_i^2-sum(u_iv_i)
1559 **************************************************************************/
1560
1561 // cout<<baseline<<" "<<exclusion<<endl ;// ResetShapeAmplitudes();/ fShpar->am[0] = baseline ;
1562
1563 lineq_dim = 2;
1564 memset(lineq_matrix, 0, sizeof(lineq_matrix)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1565 memset(lineq_vector, 0, sizeof(lineq_vector)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1566 memset(lineq_solution, 0, sizeof(lineq_solution)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1567
1568 if(exclusion >= cN) { return false; }
1569 if(lineq_dim >= cN) { return false; }
1570
1571 // setting M[0,0] V[0] V[1]
1572 for(int j = exclusion; j < cN; j++) {
1573 // vector
1574 if((cWavebuffer[j] - baseline) < 0) {
1575 exclusion++;
1576 continue;
1577 } // this is crucial for oscillations
1578 // if (j%10==0) cout<<j<<" "<< cWavebuffer[j]<<" "<<cWavebuffer[j]-baseline<<endl ;
1579 double signal = log(cWavebuffer[j] - baseline) + j / tauDecay; // sum of Y_i where Y_i = (y_i - baseline)*exp(-t_i/tauDecay)
1580 lineq_vector[0] += exp(signal);
1581 lineq_vector[1] -= exp(signal - j / tauRise); // sum of Y_i*X_i
1582 // Matrix
1583 lineq_matrix[0][0] += 1;
1584 }
1585
1586 // create matrix for linearized fit
1587 // setting elements M[0,1] M[1,0] M[1,1]
1588 long double sum = -static_cast<double>(exclusion) / tauRise + log(1. - exp(-(static_cast<double>(cN - exclusion)) / tauRise));
1589 sum -= log(1. - exp(-1. / tauRise)); // finishing the geometric sequence sum
1590 lineq_matrix[1][0] = -exp(sum);
1591 lineq_matrix[0][1] = -exp(sum);
1592
1593 double tauRise_2 = tauRise / 2.;
1594 sum = -static_cast<double>(exclusion) / tauRise_2 + log(1. - exp(-(static_cast<double>(cN - exclusion)) / tauRise_2));
1595 sum -= log(1. - exp(-1. / tauRise_2));
1596 lineq_matrix[1][1] = exp(sum);
1597
1598 // cout<<lineq_matrix[0][0]<<" "<<lineq_matrix[0][1]<<" ---------- "<<lineq_vector[0] <<endl ;
1599 // cout<<lineq_matrix[1][0]<<" "<<lineq_matrix[1][1]<<" ---------- "<<lineq_vector[1] <<endl ;
1600
1601 // solve the matrix equation Mu = v -> u = M^(-1)v where M^(-1) is the inverse
1602 // of M. note this has no solution if M is not invertable!
1603
1604 // error if the matrix cannot be inverted
1605 if(solve_lin_eq() == 0) {
1606 return false;
1607 }
1608 // calculate amplitudes
1609 double beta = lineq_solution[0];
1610 double alpha = lineq_solution[1];
1611
1612 double dom = exp(((log(alpha) - log(beta)) * tauRise) / tauDecay);
1613 if(dom > 0 || dom < 0) { cWpar->amplitude = beta / dom; }
1614
1615 double tt = (log(alpha) - log(beta)) * tauRise;
1616 if(tt > 0) { cWpar->t0 = tt; }
1617
1618 return true;
1619 }
1620 return false;
1621}
1622
1623// Significantly slower and should only be used in non-sorting analysis of poor waveform
1624// Needs initial estimates even if fitting those parameters
1625// Setting basefreq>0 opens a very experimental/bad mode
1626bool TPulseAnalyzer::GetSiliShapeTF1(double tauDecay, double tauRise, double baseline, double basefreq)
1627{
1628 TGraph* h = GetWaveGraph();
1629 if(h != nullptr) { // Graph better than hist for stats and simplicity
1630 SiliShapePrepare(tauDecay, tauRise);
1631 TF1 g = Getsilifit();
1632
1633 // g.SetParameter(0,cWpar->t0);
1634 // g.SetParameter(1,cWpar->tauDecay);
1635 // g.SetParameter(2,cWpar->tauRise);
1636 // g.SetParameter(3,cWpar->baselinefin);
1637 // g.SetParameter(4,cWpar->amplitude);
1638
1639 // Currently constrained for positive waveforms
1640 double r = cWpar->max - cWavebuffer[0];
1641 g.SetParameter(4, r * 1.05);
1642
1643 if(cWpar->bflag) { // Have reasonable T0 & base, fit the shape
1644 g.SetParLimits(0, cWpar->t0 * 0.5, cWpar->t0 * 1.5);
1645 g.SetParLimits(1, tauDecay * 0.3, tauDecay * 3.0);
1646 g.SetParLimits(2, tauRise * 0.3, tauRise * 1.5);
1647 g.FixParameter(3, cWpar->baselinefin);
1648 g.SetParLimits(4, r * 0.5, r * 2.0);
1649 } else { // Have no T0 or base, FIX the shape
1650 g.SetParLimits(0, -cN, cWpar->baseline_range);
1651 g.FixParameter(1, tauDecay);
1652 g.FixParameter(2, tauRise);
1653 g.FixParameter(3, baseline);
1654 // g.SetParameter(3,baseline);
1655 // g.SetParLimits(3,baseline-300,baseline+300);
1656 g.SetParLimits(4, r * 0.1, r * 10.0);
1657 }
1658
1659 if(basefreq > 0) {
1660 cWpar->osciflag = 1;
1661 g.ReleaseParameter(5);
1662 g.ReleaseParameter(6);
1663 g.ReleaseParameter(7);
1664 g.SetParameter(5, basefreq);
1665 g.SetParameter(6, 0.5);
1666 g.SetParameter(7, r * 0.1);
1667 g.SetParLimits(5, basefreq * 0.5, basefreq * 2.0);
1668 g.SetParLimits(6, 0, 1);
1669 g.SetParLimits(7, 0, r * 10.0);
1670 }
1671
1672 int res = h->Fit(&g, "QN");
1673 delete h;
1674
1675 if(res == 0) {
1676 cWpar->t0 = g.GetParameter(0);
1677 cWpar->tauDecay = g.GetParameter(1);
1678 cWpar->tauRise = g.GetParameter(2);
1679 cWpar->baselinefin = g.GetParameter(3);
1680 cWpar->amplitude = g.GetParameter(4);
1681 cWpar->basefreq = g.GetParameter(5);
1682 cWpar->basephase = g.GetParameter(6);
1683 cWpar->baseamp = g.GetParameter(7);
1684
1685 return true;
1686 }
1687 }
1688 return false;
1689}
1690
1691double TPulseAnalyzer::SiLiFitFunction(double* i, double* p) // NOLINT(readability-non-const-parameter)
1692{
1693 // p[0]-p[2] are t0, RC, Tau
1694 // p[3]-p[4] are baseline, A0
1695 // p[5]-p[7] are osci freq,phase,amp
1696
1697 double x = i[0] - p[0];
1698
1699 double s = p[3];
1700 if(x > 0) { s += p[4] * (1 - exp(-x / p[2])) * exp(-x / p[1]); }
1701 if(p[7] > 0) { s += p[7] * sin((p[6] + i[0] / p[5]) * 2 * TMath::Pi()); }
1702
1703 return s;
1704}
1705
1707{
1708 if(set && cWpar != nullptr) {
1709 std::ostringstream name;
1710 name << "Fit" << fNameIter;
1711 ++fNameIter;
1712 TF1 g(name.str().c_str(), SiLiFitFunction, 0, cN, 8);
1713
1714 g.SetParameter(0, cWpar->t0);
1715 g.SetParameter(1, cWpar->tauDecay);
1716 g.SetParameter(2, cWpar->tauRise);
1717 g.SetParameter(3, cWpar->baselinefin);
1718 g.SetParameter(4, cWpar->amplitude);
1719 g.FixParameter(5, cWpar->basefreq);
1720 g.FixParameter(6, cWpar->basephase);
1721 g.FixParameter(7, cWpar->baseamp);
1722
1723 g.SetLineColor(kRed);
1724
1725 return g;
1726 }
1727
1728 return {};
1729}
1730
1732{
1733 double Smirnov = 0;
1734 if(set && cWpar != nullptr) {
1735 double gsum = 0.;
1736 double wsum = 0.;
1737 TF1 g = Getsilifit();
1738 for(Int_t i = 0; i < cN; i++) {
1739 wsum += abs(cWavebuffer[i]);
1740 gsum += abs(g.Eval(i + 0.5));
1741 Smirnov += abs(wsum - gsum);
1742 }
1743 }
1744 return Smirnov;
1745}
1746
1748{
1749 if(!set) { return; }
1750 DrawWave();
1751 if(cWpar != nullptr) {
1752 Getsilifit().DrawCopy("same");
1753 std::cout << "t0:\t" << cWpar->t0 << ", A:\t" << cWpar->amplitude << std::endl;
1754 }
1755}
1756
1758{
1759 TH1I* h = GetWaveHist();
1760 if(h != nullptr) {
1761 h->DrawCopy();
1762 delete h;
1763 }
1764}
1765
1768{
1769 if(cN == 0 || !set) {
1770 return nullptr;
1771 }
1772 std::ostringstream name;
1773 name << "WaveformHist" << fNameIter;
1774 ++fNameIter; // Avoid naming conflicts with TNamed
1775 TH1I* h = new TH1I(name.str().c_str(), name.str().c_str(), cN, -0.5, cN - 0.5); // midpoint should be the value, else time is off
1776 for(Int_t i = 0; i < cN; i++) {
1777 h->SetBinContent(i + 1, cWavebuffer[i]);
1778 }
1779 return h;
1780}
1781
1783{
1784 if(cN == 0 || !set) {
1785 return nullptr;
1786 }
1787 auto* g = new TGraph();
1788 for(Int_t i = 0; i < cN; i++) {
1789 g->SetPoint(i, i, cWavebuffer[i]);
1790 }
1791 return g;
1792}
1793
1795{
1796 if(cN == 0 || !set) {
1797 return;
1798 }
1799
1800 DrawWave();
1801
1802 if(spar != nullptr) {
1803 TF1 f("fit", "[2] + [0]*sin(6.283185307 * (x - [1])/(2*8.48409))", 0, cN);
1804
1805 f.SetParameter(0, spar->A);
1806 f.SetParameter(1, spar->t0);
1807 f.SetParameter(2, spar->C);
1808
1809 f.DrawCopy("same");
1810
1811 std::cout << "t0:\t" << spar->t0 << ", A:\t" << spar->A << ", O:\t" << spar->C << std::endl;
1812 }
1813}
1814
1816{
1817
1818 if(cN == 0 || !set) {
1819 return;
1820 }
1821
1822 DrawWave();
1823
1824 if(cWpar != nullptr) {
1825 TF1 g("fit", "[0]+[1]*x", 0, cWpar->temax);
1826 TF1 f("fit", "[0]+[1]*x+[2]*x*x", cWpar->temin, cWpar->thigh);
1827
1828 g.SetParameter(0, cWpar->b0);
1829 g.SetParameter(1, cWpar->b1);
1830 g.SetLineColor(kRed);
1831
1832 f.SetParameter(0, cWpar->s0);
1833 f.SetParameter(1, cWpar->s1);
1834 f.SetParameter(2, cWpar->s2);
1835 f.SetLineColor(8);
1836
1837 f.DrawCopy("same");
1838 g.DrawCopy("same");
1839
1840 std::cout << "t0:\t" << cWpar->t0 << std::endl;
1841 }
1842}
1843
1845{
1846
1847 if(cN == 0 || !set) {
1848 return;
1849 }
1850
1851 DrawWave();
1852 if(cWpar != nullptr) {
1853 TF1 f("base", "[0]", 0, cWpar->baseline_range);
1854 TF1 g("basemin", "[0]", cWpar->temin, cWpar->temax);
1855 TF1 h("basemax", "[0]", cWpar->temin, cWpar->temax);
1856 TF1 r("risetime", "[0]*x+[1]", cWpar->temin, cWpar->temax + 3 * FILTER);
1857
1858 std::cout << "Baseline:\t" << cWpar->baseline << std::endl;
1859 std::cout << "Zero crossing:\t" << cWpar->t0 << std::endl;
1860
1861 f.SetParameter(0, cWpar->baseline);
1862 f.SetLineColor(kGreen);
1863
1864 g.SetParameter(0, cWpar->baselineMin);
1865 g.SetLineColor(kBlue);
1866
1867 h.SetParameter(0, cWpar->baselineMax);
1868 h.SetLineColor(kBlack);
1869
1870 r.SetParameter(0, cWpar->afit);
1871 r.SetParameter(1, cWpar->bfit);
1872 r.SetLineColor(kRed);
1873
1874 f.DrawCopy("same");
1875 g.DrawCopy("same");
1876 h.DrawCopy("same");
1877 r.DrawCopy("same");
1878 }
1879}
1880
1882{
1883
1884 if(cN == 0 || !set || !CsIIsSet()) {
1885 return;
1886 }
1887
1888 DrawWave();
1889 if(cWpar != nullptr) {
1890 TF1 shape("shape", TGRSIFunctions::CsIFitFunction, 0, cN, 9);
1891
1892 shape.SetParameter(0, shpar->t[0]);
1893 shape.SetParameter(1, shpar->t[1]);
1894 shape.SetParameter(2, shpar->t[2]);
1895 shape.SetParameter(3, shpar->t[3]);
1896 shape.SetParameter(4, shpar->t[4]);
1897 shape.SetParameter(5, shpar->am[0]);
1898 shape.SetParameter(6, shpar->am[2]);
1899 shape.SetParameter(7, shpar->am[3]);
1900 shape.SetParameter(8, shpar->am[4]);
1901 shape.SetLineColor(kRed);
1902
1903 std::cout << "t0:\t" << shpar->t[0] << ",\ttRC:\t" << shpar->t[1] << ",\ttF:\t" << shpar->t[2] << ",\ttS:\t" << shpar->t[3] << ",\tTGamma:\t" << shpar->t[4] << std::endl;
1904 std::cout << "Baseline:\t" << shpar->am[0] << ",\tFast:\t" << shpar->am[2] << ",\tSlow:\t" << shpar->am[3] << ",\tGamma:\t" << shpar->am[4] << std::endl;
1905
1906 shape.DrawCopy("same");
1907 }
1908}
1909// NOLINTEND(cppcoreguidelines-narrowing-conversions)
1910
1911/*======================================================*/
1913{
1914 std::cout << "== Currently established waveform parameters ============" << std::endl;
1915 std::cout << "baseline : " << std::setw(10) << cWpar->baseline << std::endl;
1916 std::cout << "baseline st. dev.: " << std::setw(10) << cWpar->baselineStDev << std::endl;
1917 std::cout << "max : " << std::setw(10) << cWpar->max << std::endl;
1918 std::cout << "tmax : " << std::setw(10) << cWpar->tmax << std::endl;
1919 std::cout << "temin : " << std::setw(10) << static_cast<double>(cWpar->temin) << std::endl;
1920 std::cout << "temax : " << std::setw(10) << static_cast<double>(cWpar->temax) << std::endl;
1921 std::cout << "t0 : " << std::setw(10) << cWpar->t0 << std::endl;
1922}
const std::vector< Short_t > * GetWaveform() const
!
virtual bool HasWave() const
!
long double lineq_solution[20]
std::vector< Short_t > cWavebuffer
double fit_rf(double=2 *8.48409)
long double determinant(int)
bool CsIIsSet() const
static const int BADCHISQ_PAR_T0
static const int BADCHISQ_LIN_T0
bool GetSiliShapeTF1(double tauDecay, double tauRise, double baseline, double basefreq=0)
static const int CSI_BASELINE_RANGE
static const int BAD_EXCLUSION_ZONE
double GetCsIt0(ShapePar *, WaveFormPar *)
double get_tfrac(double, double, double)
static const int NSHAPE
void SetCsI(bool option=true)
static const int BADCHISQ_FAIL_DIRECT
void Clear(Option_t *opt="")
bool IsSet() const
double GetCsITau(int, ShapePar *)
static const int BAD_BASELINE_RANGE
static double SiLiFitFunction(double *i, double *p)
long double copy_matrix[20][20]
ShapePar * csiTestShpar[4]
static const int BADCHISQ_SMOOTH_T0
long double lineq_vector[20]
bool SiliShapePrepare(double tauDecay, double tauRise)
int fit_parabola(int, int, ParPar *)
int fit_line(int, int, LinePar *)
WaveFormPar * cWpar
static int fNameIter
static const int BADCHISQ_AMPL
int fit_smooth_parabola(int, int, double, ParPar *)
static const int MAX_SAMPLES
static const int BADCHISQ_MAT
static const int BAD_MAX
WaveFormPar * csiTestWpar[4]
int16_t good_baseline()
long double lineq_matrix[20][20]
virtual ~TPulseAnalyzer()
int FitCsIShape(int, ShapePar *, WaveFormPar *)
double get_parabolic_T0()
static const int NOISE_LEVEL_CSI
static const int BADCHISQ_NEG
static const int BADCHISQ_T0
void SetData(const TFragment &fragment, double=0)
TGraph * GetWaveGraph()
bool GetSiliShape(double tauDecay, double tauRise)
double get_sin_par(double)
static const int BAD_PID
Double_t CsIFitFunction(Double_t *time, Double_t *par)