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>
32 double calErrorsSq,
int shift,
double rmsNoise,
34 m_moduleID(moduleID), m_pixelID(pixelID), m_timeOffset(timeOffset),
35 m_calErrorsSq(calErrorsSq), m_rmsNoise(rmsNoise), m_sampleTimes(&sampleTimes),
39 m_channel = channelMapper.getChannel(pixelID);
40 if (!channelMapper.isChannelValid(
m_channel)) {
41 B2ERROR(
"TimeDigitizer::TimeDigitizer: invalid channel");
51 B2ERROR(
"TimeDigitizer::TimeDigitizer: no valid frontend map found");
64 hit.pulseHeight = pulseHeight;
68 case static_cast<int>(c_Hit):
71 case static_cast<int>(c_ChargeShare):
74 case static_cast<int>(c_CrossTalk):
76 B2ERROR(
"TOP::TimeDigitizer: waveform shape of cross-talk not yet available");
78 case static_cast<int>(c_CalPulse):
84 m_times.insert(std::pair<double, const Hit>(t, hit));
91 typedef std::multimap<double, const TimeDigitizer::Hit>::const_iterator Iterator;
97 double timeJitter)
const
106 double pileupTime = tdc.getPileupTime();
109 std::vector<Iterator> ranges;
110 ranges.push_back(
m_times.begin());
111 double prevTime =
m_times.begin()->first;
113 if (it->first - prevTime > hitResolveTime) ranges.push_back(it);
114 prevTime = it->first;
116 ranges.push_back(
m_times.end());
119 for (
unsigned k = 0; k < ranges.size() - 1; k++) {
122 std::vector<double> times;
123 std::vector<double> weights;
124 std::vector<const TOPSimHit*> simHits;
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;
139 for (
const auto& weight : weights) height += weight;
142 for (
auto& weight : weights) weight /= height;
145 double width = gRandom->Gaus(2.3, 0.4);
146 if (width < 0.5) width = 0.5;
150 for (
unsigned j = 0; j < times.size(); j++) time += times[j] * weights[j];
153 if (timeJitter > 0) time += gRandom->Gaus(0., timeJitter);
157 double mean = time + width / 2;
158 double sigma = width / 2.35482;
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;
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);
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);
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;
184 int numSamples = (sampleFall - sampleRise) * 4;
185 double sigmaIntegral = 0;
186 if (numSamples > 1) sigmaIntegral =
m_rmsNoise *
sqrt(numSamples - 1);
187 double integral = gRandom->Gaus(height * 7.0, sigmaIntegral);
192 rawDigit->setASICNumber(
m_asic);
193 rawDigit->setASICChannel(
m_chan);
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);
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);
216 digit->setTime(cfdTime);
217 digit->setTimeError(timeJitter);
218 digit->setPulseHeight(rawDigit->getValuePeak());
219 digit->setIntegral(rawDigit->getIntegral());
220 digit->setPulseWidth(cfdWidth);
222 digit->setFirstWindow(rawDigit->getASICWindow());
223 digit->setStatus(TOPDigit::c_OffsetSubtracted);
225 digit->addStatus(TOPDigit::c_TimeBaseCalibrated);
227 digit->addRelationTo(rawDigit);
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];
236 digit->addRelationTo(simHit, weight);
238 for (
unsigned i = 0; i < particles.size(); ++i) {
239 relatedMCParticles[particles[i]] += particles.weight(i) * weight;
243 for (
const auto& x : relatedMCParticles) digit->addRelationTo(x.first, x.second);
256 int thresholdCount)
const
266 std::vector<unsigned short> windowNumbers;
268 int winnum =
s_window + offsetWindows;
272 windowNumbers.push_back((windowNumbers.back() + 1) %
s_storageDepth);
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();
281 auto wfData =
generateWaveform(startSample, baselines, rmsNoises, pedestals,
287 windowNumbers[0], 0, wfData);
288 waveform->setStorageWindows(windowNumbers);
289 waveform->setPedestalSubtractedFlag(
true);
293 waveform->featureExtraction(threshold, hysteresis, thresholdCount);
297 for (
const auto& feature : waveform->getFeatureExtractionData()) {
298 int sampleRise = feature.sampleRise;
299 if (sampleRise < 0)
continue;
301 if (iwin >= windowNumbers.size())
continue;
305 unsigned DsamplePeak = feature.samplePeak - feature.sampleRise;
306 if ((DsamplePeak & 0x0F) != DsamplePeak)
continue;
308 unsigned DsampleFall = feature.sampleFall - feature.sampleRise;
309 if ((DsampleFall & 0x0F) != DsampleFall)
continue;
311 std::vector<unsigned short> winNumbers;
312 for (
unsigned i = iwin; i < windowNumbers.size(); i++) {
313 winNumbers.push_back(windowNumbers[i]);
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();
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);
339 double rawTimeFalling = rawDigit->getCFDFallingTime();
340 double rawTimeErr = rawDigit->getCFDLeadingTimeError(
m_rmsNoise);
343 int nwin = iwin + offsetWindows;
351 int sample =
static_cast<int>(rawTimeLeading);
352 if (rawTimeLeading < 0) sample--;
355 int pulseHeight = rawDigit->getValuePeak();
358 cfdTime -= (*s_timeWalk)->getTimeWalk(pulseHeight);
359 timeErrorSq += (*s_timeWalk)->getSigmaSq(pulseHeight);
364 digit->setTime(cfdTime);
365 digit->setTimeError(
sqrt(timeErrorSq));
366 digit->setPulseHeight(pulseHeight);
367 digit->setIntegral(rawDigit->getIntegral());
368 digit->setPulseWidth(width);
372 digit->addStatus(TOPDigit::c_TimeBaseCalibrated);
374 digit->addRelationTo(rawDigit);
378 if (!rawDigit->isFEValid() or rawDigit->isPedestalJump()) {
379 digit->setHitQuality(TOPDigit::c_Junk);
384 std::multimap<double, const Hit*, std::greater<double>> weights;
385 for (
const auto& hit :
m_times) {
386 double hitTime = hit.first;
388 const auto* pulseShape = hit.second.shape;
390 weight = fabs(pulseShape->getValue(cfdTime - hitTime));
393 weight *= hit.second.pulseHeight;
394 weights.insert(std::pair<double, const Hit*>(weight, &hit.second));
398 for (
const auto& w : weights) sum += w.first;
399 if (sum == 0)
continue;
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);
408 for (
unsigned i = 0; i < particles.size(); ++i) {
409 relatedMCParticles[particles[i]] += particles.weight(i) * weight;
411 }
else if (w.second->type == c_CalPulse and weight > 0.90) {
412 digit->setHitQuality(TOPDigit::c_CalPulse);
415 for (
const auto& x : relatedMCParticles) digit->addRelationTo(x.first, x.second);
422 const vector<double>& baselines,
423 const vector<double>& rmsNoises,
424 const vector<double>& pedestals,
428 if (baselines.empty() or baselines.size() != rmsNoises.size() or
429 baselines.size() != pedestals.size())
430 B2FATAL(
"TOP::TimeDigitizer: inconsistent vector sizes");
436 const auto& signalShape = geo->getSignalShape();
438 double dt = tdc.getSampleWidth();
439 double fmax = 1 / (2 * dt);
440 double tau1 = 1 / (2 * M_PI * signalShape.getPole1());
441 double tau2 = 1 / (2 * M_PI * signalShape.getPole2());
442 double bw1 = 1 / (4 * tau1);
443 double bw2 = 1 / (4 * (tau1 + tau2));
447 vector<double> waveform;
448 for (
auto rms : rmsNoises) {
449 double rms0 = rms *
sqrt(fmax / bw2);
451 waveform.push_back(gRandom->Gaus(0, rms0));
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) {
461 value = a1 * prevValue + (1 - a1) * value;
464 double a2 = exp(-dt / tau2);
466 for (
auto& value : waveform) {
467 value = a2 * prevValue + (1 - a2) * value;
474 for (
auto baseline : baselines) {
476 waveform[k] += baseline;
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;
489 int size = waveform.size();
490 for (
int sample = 0; sample < size; sample++) {
492 waveform[sample] += pulseHeight * pulseShape->getValue(t - timeWalk - hitTime);
501 for (
auto pedestal : pedestals) {
503 int adc = waveform[k] + pedestal;
504 if (adc < 0) adc = 0;
505 if (adc > adcRange) adc = adcRange;
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);
528 double t = (*s_timeWalk)->getTimeWalk(pulseHeight);
529 double sig = (*s_timeWalk)->getSigma(pulseHeight);
531 return gRandom->Gaus(t, sig);
A Class to store the Monte Carlo particle information.
Class for type safe access to objects that are referred to in relations.
Accessor to arrays stored in the data store.
T * appendNew()
Construct a new T object at the end of the array.
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
@ c_MC
from MC digitization
@ c_WindowSize
number of samples per window
Calibration constants of a singe ASIC channel: time axis (sample times)
bool isCalibrated() const
Returns calibration status.
double getDeltaTime(int window, double sample2, double sample1) const
Returns time difference between sample2 and sample1.
double getTime(int window, double sample) const
Returns time w.r.t SSTin that corresponds to the window number.
Class to store simulated hits of Cherenkov photons on PMT's input for digitization module (TOPDigitiz...
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
double getTimeBin(int window, int sampleNumber) const
Returns time bin of a given sample number and window (e.g.
Abstract base class for different kinds of events.