Project
Loading...
Searching...
No Matches
Digitizer.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
15#include "Framework/Logger.h"
19#include <TRandom.h>
20
21using namespace o2::zdc;
22
23//______________________________________________________________________________
25{
26 memset(&data, 0, NChannels * sizeof(ChannelBCDataF));
27}
28
29//______________________________________________________________________________
31{
32 if (md.id < 0 || md.id >= NModules) {
33 LOG(fatal) << "Module id = " << md.id << " not in allowed range [0:" << NModules << ")";
34 }
35 // construct aux helper from full module description
36 for (int ic = Module::MaxChannels; ic--;) {
37 if (md.channelID[ic] >= IdDummy) {
38 if (md.readChannel[ic]) {
39 readChannels |= 0x1 << (NChPerModule * md.id + ic);
40 }
41 if (md.trigChannel[ic]) {
42 trigChannels |= 0x1 << (NChPerModule * md.id + ic);
43 }
44 }
45 }
46}
47
48//______________________________________________________________________________
49// this will process hits and fill the digit vector with digits which are finalized
50void Digitizer::process(const std::vector<o2::zdc::Hit>& hits,
51 std::vector<o2::zdc::BCData>& digitsBC,
52 std::vector<o2::zdc::ChannelData>& digitsCh,
54{
55 // loop over all hits and produce digits
56 LOG(debug) << "Processing IR = " << mIR << " | NHits = " << hits.size();
57
58 flush(digitsBC, digitsCh, labels); // flush cached signal which cannot be affect by new event
59
60 for (auto& hit : hits) {
61
62 std::array<o2::InteractionRecord, NBC2Cache> cachedIR;
63 // hit of given IR can con
64 // for each hit find out sector + detector information
65 int detID = hit.GetDetectorID();
66 int secID = hit.getSector();
67 float nPhotons;
68 if (detID == ZEM) {
69 // ZEM calorimeters have only common PM
70 nPhotons = hit.getPMCLightYield();
71 } else {
72 nPhotons = (secID == Common) ? hit.getPMCLightYield() : hit.getPMQLightYield();
73 }
74 if (!nPhotons) {
75 continue;
76 }
77 if (nPhotons < 0 || nPhotons > 1e6) {
78 int chan = toChannel(detID, secID);
79 LOG(error) << "Anomalous number of photons " << nPhotons << " for channel " << chan << '(' << channelName(chan) << ')';
80 continue;
81 }
82
83 double hTime = hit.GetTime() - getTOFCorrection(detID); // account for TOF to detector
84 hTime += mIR.getTimeNS();
85 o2::InteractionRecord irHit(hTime); // BC in which the hit appears (might be different from interaction BC for slow particles)
86
87 // we neglect hits which arrive more than one orbit after the interaction (very slow neutrons for example)
88 auto diffBC = irHit.differenceInBC(mIR);
90 LOG(debug) << "Skipping hit with more than " << diffBC << " difference in bunch crossing";
91 continue;
92 }
93
94 // nominal time of the BC to which the hit will be attributed
95 double bcTime = o2::InteractionRecord::bc2ns(irHit.bc, irHit.orbit);
96
97 int nCachedIR = 0;
98 for (int i = BCCacheMin; i < BCCacheMax + 1; i++) {
99 double tNS = hTime + o2::constants::lhc::LHCBunchSpacingNS * i;
100 cachedIR[nCachedIR].setFromNS(tNS);
101 if (tNS < 0 && cachedIR[nCachedIR] > irHit) {
102 continue; // don't go to negative BC/orbit (it will wrap)
103 }
104 getCreateBCCache(cachedIR[nCachedIR++]); // ensure existence of cached container
105 }
106 auto channel = toChannel(detID, secID);
107 phe2Sample(nPhotons, hit.getParentID(), hTime, cachedIR, nCachedIR, channel);
108 // if digit for this sector does not exist, create one otherwise add to it
109 }
110}
111
112//______________________________________________________________________________
113void Digitizer::flush(std::vector<o2::zdc::BCData>& digitsBC,
114 std::vector<o2::zdc::ChannelData>& digitsCh,
116{
117 // do we have something to flush? We can do this only for cached BC data which is distanced from currently processed BC by NBCReadOut
118 int lastDoneBCid = -1, diff2Last = 0;
119 int nCached = mCache.size();
120 if (nCached < 1) {
121 return;
122 }
123 if (mIR.differenceInBC(mCache.back()) > -BCCacheMin) {
124 LOG(debug) << "Generating new pedestal BL fluct. for BC range " << mCache.front() << " : " << mCache.back();
125 generatePedestal();
126 } else {
127 return;
128 }
129 o2::InteractionRecord ir0(mCache.front());
130 int cacheSpan = 1 + mCache.back().differenceInBC(ir0);
131 LOG(debug) << "Cache spans " << cacheSpan << " with " << nCached << " BCs cached";
132
133 mFastCache.clear();
134 mFastCache.resize(cacheSpan, nullptr);
135 mStoreChanMask.clear();
136 mStoreChanMask.resize(cacheSpan + mNBCAHead, 0);
137
138 for (int ibc = nCached; ibc--;) { // digitize BCs which might not be affected by future events
139 auto& bc = mCache[ibc];
140 lastDoneBCid = ibc;
141 if (!bc.digitized) {
142 digitizeBC(bc);
143 }
144 int bcSlot = mCache[ibc].differenceInBC(ir0);
145 mFastCache[bcSlot] = &mCache[ibc]; // add to fast access cache
146 }
147 mDummyBC.clear();
148 digitizeBC(mDummyBC);
149
150 // check trigger condition for digitized BCs
151 for (int ibc = 0; ibc < cacheSpan; ibc++) {
152 if (mFastCache[ibc] && !mFastCache[ibc]->triggerChecked) {
153 triggerBC(ibc);
154 }
155 } // all allowed BCs are checked for trigger
156
157 // store triggered BC with requested few BCs ahead
158 for (int ibc = -mNBCAHead; ibc < cacheSpan; ibc++) {
159 auto bcr = mStoreChanMask[ibc + mNBCAHead];
160 if (!bcr) {
161 continue; // nothing to store for this BC
162 }
163 BCCache* bcPtr = nullptr;
164 if (ibc < 0 || !mFastCache[ibc]) {
165 mDummyBC = ir0 + ibc; // fix the IR of the dummy BC
166 bcPtr = &mDummyBC;
167 } else {
168 bcPtr = mFastCache[ibc];
169 }
170 storeBC(*bcPtr, bcr, digitsBC, digitsCh, labels);
171 } // all allowed BCs are checked for trigger
172
173 // clean cache for BCs which are not needed anymore
174 LOG(debug) << "Cleaning cache";
175 mCache.erase(mCache.begin(), mCache.end());
176}
177
178//______________________________________________________________________________
179void Digitizer::generatePedestal()
180{
181 for (int idet : {ZNA, ZPA, ZNC, ZPC}) {
182 int chanSum = toChannel(idet, Sum);
183 // Uncorrelated baseline oscillations for sum channels
184 mPedestalBLFluct[chanSum] = gRandom->Gaus(0, mSimCondition->channels[chanSum].pedestalFluct);
185 int comm = toChannel(idet, Common);
186 mPedestalBLFluct[comm] = gRandom->Gaus(0, mSimCondition->channels[comm].pedestalFluct);
187 for (int ic : {Ch1, Ch2, Ch3, Ch4}) {
188 int chan = toChannel(idet, ic);
189 mPedestalBLFluct[chan] = gRandom->Gaus(0, mSimCondition->channels[chan].pedestalFluct);
190 // Correlated baseline oscillations for sum channels
191 mPedestalBLFluct[chanSum] += mPedestalBLFluct[chan];
192 }
193 }
194 mPedestalBLFluct[IdZEM1] = gRandom->Gaus(0, mSimCondition->channels[IdZEM1].pedestalFluct);
195 mPedestalBLFluct[IdZEM2] = gRandom->Gaus(0, mSimCondition->channels[IdZEM2].pedestalFluct);
196}
197
198//______________________________________________________________________________
199void Digitizer::digitizeBC(BCCache& bc)
200{
201 auto& bcdata = bc.data;
202 auto& bcdigi = bc.digi;
203 // apply gain
204 for (int idet : {ZNA, ZPA, ZNC, ZPC}) {
205 for (int ic : {Common, Ch1, Ch2, Ch3, Ch4}) {
206 int chan = toChannel(idet, ic);
207 auto gain = mSimCondition->channels[chan].gain;
208 for (int ib = NTimeBinsPerBC; ib--;) {
209 bcdata[chan][ib] *= gain;
210 }
211 }
212 }
213 for (int ib = NTimeBinsPerBC; ib--;) {
214 bcdata[IdZEM1][ib] *= mSimCondition->channels[IdZEM1].gain;
215 bcdata[IdZEM2][ib] *= mSimCondition->channels[IdZEM2].gain;
216 }
217 // Prepare sum of towers before adding noise
218 for (int ib = NTimeBinsPerBC; ib--;) {
219 // Only for proton calorimeters we allow for gain modification (attenuation) before entering into the sum
220 bcdata[IdZNASum][ib] = mSimCondition->channels[IdZNASum].gain *
221 (bcdata[IdZNA1][ib] + bcdata[IdZNA2][ib] + bcdata[IdZNA3][ib] + bcdata[IdZNA4][ib]);
222 bcdata[IdZPASum][ib] = mSimCondition->channels[IdZPASum].gain *
223 (bcdata[IdZPA1][ib] * mSimCondition->channels[IdZPA1].gainInSum +
224 bcdata[IdZPA2][ib] * mSimCondition->channels[IdZPA2].gainInSum +
225 bcdata[IdZPA3][ib] * mSimCondition->channels[IdZPA3].gainInSum +
226 bcdata[IdZPA4][ib] * mSimCondition->channels[IdZPA4].gainInSum);
227 bcdata[IdZNCSum][ib] = mSimCondition->channels[IdZNCSum].gain *
228 (bcdata[IdZNC1][ib] + bcdata[IdZNC2][ib] + bcdata[IdZNC3][ib] + bcdata[IdZNC4][ib]);
229 bcdata[IdZPCSum][ib] = mSimCondition->channels[IdZPCSum].gain *
230 (bcdata[IdZPC1][ib] * mSimCondition->channels[IdZPC1].gainInSum +
231 bcdata[IdZPC2][ib] * mSimCondition->channels[IdZPC2].gainInSum +
232 bcdata[IdZPC3][ib] * mSimCondition->channels[IdZPC3].gainInSum +
233 bcdata[IdZPC4][ib] * mSimCondition->channels[IdZPC4].gainInSum);
234 }
235 // Digitize the signals connected to each channel of the different modules
236 for (const auto& md : mModuleConfig->modules) {
237 if (md.id >= 0 && md.id < NModules) {
238 for (int ic = Module::MaxChannels; ic--;) {
239 int id = md.channelID[ic];
240 if (id >= 0 && id < NChannels) {
241 const auto& chanConf = mSimCondition->channels[id];
242 auto pedBaseLine = mSimCondition->channels[id].pedestal;
243 // Geographical position of signal in the setup
244 auto ipos = NChPerModule * md.id + ic;
245 for (int ib = NTimeBinsPerBC; ib--;) {
246 bcdigi[ipos][ib] = bcdata[id][ib] + gRandom->Gaus(pedBaseLine + mPedestalBLFluct[id], chanConf.pedestalNoise);
247 int adc = std::nearbyint(bcdigi[ipos][ib]);
248 bcdigi[ipos][ib] = adc < ADCMax ? (adc > ADCMin ? adc : ADCMin) : ADCMax;
249 }
250 LOG(debug) << "md " << md.id << " ch " << ic << " sig " << id << " " << ChannelNames[id]
251 << bcdigi[ipos][0] << " " << bcdigi[ipos][1] << " " << bcdigi[ipos][2] << " " << bcdigi[ipos][3] << " " << bcdigi[ipos][4] << " " << bcdigi[ipos][5] << " "
252 << bcdigi[ipos][6] << " " << bcdigi[ipos][7] << " " << bcdigi[ipos][8] << " " << bcdigi[ipos][9] << " " << bcdigi[ipos][10] << " " << bcdigi[ipos][11];
253 }
254 }
255 }
256 }
257 bc.digitized = true;
258}
259
260//______________________________________________________________________________
261bool Digitizer::triggerBC(int ibc)
262{
263 // check trigger for the cached BC in the position ibc
264 auto& bcCached = *mFastCache[ibc];
265
266 LOG(debug) << "CHECK TRIGGER " << ibc << " IR=" << bcCached;
267
268 // Check trigger condition regardless of run type, will apply later the trigger mask
269 for (const auto& md : mModuleConfig->modules) {
270 if (md.id >= 0 && md.id < NModules) {
271 for (int ic = Module::MaxChannels; ic--;) {
272 // int id=md.channelID[ic];
273 auto trigCh = md.trigChannelConf[ic];
274 int id = trigCh.id;
275 if (id >= 0 && id < NChannels) {
276 auto ipos = NChPerModule * md.id + ic;
277 int last1 = trigCh.last + 2;
278 bool okPrev = false;
279#ifdef ZDC_DOUBLE_TRIGGER_CONDITION
280 // look for 2 consecutive bins (the 1st one spanning trigCh.first : trigCh.last range) so that
281 // signal[bin]-signal[bin+trigCh.shift] > trigCh.threshold
282 for (int ib = trigCh.first; ib < last1; ib++) { // ib may be negative, so we shift by offs and look in the ADC cache
283 int binF, bcFidx = ibc + binHelper(ib, binF);
284 int binL, bcLidx = ibc + binHelper(ib + trigCh.shift, binL);
285 const auto& bcF = (bcFidx < 0 || !mFastCache[bcFidx]) ? mDummyBC : *mFastCache[bcFidx];
286 const auto& bcL = (bcLidx < 0 || !mFastCache[bcLidx]) ? mDummyBC : *mFastCache[bcLidx];
287 bool ok = bcF.digi[ipos][binF] - bcL.digi[ipos][binL] > trigCh.threshold;
288 if (ok && okPrev) { // trigger ok!
289 bcCached.trigChanMask |= 0x1 << (NChPerModule * md.id + ic); // register trigger mask
290 LOG(debug) << bcF.digi[ipos][binF] << " - " << bcL.digi[ipos][binL] << " = " << bcF.digi[ipos][binF] - bcL.digi[ipos][binL] << " > " << trigCh.threshold;
291 LOG(debug) << " hit [" << md.id << "," << ic << "] " << int(id) << "(" << ChannelNames[id] << ") => " << bcCached.trigChanMask;
292 break;
293 }
294 okPrev = ok;
295 }
296#else
297 bool okPPrev = false;
298 // look for 3 consecutive bins (the 1st one spanning trigCh.first : trigCh.last range) so that
299 // signal[bin]-signal[bin+trigCh.shift] > trigCh.threshold
300 for (int ib = trigCh.first - 1; ib < last1; ib++) { // ib may be negative, so we shift by offs and look in the ADC cache
301 int binF, bcFidx = ibc + binHelper(ib, binF);
302 int binL, bcLidx = ibc + binHelper(ib + trigCh.shift, binL);
303 const auto& bcF = (bcFidx < 0 || !mFastCache[bcFidx]) ? mDummyBC : *mFastCache[bcFidx];
304 const auto& bcL = (bcLidx < 0 || !mFastCache[bcLidx]) ? mDummyBC : *mFastCache[bcLidx];
305 bool ok = bcF.digi[ipos][binF] - bcL.digi[ipos][binL] > trigCh.threshold;
306 if (ok && okPrev && okPPrev) { // trigger ok!
307 bcCached.trigChanMask |= 0x1 << (NChPerModule * md.id + ic); // register trigger mask
308 LOG(debug) << bcF.digi[ipos][binF] << " - " << bcL.digi[ipos][binL] << " = " << bcF.digi[ipos][binF] - bcL.digi[ipos][binL] << " > " << trigCh.threshold;
309 LOG(debug) << " hit [" << md.id << "," << ic << "] " << int(id) << "(" << ChannelNames[id] << ") => " << bcCached.trigChanMask;
310 break;
311 }
312 okPPrev = okPrev;
313 okPrev = ok;
314 }
315#endif
316 }
317 }
318 }
319 }
320
321 // just check if this BC IR corresponds to external trigger
322 if (!mIRExternalTrigger.empty() && mIRExternalTrigger.front() == bcCached) {
323 bcCached.extTrig = ALICETriggerMask;
324 mIRExternalTrigger.pop_front(); // suppress accounted external trigger
325 }
326 // This works in autotrigger mode, partially for triggered mode
327 if (bcCached.trigChanMask & mTriggerableChanMask) { // there are triggered channels, flag modules/channels to read
328 for (int ibcr = ibc - mNBCAHead; ibcr <= ibc; ibcr++) {
329 auto& bcr = mStoreChanMask[ibcr + mNBCAHead];
330 for (const auto& mdh : mModConfAux) {
331 if (bcCached.trigChanMask & mdh.trigChannels) { // are there triggered channels in this module?
332 bcr |= mdh.readChannels; // flag channels to store
333 }
334 }
335 }
336 }
337 bcCached.triggerChecked = true;
338 return bcCached.trigChanMask;
339}
340
341//______________________________________________________________________________
342void Digitizer::storeBC(const BCCache& bc, uint32_t chan2Store,
343 std::vector<o2::zdc::BCData>& digitsBC, std::vector<o2::zdc::ChannelData>& digitsCh,
345{
346 // store selected data of selected BC
347 if (!chan2Store) {
348 return;
349 }
350 LOG(debug) << "Storing ch: " << chanPattern(chan2Store) << " trigger: " << chanPattern(bc.trigChanMask) << " for BC " << bc;
351
352 int first = digitsCh.size(), nSto = 0;
353 for (const auto& md : mModuleConfig->modules) {
354 if (md.id >= 0 && md.id < NModules) {
355 for (int ic = 0; ic < Module::MaxChannels; ic++) {
356 auto ipos = NChPerModule * md.id + ic;
357 if (chan2Store & (0x1 << ipos)) {
358 digitsCh.emplace_back(md.channelID[ic], bc.digi[ipos]);
359 nSto++;
360 }
361 }
362 }
363 }
364
365 int nBC = digitsBC.size();
366 digitsBC.emplace_back(first, nSto, bc, chan2Store, bc.trigChanMask, bc.extTrig);
367 // TODO clarify if we want to store MC labels for all channels or only for stored ones
368 if (!mSkipMCLabels) {
369 for (const auto& lbl : bc.labels) {
370 if (chan2Store & (0x1 << lbl.getChannel())) {
371 labels.addElement(nBC, lbl);
372 }
373 }
374 }
375}
376
377//______________________________________________________________________________
378void Digitizer::phe2Sample(int nphe, int parID, double timeHit, std::array<o2::InteractionRecord, NBC2Cache> const& cachedIR, int nCachedIR, int channel)
379{
380 // function to simulate the waveform from no. of photoelectrons seen in a given sample
381 // for electrons at timeInSample wrt beginning of the sample
382
383 double time0 = cachedIR[0].bc2ns(); // start time of the 1st cashed BC
384 const auto& chanConfig = mSimCondition->channels[channel];
385
386 float timeDiff = time0 - timeHit;
387 int sample = (timeDiff - gRandom->Gaus(chanConfig.timePosition, chanConfig.timeJitter)) * ChannelSimCondition::ShapeBinWidthInv + chanConfig.ampMinID + TSNH;
388 int ir = 0;
389 bool stop = false;
390
391 do {
392 auto bcCache = getBCCache(cachedIR[ir]);
393 bool added = false;
394 for (int ib = 0; ib < NTimeBinsPerBC; ib++) {
395 if (sample >= chanConfig.shape.size()) {
396 stop = true;
397 break;
398 }
399 if (sample >= 0) {
400 auto signal = chanConfig.shape[sample] * nphe; // signal not accounting for the gain
401 (*bcCache).data[channel][ib] += signal;
402 added = true;
403 }
405 }
406 if (added) {
407 (*bcCache).labels.emplace_back(parID, mEventID, mSrcID, channel);
408 }
409 } while (++ir < nCachedIR && !stop);
410}
411
412//______________________________________________________________________________
413o2::zdc::Digitizer::BCCache& Digitizer::getCreateBCCache(const o2::InteractionRecord& ir)
414{
415 if (mCache.empty() || mCache.back() < ir) {
416 mCache.emplace_back();
417 auto& cb = mCache.back();
418 cb = ir;
419 return cb;
420 }
421 if (mCache.front() > ir) {
422 mCache.emplace_front();
423 auto& cb = mCache.front();
424 cb = ir;
425 return cb;
426 }
427
428 for (auto cb = mCache.begin(); cb != mCache.end(); cb++) {
429 if ((*cb) == ir) {
430 return *cb;
431 }
432 if (ir < (*cb)) {
433 auto cbnew = mCache.emplace(cb); // insert new element before cb
434 (*cbnew) = ir;
435 return (*cbnew);
436 }
437 }
438 return mCache.front();
439}
440
441//______________________________________________________________________________
442o2::zdc::Digitizer::BCCache* Digitizer::getBCCache(const o2::InteractionRecord& ir)
443{
444 // get pointer on existing cache
445 for (auto cb = mCache.begin(); cb != mCache.end(); cb++) {
446 if ((*cb) == ir) {
447 return &(*cb);
448 }
449 }
450 return nullptr;
451}
452
453//______________________________________________________________________________
455{
456 auto& sopt = ZDCSimParam::Instance();
457 mIsContinuous = sopt.continuous;
458 mNBCAHead = mIsContinuous ? sopt.nBCAheadCont : sopt.nBCAheadTrig;
459 LOG(info) << "Initialized in " << (mIsContinuous ? "Cont" : "Trig") << " mode, " << mNBCAHead
460 << " BCs will be stored ahead of Trigger";
461 LOG(info) << "Trigger bit masking is " << (mMaskTriggerBits ? "ON (default)" : "OFF (debugging)");
462 LOG(info) << "MC Labels are " << (mSkipMCLabels ? "SKIPPED" : "SAVED (default)");
463
464 mTriggerConfig.clear();
465 mModConfAux.clear();
466 for (const auto& md : mModuleConfig->modules) {
467 if (md.id >= 0 && md.id < NModules) {
468 mModConfAux.emplace_back(md);
469 for (int ic = Module::MaxChannels; ic--;) {
470 // We consider all channels that can produce a hit
471 if (md.trigChannel[ic] || (md.trigChannelConf[ic].shift > 0 && md.trigChannelConf[ic].threshold > 0)) {
472 const auto& trgChanConf = md.trigChannelConf[ic];
473 if (trgChanConf.last + trgChanConf.shift + 1 >= NTimeBinsPerBC) {
474 LOG(error) << "Wrong trigger settings";
475 }
476 mTriggerConfig.emplace_back(trgChanConf);
477 // We insert in the trigger mask only the channels that are actually triggering
478 // Trigger mask is geographical, bit position is relative to the module and channel
479 // where signal is connected
480 if (md.trigChannel[ic]) {
481 LOG(info) << "Adding channel [" << md.id << "," << ic << "] " << int(trgChanConf.id) << '(' << channelName(trgChanConf.id) << ") as triggering one";
482 // TODO insert check if bit is already used. Should never happen
483 mTriggerableChanMask |= 0x1 << (NChPerModule * md.id + ic);
484 } else {
485 LOG(info) << "Adding channel [" << md.id << "," << ic << "] " << int(trgChanConf.id) << '(' << channelName(trgChanConf.id) << ") as discriminator";
486 }
487 if (trgChanConf.first < mTrigBinMin) {
488 mTrigBinMin = trgChanConf.first;
489 }
490 if (trgChanConf.last + trgChanConf.shift > mTrigBinMax) {
491 mTrigBinMax = trgChanConf.last + trgChanConf.shift;
492 }
493 }
494 if (md.feeID[ic] < 0 || md.feeID[ic] >= NLinks) {
495 LOG(fatal) << "FEEID " << md.feeID[ic] << " not in allowed range [0:" << NLinks << ")";
496 }
497 }
498 } else {
499 LOG(fatal) << "Module id: " << md.id << " is out of range";
500 }
501 }
502 mModuleConfig->print();
503 mSimCondition->print();
504 setTriggerMask();
505 setReadoutMask();
506}
507
508//______________________________________________________________________________
510{
511 std::bitset<NChannels> tmsk(trigChanMask);
512 printf("Cached Orbit:%5d/BC:%4d | digitized:%d triggerChecked:%d (trig.: %s)\n",
513 orbit, bc, digitized, triggerChecked, tmsk.to_string().c_str());
514 for (int ic = 0; ic < NChannels; ic++) {
515 printf("Ch[%d](%s) | ", ic, channelName(ic));
516 for (int ib = 0; ib < NTimeBinsPerBC; ib++) {
517 printf("%+8.1f ", data[ic][ib]);
518 }
519 printf("\n");
520 }
521}
522
523//______________________________________________________________________________
524void Digitizer::setTriggerMask()
525{
526 mTriggerMask = 0;
527 std::string printTriggerMask{};
528
529 for (int im = 0; im < NModules; im++) {
530 if (im > 0) {
531 printTriggerMask += " ";
532 }
533 printTriggerMask += std::to_string(im);
534 printTriggerMask += "[";
535 for (int ic = 0; ic < NChPerModule; ic++) {
536 if (mModuleConfig->modules[im].trigChannel[ic]) {
537 uint32_t tmask = 0x1 << (im * NChPerModule + ic);
538 mTriggerMask = mTriggerMask | tmask;
539 printTriggerMask += "T";
540 } else {
541 printTriggerMask += " ";
542 }
543 }
544 printTriggerMask += "]";
545#ifdef O2_ZDC_DEBUG
546 uint32_t mytmask = mTriggerMask >> (im * NChPerModule);
547 LOGF(info, "Trigger mask for module %d 0123 %c%c%c%c", im, mytmask & 0x1 ? 'T' : 'N', mytmask & 0x2 ? 'T' : 'N', mytmask & 0x4 ? 'T' : 'N', mytmask & 0x8 ? 'T' : 'N');
548#endif
549 }
550 LOGF(info, "TriggerMask=0x%08x %s", mTriggerMask, printTriggerMask.c_str());
551}
552
553//______________________________________________________________________________
554void Digitizer::setReadoutMask()
555{
556 mReadoutMask = 0;
557 std::string printReadoutMask{};
558
559 for (int im = 0; im < NModules; im++) {
560 if (im > 0) {
561 printReadoutMask += " ";
562 }
563 printReadoutMask += std::to_string(im);
564 printReadoutMask += "[";
565 for (int ic = 0; ic < NChPerModule; ic++) {
566 if (mModuleConfig->modules[im].readChannel[ic]) {
567 uint32_t rmask = 0x1 << (im * NChPerModule + ic);
568 mReadoutMask = mReadoutMask | rmask;
569 printReadoutMask += "R";
570 } else {
571 printReadoutMask += " ";
572 }
573 }
574 printReadoutMask += "]";
575#ifdef O2_ZDC_DEBUG
576 uint32_t myrmask = mReadoutMask >> (im * NChPerModule);
577 LOGF(info, "Readout mask for module %d 0123 %c%c%c%c", im, myrmask & 0x1 ? 'R' : 'N', myrmask & 0x2 ? 'R' : 'N', myrmask & 0x4 ? 'R' : 'N', myrmask & 0x8 ? 'R' : 'N');
578#endif
579 }
580 LOGF(info, "ReadoutMask=0x%08x %s", mReadoutMask, printReadoutMask.c_str());
581}
582
583//______________________________________________________________________________
584void Digitizer::assignTriggerBits(uint32_t ibc, std::vector<BCData>& bcData)
585{
586 // Triggers refer to the HW trigger conditions (32 possible channels)
587
588 uint32_t nbcTot = bcData.size();
589 auto& currBC = bcData[ibc];
590
591 for (int is = -1; is < 4; is++) {
592 int ibc_peek = ibc + is;
593 if (ibc_peek < 0) {
594 continue;
595 }
596 if (ibc_peek >= nbcTot) {
597 break;
598 }
599 const auto& otherBC = bcData[ibc_peek];
600 auto diffBC = otherBC.ir.differenceInBC(currBC.ir);
601 if (diffBC < -1) {
602 continue;
603 }
604 if (diffBC > 3) {
605 break;
606 }
607 if (otherBC.ext_triggers && diffBC >= 0) {
608 for (int im = 0; im < NModules; im++) {
609 currBC.moduleTriggers[im] |= 0x1 << diffBC;
610 }
611 }
612 if (otherBC.triggers) {
613 // Assign trigger bits in payload
614 for (int im = 0; im < NModules; im++) {
615 uint32_t tmask = (0xf << (im * NChPerModule)) & mTriggerMask;
616 if (otherBC.triggers & tmask) {
617 currBC.moduleTriggers[im] |= 0x1 << (5 + diffBC);
618 }
619 }
620 }
621 }
622 // Printout before cleanup
623 // currBC.print(mTriggerMask);
624}
625
626void Digitizer::Finalize(std::vector<BCData>& bcData, std::vector<o2::zdc::OrbitData>& pData)
627{
628 // Compute scalers for digitized data (works only for triggering channels)
629 for (Int_t ipd = 0; ipd < pData.size(); ipd++) {
630 for (int id = 0; id < NChannels; id++) {
631 pData[ipd].scaler[id] = 0;
632 }
633 }
634 for (int im = 0; im < NModules; im++) {
635 for (int ic = 0; ic < NChPerModule; ic++) {
636 uint32_t mask = 0x1 << (im * NChPerModule + ic);
637 auto id = mModuleConfig->modules[im].channelID[ic];
638 for (uint32_t ibc = 0; ibc < bcData.size(); ibc++) {
639 auto& currBC = bcData[ibc];
640 if ((currBC.triggers & mask) && (mReadoutMask & mask)) {
641 for (Int_t ipd = 0; ipd < pData.size(); ipd++) {
642 if (pData[ipd].ir.orbit == currBC.ir.orbit) {
643 pData[ipd].scaler[id]++;
644 }
645 }
646 }
647 }
648 }
649 }
650 // Default is to mask trigger bits, we can leave them for debugging
651 if (mMaskTriggerBits) {
652 for (uint32_t ibc = 0; ibc < bcData.size(); ibc++) {
653 auto& currBC = bcData[ibc];
654 for (int im = 0; im < NModules; im++) {
655 // Cleanup hits if module has no trigger
656 uint32_t mmask = 0xf << im;
657 if ((currBC.triggers & mTriggerMask & mmask) == 0) {
658 currBC.triggers = currBC.triggers & (~mmask);
659 }
660 }
661 // Cleanup trigger bits for channels that are not readout
662 currBC.triggers &= mReadoutMask;
663 // Printout after cleanup
664 // currBC.print(mTriggerMask);
665 }
666 }
667}
668
669//______________________________________________________________________________
670void Digitizer::findEmptyBunches(const std::bitset<o2::constants::lhc::LHCMaxBunches>& bunchPattern)
671{
672 // Baseline parameters from CTP -> DCS -> ModuleConfig
673 if (mModuleConfig->nBunchAverage > 0 || mModuleConfig->baselineFactor == 0) {
674 mNEmptyBCs = mModuleConfig->nBunchAverage;
675 mPedFactor = 1. / mModuleConfig->baselineFactor;
676 LOG(info) << "Empty bunches from ModuleConfig: " << mNEmptyBCs << " Baseline factor: " << mPedFactor;
677 } else {
678 LOG(fatal) << "Invalid configuration for baseline computation from ModuleConfig object";
679 }
680}
681
682//______________________________________________________________________________
684{
685 // Compute or update baseline reference
686 for (uint32_t id = 0; id < NChannels; id++) {
687 auto base_m = mSimCondition->channels[id].pedestal; // Average pedestal
688 auto base_s = mSimCondition->channels[id].pedestalFluct; // Baseline oscillations
689 auto base_n = mSimCondition->channels[id].pedestalNoise; // Electronic noise
690 // We don't know the time scale of the fluctuations of the baseline. As a rough guess we consider two bunch crossings
691 // sum = 12 * (mNEmptyBCs/2) * (2*base_m) = 12 * mNEmptyBCs * base_m
692 float mean_sum = 12. * mNEmptyBCs * base_m; // Adding 12 samples for bunch crossing
693 float rms_sum = 12. * 2. * base_s * std::sqrt(mNEmptyBCs / 2.); // 2 for fluctuation every 2 BCs
694 float rms_noise_sum = base_n * std::sqrt(12. * mNEmptyBCs);
695 float ped = gRandom->Gaus(mean_sum, rms_sum) + gRandom->Gaus(0, rms_noise_sum);
696 int16_t peds = std::round(ped / mNEmptyBCs / 12. / mModuleConfig->baselineFactor);
697 if (peds < SHRT_MIN) {
698 peds = SHRT_MIN;
699 } else if (peds > SHRT_MAX) {
700 peds = SHRT_MAX;
701 }
702 pdata.data[id] = peds;
703 }
704}
uint64_t orbit
Definition RawEventData.h:6
uint64_t bc
Definition RawEventData.h:5
int32_t i
Header to collect LHC related constants.
std::ostringstream debug
A container to hold and manage MC truth information/labels.
void addElement(uint32_t dataindex, TruthElement const &element, bool noElement=false)
void updatePedestalReference(OrbitData &pdata)
void flush(std::vector< o2::zdc::BCData > &digitsBC, std::vector< o2::zdc::ChannelData > &digitsCh, o2::dataformats::MCTruthContainer< o2::zdc::MCLabel > &labels)
void process(const std::vector< o2::zdc::Hit > &hits, std::vector< o2::zdc::BCData > &digitsBC, std::vector< o2::zdc::ChannelData > &digitsCh, o2::dataformats::MCTruthContainer< o2::zdc::MCLabel > &labels)
Definition Digitizer.cxx:50
void Finalize(std::vector< BCData > &bcData, std::vector< o2::zdc::OrbitData > &pData)
void assignTriggerBits(uint32_t ibc, std::vector< BCData > &bcData)
void findEmptyBunches(const std::bitset< o2::constants::lhc::LHCMaxBunches > &bunchPattern)
GLboolean * data
Definition glcorearb.h:298
GLint GLuint mask
Definition glcorearb.h:291
GLuint id
Definition glcorearb.h:650
constexpr int LHCMaxBunches
constexpr double LHCBunchSpacingNS
constexpr int IdZNCSum
constexpr int IdZPC4
constexpr int IdZPA2
constexpr int NModules
Definition Constants.h:68
constexpr int IdZNA3
constexpr int IdZNA1
constexpr int IdZPA1
constexpr int IdZPCSum
constexpr int IdZNC2
constexpr int toChannel(int det, int tower)
Definition Constants.h:308
constexpr int IdZEM1
constexpr float getTOFCorrection(int det)
Definition Constants.h:126
constexpr int NTimeBinsPerBC
Definition Constants.h:53
constexpr int NChPerModule
Definition Constants.h:69
constexpr int NChannels
Definition Constants.h:65
constexpr int IdZPC2
constexpr int IdZPA4
constexpr const char * channelName(int channel)
Definition Constants.h:318
constexpr int IdZNC1
constexpr int IdZNC3
constexpr uint8_t ALICETriggerMask
Definition Constants.h:66
constexpr int IdZPC1
constexpr int IdZPA3
constexpr int IdZNC4
constexpr int IdZEM2
constexpr int IdZNA2
constexpr int IdZNASum
constexpr int IdDummy
constexpr int ADCMin
Definition Constants.h:76
constexpr int IdZPC3
constexpr int IdZNA4
constexpr int NLinks
Definition Constants.h:70
constexpr std::string_view ChannelNames[]
Definition Constants.h:147
constexpr int TSNH
Definition Constants.h:95
constexpr int ADCMax
Definition Constants.h:76
constexpr int IdZPASum
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
uint32_t orbit
LHC orbit.
uint16_t bc
bunch crossing ID of interaction
int64_t differenceInBC(const InteractionRecord &other) const
double getTimeNS() const
get time in ns from orbit=0/bc=0
static constexpr float ShapeBinWidthInv
std::array< Module, MaxNModules > modules
std::array< bool, MaxChannels > trigChannel
std::array< bool, MaxChannels > readChannel
static constexpr int MaxChannels
std::array< int8_t, MaxChannels > channelID
std::array< int16_t, NChannels > data
Definition OrbitData.h:31
std::array< ChannelSimCondition, NChannels > channels
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
o2::InteractionRecord ir(0, 0)
o2::InteractionRecord ir0(3, 5)
ArrayADC adc