REST-for-Physics  v2.3
Rare Event Searches ToolKit for Physics
src/TRestRawSignal.cxx
1/*************************************************************************
2 * This file is part of the REST software framework. *
3 * *
4 * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza) *
5 * For more information see http://gifna.unizar.es/trex *
6 * *
7 * REST is free software: you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation, either version 3 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * REST is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have a copy of the GNU General Public License along with *
18 * REST in $REST_PATH/LICENSE. *
19 * If not, see http://www.gnu.org/licenses/. *
20 * For the list of contributors see $REST_PATH/CREDITS. *
21 *************************************************************************/
22
60#include "TRestRawSignal.h"
61
62#include <TAxis.h>
63#include <TF1.h>
64#include <TH1D.h>
65#include <TMath.h>
66#include <TRandom3.h>
67
68#include <numeric>
69
70using namespace std;
71
72ClassImp(TRestRawSignal);
73
78 fGraph = nullptr;
79
80 Initialize();
81}
82
88 fGraph = nullptr;
89
90 Initialize();
91
92 fSignalData.resize(nBins, 0);
93}
94
99
104 fSignalData.clear();
105 fPointsOverThreshold.clear();
106 fSignalID = -1;
107
109
110 fHeadPoints = 0;
111 fTailPoints = 0;
112
113 fBaseLine = 0;
114 fBaseLineSigma = 0;
115}
116
122 Int_t nBins = GetNumberOfPoints();
123 Initialize();
124 fSignalData.resize(nBins, 0);
125}
126
130void TRestRawSignal::AddPoint(Short_t value) { fSignalData.push_back(value); }
131
135void TRestRawSignal::AddPoint(Double_t value) {
136 if (value > numeric_limits<Short_t>::max()) {
137 value = numeric_limits<Short_t>::max();
138 } else if (value < numeric_limits<Short_t>::min()) {
139 value = numeric_limits<Short_t>::min();
140 }
141 AddPoint((Short_t)value);
142}
143
150 if (n >= GetNumberOfPoints()) {
151 if (fShowWarnings) {
152 std::cout << "TRestRawSignal::GetSignalData: outside limits" << endl;
153 std::cout << "Warnings at TRestRawSignal have been disabled" << endl;
154 fShowWarnings = false;
155 }
156 return 0xFFFF;
157 }
158 return fSignalData[n];
159}
160
170Double_t TRestRawSignal::GetData(Int_t n) const { return (Double_t)fSignalData[n] - fBaseLine; }
171
176Double_t TRestRawSignal::GetRawData(Int_t n) const { return (Double_t)fSignalData[n]; }
177
181void TRestRawSignal::IncreaseBinBy(Int_t bin, Double_t data) {
182 if (bin >= GetNumberOfPoints()) {
183 if (fShowWarnings) {
184 std::cout << "TRestRawSignal::IncreaseBinBy: outside limits" << endl;
185 std::cout << "Warnings at TRestRawSignal have been disabled" << endl;
186 fShowWarnings = false;
187 }
188
189 return;
190 }
191
192 fSignalData[bin] += data;
193}
194
223void TRestRawSignal::InitializePointsOverThreshold(const TVector2& thrPar, Int_t nPointsOver,
224 Int_t nPointsFlat) {
225 if (fRange.X() < 0) fRange.SetX(0);
226 if (fRange.Y() <= 0) fRange.SetY(GetNumberOfPoints());
227
228 fPointsOverThreshold.clear();
229
230 double pointTh = thrPar.X();
231 double signalTh = thrPar.Y();
232
233 double threshold = pointTh * fBaseLineSigma;
234
235 for (int i = fRange.X(); i < fRange.Y(); i++) {
236 // Filling a pulse with consecutive points that are over threshold
237 if (this->GetData(i) > threshold) {
238 int pos = i;
239 std::vector<double> pulse;
240 pulse.push_back(this->GetData(i));
241 i++;
242
243 // If the pulse ends in a flat end above the threshold, the parameter
244 // nPointsFlat will serve to artificially end the pulse.
245 // If nPointsFlat is big enough, this parameter will not affect the
246 // decision to cut this anomalous behaviour. And all points over threshold
247 // will be added to the pulse vector.
248 int flatN = 0;
249 while (i < fRange.Y() && this->GetData(i) > threshold) {
250 if (TMath::Abs(this->GetData(i) - this->GetData(i - 1)) > threshold) {
251 flatN = 0;
252 } else {
253 flatN++;
254 }
255
256 if (flatN < nPointsFlat) {
257 pulse.push_back(this->GetData(i));
258 i++;
259 } else {
260 break;
261 }
262 }
263
264 if (pulse.size() >= (unsigned int)nPointsOver) {
265 // auto stdev = TMath::StdDev(begin(pulse), end(pulse));
266 // calculate stdev
267 double mean = std::accumulate(pulse.begin(), pulse.end(), 0.0) / double(pulse.size());
268 double sq_sum = std::inner_product(pulse.begin(), pulse.end(), pulse.begin(), 0.0);
269 double stdev = std::sqrt(sq_sum / double(pulse.size()) - mean * mean);
270
271 if (stdev > signalTh * fBaseLineSigma) {
272 for (int j = pos; j < i; j++) {
273 fPointsOverThreshold.push_back(j);
274 }
275 }
276 }
277 }
278 }
279
281}
282
289 if (fRange.X() < 0) {
290 fRange.SetX(0);
291 }
292 if (fRange.Y() <= 0 || fRange.Y() > GetNumberOfPoints()) {
294 }
295
297
298 for (int n : fPointsOverThreshold) {
299 if (n >= fRange.X() && n < fRange.Y()) {
301 }
302 }
303}
304
311 if (fRange.X() < 0) {
312 fRange.SetX(0);
313 }
314 if (fRange.Y() <= 0 || fRange.Y() > GetNumberOfPoints()) {
316 }
317
318 Double_t sum = 0;
319 for (int i = fRange.X(); i < fRange.Y(); i++) {
320 sum += GetData(i);
321 }
322 return sum;
323}
324
329Double_t TRestRawSignal::GetIntegralInRange(Int_t startBin, Int_t endBin) {
330 if (startBin < 0) {
331 startBin = 0;
332 }
333 if (endBin <= 0 || endBin > GetNumberOfPoints()) {
334 endBin = GetNumberOfPoints();
335 }
336
337 Double_t sum = 0;
338 for (int i = startBin; i < endBin; i++) {
339 sum += GetRawData(i);
340 }
341 return sum;
342}
343
350 if (fThresholdIntegral == -1) {
351 if (fShowWarnings) {
352 std::cout << "TRestRawSignal::GetThresholdIntegral. "
353 "InitializePointsOverThreshold should be "
354 "called first!"
355 << endl;
356 fShowWarnings = false;
357 }
358 }
359 return fThresholdIntegral;
360}
361
368 if (fThresholdIntegral == -1)
369 cout << "REST Warning. TRestRawSignal::GetSlopeIntegral. "
370 "InitializePointsOverThreshold should be called first."
371 << endl;
372
373 Double_t sum = 0;
374 for (unsigned int n = 0; n < fPointsOverThreshold.size(); n++) {
375 if (n + 1 >= fPointsOverThreshold.size()) return sum;
376
377 sum += GetData(fPointsOverThreshold[n]);
378
379 if (GetData(fPointsOverThreshold[n + 1]) - GetData(fPointsOverThreshold[n]) < 0) break;
380 }
381 return sum;
382}
383
391 if (fThresholdIntegral == -1)
392 cout << "REST Warning. TRestRawSignal::GetRiseSlope. "
393 "InitializePointsOverThreshold should be called first."
394 << endl;
395
396 if (fPointsOverThreshold.size() < 2) {
397 // cout << "REST Warning. TRestRawSignal::GetRiseSlope. Less than 2 points!." << endl;
398 return 0;
399 }
400
401 Int_t maxBin = GetMaxPeakBin() - 1;
402
403 Double_t hP = GetData(maxBin);
404
405 Double_t lP = GetData(fPointsOverThreshold[0]);
406
407 return (hP - lP) / (maxBin - fPointsOverThreshold[0]);
408}
409
415 if (fThresholdIntegral == -1)
416 cout << "REST Warning. TRestRawSignal::GetRiseTime. "
417 "InitializePointsOverThreshold should be called first."
418 << endl;
419
420 if (fPointsOverThreshold.size() < 2) {
421 // cout << "REST Warning. TRestRawSignal::GetRiseTime. Less than 2 points!." << endl;
422 return 0;
423 }
424
426}
427
433 if (fRange.X() < 0) fRange.SetX(0);
434 if (fRange.Y() <= 0 || fRange.Y() > GetNumberOfPoints()) fRange.SetY(GetNumberOfPoints());
435
436 if (fThresholdIntegral == -1) {
437 cout << "REST Warning. TRestRawSignal::GetTripleMaxIntegral. "
438 "InitializePointsOverThreshold should be called first."
439 << endl;
440 return 0;
441 }
442
443 if (fPointsOverThreshold.size() < 2) {
444 // cout << "REST Warning. TRestRawSignal::GetTripleMaxIntegral. Points over
445 // "
446 // "threshold = "
447 // << fPointsOverThreshold.size() << endl;
448 return 0;
449 }
450
451 Int_t cBin = GetMaxPeakBin();
452
453 if (cBin + 1 >= GetNumberOfPoints()) return 0;
454
455 Double_t a1 = GetData(cBin);
456 Double_t a2 = GetData(cBin - 1);
457 Double_t a3 = GetData(cBin + 1);
458
459 return a1 + a2 + a3;
460}
461
466Double_t TRestRawSignal::GetAverageInRange(Int_t startBin, Int_t endBin) {
467 if (startBin < 0) startBin = 0;
468 if (endBin <= 0 || endBin > GetNumberOfPoints()) endBin = GetNumberOfPoints();
469
470 Double_t sum = 0;
471 for (int i = startBin; i <= endBin; i++) sum += this->GetData(i);
472
473 return sum / (endBin - startBin + 1);
474}
475
481 Int_t mIndex = this->GetMaxPeakBin();
482 Double_t maxValue = this->GetData(mIndex);
483
484 Double_t value = maxValue;
485 Int_t rightIndex = mIndex;
486 while (value > maxValue / 2) {
487 value = this->GetData(rightIndex);
488 rightIndex++;
489 }
490 Int_t leftIndex = mIndex;
491 value = maxValue;
492 while (value > maxValue / 2) {
493 value = this->GetData(leftIndex);
494 leftIndex--;
495 }
496
497 return rightIndex - leftIndex;
498}
499
506
511 Double_t max = numeric_limits<Double_t>::min();
512
513 Int_t index = 0;
514
515 if (fRange.Y() == 0 || fRange.Y() > GetNumberOfPoints()) fRange.SetY(GetNumberOfPoints());
516 if (fRange.X() < 0) fRange.SetX(0);
517
518 for (int i = fRange.X(); i < fRange.Y(); i++) {
519 if (this->GetData(i) > max) {
520 max = GetData(i);
521 index = i;
522 }
523 }
524
525 return index;
526}
527
534
539 Double_t min = numeric_limits<Double_t>::max();
540 Int_t index = 0;
541
542 if (fRange.Y() == 0 || fRange.Y() > GetNumberOfPoints()) fRange.SetY(GetNumberOfPoints());
543 if (fRange.X() < 0) fRange.SetX(0);
544
545 for (int i = fRange.X(); i < fRange.Y(); i++) {
546 if (this->GetData(i) < min) {
547 min = GetData(i);
548 index = i;
549 }
550 }
551
552 return index;
553}
554
559 if (Nflat <= 0) return false;
560 // GetMaxPeakBin() will always find the first max peak bin if multiple bins are in same max value.
561 int index = GetMaxPeakBin();
562 Short_t value = fSignalData[index];
563
564 bool sat = false;
565 if (index + Nflat <= (int)fSignalData.size()) {
566 for (int i = index; i < index + Nflat; i++) {
567 if (fSignalData[i] != value) {
568 break;
569 }
570 if (i == index + Nflat - 1) {
571 sat = true;
572 }
573 }
574 }
575
576 return sat;
577}
578
588void TRestRawSignal::GetDifferentialSignal(TRestRawSignal* diffSignal, Int_t smearPoints) {
589 if (smearPoints <= 0) smearPoints = 1;
590 diffSignal->Initialize();
591
592 for (int i = 0; i < smearPoints; i++) {
593 diffSignal->AddPoint((Short_t)0);
594 }
595
596 for (int i = smearPoints; i < this->GetNumberOfPoints() - smearPoints; i++) {
597 Double_t value = 0.5 * (this->GetData(i + smearPoints) - GetData(i - smearPoints)) / smearPoints;
598
599 diffSignal->AddPoint((Short_t)value);
600 }
601
602 for (int i = GetNumberOfPoints() - smearPoints; i < GetNumberOfPoints(); i++) {
603 diffSignal->AddPoint((Short_t)0);
604 }
605}
606
615void TRestRawSignal::GetWhiteNoiseSignal(TRestRawSignal* noiseSignal, Double_t noiseLevel) {
616 TRandom3 random(fSeed);
617
618 for (int i = 0; i < GetNumberOfPoints(); i++) {
619 Double_t value = this->GetData(i) + random.Gaus(0, noiseLevel);
620 // do not cast as short so that there are no problems with overflows
621 // (https://github.com/rest-for-physics/rawlib/issues/113)
622 noiseSignal->AddPoint(value);
623 }
624}
625
633void TRestRawSignal::GetSignalSmoothed(TRestRawSignal* smoothedSignal, Int_t averagingPoints) {
634 smoothedSignal->Initialize();
635
636 averagingPoints = (averagingPoints / 2) * 2 + 1; // make it odd >= averagingPoints
637
638 Double_t sumAvg = GetIntegralInRange(0, averagingPoints) / averagingPoints;
639
640 for (int i = 0; i <= averagingPoints / 2; i++) smoothedSignal->AddPoint((Short_t)sumAvg);
641
642 for (int i = averagingPoints / 2 + 1; i < GetNumberOfPoints() - averagingPoints / 2; i++) {
643 sumAvg -= this->GetRawData(i - (averagingPoints / 2 + 1)) / averagingPoints;
644 sumAvg += this->GetRawData(i + averagingPoints / 2) / averagingPoints;
645 smoothedSignal->AddPoint((Short_t)sumAvg);
646 }
647
648 for (int i = GetNumberOfPoints() - averagingPoints / 2; i < GetNumberOfPoints(); i++)
649 smoothedSignal->AddPoint(sumAvg);
650}
651
661std::vector<Float_t> TRestRawSignal::GetSignalSmoothed(Int_t averagingPoints, std::string option) {
662 std::vector<Float_t> result;
663
664 if (option == "") {
665 result.resize(GetNumberOfPoints());
666
667 averagingPoints = (averagingPoints / 2) * 2 + 1; // make it odd >= averagingPoints
668
669 Float_t sumAvg = (Float_t)GetIntegralInRange(0, averagingPoints) / averagingPoints;
670
671 for (int i = 0; i <= averagingPoints / 2; i++) result[i] = sumAvg;
672
673 for (int i = averagingPoints / 2 + 1; i < GetNumberOfPoints() - averagingPoints / 2; i++) {
674 sumAvg -= this->GetRawData(i - (averagingPoints / 2 + 1)) / averagingPoints;
675 sumAvg += this->GetRawData(i + averagingPoints / 2) / averagingPoints;
676 result[i] = sumAvg;
677 }
678
679 for (int i = GetNumberOfPoints() - averagingPoints / 2; i < GetNumberOfPoints(); i++)
680 result[i] = sumAvg;
681 } else if (ToUpper(option) == "EXCLUDE OUTLIERS") {
682 result = GetSignalSmoothed_ExcludeOutliers(averagingPoints);
683 } else {
684 cout << "TRestRawSignal::GetSignalSmoothed. Error! No such option!" << endl;
685 }
686 return result;
687}
688
698std::vector<Float_t> TRestRawSignal::GetSignalSmoothed_ExcludeOutliers(Int_t averagingPoints) {
699 std::vector<Float_t> result(GetNumberOfPoints());
700
701 if (fBaseLine == 0) CalculateBaseLine(5, GetNumberOfPoints() - 5, "ROBUST");
702
703 averagingPoints = (averagingPoints / 2) * 2 + 1; // make it odd >= averagingPoints
704
705 Float_t sumAvg = (Float_t)GetIntegralInRange(0, averagingPoints) / averagingPoints;
706
707 // Points at the beginning, where we can calculate a moving average
708 for (int i = 0; i <= averagingPoints / 2; i++) result[i] = sumAvg;
709
710 // Points in the middle
711 float_t amplitude;
712 for (int i = averagingPoints / 2 + 1; i < GetNumberOfPoints() - averagingPoints / 2; i++) {
713 amplitude = this->GetRawData(i - (averagingPoints / 2 + 1));
714 sumAvg -= (std::abs(amplitude - fBaseLine) > 3 * fBaseLineSigma) ? fBaseLine / averagingPoints
715 : amplitude / averagingPoints;
716 amplitude = this->GetRawData(i + averagingPoints / 2);
717 sumAvg += (std::abs(amplitude - fBaseLine) > 3 * fBaseLineSigma) ? fBaseLine / averagingPoints
718 : amplitude / averagingPoints;
719 result[i] = sumAvg;
720 }
721
722 // Points at the end, where we can calculate a moving average
723 for (int i = GetNumberOfPoints() - averagingPoints / 2; i < GetNumberOfPoints(); i++) result[i] = sumAvg;
724 return result;
725}
726
737void TRestRawSignal::GetBaseLineCorrected(TRestRawSignal* smoothedSignal, Int_t averagingPoints) {
738 smoothedSignal->Initialize();
739
740 std::vector<Float_t> averagedSignal = GetSignalSmoothed(averagingPoints, "EXCLUDE OUTLIERS");
741
742 for (int i = 0; i < GetNumberOfPoints(); i++) {
743 smoothedSignal->AddPoint(GetRawData(i) - averagedSignal[i]);
744 }
745}
746
752void TRestRawSignal::CalculateBaseLineMean(Int_t startBin, Int_t endBin) {
753 if (endBin - startBin <= 0) {
754 fBaseLine = 0.;
755 } else if (endBin > (int)fSignalData.size()) {
756 cout << "TRestRawSignal::CalculateBaseLine. Error! Baseline range exceeds the rawdata depth!!"
757 << endl;
758 endBin = fSignalData.size();
759 } else {
760 Double_t baseLine = 0;
761 for (int i = startBin; i < endBin; i++) baseLine += fSignalData[i];
762 fBaseLine = baseLine / (endBin - startBin);
763 }
764}
765
771void TRestRawSignal::CalculateBaseLineMedian(Int_t startBin, Int_t endBin) {
772 if (endBin - startBin <= 0) {
773 fBaseLine = 0.;
774 } else if (endBin > (int)fSignalData.size()) {
775 cout << "TRestRawSignal::CalculateBaseLine. Error! Baseline range exceeds the rawdata depth!!"
776 << endl;
777 endBin = fSignalData.size();
778 } else {
779 vector<Short_t>::const_iterator first = fSignalData.begin() + startBin;
780 vector<Short_t>::const_iterator last = fSignalData.begin() + endBin;
781 vector<Short_t> v(first, last);
782 const Short_t* signalInRange = &v[0];
783 fBaseLine = TMath::Median(endBin - startBin, signalInRange);
784 }
785}
786
794 if (endBin - startBin <= 0) {
795 fBaseLine = 0.;
796 return;
797 } else if (endBin > static_cast<int>(fSignalData.size())) {
798 cout << "TRestRawSignal::CalculateBaseLine. Error! Baseline range exceeds the rawdata depth!!"
799 << endl;
800 endBin = fSignalData.size();
801 } else {
802 // Extract the data within the interval
803 std::vector<Short_t> data(fSignalData.begin() + startBin, fSignalData.begin() + endBin);
804 std::sort(data.begin(), data.end());
805
806 // Calculate Q1 and Q3 for IQR
807 size_t dataSize = data.size();
808 Short_t Q1 = data[dataSize / 4];
809 Short_t Q3 = data[(3 * dataSize) / 4];
810 Double_t lowerBound = Q1;
811 Double_t upperBound = Q3;
812
813 // Filter out the outliers
814 std::vector<Short_t> filteredData;
815 for (const auto& value : data) {
816 if (value >= lowerBound && value <= upperBound) {
817 filteredData.emplace_back(value);
818 }
819 }
820
821 // Calculate median of filtered data
822 if (filteredData.empty()) {
823 fBaseLine = TMath::Median(data.size(),
824 &data[0]); // Fall back to original median if all values are outliers
825 } else {
826 fBaseLine = TMath::Median(filteredData.size(), &filteredData[0]);
827 }
828 }
829}
830
842void TRestRawSignal::CalculateBaseLine(Int_t startBin, Int_t endBin, const std::string& option) {
843 if (ToUpper(option) == "ROBUST") {
844 CalculateBaseLineMedian(startBin, endBin);
845 CalculateBaseLineSigmaIQR(startBin, endBin);
846 } else if (ToUpper(option) == "OUTLIERS") {
849 } else {
850 CalculateBaseLineMean(startBin, endBin);
851 CalculateBaseLineSigmaSD(startBin, endBin);
852 }
853}
854
860void TRestRawSignal::CalculateBaseLineSigmaSD(Int_t startBin, Int_t endBin) {
861 if (endBin - startBin <= 0) {
862 fBaseLineSigma = 0;
863 } else {
864 Double_t baseLineSigma = 0;
865 for (int i = startBin; i < endBin; i++)
866 baseLineSigma += (fBaseLine - fSignalData[i]) * (fBaseLine - fSignalData[i]);
867 fBaseLineSigma = TMath::Sqrt(baseLineSigma / (endBin - startBin));
868 }
869}
870
877void TRestRawSignal::CalculateBaseLineSigmaIQR(Int_t startBin, Int_t endBin) {
878 if (endBin - startBin <= 0) {
879 fBaseLineSigma = 0;
880 } else {
881 vector<Short_t>::const_iterator first = fSignalData.begin() + startBin;
882 vector<Short_t>::const_iterator last = fSignalData.begin() + endBin;
883 vector<Short_t> v(first, last);
884 std::sort(v.begin(), v.end());
885 Short_t Q1 = v[(int)(endBin - startBin) * 0.25];
886 Short_t Q3 = v[(int)(endBin - startBin) * 0.75];
887 Double_t IQR = Q3 - Q1;
889 IQR / 1.349; // IQR/1.349 equals the standard deviation in case of normally distributed data
890 }
891}
892
900 if (endBin - startBin <= 0) {
901 fBaseLineSigma = 0.;
902 return;
903 } else if (endBin > static_cast<int>(fSignalData.size())) {
904 cout << "TRestRawSignal::CalculateBaseLineSigma. Error! Range exceeds the rawdata depth!!" << endl;
905 endBin = fSignalData.size();
906 } else {
907 // Extract the data within the interval
908 std::vector<Short_t> data(fSignalData.begin() + startBin, fSignalData.begin() + endBin);
909 std::sort(data.begin(), data.end());
910
911 // Calculate Q1 and Q3 for IQR
912 size_t dataSize = data.size();
913 Short_t Q1 = data[dataSize / 4];
914 Short_t Q3 = data[(3 * dataSize) / 4];
915 Double_t lowerBound = Q1;
916 Double_t upperBound = Q3;
917
918 // Filter out the outliers
919 std::vector<Short_t> filteredData;
920 for (const auto& value : data) {
921 if (value >= lowerBound && value <= upperBound) {
922 filteredData.emplace_back(value);
923 }
924 }
925
926 // Calculate standard deviation of filtered data
927 if (filteredData.empty()) {
928 fBaseLineSigma = 0.; // If all values are outliers, set sigma to zero
929 } else {
930 double mean =
931 std::accumulate(filteredData.begin(), filteredData.end(), 0.0) / filteredData.size();
932 double variance = 0.0;
933 for (const auto& value : filteredData) {
934 variance += std::pow(value - mean, 2);
935 }
936 fBaseLineSigma = std::sqrt(variance / filteredData.size()); // Standard deviation
937 }
938 }
939}
940
944void TRestRawSignal::AddOffset(Short_t offset) {
945 if (fBaseLine != 0 || fBaseLineSigma != 0) fBaseLineSigma += (Double_t)offset;
946 for (int i = 0; i < GetNumberOfPoints(); i++) fSignalData[i] = fSignalData[i] + offset;
947}
948
952void TRestRawSignal::Scale(Double_t value) {
953 for (int i = 0; i < GetNumberOfPoints(); i++) {
954 Double_t scaledValue = value * fSignalData[i];
955 fSignalData[i] = (Short_t)scaledValue;
956 }
957}
958
964 if (this->GetNumberOfPoints() != signal.GetNumberOfPoints()) {
965 cout << "ERROR : TRestRawSignal::SignalAddition." << endl;
966 cout << "I cannot add two signals with different number of points" << endl;
967 return;
968 }
969
970 for (int i = 0; i < GetNumberOfPoints(); i++) {
971 fSignalData[i] += signal.GetData(i);
972 }
973}
974
978void TRestRawSignal::WriteSignalToTextFile(const TString& filename) {
979 // We should check it is writable
980 FILE* file = fopen(filename.Data(), "w");
981 for (int i = 0; i < GetNumberOfPoints(); i++) fprintf(file, "%d\t%lf\n", i, GetData(i));
982 fclose(file);
983}
984
989 cout << "---------------------" << endl;
990 cout << "Signal id : " << this->GetSignalID() << endl;
991 cout << "Baseline : " << fBaseLine << endl;
992 cout << "Baseline sigma : " << fBaseLineSigma << endl;
993 cout << "+++++++++++++++++++++" << endl;
994 for (int i = 0; i < GetNumberOfPoints(); i++)
995 cout << "Bin : " << i << " amplitude : " << GetRawData(i) << endl;
996 cout << "---------------------" << endl;
997}
998
1002TGraph* TRestRawSignal::GetGraph(Int_t color) {
1003 delete fGraph;
1004 fGraph = new TGraph();
1005
1006 fGraph->SetLineWidth(2);
1007 fGraph->SetLineColor(color % 8 + 1);
1008 fGraph->SetMarkerStyle(7);
1009
1010 for (int i = 0; i < GetNumberOfPoints(); i++) {
1011 fGraph->SetPoint(i, i, GetData(i));
1012 }
1013
1014 fGraph->GetXaxis()->SetLimits(0, GetNumberOfPoints() - 1);
1015
1016 /*
1017 * To draw x axis in multiples of 2
1018 for (int i = 0; i < values.size(); i++) {
1019 if (i % 32 != 0 && i != values.size() - 1){
1020 continue;
1021 }
1022 fGraph->GetXaxis()->SetBinLabel(fGraph->GetXaxis()->FindBin(i), std::to_string(i).c_str());
1023 }
1024 */
1025
1026 return fGraph;
1027}
1028
1029vector<pair<UShort_t, double>> TRestRawSignal::GetPeaks(double threshold, UShort_t distance) const {
1030 vector<pair<UShort_t, double>> peaks;
1031
1032 const UShort_t smoothingWindow =
1033 10; // Region to compare for peak/no peak classification. 10 means 5 bins to each side
1034 const size_t numPoints = GetNumberOfPoints();
1035
1036 if (numPoints == 0) {
1037 return peaks;
1038 }
1039
1040 // Pre-calculate smoothed values for all bins using a rolling sum
1041 vector<double> smoothedValues(numPoints, 0.0);
1042 double currentSum = 0.0;
1043 UShort_t windowSize = smoothingWindow + 1;
1044
1045 // Initialize the sum for the first window
1046 for (UShort_t i = 0; i < static_cast<UShort_t>(std::min<size_t>(windowSize, numPoints)); ++i) {
1047 currentSum += GetRawData(i);
1048 }
1049 smoothedValues[0] = currentSum / windowSize;
1050
1051 for (UShort_t i = 1; i < numPoints; ++i) {
1052 if (i < smoothingWindow / 2 + 1) {
1053 // Adjust the window size at the beginning
1054 currentSum = 0.0;
1055 UShort_t currentWindowSize =
1056 static_cast<UShort_t>(std::min<size_t>(windowSize, i + smoothingWindow / 2 + 1));
1057 for (UShort_t j = 0; j < currentWindowSize; ++j) {
1058 currentSum += GetRawData(j);
1059 }
1060 smoothedValues[i] = currentSum / currentWindowSize;
1061 } else if (i > numPoints - smoothingWindow / 2 - 1) {
1062 // Adjust the window size at the end
1063 currentSum = 0.0;
1064 UShort_t currentWindowSize =
1065 static_cast<UShort_t>(std::min<size_t>(windowSize, numPoints - i + smoothingWindow / 2));
1066 for (UShort_t j = i - smoothingWindow / 2; j < numPoints; ++j) {
1067 currentSum += GetRawData(j);
1068 }
1069 smoothedValues[i] = currentSum / currentWindowSize;
1070 } else {
1071 // Use the rolling sum for the middle bins
1072 currentSum -= GetRawData(i - smoothingWindow / 2 - 1);
1073 currentSum += GetRawData(i + smoothingWindow / 2);
1074 smoothedValues[i] = currentSum / windowSize;
1075 }
1076 }
1077
1078 // Compare pre-calculated smoothed values to identify peaks
1079 for (UShort_t i = 0; i < numPoints; ++i) {
1080 const double smoothedValue = smoothedValues[i];
1081
1082 if (i >= smoothingWindow / 2 && i < numPoints - smoothingWindow / 2) {
1083 bool isPeak = true;
1084 int numGreaterEqual = 0; // Counter for smoothed values greater or equal to the studied bin
1085
1086 for (UShort_t j = i - smoothingWindow / 2; j <= i + smoothingWindow / 2; ++j) {
1087 if (j != i && smoothedValue <= smoothedValues[j]) {
1088 numGreaterEqual++;
1089 if (numGreaterEqual >
1090 3) { // If more than one smoothed value is greater or equal, it's not a peak
1091 isPeak = false;
1092 break;
1093 }
1094 }
1095 }
1096
1097 // If it's a peak and it´s above the threshold and further than distance to the previous peak, add
1098 // to peaks the biggest amplitude bin within the next "distance" bins and as amplitude the
1099 // TripleMaxAverage. This is because for flat regions the detected peak is more to the left than
1100 // the actual one.
1101 if (isPeak && smoothedValue > threshold) {
1102 if (peaks.empty() || i - peaks.back().first >= distance) {
1103 // Initialize variables to find the max amplitude within the next "distance" bins
1104 int maxBin = i;
1105 double maxAmplitude = smoothedValues[i];
1106
1107 // Look ahead within the specified distance to find the bin with the maximum amplitude
1108 for (std::vector<double>::size_type j = i + 1;
1109 j <= i + distance && j < smoothedValues.size(); ++j) {
1110 if (smoothedValues[j] > maxAmplitude) {
1111 maxAmplitude = smoothedValues[j];
1112 maxBin = j;
1113 }
1114 }
1115
1116 // Calculate the peak amplitude as the average of maxBin and its two neighbors
1117 double amplitude1 = GetRawData(maxBin - 1);
1118 double amplitude2 = GetRawData(maxBin);
1119 double amplitude3 = GetRawData(maxBin + 1);
1120 double peakAmplitude = (amplitude1 + amplitude2 + amplitude3) / 3.0;
1121
1122 // Store the peak position and amplitude
1123 peaks.emplace_back(maxBin, peakAmplitude);
1124 }
1125 }
1126 }
1127 }
1128
1129 return peaks;
1130}
1131
1132vector<pair<UShort_t, double>> TRestRawSignal::GetPeaksVeto(double threshold, UShort_t distance) const {
1133 vector<pair<UShort_t, double>> peaks;
1134
1135 const UShort_t smoothingWindow =
1136 4; // Region to compare for peak/no peak classification. 10 means 5 bins to each side
1137 const size_t numPoints = GetNumberOfPoints();
1138
1139 if (numPoints == 0) {
1140 return peaks;
1141 }
1142
1143 // Pre-calculate smoothed values for all bins using a rolling sum
1144 vector<double> smoothedValues(numPoints, 0.0);
1145 double currentSum = 0.0;
1146 UShort_t windowSize = smoothingWindow + 1;
1147
1148 // Initialize the sum for the first window
1149 for (UShort_t i = 0; i < static_cast<UShort_t>(std::min<size_t>(windowSize, numPoints)); ++i) {
1150 currentSum += GetRawData(i);
1151 }
1152 smoothedValues[0] = currentSum / windowSize;
1153
1154 for (UShort_t i = 1; i < numPoints; ++i) {
1155 if (i < smoothingWindow / 2 + 1) {
1156 // Adjust the window size at the beginning
1157 currentSum = 0.0;
1158 UShort_t currentWindowSize =
1159 static_cast<UShort_t>(std::min<size_t>(windowSize, i + smoothingWindow / 2 + 1));
1160 for (UShort_t j = 0; j < currentWindowSize; ++j) {
1161 currentSum += GetRawData(j);
1162 }
1163 smoothedValues[i] = currentSum / currentWindowSize;
1164 } else if (i > numPoints - smoothingWindow / 2 - 1) {
1165 // Adjust the window size at the end
1166 currentSum = 0.0;
1167 UShort_t currentWindowSize =
1168 static_cast<UShort_t>(std::min<size_t>(windowSize, numPoints - i + smoothingWindow / 2));
1169 for (UShort_t j = i - smoothingWindow / 2; j < numPoints; ++j) {
1170 currentSum += GetRawData(j);
1171 }
1172 smoothedValues[i] = currentSum / currentWindowSize;
1173 } else {
1174 // Use the rolling sum for the middle bins
1175 currentSum -= GetRawData(i - smoothingWindow / 2 - 1);
1176 currentSum += GetRawData(i + smoothingWindow / 2);
1177 smoothedValues[i] = currentSum / windowSize;
1178 }
1179 }
1180
1181 // Compare pre-calculated smoothed values to identify peaks
1182 for (size_t i = 0; i < numPoints; ++i) {
1183 const double smoothedValue = smoothedValues[i];
1184
1185 if (i >= smoothingWindow / 2 && i < numPoints - smoothingWindow / 2) {
1186 bool isPeak = true;
1187 int numGreaterEqual = 0; // Counter for smoothed values greater or equal to the studied bin
1188
1189 for (size_t j = i - smoothingWindow / 2; j <= i + smoothingWindow / 2; ++j) {
1190 if (j != i && smoothedValue <= smoothedValues[j]) {
1191 numGreaterEqual++;
1192 if (numGreaterEqual >
1193 0) { // If more than one smoothed value is greater or equal, it's not a peak
1194 isPeak = false;
1195 break;
1196 }
1197 }
1198 }
1199
1200 // If it's a peak and it´s above the threshold and further than distance to the previous peak, add
1201 // to peaks
1202 if (isPeak && smoothedValue > threshold) {
1203 if (peaks.empty() || i - peaks.back().first >= distance) {
1204 auto peakPosition = double(i);
1205 auto formattedPeakPosition = static_cast<UShort_t>(peakPosition);
1206 double peakAmplitude = GetRawData(formattedPeakPosition);
1207
1208 peaks.emplace_back(formattedPeakPosition, peakAmplitude);
1209 }
1210 }
1211 }
1212 }
1213
1214 return peaks;
1215}
It defines a Short_t array with a physical parameter that evolves in time using a fixed time bin.
Int_t GetMinPeakBin()
It returns the bin at which the minimum peak amplitude happens.
void WriteSignalToTextFile(const TString &filename)
This method dumps to a text file the data inside fSignalData.
Double_t GetThresholdIntegral()
It returns the integral of points identified over threshold. InitializePointsOverThreshold should hav...
~TRestRawSignal()
Default destructor.
TGraph * fGraph
A TGraph pointer used to store the TRestRawSignal drawing.
Double_t fBaseLine
This baseline value will be subtracted from GetData for any raw signal observable calculation.
void GetSignalSmoothed(TRestRawSignal *smoothedSignal, Int_t averagingPoints)
It smoothes the existing signal and places it at the signal pointer given by argument.
Double_t GetRiseSlope()
It returns the rate of change or slope from the points that have been identified over threshlold on t...
std::vector< Short_t > fSignalData
Vector with the data of the signal.
void Print() const
It prints the signal data on screen.
Double_t fThresholdIntegral
It stores the integral value obtained from the points identified over threshold.
Int_t fSignalID
An integer value used to attribute a unique identification number to the signal.
Double_t GetMaxPeakValue()
It returns the amplitude of the signal maximum, baseline will be corrected if CalculateBaseLine was c...
void CalculateBaseLineSigmaExcludeOutliers(Int_t startBin, Int_t endBin)
This method is called by CalculateBaseLine with the "OUTLIERS"-option to determine the value of the b...
std::vector< Float_t > GetSignalSmoothed_ExcludeOutliers(Int_t averagingPoints)
It smooths the existing signal and returns it in a vector of Float_t values. This method excludes poi...
Double_t GetMinPeakValue()
It returns the amplitude of the signal minimum, baseline will be corrected if CalculateBaseLine was c...
void CalculateBaseLine(Int_t startBin, Int_t endBin, const std::string &option="")
This method calculates the average and fluctuation of the baseline in the specified range and writes ...
void CalculateThresholdIntegral()
This method will be called each time InitializePointsOverThreshold is called to re-define the value o...
void Initialize()
Initialization of TRestRawSignal members.
void CalculateBaseLineMean(Int_t startBin, Int_t endBin)
This method is called by CalculateBaseLine and is used to determine the value of the baseline as aver...
void CalculateBaseLineSigmaSD(Int_t startBin, Int_t endBin)
This method is called by CalculateBaseLine to determine the value of the baseline fluctuation as its ...
TRestRawSignal()
Default constructor.
Short_t operator[](Int_t n)
It overloads the operator [] so that we can retrieve a particular point n in the form rawSignal[n].
void InitializePointsOverThreshold(const TVector2 &thrPar, Int_t nPointsOver, Int_t nPointsFlat=512)
It initializes the fPointsOverThreshold array with the indexes of data points that are found over thr...
Double_t GetIntegralInRange(Int_t startBin, Int_t endBin)
It returns the integral of points found in the specific range given by (startBin,endBin).
void Reset()
Initializes the existing signal data and sets it to zero while keeping the array size.
std::vector< std::pair< UShort_t, double > > GetPeaks(double threshold, UShort_t distance=5) const
void SignalAddition(const TRestRawSignal &signal)
This method adds the signal provided by argument to the existing signal.
Int_t fTailPoints
It defines the number of points to include after point over threshold definition. NOT implemented.
void AddOffset(Short_t offset)
This method adds an offset to the signal data.
Int_t GetMaxPeakWidth()
It returns the temporal width of the peak with maximum amplitude inside the signal.
Int_t GetRiseTime()
It returns the time required from the signal to reach the maximum. InitializePointsOverThreshold shou...
void CalculateBaseLineMedianExcludeOutliers(Int_t startBin, Int_t endBin)
This method is called by CalculateBaseLine with the "OUTLIERS"-option and is used to determine the va...
Double_t GetAverageInRange(Int_t startBin, Int_t endBin)
It returns the average of the points found in the range (startBin, endBin)
void IncreaseBinBy(Int_t bin, Double_t data)
It adds the content of data to fSignalData[bin].
void GetDifferentialSignal(TRestRawSignal *diffSignal, Int_t smearPoints)
It calculates the differential signal of the existing signal and it will place at the signal pointer ...
void CalculateBaseLineMedian(Int_t startBin, Int_t endBin)
This method is called by CalculateBaseLine with the "ROBUST"-option and is used to determine the valu...
std::vector< Int_t > fPointsOverThreshold
A std::vector containing the index of points that are identified over threshold.
Double_t GetRawData(Int_t n) const
It returns the original data value of point n without baseline correction.
Double_t GetData(Int_t n) const
It returns the data value of point n including baseline correction.
Double_t GetTripleMaxIntegral()
It returns the integral calculated using the maximum signal amplitude and its neightbour points.
void Scale(Double_t value)
This method scales the signal by a given value.
Double_t GetIntegral()
It returns the integral of points found in the region defined by fRange. If fRange was not defined th...
void AddPoint(Short_t)
Adds a new point to the end of the signal data array.
TGraph * GetGraph(Int_t color=1)
It builds a TGraph object that can be used for drawing.
UInt_t fSeed
Seed used for random number generation.
Double_t GetSlopeIntegral()
It returns the integral of points identified over threshold found in the first positive rise of the s...
Double_t fBaseLineSigma
The baseline fluctuation calculated as the standard deviation of the baseline.
void GetWhiteNoiseSignal(TRestRawSignal *noiseSignal, Double_t noiseLevel=1.)
It calculates an arbitrary Gaussian noise placing it at the signal pointer given by argument....
Bool_t IsADCSaturation(int Nflat=3)
It returns whether the signal has ADC saturation.
Int_t fHeadPoints
It defines the number of points to include before point over threshold definition....
Int_t GetMaxPeakBin()
It returns the bin at which the maximum peak amplitude happens.
Int_t GetSignalID() const
Returns the value of signal ID.
void GetBaseLineCorrected(TRestRawSignal *smoothedSignal, Int_t averagingPoints)
It applies the moving average filter (GetSignalSmoothed) to the signal, which is then subtracted from...
TVector2 fRange
Any signal calculation will be restricted to the following range definition.
void CalculateBaseLineSigmaIQR(Int_t startBin, Int_t endBin)
This method is called by CalculateBaseLine with the "ROBUST"-option to determine the value of the bas...
Int_t GetNumberOfPoints() const
Returns the actual number of points, or size of the signal.