Skip to content

Commit 319cc97

Browse files
authored
Merge pull request #19 from rest-for-physics/lobis-detector-to-raw-calibration
Update `TRestDetectorSignalToRawSignalProcess` to support calibration points and shaping
2 parents 100d36e + 66a837b commit 319cc97

File tree

2 files changed

+195
-88
lines changed

2 files changed

+195
-88
lines changed

inc/TRestDetectorSignalToRawSignalProcess.h

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,9 @@
2424
#define RestCore_TRestDetectorSignalToRawSignalProcess
2525

2626
#include <TRestDetectorSignalEvent.h>
27+
#include <TRestEventProcess.h>
2728
#include <TRestRawSignalEvent.h>
2829

29-
#include "TRestEventProcess.h"
30-
3130
//! A process to convert a TRestDetectorSignalEvent into a TRestRawSignalEvent
3231
class TRestDetectorSignalToRawSignalProcess : public TRestEventProcess {
3332
private:
@@ -52,14 +51,34 @@ class TRestDetectorSignalToRawSignalProcess : public TRestEventProcess {
5251
TString fTriggerMode = "firstDeposit";
5352

5453
/// The number of time bins the time start is delayed in the resulting output signal.
55-
Int_t fTriggerDelay = 100; // ns
54+
Int_t fTriggerDelay = 100;
55+
56+
/// The starting time for the "fixed" trigger mode (can be offset by the trigger delay)
57+
Int_t fTriggerFixedStartTime = 0;
5658

57-
/// A factor the data values will be multiplied by at the output signal.
58-
Double_t fGain = 100.0;
59+
/// fCalibrationGain and fCalibrationOffset define the linear calibration.
60+
/// output = input * fCalibrationGain + calibrationOffset
61+
Double_t fCalibrationGain = 100.0;
62+
Double_t fCalibrationOffset = 0.0; // adc units
5963

6064
/// This parameter is used by integralWindow trigger mode to define the acquisition window.
6165
Double_t fIntegralThreshold = 1229.0;
6266

67+
/// two distinct energy values used for calibration
68+
TVector2 fCalibrationEnergy = TVector2(0.0, 0.0);
69+
/// position in the range corresponding to the energy in 'fCalibrationEnergy'. Values between 0 and 1
70+
TVector2 fCalibrationRange = TVector2(0.0, 0.0);
71+
/// Usage: fCalibrationEnergy = (0, 100 MeV) and fCalibrationRange = (0.1, 0.9)
72+
/// will perform a linear calibration with 0 equal to 0.1 of the range (0.1 * (max - min) + min) and 100
73+
/// MeV equal to 0.9 of the range. The range is the one corresponding to a Short_t for rawsignal.
74+
75+
/// If defined ( > 0 ) we will compute the sin shaping of the signal, this is done in this process to
76+
/// avoid artifacts in the signal (e.g. signals not getting cut when they should)
77+
Double_t fShapingTime = 0.0; // us
78+
79+
Double_t fTimeStart; //!
80+
Double_t fTimeEnd; //!
81+
6382
public:
6483
inline Double_t GetSampling() const { return fSampling; }
6584
inline void SetSampling(Double_t sampling) { fSampling = sampling; }
@@ -73,15 +92,36 @@ class TRestDetectorSignalToRawSignalProcess : public TRestEventProcess {
7392
inline Int_t GetTriggerDelay() const { return fTriggerDelay; }
7493
inline void SetTriggerDelay(Int_t triggerDelay) { fTriggerDelay = triggerDelay; }
7594

76-
inline Double_t GetGain() const { return fGain; }
77-
inline void SetGain(Double_t gain) { fGain = gain; }
95+
inline Double_t GetGain() const { return fCalibrationGain; }
96+
inline void SetGain(Double_t gain) { fCalibrationGain = gain; }
97+
98+
inline Double_t GetCalibrationOffset() const { return fCalibrationOffset; }
99+
inline void SetCalibrationOffset(Double_t offset) { fCalibrationOffset = offset; }
78100

79101
inline Double_t GetIntegralThreshold() const { return fIntegralThreshold; }
80102
inline void SetIntegralThreshold(Double_t integralThreshold) { fIntegralThreshold = integralThreshold; }
81103

104+
inline Double_t GetShapingTime() const { return fShapingTime; }
105+
inline void SetShapingTime(Double_t shapingTime) { fShapingTime = shapingTime; }
106+
107+
inline bool IsShapingEnabled() const { return fShapingTime > 0; }
108+
109+
inline bool IsLinearCalibration() const {
110+
// Will return true if two points have been given for calibration
111+
return (fCalibrationEnergy.Mod() != 0 && fCalibrationRange.Mod() != 0);
112+
}
113+
114+
inline TVector2 GetCalibrationEnergy() const { return fCalibrationEnergy; }
115+
inline void SetCalibrationEnergy(TVector2 calibrationEnergy) { fCalibrationEnergy = calibrationEnergy; }
116+
117+
inline TVector2 GetCalibrationRange() const { return fCalibrationRange; }
118+
inline void SetCalibrationRange(TVector2 calibrationRange) { fCalibrationRange = calibrationRange; }
119+
82120
any GetInputEvent() const override { return fInputSignalEvent; }
83121
any GetOutputEvent() const override { return fOutputRawSignalEvent; }
84122

123+
void InitProcess() override;
124+
85125
TRestEvent* ProcessEvent(TRestEvent* inputEvent) override;
86126

87127
void LoadConfig(const std::string& configFilename, const std::string& name = "");
@@ -94,7 +134,19 @@ class TRestDetectorSignalToRawSignalProcess : public TRestEventProcess {
94134
RESTMetadata << "Points per channel : " << fNPoints << RESTendl;
95135
RESTMetadata << "Trigger mode : " << fTriggerMode << RESTendl;
96136
RESTMetadata << "Trigger delay : " << fTriggerDelay << " time units" << RESTendl;
97-
RESTMetadata << "ADC gain : " << fGain << RESTendl;
137+
138+
if (IsLinearCalibration()) {
139+
RESTMetadata << "Calibration energy : (" << fCalibrationEnergy.X() << ", "
140+
<< fCalibrationEnergy.Y() << ") keV" << RESTendl;
141+
RESTMetadata << "Calibration range : (" << fCalibrationRange.X() << ", " << fCalibrationRange.Y()
142+
<< ")" << RESTendl;
143+
}
144+
RESTMetadata << "ADC Gain : " << fCalibrationGain << RESTendl;
145+
RESTMetadata << "ADC Offset : " << fCalibrationOffset << RESTendl;
146+
147+
if (IsShapingEnabled()) {
148+
RESTMetadata << "Shaping time : " << fShapingTime << " us" << RESTendl;
149+
}
98150

99151
EndPrintProcess();
100152
}
@@ -112,6 +164,6 @@ class TRestDetectorSignalToRawSignalProcess : public TRestEventProcess {
112164
// Destructor
113165
~TRestDetectorSignalToRawSignalProcess();
114166

115-
ClassDefOverride(TRestDetectorSignalToRawSignalProcess, 2);
167+
ClassDefOverride(TRestDetectorSignalToRawSignalProcess, 3);
116168
};
117169
#endif

src/TRestDetectorSignalToRawSignalProcess.cxx

Lines changed: 134 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,11 @@
8282
/// \htmlonly <style>div.image img[src="trigger.png"]{width:500px;}</style> \endhtmlonly
8383
///
8484
/// The following figure shows the integralThreshold trigger definition for a NLDBD
85-
/// event. tStart and tEnd define the acquision window, centered on the time
86-
/// when the signal integral is above the threshold defined. tStart has been
85+
/// event. fTimeStart and fTimeEnd define the acquisition window, centered on the time
86+
/// when the signal integral is above the threshold defined. fTimeStart has been
8787
/// shifted by a triggerDelay = 60 samples * 200ns
8888
///
89-
/// ![An ilustration of the trigger definition](trigger.png)
89+
/// ![An illustration of the trigger definition](trigger.png)
9090
///
9191
/// * **triggerDelay**: The time start obtained by the trigger mode
9292
/// definition can be shifted using this parameter. The shift is
@@ -113,7 +113,9 @@
113113
///
114114
/// <hr>
115115
///
116+
116117
#include "TRestDetectorSignalToRawSignalProcess.h"
118+
117119
using namespace std;
118120

119121
ClassImp(TRestDetectorSignalToRawSignalProcess);
@@ -181,115 +183,130 @@ void TRestDetectorSignalToRawSignalProcess::Initialize() {
181183
TRestEvent* TRestDetectorSignalToRawSignalProcess::ProcessEvent(TRestEvent* inputEvent) {
182184
fInputSignalEvent = (TRestDetectorSignalEvent*)inputEvent;
183185

184-
if (fInputSignalEvent->GetNumberOfSignals() <= 0) return nullptr;
186+
if (fInputSignalEvent->GetNumberOfSignals() <= 0) {
187+
return nullptr;
188+
}
185189

186-
if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Debug) fOutputRawSignalEvent->PrintEvent();
190+
if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Debug) {
191+
fOutputRawSignalEvent->PrintEvent();
192+
}
187193

188194
fOutputRawSignalEvent->SetID(fInputSignalEvent->GetID());
189195
fOutputRawSignalEvent->SetSubID(fInputSignalEvent->GetSubID());
190196
fOutputRawSignalEvent->SetTimeStamp(fInputSignalEvent->GetTimeStamp());
191197
fOutputRawSignalEvent->SetSubEventTag(fInputSignalEvent->GetSubEventTag());
192198

193-
// The time event window is defined (tStart, tEnd)
194-
Double_t tStart = std::numeric_limits<double>::quiet_NaN();
195-
Double_t tEnd = 10000;
196199
if (fTriggerMode == "firstDeposit") {
197-
tStart = fInputSignalEvent->GetMinTime() - fTriggerDelay * fSampling;
198-
tEnd = fInputSignalEvent->GetMinTime() + (fNPoints - fTriggerDelay) * fSampling;
200+
fTimeStart = fInputSignalEvent->GetMinTime() - fTriggerDelay * fSampling;
201+
fTimeEnd = fInputSignalEvent->GetMinTime() + (fNPoints - fTriggerDelay) * fSampling;
199202
} else if (fTriggerMode == "integralThreshold") {
200-
Double_t maxT = fInputSignalEvent->GetMaxTime();
201-
Double_t minT = fInputSignalEvent->GetMinTime();
202-
203-
for (Double_t t = minT - fNPoints * fSampling; t <= maxT + fNPoints * fSampling; t = t + 0.5) {
204-
Double_t en = fInputSignalEvent->GetIntegralWithTime(t, t + (fSampling * fNPoints) / 2.);
205-
206-
if (en > fIntegralThreshold) {
207-
tStart = t - fTriggerDelay * fSampling;
208-
tEnd = t + (fNPoints - fTriggerDelay) * fSampling;
203+
bool thresholdReached = false;
204+
for (Double_t t = fInputSignalEvent->GetMinTime() - fNPoints * fSampling;
205+
t <= fInputSignalEvent->GetMaxTime() + fNPoints * fSampling; t = t + 0.5) {
206+
Double_t energy = fInputSignalEvent->GetIntegralWithTime(t, t + (fSampling * fNPoints) / 2.);
207+
208+
if (energy > fIntegralThreshold) {
209+
fTimeStart = t - fTriggerDelay * fSampling;
210+
fTimeEnd = t + (fNPoints - fTriggerDelay) * fSampling;
211+
thresholdReached = true;
209212
}
210213
}
211-
} else {
212-
if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Warning) {
213-
cout << "REST WARNING. TRestDetectorSignalToRawSignalProcess. fTriggerMode not "
214-
"recognized!"
215-
<< endl;
216-
cout << "Setting fTriggerMode to firstDeposit" << endl;
217-
}
218-
219-
fTriggerMode = "firstDeposit";
220-
221-
tStart = fInputSignalEvent->GetMinTime() - fTriggerDelay * fSampling;
222-
tEnd = fInputSignalEvent->GetMinTime() + (fNPoints - fTriggerDelay) * fSampling;
223-
}
224-
225-
if (std::isnan(tStart)) {
226-
if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Warning) {
227-
cout << endl;
228-
cout << "REST WARNING. TRestDetectorSignalToRawSignalProcess. tStart was not "
229-
"defined."
230-
<< endl;
231-
cout << "Setting tStart = 0. tEnd = fNPoints * fSampling" << endl;
232-
cout << endl;
214+
if (!thresholdReached) {
215+
RESTWarning << "Integral threshold for trigger not reached" << RESTendl;
216+
fTimeStart = 0;
217+
fTimeEnd = fNPoints * fSampling;
233218
}
234-
235-
tEnd = fNPoints * fSampling;
236219
}
237220

238-
if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Debug) {
239-
cout << "tStart : " << tStart << " us " << endl;
240-
cout << "tEnd : " << tEnd << " us " << endl;
241-
}
221+
RESTDebug << "fTimeStart : " << fTimeStart << " us " << RESTendl;
222+
RESTDebug << "fTimeEnd : " << fTimeEnd << " us " << RESTendl;
242223

243224
for (int n = 0; n < fInputSignalEvent->GetNumberOfSignals(); n++) {
244-
vector<Double_t> sData(fNPoints);
245-
for (int i = 0; i < fNPoints; i++) sData[i] = 0;
246-
247-
TRestDetectorSignal* sgnl = fInputSignalEvent->GetSignal(n);
248-
Int_t signalID = sgnl->GetSignalID();
225+
vector<Double_t> data(fNPoints, fCalibrationOffset);
226+
TRestDetectorSignal* signal = fInputSignalEvent->GetSignal(n);
227+
Int_t signalID = signal->GetSignalID();
249228

250-
for (int m = 0; m < sgnl->GetNumberOfPoints(); m++) {
251-
Double_t t = sgnl->GetTime(m);
252-
Double_t d = sgnl->GetData(m);
229+
for (int m = 0; m < signal->GetNumberOfPoints(); m++) {
230+
Double_t t = signal->GetTime(m);
231+
Double_t d = signal->GetData(m);
253232

254-
if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Debug && n < 3 && m < 5)
233+
if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Debug && n < 3 && m < 5) {
255234
cout << "Signal : " << n << " Sample : " << m << " T : " << t << " Data : " << d << endl;
235+
}
256236

257-
if (t > tStart && t < tEnd) {
237+
if (t > fTimeStart && t < fTimeEnd) {
258238
// convert physical time (in us) to timeBin
259-
Int_t timeBin = (Int_t)round((t - tStart) / fSampling);
239+
Int_t timeBin = (Int_t)round((t - fTimeStart) / fSampling);
260240

261-
if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Warning)
241+
if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Warning) {
262242
if (timeBin < 0 || timeBin > fNPoints) {
263243
cout << "Time bin out of range!!! bin value : " << timeBin << endl;
264244
timeBin = 0;
265245
}
246+
}
266247

267-
RESTDebug << "Adding data : " << sgnl->GetData(m) << " to Time Bin : " << timeBin << RESTendl;
268-
sData[timeBin] += fGain * sgnl->GetData(m);
248+
RESTDebug << "Adding data : " << signal->GetData(m) << " to Time Bin : " << timeBin
249+
<< RESTendl;
250+
data[timeBin] += fCalibrationGain * signal->GetData(m);
269251
}
270252
}
271253

272-
if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Warning)
273-
for (int x = 0; x < fNPoints; x++)
274-
if (sData[x] < -32768. || sData[x] > 32768.)
275-
cout << "REST Warning : data is outside short range : " << sData[x] << endl;
254+
if (IsShapingEnabled()) {
255+
const auto shapingFunction = [](Double_t t) -> Double_t {
256+
if (t <= 0) {
257+
return 0;
258+
}
259+
// function is normalized such that its absolute maximum is 1.0
260+
// max is at x = 1.1664004483744728
261+
return TMath::Exp(-3.0 * t) * TMath::Power(t, 3.0) * TMath::Sin(t) * 22.68112123672292;
262+
};
263+
264+
vector<Double_t> dataAfterShaping(fNPoints, fCalibrationOffset);
265+
for (int i = 0; i < fNPoints; i++) {
266+
const Double_t value = data[i] - fCalibrationOffset;
267+
if (value <= 0) {
268+
// Only positive values are possible, 0 means no signal in this bin
269+
continue;
270+
}
271+
for (int j = 0; j < fNPoints; j++) {
272+
dataAfterShaping[j] += value * shapingFunction(((j - i) * fSampling) / fShapingTime);
273+
}
274+
}
275+
data = dataAfterShaping;
276+
}
276277

277-
TRestRawSignal rSgnl;
278-
rSgnl.SetSignalID(signalID);
278+
TRestRawSignal rawSignal;
279+
rawSignal.SetSignalID(signalID);
279280
for (int x = 0; x < fNPoints; x++) {
280-
if (sData[x] < -32768. || sData[x] > 32768.) fOutputRawSignalEvent->SetOK(false);
281+
double value = round(data[x]);
282+
if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Warning) {
283+
if (value < numeric_limits<Short_t>::min() || value > numeric_limits<Short_t>::max()) {
284+
RESTWarning << "value (" << value << ") is outside short range ("
285+
<< numeric_limits<Short_t>::min() << ", " << numeric_limits<Short_t>::max()
286+
<< ")" << RESTendl;
287+
}
288+
}
281289

282-
Short_t value = (Short_t)round(sData[x]);
283-
rSgnl.AddPoint(value);
290+
if (value < numeric_limits<Short_t>::min()) {
291+
value = numeric_limits<Short_t>::min();
292+
fOutputRawSignalEvent->SetOK(false);
293+
} else if (value > numeric_limits<Short_t>::max()) {
294+
value = numeric_limits<Short_t>::max();
295+
fOutputRawSignalEvent->SetOK(false);
296+
}
297+
rawSignal.AddPoint((Short_t)value);
284298
}
285299

286-
if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Debug) rSgnl.Print();
300+
if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Debug) {
301+
rawSignal.Print();
302+
}
287303
RESTDebug << "Adding signal to raw signal event" << RESTendl;
288-
fOutputRawSignalEvent->AddSignal(rSgnl);
304+
305+
fOutputRawSignalEvent->AddSignal(rawSignal);
289306
}
290307

291308
RESTDebug << "TRestDetectorSignalToRawSignalProcess. Returning event with N signals "
292-
<< fOutputRawSignalEvent->GetNumberOfSignals() << RESTendl;
309+
<< fOutputRawSignalEvent->GetNumberOfSignals() << RESTendl;
293310

294311
return fOutputRawSignalEvent;
295312
}
@@ -299,10 +316,48 @@ TRestEvent* TRestDetectorSignalToRawSignalProcess::ProcessEvent(TRestEvent* inpu
299316
/// TRestDetectorSignalToRawSignalProcess metadata section
300317
///
301318
void TRestDetectorSignalToRawSignalProcess::InitFromConfigFile() {
302-
fSampling = GetDblParameterWithUnits("sampling");
303-
fNPoints = StringToInteger(GetParameter("Npoints", "512"));
304-
fTriggerMode = GetParameter("triggerMode", "firstDeposit");
305-
fTriggerDelay = StringToInteger(GetParameter("triggerDelay", 100));
306-
fGain = StringToDouble(GetParameter("gain", "100"));
307-
fIntegralThreshold = StringToDouble(GetParameter("integralThreshold", "1229"));
319+
auto nPoints = GetParameter("nPoints");
320+
if (nPoints == PARAMETER_NOT_FOUND_STR) {
321+
nPoints = GetParameter("Npoints", fNPoints);
322+
}
323+
fNPoints = StringToInteger(nPoints);
324+
325+
fTriggerMode = GetParameter("triggerMode", fTriggerMode);
326+
const set<string> validTriggerModes = {"firstDeposit", "integralThreshold", "fixed"};
327+
if (validTriggerModes.count(fTriggerMode.Data()) == 0) {
328+
RESTError << "Trigger mode set to: '" << fTriggerMode
329+
<< "' which is not a valid trigger mode. Please use one of the following trigger modes: ";
330+
for (const auto& triggerMode : validTriggerModes) {
331+
RESTError << triggerMode << " ";
332+
}
333+
RESTError << RESTendl;
334+
exit(1);
335+
}
336+
337+
fSampling = GetDblParameterWithUnits("sampling", fSampling);
338+
fTriggerDelay = StringToInteger(GetParameter("triggerDelay", fTriggerDelay));
339+
fIntegralThreshold = StringToDouble(GetParameter("integralThreshold", fIntegralThreshold));
340+
fTriggerFixedStartTime = GetDblParameterWithUnits("triggerFixedStartTime", fTriggerFixedStartTime);
341+
342+
fCalibrationGain = StringToDouble(GetParameter("gain", fCalibrationGain));
343+
fCalibrationOffset = StringToDouble(GetParameter("offset", fCalibrationOffset));
344+
fCalibrationEnergy = Get2DVectorParameterWithUnits("calibrationEnergy", fCalibrationEnergy);
345+
fCalibrationRange = Get2DVectorParameterWithUnits("calibrationRange", fCalibrationRange);
346+
347+
if (IsLinearCalibration()) {
348+
const auto range = numeric_limits<Short_t>::max() - numeric_limits<Short_t>::min();
349+
fCalibrationGain = range * (fCalibrationRange.Y() - fCalibrationRange.X()) /
350+
(fCalibrationEnergy.Y() - fCalibrationEnergy.X());
351+
fCalibrationOffset = range * (fCalibrationRange.X() - fCalibrationGain * fCalibrationEnergy.X()) +
352+
numeric_limits<Short_t>::min();
353+
}
354+
355+
fShapingTime = GetDblParameterWithUnits("shapingTime", fShapingTime);
356+
}
357+
358+
void TRestDetectorSignalToRawSignalProcess::InitProcess() {
359+
if (fTriggerMode == "fixed") {
360+
fTimeStart = fTriggerFixedStartTime - fTriggerDelay * fSampling;
361+
fTimeEnd = fTimeStart + fNPoints * fSampling;
362+
}
308363
}

0 commit comments

Comments
 (0)