StationTriggerAlgorithm.h
Go to the documentation of this file.
1 #ifndef _sdet_StationTriggerAlgorithm_h_
2 #define _sdet_StationTriggerAlgorithm_h_
3 
4 #include <utl/Accumulator.h>
5 #include <sevt/StationTriggerData.h>
6 #include <sdet/StationTriggerInfo.h>
7 #include <sdet/UUBDownsampleFilter.h>
8 #include <utl/Math.h>
9 #include <vector>
10 
11 
12 namespace sdet {
13 
14  /*
15  DV: In the firmware, various thresholds are keept as integers while
16  baselines are implemented as fixed-point numbers. The latter we here keep as
17  doubles. The input signal is always an integer representing the ADC counts.
18  */
19 
20  namespace Trigger {
21 
22  // important note: "threshold" in all of the triggers is defined in such a
23  // way that the threshold is considered passed when a trace value is
24  // strictly larger (ie > and not >=) than the threshold. the same holds
25  // for other similar quantities, like minimal integral requirement
26 
27  class Threshold {
28  // simple treshold trigger
29  public:
30  explicit
31  Threshold(const int threshold,
32  const unsigned int multiplicity, const unsigned int n) :
33  fThreshold(n, threshold),
34  fMultiplicity(multiplicity)
35  { }
36 
37  template<class Array>
38  explicit
39  Threshold(const Array& thresholds,
40  const unsigned int multiplicity, const unsigned int n) :
41  fThreshold(&thresholds[0], &thresholds[n]),
42  fMultiplicity(multiplicity)
43  { }
44 
45  bool
46  operator()(const int* const v)
47  const
48  {
49  unsigned int coincidences = 0;
50  for (unsigned int i = 0, n = fThreshold.size(); i < n; ++i)
51  if (v[i] > fThreshold[i])
52  ++coincidences;
53  return coincidences >= fMultiplicity;
54  }
55 
56  protected:
57  std::vector<int> fThreshold;
58  const unsigned int fMultiplicity;
59  };
60 
61 
62  class ThresholdUp : public Threshold {
63  // the same as class Threshold but only on positive (up) slope
64  public:
65  explicit
66  ThresholdUp(const int threshold,
67  const unsigned int multiplicity, const unsigned int n) :
68  Threshold(threshold, multiplicity, n),
69  fPrevious(n, false)
70  { }
71 
72  template<class Array>
73  explicit
74  ThresholdUp(const Array& threshold,
75  const unsigned int multiplicity, const unsigned int n) :
76  Threshold(threshold, multiplicity, n),
77  fPrevious(n, false)
78  { }
79 
80  bool
81  operator()(const int* const v)
82  {
83  unsigned int coincidences = 0;
84  for (unsigned int i = 0, n = Threshold::fThreshold.size(); i < n; ++i) {
85  const auto t = (v[i] > Threshold::fThreshold[i]);
86  auto& p = fPrevious[i];
87  if (t && !p)
88  ++coincidences;
89  p = t;
90  }
91  return coincidences >= fMultiplicity;
92  }
93 
94  private:
95  std::vector<int> fPrevious;
96  };
97 
98 
99  template<typename T>
100  class Fifo {
101  // fifo to store adc history for various window operations
102  public:
103  Fifo() = default;
104 
105  explicit Fifo(const unsigned int length, const T& init = T()) : fFifo(length, init) { }
106 
107  void Assign(const unsigned int length, const T& init = T())
108  { fFifo.assign(length, init); }
109 
110  T
111  PushPop(const T& x)
112  {
113  fFifo.push_front(x);
114  const auto y = fFifo.back();
115  fFifo.pop_back();
116  return y;
117  }
118 
119  unsigned int GetLength() const { return fFifo.size(); }
120 
121  private:
122  std::deque<T> fFifo;
123  };
124 
125 
127  // time over threshold
128  public:
129  explicit
130  TimeOverThreshold(const int threshold,
131  const int minOccupancy, const unsigned int window,
132  const unsigned int multiplicity, const unsigned int n) :
133  fThreshold(threshold, multiplicity, n),
134  fMinOccupancy(minOccupancy),
135  fTriggersInWindow(window)
136  { }
137 
138  template<class Vector>
139  explicit
140  TimeOverThreshold(const Vector& thresholds,
141  const int minOccupancy, const unsigned int window,
142  const unsigned int multiplicity, const unsigned int n) :
143  fThreshold(thresholds, multiplicity, n),
144  fMinOccupancy(minOccupancy),
145  fTriggersInWindow(window)
146  { }
147 
148  bool
149  operator()(const int* const v)
150  {
151  // fifo
152  const auto trigger = fThreshold(v);
153  fCount += int(trigger) - int(fTriggersInWindow.PushPop(trigger));
154  // note that >= would be more appropriate here but in the
155  // firmware > is used with the fMinOccupancy definition
156  return fCount > fMinOccupancy;
157  }
158 
159  int GetCount() const { return fCount; }
160 
161  private:
163  const int fMinOccupancy;
164  int fCount = 0;
166  };
167 
168 
170  // leaky history integrator needed by ToTd and MoPS
171 
172  // note that the threshold for the integral is a double
173  public:
174  explicit
175  DecayingIntegral(const unsigned int window,
176  const double decrement, const double threshold,
177  const double baseline,
178  const double baselineTolerance, const double baselineStep,
179  const unsigned int n) :
180  fWindow(window),
181  fIntegral(n),
182  fIntegralQueue(n),
183  fBaseline(n, baseline),
184  fDecrement(decrement),
185  fThreshold(threshold),
186  fBaselineTolerance(baselineTolerance),
187  fBaselineStep(baselineStep)
188  { }
189 
190  template<class Array>
191  explicit
192  DecayingIntegral(const unsigned int window,
193  const double decrement, const double threshold,
194  const Array& baseline,
195  const double baselineTolerance, const double baselineStep,
196  const unsigned int n) :
197  fWindow(window),
198  fIntegral(n),
199  fIntegralQueue(n),
200  fBaseline(&baseline[0], &baseline[n]),
201  fDecrement(decrement),
202  fThreshold(threshold),
203  fBaselineTolerance(baselineTolerance),
204  fBaselineStep(baselineStep)
205  { }
206 
207  void
208  operator()(const int* const v)
209  {
210  const unsigned int n = fIntegral.size();
211  // init on first call
212  if (!fIntegralQueue.front().GetLength()) {
213  for (unsigned int i = 0; i < n; ++i)
214  fIntegralQueue[i].Assign(fWindow, 0);
215  }
216  for (unsigned int i = 0; i < n; ++i) {
217  auto& baseline = fBaseline[i];
218  const auto& value = v[i];
219  UpdateBaseline(baseline, value);
220  auto& currentIntegral = fIntegral[i];
221  const auto windowIntegral = fIntegralQueue[i].PushPop(currentIntegral);
222  currentIntegral += value - baseline - fDecrement;
223  if (windowIntegral > fThreshold || currentIntegral < 0)
224  currentIntegral = 0;
225  const auto twiceThreshold = 2*fThreshold;
226  if (currentIntegral > twiceThreshold)
227  currentIntegral = twiceThreshold;
228  }
229  }
230 
231  bool HasTriggered(const unsigned int i) const { return fIntegral[i] > fThreshold; }
232 
233  private:
234  void
235  UpdateBaseline(double& baseline, const int v)
236  {
237  if (v > baseline) {
238  if (v > baseline + fBaselineTolerance)
239  baseline += 2*fBaselineStep;
240  else
241  baseline += fBaselineStep;
242  } else if (v < baseline) {
243  if (v < baseline - fBaselineTolerance)
244  baseline -= 2*fBaselineStep;
245  else
246  baseline -= fBaselineStep;
247  }
248  }
249 
250  const unsigned int fWindow;
251  std::vector<double> fIntegral;
252  std::vector<Fifo<double>> fIntegralQueue;
253  std::vector<double> fBaseline;
254  const double fDecrement;
255  const double fThreshold;
256  const double fBaselineTolerance;
257  const double fBaselineStep;
258  };
259 
260 
262  // time over threshold with deconvolved trace
263 
264  // works on deconvolved trace; note that compared to the
265  // TimeOverThreshold trigger, the coincidence and the multiplicity
266  // conditions are swapped, ie n TimeOverThreshold triggers are run with
267  // multiplicity=1 on n individual deconvolved traces and bound together
268  // with the multiplicity condition given as the ctor parameter
269  private:
271  public:
272  Deconvolution(const int decayFD, const int decayFN)
273  : fFD(decayFD), fFN(decayFN) { }
274 
275  int
276  operator()(const int current, const int previous)
277  const
278  {
279  const int x = std::max(current*64 - previous*fFD, 0);
280  constexpr int half = 1 << 9;
281  return std::min((x*fFN + half) >> 10, 4096);
282  }
283 
284  private:
285  const int fFD;
286  const int fFN;
287  };
288 
289  public:
290  explicit
291  TimeOverThresholdDeconvolved(const int threshold,
292  const int decayFD, const int decayFN,
293  const unsigned int minCount, const unsigned int window,
294  const unsigned int multiplicity, const unsigned int n) :
295  fDeconvolve(decayFD, decayFN),
296  fMultiplicity(multiplicity)
297  {
298  fTimeOverThreshold.reserve(n);
299  for (unsigned int i = 0; i < n; ++i)
300  fTimeOverThreshold.emplace_back(threshold, minCount, window, 1, 1);
301  }
302 
303  template<class Array>
304  explicit
306  const int decayFD, const int decayFN,
307  const unsigned int minCount, const unsigned int window,
308  const unsigned int multiplicity, const unsigned int n) :
309  fDeconvolve(decayFD, decayFN),
310  fMultiplicity(multiplicity)
311  {
312  fTimeOverThreshold.reserve(n);
313  for (unsigned int i = 0; i < n; ++i)
314  fTimeOverThreshold.emplace_back(threshold[i], minCount, window, 1, 1);
315  }
316 
317  bool
318  operator()(const int* const v, const DecayingIntegral& integ)
319  {
320  const unsigned int n = fTimeOverThreshold.size();
321  // init on first call
322  if (fPrevious.empty())
323  fPrevious.assign(v, v + n);
324  unsigned int mult = 0;
325  for (unsigned int i = 0; i < n; ++i) {
326  const int value = v[i];
327  const int decon = fDeconvolve(value, fPrevious[i]);
328  fPrevious[i] = value;
329  mult += (unsigned int)(fTimeOverThreshold[i](&decon) && integ.HasTriggered(i));
330  }
331  return mult >= fMultiplicity;
332  }
333 
334  private:
336  std::vector<int> fPrevious;
337  std::vector<TimeOverThreshold> fTimeOverThreshold;
338  const unsigned int fMultiplicity;
339  };
340 
341 
343  // mops helper class running on a single trace
344  public:
345  PositiveStepCounter(const int occupancy, const unsigned int window,
346  const int minUp, const int maxUp,
347  const int vetoOffset) :
348  fOccupancy(occupancy),
349  fWindow(window),
350  fMinUp(minUp),
351  fMaxUp(maxUp),
352  fVetoOffset(vetoOffset)
353  { }
354 
355  bool
356  operator()(const int v)
357  {
358  if (fVeto) {
359  fStart = v;
360  --fVeto;
361  return Update(v, false);
362  } else if (v >= fPrevious) {
363  // positive (or zero) increase
364  return Update(v, false);
365  } else {
366  // negative drop
367  const int jump = fPrevious - fStart;
368  if (jump > 0)
369  fVeto = std::max(0, IntLog2(jump) - fVetoOffset);
370  fStart = v;
371  return Update(v, fMinUp < jump && jump <= fMaxUp);
372  }
373  }
374 
375  private:
376  bool
377  Update(const int v, const bool isStep)
378  {
379  fPrevious = v;
380  const auto prevCounts = fCounts;
381  fCounts += int(isStep) - int(fWindow.PushPop(isStep));
382  return prevCounts > fOccupancy;
383  }
384 
385  static
386  int
387  IntLog2(unsigned int x)
388  {
389  if (x == 0)
390  return -1; // kind of -inf
391 #ifdef __GNUG__
392  typedef std::numeric_limits<decltype(x)> nl;
393  return nl::digits + nl::is_signed - 1 - __builtin_clz(x);
394 #else
395  int r = 0;
396  while (x >>= 1)
397  ++r;
398  return r;
399 #endif
400  }
401 
402  const int fOccupancy;
404  const int fMinUp;
405  const int fMaxUp;
406  const int fVetoOffset;
407  int fPrevious = 0;
408  int fStart = 0;
409  int fVeto = 1;
410  int fCounts = 0;
411  };
412 
413 
415  // multiplicity of positive steps
416  public:
417  MultiplicityOfPositiveSteps(const int occupancy, const unsigned int window,
418  const int minUp, const int maxUp, const int vetoOffset,
419  const unsigned int multiplicity,
420  const unsigned int n) : // =1 only for UB FADC traces
421  fStepCounter(n, { occupancy, window, minUp, maxUp, vetoOffset }),
422  fMultiplicity(multiplicity)
423  { }
424 
425  bool
426  operator()(const int* const v, const DecayingIntegral& integ)
427  {
428  unsigned int trig = 0;
429  for (unsigned int i = 0, n = fStepCounter.size(); i < n; ++i)
430  trig += (unsigned int)(fStepCounter[i](v[i]) && integ.HasTriggered(i));
431  return trig >= fMultiplicity;
432  }
433 
434  private:
435  std::vector<PositiveStepCounter> fStepCounter;
436  const unsigned int fMultiplicity;
437  };
438 
439  }
440 
441 
454 
455  public:
457  const Trigger::ThresholdUp& t2th,
458  const Trigger::TimeOverThreshold& tot,
459  const Trigger::DecayingIntegral& integ,
462  const int latchBin, const int traceLength) :
463  fT1ThresholdTrigger(t1th),
464  fT2ThresholdTrigger(t2th),
465  fTOTTrigger(tot),
466  fIntegral(integ),
467  fTOTdTrigger(totd),
468  fMOPSTrigger(mops),
469  fLatchBin(latchBin),
470  fTraceLength(traceLength)
471  { }
472 
473  template<typename T, template<typename> class Trace> // trace here is either Trace or TimeDistribution
474  std::vector<StationTriggerInfo>
475  Run(const int start, const int stop, const std::vector<const Trace<T>*>& traces)
476  {
477  const double binning = 25*utl::nanosecond;
478  if (std::fabs(traces.front()->GetBinning() - binning)/binning < 1e-3)
479  return RunUB(start, stop, fLatchBin, traces.size(), &traces.front(), fTraceLength);
480 
481  // UUB
482  const double uubBinning = binning / 3;
483  if (std::fabs(traces.front()->GetBinning() - uubBinning)/uubBinning > 1e-3) {
484  std::ostringstream err;
485  err << "The implemented trigger algorithms work correctly only for "
486  << binning/utl::nanosecond << " ns or " << uubBinning/utl::nanosecond << " ns sampling rate "
487  "while " << traces.front()->GetBinning()/utl::nanosecond << " ns was received";
488  throw utl::DoesNotComputeException(err.str());
489  }
490  const auto downsampledTraces = UUBDownsampleFilter(traces);
491  const auto newStart = utl::FloorDiv(start, 3);
492  const auto newStop = utl::FloorDiv(stop, 3);
493  auto triggerInfos = RunUB(newStart, newStop, fLatchBin/3, downsampledTraces, fTraceLength/3 + 1);
494  // scale back to UUB
495  for (auto& info : triggerInfos) {
496  const int scaledStart = 3*info.GetStart();
497  const int scaledLatch = 3*info.GetLatch();
498  const int scaledStop = scaledStart + fTraceLength;
499  info.SetStartLatchStop(scaledStart, scaledLatch, scaledStop);
500  }
501  return triggerInfos;
502  }
503 
504  private:
505  template<typename Trace>
506  std::vector<StationTriggerInfo>
507  RunUB(const int start, const int stop, const int latch,
508  const std::vector<Trace>& traces, const int traceLength)
509  {
510  std::vector<const Trace*> tracePtrs;
511  for (const auto& t : traces)
512  tracePtrs.push_back(&t);
513  return RunUB(start, stop, latch, tracePtrs.size(), &tracePtrs.front(), traceLength);
514  }
515 
521  template<typename Trace>
522  std::vector<StationTriggerInfo>
523  RunUB(const int start, const int stop, const int latch,
524  const int n, const Trace* const traces, const int traceLength)
525  {
526  std::vector<StationTriggerInfo> info;
527 
528  int i = start;
529  do {
531  utl::Accumulator::Max<bool> isT2Th(false);
532  // find latch triggers first
533  for ( ; i < stop; ++i) {
534  int values[n];
535  for (int j = 0; j < n; ++j)
536  values[j] = (*traces[j])[i];
537 
538  if (fT1ThresholdTrigger(values))
540  isT2Th(fT2ThresholdTrigger(values));
541  if (fTOTTrigger(values))
543  fIntegral(values);
544  if (fTOTdTrigger(values, fIntegral))
546  if (fMOPSTrigger(values, fIntegral))
548  // latch if any trigger found
550  break;
551  }
552 
553  // nothing found
555  return info;
556 
557  // latch
558  const int trigLatch = i;
559  const int trigStart = trigLatch - latch + 1;
560  const int trigStop = trigStart + traceLength;
561 
562  const int iMax = std::min(trigStop, stop);
563 
564  // any other triggers in the rest of the triggered trace?
565  for (++i; i < iMax; ++i) {
566  int values[n];
567  for (int j = 0; j < n; ++j)
568  values[j] = (*traces[j])[i];
569  if (fT1ThresholdTrigger(values))
571  isT2Th(fT2ThresholdTrigger(values));
572  if (fTOTTrigger(values))
574  fIntegral(values);
575  if (fTOTdTrigger(values, fIntegral))
577  if (fMOPSTrigger(values, fIntegral))
579  }
580 
581  info.emplace_back(pld, isT2Th.GetMax(), trigStart, trigLatch, trigStop);
582 
583  // we do not simulate trigger dead-time although it definitely can be observed in the data
584 
585  } while (i < stop);
586 
587  return info;
588  }
589 
596  int fLatchBin = 0;
597  int fTraceLength = 0;
598 
599  };
600 
601 }
602 
603 
604 #endif
bool operator()(const int *const v) const
Trigger::TimeOverThreshold fTOTTrigger
Threshold(const int threshold, const unsigned int multiplicity, const unsigned int n)
void UpdateBaseline(double &baseline, const int v)
std::vector< StationTriggerInfo > RunUB(const int start, const int stop, const int latch, const int n, const Trace *const traces, const int traceLength)
std::vector< PositiveStepCounter > fStepCounter
StationTriggerAlgorithm(const Trigger::ThresholdUp &t1th, const Trigger::ThresholdUp &t2th, const Trigger::TimeOverThreshold &tot, const Trigger::DecayingIntegral &integ, const Trigger::TimeOverThresholdDeconvolved &totd, const Trigger::MultiplicityOfPositiveSteps &mops, const int latchBin, const int traceLength)
std::vector< TimeOverThreshold > fTimeOverThreshold
std::vector< Fifo< double > > fIntegralQueue
int operator()(const int current, const int previous) const
const char nl
Definition: SdInspector.cc:36
#define max(a, b)
Fifo(const unsigned int length, const T &init=T())
TimeOverThresholdDeconvolved(const Array &threshold, const int decayFD, const int decayFN, const unsigned int minCount, const unsigned int window, const unsigned int multiplicity, const unsigned int n)
std::vector< StationTriggerInfo > RunUB(const int start, const int stop, const int latch, const std::vector< Trace > &traces, const int traceLength)
constexpr double nanosecond
Definition: AugerUnits.h:143
utl::TraceI UUBDownsampleFilter(const utl::TraceI &trace, const int phase=1)
TimeOverThresholdDeconvolved(const int threshold, const int decayFD, const int decayFN, const unsigned int minCount, const unsigned int window, const unsigned int multiplicity, const unsigned int n)
ThresholdUp(const Array &threshold, const unsigned int multiplicity, const unsigned int n)
Trigger::MultiplicityOfPositiveSteps fMOPSTrigger
constexpr int FloorDiv(const int num, const int den)
Trigger::DecayingIntegral fIntegral
TimeOverThreshold(const int threshold, const int minOccupancy, const unsigned int window, const unsigned int multiplicity, const unsigned int n)
Local station hardware (PLD) and software (T2) trigger algorithm.
ThresholdUp(const int threshold, const unsigned int multiplicity, const unsigned int n)
Base class for inconsistency/illogicality exceptions.
TimeOverThreshold(const Vector &thresholds, const int minOccupancy, const unsigned int window, const unsigned int multiplicity, const unsigned int n)
bool operator()(const int *const v)
unsigned int GetLength() const
void Assign(const unsigned int length, const T &init=T())
bool HasTriggered(const unsigned int i) const
DecayingIntegral(const unsigned int window, const double decrement, const double threshold, const double baseline, const double baselineTolerance, const double baselineStep, const unsigned int n)
PositiveStepCounter(const int occupancy, const unsigned int window, const int minUp, const int maxUp, const int vetoOffset)
std::vector< StationTriggerInfo > Run(const int start, const int stop, const std::vector< const Trace< T > * > &traces)
Threshold(const Array &thresholds, const unsigned int multiplicity, const unsigned int n)
bool operator()(const int *const v, const DecayingIntegral &integ)
Trigger::TimeOverThresholdDeconvolved fTOTdTrigger
MultiplicityOfPositiveSteps(const int occupancy, const unsigned int window, const int minUp, const int maxUp, const int vetoOffset, const unsigned int multiplicity, const unsigned int n)
TabulatedPDF::Array Array
Definition: TabulatedPDF.cc:15
bool Update(const int v, const bool isStep)
bool operator()(const int *const v, const DecayingIntegral &integ)
DecayingIntegral(const unsigned int window, const double decrement, const double threshold, const Array &baseline, const double baselineTolerance, const double baselineStep, const unsigned int n)

, generated on Tue Sep 26 2023.