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
990 Short_t offset = 0;
991 for (int i = 0; i < GetNumberOfPoints(); i++) {
992 const Short_t val = fSignalData[i];
993 if (val == 0) continue;
994 if (offset == 0) offset = val;
995 fSignalData[i] = val - offset;
996 }
997}
998
1003 cout << "---------------------" << endl;
1004 cout << "Signal id : " << this->GetSignalID() << endl;
1005 cout << "Baseline : " << fBaseLine << endl;
1006 cout << "Baseline sigma : " << fBaseLineSigma << endl;
1007 cout << "+++++++++++++++++++++" << endl;
1008 for (int i = 0; i < GetNumberOfPoints(); i++)
1009 cout << "Bin : " << i << " amplitude : " << GetRawData(i) << endl;
1010 cout << "---------------------" << endl;
1011}
1012
1016TGraph* TRestRawSignal::GetGraph(Int_t color) {
1017 delete fGraph;
1018 fGraph = new TGraph();
1019
1020 fGraph->SetLineWidth(2);
1021 fGraph->SetLineColor(color % 8 + 1);
1022 fGraph->SetMarkerStyle(7);
1023
1024 for (int i = 0; i < GetNumberOfPoints(); i++) {
1025 fGraph->SetPoint(i, i, GetData(i));
1026 }
1027
1028 fGraph->GetXaxis()->SetLimits(0, GetNumberOfPoints() - 1);
1029
1030 /*
1031 * To draw x axis in multiples of 2
1032 for (int i = 0; i < values.size(); i++) {
1033 if (i % 32 != 0 && i != values.size() - 1){
1034 continue;
1035 }
1036 fGraph->GetXaxis()->SetBinLabel(fGraph->GetXaxis()->FindBin(i), std::to_string(i).c_str());
1037 }
1038 */
1039
1040 return fGraph;
1041}
1042
1043std::vector<std::tuple<double, UShort_t, double>> TRestRawSignal::GetPeaks(double threshold,
1044 UShort_t distance,
1045 double signalBaseLine) const {
1046 std::vector<std::tuple<double, UShort_t, double>> peaks;
1047
1048 const UShort_t smoothingWindow =
1049 10; // Region to compare for peak/no peak classification. 10 means 5 bins to each side
1050 const size_t numPoints = GetNumberOfPoints();
1051
1052 if (numPoints == 0) {
1053 return peaks;
1054 }
1055
1056 // Pre-calculate smoothed values for all bins using a rolling sum
1057 vector<double> smoothedValues(numPoints, 0.0);
1058 double currentSum = 0.0;
1059 UShort_t windowSize = smoothingWindow + 1;
1060
1061 // Initialize the sum for the first window
1062 for (UShort_t i = 0; i < static_cast<UShort_t>(std::min<size_t>(windowSize, numPoints)); ++i) {
1063 currentSum += GetRawData(i);
1064 }
1065 smoothedValues[0] = currentSum / windowSize;
1066
1067 for (UShort_t i = 1; i < numPoints; ++i) {
1068 if (i < smoothingWindow / 2 + 1) {
1069 // Adjust the window size at the beginning
1070 currentSum = 0.0;
1071 UShort_t currentWindowSize =
1072 static_cast<UShort_t>(std::min<size_t>(windowSize, i + smoothingWindow / 2 + 1));
1073 for (UShort_t j = 0; j < currentWindowSize; ++j) {
1074 currentSum += GetRawData(j);
1075 }
1076 smoothedValues[i] = currentSum / currentWindowSize;
1077 } else if (i > numPoints - smoothingWindow / 2 - 1) {
1078 // Adjust the window size at the end
1079 currentSum = 0.0;
1080 UShort_t currentWindowSize =
1081 static_cast<UShort_t>(std::min<size_t>(windowSize, numPoints - i + smoothingWindow / 2));
1082 for (UShort_t j = i - smoothingWindow / 2; j < numPoints; ++j) {
1083 currentSum += GetRawData(j);
1084 }
1085 smoothedValues[i] = currentSum / currentWindowSize;
1086 } else {
1087 // Use the rolling sum for the middle bins
1088 currentSum -= GetRawData(i - smoothingWindow / 2 - 1);
1089 currentSum += GetRawData(i + smoothingWindow / 2);
1090 smoothedValues[i] = currentSum / windowSize;
1091 }
1092 }
1093
1094 // Compare pre-calculated smoothed values to identify peaks
1095 for (UShort_t i = 0; i < numPoints; ++i) {
1096 const double smoothedValue = smoothedValues[i];
1097
1098 if (i >= smoothingWindow / 2 && i < numPoints - smoothingWindow / 2) {
1099 bool isPeak = true;
1100 int numGreaterEqual = 0; // Counter for smoothed values greater or equal to the studied bin
1101
1102 for (UShort_t j = i - smoothingWindow / 2; j <= i + smoothingWindow / 2; ++j) {
1103 if (j != i && smoothedValue <= smoothedValues[j]) {
1104 numGreaterEqual++;
1105 if (numGreaterEqual >
1106 3) { // If more than one smoothed value is greater or equal, it's not a peak
1107 isPeak = false;
1108 break;
1109 }
1110 }
1111 }
1112
1113 // If it's a peak and it´s above the threshold and further than distance to the previous peak, add
1114 // to peaks the biggest amplitude bin within the next "distance" bins and as amplitude the
1115 // TripleMaxAverage. This is because for flat regions the detected peak is more to the left than
1116 // the actual one.
1117 if (isPeak && smoothedValue > threshold) {
1118 if (peaks.empty() || i - std::get<0>(peaks.back()) >= distance) {
1119 // Initialize variables to find the max amplitude within the next "distance" bins
1120 int maxBin = i;
1121 double maxAmplitude = smoothedValues[i];
1122
1123 // Look ahead within the specified distance to find the bin with the maximum amplitude
1124 for (std::vector<double>::size_type j = i + 1;
1125 j <= i + distance && j < smoothedValues.size(); ++j) {
1126 if (smoothedValues[j] > maxAmplitude) {
1127 maxAmplitude = smoothedValues[j];
1128 maxBin = j;
1129 }
1130 }
1131
1132 // Calculate the peak amplitude as the average of maxBin and its two neighbors
1133 double amplitude1 = GetRawData(maxBin - 1);
1134 double amplitude2 = GetRawData(maxBin);
1135 double amplitude3 = GetRawData(maxBin + 1);
1136 double peakAmplitude = (amplitude1 + amplitude2 + amplitude3) / 3.0;
1137 double peakAmplitudeBaseLineCorrected = peakAmplitude - signalBaseLine;
1138
1139 // Store the peak position and amplitude
1140 peaks.emplace_back(maxBin, peakAmplitude, peakAmplitudeBaseLineCorrected);
1141 }
1142 }
1143 }
1144 }
1145
1146 return peaks;
1147}
1148
1149std::vector<std::tuple<double, UShort_t, double>> TRestRawSignal::GetPeaksVeto(double threshold,
1150 UShort_t distance,
1151 double signalBaseLine) const {
1152 std::vector<std::tuple<double, UShort_t, double>> peaks;
1153
1154 const UShort_t smoothingWindow =
1155 4; // Region to compare for peak/no peak classification. 10 means 5 bins to each side
1156 const size_t numPoints = GetNumberOfPoints();
1157
1158 if (numPoints == 0) {
1159 return peaks;
1160 }
1161
1162 // Pre-calculate smoothed values for all bins using a rolling sum
1163 vector<double> smoothedValues(numPoints, 0.0);
1164 double currentSum = 0.0;
1165 UShort_t windowSize = smoothingWindow + 1;
1166
1167 // Initialize the sum for the first window
1168 for (UShort_t i = 0; i < static_cast<UShort_t>(std::min<size_t>(windowSize, numPoints)); ++i) {
1169 currentSum += GetRawData(i);
1170 }
1171 smoothedValues[0] = currentSum / windowSize;
1172
1173 for (UShort_t i = 1; i < numPoints; ++i) {
1174 if (i < smoothingWindow / 2 + 1) {
1175 // Adjust the window size at the beginning
1176 currentSum = 0.0;
1177 UShort_t currentWindowSize =
1178 static_cast<UShort_t>(std::min<size_t>(windowSize, i + smoothingWindow / 2 + 1));
1179 for (UShort_t j = 0; j < currentWindowSize; ++j) {
1180 currentSum += GetRawData(j);
1181 }
1182 smoothedValues[i] = currentSum / currentWindowSize;
1183 } else if (i > numPoints - smoothingWindow / 2 - 1) {
1184 // Adjust the window size at the end
1185 currentSum = 0.0;
1186 UShort_t currentWindowSize =
1187 static_cast<UShort_t>(std::min<size_t>(windowSize, numPoints - i + smoothingWindow / 2));
1188 for (UShort_t j = i - smoothingWindow / 2; j < numPoints; ++j) {
1189 currentSum += GetRawData(j);
1190 }
1191 smoothedValues[i] = currentSum / currentWindowSize;
1192 } else {
1193 // Use the rolling sum for the middle bins
1194 currentSum -= GetRawData(i - smoothingWindow / 2 - 1);
1195 currentSum += GetRawData(i + smoothingWindow / 2);
1196 smoothedValues[i] = currentSum / windowSize;
1197 }
1198 }
1199
1200 // Compare pre-calculated smoothed values to identify peaks
1201 for (size_t i = 0; i < numPoints; ++i) {
1202 const double smoothedValue = smoothedValues[i];
1203
1204 if (i >= smoothingWindow / 2 && i < numPoints - smoothingWindow / 2) {
1205 bool isPeak = true;
1206 int numGreaterEqual = 0; // Counter for smoothed values greater or equal to the studied bin
1207
1208 for (size_t j = i - smoothingWindow / 2; j <= i + smoothingWindow / 2; ++j) {
1209 if (j != i && smoothedValue <= smoothedValues[j]) {
1210 numGreaterEqual++;
1211 if (numGreaterEqual >
1212 0) { // If more than one smoothed value is greater or equal, it's not a peak
1213 isPeak = false;
1214 break;
1215 }
1216 }
1217 }
1218
1219 // If it's a peak and it´s above the threshold and further than distance to the previous peak, add
1220 // to peaks
1221 if (isPeak && smoothedValue > threshold) {
1222 if (peaks.empty() || i - std::get<0>(peaks.back()) >= distance) {
1223 auto peakPosition = double(i);
1224 auto formattedPeakPosition = static_cast<UShort_t>(peakPosition);
1225 double peakAmplitude = GetRawData(formattedPeakPosition);
1226 double peakAmplitudeBaseLineCorrected = peakAmplitude - signalBaseLine;
1227
1228 peaks.emplace_back(formattedPeakPosition, peakAmplitude, peakAmplitudeBaseLineCorrected);
1229 }
1230 }
1231 }
1232 }
1233
1234 return peaks;
1235}
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.
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].
std::vector< std::tuple< double, UShort_t, double > > GetPeaks(double threshold, UShort_t distance=5, double signalBaseLine=0.0) const
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.
void ZeroSuppressionToRaw()
This method transforms zero suppression raw data into a raw data event using the firs.
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.