Belle II Software development
TimeDigitizer.cc
1/**************************************************************************
2 * basf2 (Belle II Analysis Software Framework) *
3 * Author: The Belle II Collaboration *
4 * *
5 * See git log for contributors and copyright holders. *
6 * This file is licensed under LGPL-3.0, see LICENSE.md. *
7 **************************************************************************/
8
9#include <top/modules/TOPDigitizer/TimeDigitizer.h>
10#include <top/geometry/TOPGeometryPar.h>
11#include <mdst/dataobjects/MCParticle.h>
12#include <framework/logging/Logger.h>
13#include <TRandom.h>
14
15using namespace std;
16
17namespace Belle2 {
22 namespace TOP {
23
27 unsigned TimeDigitizer::s_window = 0;
29 DBObjPtr<TOPCalTimeWalk>* TimeDigitizer::s_timeWalk = 0;
30
31 TimeDigitizer::TimeDigitizer(int moduleID, int pixelID, double timeOffset,
32 double calErrorsSq, int shift, double rmsNoise,
33 const TOPSampleTimes& sampleTimes):
34 m_moduleID(moduleID), m_pixelID(pixelID), m_timeOffset(timeOffset),
35 m_calErrorsSq(calErrorsSq), m_rmsNoise(rmsNoise), m_sampleTimes(&sampleTimes),
36 m_windowShift(shift)
37 {
38 const auto& channelMapper = TOPGeometryPar::Instance()->getChannelMapper();
39 m_channel = channelMapper.getChannel(pixelID);
40 if (!channelMapper.isChannelValid(m_channel)) {
41 B2ERROR("TimeDigitizer::TimeDigitizer: invalid channel");
42 return;
43 }
44
45 unsigned bs = 0;
46 channelMapper.splitChannelNumber(m_channel, bs, m_carrier, m_asic, m_chan);
47
48 const auto& frontEndMapper = TOPGeometryPar::Instance()->getFrontEndMapper();
49 const auto* map = frontEndMapper.getMap(m_moduleID, bs);
50 if (!map) {
51 B2ERROR("TimeDigitizer::TimeDigitizer: no valid frontend map found");
52 return;
53 }
54
55 m_scrodID = map->getScrodID();
56 m_valid = true;
57
58 }
59
60 void TimeDigitizer::addTimeOfHit(double t, double pulseHeight, EType type,
61 const TOPSimHit* simHit)
62 {
63 Hit hit;
64 hit.pulseHeight = pulseHeight;
65 hit.type = type;
66 hit.simHit = simHit;
67 switch (type) {
68 case static_cast<int>(c_Hit):
70 break;
71 case static_cast<int>(c_ChargeShare):
73 break;
74 case static_cast<int>(c_CrossTalk):
75 hit.shape = 0;
76 B2ERROR("TOP::TimeDigitizer: waveform shape of cross-talk not yet available");
77 break;
78 case static_cast<int>(c_CalPulse):
80 break;
81 default:
82 hit.shape = 0;
83 }
84 m_times.insert(std::pair<double, const Hit>(t, hit));
85 }
86
87 //-------- simplified pile-up and double-hit-resolution model ------- //
88 // this function will probably be removed in the future, therefore I don't
89 // care about some hard-coded values
90
91 typedef std::multimap<double, const TimeDigitizer::Hit>::const_iterator Iterator;
92
95 int threshold,
96 int thresholdCount,
97 double timeJitter) const
98 {
99
100 if (m_times.empty()) return;
101
102 // get parameters of the model
103 const auto* geo = TOPGeometryPar::Instance()->getGeometry();
104 const auto& tdc = geo->getNominalTDC();
105 double hitResolveTime = tdc.getDoubleHitResolution();
106 double pileupTime = tdc.getPileupTime();
107
108 // split time pattern into multiple hits (according to double-hit resolution)
109 std::vector<Iterator> ranges;
110 ranges.push_back(m_times.begin());
111 double prevTime = m_times.begin()->first;
112 for (Iterator it = m_times.begin(); it != m_times.end(); ++it) {
113 if (it->first - prevTime > hitResolveTime) ranges.push_back(it);
114 prevTime = it->first;
115 }
116 ranges.push_back(m_times.end());
117
118 // loop over splitted regions
119 for (unsigned k = 0; k < ranges.size() - 1; k++) {
120
121 // temporary containers
122 std::vector<double> times;
123 std::vector<double> weights;
124 std::vector<const TOPSimHit*> simHits;
125
126 // take only hits within pileup time and discard others
127 prevTime = ranges[k]->first;
128 for (Iterator it = ranges[k]; it != ranges[k + 1]; ++it) {
129 if (it->first - prevTime > pileupTime) break;
130 times.push_back(it->first);
131 double pulseHeight = gRandom->Gaus(it->second.pulseHeight, m_rmsNoise);
132 weights.push_back(pulseHeight);
133 simHits.push_back(it->second.simHit);
134 prevTime = it->first;
135 }
136
137 // determine pulse height
138 double height = 0;
139 for (const auto& weight : weights) height += weight;
140
141 // set weights
142 for (auto& weight : weights) weight /= height;
143
144 // generate pulse width
145 double width = gRandom->Gaus(2.3, 0.4);
146 if (width < 0.5) width = 0.5;
147
148 // determine detection time
149 double time = 0;
150 for (unsigned j = 0; j < times.size(); j++) time += times[j] * weights[j];
151
152 // add additional time jitter
153 if (timeJitter > 0) time += gRandom->Gaus(0., timeJitter);
154
155 // emulate feature extraction data
156
157 double mean = time + width / 2;
158 double sigma = width / 2.35482;
159
160 int samplePeak = tdc.getSample(mean);
161 if (mean - tdc.getSampleTime(samplePeak) > tdc.getSampleWidth() / 2) samplePeak++;
162 short vPeak = height * gauss(tdc.getSampleTime(samplePeak), mean, sigma);
163 if (vPeak < threshold) continue;
164 if (vPeak > 3000) vPeak = 3000; // saturation - roughly (e.g. 4096 - pedestal)
165
166 double halfWid = sigma * sqrt(-2 * log(0.5 * vPeak / height));
167 int sampleRise = tdc.getSample(mean - halfWid);
168 if (!tdc.isSampleValid(sampleRise)) continue;
169 short vRise0 = height * gauss(tdc.getSampleTime(sampleRise), mean, sigma);
170 short vRise1 = height * gauss(tdc.getSampleTime(sampleRise + 1), mean, sigma);
171
172 int sampleFall = tdc.getSample(mean + halfWid);
173 if (!tdc.isSampleValid(sampleFall + 1)) continue;
174 short vFall0 = height * gauss(tdc.getSampleTime(sampleFall), mean, sigma);
175 short vFall1 = height * gauss(tdc.getSampleTime(sampleFall + 1), mean, sigma);
176
177 if (threshold > 0) {
178 double halfDt = sigma * sqrt(-2 * log(threshold / height));
179 int overThr = tdc.getSample(mean + halfDt) - tdc.getSample(mean - halfDt);
180 if (overThr < thresholdCount) continue;
181 }
182
183 // determine integral
184 int numSamples = (sampleFall - sampleRise) * 4; // according to topcaf
185 double sigmaIntegral = 0;
186 if (numSamples > 1) sigmaIntegral = m_rmsNoise * sqrt(numSamples - 1);
187 double integral = gRandom->Gaus(height * 7.0, sigmaIntegral);
188
189 // append new raw digit
190 auto* rawDigit = rawDigits.appendNew(m_scrodID, TOPRawDigit::c_MC);
191 rawDigit->setCarrierNumber(m_carrier);
192 rawDigit->setASICNumber(m_asic);
193 rawDigit->setASICChannel(m_chan);
194 rawDigit->setASICWindow(s_window);
195 rawDigit->setSampleRise(sampleRise);
196 rawDigit->setDeltaSamplePeak(samplePeak - sampleRise);
197 rawDigit->setDeltaSampleFall(sampleFall - sampleRise);
198 rawDigit->setValueRise0(vRise0);
199 rawDigit->setValueRise1(vRise1);
200 rawDigit->setValueFall0(vFall0);
201 rawDigit->setValueFall1(vFall1);
202 rawDigit->setValuePeak(vPeak);
203 rawDigit->setIntegral(integral);
204
205 double rawTime = rawDigit->getCFDLeadingTime();
206 double cfdTime = rawTime * tdc.getSampleWidth() - tdc.getOffset();
207 double cfdWidth = rawDigit->getFWHM() * tdc.getSampleWidth();
208 int sampleDivisions = 0x1 << tdc.getSubBits();
209 int tfine = int(rawTime * sampleDivisions) % sampleDivisions;
210 if (tfine < 0) tfine += sampleDivisions;
211 rawDigit->setTFine(tfine);
212
213 // append new digit
214
215 auto* digit = digits.appendNew(m_moduleID, m_pixelID, rawTime);
216 digit->setTime(cfdTime);
217 digit->setTimeError(timeJitter);
218 digit->setPulseHeight(rawDigit->getValuePeak());
219 digit->setIntegral(rawDigit->getIntegral());
220 digit->setPulseWidth(cfdWidth);
221 digit->setChannel(m_channel);
222 digit->setFirstWindow(rawDigit->getASICWindow());
223 digit->setStatus(TOPDigit::c_OffsetSubtracted);
224 if (m_sampleTimes->isCalibrated()) {
225 digit->addStatus(TOPDigit::c_TimeBaseCalibrated);
226 }
227 digit->addRelationTo(rawDigit);
228
229 // set relations to simulated hits and MC particles
230
231 std::map<MCParticle*, double> relatedMCParticles;
232 for (unsigned j = 0; j < simHits.size(); j++) {
233 const auto* simHit = simHits[j];
234 const double& weight = weights[j];
235 if (simHit) {
236 digit->addRelationTo(simHit, weight);
237 RelationVector<MCParticle> particles = simHit->getRelationsFrom<MCParticle>();
238 for (unsigned i = 0; i < particles.size(); ++i) {
239 relatedMCParticles[particles[i]] += particles.weight(i) * weight;
240 }
241 }
242 }
243 for (const auto& x : relatedMCParticles) digit->addRelationTo(x.first, x.second);
244
245 } // end loop over splitted regions
246 }
247
248
249 // -------- full waveform digitization ------- //
250
252 StoreArray<TOPRawDigit>& rawDigits,
253 StoreArray<TOPDigit>& digits,
254 int threshold,
255 int hysteresis,
256 int thresholdCount) const
257 {
258
259 // get parameters of the model
260
261 const auto* geo = TOPGeometryPar::Instance()->getGeometry();
262 const auto& tdc = geo->getNominalTDC();
263
264 // generate waveform
265
266 std::vector<unsigned short> windowNumbers;
267 int offsetWindows = s_offsetWindows + m_windowShift;
268 int winnum = s_window + offsetWindows;
269 if (winnum < 0) winnum += s_storageDepth;
270 windowNumbers.push_back(winnum % s_storageDepth);
271 for (unsigned i = 1; i < s_readoutWindows; i++) {
272 windowNumbers.push_back((windowNumbers.back() + 1) % s_storageDepth);
273 }
274 std::vector<double> baselines(windowNumbers.size(), 0);
275 std::vector<double> rmsNoises(windowNumbers.size(), m_rmsNoise);
276 double averagePedestal = tdc.getAveragePedestal();
277 std::vector<double> pedestals(windowNumbers.size(), averagePedestal);
278 int adcRange = tdc.getADCRange();
279
280 int startSample = -offsetWindows * TOPRawWaveform::c_WindowSize;
281 auto wfData = generateWaveform(startSample, baselines, rmsNoises, pedestals,
282 adcRange);
283
284 // store waveform
285
286 auto* waveform = waveforms.appendNew(m_moduleID, m_pixelID, m_channel, m_scrodID,
287 windowNumbers[0], 0, wfData);
288 waveform->setStorageWindows(windowNumbers);
289 waveform->setPedestalSubtractedFlag(true);
290
291 // do feature extraction
292
293 waveform->featureExtraction(threshold, hysteresis, thresholdCount);
294
295 // digits
296
297 for (const auto& feature : waveform->getFeatureExtractionData()) {
298 int sampleRise = feature.sampleRise;
299 if (sampleRise < 0) continue;
300 unsigned iwin = sampleRise / TOPRawWaveform::c_WindowSize;
301 if (iwin >= windowNumbers.size()) continue;
302 sampleRise -= iwin * TOPRawWaveform::c_WindowSize; // should fit raw data bits
303 if (s_maskSamples and (sampleRise % 64) > 55) continue;
304
305 unsigned DsamplePeak = feature.samplePeak - feature.sampleRise;
306 if ((DsamplePeak & 0x0F) != DsamplePeak) continue; // does not fit raw data bits
307
308 unsigned DsampleFall = feature.sampleFall - feature.sampleRise;
309 if ((DsampleFall & 0x0F) != DsampleFall) continue; // does not fit raw data bits
310
311 std::vector<unsigned short> winNumbers;
312 for (unsigned i = iwin; i < windowNumbers.size(); i++) {
313 winNumbers.push_back(windowNumbers[i]);
314 }
315
316 // append new raw digit and set it
317 auto* rawDigit = rawDigits.appendNew(m_scrodID, TOPRawDigit::c_ProductionDebug);
318 rawDigit->setCarrierNumber(m_carrier);
319 rawDigit->setASICNumber(m_asic);
320 rawDigit->setASICChannel(m_chan);
321 rawDigit->setASICWindow(windowNumbers[iwin]);
322 rawDigit->setStorageWindows(winNumbers);
323 rawDigit->setSampleRise(sampleRise);
324 rawDigit->setDeltaSamplePeak(DsamplePeak);
325 rawDigit->setDeltaSampleFall(DsampleFall);
326 rawDigit->setValueRise0(feature.vRise0);
327 rawDigit->setValueRise1(feature.vRise1);
328 rawDigit->setValueFall0(feature.vFall0);
329 rawDigit->setValueFall1(feature.vFall1);
330 rawDigit->setValuePeak(feature.vPeak);
331 rawDigit->setIntegral(feature.integral);
332 double rawTimeLeading = rawDigit->getCFDLeadingTime(); // time in [samples]
333 int sampleDivisions = 0x1 << tdc.getSubBits();
334 int tfine = int(rawTimeLeading * sampleDivisions) % sampleDivisions;
335 if (tfine < 0) tfine += sampleDivisions;
336 rawDigit->setTFine(tfine);
337 rawDigit->addRelationTo(waveform);
338
339 double rawTimeFalling = rawDigit->getCFDFallingTime(); // time in [samples]
340 double rawTimeErr = rawDigit->getCFDLeadingTimeError(m_rmsNoise); // in [samples]
341
342 // convert to [ns] using time base calibration
343 int nwin = iwin + offsetWindows;
344 rawTimeLeading += nwin * TOPRawDigit::c_WindowSize;
345 rawTimeFalling += nwin * TOPRawDigit::c_WindowSize;
346
347 double cfdTime = m_sampleTimes->getTime(s_window, rawTimeLeading) - m_timeOffset;
348 double width = m_sampleTimes->getDeltaTime(s_window,
349 rawTimeFalling,
350 rawTimeLeading);
351 int sample = static_cast<int>(rawTimeLeading);
352 if (rawTimeLeading < 0) sample--;
353 double timeError = rawTimeErr * m_sampleTimes->getTimeBin(s_window, sample);
354 double timeErrorSq = timeError * timeError + m_calErrorsSq;
355 int pulseHeight = rawDigit->getValuePeak();
356
357 if (s_timeWalk) {
358 cfdTime -= (*s_timeWalk)->getTimeWalk(pulseHeight);
359 timeErrorSq += (*s_timeWalk)->getSigmaSq(pulseHeight);
360 }
361
362 // append new digit and set it
363 auto* digit = digits.appendNew(m_moduleID, m_pixelID, rawTimeLeading);
364 digit->setTime(cfdTime);
365 digit->setTimeError(sqrt(timeErrorSq));
366 digit->setPulseHeight(pulseHeight);
367 digit->setIntegral(rawDigit->getIntegral());
368 digit->setPulseWidth(width);
369 digit->setChannel(m_channel);
370 digit->setFirstWindow(s_window);
371 if (m_sampleTimes->isCalibrated()) {
372 digit->addStatus(TOPDigit::c_TimeBaseCalibrated);
373 }
374 digit->addRelationTo(rawDigit);
375
376 // check validity of feature extraction
377
378 if (!rawDigit->isFEValid() or rawDigit->isPedestalJump()) {
379 digit->setHitQuality(TOPDigit::c_Junk);
380 }
381
382 // set relations to simulated hits and MC particles, largest weight first
383
384 std::multimap<double, const Hit*, std::greater<double>> weights;
385 for (const auto& hit : m_times) {
386 double hitTime = hit.first;
387 double weight = 0;
388 const auto* pulseShape = hit.second.shape;
389 if (pulseShape) {
390 weight = fabs(pulseShape->getValue(cfdTime - hitTime));
391 }
392 if (weight > 0.01) {
393 weight *= hit.second.pulseHeight;
394 weights.insert(std::pair<double, const Hit*>(weight, &hit.second));
395 }
396 }
397 double sum = 0;
398 for (const auto& w : weights) sum += w.first;
399 if (sum == 0) continue; // noisy hit
400
401 std::map<MCParticle*, double> relatedMCParticles;
402 for (const auto& w : weights) {
403 auto weight = w.first / sum;
404 const auto* simHit = w.second->simHit;
405 if (simHit and weight > 0) {
406 digit->addRelationTo(simHit, weight);
407 RelationVector<MCParticle> particles = simHit->getRelationsFrom<MCParticle>();
408 for (unsigned i = 0; i < particles.size(); ++i) {
409 relatedMCParticles[particles[i]] += particles.weight(i) * weight;
410 }
411 } else if (w.second->type == c_CalPulse and weight > 0.90) {
412 digit->setHitQuality(TOPDigit::c_CalPulse);
413 }
414 }
415 for (const auto& x : relatedMCParticles) digit->addRelationTo(x.first, x.second);
416 }
417
418 }
419
420
421 vector<short> TimeDigitizer::generateWaveform(int startSample,
422 const vector<double>& baselines,
423 const vector<double>& rmsNoises,
424 const vector<double>& pedestals,
425 int adcRange) const
426 {
427
428 if (baselines.empty() or baselines.size() != rmsNoises.size() or
429 baselines.size() != pedestals.size())
430 B2FATAL("TOP::TimeDigitizer: inconsistent vector sizes");
431
432 // model parameters
433
434 const auto* geo = TOPGeometryPar::Instance()->getGeometry();
435 const auto& tdc = geo->getNominalTDC();
436 const auto& signalShape = geo->getSignalShape();
437
438 double dt = tdc.getSampleWidth();
439 double fmax = 1 / (2 * dt); // Nyquist frequency
440 double tau1 = 1 / (2 * M_PI * signalShape.getPole1());
441 double tau2 = 1 / (2 * M_PI * signalShape.getPole2());
442 double bw1 = 1 / (4 * tau1); // bandwidth of first order filter
443 double bw2 = 1 / (4 * (tau1 + tau2)); // bandwidth of second order filter
444
445 // construct waveform vector containing white noise
446
447 vector<double> waveform;
448 for (auto rms : rmsNoises) {
449 double rms0 = rms * sqrt(fmax / bw2);
450 for (unsigned i = 0; i < TOPRawWaveform::c_WindowSize; i++) {
451 waveform.push_back(gRandom->Gaus(0, rms0));
452 }
453 }
454
455 // noise smoothing according to second order low pass filter
456
457 double a1 = exp(-dt / tau1);
458 double v0 = gRandom->Gaus(0, rmsNoises[0] * sqrt(bw1 / bw2));
459 double prevValue = v0;
460 for (auto& value : waveform) { // first order filter (pole1)
461 value = a1 * prevValue + (1 - a1) * value;
462 prevValue = value;
463 }
464 double a2 = exp(-dt / tau2);
465 prevValue = v0;
466 for (auto& value : waveform) { // first order filter again (pole2)
467 value = a2 * prevValue + (1 - a2) * value;
468 prevValue = value;
469 }
470
471 // add possible baseline shifts
472
473 int k = 0;
474 for (auto baseline : baselines) {
475 for (unsigned i = 0; i < TOPRawWaveform::c_WindowSize; i++) {
476 waveform[k] += baseline;
477 k++;
478 }
479 }
480
481 // add signal
482
483 for (const auto& hit : m_times) {
484 double hitTime = hit.first;
485 double pulseHeight = hit.second.pulseHeight;
486 const auto* pulseShape = hit.second.shape;
487 if (not pulseShape) continue;
488 double timeWalk = generateTimeWalk(hitTime, pulseShape->getPeakingTime());
489 int size = waveform.size();
490 for (int sample = 0; sample < size; sample++) {
491 double t = m_sampleTimes->getTime(s_window, sample - startSample) - m_timeOffset;
492 waveform[sample] += pulseHeight * pulseShape->getValue(t - timeWalk - hitTime);
493 }
494 }
495
496 // model saturation effects and convert to short
497 // (exact modelling would require pedestals for each sample)
498
499 vector<short> wf;
500 k = 0;
501 for (auto pedestal : pedestals) {
502 for (unsigned i = 0; i < TOPRawWaveform::c_WindowSize; i++) {
503 int adc = waveform[k] + pedestal;
504 if (adc < 0) adc = 0;
505 if (adc > adcRange) adc = adcRange;
506 adc -= pedestal;
507 wf.push_back(adc);
508 k++;
509 }
510 }
511
512 return wf;
513 }
514
515
516 double TimeDigitizer::generateTimeWalk(double hitTime, double peakTime) const
517 {
518 if (not s_timeWalk) return 0;
519
520 double pulseHeight = 0;
521 for (const auto& hit : m_times) {
522 double hTime = hit.first;
523 const auto* pulseShape = hit.second.shape;
524 if (not pulseShape) continue;
525 pulseHeight += hit.second.pulseHeight * pulseShape->getValue(hTime - hitTime + peakTime);
526 }
527
528 double t = (*s_timeWalk)->getTimeWalk(pulseHeight);
529 double sig = (*s_timeWalk)->getSigma(pulseHeight);
530
531 return gRandom->Gaus(t, sig);
532 }
533
534 } // TOP namespace
536} // Belle2 namespace
A Class to store the Monte Carlo particle information.
Definition MCParticle.h:32
Class for type safe access to objects that are referred to in relations.
Accessor to arrays stored in the data store.
Definition StoreArray.h:113
T * appendNew()
Construct a new T object at the end of the array.
Definition StoreArray.h:246
const TOPNominalTDC & getNominalTDC() const
Returns nominal time-to-digit conversion parameters.
const TOPSignalShape & getSignalShape() const
Returns single photon signal shape.
const TOPSignalShape & getCalPulseShape() const
Returns calibration pulse shape.
double getDoubleHitResolution() const
Returns double hit resolution time.
@ c_ProductionDebug
from production debugging format
Definition TOPRawDigit.h:46
@ c_MC
from MC digitization
Definition TOPRawDigit.h:43
@ c_WindowSize
number of samples per window
Definition TOPRawDigit.h:54
@ c_WindowSize
number of samples per ASIC window
Calibration constants of a singe ASIC channel: time axis (sample times)
Class to store simulated hits of Cherenkov photons on PMT's input for digitization module (TOPDigitiz...
Definition TOPSimHit.h:27
const TOPFrontEndMap * getMap(int moduleID, int bs) const
Return map from TOP module side.
const TOPGeometry * getGeometry() const
Returns pointer to geometry object using basf2 units.
const ChannelMapper & getChannelMapper() const
Returns default channel mapper (mapping of channels to pixels)
static TOPGeometryPar * Instance()
Static method to obtain the pointer to its instance.
const FrontEndMapper & getFrontEndMapper() const
Returns front-end mapper (mapping of SCROD's to positions within TOP modules)
std::vector< short > generateWaveform(int startSample, const std::vector< double > &baselines, const std::vector< double > &rmsNoises, const std::vector< double > &pedestals, int ADCRange) const
Generate waveform.
unsigned m_asic
ASIC number.
int m_windowShift
additional wf window shift due to asic mis-alignment
unsigned m_scrodID
SCROD ID.
double m_rmsNoise
r.m.s of noise [ADC counts]
static unsigned s_storageDepth
ASIC analog storage depth.
unsigned m_channel
hardware channel number (0-based)
static DBObjPtr< TOPCalTimeWalk > * s_timeWalk
pointer to DB object
double m_calErrorsSq
calibration uncertainties squared
const TOPSampleTimes * m_sampleTimes
sample times
int m_moduleID
module ID (1-based)
TimeDigitizer(int moduleID, int pixelID, double timeOffset, double calErrorsSq, int shift, double rmsNoise, const TOPSampleTimes &sampleTimes)
Constructor.
bool m_valid
true, if module/pixel is mapped to hardware
static int s_offsetWindows
number of windows before first wf window
double generateTimeWalk(double hitTime, double peakTime) const
Generate time walk by taking into account pile-up of hits.
static unsigned s_window
first window number
double gauss(double x, double mean, double sigma) const
Gauss function (pulse shape approximation)
void addTimeOfHit(double t, double pulseHeight, EType type, const TOPSimHit *simHit=0)
Add time of simulated hit.
static bool s_maskSamples
mask samples at window boundary (phase-2)
double m_timeOffset
time offset [ns]
std::multimap< double, const Hit > m_times
hits sorted by time
static unsigned s_readoutWindows
number of readout windows
unsigned m_carrier
carrier board number
EType
hit type enumerators
unsigned m_chan
ASIC channel number.
void digitize(StoreArray< TOPRawDigit > &rawDigits, StoreArray< TOPDigit > &digits, int threshold=0, int thresholdCount=0, double timeJitter=0) const
Do time digitization using simplified pile-up and double-hit-resolution model.
double sqrt(double a)
sqrt for double
Definition beamHelpers.h:28
Abstract base class for different kinds of events.
STL namespace.
hit data other than time