REST-for-Physics  v2.3
Rare Event Searches ToolKit for Physics
Loading...
Searching...
No Matches
TRestDetectorSignalToRawSignalProcess.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
135
136#include "TRestDetectorSignalToRawSignalProcess.h"
137
138#include <TObjString.h>
139#include <TRestRawReadoutMetadata.h>
140
141#include <limits>
142
143using namespace std;
144
146
151
168
175
188void TRestDetectorSignalToRawSignalProcess::LoadConfig(const string& configFilename, const string& name) {
189 LoadConfigFromFile(configFilename, name);
190}
191
197 SetSectionName(this->ClassName());
198 SetLibraryVersion(LIBRARY_VERSION);
199
200 fInputSignalEvent = nullptr;
202}
203
209
210 if (fInputSignalEvent->GetNumberOfSignals() <= 0) {
211 return nullptr;
212 }
213
215 fOutputRawSignalEvent->PrintEvent();
216 }
217
219 fOutputRawSignalEvent->SetSubID(fInputSignalEvent->GetSubID());
220 fOutputRawSignalEvent->SetTimeStamp(fInputSignalEvent->GetTimeStamp());
221 fOutputRawSignalEvent->SetSubEventTag(fInputSignalEvent->GetSubEventTag());
222
223 double startTimeNoOffset = 0;
224
225 if (fTriggerMode == "firstDeposit") {
226 startTimeNoOffset = fInputSignalEvent->GetMinTime();
227 } else if (fTriggerMode == "integralThreshold") {
228 bool thresholdReached = false;
229 for (Double_t t = fInputSignalEvent->GetMinTime() - fNPoints * fSampling;
230 t <= fInputSignalEvent->GetMaxTime() + fNPoints * fSampling; t = t + 0.5) {
231 Double_t energy = fInputSignalEvent->GetIntegralWithTime(t, t + (fSampling * fNPoints) / 2.);
232
233 if (energy > fIntegralThreshold) {
234 startTimeNoOffset = t;
235 thresholdReached = true;
236 }
237 }
238 if (!thresholdReached) {
239 RESTWarning << "Integral threshold for trigger not reached" << RESTendl;
240 startTimeNoOffset = 0;
241 }
242 } else if (fTriggerMode == "observable") {
243 const auto obs = GetObservableValue<double>(fTriggerModeObservableName);
244 startTimeNoOffset = obs;
245
246 } else if (fTriggerMode == "firstDepositTPC" || fTriggerMode == "integralThresholdTPC") {
247 fReadout = GetMetadata<TRestDetectorReadout>();
248
249 if (fReadout == nullptr) {
250 RESTError << "TRestDetectorSignalToRawSignalProcess::ProcessEvent: "
251 << "TRestDetectorReadout metadata not found" << RESTendl;
252 exit(1);
253 }
254
255 set<const TRestDetectorSignal*> tpcSignals;
256
257 for (int n = 0; n < fInputSignalEvent->GetNumberOfSignals(); n++) {
258 TRestDetectorSignal* signal = fInputSignalEvent->GetSignal(n);
259 if (signal->GetSignalType() == "tpc") {
260 tpcSignals.insert(signal);
261 }
262 /*
263 const auto allDaqIds = fReadout->GetAllDaqIds();
264 for (const auto& daqId : allDaqIds) {
265 const auto& channel = fReadout->GetReadoutChannelWithDaqID(daqId);
266 // TODO: sometimes channel type does not match signal type, why?
267 }
268 */
269 }
270
271 if (tpcSignals.empty()) {
272 return nullptr;
273 }
274
275 if (fTriggerMode == "firstDepositTPC") {
276 double startTime = std::numeric_limits<float>::max();
277 for (const auto& signal : tpcSignals) {
278 const auto minTime = signal->GetMinTime();
279 if (minTime < startTime) {
280 startTime = minTime;
281 }
282 }
283
284 if (startTime >= std::numeric_limits<float>::max()) {
285 return nullptr;
286 }
287 startTimeNoOffset = startTime;
288
289 } else if (fTriggerMode == "integralThresholdTPC") {
290 RESTDebug << "TRestDetectorSignalToRawSignalProcess::ProcessEvent: "
291 << "Trigger mode integralThresholdTPC" << RESTendl;
292
293 const double signalIntegralThreshold = 0.5; // keV
294 double totalEnergy = 0;
295 for (const auto& signal : tpcSignals) {
296 totalEnergy += signal->GetIntegral();
297 }
298 if (totalEnergy < signalIntegralThreshold) {
299 return nullptr;
300 }
301
302 double maxTime = 0;
303 double minTime = std::numeric_limits<float>::max();
304 for (const auto& signal : tpcSignals) {
305 const auto maxSignalTime = signal->GetMaxTime();
306 if (maxSignalTime > maxTime) {
307 maxTime = maxSignalTime;
308 }
309 const auto minSignalTime = signal->GetMinTime();
310 if (minSignalTime < minTime) {
311 minTime = minSignalTime;
312 }
313 }
314
315 // lots of problem with signal methods (GetMinTime, GetMaxTime, etc.)
316 if (minTime > maxTime) {
317 // TODO: this should raise an exception
318 RESTWarning << "TRestDetectorSignalToRawSignalProcess::ProcessEvent: "
319 << "minTime > maxTime" << RESTendl;
320 // exit(1);
321 return nullptr;
322 }
323 if (minTime < 0) {
324 RESTWarning
325 << "TRestDetectorSignalToRawSignalProcess::ProcessEvent: " << inputEvent->GetID()
326 << " signal minTime < 0. Setting min time to 0, but this should probably never happen"
327 << RESTendl;
328 minTime = 0;
329 // TODO: this should raise an exception
330 // exit(1);
331 }
332
333 double t = minTime;
334 bool thresholdReached = false;
335 double maxEnergy = 0;
336 while (t < maxTime) {
337 // iterate over number of signals
338 double energy = 0;
339 for (const auto& signal : tpcSignals) {
340 energy += signal->GetIntegralWithTime(t - fSampling * fNPoints, t);
341 }
342 if (energy > maxEnergy) {
343 maxEnergy = energy;
344 }
345 if (maxEnergy > signalIntegralThreshold) {
346 thresholdReached = true;
347 break;
348 }
349 t += fSampling;
350 }
351
352 if (!thresholdReached) {
353 /*
354 cout << "TRestDetectorSignalToRawSignalProcess::ProcessEvent: "
355 << "Integral threshold for trigger not reached. Maximum energy reached: " << maxEnergy
356 << endl;
357 */
358 return nullptr;
359 }
360
361 double startTime = t;
362 startTimeNoOffset = startTime;
363
364 SetObservableValue("triggerTimeTPC", startTime);
365 }
366
367 } else if (fTriggerMode == "fixed") {
368 startTimeNoOffset = fTriggerFixedStartTime;
369 } else {
370 cerr << "TRestDetectorSignalToRawSignalProcess::ProcessEvent: "
371 << "Trigger mode not recognized" << RESTendl;
372 exit(1);
373 }
374
375 for (int n = 0; n < fInputSignalEvent->GetNumberOfSignals(); n++) {
376 TRestDetectorSignal* signal = fInputSignalEvent->GetSignal(n);
377 Int_t signalID = signal->GetSignalID();
378 string type = signal->GetSignalType();
379 // Check type is in the map
380 if (fParametersMap.find(type) == fParametersMap.end()) {
381 RESTWarning << "TRestDetectorSignalToRawSignalProcess::ProcessEvent: "
382 << "type " << type << " not found in parameters map" << RESTendl;
383 type = "";
384 }
385
386 double sampling = fParametersMap.at(type).sampling;
387 double shapingTime = fParametersMap.at(type).shapingTime;
388 double calibrationGain = fParametersMap.at(type).calibrationGain;
389 double calibrationOffset = fParametersMap.at(type).calibrationOffset;
390
391 double timeStart = startTimeNoOffset - fTriggerDelay * sampling;
392 double timeEnd = timeStart + fNPoints * sampling;
393 RESTDebug << "fTimeStart: " << timeStart << " us " << RESTendl;
394 RESTDebug << "fTimeEnd: " << timeEnd << " us " << RESTendl;
395
396 if (timeStart + fTriggerDelay * sampling < 0) {
397 // This means something is wrong (negative times somewhere). This should never happen
398 cerr << "TRestDetectorSignalToRawSignalProcess::ProcessEvent: "
399 << "fTimeStart < - fTriggerDelay * fSampling" << endl;
400 exit(1);
401 }
402
403 // TODO: time offset may not be working correctly
404 // TODO: event drawing not working correctly (some signals are clipped)
405
406 if (timeStart + fTriggerDelay * fSampling < 0) {
407 // This means something is wrong (negative times somewhere). This should never happen
408 RESTError << "TRestDetectorSignalToRawSignalProcess::ProcessEvent: "
409 << "fTimeStart < - fTriggerDelay * fSampling" << RESTendl;
410 exit(1);
411 }
412
413 vector<Double_t> data(fNPoints, calibrationOffset);
414
415 for (int m = 0; m < signal->GetNumberOfPoints(); m++) {
416 Double_t t = signal->GetTime(m);
417 Double_t d = signal->GetData(m);
418
420 cout << "Signal: " << n << " Sample: " << m << " T: " << t << " Data: " << d << endl;
421 }
422
423 if (t > timeStart && t < timeEnd) {
424 // convert physical time (in us) to timeBin
425 Int_t timeBin = (Int_t)round((t - timeStart) / sampling);
426
427 if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Warning) {
428 if (timeBin < 0 || timeBin > fNPoints) {
429 cout << "Time bin out of range!!! bin value: " << timeBin << endl;
430 timeBin = 0;
431 }
432 }
433
434 RESTDebug << "Adding data: " << signal->GetData(m) << " to Time Bin: " << timeBin << RESTendl;
435 data[timeBin] += calibrationGain * signal->GetData(m);
436 }
437 }
438
439 if (shapingTime > 0) {
440 const auto sinShaper = [](Double_t t) -> Double_t {
441 if (t <= 0) {
442 return 0;
443 }
444 // function is normalized such that its absolute maximum is 1.0
445 // max is at x = 1.1664004483744728
446 return TMath::Exp(-3.0 * t) * TMath::Power(t, 3.0) * TMath::Sin(t) * 22.68112123672292;
447 };
448
449 const auto shapingFunction = [&sinShaper](Double_t t) -> Double_t {
450 if (t <= 0) {
451 return 0;
452 }
453 // function is normalized such that its absolute maximum is 1.0
454 // max is at x = 1.1664004483744728
455 // return sinShaper(t) - 1.0 * sinShaper(t - 1); // to add undershoot
456 return sinShaper(t);
457 };
458
459 vector<Double_t> dataAfterShaping(fNPoints, calibrationOffset);
460 for (int i = 0; i < fNPoints; i++) {
461 const Double_t value = data[i] - calibrationOffset;
462 if (value <= 0) {
463 // Only positive values are possible, 0 means no signal in this bin
464 continue;
465 }
466 for (int j = 0; j < fNPoints; j++) {
467 dataAfterShaping[j] += value * shapingFunction(((j - i) * sampling) / shapingTime);
468 }
469 }
470 data = dataAfterShaping;
471 }
472
473 TRestRawSignal rawSignal;
474 rawSignal.SetSignalID(signalID);
475 for (int x = 0; x < fNPoints; x++) {
476 double value = round(data[x]);
477 if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Warning) {
478 if (value < numeric_limits<Short_t>::min() || value > numeric_limits<Short_t>::max()) {
479 RESTDebug << "value (" << value << ") is outside short range ("
480 << numeric_limits<Short_t>::min() << ", " << numeric_limits<Short_t>::max()
481 << ")" << RESTendl;
482 }
483 }
484
485 if (value < numeric_limits<Short_t>::min()) {
486 value = numeric_limits<Short_t>::min();
487 fOutputRawSignalEvent->SetOK(false);
488 } else if (value > numeric_limits<Short_t>::max()) {
489 value = numeric_limits<Short_t>::max();
490 fOutputRawSignalEvent->SetOK(false);
491 }
492 rawSignal.AddPoint((Short_t)value);
493 }
494
496 rawSignal.Print();
497 }
498 RESTDebug << "Adding signal to raw signal event" << RESTendl;
499
500 fOutputRawSignalEvent->AddSignal(rawSignal);
501 }
502
503 RESTDebug << "TRestDetectorSignalToRawSignalProcess. Returning event with N signals "
504 << fOutputRawSignalEvent->GetNumberOfSignals() << RESTendl;
505
507}
508
514 TString readoutTypesString = GetParameter("readoutTypes", "");
515 // split it by ","
516 TObjArray* readoutTypesArray = readoutTypesString.Tokenize(",");
517 for (int i = 0; i < readoutTypesArray->GetEntries(); i++) {
518 fReadoutTypes.insert(((TObjString*)readoutTypesArray->At(i))->GetString().Data());
519 }
520 cout << "readout types: ";
521 for (const auto& type : fReadoutTypes) {
522 cout << type << " ";
523 }
524 cout << endl;
525
526 // add default type ""
527 const string defaultType;
528 fReadoutTypes.insert(defaultType);
529 fParametersMap[defaultType] = {};
530
531 for (const auto& type : fReadoutTypes) {
532 fReadoutTypes.insert(type);
533 fParametersMap[type] = {};
534
535 string typeCamelCase = type;
536 typeCamelCase[0] = toupper(typeCamelCase[0]);
537
538 Parameters parameters;
539 parameters.sampling = GetDblParameterWithUnits("sampling" + typeCamelCase, parameters.sampling);
540 parameters.shapingTime =
541 GetDblParameterWithUnits("shapingTime" + typeCamelCase, parameters.shapingTime);
542 parameters.calibrationGain =
543 GetDblParameterWithUnits("gain" + typeCamelCase, parameters.calibrationGain);
544 parameters.calibrationOffset =
545 GetDblParameterWithUnits("offset" + typeCamelCase, parameters.calibrationOffset);
546 parameters.calibrationEnergy =
547 Get2DVectorParameterWithUnits("calibrationEnergy" + typeCamelCase, parameters.calibrationEnergy);
548 parameters.calibrationRange =
549 Get2DVectorParameterWithUnits("calibrationRange" + typeCamelCase, parameters.calibrationRange);
550
551 const bool isLinearCalibration =
552 (parameters.calibrationEnergy.Mod() != 0 && parameters.calibrationRange.Mod() != 0);
553 ;
554 if (isLinearCalibration) {
555 const auto range = numeric_limits<Short_t>::max() - numeric_limits<Short_t>::min();
556 parameters.calibrationGain =
557 range * (parameters.calibrationRange.Y() - parameters.calibrationRange.X()) /
558 (parameters.calibrationEnergy.Y() - parameters.calibrationEnergy.X());
559 parameters.calibrationOffset =
560 range * (parameters.calibrationRange.X() -
561 parameters.calibrationGain * parameters.calibrationEnergy.X()) +
562 numeric_limits<Short_t>::min();
563 }
564 fParametersMap[type] = parameters;
565 }
566
567 auto nPoints = GetParameter("nPoints");
568 if (nPoints == PARAMETER_NOT_FOUND_STR) {
569 nPoints = GetParameter("Npoints", fNPoints);
570 }
571 fNPoints = StringToInteger(nPoints);
572
573 fTriggerMode = GetParameter("triggerMode", fTriggerMode);
574 const set<string> validTriggerModes = {"firstDeposit", "integralThreshold", "fixed",
575 "observable", "firstDepositTPC", "integralThresholdTPC"};
576 if (validTriggerModes.count(fTriggerMode) == 0) {
577 RESTError << "Trigger mode set to: '" << fTriggerMode
578 << "' which is not a valid trigger mode. Please use one of the following trigger modes: ";
579 for (const auto& triggerMode : validTriggerModes) {
580 RESTError << triggerMode << " ";
581 }
582 RESTError << RESTendl;
583 exit(1);
584 }
585
588 fTriggerFixedStartTime = GetDblParameterWithUnits("triggerFixedStartTime", fTriggerFixedStartTime);
589
590 // load default parameters (for backward compatibility)
591 fSampling = fParametersMap.at(defaultType).sampling;
592 fShapingTime = fParametersMap.at(defaultType).shapingTime;
593 fCalibrationGain = fParametersMap.at(defaultType).calibrationGain;
594 fCalibrationOffset = fParametersMap.at(defaultType).calibrationOffset;
595 fCalibrationEnergy = fParametersMap.at(defaultType).calibrationEnergy;
596 fCalibrationRange = fParametersMap.at(defaultType).calibrationRange;
597
598 if (fTriggerMode == "observable") {
599 fTriggerModeObservableName = GetParameter("triggerModeObservableName", "");
600 if (fTriggerModeObservableName.empty()) {
601 RESTError << "You need to set 'triggerModeObservableName' to a valid analysis tree observable"
602 << RESTendl;
603 exit(1);
604 }
605 }
606}
607
609
610Double_t TRestDetectorSignalToRawSignalProcess::GetEnergyFromADC(Double_t adc, const string& type) const {
611 if (fParametersMap.find(type) == fParametersMap.end()) {
612 RESTWarning << "TRestDetectorSignalToRawSignalProcess::GetEnergyFromADC: "
613 << "type " << type << " not found in parameters map" << RESTendl;
614 return 0;
615 }
616 const auto gain = fParametersMap.at(type).calibrationGain;
617 const auto offset = fParametersMap.at(type).calibrationOffset;
618 return (adc - offset) / gain;
619}
620
621Double_t TRestDetectorSignalToRawSignalProcess::GetADCFromEnergy(Double_t energy, const string& type) const {
622 if (fParametersMap.find(type) == fParametersMap.end()) {
623 RESTWarning << "TRestDetectorSignalToRawSignalProcess::GetEnergyFromADC: "
624 << "type " << type << " not found in parameters map" << RESTendl;
625 return 0;
626 }
627 const auto gain = fParametersMap.at(type).calibrationGain;
628 const auto offset = fParametersMap.at(type).calibrationOffset;
629 return energy * gain + offset;
630}
631
632Double_t TRestDetectorSignalToRawSignalProcess::GetTimeFromBin(Double_t bin, const string& type) const {
633 if (fParametersMap.find(type) == fParametersMap.end()) {
634 RESTWarning << "TRestDetectorSignalToRawSignalProcess::GetEnergyFromADC: "
635 << "type " << type << " not found in parameters map" << RESTendl;
636 return 0;
637 }
638 const auto sampling = fParametersMap.at(type).sampling;
639 return (bin - fTriggerDelay) * sampling;
640}
641
642Double_t TRestDetectorSignalToRawSignalProcess::GetBinFromTime(Double_t time, const string& type) const {
643 if (fParametersMap.find(type) == fParametersMap.end()) {
644 RESTWarning << "TRestDetectorSignalToRawSignalProcess::GetEnergyFromADC: "
645 << "type " << type << " not found in parameters map" << RESTendl;
646 return 0;
647 }
648 const auto sampling = fParametersMap.at(type).sampling;
649 return (UShort_t)((time + fTriggerDelay * sampling) / sampling);
650}
651
654
655 RESTMetadata << "Points per channel: " << fNPoints << RESTendl;
656 RESTMetadata << "Trigger mode: " << fTriggerMode << RESTendl;
657 RESTMetadata << "Trigger delay: " << fTriggerDelay << " units" << RESTendl;
658
659 for (const auto& readoutType : fReadoutTypes) {
660 RESTMetadata << RESTendl;
661 string type = readoutType;
662 if (type.empty()) {
663 type = "default";
664 }
665 RESTMetadata << "Readout type: " << type << RESTendl;
666 RESTMetadata << "Sampling time: " << fParametersMap.at(readoutType).sampling * 1000 << " ns"
667 << RESTendl;
668 const double shapingTime = fParametersMap.at(readoutType).shapingTime;
669 if (shapingTime > 0) {
670 RESTMetadata << "Shaping time: " << shapingTime * 1000 << " ns" << RESTendl;
671 }
672
673 if (IsLinearCalibration()) {
674 RESTMetadata << "Calibration energies: (" << fParametersMap.at(readoutType).calibrationEnergy.X()
675 << ", " << fParametersMap.at(readoutType).calibrationEnergy.Y() << ") keV"
676 << RESTendl;
677 RESTMetadata << "Calibration range: (" << fParametersMap.at(readoutType).calibrationRange.X()
678 << ", " << fParametersMap.at(readoutType).calibrationRange.Y() << ")" << RESTendl;
679 }
680 RESTMetadata << "ADC Gain: " << fParametersMap.at(readoutType).calibrationGain << RESTendl;
681 RESTMetadata << "ADC Offset: " << fParametersMap.at(readoutType).calibrationOffset << RESTendl;
682 }
683 EndPrintProcess();
684}
A process to convert a TRestDetectorSignalEvent into a TRestRawSignalEvent.
TVector2 fCalibrationEnergy
two distinct energy values used for calibration
TRestDetectorSignalEvent * fInputSignalEvent
A pointer to the specific TRestDetectorSignalEvent input.
TRestEvent * ProcessEvent(TRestEvent *inputEvent) override
The main processing event function.
Double_t fSampling
The sampling time from the binned raw output signal.
void Initialize() override
Function to initialize input/output event members and define the section name.
void InitProcess() override
To be executed at the beginning of the run (outside event loop)
TRestRawSignalEvent * fOutputRawSignalEvent
A pointer to the specific TRestRawSignalEvent input.
std::string fTriggerMode
It is used to define the way the time start will be fixed.
void PrintMetadata() override
It prints out the process parameters stored in the metadata structure.
TVector2 fCalibrationRange
position in the range corresponding to the energy in 'fCalibrationEnergy'. Values between 0 and 1
Double_t fIntegralThreshold
This parameter is used by integralWindow trigger mode to define the acquisition window.
Int_t fNPoints
The number of points of the resulting output signal.
std::string fTriggerModeObservableName
The name of the observable used to define the trigger mode (i.e. g4Ana_sensitiveVolumeFirstHitTime)
Int_t fTriggerFixedStartTime
The starting time for the "fixed" trigger mode (can be offset by the trigger delay)
Int_t fTriggerDelay
The number of time bins the time start is delayed in the resulting output signal.
void LoadConfig(const std::string &configFilename, const std::string &name="")
Function to load the configuration from an external configuration file.
void InitFromConfigFile() override
Function reading input parameters from the RML TRestDetectorSignalToRawSignalProcess metadata section...
void BeginPrintProcess()
[name, cut range]
void SetObservableValue(const std::string &name, const T &value)
Set observable value for AnalysisTree.
A base class for any REST event.
Definition TRestEvent.h:38
endl_t RESTendl
Termination flag object for TRestStringOutput.
Int_t LoadConfigFromFile(const std::string &configFilename, const std::string &sectionName="")
Give the file name, find out the corresponding section. Then call the main starter.
void SetLibraryVersion(TString version)
Set the library version of this metadata class.
TRestStringOutput::REST_Verbose_Level GetVerboseLevel()
returns the verboselevel in type of REST_Verbose_Level enumerator
void SetSectionName(std::string sName)
set the section name, clear the section content
std::string GetParameter(std::string parName, TiXmlElement *e, TString defaultValue=PARAMETER_NOT_FOUND_STR)
Returns the value for the parameter named parName in the given section.
An event container for time rawdata signals with fixed length.
It defines a Short_t array with a physical parameter that evolves in time using a fixed time bin.
void Print() const
It prints the signal data on screen.
void SetSignalID(Int_t sID)
It sets the id number of the signal.
void AddPoint(Short_t)
Adds a new point to the end of the signal data array.
@ REST_Debug
+show the defined debug messages
Double_t StringToDouble(std::string in)
Gets a double from a string.
Int_t StringToInteger(std::string in)
Gets an integer from a string.