SdCalibrator.cc
Go to the documentation of this file.
1 #include <fwk/CentralConfig.h>
2 #include <fwk/RunController.h>
3 
4 #include <det/Detector.h>
5 #include <sdet/SDetector.h>
6 #include <sdet/Station.h>
7 #include <sdet/StationTriggerAlgorithm.h>
8 #include <sdet/PMTConstants.h>
9 
10 #include <evt/Event.h>
11 
12 #include <sevt/EventTrigger.h>
13 #include <sevt/Header.h>
14 #include <sevt/SEvent.h>
15 #include <sevt/Station.h>
16 #include <sevt/Scintillator.h>
17 #include <sevt/PMT.h>
18 #include <sevt/PMTCalibData.h>
19 #include <sevt/SmallPMTData.h>
20 #include <sevt/SmallPMTCalibData.h>
21 #include <sevt/PMTRecData.h>
22 #include <sevt/StationGPSData.h>
23 #include <sevt/StationRecData.h>
24 #include <sevt/ScintillatorRecData.h>
25 #include <sevt/StationTriggerData.h>
26 #include <sevt/StationCalibData.h>
27 #include <sevt/SignalSegment.h>
28 
29 #include <utl/TimeStamp.h>
30 #include <utl/TimeInterval.h>
31 #include <utl/Trace.h>
32 #include <utl/TraceAlgorithm.h>
33 #include <utl/Reader.h>
34 #include <utl/ErrorLogger.h>
35 #include <utl/Math.h>
36 #include <utl/Accumulator.h>
37 #include <utl/QuadraticFitter.h>
38 #include <utl/ExponentialFitter.h>
39 #include <utl/String.h>
40 #include <utl/ShadowPtr.h>
41 #include <utl/TabularStream.h>
42 
43 #include "SdCalibrator.h"
44 #include "CalibrationParameters.h"
45 
46 #include <iostream>
47 #include <sstream>
48 #include <algorithm>
49 
50 #define USE_SPMT_SIGNAL_AS_TOTAL 0
51 
52 #include <config.h>
53 
54 using namespace fwk;
55 using namespace evt;
56 using namespace sevt;
57 using namespace utl;
58 using namespace std;
59 
60 
61 namespace SdCalibratorOG {
62 
64  public:
65  void RemoveVEMTrace() { fTrace.RemoveTrace(StationConstants::eTotal); }
66  };
67 
68 
69  // pair<> output helper
70  template<typename T1, typename T2>
71  inline ostream&
72  operator<<(ostream& os, pair<T1, T2>& p)
73  {
74  return os << '[' << p.first << ", " << p.second << ']';
75  }
76 
77 
78  inline
79  bool
80  IsInRange(const pair<double, double>& r, const double a, const double b)
81  {
82  return r.first <= r.second && a <= r.first && r.second <= b;
83  }
84 
85 
86  // Read parameters from XML files
89  {
90  auto topB = CentralConfig::GetInstance()->GetTopBranch("SdCalibrator");
91 
92  topB.GetChild("pmtSummationCutoff").GetData(fPMTSummationCutoff);
93  topB.GetChild("peakFitRange").GetData(fPeakFitRange);
94  topB.GetChild("peakFitChi2Accept").GetData(fPeakFitChi2Accept);
95  topB.GetChild("peakOnlineToVEMFactor").GetData(fPeakOnlineToVEMFactor);
96  topB.GetChild("peakHistogramToVEMFactor").GetData(fPeakHistogramToVEMFactor);
97  topB.GetChild("peakOnlineToMIPFactor").GetData(fPeakOnlineToMIPFactor);
98  topB.GetChild("peakHistogramToMIPFactor").GetData(fPeakHistogramToMIPFactor);
99  topB.GetChild("chargeFitShoulderHeadRatio").GetData(fChargeFitShoulderHeadRatio);
100  topB.GetChild("chargeFitChi2Accept").GetData(fChargeFitChi2Accept);
101  topB.GetChild("chargeOnlineToVEMFactor").GetData(fChargeOnlineToVEMFactor);
102  topB.GetChild("chargeHistogramToVEMFactor").GetData(fChargeHistogramToVEMFactor);
103  topB.GetChild("chargeOnlineToMIPFactor").GetData(fChargeOnlineToMIPFactor);
104  topB.GetChild("chargeHistogramToMIPFactor").GetData(fChargeHistogramToMIPFactor);
105 
106  auto shapeFitB = topB.GetChild("shapeFitRange");
107  shapeFitB.GetChild("beforeCalibrationVersion12").GetData(fShapeFitRangeBefore12);
108  shapeFitB.GetChild("sinceCalibrationVersion12").GetData(fShapeFitRangeSince12);
109 
110  topB.GetChild("riseTimeFractions").GetData(fRiseTimeFractions);
111  topB.GetChild("fallTimeFractions").GetData(fFallTimeFractions);
112  if (!IsInRange(fRiseTimeFractions, 0, 1) || !IsInRange(fFallTimeFractions, 0, 1)) {
113  ERROR("Rise/fall time fractions must be ascending and within [0, 1]");
114  return eFailure;
115  }
116 
117  const auto testStations = topB.GetChild("testStations").Get<vector<int>>();
118  fTestStations = set<int>(testStations.begin(), testStations.end());
119 
120  auto fsB = topB.GetChild("findSignal");
121  fsB.GetChild("threshold").GetData(fFindSignalThreshold);
122  fsB.GetChild("binsAboveThreshold").GetData(fFindSignalBinsAboveThreshold);
123 
124  fTreatHGLGEqualInSignalSearch = bool(topB.GetChild("treatHGLGEqualInSignalSearch"));
125 
126  fApplyBackwardFlatPieceCheck = bool(topB.GetChild("applyBackwardFlatPieceCheck"));
127 
128  fDecreaseLGFlatPieceTolerance = bool(topB.GetChild("decreaseLGFlatPieceTolerance"));
129 
130  fIncludeWaterCherenkovDetectorInScintillatorStartStopDetermination =
131  topB.GetChild("includeWaterCherenkovDetectorInScintillatorStartStopDetermination").Get<bool>();
132 
133  topB.GetChild("binsBeforeStartForSPMT").GetData(fBinsBeforeStartForSPMT);
134 
135  ostringstream info;
136  info << "Parameters:\n"
137  "pmtSummationCutoff: " << fPMTSummationCutoff << "\n"
138  "peakFitRange: " << fPeakFitRange << "\n"
139  "peakFitChi2Accept: " << fPeakFitChi2Accept << "\n"
140  "peakOnlineToVEMFactor: " << fPeakOnlineToVEMFactor << "\n"
141  "peakHistogramToVEMFactor: " << fPeakHistogramToVEMFactor << "\n"
142  "peakOnlineToMIPFactor: " << fPeakOnlineToMIPFactor << "\n"
143  "peakHistogramToMIPFactor: " << fPeakHistogramToMIPFactor << "\n"
144  "chargeFitShoulderHeadRatio: " << fChargeFitShoulderHeadRatio << "\n"
145  "chargeFitChi2Accept: " << fChargeFitChi2Accept << "\n"
146  "chargeOnlineToVEMFactor: " << fChargeOnlineToVEMFactor << "\n"
147  "chargeHistogramToVEMFactor: " << fChargeHistogramToVEMFactor << "\n"
148  "chargeOnlineToMIPFactor: " << fChargeOnlineToMIPFactor << "\n"
149  "chargeHistogramToMIPFactor: " << fChargeHistogramToMIPFactor << "\n"
150  "shapeFitRange:\n"
151  " beforeCalibrationVersion12: " << fShapeFitRangeBefore12 << "\n"
152  " sinceCalibrationVersion12: " << fShapeFitRangeSince12 << "\n"
153  "riseTimeFractions: " << fRiseTimeFractions << "\n"
154  "fallTimeFractions: " << fFallTimeFractions << "\n"
155  "testStations: " << Join(" ", fTestStations) << "\n"
156  "findSignal:\n"
157  " threshold: " << fFindSignalThreshold << "\n"
158  " binsAboveThreshold: " << fFindSignalBinsAboveThreshold << "\n"
159  "treatHGLGEqualInSignalSearch: " << fTreatHGLGEqualInSignalSearch << "\n"
160  "applyBackwardFlatPieceCheck: " << fApplyBackwardFlatPieceCheck << "\n"
161  "decreaseLGFlatPieceTolerance: " << fDecreaseLGFlatPieceTolerance;
162  INFO(info);
163 
164  return eSuccess;
165  }
166 
167 
169  SdCalibrator::Run(evt::Event& event)
170  {
171  if (!event.HasSEvent())
172  return eSuccess;
173  auto& sEvent = event.GetSEvent();
174 
175  fToldYaPeak = false;
176  fToldYaCharge = false;
177  fToldYaShape = false;
178 
179  vector<int> noVEMStations;
180  vector<int> randomTrigger;
181  vector<int> badCompression;
182  int noTrigger = 0;
183 
184  if (sEvent.StationsBegin() != sEvent.StationsEnd())
185  ++RunController::GetInstance().GetRunData().GetNamedCounters()["SdCalibrator/CalibratedEvents"];
186 
187  const bool isCommsCrisis = kCommsCrisis.IsInRange(det::Detector::GetInstance().GetTime());
188 
189  // loop on stations
190  int nErrorZero = 0;
191  for (auto& station : sEvent.StationsRange()) {
192 
193  if (!station.HasTriggerData()) {
194  station.SetRejected(StationConstants::eNoTrigger);
195  ++noTrigger;
196  continue;
197  }
198 
199  // T2Life() has to be checked before SetSilent() is applied!
200  if (!station.IsT2Life())
201  station.SetRejected(StationConstants::eNotAliveT2);
202  if (isCommsCrisis && !station.IsT2Life120())
203  station.SetRejected(StationConstants::eNotAliveT120);
204 
205  const auto& trig = station.GetTriggerData();
206  if (trig.GetErrorCode() & 0xff) { // for UUBs errorcodes are above 256.
207  if (trig.IsSilent() && !station.IsRejected())
208  station.SetSilent();
209  else
210  station.SetRejected(StationConstants::eErrorCode);
211  continue;
212  }
213 
214  if (trig.IsRandom()) {
215  randomTrigger.push_back(station.GetId());
216  station.SetRejected(StationConstants::eRandom);
217  continue;
218  }
219 
220  if (!station.HasCalibData()) {
221  station.SetRejected(StationConstants::eNoCalibData);
222  continue;
223  }
224 
225  // exclude FADCs with Patrick's data
226  if (station.GetCalibData().GetVersion() > 32000) {
227  ostringstream warn;
228  warn << "Station " << station.GetId() << " has LS calibration version "
229  << station.GetCalibData().GetVersion() << '!';
230  WARNING(warn);
231  station.SetRejected(StationConstants::eNoCalibData);
232  continue;
233  }
234 
235  if (!station.HasGPSData()) {
236  station.SetRejected(StationConstants::eNoGPSData);
237  continue;
238  }
239 
240  ApplyTimeCorrection(station);
241  // check for "bad compressed data"
242  if (sEvent.HasTrigger()) {
243  const int trigSecond = sEvent.GetTrigger().GetTime().GetGPSSecond();
244  const int timeDiff = int(station.GetGPSData().GetSecond()) - trigSecond;
245  if (abs(timeDiff) > 1) {
246  station.GetTriggerData().SetErrorCode(StationTriggerData::eBadCompress);
247  station.SetRejected(StationConstants::eBadCompress);
248  const int sId = station.GetId();
249  badCompression.push_back(sId);
250  ostringstream info;
251  info << "Bad compress data: station " << sId << " has " << timeDiff
252  << " sec of time difference to event trigger.";
253  INFO(info);
254  continue;
255  }
256  }
257 
258  const auto& dStation = det::Detector::GetInstance().GetSDetector().GetStation(station);
259  fIsUUB = dStation.IsUUB();
260 
261  CalculatePeakAndCharge(station);
262 
263  if (fIsUUB && station.HasSmallPMTData()) {
264  if (CopySmallPMTCalibData(station)) {
265  station.GetSmallPMTData().SetIsTubeOk();
266  } else {
267  station.GetSmallPMTData().SetIsTubeOk(false);
268  station.GetSmallPMT().GetCalibData().SetIsTubeOk(false);
269  ostringstream warn;
270  warn << "Station " << station.GetId() << ": No valid SmallPMT calibration. "
271  "Setting IsTubeOk as false for SmallPMT.";
272  WARNING(warn);
273  }
274  }
275 
276  if (!ComputeBaselines(station) ||
277  !BuildSignals(station) ||
278  !MergeSignals(station) ||
279  !SelectSignal(station)) {
280  station.SetRejected(StationConstants::eBadCalib);
281  continue;
282  }
283 
284  // The default for stations is to be candidate stations
285  // A station should never be reset to Candidate ever in the Module Sequence
286  // because it overrules any previously done rejection not known to the SdCalibrator
287  // It should only be rejected or set silent as done above or by other Modules before or after this module
288 
289  ++nErrorZero;
290 
291  }
292 
293  sEvent.SetNErrorZeroStations(nErrorZero);
294 
295  String::StationIdListWithMessage(noVEMStations, "without VEM trace rejected.");
296  String::StationIdListWithMessage(randomTrigger, "with random trigger rejected.");
297  String::StationIdListWithMessage(badCompression, "with bad compression data rejected.");
298 
299  if (noTrigger) {
300  ostringstream info;
301  info << noTrigger << " station" << String::Plural(noTrigger)
302  << String::OfIds(badCompression) << " without trigger data rejected.";
303  INFO(info);
304  }
305 
306  return eSuccess;
307  }
308 
309 
310  void
312  {
313  auto& gps = station.GetGPSData();
314 
315  // NEW : TAP 26/04/2003 -> From CDAS v1r2 : taking into account Offsets...
316  // Warning, part of the field is used for the tick offset:
317  // GPS Offset = 0.01*(short)(gps->Offset & 0xffff)
318  // Tick Offset = (short)(gps->Offset>>16)
319  // New: taking into account 100ns jumps
320  // From Moulin Rouge and Dia Noche we found that the TickFall-Tick
321  // can be 0, 9, 10, 11 or a big number. The big number could be
322  // understood if it is the trigger of another event. It was found
323  // that if the dt is 0, there is a 100ns jump in the event, and not
324  // in any other case, including big values. Hence this empiric
325  // correction
326  //
327  // This is the code from IoSd v1r2 :
328  // gps->NanoSecond =
329  // (unsigned int)((gps->Tick*(1000000000.0 + gps->NextST - gps->CurrentST)
330  // /gps->Next100) + gps->CurrentST + 0.01*(short)(gps->Offset & 0xffff))
331  // -100*(gps->TickFall == gps->Tick);
332 
333  const unsigned int tick = gps.GetTick();
334  const int currentST = gps.GetCurrentST();
335  const int next100 = gps.GetNext100();
336  const int nextST = gps.GetNextST();
337 
338 #ifndef IOSD_V1R0
339  const unsigned int tickFall = gps.GetTickFall();
340  const int offset = gps.GetOffset();
341 
342  const unsigned int nanosecond =
343  (unsigned int)((tick * (1e9 + nextST - currentST) / next100) + currentST +
344  0.01 * short(offset & 0xffff)) - 100 * (tickFall == tick);
345 #else
346  const unsigned int nanosecond =
347  (unsigned int)((tick * (1e9 + nextST - currentST) / next100) + currentST);
348 #endif
349 
350  gps.SetCorrectedNanosecond(nanosecond);
351  }
352 
353 
354  bool
355  SdCalibrator::CopySmallPMTCalibData(Station& station)
356  {
357  // Check specific SmallPMT quantities
358  const auto& spmt = station.GetSmallPMTData();
359  if (!spmt.HasCalibData())
360  return false;
361 
362  const auto& spmtCalib = spmt.GetCalibData();
363 
364  if (!spmtCalib.IsTubeOk())
365  return false;
366 
367  // Check standard PMT quantities
368  auto& pmt = station.GetSmallPMT();
369  if (!pmt.HasCalibData())
370  return false;
371 
372  const auto& pmtCalib = pmt.GetCalibData();
373 
374  if (!pmtCalib.IsTubeOk())
375  return false;
376 
377  if (!pmt.HasRecData())
378  pmt.MakeRecData();
379  auto& pmtRec = pmt.GetRecData();
380 
381  const double spmtVemCharge = 1 / (spmtCalib.GetBeta() * spmtCalib.GetCorrectionFactor());
382  const double spmtVemChargeErr =
383  spmtCalib.GetBetaError() * spmtCalib.GetCorrectionFactor() * Sqr(spmtVemCharge);
384 
385  if (std::isnan(spmtVemCharge) || std::isinf(spmtVemCharge) ||
386  spmtVemCharge <= 0 || spmtVemChargeErr <= 0)
387  return false;
388 
389  pmtRec.SetVEMCharge(spmtVemCharge, spmtVemChargeErr);
390 
391  // SmallPMT "VEMpeak" estimation
392  double muonAreaOverPeak = 0;
393  for (const auto& lpmt : station.PMTsRange()) {
394  if (lpmt.HasCalibData() && lpmt.GetCalibData().IsTubeOk() && lpmt.HasRecData()) {
395  const auto& rec = lpmt.GetRecData();
396  const auto peak = rec.GetVEMPeak();
397  if (peak > 0)
398  muonAreaOverPeak += rec.GetVEMCharge() / peak;
399  }
400  }
401  if (muonAreaOverPeak > 0)
402  pmtRec.SetVEMPeak(spmtVemCharge / muonAreaOverPeak, 0);
403  else {
404  ostringstream warn;
405  warn << "Station " << station.GetId()
406  << ": Cannot calculate properly SmallPMT VEMpeak. Using default value = 1/3";
407  WARNING(warn);
408  pmtRec.SetVEMPeak(1./3, 0);
409  }
410 
411  ostringstream info;
412  info << "SmallPMT calibration for station " << station.GetId() << "\n"
413  << " beta = " << spmtCalib.GetBeta() << " +- " << spmtCalib.GetBetaError()
414  << " ---> VEMcharge = " << pmtRec.GetVEMCharge() << " +- " << pmtRec.GetVEMChargeError()
415  << " (VEMpeak = " << pmtRec.GetVEMPeak() << ")";
416  INFO(info);
417 
418  pmtRec.SetIsVEMChargeFromHistogram(false);
419  pmtRec.SetIsVEMPeakFromHistogram(false);
420 
421  pmtRec.SetMuonChargeSlope(0);
422  pmtRec.SetMuonPulseDecayTime(0, 0);
423  pmtRec.SetGainRatio(1);
424 
425  return true;
426  }
427 
428 
429  void
430  SdCalibrator::CalculatePeakAndCharge(Station& station)
431  {
432  const auto& stationCalib = station.GetCalibData();
433 
434  const auto calibVersion = stationCalib.GetVersion();
435  const auto& dStation = det::Detector::GetInstance().GetSDetector().GetStation(station);
436 
437  typedef VariableBinHistogramWrap<short, int> CalibHistogram;
438  typedef VariableBinHistogramWrap<double, int> CalibHistogramD;
439 
440  for (auto& pmt : station.PMTsRange(sdet::PMTConstants::eAnyType)) {
441 
442  const auto type = pmt.GetType();
443 
444  // No muon histograms for SmallPMT.
445  // It has its own dedicated function "CopySmallPMTCalibData"
446  // to fill VEM values in RecData from SmallPMTCalibData class.
448  continue;
449 
450  if (!pmt.HasCalibData())
451  continue;
452 
453  const auto& pmtCalib = pmt.GetCalibData();
454 
455  if (!pmtCalib.IsTubeOk())
456  continue;
457 
458  if (!pmt.HasRecData())
459  pmt.MakeRecData();
460  auto& pmtRec = pmt.GetRecData();
461 
462  {
463  double gainRatio = pmtCalib.GetGainRatio();
464  if (calibVersion == 12)
465  gainRatio /= 1.07;
466  if (!gainRatio) {
467  const int pmtId = pmt.GetId();
468  ostringstream warn;
469  warn << "Gain ratio zero encountered for PMT " << pmtId << "; replacing it with the nominal value.";
470  WARNING(warn);
471  gainRatio = dStation.GetPMT(pmtId).GetGainRatio();
472  }
473  pmtRec.SetGainRatio(gainRatio);
474  }
475 
476  if (!pmtRec.GetVEMPeak()) {
477  // first approximation is corrected online value
478  const double onlineFactor =
479  (type == sdet::PMTConstants::eScintillator) ? fPeakOnlineToMIPFactor : fPeakOnlineToVEMFactor;
480  double vemPeak = pmtCalib.GetOnlinePeak() * onlineFactor;
481  double vemPeakErr = 0;
482  pmtRec.SetOnlineVEMPeak(vemPeak, vemPeakErr);
483  if (!vemPeak)
484  vemPeak = dStation.GetPMT(pmt.GetId()).GetNominalVEMPeak();
485  bool vemPeakFromHisto = false;
486 
487  // try to improve values above with histogram fitting
488  if (calibVersion > 8) {
489  const auto& peakHistoBinning = dStation.GetMuonPeakHistogramBinning<short>(type, calibVersion);
490  const int muonPeakHistoSize = pmtCalib.GetMuonPeakHisto().size();
491  if (!muonPeakHistoSize || int(peakHistoBinning.size())-1 != muonPeakHistoSize) {
492  if (!fToldYaPeak) {
493  WARNING("According to the LS calibration version there should be a muon peak histogram... Will not tell you again!");
494  fToldYaPeak = true;
495  }
496  } else {
497  const CalibHistogram peakHisto(peakHistoBinning, pmtCalib.GetMuonPeakHisto());
498  const double histoFactor =
499  (type == sdet::PMTConstants::eScintillator) ? fPeakHistogramToMIPFactor : fPeakHistogramToVEMFactor;
500  const double predictedHistoPeak = vemPeak / histoFactor;
501  if (predictedHistoPeak > 0 && peakHisto.GetMaximum() > 200) {
502  const double baseEstimate = pmtCalib.GetBaseline() - pmtCalib.GetMuonPeakHistoOffset();
503  const double base = (fabs(baseEstimate) < 20) ? baseEstimate : 0;
504  const double peakLow = fPeakFitRange.first * predictedHistoPeak;
505  const double peakHigh = fPeakFitRange.second * predictedHistoPeak;
506 
507  if (peakHigh - peakLow >= 5) {
508  try {
509  // note: x axis is offset by base, in order to be the same
510  // for all histograms
511  QuadraticFitData qf;
512  MakeQuadraticFitter(peakHisto, peakLow + base, peakHigh + base).GetFitData(qf);
513  pmtRec.GetMuonPeakFitData() = qf;
514  const double fittedPeak = qf.GetExtremePosition() - base;
515  // reasonable limits for the result
516  if (peakLow <= fittedPeak && fittedPeak <= peakHigh &&
517  qf.GetChi2() <= fPeakFitChi2Accept*qf.GetNdof()) {
518  vemPeak = fittedPeak * histoFactor;
519  vemPeakErr = qf.GetExtremePositionError() * histoFactor;
520  pmtRec.SetHistogramVEMPeak(vemPeak, vemPeakErr);
521  vemPeakFromHisto = true;
522  }
523  } catch (OutOfBoundException& ex) {
524  WARNING(ex.GetMessage());
525  }
526  }
527  }
528  }
529  }
530  pmtRec.SetVEMPeak(vemPeak, vemPeakErr);
531  pmtRec.SetIsVEMPeakFromHistogram(vemPeakFromHisto);
532  }
533 
534  if (!pmtRec.GetVEMCharge()) {
535  // The charge conversion factor is the correction for the difference between the mode
536  // of charge histograms for the VEM (vertical-centered muons) or MIP (vertical muons)
537  // and the mode of charge histogram for omni-directional background particles.
538  // charge conversion factor depends on PMT type, so far eScintillator, and eWaterCherenkovLarge;
539  const double onlineFactor =
540  (type == sdet::PMTConstants::eScintillator) ? fChargeOnlineToMIPFactor : fChargeOnlineToVEMFactor;
541  double vemCharge = pmtCalib.GetOnlineCharge() * onlineFactor;
542  double vemChargeErr = 20 * onlineFactor;
543  pmtRec.SetOnlineVEMCharge(vemCharge, vemChargeErr);
544  bool vemChargeFromHisto = false;
545  double muChargeSlope = 0;
546 
547  // try to improve values above with histogram fitting
548  if (calibVersion > 8) {
549  const auto& chargeHistoBinning = dStation.GetMuonChargeHistogramBinning<double>(type, calibVersion);
550  const auto& calibChargeHisto = pmtCalib.GetMuonChargeHisto();
551  const int chargeHistoSize = calibChargeHisto.size();
552 
553  if (!chargeHistoSize || int(chargeHistoBinning.size()) - 1 != chargeHistoSize) {
554  if (!fToldYaCharge) {
555  WARNING("According to the LS calibration version there should be a muon charge histogram... Will not tell you again!");
556  fToldYaCharge = true;
557  }
558  } else {
559  const CalibHistogramD chargeHisto(chargeHistoBinning, pmtCalib.GetMuonChargeHisto());
560  // apparently there were 19 bins in calibration version 13
561  const double baseEstimate = pmtCalib.GetBaseline() *
562  (calibVersion == 13 ? 19 : 20) - pmtCalib.GetMuonChargeHistoOffset();
563  const double base = (fabs(baseEstimate) < 20) ? baseEstimate : 0;
564 
565  if (chargeHisto.GetMaximum() > 500) {
566 
567  // skip 5 last bins and any small values at the high end
568  const int size = chargeHisto.GetNBins();
569  int start = (size - 1) - 5;
570 
571  {
573  for ( ; start >= 2 && chargeHisto.GetBinAverage(start) < small; --start)
574  ;
575  }
576 
577  // find "head-and-shoulder": from the upper side of the histogram,
578  // search for local maximum (head), surrounded by drops (shoulders)
579  // with shoulder/head value ratio of less than
580  // fChargeWindowShoulderHeadRatio
581  int maxBin = start;
582  double maxValue = chargeHisto.GetBinAverage(start);
583  int shoulderLow = 0;
584  {
585  double value = chargeHisto.GetBinAverage(start - 1);
586  double value1 = chargeHisto.GetBinAverage(start - 2);
587  for (int pos = start - 1; pos >= 2; --pos) {
588  if (maxValue < value) {
589  maxValue = value;
590  maxBin = pos;
591  }
592  const double reducedMax = maxValue * fChargeFitShoulderHeadRatio;
593  // require 3 consecutive values to be lower than reducedMax
594  // to qualify as a shoulder
595  const double value2 = chargeHisto.GetBinAverage(pos - 2);
596  if (value <= reducedMax && value1 <= reducedMax && value2 <= reducedMax) {
597  shoulderLow = pos;
598  break;
599  }
600  value = value1;
601  value1 = value2;
602  }
603  }
604  if (shoulderLow) {
605  // find upper shoulder
606  const double reducedMax = maxValue * fChargeFitShoulderHeadRatio;
607  const int size2 = size - 2;
608 
609  int shoulderHigh = 0;
610  {
611  double value = chargeHisto.GetBinAverage(maxBin + 1);
612  double value1 = chargeHisto.GetBinAverage(maxBin + 2);
613  for (int pos = maxBin + 1; pos < size2; ++pos) {
614  const double value2 = chargeHisto.GetBinAverage(pos+2);
615  if (value <= reducedMax && value1 <= reducedMax && value2 <= reducedMax) {
616  shoulderHigh = pos;
617  break;
618  }
619  value = value1;
620  value1 = value2;
621  }
622  }
623 
624  if (shoulderHigh) {
625  const double chargeLow = chargeHisto.GetBinCenter(shoulderLow);
626  const double chargeHigh = chargeHisto.GetBinCenter(shoulderHigh);
627  const double histoFactor =
628  (type == sdet::PMTConstants::eScintillator) ? fChargeHistogramToMIPFactor : fChargeHistogramToVEMFactor;
629  const double predictedHistoCharge = vemCharge / histoFactor;
630  if (chargeLow <= predictedHistoCharge && predictedHistoCharge <= chargeHigh) {
631  try {
632  // now fit in shoulder window
633  QuadraticFitData qf;
634  MakeQuadraticFitter(chargeHisto, chargeLow, chargeHigh).GetFitData(qf);
635  pmtRec.GetMuonChargeFitData() = qf;
636  const double fittedCharge = qf.GetExtremePosition() - base;
637 
638  // reasonable limits for the result
639  if (chargeLow <= fittedCharge && fittedCharge <= chargeHigh &&
640  qf.GetChi2() <= fChargeFitChi2Accept*qf.GetNdof()) {
641  vemCharge = fittedCharge * histoFactor;
642  vemChargeErr = qf.GetExtremePositionError() * histoFactor;
643  pmtRec.SetHistogramVEMCharge(vemCharge, vemChargeErr);
644  vemChargeFromHisto = true;
645  }
646  } catch (OutOfBoundException& ex) {
647  ostringstream warn;
648  warn << ex.GetMessage() << "\n"
649  "Quadratic fit between bins " << chargeLow << " and " << chargeHigh
650  << " failed on this charge histogram:\n";
651  for (unsigned int i = 0, n = chargeHisto.GetNBins(); i < n; ++i)
652  warn << chargeHisto.GetBinCenter(i) << ' ' << chargeHisto.GetBinAverage(i) << '\n';
653  WARNING(warn);
654  }
655 
656  try {
657  // slope of muon charge
659  const double start = chargeHigh + 10;
660  const double stop = min(2*start, 950.);
661  if (start+10 < stop) {
662  MakeExponentialFitter(chargeHisto, start, stop).GetFit(ef);
663  pmtRec.GetMuonChargeSlopeFitData() = ef;
664 
665  const double slope = vemCharge * ef.GetSlope();
666 
667  if (slope < -0.5)
668  muChargeSlope = slope;
669  }
670  } catch (OutOfBoundException& ex) {
671  WARNING(ex.GetMessage());
672  }
673  }
674  }
675  }
676  } // if max > 500
677  }
678  }
679  pmtRec.SetVEMCharge(vemCharge, vemChargeErr);
680  pmtRec.SetIsVEMChargeFromHistogram(vemChargeFromHisto);
681  pmtRec.SetMuonChargeSlope(muChargeSlope);
682  }
683 
684  // shape histogram
685  double muDecayTime = 0;
686  double muDecayTimeErr = 0;
687  if (calibVersion > 8) {
688  // muon decay time from muon shape histogram
689  if (pmtCalib.GetMuonShapeHisto().empty()) {
690  if (!fToldYaShape) {
691  WARNING("According to the LS calibration version there should be a muon shape histogram... Will not tell you again!");
692  fToldYaShape = true;
693  }
694  } else {
695  const auto& muShapeHisto = pmtCalib.GetMuonShapeHisto();
696  const unsigned int size = muShapeHisto.size();
697  if (size) {
698  try {
700 
701  typedef VariableBinHistogramWrap<double, int> ShapeHistogram;
702  const auto& shapeHistoBinning = dStation.GetMuonShapeHistogramBinning(calibVersion);
703  const double subtract = muShapeHisto[0];
705  ShapeHistogram(shapeHistoBinning, muShapeHisto),
706  calibVersion < 12 ? fShapeFitRangeBefore12 : fShapeFitRangeSince12
707  ).GetFit(ef, subtract);
708  pmtRec.GetMuonShapeFitData() = ef;
709 
710  const double slope = ef.GetSlope();
711  const double slopeErr = ef.GetSlopeError();
712 
713  if (slope) {
714  const double decayTime = -1 / slope;
715  if (0 <= decayTime && decayTime < 1000*nanosecond) {
716  muDecayTime = decayTime;
717  muDecayTimeErr = slopeErr / Sqr(slope);
718  }
719  }
720  } catch (OutOfBoundException& ex) {
721  WARNING(ex.GetMessage());
722  }
723  }
724  }
725  }
726  pmtRec.SetMuonPulseDecayTime(muDecayTime, muDecayTimeErr);
727 
728  }
729  }
730 
731 
732  // set rise/fall time for PMT trace and return shape parameter
733  void
734  SdCalibrator::ComputeShapeRiseFallPeak(PMTRecData& pmtRec,
735  const double binTiming,
736  const unsigned int startBin,
737  const unsigned int startIntegration,
738  const unsigned int endIntegration, // end is exclusive
739  const double traceIntegral)
740  const
741  {
742  // sorry for not using the nice TraceAlgorithms, but all this values can
743  // be calculated in single trace pass
744 
745  if (traceIntegral <= 0)
746  return;
747 
748  const double riseStartSentry = fRiseTimeFractions.first * traceIntegral;
749  const double riseEndSentry = fRiseTimeFractions.second * traceIntegral;
750  const double fallStartSentry = fFallTimeFractions.first * traceIntegral;
751  const double fallEndSentry = fFallTimeFractions.second * traceIntegral;
752  const unsigned int shapeSentry = startIntegration + (unsigned int)(600*nanosecond / binTiming);
753  const double t40Sentry = 0.4 * traceIntegral;
754  const double t50Sentry = 0.5 * traceIntegral;
755  double riseStartBin = 0;
756  double riseEndBin = 0;
757  double fallStartBin = 0;
758  double fallEndBin = 0;
759  double t40Bin = 0;
760  double t50Bin = 0;
761  double sumEarly = 0;
762 
763  double peakAmplitude = 0;
764  double runningSum = 0;
765  double oldSum = 0;
766 
767  const auto& trace = pmtRec.GetVEMTrace();
768 
769  for (unsigned int i = startIntegration; i < endIntegration; ++i) {
770 
771  const double binValue = trace[i];
772  runningSum += binValue;
773 
774  if (peakAmplitude < binValue)
775  peakAmplitude = binValue;
776 
777  if (!sumEarly && i >= shapeSentry)
778  sumEarly = oldSum;
779 
780  if (!riseStartBin && runningSum > riseStartSentry)
781  riseStartBin = i - (runningSum - riseStartSentry) / (runningSum - oldSum);
782 
783  if (!riseEndBin && runningSum > riseEndSentry)
784  riseEndBin = i - (runningSum - riseEndSentry) / (runningSum - oldSum);
785 
786  if (!fallStartBin && runningSum > fallStartSentry)
787  fallStartBin = i - (runningSum - fallStartSentry) / (runningSum - oldSum);
788 
789  if (!fallEndBin && runningSum > fallEndSentry)
790  fallEndBin = i - (runningSum - fallEndSentry) / (runningSum - oldSum);
791 
792  if (!t40Bin && runningSum > t40Sentry)
793  t40Bin = i - (runningSum - t40Sentry) / (runningSum - oldSum);
794 
795  if (!t50Bin && runningSum > t50Sentry)
796  t50Bin = i - (runningSum - t50Sentry) / (runningSum - oldSum);
797 
798  oldSum = runningSum;
799 
800  }
801 
802  pmtRec.SetPeakAmplitude(peakAmplitude);
803  pmtRec.SetRiseTime(binTiming * (riseEndBin - riseStartBin), 0);
804  pmtRec.SetFallTime(binTiming * (fallEndBin - fallStartBin), 0);
805  pmtRec.SetT40(binTiming * (t40Bin - startBin));
806  pmtRec.SetT50(binTiming * (t50Bin - startBin));
807  if (shapeSentry < endIntegration) {
808  const double sumLate = runningSum - sumEarly;
809  if (sumLate > 1e-3)
810  pmtRec.SetShapeParameter(sumEarly / sumLate);
811  }
812  }
813 
814 
815  void
816  SdCalibrator::SumPMTComponents(Station& station)
817  const
818  {
819  // Start bin has been found and total trace and timing set. Copy individual
820  // component trace information TAP - 01/02/2006.
821 
822  vector<const TraceD*> compTrace;
823 
824  const auto& dStation = det::Detector::GetInstance().GetSDetector().GetStation(station);
825 
826  for (unsigned int comp = 1; comp <= StationConstants::eLastSource; ++comp) {
827 
828  const auto component = static_cast<StationConstants::SignalComponent>(comp);
829 
830  compTrace.clear();
831 
832  // is component present?
833  for (const auto& pmt : station.PMTsRange())
834  if (pmt.HasRecData()) {
835  const auto& pmtRec = pmt.GetRecData();
836  if (pmtRec.HasVEMTrace(component))
837  compTrace.push_back(&pmtRec.GetVEMTrace(component));
838  }
839 
840  const unsigned int nPMTs = compTrace.size();
841 
842  if (nPMTs) {
843 
844  const unsigned int fadcTraceLength = dStation.GetFADCTraceLength();
845 
846  TraceD sumTrace(fadcTraceLength, dStation.GetFADCBinSize());
847 
848  for (unsigned int pos = 0; pos < fadcTraceLength; ++pos) {
849 
850  double& sum = sumTrace[pos];
851 
852  sum = 0;
853  int n = 0;
854  for (unsigned int pmtIndex = 0; pmtIndex < nPMTs; ++pmtIndex) {
855  const double value = (*compTrace[pmtIndex])[pos];
856  if (value > fPMTSummationCutoff) {
857  sum += value;
858  ++n;
859  }
860  }
861  if (n)
862  sum /= n;
863 
864  }
865 
866  if (!station.HasVEMTrace(component))
867  station.MakeVEMTrace(component);
868  station.GetVEMTrace(component) = sumTrace;
869 
870  }
871 
872  }
873  }
874 
875 
876  void
877  SdCalibrator::MakeFlatBaseline(PMT& pmt, const sdet::PMTConstants::PMTGain gain)
878  const
879  {
880  auto& pmtRec = pmt.GetRecData();
881  if (!pmtRec.HasFADCBaseline(gain))
882  pmtRec.MakeFADCBaseline(gain);
883  auto& baseline = pmtRec.GetFADCBaseline(gain);
884  const double onlineBaseline = pmt.GetCalibData().GetBaseline(gain);
885  const int n = baseline.GetSize();
886  for (int i = 0; i < n; ++i)
887  baseline[i] = onlineBaseline;
888  }
889 
890 
891  bool
892  SdCalibrator::ComputeBaselines(Station& station)
893  const
894  {
895  int doneSome = 0;
896  for (auto& pmt : station.PMTsRange(sdet::PMTConstants::eAnyType))
897  doneSome += ComputeBaseline(station, pmt, sdet::PMTConstants::eHighGain) +
898  ComputeBaseline(station, pmt, sdet::PMTConstants::eLowGain);
899 
900  return doneSome;
901  }
902 
903 
904 
905  bool
906  SdCalibrator::ComputeBaseline(const Station& station, PMT& pmt, const sdet::PMTConstants::PMTGain gain)
907  const
908  {
909  const auto& dStation = det::Detector::GetInstance().GetSDetector().GetStation(station);
910 
911  if (!pmt.HasFADCTrace() || !pmt.GetCalibData().IsTubeOk())
912  return false;
913 
914  if (gain == sdet::PMTConstants::eLowGain && !pmt.GetCalibData().IsLowGainOk())
915  return false;
916 
917  const auto& trace = pmt.GetFADCTrace(gain);
918  const int traceLength = trace.GetSize();
919 
920  if (!pmt.HasRecData())
921  pmt.MakeRecData();
922  auto& pmtRec = pmt.GetRecData();
923 
924  auto& flatPieces = pmtRec.GetBaselineFlatPieces(gain);
925  flatPieces.clear();
926 
927  int minLength = CalibrationParameters::GetMinLength(fIsUUB);
928 
929  const int saturationValue = dStation.GetSaturationValue();
930 
931  // increase signal variability
932  bool seenSaturation = false;
933  bool hitsZero = false;
934  int sigma = (fDecreaseLGFlatPieceTolerance && gain == sdet::PMTConstants::eLowGain) ? 1 : 2;
935 
936  do {
937  ++sigma;
938 
939  int startBin = 0;
940  int stopBin = 0;
941  do {
942 
943  const int startValue = trace[startBin];
944 
945  // find sigma-flat piece
946  while (stopBin < traceLength) {
947  if (abs(startValue - trace[stopBin]) > sigma)
948  break;
949  ++stopBin;
950  }
951 
952  if (startValue >= saturationValue) {
953  if (!startBin && stopBin == traceLength) {
954  // whole trace is saturated
955  flatPieces.push_back(PMTRecData::Piece(0, traceLength));
956  seenSaturation = true;
957  ostringstream warn;
958  warn << "Station " << station.GetId() << ", PMT " << pmt.GetId() << ", "
959  << (gain == sdet::PMTConstants::eHighGain ? "high" : "low")
960  << " gain (saturated): Whole trace saturated.";
961  WARNING(warn);
962  break;
963  } else {
964  // start again
965  stopBin = startBin;
966  minLength = 0.25 * CalibrationParameters::GetMinLength(fIsUUB);
967  if (sigma < 4)
968  sigma = 4;
969  seenSaturation = true;
970  }
971  }
972  if (!startValue && seenSaturation) {
973  // (under)saturation of undershoot: baseline is 0 till the end
974  flatPieces.push_back(PMTRecData::Piece(stopBin, traceLength));
975  startBin = stopBin = traceLength;
976  hitsZero = true;
977  }
978  if (stopBin-startBin < minLength) {
979  // nothing useful found
980  ++startBin;
981  stopBin = startBin;
982  } else {
983  // RB : propagate from end of flat piece back, window centered, to check
984  // if the start is found back, if not, try next bin as start
985  if (fApplyBackwardFlatPieceCheck) {
986 
987  const int reverseStartValue =
988  accumulate(&trace[startBin], &trace[stopBin], 0) / (stopBin - startBin);
989  int reverseBin = stopBin - 1;
990  while (reverseBin > startBin) {
991  if (abs(reverseStartValue - trace[reverseBin]) > sigma)
992  break;
993  --reverseBin;
994  }
995 
996  if (reverseBin == startBin) {
997  flatPieces.push_back(PMTRecData::Piece(startBin, stopBin));
998  startBin = stopBin;
999  } else {
1000  ++startBin;
1001  stopBin = startBin;
1002  }
1003 
1004  } else {
1005  flatPieces.push_back(PMTRecData::Piece(startBin, stopBin));
1006  startBin = stopBin;
1007  }
1008 
1009  }
1010 
1011  } while (stopBin < traceLength);
1012 
1013  // should have some pieces
1014  if (!flatPieces.empty() &&
1015  flatPieces[0].first > CalibrationParameters::GetUsefulBins(fIsUUB) &&
1016  sigma < 5) {
1017  // try again with larger sigma
1018  flatPieces.clear();
1019  sigma = 4;
1020  ostringstream warn;
1021  warn << "Station " << station.GetId() << ", PMT " << pmt.GetId() << ", "
1022  << (gain == sdet::PMTConstants::eHighGain ? "high" : "low") << " gain: "
1023  "No useful baseline found in the first "
1024  << CalibrationParameters::GetUsefulBins(fIsUUB) << " bins.";
1025  WARNING(warn);
1026  }
1027 
1028  } while (flatPieces.empty() && sigma <= saturationValue);
1029 
1030  if (flatPieces.empty()) {
1031  MakeFlatBaseline(pmt, gain);
1032  if (seenSaturation)
1033  pmtRec.SetFADCSaturatedBins(-1, gain);
1034  ostringstream warn;
1035  warn << "Station " << station.GetId() << ", PMT " << pmt.GetId() << ", "
1036  << (gain == sdet::PMTConstants::eHighGain ? "high" : "low") << " gain"
1037  << (seenSaturation ? " (saturated):" : ":")
1038  << "No baseline found; using LS value.";
1039  WARNING(warn);
1040  return false;
1041  }
1042 
1043  // compute baselines
1044  vector<double> flatPieceMean;
1045  flatPieceMean.reserve(flatPieces.size());
1046  double meanErrorMaxPiece = 0;
1047  int maxPieceLength = 0;
1048  for (const auto& fp : flatPieces) {
1049  const auto sigma = for_each(&trace[fp.first], &trace[fp.second], Accumulator::SampleStandardDeviation());
1050  flatPieceMean.push_back(sigma.GetAverage());
1051  if (sigma.GetN() > maxPieceLength) {
1052  maxPieceLength = sigma.GetN();
1053  meanErrorMaxPiece = sigma.GetStandardDeviation();
1054  }
1055  }
1056 
1057  if (hitsZero)
1058  flatPieceMean.back() = 0;
1059 
1060  pmtRec.SetFADCBaselineError(meanErrorMaxPiece, gain);
1061 
1062  if (sigma > 3 && !seenSaturation) {
1063  ostringstream warn;
1064  warn << "Station " << station.GetId() << ", PMT " << pmt.GetId() << ", "
1065  << (gain == sdet::PMTConstants::eHighGain ? "high" : "low") << " gain: "
1066  "Noisy baseline, sigma = " << sigma << ", RMS = " << meanErrorMaxPiece;
1067  WARNING(warn);
1068  }
1069 
1070  pmtRec.SetFADCBaselineWindow(sigma, gain);
1071 
1072  if (!pmtRec.HasFADCBaseline(gain))
1073  pmtRec.MakeFADCBaseline(gain);
1074  auto& baseline = pmtRec.GetFADCBaseline(gain);
1075 
1076  // this comes from the Torino PMT baseline study
1077  // done by Simone Maldera and Gianni Navarra, GAP-2005-006 and GAP-2005-025
1078  const double recoveryFactor = 0.000158;
1079 
1080  double previousBaseline = flatPieceMean[0];
1081 
1082  // beginning of online baseline, before first piece
1083  {
1084  double charge = 0;
1085  for (int i = flatPieces[0].first - 1; i >= 0; --i) {
1086  const double signal = trace[i] - previousBaseline;
1087  if (signal > 0)
1088  charge += signal;
1089  baseline[i] = previousBaseline + charge * recoveryFactor;
1090  }
1091  }
1092 
1093  // first piece
1094  for (unsigned int i = flatPieces[0].first; i < flatPieces[0].second; ++i)
1095  baseline[i] = previousBaseline;
1096 
1097  // hole-centric: previous piece | hole | next piece
1098  const unsigned int nPieces = flatPieces.size();
1099  for (unsigned int p = 1; p < nPieces; ++p) {
1100  const double nextBaseline = flatPieceMean[p];
1101  const int start = flatPieces[p-1].second;
1102  const int stop = flatPieces[p].first;
1103  const int holeLength = stop - start;
1104  const double deltaBaselinePerBin =
1105  (nextBaseline - previousBaseline) / holeLength;
1106  // charge in the hole
1107  double charge = 0;
1108  for (int i = start ; i < stop; ++i) {
1109  const double base = previousBaseline + (i - start) * deltaBaselinePerBin;
1110  const double signal = trace[i] - base;
1111  if (signal > 0)
1112  charge += signal;
1113  }
1114  const double totalCharge = charge;
1115  if (totalCharge / holeLength < 2) {
1116  // linear interpolation over bins
1117  for (int i = start ; i < stop; ++i)
1118  baseline[i] = previousBaseline + (i - start) * deltaBaselinePerBin;
1119  } else {
1120  const double deltaBaselinePerCharge =
1121  (nextBaseline - previousBaseline) / totalCharge;
1122  // interpolate in the hole according to the charge increase
1123  charge = 0;
1124  for (int i = start ; i < stop; ++i) {
1125  const double base = previousBaseline + (i - start) * deltaBaselinePerBin;
1126  const double signal = trace[i] - base;
1127  if (signal > 0)
1128  charge += trace[i] - base;
1129  baseline[i] = previousBaseline + charge * deltaBaselinePerCharge;
1130  }
1131  }
1132  // fill next piece
1133  for (unsigned int i = stop; i < flatPieces[p].second; ++i)
1134  baseline[i] = nextBaseline;
1135  previousBaseline = nextBaseline;
1136  }
1137 
1138  // fill end (if not there already)
1139  {
1140  double charge = 0;
1141  for (int i = flatPieces[nPieces-1].second; i < traceLength; ++i) {
1142  const double signal = trace[i] - previousBaseline;
1143  if (signal > 0)
1144  charge += signal;
1145  baseline[i] = previousBaseline - charge * recoveryFactor;
1146  }
1147  }
1148 
1149  if (seenSaturation)
1150  pmtRec.SetFADCSaturatedBins(-1, gain);
1151 
1152  // DV: fix for cyclon boards that send register data in the last 8 bins
1153  // of FADC traces: solution is to set baseline equal to FADC values in
1154  // these last bins so that the signal there becomes zero.
1155  if (IsTestStation(station.GetId()) &&
1156  (traceLength == int(dStation.GetFADCTraceLength()))) {
1157  const int registersStart = int(dStation.GetFADCTraceLength()) - 8;
1158  for (int i = registersStart; i < int(dStation.GetFADCTraceLength()); ++i)
1159  baseline[i] = trace[i];
1160  }
1161 
1162  return true;
1163  }
1164 
1165  bool
1166  SdCalibrator::MakeComponentVEMTraces(PMT& pmt)
1167  const
1168  {
1169  auto& pmtRec = pmt.GetRecData();
1170  const auto gainUsed = pmtRec.GetGainUsed();
1171  const double vemFactor =
1172  (gainUsed == sdet::PMTConstants::eHighGain ? 1 : pmtRec.GetGainRatio()) / pmtRec.GetVEMPeak();
1173  const auto& multiFADCTrace = pmt.GetMultiFADCTrace(gainUsed);
1174  if (multiFADCTrace.GetNLabels() < 2)
1175  return false;
1176 
1177  bool didComponents = false;
1178  for (const auto& component : multiFADCTrace) {
1179  const auto comp = static_cast<sevt::StationConstants::SignalComponent>(component.GetLabel());
1180 
1181  if (comp == sevt::StationConstants::eTotal)
1182  continue;
1183 
1184  if (!pmtRec.HasVEMTrace(comp))
1185  pmtRec.MakeVEMTrace(comp);
1186  auto& vemTrace = pmtRec.GetVEMTrace(comp);
1187 
1188  // The components are taken from the double-valued traces.
1189  const auto& fadcTrace = pmt.GetFADCTraceD(gainUsed, comp);
1190  const int n = fadcTrace.GetSize();
1191  const auto& baseline = pmtRec.GetFADCBaseline(gainUsed);
1192 
1193  // substract baseline from unsaturated trace
1195 
1196  const auto& fadcTrace = pmt.GetFADCTrace(gainUsed, comp);
1197  const int n2 = fadcTrace.GetSize();
1198 
1199  for (int i = 0; i < n2; ++i)
1200  vemTrace[i] = (fadcTrace[i] - baseline[i]) * vemFactor;
1201  } else
1202  for (int i = 0; i < n; ++i)
1203  vemTrace[i] = fadcTrace[i] * vemFactor;
1204 
1205  didComponents = true;
1206  }
1207 
1208  return didComponents;
1209  }
1210 
1211 
1212  int
1213  SdCalibrator::BuildSignals(Station& station)
1214  const
1215  {
1216  vector<const PMT*> validPMTs;
1217 
1218  bool didComponents = false;
1219 
1220  const auto& dStation = det::Detector::GetInstance().GetSDetector().GetStation(station);
1221 
1222  for (auto& pmt : station.PMTsRange(sdet::PMTConstants::eAnyType)) {
1223 
1224  if (pmt.HasCalibData() && pmt.GetCalibData().IsTubeOk() && pmt.HasFADCTrace()) {
1225 
1226  if (BuildSignals(pmt, dStation.GetFADCTraceLength(), dStation.GetSaturationValue()) && pmt.HasRecData()) {
1227 
1228  if (pmt.GetType() == sdet::PMTConstants::eWaterCherenkovLarge)
1229  validPMTs.push_back(&(pmt)); // since there are multiple such PMTs, they are processed later
1230  else if (pmt.GetType() == sdet::PMTConstants::eScintillator) {
1231  auto& scintillator = station.GetScintillator();
1232  if (!scintillator.HasMIPTrace())
1233  scintillator.MakeMIPTrace();
1234  else
1235  scintillator.GetMIPTrace().ResetAll();
1236  auto& mipTrace = scintillator.GetMIPTrace();
1237  const int traceLength = mipTrace.GetSize();
1238  const auto& pmtTrace = pmt.GetRecData().GetVEMTrace();
1239  for (int i = 0; i < traceLength; ++i)
1240  mipTrace[i] = pmtTrace[i];
1241  }
1242 
1243  }
1244  if (pmt.HasSimData() && MakeComponentVEMTraces(pmt))
1245  didComponents = true;
1246  }
1247  }
1248 
1249  if (!validPMTs.empty()) {
1250  const int nPMTs = validPMTs.size();
1251  if (!station.HasVEMTrace())
1252  station.MakeVEMTrace();
1253  else {
1254  // clear if it exists
1255  station.GetVEMTrace().ResetAll();
1256  }
1257  auto& vemTrace = station.GetVEMTrace();
1258  const int traceLength = vemTrace.GetSize();
1259  for (const auto& pmt : validPMTs) {
1260  const auto& pmtTrace = (pmt)->GetRecData().GetVEMTrace();
1261  for (int i = 0; i < traceLength; ++i)
1262  vemTrace[i] += pmtTrace[i] / nPMTs;
1263  }
1264  }
1265 
1266  if (didComponents)
1267  SumPMTComponents(station);
1268 
1269  return validPMTs.size();
1270  }
1271 
1272 
1273  bool
1274  SdCalibrator::BuildSignals(PMT& pmt, const unsigned int traceLength, const int saturationValue)
1275  const
1276  {
1277  const auto& highGainTrace = pmt.GetFADCTrace(sdet::PMTConstants::eHighGain);
1278 
1279  // check for saturation
1280  int highGainSaturatedBins = 0;
1281  for (unsigned int i = 0; i < traceLength; ++i)
1282  if (highGainTrace[i] >= saturationValue)
1283  ++highGainSaturatedBins;
1284 
1285  if (!pmt.HasRecData())
1286  pmt.MakeRecData();
1287  auto& pmtRec = pmt.GetRecData();
1288  pmtRec.SetFADCSaturatedBins(highGainSaturatedBins, sdet::PMTConstants::eHighGain);
1289 
1290  const auto& lowGainTrace = pmt.GetFADCTrace(sdet::PMTConstants::eLowGain);
1291  auto& pmtCalib = pmt.GetCalibData();
1292  const bool lgOK = pmtCalib.IsLowGainOk();
1293  const bool isSPMT = (pmt.GetType() == sdet::PMTConstants::eWaterCherenkovSmall);
1294 
1295  if (lgOK) {
1296  int lowGainSaturatedBins = 0;
1297  for (unsigned int i = 0; i < traceLength; ++i)
1298  if (lowGainTrace[i] >= saturationValue)
1299  ++lowGainSaturatedBins;
1300  pmtRec.SetFADCSaturatedBins(lowGainSaturatedBins, sdet::PMTConstants::eLowGain);
1301 
1302  const auto maxBins = CalibrationParameters::GetSaturatedBinsMaximum(fIsUUB);
1303  // The SmallPMT only presents the LowGain channel
1304  if (isSPMT) {
1305  if (lowGainSaturatedBins > maxBins) {
1306  pmtCalib.SetIsTubeOk(false);
1307  return false;
1308  }
1309  } else {
1310  if (highGainSaturatedBins > maxBins || lowGainSaturatedBins > maxBins ||
1311  (lowGainSaturatedBins && !highGainSaturatedBins)) {
1312  // this is for the case where we have all or almost all of the low gain
1313  // saturated and high gain saturated at the same time => no useful trace,
1314  // or low gain saturated but not the high gain...
1315  pmtCalib.SetIsTubeOk(false);
1316  return false;
1317  }
1318  }
1319  } else {
1320  // if low gain is broken and high gain is saturated or if SPMT => no useful trace
1321  if (highGainSaturatedBins || isSPMT) {
1322  pmtCalib.SetIsTubeOk(false);
1323  return false;
1324  }
1325  }
1326 
1327  const auto gainUsed =
1328  ((highGainSaturatedBins && lgOK) || isSPMT) ? sdet::PMTConstants::eLowGain : sdet::PMTConstants::eHighGain;
1329  pmtRec.SetGainUsed(gainUsed);
1330 
1331  if (!pmtRec.HasVEMTrace())
1332  pmtRec.MakeVEMTrace();
1333  auto& vemTrace = pmtRec.GetVEMTrace();
1334 
1335  // find signal(s)
1336  auto& rawSignals = pmtRec.GetRawSignals();
1337  rawSignals.clear();
1338 
1339  const double vemChargeFactor = pmtRec.GetVEMPeak() / pmtRec.GetVEMCharge();
1340 
1341  bool isTubeOK = true;
1342 
1343  const double gainFactor =
1344  (gainUsed == sdet::PMTConstants::eLowGain) ? pmtRec.GetGainRatio() : 1;
1345  const double gainPeakFactor = gainFactor / pmtRec.GetVEMPeak();
1346 
1347  const auto& trace = (gainUsed == sdet::PMTConstants::eLowGain) ? lowGainTrace : highGainTrace;
1348  const auto& baseline = pmtRec.GetFADCBaseline(gainUsed);
1349  const auto& highGainBaseline = pmtRec.GetFADCBaseline(sdet::PMTConstants::eHighGain);
1350 
1351  const auto findSignalThresholdMultiplier =
1353  const auto largeFADCThreshold = CalibrationParameters::GetLargeFADCThreshold(fIsUUB);
1354  const auto minFADCValue = CalibrationParameters::GetMinFADCValue(fIsUUB);
1355  int binsWithLargeSignal = 0;
1356  int binsWithSignal = 0;
1357  int binsOverThresh = 0;
1358  int start = 0;
1359  double max = 0;
1360  double charge = 0;
1361  for (int i = 0; i < int(traceLength); ++i) {
1362  const int fadc = trace[i];
1363  if (fadc > largeFADCThreshold)
1364  ++binsWithLargeSignal;
1365  const double fadcSignal = fadc - baseline[i];
1366  // before the minimum FADC value was 10 and thus it might reject small signals,
1367  // knowing that normal baseline fluctuations are at the level of 1-2 FADC bins,
1368  // and we have new triggers with small signals it is now 4
1369  if (fadcSignal > minFADCValue)
1370  ++binsWithSignal;
1371  const double signal = fadcSignal * gainPeakFactor;
1372  vemTrace[i] = signal;
1373 
1374  // allways on high gain, RB: not anymore
1375  const double testSignal =
1376  fTreatHGLGEqualInSignalSearch ? fadcSignal : highGainTrace[i] - highGainBaseline[i];
1377  if (testSignal > findSignalThresholdMultiplier * fFindSignalThreshold) {
1378  // first ?
1379  if (!binsOverThresh)
1380  start = i;
1381  ++binsOverThresh;
1382  charge += signal;
1383  if (signal > max)
1384  max = signal;
1385  } else {
1386  // require at least 2 bins
1387  if (binsOverThresh >= findSignalThresholdMultiplier * fFindSignalBinsAboveThreshold)
1388  rawSignals.push_back(SignalSegment(start, i, binsOverThresh, charge * vemChargeFactor, max));
1389  binsOverThresh = 0;
1390  max = 0;
1391  charge = 0;
1392  }
1393  }
1394 
1395  if (binsOverThresh >= findSignalThresholdMultiplier * fFindSignalBinsAboveThreshold) {
1396  rawSignals.push_back(SignalSegment(start, traceLength, binsOverThresh, charge * vemChargeFactor, max));
1397  if (binsWithLargeSignal > CalibrationParameters::GetBinsWithLargeSignalThreshold(fIsUUB) ||
1398  binsWithSignal < CalibrationParameters::GetBinsWithSignalThreshold(fIsUUB))
1399  isTubeOK = false;
1400  }
1401 
1402  if (!isTubeOK && !fIsUUB) {
1403  pmtCalib.SetIsTubeOk(false);
1404  rawSignals.clear();
1405  return false;
1406  }
1407 
1408  auto rawIt = rawSignals.begin();
1409  if (rawIt != rawSignals.end()) {
1410  // joined signals
1411  auto& signals = pmtRec.GetSignals();
1412  signals.clear();
1413  // put first in
1414  signals.push_back(*rawIt);
1415  const auto signalMaxDist = CalibrationParameters::GetSignalMaxDist(fIsUUB);
1416  for (++rawIt; rawIt != rawSignals.end(); ++rawIt) {
1417  auto& current = signals.back();
1418  const int dist = rawIt->fStart - current.fStop;
1419  const int maxDist = signalMaxDist + current.fBinsOverThresh;
1420  // join raw signals as long they match the conditions
1421  if (dist >= maxDist ||
1422  (0.3*rawIt->fCharge >= current.fCharge && rawIt->fMaxValue >= 5) ||
1423  !rawIt->fCharge) { // this one is probably not needed
1424  signals.push_back(*rawIt);
1425  } else {
1426  // add bins inbetween
1427  const double addCharge =
1428  accumulate(&vemTrace[current.fStop], &vemTrace[rawIt->fStart], rawIt->fCharge);
1429  current.fCharge += addCharge * vemChargeFactor;
1430  current.fStop = rawIt->fStop;
1431  current.fBinsOverThresh += rawIt->fBinsOverThresh;
1432  if (current.fMaxValue < rawIt->fMaxValue)
1433  current.fMaxValue = rawIt->fMaxValue;
1434  }
1435  }
1436  }
1437 
1438  return true;
1439  }
1440 
1441 
1442  template<typename T1, typename T2>
1443  class Interval : public pair<T1, T2> {
1444  public:
1445  Interval(const pair<T1, T2>& p) : pair<T1, T2>(p) { }
1446 
1447  bool operator<(const Interval& interval) const
1448  { return this->second < interval.first; }
1449 
1450  bool operator==(const Interval& interval) const
1451  { return interval.second > this->first && interval.first < this->second; }
1452 
1453  void
1454  Merge(const Interval& interval)
1455  {
1456  if (interval.first < this->first)
1457  this->first = interval.first;
1458  if (interval.second > this->second)
1459  this->second = interval.second;
1460  }
1461  };
1462 
1463 
1464  bool
1465  SdCalibrator::MergeSignals(Station& station)
1466  const
1467  {
1468  vector<PMT*> validPMTs;
1469  typedef set<Interval<int, int>> Sections;
1470  Sections sections;
1471 
1472  const auto& dStation = det::Detector::GetInstance().GetSDetector().GetStation(station);
1473 
1474  for (auto& pmt : station.PMTsRange()) {
1475 
1476  if (pmt.HasCalibData() &&
1477  pmt.GetCalibData().IsTubeOk() &&
1478  pmt.HasRecData()) {
1479 
1480  validPMTs.push_back(&pmt);
1481 
1482  auto& signals = pmt.GetRecData().GetSignals();
1483 
1484  for (const auto& sig : signals) {
1485 
1486  Interval<int, int> newSection(make_pair(sig.fStart, sig.fStop));
1487  // try to insert, then merge until insert succeeds
1488  for (pair<Sections::iterator, bool> where; ; ) {
1489  where = sections.insert(newSection);
1490  if (!where.second) {
1491  // insert failed
1492  newSection.Merge(*where.first);
1493  sections.erase(where.first);
1494  } else
1495  break;
1496  }
1497 
1498  }
1499 
1500  }
1501 
1502  }
1503 
1504  const int nPMTs = validPMTs.size();
1505  auto& stationSignals = station.GetSignals();
1506 
1507  // we have ordered set of overlapping interval unions
1508  const int traceLength = dStation.GetFADCTraceLength();
1509  const auto findSignalThresholdMultiplier =
1511  auto nextSectionIt = sections.begin();
1512  auto currentSectionIt = nextSectionIt;
1513  while (currentSectionIt != sections.end()) {
1514  // add 10 bins at the end (if possible)
1515  int newStop = currentSectionIt->second + 10;
1516  if (newStop > traceLength)
1517  newStop = traceLength;
1518  ++nextSectionIt;
1519  if (nextSectionIt != sections.end() && newStop > nextSectionIt->first)
1520  newStop = nextSectionIt->first;
1521  const int start = currentSectionIt->first;
1522  // fill station signals
1523  {
1524  const auto& vemTrace = station.GetVEMTrace();
1525  int binsOverThresh = 0;
1526  double charge = 0;
1527  for (const auto& pmtp : validPMTs) {
1528  const auto& pmt = *pmtp;
1529  const auto& pmtRec = pmt.GetRecData();
1530  const auto gainUsed = pmtRec.GetGainUsed();
1531 
1532  const auto& trace =
1533  pmt.GetFADCTrace(fTreatHGLGEqualInSignalSearch ? gainUsed : sdet::PMTConstants::eHighGain);
1534  const auto& baseline =
1535  pmtRec.GetFADCBaseline(fTreatHGLGEqualInSignalSearch ? gainUsed : sdet::PMTConstants::eHighGain);
1536 
1537  double vemSum = 0;
1538  for (int i = start; i < newStop; ++i) {
1539  if (trace[i] - baseline[i] >= findSignalThresholdMultiplier * fFindSignalThreshold)
1540  ++binsOverThresh;
1541  vemSum += vemTrace[i];
1542  }
1543  const double factor = pmtRec.GetVEMPeak() / pmtRec.GetVEMCharge();
1544  charge += vemSum * factor;
1545  }
1546  stationSignals.emplace_back(start, newStop, double(binsOverThresh)/nPMTs, charge/nPMTs);
1547  }
1548  ++currentSectionIt;
1549  }
1550 
1551  // Since the Scintillator only has one PMT, it's PMTRecData signals are the ScintillatorRecData
1552  // Signals and no merging is necessary. Direct copying occurs from the PMTRecData
1553  // SignalSegmentCollection to the ScintillatorRecData SignalSegmentCollection below.
1554  if (station.HasScintillator()) {
1555  const auto& pmt = station.GetScintillatorPMT();
1556  if (pmt.HasRecData()) {
1557  const auto& pmtSignals = station.GetScintillatorPMT().GetRecData().GetSignals();
1558  auto& scintillatorSignals = station.GetScintillator().GetSignals();
1559  scintillatorSignals.clear();
1560  for (auto const signal : pmtSignals)
1561  scintillatorSignals.push_back(signal);
1562  }
1563  }
1564 
1565  return true;
1566  }
1567 
1568 
1569  bool
1570  SdCalibrator::SelectSignal(Station& station)
1571  const
1572  {
1573  const auto& dStation = det::Detector::GetInstance().GetSDetector().GetStation(station);
1574 
1575  const auto& signals = station.GetSignals();
1576  const unsigned int nSignals = signals.size();
1577 
1578  if (!nSignals) {
1579  // no signals found, check for saturation
1580  // StationRecData ctor sets all other values to zero
1581  for (auto& pmt : station.PMTsRange()) {
1582  if (pmt.HasRecData() &&
1583  pmt.HasCalibData() && pmt.GetCalibData().IsTubeOk()) {
1584  const auto& pmtRec = pmt.GetRecData();
1585  if (pmtRec.GetFADCSaturatedBins(sdet::PMTConstants::eLowGain))
1586  station.SetLowGainSaturation();
1587  if (pmtRec.GetFADCSaturatedBins(sdet::PMTConstants::eHighGain))
1588  station.SetHighGainSaturation();
1589  }
1590  }
1591  return false;
1592  }
1593 
1594  int maxSignalIndex = 0;
1595  double maxSignal = signals[0].fCharge;
1596  for (unsigned int i = 1; i < nSignals; ++i) {
1597  if (maxSignal < signals[i].fCharge) {
1598  maxSignalIndex = i;
1599  maxSignal = signals[i].fCharge;
1600  }
1601  }
1602 
1603  const int start = signals[maxSignalIndex].fStart;
1604  const int stop = signals[maxSignalIndex].fStop;
1605 
1606  if (!station.HasRecData())
1607  station.MakeRecData();
1608  auto& stRec = station.GetRecData();
1609 
1610  stRec.SetSignalStartSlot(start);
1611  // note that end slot is (still) inclusive
1612  stRec.SetSignalEndSlot(stop - 1);
1613 
1614  if (station.HasScintillator()) {
1615 
1616  int scintillatorStart = 0;
1617  int scintillatorStop = 0;
1618 
1619  auto& scintillator = station.GetScintillator();
1620  const auto& scintillatorSignals = scintillator.GetSignals();
1621 
1622  const int length = dStation.GetFADCTraceLength();
1623 
1624  if (scintillatorSignals.empty() ||
1625  fIncludeWaterCherenkovDetectorInScintillatorStartStopDetermination) {
1626  const double timeOffset = dStation.GetScintillatorPMT().GetTimeOffset();
1627  const int traceBinOffset = floor(timeOffset / dStation.GetFADCBinSize());
1628  scintillatorStart = min(start + traceBinOffset, length);
1629  scintillatorStop = min(stop + traceBinOffset, length);
1630  }
1631 
1632  if (!scintillatorSignals.empty()) {
1633  // Redundant. Perhaps SignalSegmentCollection should be turned into a class
1634  // with functions such as GetMaxSignal that return the SignalSegment with the
1635  // maximum charge.
1636  int maxIndex = 0;
1637  double maxCharge = scintillatorSignals[0].fCharge;
1638  for (unsigned int i = 1, n = scintillatorSignals.size(); i < n; ++i) {
1639  if (maxCharge < scintillatorSignals[i].fCharge) {
1640  maxIndex = i;
1641  maxCharge = scintillatorSignals[i].fCharge;
1642  }
1643  }
1644  // start/stop considering Scintillator MIP trace
1645  const auto& maxSignal = scintillatorSignals[maxIndex];
1646  if (scintillatorStart && scintillatorStop) {
1647  scintillatorStart = max(0, min(scintillatorStart, maxSignal.fStart));
1648  scintillatorStop = min(length, max(scintillatorStop, maxSignal.fStop));
1649  } else {
1650  scintillatorStart = max(0, maxSignal.fStart);
1651  scintillatorStop = min(length, maxSignal.fStop);
1652  }
1653  }
1654 
1655  if (!scintillator.HasRecData())
1656  scintillator.MakeRecData();
1657  auto& scintillatorRec = scintillator.GetRecData();
1658 
1659  scintillatorRec.SetSignalStartSlot(scintillatorStart);
1660  // note that stop is inclusive in offline event
1661  scintillatorRec.SetSignalEndSlot(scintillatorStop - 1);
1662 
1663  }
1664 
1665  {
1666  const auto& vemTrace = station.GetVEMTrace();
1667  const auto peak = for_each(&vemTrace[start+1], &vemTrace[stop], Accumulator::Max<double>(vemTrace[start]));
1668  stRec.SetPeakAmplitude(peak.GetMax());
1669  }
1670 
1671  bool highGainSaturation = false;
1672  bool lowGainSaturation = false;
1673  int nPMTs = 0;
1674  double totalCharge = 0;
1675  double spmtCharge = 0;
1676  double spmtChargeErr = 0;
1677 
1678  Accumulator::SampleStandardDeviationN shapeStat;
1679  Accumulator::SampleStandardDeviationN riseStat;
1680  Accumulator::SampleStandardDeviationN fallStat;
1681  Accumulator::SampleStandardDeviationN t40Stat;
1682  Accumulator::SampleStandardDeviationN t50Stat;
1683  for (auto& pmt : station.PMTsRange(sdet::PMTConstants::eAnyType)) {
1684 
1685  if (pmt.HasCalibData() && pmt.GetCalibData().IsTubeOk()) {
1686  auto& pmtRec = pmt.GetRecData();
1687 
1688  if (pmt.GetType() == sdet::PMTConstants::eWaterCherenkovLarge) {
1689 
1690  if (pmtRec.GetFADCSaturatedBins(sdet::PMTConstants::eHighGain))
1691  highGainSaturation = true;
1692  if (pmtRec.GetFADCSaturatedBins(sdet::PMTConstants::eLowGain))
1693  lowGainSaturation = true;
1694 
1695  const auto& vemTrace = pmtRec.GetVEMTrace();
1696 
1697  double charge = accumulate(&vemTrace[start], &vemTrace[stop], 0.);
1698 
1699  ComputeShapeRiseFallPeak(pmtRec, dStation.GetFADCBinSize(), start, start, stop, charge);
1700  charge *= pmtRec.GetVEMPeak() / pmtRec.GetVEMCharge();
1701  pmtRec.SetTotalCharge(charge);
1702  totalCharge += charge;
1703  shapeStat(pmtRec.GetShapeParameter());
1704  riseStat(pmtRec.GetRiseTime());
1705  fallStat(pmtRec.GetFallTime());
1706  t40Stat(pmtRec.GetT40());
1707  t50Stat(pmtRec.GetT50());
1708  const double peak = pmtRec.GetPeakAmplitude();
1709  if (peak)
1710  pmtRec.SetAreaOverPeak(charge / peak);
1711  ++nPMTs;
1712 
1713  } else if (pmt.GetType() == sdet::PMTConstants::eScintillator) {
1714 
1715  auto& scintillator = station.GetScintillator();
1716 
1717  if (!scintillator.HasMIPTrace())
1718  scintillator.MakeMIPTrace();
1719 
1720  if (!scintillator.HasRecData())
1721  scintillator.MakeRecData();
1722  auto& scinRec = scintillator.GetRecData();
1723 
1724  const unsigned int scintillatorStart = scinRec.GetSignalStartSlot();
1725  const unsigned int scintillatorStop = scinRec.GetSignalEndSlot() + 1; // stop here is exclusive
1726 
1727  const auto& mipTrace = scintillator.GetMIPTrace();
1728 
1729  if (pmtRec.GetFADCSaturatedBins(sdet::PMTConstants::eHighGain))
1730  scintillator.SetHighGainSaturation();
1731  if (pmtRec.GetFADCSaturatedBins(sdet::PMTConstants::eLowGain))
1732  scintillator.SetLowGainSaturation();
1733 
1734  double charge = accumulate(&mipTrace[scintillatorStart], &mipTrace[scintillatorStop], 0.);
1735  ComputeShapeRiseFallPeak(pmtRec, dStation.GetFADCBinSize(), scintillatorStart, scintillatorStart, scintillatorStop, charge);
1736  charge *= pmtRec.GetVEMPeak() / pmtRec.GetVEMCharge();
1737  pmtRec.SetTotalCharge(charge);
1738  if (charge <= 0)
1739  charge = 0;
1740  scinRec.SetTotalSignal(charge, 0);
1741  scinRec.SetRiseTime(pmtRec.GetRiseTime(), 0);
1742 
1743  } else if (pmt.GetType() == sdet::PMTConstants::eWaterCherenkovSmall) {
1744 
1745  // Add SmallPMT saturation info to sevt::Station data
1746  if (pmtRec.GetFADCSaturatedBins(sdet::PMTConstants::eLowGain))
1747  station.SetSmallPMTSaturation();
1748 
1749  double peak = 0;
1750  double charge = 0;
1751  const int spmtStart = max(0, start - fBinsBeforeStartForSPMT);
1752 
1753  if (pmtRec.HasVEMTrace()) {
1754  const auto& vemTrace = pmtRec.GetVEMTrace();
1755  charge = accumulate(&vemTrace[spmtStart], &vemTrace[stop], 0.);
1756  ComputeShapeRiseFallPeak(pmtRec, dStation.GetFADCBinSize(), spmtStart, spmtStart, stop, charge);
1757  charge *= pmtRec.GetVEMPeak() / pmtRec.GetVEMCharge();
1758  peak = pmtRec.GetPeakAmplitude();
1759  }
1760  spmtCharge = charge;
1761  spmtChargeErr = charge * pmtRec.GetVEMChargeError() / pmtRec.GetVEMCharge();
1762 
1763  pmtRec.SetTotalCharge(spmtCharge, spmtChargeErr);
1764  if (peak > 0)
1765  pmtRec.SetAreaOverPeak(charge / peak);
1766 
1767  }
1768  }
1769  }
1770 
1771  // this was done on the pmt vem traces due to individual pmt vem peak/charge values
1772  totalCharge /= nPMTs; // only WCD large PMTs!
1773  stRec.SetTotalSignal(totalCharge);
1774 
1775  if (highGainSaturation)
1776  station.SetHighGainSaturation();
1777 
1778  if (lowGainSaturation) {
1779  station.SetLowGainSaturation();
1780 #if USE_SPMT_SIGNAL_AS_TOTAL
1781  // if LPMTs LowGain is saturated, use SmallPMT signal
1782  if (fIsUUB && station.HasSmallPMTData() &&
1783  station.GetSmallPMTData().IsTubeOk()) {
1784  if (spmtCharge > 0 && spmtChargeErr > 0)
1785  stRec.SetTotalSignal(spmtCharge, spmtChargeErr);
1786  else {
1787  station.GetSmallPMTData().SetIsTubeOk(false);
1788  ostringstream warn;
1789  warn << "Station " << station.GetId() << ": zero SmallPMT signal after successful calibration!";
1790  WARNING(warn);
1791  }
1792  }
1793 #endif
1794  }
1795 
1796  if (totalCharge <= 0)
1797  return false;
1798 
1799  if (nPMTs < 2) {
1800  stRec.SetShapeParameter(shapeStat.GetAverage(nPMTs), 0);
1801  stRec.SetRiseTime(riseStat.GetAverage(nPMTs), 0);
1802  stRec.SetFallTime(fallStat.GetAverage(nPMTs), 0);
1803  stRec.SetT40(t40Stat.GetAverage(nPMTs), 0);
1804  stRec.SetT50(t50Stat.GetAverage(nPMTs), 0);
1805  } else {
1806  stRec.SetShapeParameter(shapeStat.GetAverage(nPMTs),
1807  shapeStat.GetStandardDeviation(nPMTs));
1808  stRec.SetRiseTime(riseStat.GetAverage(nPMTs),
1809  riseStat.GetStandardDeviation(nPMTs));
1810  stRec.SetFallTime(fallStat.GetAverage(nPMTs),
1811  fallStat.GetStandardDeviation(nPMTs));
1812  stRec.SetT40(t40Stat.GetAverage(nPMTs),
1813  t40Stat.GetStandardDeviation(nPMTs));
1814  stRec.SetT50(t50Stat.GetAverage(nPMTs),
1815  t50Stat.GetStandardDeviation(nPMTs));
1816  }
1817 
1818  const auto& gps = station.GetGPSData();
1819 
1820  // timing of the trace END
1821  const TimeStamp gpsTime(gps.GetSecond(), gps.GetCorrectedNanosecond());
1822 
1823  const double fadcBinSize = dStation.GetFADCBinSize();
1824 
1825  const TimeInterval pldTimeOffset = station.GetTriggerData().GetPLDTimeOffset();
1826 
1827  // timing of the trace BEGINNING
1828  const TimeStamp traceTime = gpsTime + pldTimeOffset -
1829  TimeInterval(dStation.GetFADCTraceLength() * fadcBinSize);
1830  station.SetTraceStartTime(traceTime);
1831 
1832  // timing of the SIGNAL START
1833  const TimeStamp signalTime = traceTime + TimeInterval((start - 0.5) * fadcBinSize);
1834  stRec.SetSignalStartTime(signalTime);
1835 
1836  // timing of scintillator
1837  if (station.HasScintillator()) {
1838  auto& scintillator = station.GetScintillator();
1839  if (scintillator.HasRecData()) {
1840  auto& scinRec = scintillator.GetRecData();
1841  const TimeStamp scinSignalTime = traceTime + TimeInterval((scinRec.GetSignalStartSlot() - 0.5) * fadcBinSize);
1842  scinRec.SetSignalStartTime(scinSignalTime);
1843  }
1844  }
1845 
1846  return true;
1847  }
1848 
1849 }
utl::TraceD & GetVEMTrace(const StationConstants::SignalComponent source=StationConstants::eTotal)
Traces calibrated in VEM Peak.
Definition: PMTRecData.h:46
int GetId() const
Get the station Id.
bool HasRecData() const
Check for existenc of PMT reconstructed data object.
Definition: SEvent/PMT.h:53
constexpr T Sqr(const T &x)
Holds result of the quadratic fit.
class to hold data at PMT level
Definition: SEvent/PMT.h:28
total (shower and background)
void SetSignalStartSlot(const unsigned int slot)
Start time of the signal in time slots from beginning of trace.
bool IsTubeOk() const
Definition: PMTCalibData.h:29
PMTCalibData & GetCalibData()
Get object containing PMT calibration data.
Definition: SEvent/PMT.h:56
sdet::PMTConstants::PMTType GetType() const
Definition: SEvent/PMT.h:35
double GetExtremePositionError() const
void SetShapeParameter(const double shape)
Definition: PMTRecData.h:207
PMTRecData & GetRecData()
Get object containing PMT reconstructed data.
Definition: SEvent/PMT.h:48
SignalSegmentCollection & GetSignals()
void SetFallTime(const double fallTime, const double rms)
Definition: PMTRecData.h:177
sevt::StationRecData & GetRecData()
Get station level reconstructed data.
ScintillatorRecData & GetRecData()
Get object containing scintillator reconstructed data.
#define INFO(message)
Macro for logging informational messages.
Definition: ErrorLogger.h:161
void SetTraceStartTime(const utl::TimeStamp &Time)
Set absolute start time of the VEM trace.
void Init()
Initialise the registry.
Branch GetChild(const std::string &childName) const
Get child of this Branch by child name.
Definition: Branch.cc:211
void SetIsTubeOk(const bool ok=true)
Definition: SmallPMTData.h:27
int gain
Definition: dump1090.h:241
void MakeFADCBaseline(const sdet::PMTConstants::PMTGain gain=sdet::PMTConstants::eHighGain)
Definition: PMTRecData.h:72
unsigned int GetId() const
Return Id of the PMT.
Definition: SEvent/PMT.h:32
void ApplyTimeCorrection(StationGPSData &gpsData)
bool HasSmallPMTData() const
void MakeRecData()
Make PMT reconstructed data object.
Definition: SEvent/PMT.cc:33
A TimeStamp holds GPS second and nanosecond for some event.
Definition: TimeStamp.h:110
utl::TraceD & GetVEMTrace(const StationConstants::SignalComponent source=StationConstants::eTotal)
signal trace calibrated in [VEM charge]
Exception for reporting variable out of valid range.
SmallPMTCalibData & GetCalibData()
Definition: SmallPMTData.h:21
void SetPeakAmplitude(const double peak)
Definition: PMTRecData.h:206
const char * Plural(const T n)
Definition: String.h:104
unsigned int GetTick() const
FlatPieceCollection & GetBaselineFlatPieces(const sdet::PMTConstants::PMTGain gain=sdet::PMTConstants::eHighGain)
Definition: PMTRecData.h:91
oss<< "0b";oss<< ((x >> i)&1);return oss.str();}template< class S, class V > std::string Join(const S &sep, const V &v)
Definition: String.h:60
void SetFADCSaturatedBins(const int num, const sdet::PMTConstants::PMTGain gain=sdet::PMTConstants::eHighGain)
Definition: PMTRecData.h:220
#define max(a, b)
class to hold data at Station level
bool HasFADCTrace(const StationConstants::SignalComponent source=StationConstants::eTotal) const
Check if a FADC trace exists. Trace source may be specified.
Definition: SEvent/PMT.h:83
void SetLowGainSaturation(const bool sat=true)
total FADC trace, with no saturation applied by FADC/baseline simulator
class to hold reconstructed data at PMT level
Definition: PMTRecData.h:38
void SetT40(const double t40)
Definition: PMTRecData.h:182
ExponentialFitter< Histogram > MakeExponentialFitter(const Histogram &histogram, const T &start, const T &stop)
constexpr double nanosecond
Definition: AugerUnits.h:143
double abs(const SVector< n, T > &v)
SmallPMTData & GetSmallPMTData()
const double second
Definition: GalacticUnits.h:32
bool IsTubeOk() const
Definition: SmallPMTData.h:26
void MakeVEMTrace(const StationConstants::SignalComponent source=StationConstants::eTotal)
Make a VEM trace object.
sevt::StationGPSData & GetGPSData()
Get GPS data for the station.
bool HasVEMTrace(const StationConstants::SignalComponent source=StationConstants::eTotal) const
Check whether VEM trace exists.
utl::TraceD & GetFADCTraceD(const sdet::PMTConstants::PMTGain gain=sdet::PMTConstants::eHighGain, const StationConstants::SignalComponent source=StationConstants::eTotal)
Definition: SEvent/PMT.h:121
SizeType GetSize() const
Definition: Trace.h:156
Scintillator & GetScintillator()
#define WARNING(message)
Macro for logging warning messages.
Definition: ErrorLogger.h:163
QuadraticFitter< Histogram, ErrorPolicy > MakeQuadraticFitter(const Histogram &h, const ErrorPolicy)
void GetData(bool &b) const
Overloads of the GetData member template function.
Definition: Branch.cc:644
Holds information characterizing portions of traces with signals.
Definition: SignalSegment.h:17
sevt::StationCalibData & GetCalibData()
Get calibration data for the station.
utl::TraceI & GetFADCTrace(const sdet::PMTConstants::PMTGain gain=sdet::PMTConstants::eHighGain, const StationConstants::SignalComponent source=StationConstants::eTotal)
Definition: SEvent/PMT.h:72
bool IsLowGainOk() const
Definition: PMTCalibData.h:31
double GetBaseline(const sdet::PMTConstants::PMTGain gain=sdet::PMTConstants::eHighGain) const
Definition: PMTCalibData.h:41
bool operator<(const Interval &interval) const
Interval(const pair< T1, T2 > &p)
void Merge(const Interval &interval)
const utl::MultiTraceI & GetMultiFADCTrace(const sdet::PMTConstants::PMTGain gain=sdet::PMTConstants::eHighGain) const
Definition: SEvent/PMT.h:86
void MakeRecData()
Make station reconstructed data object.
double GetChi2() const
void ResetAll(const T &value=T())
Definition: Trace.h:160
void MakeMIPTrace(const StationConstants::SignalComponent source=StationConstants::eTotal)
Make a VEM trace object.
A TimeInterval is used to represent time elapsed between two events.
Definition: TimeInterval.h:43
ResultFlag
Flag returned by module methods to the RunController.
Definition: VModule.h:60
sdet::PMTConstants::PMTGain GetGainUsed() const
Definition: PMTRecData.h:164
bool HasRecData() const
Check whether station reconstructed data exists.
static CentralConfig * GetInstance()
Use this the first time you get an instance of central configuration.
bool operator==(const Interval &interval) const
double GetExtremePosition() const
total (shower and background)
bool HasScintillator() const
void SetT50(const double t50)
Definition: PMTRecData.h:183
int GetVersion() const
version of the onboard calibration
void SetSmallPMTSaturation(const bool sat=true)
bool IsInRange(const double x, const vector< double > &r)
PMT & GetSmallPMT()
sevt::StationTriggerData & GetTriggerData()
Get Trigger data for the station.
void SetRiseTime(const double riseTime, const double rms)
Definition: PMTRecData.h:172
SignalSegmentCollection & GetSignals()
void SetHighGainSaturation(const bool sat=true)
#define ERROR(message)
Macro for logging error messages.
Definition: ErrorLogger.h:165
std::pair< unsigned int, unsigned int > Piece
pieces of relative FADC flatnes in format [first, second)
Definition: PMTRecData.h:88
double GetSlopeError() const
bool HasSEvent() const
const std::string & GetMessage() const
Retrieve the message from the exception.
utl::Branch GetTopBranch(const std::string &id)
Get top branch for moduleConfigLink with given id (XML files)

, generated on Tue Sep 26 2023.