Project
Loading...
Searching...
No Matches
DigiReco.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
12#include <TMath.h>
13#include "Framework/Logger.h"
16
17namespace o2
18{
19namespace zdc
20{
21
23{
24 LOG(info) << "Initialization of ZDC reconstruction";
25 // Load configuration parameters
26 auto& sopt = ZDCSimParam::Instance();
27 mIsContinuous = sopt.continuous;
28 mNBCAHead = mIsContinuous ? sopt.nBCAheadCont : sopt.nBCAheadTrig;
29
30 if (!mModuleConfig) {
31 LOG(fatal) << "Missing ModuleConfig configuration object";
32 return;
33 }
34
35 mTriggerMask = mModuleConfig->getTriggerMask();
36
38
39 if (mTreeDbg) {
40 // Open debug file
41 LOG(info) << "ZDC DigiReco: opening debug output";
42 mDbg = std::make_unique<TFile>("ZDCRecoDbg.root", "recreate");
43 mTDbg = std::make_unique<TTree>("zdcr", "ZDCReco");
44 mTDbg->Branch("zdcr", "RecEventAux", &mRec);
45 }
46
47 // Update reconstruction parameters
48 // auto& ropt=RecoParamZDC::Instance();
50 ropt.print();
51 mRopt = (o2::zdc::RecoParamZDC*)&ropt;
52
53 // Fill maps to decode the pattern of channels with hit
54 for (int itdc = 0; itdc < o2::zdc::NTDCChannels; itdc++) {
55 // If the reconstruction parameters were not manually set
56 if (ropt.tmod[itdc] < 0 || ropt.tch[itdc] < 0) {
57 int isig = TDCSignal[itdc];
58 for (int im = 0; im < NModules; im++) {
59 for (uint32_t ic = 0; ic < NChPerModule; ic++) {
60 if (mModuleConfig->modules[im].channelID[ic] == isig && mModuleConfig->modules[im].readChannel[ic]) {
61 // ropt.updateFromString(TString::Format("RecoParamZDC.tmod[%d]=%d;",itdc,im));
62 // ropt.updateFromString(TString::Format("RecoParamZDC.tch[%d]=%d;",itdc,ic));
63 ropt.tmod[itdc] = im;
64 ropt.tch[itdc] = ic;
65 // Fill mask to identify TDC channels
66 mTDCMask[itdc] = (0x1 << (4 * im + ic));
67 goto next_itdc;
68 }
69 }
70 }
71 } else {
72 // Fill mask to identify TDC channels
73 mTDCMask[itdc] = (0x1 << (4 * ropt.tmod[itdc] + ropt.tch[itdc]));
74 }
75 next_itdc:;
76 if (mVerbosity > DbgZero) {
77 LOG(info) << "TDC " << itdc << "(" << ChannelNames[TDCSignal[itdc]] << ")"
78 << " mod " << ropt.tmod[itdc] << " ch " << ropt.tch[itdc];
79 }
80 }
81
82 // Signal processing
83 // N.B. Function call overrides other settings for low pass filtering
84 if (mLowPassFilterSet == false) {
85 if (ropt.low_pass_filter < 0) {
86 if (!mRecoConfigZDC) {
87 LOG(fatal) << "Configuration of low pass filtering: missing configuration object and no manual override";
88 } else {
89 ropt.low_pass_filter = mRecoConfigZDC->low_pass_filter;
90 }
91 }
92 mLowPassFilter = ropt.low_pass_filter > 0 ? true : false;
93 }
94 LOG(info) << "Low pass filtering is " << (mLowPassFilter ? "enabled" : "disabled");
95
96 // Full interpolation of waveform (N.B. function call overrides other settings)
97 if (mFullInterpolationSet == false) {
98 if (ropt.full_interpolation < 0) {
99 if (!mRecoConfigZDC) {
100 LOG(fatal) << "Configuration of interpolation: missing configuration object and no manual override";
101 } else {
102 ropt.full_interpolation = mRecoConfigZDC->full_interpolation;
103 }
104 }
105 mFullInterpolation = ropt.full_interpolation > 0 ? true : false;
106 }
107 if (ropt.full_interpolation_min_length >= 2) {
108 // Minimum is 2 consecutive bunch crossings
109 mFullInterpolationMinLength = ropt.full_interpolation_min_length;
110 }
111 if (mVerbosity > DbgZero) {
112 LOG(info) << "Full waveform interpolation is " << (mFullInterpolation ? "enabled" : "disabled") << " min length is " << mFullInterpolationMinLength;
113 }
114
115 if (ropt.triggerCondition == 0) {
116 if (!mRecoConfigZDC) {
117 LOG(fatal) << "Trigger condition: missing configuration object and no manual override";
118 } else {
119 mTriggerCondition = mRecoConfigZDC->triggerCondition;
120 }
121 } else {
122 mTriggerCondition = ropt.triggerCondition;
123 }
124
125 if (mTriggerCondition == 0x1) {
126 LOG(info) << "SINGLE trigger condition enforced in reconstruction";
127 } else if (mTriggerCondition == 0x3) {
128 LOG(info) << "DOUBLE trigger condition enforced in reconstruction";
129 } else if (mTriggerCondition == 0x7) {
130 LOG(info) << "TRIPLE trigger condition enforced in reconstruction";
131 } else {
132 LOG(fatal) << "Unsupported trigger condition in ZDC reconstruction: " << mTriggerCondition;
133 }
134
135 if (mCorrSignalSet == false) {
136 if (ropt.corr_signal < 0) {
137 if (!mRecoConfigZDC) {
138 LOG(fatal) << "Configuration of TDC signal correction: missing configuration object and no manual override";
139 } else {
140 ropt.corr_signal = mRecoConfigZDC->corr_signal;
141 }
142 }
143 mCorrSignal = ropt.corr_signal > 0 ? true : false;
144 }
145 if (mVerbosity > DbgZero) {
146 LOG(info) << "TDC signal correction is " << (mCorrSignal ? "enabled" : "disabled");
147 }
148
149 if (mCorrBackgroundSet == false) {
150 if (ropt.corr_background < 0) {
151 if (!mRecoConfigZDC) {
152 LOG(fatal) << "Configuration of TDC pile-up correction: missing configuration object and no manual override";
153 } else {
154 ropt.corr_background = mRecoConfigZDC->corr_background;
155 }
156 }
157 mCorrBackground = ropt.corr_background > 0 ? true : false;
158 }
159 if (mVerbosity > DbgZero) {
160 LOG(info) << "TDC pile-up correction is " << (mCorrBackground ? "enabled" : "disabled");
161 }
162
163 // Trigger configuration
164 for (int itdc = 0; itdc < o2::zdc::NTDCChannels; itdc++) {
165 // If the reconstruction parameters were not manually set
166 if (ropt.tth[itdc] <= 0) {
167 if (!mRecoConfigZDC) {
168 LOG(fatal) << "Trigger threshold for TDC " << itdc << " missing configuration object and no manual override";
169 } else {
170 ropt.tth[itdc] = mRecoConfigZDC->tth[itdc];
171 }
172 }
173 if (ropt.tsh[itdc] <= 0) {
174 if (!mRecoConfigZDC) {
175 LOG(fatal) << "Trigger shift for TDC " << itdc << " missing configuration object and no manual override";
176 } else {
177 ropt.tsh[itdc] = mRecoConfigZDC->tsh[itdc];
178 }
179 }
180 if (mVerbosity > DbgZero) {
181 LOG(info) << itdc << " " << ChannelNames[TDCSignal[itdc]] << " tth= " << mRopt->tth[itdc] << " tsh= " << mRopt->tsh[itdc];
182 }
183 }
184
185 // Extended search configuration
186 if (ropt.setExtendedSearch == false) {
187 if (mRecoConfigZDC == nullptr) {
188 LOG(fatal) << "Extended search configuration: missing configuration object and no manual override";
189 } else {
190 ropt.doExtendedSearch = mRecoConfigZDC->extendedSearch;
191 }
192 }
193 LOG(info) << "Extended search is " << (mRopt->doExtendedSearch ? "ON" : "OFF");
194
195 // Store hits with in-event pile-up (EvE)
196 if (ropt.setStoreEvPileup == false) {
197 if (mRecoConfigZDC == nullptr) {
198 LOG(fatal) << "Storage of hits with in-event pile-up: missing configuration object and no manual override";
199 } else {
200 ropt.doStoreEvPileup = mRecoConfigZDC->storeEvPileup;
201 }
202 }
203 LOG(info) << "Storage of EvE hits is " << (mRopt->doStoreEvPileup ? "ON" : "OFF");
204
205 // TDC calibration
206 // Recentering
207 for (int itdc = 0; itdc < o2::zdc::NTDCChannels; itdc++) {
208 float fval = ropt.tdc_shift[itdc];
209 // If the reconstruction parameters were not manually set
210 if (fval < 0) {
211 // Check if calibration object is present
212 if (!mTDCParam) {
213 LOG(fatal) << "TDC " << itdc << " missing configuration object and no manual override";
214 } else {
215 // Encode TDC shift
216 fval = mTDCParam->getShift(itdc) / o2::zdc::FTDCVal;
217 }
218 }
219 auto val = std::nearbyint(fval);
220 if (val < kMinShort) {
221 LOG(fatal) << "Shift for TDC " << itdc << " " << val << " is out of range";
222 }
223 if (val > kMaxShort) {
224 LOG(fatal) << "Shift for TDC " << itdc << " " << val << " is out of range";
225 }
226 tdc_shift[itdc] = val;
227 if (mVerbosity > DbgZero) {
228 LOG(info) << itdc << " " << ChannelNames[TDCSignal[itdc]] << " shift= " << tdc_shift[itdc] << " i.s. = " << val * o2::zdc::FTDCVal << " ns";
229 }
230 }
231
232 // Amplitude calibration
233 for (int itdc = 0; itdc < o2::zdc::NTDCChannels; itdc++) {
234 float fval = ropt.tdc_calib[itdc];
235 // If the reconstruction parameters were not manually set
236 if (fval < 0) {
237 // Check if calibration object is present
238 if (!mTDCParam) {
239 LOG(fatal) << "TDC amplitude calibration " << itdc << " missing configuration object and no manual override";
240 } else {
241 fval = mTDCParam->getFactor(itdc);
242 }
243 }
244 if (fval <= 0) {
245 LOG(fatal) << "Correction factor for TDC amplitude " << itdc << " " << fval << " is out of range";
246 }
247 tdc_calib[itdc] = fval;
248 fval = ropt.tdc_offset[itdc];
249 // If the reconstruction parameters were not manually set
250 if (fval == -FInfty) {
251 // Check if calibration object is present
252 if (!mTDCParam) {
253 LOG(fatal) << "TDC offset correction " << itdc << " missing configuration object and no manual override";
254 } else {
255 fval = mTDCParam->getOffset(itdc);
256 }
257 }
258 if (fval <= -ADCRange || fval >= ADCRange) {
259 LOG(fatal) << "TDC offset correction " << itdc << " " << fval << " is out of range";
260 }
261 tdc_offset[itdc] = fval;
262 if (mVerbosity > DbgZero) {
263 LOG(info) << itdc << " " << ChannelNames[TDCSignal[itdc]] << " factor= " << tdc_calib[itdc] << " offset= " << tdc_offset[itdc];
264 }
265 }
266
267 // TDC search zone
268 for (int itdc = 0; itdc < o2::zdc::NTDCChannels; itdc++) {
269 // If the reconstruction parameters were not manually set
270 if (ropt.tdc_search[itdc] <= 0) {
271 if (!mRecoConfigZDC) {
272 LOG(fatal) << "Search zone for TDC " << itdc << " missing configuration object and no manual override";
273 } else {
274 ropt.tdc_search[itdc] = mRecoConfigZDC->tdc_search[itdc];
275 }
276 }
277 if (mVerbosity > DbgZero) {
278 LOG(info) << itdc << " " << ChannelNames[TDCSignal[itdc]] << " search= " << ropt.tdc_search[itdc] << " i.s. = " << ropt.tdc_search[itdc] * o2::zdc::FTDCVal << " ns";
279 }
280 }
281
282 // Energy calibration
283 for (int il = 0; il < ChEnergyCalib.size(); il++) {
284 if (ropt.energy_calib[ChEnergyCalib[il]] > 0) {
285 LOG(info) << "Energy Calibration from command line " << ChannelNames[ChEnergyCalib[il]] << " = " << ropt.energy_calib[ChEnergyCalib[il]];
286 } else if (mEnergyParam && mEnergyParam->energy_calib[ChEnergyCalib[il]] > 0) {
287 ropt.energy_calib[ChEnergyCalib[il]] = mEnergyParam->energy_calib[ChEnergyCalib[il]];
288 LOG(info) << "Energy Calibration from CCDB " << ChannelNames[ChEnergyCalib[il]] << " = " << ropt.energy_calib[ChEnergyCalib[il]];
289 } else {
290 if (ChEnergyCalib[il] == CaloCommonPM[ChEnergyCalib[il]]) {
291 // Is a common PM or a ZEM
292 ropt.energy_calib[ChEnergyCalib[il]] = 1;
293 LOG(warning) << "Default Energy Calibration " << ChannelNames[ChEnergyCalib[il]] << " = " << ropt.energy_calib[ChEnergyCalib[il]];
294 } else {
295 // Is one of the analog sums -> same calibration as common PM
296 // N.B. the calibration for common has already been set in the loop
298 LOG(info) << "SUM Energy Calibration " << ChannelNames[ChEnergyCalib[il]] << " = " << ropt.energy_calib[ChEnergyCalib[il]];
299 }
300 }
301 }
302
303 // Tower calibration (intercalibration of towers)
304 // Array ChTowerCalib defines which channels use this calibration: ZN and ZP towers + ZEM2
305 for (int il = 0; il < ChTowerCalib.size(); il++) {
306 if (ropt.tower_calib[ChTowerCalib[il]] > 0) {
307 LOG(info) << "Tower Calibration from command line " << ChannelNames[ChTowerCalib[il]] << " = " << ropt.tower_calib[ChTowerCalib[il]];
308 } else if (mTowerParam && mTowerParam->tower_calib[ChTowerCalib[il]] > 0) {
309 ropt.tower_calib[ChTowerCalib[il]] = mTowerParam->tower_calib[ChTowerCalib[il]];
310 LOG(info) << "Tower Calibration from CCDB " << ChannelNames[ChTowerCalib[il]] << " = " << ropt.tower_calib[ChTowerCalib[il]];
311 } else {
312 ropt.tower_calib[ChTowerCalib[il]] = 1;
313 LOG(warning) << "Default Tower Calibration " << ChannelNames[ChTowerCalib[il]] << " = " << ropt.tower_calib[ChTowerCalib[il]];
314 }
315 }
316
317 // Tower energy calibration
318 // For the common PM and sum is given by the calibration parameters
319 // for the towers is the product of energy calibration of common PM and tower calibration
320 // It is possible to override from command line
321 for (int il = 0; il < ChTowerCalib.size(); il++) {
322 if (ropt.energy_calib[ChTowerCalib[il]] > 0) {
323 LOG(info) << "Tower Energy Calibration set to " << ChannelNames[ChTowerCalib[il]] << " = " << ropt.energy_calib[ChTowerCalib[il]];
324 } else {
326 if (mVerbosity > DbgZero) {
327 LOG(info) << "Tower Energy Calibration " << ChannelNames[ChTowerCalib[il]] << " = " << ropt.energy_calib[ChTowerCalib[il]];
328 }
329 }
330 }
331
332 // ADC offset
333 for (int ic = 0; ic < NChannels; ic++) {
334 if (ropt.adc_offset[ic] > -FInfty) {
335 LOG(info) << "ADC offset from command line " << ChannelNames[ic] << " = " << ropt.adc_offset[ic];
336 } else if (mEnergyParam && mEnergyParam->adc_offset[ic] > -FInfty) {
337 ropt.adc_offset[ic] = mEnergyParam->adc_offset[ic];
338 if (mVerbosity > DbgZero) {
339 LOG(info) << "ADC offset from CCDB " << ChannelNames[ic] << " = " << ropt.adc_offset[ic];
340 }
341 } else {
342 ropt.adc_offset[ic] = 0;
343 LOG(warning) << "Default ADC offset " << ChannelNames[ic] << " = " << ropt.energy_calib[ic];
344 }
345 }
346
347 // Fill maps channel maps for integration
348 for (int ich = 0; ich < NChannels; ich++) {
349 // If the reconstruction parameters were not manually set
350 if (ropt.amod[ich] < 0 || ropt.ach[ich] < 0) {
351 for (int im = 0; im < NModules; im++) {
352 for (uint32_t ic = 0; ic < NChPerModule; ic++) {
353 if (mModuleConfig->modules[im].channelID[ic] == ich && mModuleConfig->modules[im].readChannel[ic]) {
354 ropt.amod[ich] = im;
355 ropt.ach[ich] = ic;
356 // Fill mask to identify all channels
357 mChMask[ich] = (0x1 << (4 * im + ic));
358 goto next_ich;
359 }
360 }
361 }
362 } else {
363 // Fill mask to identify all channels
364 mChMask[ich] = (0x1 << (4 * ropt.amod[ich] + ropt.ach[ich]));
365 }
366 next_ich:;
367 if (mVerbosity > DbgZero) {
368 LOG(info) << "ADC " << ich << "(" << ChannelNames[ich] << ") mod " << ropt.amod[ich] << " ch " << ropt.ach[ich];
369 }
370 }
371
372 // Integration ranges
373 for (int ich = 0; ich < NChannels; ich++) {
374 // If the reconstruction parameters were not manually set
375 if (ropt.beg_int[ich] == DummyIntRange || ropt.end_int[ich] == DummyIntRange) {
376 if (!mRecoConfigZDC) {
377 LOG(fatal) << "Integration for signal " << ich << " missing configuration object and no manual override";
378 } else {
379 ropt.beg_int[ich] = mRecoConfigZDC->beg_int[ich];
380 ropt.end_int[ich] = mRecoConfigZDC->end_int[ich];
381 }
382 }
383 if (ropt.beg_ped_int[ich] == DummyIntRange || ropt.end_ped_int[ich] == DummyIntRange) {
384 if (!mRecoConfigZDC) {
385 LOG(fatal) << "Integration for pedestal " << ich << " missing configuration object and no manual override";
386 } else {
387 ropt.beg_ped_int[ich] = mRecoConfigZDC->beg_ped_int[ich];
388 ropt.end_ped_int[ich] = mRecoConfigZDC->end_ped_int[ich];
389 }
390 }
391 // Thresholds for pedestal
392 // If the reconstruction parameters were not manually set
393 if (ropt.beg_int[ich] == ADCRange) {
394 if (!mRecoConfigZDC) {
395 LOG(fatal) << "Pedestal threshold high for signal " << ich << " missing configuration object and no manual override";
396 } else {
397 ropt.ped_thr_hi[ich] = mRecoConfigZDC->ped_thr_hi[ich];
398 }
399 }
400 if (ropt.beg_int[ich] == ADCRange) {
401 if (!mRecoConfigZDC) {
402 LOG(fatal) << "Pedestal threshold low for signal " << ich << " missing configuration object and no manual override";
403 } else {
404 ropt.ped_thr_lo[ich] = mRecoConfigZDC->ped_thr_lo[ich];
405 }
406 }
407 if (mVerbosity > DbgZero) {
408 LOG(info) << ChannelNames[ich] << " integration: signal=[" << ropt.beg_int[ich] << ":" << ropt.end_int[ich] << "] pedestal=[" << ropt.beg_ped_int[ich] << ":" << ropt.end_ped_int[ich]
409 << "] thresholds (" << ropt.ped_thr_hi[ich] << ", " << ropt.ped_thr_lo[ich] << ")";
410 }
411 }
412 if (mVerbosity > DbgZero && mTDCCorr != nullptr) {
413 mTDCCorr->print();
414 }
415 // Information about reconstruction configuration
416 if (mLowPassFilter == false) {
417 LOG(warning) << "Low pass filtering of signal is disabled";
418 }
419 if (mCorrSignal == false) {
420 LOG(warning) << "TDC signal correction is disabled";
421 }
422 if (mCorrBackground == false) {
423 LOG(warning) << "TDC pile-up correction is disabled";
424 }
425 if (mPedParam == nullptr) {
426 LOG(warning) << "Missing average pedestals";
427 } else {
428 if (mVerbosity > DbgZero) {
429 mPedParam->print();
430 } else {
431 mPedParam->print(false);
432 }
433 }
434} // init
435
437{
438 if (mTreeDbg) {
439 LOG(info) << "o2::zdc::DigiReco: closing debug output";
440 mTDbg->Write();
441 mTDbg.reset();
442 mDbg->Close();
443 mDbg.reset();
444 }
445
447
448 if (mNLonely > 0) {
449 LOG(warn) << "Detected " << mNLonely << " lonely bunches";
450 for (int ib = 0; ib < o2::constants::lhc::LHCMaxBunches; ib++) {
451 if (mLonely[ib]) {
452 LOGF(warn, "lonely bunch %4d #times=%u #trig=%u", ib, mLonely[ib], mLonelyTrig[ib]);
453 }
454 }
455 }
456 for (int ich = 0; ich < NChannels; ich++) {
457 if (mMissingPed[ich] > 0) {
458 LOGF(error, "Missing pedestal for ch %2d %s: %u", ich, ChannelNames[ich], mMissingPed[ich]);
459 }
460 }
461}
462
464{
465 // Prepare tapered sinc function
466 // Lost reference of first interpolating function
467 // Found problems in implementation: the interpolated function is not derivable in sampled points
468 // tsc/TSN =3.75 (~ 4) and TSL*TSN*sqrt(2)/tsc >> 1 (n. of sigma)
469 // const O2_ZDC_DIGIRECO_FLT tsc = 750;
470 // Now using Kaiser function
471 O2_ZDC_DIGIRECO_FLT beta = TMath::Pi() * mAlpha;
472 O2_ZDC_DIGIRECO_FLT norm = 1. / TMath::BesselI0(beta);
473 constexpr int n = TSL * TSN;
474 for (int tsi = 0; tsi <= n; tsi++) {
476 O2_ZDC_DIGIRECO_FLT fs = 1;
477 if (arg1 != 0) {
478 fs = TMath::Sin(arg1) / arg1;
479 }
480 // First tapering window
481 // O2_ZDC_DIGIRECO_FLT arg2 = O2_ZDC_DIGIRECO_FLT(tsi) / tsc;
482 // O2_ZDC_DIGIRECO_FLT fg = TMath::Exp(-arg2 * arg2);
483 // Kaiser window
485 O2_ZDC_DIGIRECO_FLT fg = norm * TMath::BesselI0(beta * TMath::Sqrt(1. - arg2 * arg2));
486 mTS[n + tsi] = fs * fg;
487 mTS[n - tsi] = mTS[n + tsi]; // Function is even
488 }
489 LOG(info) << "Interpolation numeric precision is " << sizeof(O2_ZDC_DIGIRECO_FLT);
490 LOG(info) << "Interpolation alpha = " << mAlpha;
491}
492
493int DigiReco::process(const gsl::span<const o2::zdc::OrbitData>& orbitdata, const gsl::span<const o2::zdc::BCData>& bcdata, const gsl::span<const o2::zdc::ChannelData>& chdata)
494{
495#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
496 LOG(info) << "________________________________________________________________________________";
497 LOG(info) << __func__;
498#endif
499 // We assume that vectors contain data from a full time frame
500 mOrbitData = orbitdata;
501 mBCData = bcdata;
502 mChData = chdata;
503 mInError = false;
504
505 // Initialization of lookup structure for pedestals
506 mOrbit.clear();
507 int norb = mOrbitData.size();
508 if (mVerbosity >= DbgFull) {
509 LOG(info) << "Dump of pedestal data lookup table";
510 }
511 // TODO: send scalers to aggregator
512 uint32_t scaler[NChannels] = {0};
513 for (int iorb = 0; iorb < norb; iorb++) {
514 mOrbit[mOrbitData[iorb].ir.orbit] = iorb;
515 if (mVerbosity >= DbgFull) {
516 LOG(info) << "mOrbitData[" << mOrbitData[iorb].ir.orbit << "] = " << iorb;
517 }
518 // TODO: add only if orbit is good. Here we check only the individual scaler values
519 for (int ich = 0; ich < NChannels; ich++) {
520 if (mOrbitData[iorb].scaler[ich] <= o2::constants::lhc::LHCMaxBunches) {
521 scaler[ich] += mOrbitData[iorb].scaler[ich];
522 }
523 }
524 }
525 if (mVerbosity > DbgMinimal) {
526 for (int ich = 0; ich < NChannels; ich++) {
527 LOG(info) << ChannelNames[ich] << " cnt: " << scaler[ich];
528 }
529 }
530
531 mNBC = mBCData.size();
532 mReco.clear();
533 mReco.resize(mNBC);
534 // Initialization of reco structure
535 for (int ibc = 0; ibc < mNBC; ibc++) {
536 auto& bcr = mReco[ibc];
537#ifdef O2_ZDC_TDC_C_ARRAY
538 for (int itdc = 0; itdc < NTDCChannels; itdc++) {
539 for (int i = 0; i < MaxTDCValues; i++) {
540 bcr.tdcVal[itdc][i] = kMinShort;
541 bcr.tdcAmp[itdc][i] = kMinShort;
542 }
543 }
544#endif
545 auto& bcd = mBCData[ibc];
546 bcr.ir = bcd.ir;
547 int chEnt = bcd.ref.getFirstEntry();
548 for (int ic = 0; ic < bcd.ref.getEntries(); ic++) {
549 auto& chd = mChData[chEnt];
550 if (chd.id > IdDummy && chd.id < NChannels) {
551 bcr.ref[chd.id] = chEnt;
552 }
553 chEnt++;
554 }
555 }
556
557 // Probably this is not necessary
558 // for(int isig=0; isig<NChannels; isig++){
559 // mReco.pattern[isig]=0;
560 // for(int itb=0; itb<NTimeBinsPerBC; itb++){
561 // mReco.fired[isig][itb]=0;
562 // }
563 // for(int isb=0; isb<mNSB; isb++){
564 // mReco.inter[isig][isb]=0;
565 // }
566 // }
567
568 // Assign interaction record and event information
569 for (int ibc = 0; ibc < mNBC; ibc++) {
570 mReco[ibc].ir = mBCData[ibc].ir;
571 mReco[ibc].channels = mBCData[ibc].channels;
572 mReco[ibc].triggers = mBCData[ibc].triggers;
573 for (int isig = 0; isig < NChannels; isig++) {
574 auto ref = mReco[ibc].ref[isig];
575 if (ref == ZDCRefInitVal) {
576 for (int is = 0; is < NTimeBinsPerBC; is++) {
577 mReco[ibc].data[isig][is] = Int16MaxVal;
578 }
579 } else {
580 for (int is = 0; is < NTimeBinsPerBC; is++) {
581 mReco[ibc].data[isig][is] = mChData[ref].data[is];
582 }
583 }
584 }
585 }
586
587 // Low pass filtering
588 if (mLowPassFilter) {
589 // N.B. At the moment low pass filtering is performed only on TDC
590 // signals and not on the rest of the signals
591 lowPassFilter();
592 } else {
593 // Copy samples
594 for (int itdc = 0; itdc < NTDCChannels; itdc++) {
595 auto isig = TDCSignal[itdc];
596 for (int ibc = 0; ibc < mNBC; ibc++) {
597 auto ref_c = mReco[ibc].ref[isig];
598 if (ref_c != ZDCRefInitVal) {
599 for (int is = 0; is < NTimeBinsPerBC; is++) {
600 mReco[ibc].data[isig][is] = mChData[ref_c].data[is];
601 }
602 }
603 }
604 }
605 }
606
607 if (mFullInterpolation) {
608 // Copy remaining channels
609 for (int isig = 0; isig < NChannels; isig++) {
610 int isig_tdc = TDCSignal[SignalTDC[isig]];
611 if (isig == isig_tdc) {
612 // Already copied
613 continue;
614 }
615 for (int ibc = 0; ibc < mNBC; ibc++) {
616 auto ref_c = mReco[ibc].ref[isig];
617 if (ref_c != ZDCRefInitVal) {
618 for (int is = 0; is < NTimeBinsPerBC; is++) {
619 mReco[ibc].data[isig][is] = mChData[ref_c].data[is];
620 }
621 }
622 }
623 }
624 }
625
626 // Find consecutive bunch crossings by taking into account just the presence
627 // of bunch crossing data and then perform signal interpolation in the identified ranges.
628 // With this definition of "consecutive" bunch crossings gaps in the sample data
629 // may be present, therefore in the reconstruction method we take into account for signals
630 // that do not span the entire range
631 int seq_beg = 0;
632 int seq_end = 0;
633 if (mVerbosity > DbgMinimal) {
634 LOG(info) << "Processing ZDC reconstruction for " << mNBC << " bunch crossings";
635 }
636
637 // TDC reconstruction
638 for (int ibc = 0; ibc < mNBC; ibc++) {
639 auto& ir = mBCData[seq_end].ir;
640 auto bcd = mBCData[ibc].ir.differenceInBC(ir);
641 if (bcd < 0) {
642 LOG(error) << "Bunch order error in TDC reconstruction";
643 for (int ibcdump = 0; ibcdump < mNBC; ibcdump++) {
644 LOG(error) << "mBCData[" << ibcdump << "] @ " << mBCData[ibcdump].ir.orbit << "." << mBCData[ibcdump].ir.bc;
645 }
646 LOG(error) << "Orbit number is not increasing " << mBCData[seq_end].ir.orbit << "." << mBCData[seq_end].ir.bc << " followed by " << mBCData[ibc].ir.orbit << "." << mBCData[ibc].ir.bc;
647 return __LINE__;
648 } else if (bcd > 1) {
649 // Detected a gap
650 int rval = reconstructTDC(seq_beg, seq_end);
651 if (rval) {
652 return rval;
653 }
654
655 seq_beg = ibc;
656 seq_end = ibc;
657 } else if (ibc == (mNBC - 1)) {
658 // Last bunch
659 seq_end = ibc;
660 int rval = reconstructTDC(seq_beg, seq_end);
661 if (rval) {
662 return rval;
663 }
664
665 seq_beg = mNBC;
666 seq_end = mNBC;
667 } else {
668 // Look for another bunch
669 seq_end = ibc;
670 }
671#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
672 // Here in order to avoid mixing information
673 mBCData[ibc].print(mTriggerMask);
674#endif
675 }
676
677 // Apply pile-up correction for TDCs to get corrected TDC amplitudes and values
678 correctTDCPile();
679
680 // ADC reconstruction
681 seq_beg = 0;
682 seq_end = 0;
683 for (int ibc = 0; ibc < mNBC; ibc++) {
684 auto& ir = mBCData[seq_end].ir;
685 auto bcd = mBCData[ibc].ir.differenceInBC(ir);
686 if (bcd < 0) {
687 LOG(error) << "Bunch order error in ADC reconstruction";
688 for (int ibcdump = 0; ibcdump < mNBC; ibcdump++) {
689 LOG(error) << "mBCData[" << ibcdump << "] @ " << mBCData[ibcdump].ir.orbit << "." << mBCData[ibcdump].ir.bc;
690 }
691 LOG(error) << "Orbit number is not increasing " << mBCData[seq_end].ir.orbit << "." << mBCData[seq_end].ir.bc << " followed by " << mBCData[ibc].ir.orbit << "." << mBCData[ibc].ir.bc;
692 return __LINE__;
693 } else if (bcd > 1) {
694 // Detected a gap
695 int rval = reconstruct(seq_beg, seq_end);
696 if (rval != 0) {
697 return rval;
698 }
699 seq_beg = ibc;
700 seq_end = ibc;
701 } else if (ibc == (mNBC - 1)) {
702 // Last bunch
703 seq_end = ibc;
704 int rval = reconstruct(seq_beg, seq_end);
705 if (rval != 0) {
706 return rval;
707 }
708 seq_beg = mNBC;
709 seq_end = mNBC;
710 } else {
711 // Look for another bunch
712 seq_end = ibc;
713 }
714 }
715 return 0;
716} // process
717
718void DigiReco::lowPassFilter()
719{
720 // First attempt to low pass filtering uses the average of three consecutive samples
721 // ringing noise has T~6 ns w.r.t. a sampling period of ~ 2 ns
722 // one should get smoothing of the noise
723#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
724 LOG(info) << "________________________________________________________________________________";
725 LOG(info) << __func__;
726#endif
727 constexpr int MaxTimeBin = NTimeBinsPerBC - 1;
728 for (int itdc = 0; itdc < NTDCChannels; itdc++) {
729 auto isig = TDCSignal[itdc];
730 for (int ibc = 0; ibc < mNBC; ibc++) {
731 // Indexes of current, previous and next recorded bunch crossings
732 auto ref_c = mReco[ibc].ref[isig];
733 uint32_t ref_p = ZDCRefInitVal;
734 uint32_t ref_n = ZDCRefInitVal;
735 int64_t bcd_p = ZDCRefInitVal;
736 int64_t bcd_n = ZDCRefInitVal;
737 if (ibc > 0) { // Is not first bunch in list
738 ref_p = mReco[ibc - 1].ref[isig];
739 bcd_p = mReco[ibc].ir.differenceInBC(mReco[ibc - 1].ir); // b.c. number of (ibc) - b.c. number (ibc-1)
740 }
741 if (ibc < (mNBC - 1)) { // Is not last bunch in list
742 ref_n = mReco[ibc + 1].ref[isig];
743 bcd_n = mReco[ibc + 1].ir.differenceInBC(mReco[ibc].ir); // b.c. number of (ibc+1) - b.c. number (ibc)
744 }
745 if (ref_c != ZDCRefInitVal) { // Should always be true
746 for (int is = 0; is < NTimeBinsPerBC; is++) {
747 int32_t sum = mChData[ref_c].data[is];
748 if (is == 0) {
749 sum += mChData[ref_c].data[1];
750 if (ref_p != ZDCRefInitVal && bcd_p == 1) {
751 // Add last sample of previous bunch crossing
752 sum += mChData[ref_p].data[MaxTimeBin];
753 } else {
754 // As a backup we count twice the first sample
755 sum += mChData[ref_c].data[0];
756 }
757 } else if (is == MaxTimeBin) {
758 sum += mChData[ref_c].data[MaxTimeBin - 1];
759 if (ref_n != ZDCRefInitVal && bcd_n == 1) {
760 // Add first sample of next bunch crossing
761 sum += mChData[ref_n].data[0];
762 } else {
763 // As a backup we count twice the last sample
764 sum += mChData[ref_c].data[MaxTimeBin];
765 }
766 } else {
767 // Not on the bunch boundary
768 sum += mChData[ref_c].data[is - 1];
769 sum += mChData[ref_c].data[is + 1];
770 }
771 // Make the average taking into account rounding and sign
772 bool isNegative = sum < 0;
773 if (isNegative) {
774 sum = -sum;
775 }
776 auto mod = sum % 3;
777 sum = sum / 3;
778 if (mod == 2) {
779 sum++;
780 }
781 if (isNegative) {
782 sum = -sum;
783 }
784 // Store filtered values
785 mReco[ibc].data[isig][is] = sum;
786 }
787 }
788 }
789 }
790}
791
792int DigiReco::reconstructTDC(int ibeg, int iend)
793{
794#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
795 LOG(info) << "________________________________________________________________________________";
796 LOG(info) << __func__ << "(" << ibeg << ", " << iend << ") length=" << iend - ibeg + 1;
797 for (int itdc = 0; itdc < NTDCChannels; itdc++) {
798 mAssignedTDC[itdc] = 0;
799 }
800#endif
801 // Apply differential discrimination
802 for (int itdc = 0; itdc < NTDCChannels; itdc++) {
803 // Check if channel has valid data for consecutive bunches in current bunch range
804 // N.B. there are events recorded from ibeg-iend but we are not sure if it is the
805 // case for every TDC channel
806 int istart = -1, istop = -1;
807 // Loop allows for gaps in the data sequence for each TDC channel
808 for (int ibun = ibeg; ibun <= iend; ibun++) {
809 if (mBCData[ibun].channels & mTDCMask[itdc]) { // TDC channel has data for this event
810 if (istart < 0) {
811 istart = ibun;
812 }
813 istop = ibun;
814 } else { // No data from channel
815 // A gap is detected
816 if (istart >= 0 && (istop - istart) > 0) {
817 // Need data for at least two consecutive bunch crossings
818 int rval = 0;
819 if (mRopt->doExtendedSearch) {
820 rval = processTriggerExtended(itdc, istart, istop);
821 } else {
822 rval = processTrigger(itdc, istart, istop);
823 }
824 if (rval) {
825 return rval;
826 }
827 }
828 istart = -1;
829 istop = -1;
830 }
831 }
832 // Check if there are consecutive bunch crossings at the end of group
833 if (istart >= 0 && (istop - istart) > 0) {
834 int rval = 0;
835 if (mRopt->doExtendedSearch) {
836 rval = processTriggerExtended(itdc, istart, istop);
837 } else {
838 rval = processTrigger(itdc, istart, istop);
839 }
840 if (rval) {
841 return rval;
842 }
843 }
844 }
845 // processTrigger(..) calls interpolate(..) that assigns all TDCs
846 // The following TDC processing stage findSignals(..) assumes that time shift
847 // due to pile-up has been corrected because main-main is assumed to be
848 // in position 0
849 // In case we do full interpolation, we process also the channels that are not
850 // considered in TDC list
851 if (mFullInterpolation) {
852 for (int isig = 0; isig < NChannels; isig++) {
853 int isig_tdc = TDCSignal[SignalTDC[isig]];
854 if (isig == isig_tdc) {
855 // Already computed
856 continue;
857 }
858 // Check if channel has valid data for consecutive bunches in current bunch range
859 // N.B. there are events recorded from ibeg-iend but we are not sure if it is the
860 // case for every channel
861 int istart = -1, istop = -1;
862 // Loop allows for gaps in the data sequence for each TDC channel
863 for (int ibun = ibeg; ibun <= iend; ibun++) {
864 if (mBCData[ibun].channels & mChMask[isig]) { // Channel has data for this event
865 if (istart < 0) {
866 istart = ibun;
867 }
868 istop = ibun;
869 } else { // No data from channel
870 // A gap is detected
871 if (istart >= 0 && (istop - istart + 1) >= mFullInterpolationMinLength) {
872 // Need data for at least mFullInterpolationMinLength (two) consecutive bunch crossings
873 int rval = fullInterpolation(isig, istart, istop);
874 if (rval) {
875 return rval;
876 }
877 }
878 istart = -1;
879 istop = -1;
880 }
881 }
882 // Check if there are mFullInterpolationMinLength consecutive bunch crossings at the end of group
883 if (istart >= 0 && (istop - istart + 1) >= mFullInterpolationMinLength) {
884 int rval = fullInterpolation(isig, istart, istop);
885 if (rval) {
886 return rval;
887 }
888 }
889 }
890 }
891#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
892 printf("Assiged TDCs:");
893 bool hasMult = false;
894 for (int itdc = 0; itdc < NTDCChannels; itdc++) {
895 if (mAssignedTDC[itdc] > 0) {
896 printf(" %s:%d", ChannelNames[TDCSignal[itdc]].data(), mAssignedTDC[itdc]);
897 if (mAssignedTDC[itdc] > 1) {
898 hasMult = true;
899 }
900 }
901 }
902 if (hasMult) {
903 printf(" MULTIPLE_ASSIGNMENTS\n");
904 } else {
905 printf("\n");
906 }
907#endif
908 return 0;
909} // reconstructTDC
910
911int DigiReco::reconstruct(int ibeg, int iend)
912{
913#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
914 LOG(info) << "________________________________________________________________________________";
915 LOG(info) << __func__ << "(" << ibeg << ", " << iend << "): " << mReco[ibeg].ir.orbit << "." << mReco[ibeg].ir.bc << " - " << mReco[iend].ir.orbit << "." << mReco[iend].ir.bc;
916#endif
917 // Process consecutive BCs
918 if (ibeg == iend) {
919 mNLonely++;
920 mLonely[mReco[ibeg].ir.bc]++;
921 if (mBCData[ibeg].triggers != 0x0) {
922 mLonelyTrig[mReco[ibeg].ir.bc]++;
923 }
924 // Cannot reconstruct lonely bunch
925 // LOG(info) << "Lonely bunch " << mReco[ibeg].ir.orbit << "." << mReco[ibeg].ir.bc;
926 return 0;
927 }
928#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
929 for (int ibun = ibeg; ibun <= iend; ibun++) {
930 printf("%d CH Mask: 0x%08x TDC data for:", ibun, mBCData[ibun].channels);
931 for (int itdc = 0; itdc < NTDCChannels; itdc++) {
932 if (mBCData[ibun].channels & mTDCMask[itdc]) {
933 printf(" %s", ChannelNames[TDCSignal[itdc]].data());
934 } else {
935 printf(" ");
936 }
937 }
938 printf("\n");
939 }
940#endif
941
942 // After pile-up correction, find signals around main-main that satisfy condition on TDC
943 findSignals(ibeg, iend);
944
945 // For each calorimeter that has detects a collision at the time of main-main
946 // collisions we reconstruct integrated charges and fill output tree
947 for (int ich = 0; ich < NChannels; ich++) {
948 // We consder the longest sequence of acquired bunches that can be readout by
949 // the acquisition in case of isolated events (filling scheme with sigle bunches)
951 // Flags to investigate pile-up
952 bool hasFired[NBCReadOut] = {0}; // Channel has a TDC fired
953 bool hasHit[NBCReadOut] = {0}; // Channel has hit
954 bool hasAuto0[NBCReadOut] = {0}; // Module has Auto_0 trigger bit
955 bool hasAutoM[NBCReadOut] = {0}; // Module has Auto_m trigger bit
956 // Initialize info about previous bunches
957 for (int ibp = 1; ibp < 4; ibp++) {
958 int ibun = ibeg - ibp;
959 // For the time being, we cannot access previous time frame (ibun<0)
960 if (ibun < 0) {
961 break;
962 }
963 auto bcd = mBCData[ibeg].ir.differenceInBC(mBCData[ibun].ir);
964 if (bcd < 1) {
965 LOG(error) << "Bunches are not in ascending order: " << mBCData[ibeg].ir.orbit << "." << mBCData[ibeg].ir.bc << " followed by " << mBCData[ibun].ir.orbit << "." << mBCData[ibun].ir.bc;
966 return __LINE__;
967 }
968 if (bcd > 3) {
969 break;
970 }
971 // Assignment is done taking into account bunch crossing difference
972 ref[bcd] = mReco[ibun].ref[ich];
973 // If channel has no waverform data cannot have a hit or trigger bit assigned
974 // because all channels of a module are acquired if trigger condition is
975 // satisfied
976 if (ref[bcd] == ZDCRefInitVal) {
977 continue;
978 }
979 // This condition is not comprehensive of all pile-up sources
980 // chfired: Fired TDC condition related to channel (e.g. AND of TC and SUM @ main-main)
981 hasFired[bcd] = mReco[ibun].chfired[ich];
982 // Information from hardware autotrigger is not restricted to hits in main-main
983 // but it may extend outside the correct bunch for signals near the bunch edges
984 // hasHit refers to a single channel since it is derived from ChannelDataV0::Hit
985 hasHit[bcd] = mBCData[ibun].triggers & mChMask[ich];
986 // hasAuto0 and hasAutoM are derived from autotrigger decisions and therefore
987 // refer to a module (max 4 channels) and not to a single channel
988 ModuleTriggerMapData mt;
989 mt.w = mBCData[ibun].moduleTriggers[mRopt->amod[ich]];
990 hasAuto0[bcd] = mt.f.Auto_0;
991 hasAutoM[bcd] = mt.f.Auto_m;
992#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
993 printf("%2d %s bcd = %d ibun = %d ibeg = %d ref = %3u %s %s %s\n",
994 ich, ChannelNames[ich].data(), bcd, ibun, ibeg, ref[bcd],
995 hasHit[bcd] ? "H" : "-", hasAuto0[bcd] ? "A0" : "--", hasAutoM[bcd] ? "AM" : "--");
996#endif
997 }
998 // Analyze all bunches
999 for (int ibun = ibeg; ibun <= iend; ibun++) {
1000 updateOffsets(ibun); // Get Orbit pedestals
1001 auto& rec = mReco[ibun];
1002 // Check if the corresponding TDC is fired
1003 ref[0] = mReco[ibun].ref[ich];
1004 hasHit[0] = mBCData[ibun].triggers & mChMask[ich];
1005 ModuleTriggerMapData mt;
1006 mt.w = mBCData[ibun].moduleTriggers[mRopt->amod[ich]];
1007 hasAuto0[0] = mt.f.Auto_0;
1008 hasAutoM[0] = mt.f.Auto_m;
1009 if (rec.chfired[ich]) {
1010 // Check if channel data are present in payload
1011 if (ref[0] < ZDCRefInitVal) {
1012 // Energy reconstruction
1013 // Compute event by event pedestal
1014 bool hasEvPed = false;
1015 float evPed = 0;
1016 if (ibun > ibeg) {
1017 auto ref_m = ref[1];
1018 if (mRopt->beg_ped_int[ich] >= 0 || ref_m < ZDCRefInitVal) {
1019 for (int is = mRopt->beg_ped_int[ich]; is <= mRopt->end_ped_int[ich]; is++) {
1020 if (is < 0) {
1021 // Sample is in previous BC
1022 evPed += float(mChData[ref_m].data[is + NTimeBinsPerBC]);
1023 } else {
1024 // Sample is in current BC
1025 evPed += float(mChData[ref[0]].data[is]);
1026 }
1027 }
1028 evPed /= float(mRopt->end_ped_int[ich] - mRopt->beg_ped_int[ich] + 1);
1029 hasEvPed = true;
1030 }
1031 }
1032 // Pile-up detection using trigger information allows to identify
1033 // the presence of a signal in previous bunch and is module-wise
1034
1035 // Detection of pile-up from previous bunch by comparing event pedestal with reference
1036 // (reference can be orbit or QC). If pile-up is detected we use orbit pedestal
1037 // instead of event pedestal
1038 // TODO: pedestal event could have a TM..
1039 if (hasEvPed && (mSource[ich] == PedOr || mSource[ich] == PedQC)) {
1040 auto pedref = mOffset[ich];
1041 if (evPed > pedref && (evPed - pedref) > mRopt->ped_thr_hi[ich]) {
1042 // Anomalous offset (put a warning but use event pedestal)
1043 rec.offPed[ich] = true;
1044 } else if (evPed < pedref && (pedref - evPed) > mRopt->ped_thr_lo[ich]) {
1045 // Possible presence of pile-up (will need to use orbit pedestal)
1046 rec.pilePed[ich] = true;
1047 }
1048 }
1049 float myPed = std::numeric_limits<float>::infinity();
1050 // TODO:
1051 if (hasEvPed && rec.pilePed[ich] == false) {
1052 myPed = evPed;
1053 rec.adcPedEv[ich] = true;
1054 } else if (mSource[ich] == PedOr) {
1055 myPed = mOffset[ich];
1056 rec.adcPedOr[ich] = true;
1057 } else if (mSource[ich] == PedQC) {
1058 myPed = mOffset[ich];
1059 rec.adcPedQC[ich] = true;
1060 } else {
1061 rec.adcPedMissing[ich] = true;
1062 }
1063 if (myPed < std::numeric_limits<float>::infinity()) {
1064 float sum = 0;
1065 for (int is = mRopt->beg_int[ich]; is <= mRopt->end_int[ich]; is++) {
1066 // TODO: pile-up correction
1067 // TODO: manage signal positioned across boundary
1068 sum += (myPed - float(mChData[ref[0]].data[is]));
1069 }
1070 rec.ezdc[ich] = (sum - mRopt->adc_offset[ich]) * mRopt->energy_calib[ich];
1071 } else {
1072 LOGF(warn, "%d.%-4d CH %2d %s missing pedestal", rec.ir.orbit, rec.ir.bc, ich, ChannelNames[ich].data());
1073 }
1074 } else {
1075 // This could arise from memory corruption or in the case when TDC bits are forced to 1
1076 // due to a broken channel. For example Sum is broken and sum TDC is forced to 1
1077 // Take a very small signal that triggers on one module and not the other
1078 // 1) module 0 triggered by ZNATC is autotriggered
1079 // 2) module 1 triggered by ZNATC is not triggered -> Sum is missing
1080 // 3) sum TDC is forced to 1 and therefore the TDC is fired even if data are not present
1081 // 4) you get this error flag in reconstruction
1082 // This should happen only in case of hardware fault and signals near threshold
1083 // This should be mitigated by having a software threshold higher than the hardware one
1084 rec.adcPedMissing[ich] = true;
1085 if (mVerbosity >= DbgMedium) {
1086 LOGF(warn, "%d.%-4d CH %2d %s ADC missing, TDC present", rec.ir.orbit, rec.ir.bc, ich, ChannelNames[ich].data());
1087 }
1088 }
1089 }
1090 // Shift bunches by 1 taking into account bunch crossing difference
1091 // TODO
1092 // This is a simple shift by 1 position
1093 if (ibun != iend) {
1094 for (int ibcr = NBCReadOut - 1; ibcr > 0; ibcr--) {
1095 ref[ibcr] = ref[ibcr - 1];
1096 hasHit[ibcr] = hasHit[ibcr - 1];
1097 hasAuto0[ibcr] = hasAuto0[ibcr - 1];
1098 hasAutoM[ibcr] = hasAutoM[ibcr - 1];
1099 hasFired[ibcr] = hasFired[ibcr - 1];
1100 }
1101 }
1102 } // Loop on bunches
1103 } // Loop on channels
1104 if (mTreeDbg) {
1105 for (int ibun = ibeg; ibun <= iend; ibun++) {
1106 auto& rec = mReco[ibun];
1107 mRec = rec;
1108 mTDbg->Fill();
1109 }
1110 }
1111 return 0;
1112} // reconstruct
1113
1114void DigiReco::updateOffsets(int ibun)
1115{
1116 auto orbit = mBCData[ibun].ir.orbit;
1117 if (orbit == mOffsetOrbit) {
1118 return;
1119 }
1120 mOffsetOrbit = orbit;
1121
1122 // Reset information about pedestal origin
1123 for (int ich = 0; ich < NChannels; ich++) {
1124 mSource[ich] = PedND;
1125 mOffset[ich] = std::numeric_limits<float>::infinity();
1126 }
1127
1128 // Default TDC pedestal is from orbit
1129 // Look for Orbit pedestal offset
1130 std::map<uint32_t, int>::iterator it = mOrbit.find(orbit);
1131 if (it != mOrbit.end()) {
1132 auto& orbitdata = mOrbitData[it->second];
1133 for (int ich = 0; ich < NChannels; ich++) {
1134 auto myped = float(orbitdata.data[ich]) * mModuleConfig->baselineFactor;
1135 if (myped >= ADCMin && myped <= ADCMax) {
1136 // Pedestal information is present for this channel
1137 mOffset[ich] = myped;
1138 mSource[ich] = PedOr;
1139 }
1140 }
1141 }
1142
1143 // Use average "QC" pedestal if orbit pedestals are missing
1144 if (mPedParam != nullptr) {
1145 for (int ich = 0; ich < NChannels; ich++) {
1146 if (mSource[ich] == PedND) {
1147 auto myped = mPedParam->getCalib(ich);
1148 if (myped >= ADCMin && myped <= ADCMax) {
1149 mOffset[ich] = myped;
1150 mSource[ich] = PedQC;
1151 }
1152 }
1153 }
1154 }
1155
1156 for (int ich = 0; ich < NChannels; ich++) {
1157 if (mSource[ich] == PedND) {
1158 mMissingPed[ich]++;
1159 if (mVerbosity > DbgMinimal) {
1160 LOGF(error, "Missing pedestal for ch %2d %s orbit %u ", ich, ChannelNames[ich], mOffsetOrbit);
1161 }
1162 }
1163#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
1164 LOGF(info, "Pedestal for ch %2d %s orbit %u %s: %f", ich, ChannelNames[ich], mOffsetOrbit, mSource[ich] == PedOr ? "OR" : (mSource[ich] == PedQC ? "QC" : "??"), mOffset[ich]);
1165#endif
1166 }
1167} // updateOffsets
1168
1169int DigiReco::processTrigger(int itdc, int ibeg, int iend)
1170{
1171#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
1172 LOG(info) << __func__ << "(itdc=" << itdc << "[" << ChannelNames[TDCSignal[itdc]] << "], " << ibeg << ", " << iend << "): " << mReco[ibeg].ir.orbit << "." << mReco[ibeg].ir.bc << " - " << mReco[iend].ir.orbit << "." << mReco[iend].ir.bc;
1173#endif
1174 // Extracting TDC information for TDC number itdc, in consecutive bunches from ibeg to iend
1175 int nbun = iend - ibeg + 1;
1176 int maxs2 = NTimeBinsPerBC * nbun - 1;
1177 int shift = mRopt->tsh[itdc];
1178 int thr = mRopt->tth[itdc];
1179
1180 int is1 = 0, is2 = 1;
1181 uint8_t isfired = 0;
1182#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
1183 int16_t m[3] = {0};
1184 int16_t s[3] = {0};
1185#endif
1186 int it1 = 0, it2 = 0, ib1 = -1, ib2 = -1;
1187 for (;;) {
1188 // Shift data
1189 isfired = isfired << 1;
1190#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
1191 for (int i = 2; i > 0; i--) {
1192 m[i] = m[i - 1];
1193 s[i] = s[i - 1];
1194 }
1195#endif
1196 // Bunches and samples that are used in the difference
1197 int b1 = ibeg + is1 / NTimeBinsPerBC;
1198 int b2 = ibeg + is2 / NTimeBinsPerBC;
1199 int s1 = is1 % NTimeBinsPerBC;
1200 int s2 = is2 % NTimeBinsPerBC;
1201 auto ref_m = mReco[b1].ref[TDCSignal[itdc]]; // reference to minuend
1202 auto ref_s = mReco[b2].ref[TDCSignal[itdc]]; // reference to subtrahend
1203 // Check data consistency before computing difference
1204 if (ref_m == ZDCRefInitVal || ref_s == ZDCRefInitVal) {
1205 if (ref_m == ZDCRefInitVal) {
1206 LOG(error) << __func__ << " @ " << __LINE__ << " Missing information for bunch crossing " << mReco[b1].ir.orbit << "." << mReco[b1].ir.bc << " tdc = " << itdc << " sig = " << TDCSignal[itdc];
1207 }
1208 if (ref_s == ZDCRefInitVal) {
1209 LOG(error) << __func__ << " @ " << __LINE__ << " Missing information for bunch crossing " << mReco[b2].ir.orbit << "." << mReco[b2].ir.bc << " tdc = " << itdc << " sig = " << TDCSignal[itdc];
1210 }
1211 return __LINE__;
1212 }
1213 // Check that bunch crossings are indeed the same or consecutive
1214 auto bcd = mReco[b2].ir.differenceInBC(mReco[b1].ir);
1215 if (bcd != 0 && bcd != 1) {
1216 LOG(error) << __func__ << " @ " << __LINE__ << " Large bunch crossing difference " << mReco[b1].ir.orbit << "." << mReco[b1].ir.bc << " followed by " << mReco[b2].ir.orbit << "." << mReco[b2].ir.bc;
1217 return __LINE__;
1218 }
1219 int diff = mChData[ref_m].data[s1] - mChData[ref_s].data[s2];
1220 // Triple trigger condition
1221#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
1222 m[0] = mChData[ref_m].data[s1];
1223 s[0] = mChData[ref_s].data[s2];
1224#endif
1225 if (diff > thr) {
1226 isfired = isfired | 0x1;
1227 if ((isfired & mTriggerCondition) == mTriggerCondition) {
1228 // Fired bit is assigned to the second sample, i.e. to the one that can identify the
1229 // signal peak position
1230 mReco[b2].fired[itdc] |= mMask[s2];
1231#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
1232 if (mTriggerCondition == 0x7) {
1233 printf("0x7 TDC %d[%s] Fired @ %u.%u.s%02u (%5d-%5d)=%5d>%2d && (%5d-%5d)=%5d>%2d && (s%02d:%-5d-s%02d:%-5d)=%5d>%2d\n",
1234 itdc, ChannelNames[TDCSignal[itdc]].data(), mReco[b2].ir.orbit, mReco[b2].ir.bc, s2,
1235 m[2], s[2], (m[2] - s[2]), thr,
1236 m[1], s[1], (m[1] - s[1]), thr,
1237 s1, m[0], s2, s[0], diff, thr);
1238 } else if (mTriggerCondition == 0x3) {
1239 printf("0x3 TDC %d[%s] Fired @ %u.%u.s%02u (%5d-%5d)=%5d>%2d && (s%02d:%-5d-s%02d:(%-5d))=%5d>%2d\n",
1240 itdc, ChannelNames[TDCSignal[itdc]].data(), mReco[b2].ir.orbit, mReco[b2].ir.bc, s2,
1241 m[1], s[1], (m[1] - s[1]), thr,
1242 s1, m[0], s2, s[0], diff, thr);
1243 } else if (mTriggerCondition == 0x1) {
1244 printf("0x1 TDC %d[%s] Fired @ %u.%u.s%02u (%d-(%d))=%d>%d && (%d-(%d))=%d>%d && (s%d:%d-s%d:(%d))=%d>%d\n",
1245 itdc, ChannelNames[TDCSignal[itdc]].data(), mReco[b2].ir.orbit, mReco[b2].ir.bc, s2,
1246 s1, m[0], s2, s[0], diff, thr);
1247 }
1248#endif
1249 }
1250 }
1251 if (is2 >= shift) {
1252 is1++;
1253 }
1254 if (is2 < maxs2) {
1255 is2++;
1256 }
1257 if (is1 == maxs2) {
1258 break;
1259 }
1260 }
1261 return interpolate(itdc, ibeg, iend);
1262} // processTrigger
1263
1264int DigiReco::processTriggerExtended(int itdc, int ibeg, int iend)
1265{
1266 auto isig = TDCSignal[itdc];
1267#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
1268 LOG(info) << __func__ << "(itdc=" << itdc << "[" << ChannelNames[isig] << "], " << ibeg << ", " << iend << "): " << mReco[ibeg].ir.orbit << "." << mReco[ibeg].ir.bc << " - " << mReco[iend].ir.orbit << "." << mReco[iend].ir.bc;
1269#endif
1270 // Extends search zone at the beginning of sequence. Need pedestal information.
1271 // For simplicity we use information for current bunch/orbit
1272 updateOffsets(ibeg);
1273 if (mSource[isig] == PedND) {
1274 // Fall back to normal trigger
1275 // Message will be produced when computing amplitude (if a hit is found in this bunch)
1276 // In this framework we have a potential undetected inefficiency, however pedestal
1277 // problem is a serious problem and will be noticed anyway
1278 return processTrigger(itdc, ibeg, iend);
1279 }
1280
1281 int nbun = iend - ibeg + 1;
1282 int maxs2 = NTimeBinsPerBC * nbun - 1;
1283 int shift = mRopt->tsh[itdc];
1284 int thr = mRopt->tth[itdc];
1285
1286 int is1 = -shift, is2 = 0;
1287 uint8_t isfired = 0;
1288#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
1289 int16_t m[3] = {0};
1290 int16_t s[3] = {0};
1291#endif
1292
1293 int it1 = 0, it2 = 0, ib1 = -1, ib2 = -1;
1294 for (;;) {
1295 // Shift data
1296 isfired = isfired << 1;
1297#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
1298 for (int i = 2; i > 0; i--) {
1299 m[i] = m[i - 1];
1300 s[i] = s[i - 1];
1301 }
1302#endif
1303 // Bunches and samples that are used in the difference
1304 int diff = 0;
1305 int b2 = ibeg + is2 / NTimeBinsPerBC;
1306 int s2 = is2 % NTimeBinsPerBC;
1307 auto ref_s = mReco[b2].ref[isig]; // reference to subtrahend
1308 int s1 = is1 % NTimeBinsPerBC;
1309 if (is1 < 0) {
1310 if (ref_s == ZDCRefInitVal) {
1311 LOG(error) << __func__ << " @ " << __LINE__ << " Missing information for bunch crossing " << mReco[b2].ir.orbit << "." << mReco[b2].ir.bc << " sig = " << isig;
1312 return __LINE__;
1313 }
1314 diff = mOffset[isig] - mChData[ref_s].data[s2];
1315#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
1316 m[0] = mOffset[isig];
1317 s[0] = mChData[ref_s].data[s2];
1318#endif
1319 } else {
1320 int b1 = ibeg + is1 / NTimeBinsPerBC;
1321 auto ref_m = mReco[b1].ref[TDCSignal[itdc]]; // reference to minuend
1322 // Check data consistency before computing difference
1323 if (ref_m == ZDCRefInitVal || ref_s == ZDCRefInitVal) {
1324 LOG(error) << __func__ << " @ " << __LINE__ << " Missing information for bunch crossing " << mReco[b1].ir.orbit << "." << mReco[b1].ir.bc << " tdc = " << itdc << " sig = " << TDCSignal[itdc];
1325 return __LINE__;
1326 }
1327 // Check that bunch crossings are indeed the same or consecutive
1328 auto bcd = mReco[b2].ir.differenceInBC(mReco[b1].ir);
1329 if (bcd != 0 && bcd != 1) {
1330 LOG(error) << __func__ << ": large bunch crossing difference " << mReco[b1].ir.orbit << "." << mReco[b1].ir.bc << " followed by " << mReco[b2].ir.orbit << "." << mReco[b2].ir.bc;
1331 return __LINE__;
1332 }
1333 diff = mChData[ref_m].data[s1] - mChData[ref_s].data[s2];
1334#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
1335 m[0] = mChData[ref_m].data[s1];
1336 s[0] = mChData[ref_s].data[s2];
1337#endif
1338 }
1339 if (diff > thr) {
1340 isfired = isfired | 0x1;
1341 // Check trigger condition
1342 if ((isfired & mTriggerCondition) == mTriggerCondition) {
1343 // Fired bit is assigned to the second sample, i.e. to the one that can identify the
1344 // signal peak position
1345 mReco[b2].fired[itdc] |= mMask[s2];
1346#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
1347 if (mTriggerCondition == 0x7) {
1348 printf("0x7E TDC %d[%s] Fired @ %u.%u.s%02u (%5d-%5d)=%5d>%2d && (%5d-%5d)=%5d>%5d && (s%02d:%-5d-s%02d:%-5d)=%-5d>%2d\n",
1349 itdc, ChannelNames[TDCSignal[itdc]].data(), mReco[b2].ir.orbit, mReco[b2].ir.bc, s2,
1350 m[2], s[2], (m[2] - s[2]), thr,
1351 m[1], s[1], (m[1] - s[1]), thr,
1352 s1, m[0], s2, s[0], diff, thr);
1353 } else if (mTriggerCondition == 0x3) {
1354 printf("0x3E TDC %d[%s] Fired @ %u.%u.s%02u (%5d-%5d)=%5d>%2d && (s%02d:%-5d-s%02d:(%-5d))=%-5d>%2d\n",
1355 itdc, ChannelNames[TDCSignal[itdc]].data(), mReco[b2].ir.orbit, mReco[b2].ir.bc, s2,
1356 m[1], s[1], (m[1] - s[1]), thr,
1357 s1, m[0], s2, s[0], diff, thr);
1358 } else if (mTriggerCondition == 0x1) {
1359 printf("0x1E TDC %d[%s] Fired @ %u.%u.s%02u (%5d-(%5d))=%5d>%2d && (%5d-(%5d))=%5d>%2d && (s%d:%5d-s%d:(%5d))=%5d>%2d\n",
1360 itdc, ChannelNames[TDCSignal[itdc]].data(), mReco[b2].ir.orbit, mReco[b2].ir.bc, s2,
1361 s1, m[0], s2, s[0], diff, thr);
1362 }
1363#endif
1364 }
1365 }
1366 is1++;
1367 if (is2 < maxs2) {
1368 is2++;
1369 }
1370 if (is1 == maxs2) {
1371 break;
1372 }
1373 }
1374 return interpolate(itdc, ibeg, iend);
1375} // processTriggerExtended
1376
1377// Interpolation for single point
1378O2_ZDC_DIGIRECO_FLT DigiReco::getPoint(int isig, int ibeg, int iend, int i)
1379{
1380 constexpr int nsbun = TSN * NTimeBinsPerBC; // Total number of interpolated points per bunch crossing
1381 if (i >= mNtot || i < 0) {
1382 LOG(error) << "Error addressing isig=" << isig << " i=" << i << " mNtot=" << mNtot;
1383 mInError = true;
1384 return std::numeric_limits<float>::infinity();
1385 }
1386 // Constant extrapolation at the beginning and at the end of the array
1387 if (i < TSNH) {
1388 // Return value of first sample
1389 return mFirstSample;
1390 } else if (i >= mIlast) {
1391 // Return value of last sample
1392 return mLastSample;
1393 } else {
1394 // Identification of the point to be assigned
1395 int ibun = ibeg + i / nsbun;
1396 // Interpolation between acquired points (N.B. from 0 to mNint)
1397 i = i - TSNH;
1398 int im = i % TSN;
1399 if (im == 0) {
1400 // This is an acquired point
1401 int ip = (i / TSN) % NTimeBinsPerBC;
1402 int ib = ibeg + (i / TSN) / NTimeBinsPerBC;
1403 if (ib != ibun) {
1404 LOG(error) << "ib=" << ib << " ibun=" << ibun;
1405 mInError = true;
1406 return std::numeric_limits<float>::infinity();
1407 }
1408 return mReco[ibun].data[isig][ip]; // Filtered point
1409 // return mChData[mReco[ibun].ref[isig]].data[ip]; // Original point
1410 } else {
1411 // Do the actual interpolation
1413 int ip = i / TSN;
1415 for (int is = TSN - im, ii = ip - TSL + 1; is < NTS; is += TSN, ii++) {
1416 // Default is first point in the array
1417 O2_ZDC_DIGIRECO_FLT yy = mFirstSample;
1418 if (ii > 0) {
1419 if (ii < mNsam) {
1420 int ip = ii % NTimeBinsPerBC;
1421 int ib = ibeg + ii / NTimeBinsPerBC;
1422 yy = mReco[ib].data[isig][ip];
1423 // yy = mChData[mReco[ib].ref[isig]].data[ip];
1424 } else {
1425 // Last acquired point
1426 yy = mLastSample;
1427 }
1428 }
1429 sum += mTS[is];
1430 y += yy * mTS[is];
1431 }
1432 y = y / sum;
1433 return y;
1434 }
1435 }
1436}
1437
1438void DigiReco::setPoint(int isig, int ibeg, int iend, int i)
1439{
1440 // This function needs to be used only if mFullInterpolation is true otherwise the
1441 // vectors are not allocated
1442 if (!mFullInterpolation) {
1443 LOG(fatal) << __func__ << " call with mFullInterpolation = " << mFullInterpolation;
1444 return;
1445 }
1446 constexpr int nsbun = TSN * NTimeBinsPerBC; // Total number of interpolated points per bunch crossing
1447 if (i >= mNtot || i < 0) {
1448 LOG(error) << "Error addressing signal isig=" << isig << " i=" << i << " mNtot=" << mNtot;
1449 mInError = true;
1450 return;
1451 }
1452 // Constant extrapolation at the beginning and at the end of the array
1453 if (i < TSNH) {
1454 // Assign value of first sample
1455 mReco[ibeg].inter[isig][i] = mFirstSample;
1456 } else if (i >= mIlast) {
1457 // Assign value of last sample
1458 int isam = i % nsbun;
1459 mReco[iend].inter[isig][isam] = mLastSample;
1460 } else {
1461 // Identification of the point to be assigned
1462 int ibun = ibeg + i / nsbun;
1463 int isam = i % nsbun;
1464 mReco[ibun].inter[isig][isam] = getPoint(isig, ibeg, iend, i);
1465 }
1466} // setPoint
1467
1468int DigiReco::fullInterpolation(int isig, int ibeg, int iend)
1469{
1470 // Interpolation of signal isig, in consecutive bunches from ibeg to iend
1471 // This function works for all signals and does not evaluate trigger
1472 // You need to call interpolate(int itdc... for the TDC signals
1473#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
1474 LOG(info) << __func__ << "(isig=" << isig << "[" << ChannelNames[isig] << "], " << ibeg << ", " << iend << "): " << mReco[ibeg].ir.orbit << "." << mReco[ibeg].ir.bc << " - " << mReco[iend].ir.orbit << "." << mReco[iend].ir.bc;
1475#endif
1476
1477 // TODO: get data from preceding time frame in case there are bunches
1478 // with signal at the beginning of the first orbit of a time frame
1479
1480 constexpr int MaxTimeBin = NTimeBinsPerBC - 1; //< number of samples per BC
1481
1482 // Set data members for interpolation of the current channel
1483 mNbun = iend - ibeg + 1; // Number of adjacent bunches
1484 mNsam = mNbun * NTimeBinsPerBC; // Number of acquired samples
1485 mNtot = mNsam * TSN; // Total number of points in the interpolated arrays
1486 mNint = (mNbun * NTimeBinsPerBC - 1) * TSN; // Total points in the interpolation region (-1)
1487 mIlast = mNtot - TSNH; // Index of last acquired sample
1488
1489 // At this level there should be no need to check if the channel is connected
1490 // since a fatal should have been raised already
1491 for (int ibun = ibeg; ibun <= iend; ibun++) {
1492 auto ref = mReco[ibun].ref[isig];
1493 if (ref == ZDCRefInitVal) {
1494 LOG(error) << __func__ << " @ " << __LINE__ << " Missing information for bunch crossing " << mReco[ibun].ir.orbit << "." << mReco[ibun].ir.bc << " sig = " << isig;
1495 return __LINE__;
1496 }
1497 }
1498
1499 mFirstSample = mReco[ibeg].data[isig][0];
1500 mLastSample = mReco[iend].data[isig][MaxTimeBin];
1501
1502 // Allocate and fill array of interpolated points
1503 for (int ibun = ibeg; ibun <= iend; ibun++) {
1504 mReco[ibun].allocate(isig);
1505 }
1506 for (int i = 0; i < mNtot; i++) {
1507 setPoint(isig, ibeg, iend, i);
1508 }
1509 if (mInError) {
1510 return __LINE__;
1511 }
1512 return 0;
1513}
1514
1515int DigiReco::interpolate(int itdc, int ibeg, int iend)
1516{
1517 // Interpolation of TDC channel itdc, in consecutive bunches from ibeg to iend
1518 int isig = TDCSignal[itdc];
1519
1520#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
1521 LOG(info) << __func__ << "(itdc=" << itdc << "[" << ChannelNames[isig] << "], " << ibeg << ", " << iend << "): " << mReco[ibeg].ir.orbit << "." << mReco[ibeg].ir.bc << " - " << mReco[iend].ir.orbit << "." << mReco[iend].ir.bc;
1522#endif
1523
1524 // TODO: get data from preceding time frame in case there are bunches
1525 // with signal at the beginning of the first orbit of a time frame
1526
1527 constexpr int MaxTimeBin = NTimeBinsPerBC - 1; //< number of samples per BC
1528 constexpr int nsbun = TSN * NTimeBinsPerBC; // Total number of interpolated points per bunch crossing
1529
1530 // Set data members for interpolation of the current TDC
1531 mNbun = iend - ibeg + 1; // Number of adjacent bunches
1532 mNsam = mNbun * NTimeBinsPerBC; // Number of acquired samples
1533 mNtot = mNsam * TSN; // Total number of points in the interpolated arrays
1534 mNint = (mNbun * NTimeBinsPerBC - 1) * TSN; // Total points in the interpolation region (-1)
1535 mIlast = mNtot - TSNH; // Index of last acquired sample
1536
1537 constexpr int nsp = 5; // Number of points to be searched
1538
1539 // At this level there should be no need to check if the channel is connected
1540 // since a fatal should have been raised already
1541 for (int ibun = ibeg; ibun <= iend; ibun++) {
1542 auto ref = mReco[ibun].ref[isig];
1543 if (ref == ZDCRefInitVal) {
1544 LOG(error) << __func__ << " @ " << __LINE__ << " Missing information for bunch crossing " << mReco[ibun].ir.orbit << "." << mReco[ibun].ir.bc << " sig = " << isig;
1545 return __LINE__;
1546 }
1547 }
1548
1549 // auto ref_beg = mReco[ibeg].ref[isig];
1550 // auto ref_end = mReco[iend].ref[isig];
1551 // mFirstSample = mChData[ref_beg].data[0]; // Original points
1552 // mLastSample = mChData[ref_end].data[MaxTimeBin]; // Original points
1553
1554 mFirstSample = mReco[ibeg].data[isig][0];
1555 mLastSample = mReco[iend].data[isig][MaxTimeBin];
1556
1557 // mFullInterpolation turns on full interpolation for debugging
1558 // otherwise the interpolation is performed only around actual signal
1559 if (mFullInterpolation) {
1560 for (int ibun = ibeg; ibun <= iend; ibun++) {
1561 mReco[ibun].allocate(isig);
1562 }
1563 for (int i = 0; i < mNtot; i++) {
1564 setPoint(isig, ibeg, iend, i);
1565 }
1566 }
1567 if (mInError) {
1568 return __LINE__;
1569 }
1570 // Looking for a local maximum in a search zone
1571 O2_ZDC_DIGIRECO_FLT amp = std::numeric_limits<float>::infinity(); // Amplitude to be stored
1572 int isam_amp = 0; // Sample at maximum amplitude (relative to beginning of group)
1573 int ip_old = -1, ip_cur = -1, ib_cur = -1; // Current and old points
1574 bool is_searchable = false; // Flag for point in the search zone for maximum amplitude
1575 bool was_searchable = false; // Flag for point in the search zone for maximum amplitude
1576 int ib[nsp] = {-1, -1, -1, -1, -1};
1577 int ip[nsp] = {-1, -1, -1, -1, -1};
1578 // N.B. Points at the extremes are constant therefore no local maximum
1579 // can occur in these two regions
1580 for (int i = 0; i < mNint; i += mInterpolationStep) {
1581 int isam = i + TSNH;
1582 // Check if trigger is fired for this point
1583 // For the moment we don't take into account possible extensions of the search zone
1584 // ip_cur can span several bunches and is used just to identify transitions
1585 ip_cur = isam / TSN;
1586 // Speed up computation
1587 if (ip_cur != ip_old) {
1588 ip_old = ip_cur;
1589 for (int j = 0; j < 5; j++) {
1590 ib[j] = -1;
1591 ip[j] = -1;
1592 }
1593 // There are three possible triple conditions that involve current point (middle is current point)
1594 ip[2] = ip_cur % NTimeBinsPerBC;
1595 ib[2] = ibeg + ip_cur / NTimeBinsPerBC;
1596 ib_cur = ib[2];
1597 if (ip[2] > 0) {
1598 ip[1] = ip[2] - 1;
1599 ib[1] = ib[2];
1600 } else if (ip[2] == 0) {
1601 if (ib[2] > ibeg) {
1602 ib[1] = ib[2] - 1;
1603 ip[1] = MaxTimeBin;
1604 }
1605 }
1606 if (ip[1] > 0) {
1607 ip[0] = ip[1] - 1;
1608 ib[0] = ib[1];
1609 } else if (ip[1] == 0) {
1610 if (ib[1] > ibeg) {
1611 ib[0] = ib[1] - 1;
1612 ip[0] = MaxTimeBin;
1613 }
1614 }
1615 if (ip[2] < MaxTimeBin) {
1616 ip[3] = ip[2] + 1;
1617 ib[3] = ib[2];
1618 } else if (ip[2] == MaxTimeBin) {
1619 if (ib[2] < iend) {
1620 ib[3] = ib[2] + 1;
1621 ip[3] = 0;
1622 }
1623 }
1624 if (ip[3] < MaxTimeBin) {
1625 ip[4] = ip[3] + 1;
1626 ib[4] = ib[3];
1627 } else if (ip[3] == MaxTimeBin) {
1628 if (ib[3] < iend) {
1629 ib[4] = ib[3] + 1;
1630 ip[4] = 0;
1631 }
1632 }
1633 // meet the threshold condition
1634 was_searchable = is_searchable;
1635 // Search conditions with list of allowed patterns
1636 // No need to double check ib[?] and ip[?] because either we assign both or none
1637 uint16_t triggered = 0x0000;
1638 for (int j = 0; j < 5; j++) {
1639 if (ib[j] >= 0 && (mReco[ib[j]].fired[itdc] & mMask[ip[j]]) > 0) {
1640 triggered |= (0x1 << j);
1641 }
1642 }
1643 // Reject conditions:
1644 // 00000
1645 // 10001
1646 // One among 10000 and 00001
1647 // Accept conditions:
1648 constexpr uint16_t accept[14] = {
1649 // 0x01, // 00001 extend search zone before maximum
1650 0x02, // 00010
1651 0x04, // 00100
1652 0x08, // 01000
1653 0x10, // 10000 extend after
1654 0x03, // 00011
1655 0x06, // 00110
1656 0x0c, // 01100
1657 0x18, // 11000
1658 0x07, // 00111
1659 0x0e, // 01110
1660 0x1c, // 11100
1661 0x0f, // 01111
1662 0x1e, // 11110
1663 0x1f // 11111
1664 };
1665 // All other are not correct (->reject)
1666 is_searchable = 0;
1667 if (triggered != 0) {
1668 for (int j = 0; j < 14; j++) {
1669 if (triggered == accept[j]) {
1670 is_searchable = 1;
1671 break;
1672 }
1673 }
1674 }
1675 }
1676 // We do not restrict search zone around expected main-main collision
1677 // because we would like to be able to identify pile-up from collisions
1678 // with satellites
1679 // This is buggy because you can get just one TDC for each search zone
1680 // If more than one signal is present, just the largest one is saved
1681 // To overcome this limitation one should implement more refined analysis
1682 // techniques to perform shape recognition
1683
1684 // If we exit from searching zone
1685 if (was_searchable && !is_searchable) {
1686 if (amp <= ADCMax) {
1687 if (mInterpolationStep > 1) {
1688 // Refine peak search
1689 int sbeg = (isam_amp - TSNH);
1690 int send = (isam_amp + TSNH);
1691 if (sbeg < 0) {
1692 sbeg = 0;
1693 send = sbeg + TSN;
1694 }
1695 if (send > (mNint + TSNH)) {
1696 send = mNint + TSNH;
1697 sbeg = send - TSN;
1698 }
1699 if (sbeg < 0) {
1700 sbeg = 0;
1701 }
1702 for (int spos = sbeg; spos < send; spos++) {
1703 // Perform interpolation for the searched point
1704 O2_ZDC_DIGIRECO_FLT myval = getPoint(isig, ibeg, iend, spos);
1705 // Get local minimum of waveform
1706 if (myval < amp) {
1707 amp = myval;
1708 isam_amp = spos;
1709 }
1710 }
1711 }
1712 // Store identified peak
1713 int ibun = ibeg + isam_amp / nsbun;
1714 updateOffsets(ibun);
1715 // At this level offsets are from Orbit or QC therefore
1716 // the TDC amplitude and time are affected by pile-up from
1717 // previous collisions. Pile up correction needs to be
1718 // performed after all signals have been identified
1719 if (mSource[isig] != PedND) {
1720 amp = mOffset[isig] - amp;
1721 } else {
1722 LOGF(error, "%u.%-4d Missing pedestal for TDC %d %s ", mBCData[ibun].ir.orbit, mBCData[ibun].ir.bc, itdc, ChannelNames[TDCSignal[itdc]]);
1723 amp = std::numeric_limits<float>::infinity();
1724 }
1725 int tdc = isam_amp % nsbun;
1726 assignTDC(ibun, ibeg, iend, itdc, tdc, amp);
1727 }
1728 amp = std::numeric_limits<float>::infinity();
1729 isam_amp = 0;
1730 was_searchable = 0;
1731 }
1732 if (is_searchable) {
1733 int mysam = isam % nsbun;
1734 O2_ZDC_DIGIRECO_FLT myval;
1735 if (mFullInterpolation) {
1736 // Already interpolated
1737 myval = mReco[ib_cur].inter[isig][mysam];
1738 } else {
1739 // Perform interpolation for the searched point
1740 myval = getPoint(isig, ibeg, iend, isam);
1741 }
1742 // Get local minimum of waveform
1743 if (myval < amp) {
1744 amp = myval;
1745 isam_amp = isam;
1746 }
1747 }
1748 } // Loop on interpolated points
1749 if (mInError) {
1750 return __LINE__;
1751 }
1752
1753 // Trigger flag still present at the of the scan
1754 if (is_searchable) {
1755 // Add last identified peak
1756 if (amp <= ADCMax) {
1757 if (mInterpolationStep > 1) {
1758 // Refine peak search
1759 int sbeg = (isam_amp - TSNH);
1760 int send = (isam_amp + TSNH);
1761 if (sbeg < 0) {
1762 sbeg = 0;
1763 send = sbeg + TSN;
1764 }
1765 if (send > (mNint + TSNH)) {
1766 send = mNint + TSNH;
1767 sbeg = send - TSN;
1768 }
1769 if (sbeg < 0) {
1770 sbeg = 0;
1771 }
1772 for (int spos = sbeg; spos < send; spos++) {
1773 // Perform interpolation for the searched point
1774 O2_ZDC_DIGIRECO_FLT myval = getPoint(isig, ibeg, iend, spos);
1775 // Get local minimum of waveform
1776 if (myval < amp) {
1777 amp = myval;
1778 isam_amp = spos;
1779 }
1780 }
1781 }
1782 // Store identified peak
1783 int ibun = ibeg + isam_amp / nsbun;
1784 updateOffsets(ibun);
1785 if (mSource[isig] != PedND) {
1786 amp = mOffset[isig] - amp;
1787 } else {
1788 LOGF(error, "%u.%-4d Missing pedestal for TDC %d %s ", mBCData[ibun].ir.orbit, mBCData[ibun].ir.bc, itdc, ChannelNames[TDCSignal[itdc]]);
1789 amp = std::numeric_limits<float>::infinity();
1790 }
1791 int tdc = isam_amp % nsbun;
1792 assignTDC(ibun, ibeg, iend, itdc, tdc, amp);
1793 }
1794 }
1795 if (mInError) {
1796 return __LINE__;
1797 }
1798 // TODO: add logic to assign TDC in presence of overflow
1799 return 0;
1800} // interpolate
1801
1802void DigiReco::assignTDC(int ibun, int ibeg, int iend, int itdc, int tdc, float amp)
1803{
1804 constexpr int nsbun = TSN * NTimeBinsPerBC; // Total number of interpolated points per bunch crossing
1805 constexpr int tdc_max = nsbun / 2;
1806 constexpr int tdc_min = -tdc_max;
1807
1808 auto& rec = mReco[ibun];
1809
1810 // Flag hit position in sequence
1811 if (ibun == ibeg) {
1812 rec.isBeg[itdc] = true;
1813 }
1814 if (ibun == iend) {
1815 rec.isEnd[itdc] = true;
1816 }
1817
1818 int isig = TDCSignal[itdc];
1819 float TDCVal = tdc;
1820 float TDCAmp = amp;
1821
1822 // Correct for time bias on single signals
1823 float TDCValCorr = 0, TDCAmpCorr = 0;
1824 if (mCorrSignal == false || correctTDCSignal(itdc, tdc, amp, TDCValCorr, TDCAmpCorr, rec.isBeg[itdc], rec.isEnd[itdc]) != 0) {
1825 // Cannot apply amplitude correction for isolated signal -> Flag error condition
1826 rec.tdcSigE[TDCSignal[itdc]] = true;
1827 } else {
1828 TDCVal = TDCValCorr;
1829 // Cannot correct amplitude if pedestal is missing
1830 if (!rec.tdcPedMissing[isig]) {
1831 TDCAmp = TDCAmpCorr;
1832 }
1833 }
1834
1835 // rec.genericE[TDCSignal[itdc]] = true; // Produce fake error
1836
1837 // TDC calibration
1838 TDCVal = TDCVal - tdc_shift[itdc];
1839 if (!rec.tdcPedMissing[isig]) {
1840 // Cannot correct amplitude if pedestal is missing
1841 TDCAmp = (TDCAmp - tdc_offset[itdc]) * tdc_calib[itdc];
1842 }
1843
1844 // Encode amplitude and assign
1845 rec.TDCVal[itdc].push_back(TDCVal);
1846 rec.TDCAmp[itdc].push_back(TDCAmp);
1847 int& ihit = mReco[ibun].ntdc[itdc];
1848#ifdef O2_ZDC_TDC_C_ARRAY
1849 if (ihit < MaxTDCValues) {
1850 rec.tdcVal[itdc][ihit] = TDCVal;
1851 rec.tdcAmp[itdc][ihit] = TDCAmp;
1852 } else {
1853 LOG(error) << rec.ir.orbit << "." << rec.ir.bc << " ibun=" << ibun << " itdc=" << itdc << " tdc=" << tdc << " TDCVal=" << TDCVal * o2::zdc::FTDCVal << " TDCAmp=" << TDCAmp << " OVERFLOW";
1854 }
1855#endif
1856 // Assign info about pedestal subtration
1857 if (mSource[isig] == PedOr) {
1858 rec.tdcPedOr[isig] = true;
1859 } else if (mSource[isig] == PedQC) {
1860 rec.tdcPedQC[isig] = true;
1861 } else if (mSource[isig] == PedEv) {
1862 // In present implementation this never happens
1863 rec.tdcPedEv[isig] = true;
1864 } else {
1865 rec.tdcPedMissing[isig] = true;
1866 }
1867#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
1868 LOG(info) << __func__ << " itdc=" << itdc << " " << ChannelNames[isig] << " @ ibun=" << ibun << " " << mReco[ibun].ir.orbit << "." << mReco[ibun].ir.bc << " "
1869 << " tdc=" << tdc << " -> " << TDCValCorr << " shift=" << tdc_shift[itdc] << " -> TDCVal=" << TDCVal << "=" << TDCVal * o2::zdc::FTDCVal
1870 << " mSource[" << isig << "] = " << unsigned(mSource[isig]) << " = " << mOffset[isig]
1871 << " amp=" << amp << " -> " << TDCAmpCorr << " calib=" << tdc_calib[itdc] << " offset=" << tdc_offset[itdc] << " -> TDCAmp=" << TDCAmp
1872 << (ibun == ibeg ? " B" : "") << (ibun == iend ? " E" : "");
1873 mAssignedTDC[itdc]++;
1874#endif
1875 ihit++;
1876} // assignTDC
1877
1878void DigiReco::findSignals(int ibeg, int iend)
1879{
1880 // N.B. findSignals is called after pile-up correction on TDCs
1881#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
1882 LOG(info) << __func__ << "(" << ibeg << ", " << iend << "): " << mReco[ibeg].ir.orbit << "." << mReco[ibeg].ir.bc << " - " << mReco[iend].ir.orbit << "." << mReco[iend].ir.bc;
1883#endif
1884 // Identify TDC signals
1885 for (int ibun = ibeg; ibun <= iend; ibun++) {
1886 updateOffsets(ibun); // Get orbit pedestals or run pedestals as a fallback
1887 auto& rec = mReco[ibun];
1888 for (int itdc = 0; itdc < NTDCChannels; itdc++) {
1889#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
1890 bool msg = false;
1891 if (rec.fired[itdc] != 0x0) {
1892 msg = true;
1893 printf("%d %u.%-4u TDCDiscr %d [%s] 0x%04hx -> ", ibun, rec.ir.orbit, rec.ir.bc, itdc, ChannelNames[TDCSignal[itdc]].data(), rec.fired[itdc]);
1894 for (int isam = 0; isam < NTimeBinsPerBC; isam++) {
1895 printf("%d", rec.fired[itdc] & mMask[isam] ? 1 : 0);
1896 }
1897 }
1898#endif
1899 rec.pattern[itdc] = 0;
1900 for (int32_t i = 0; i < rec.ntdc[itdc]; i++) {
1901#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
1902 msg = true;
1903 printf(" %d TDC A=%5.0f @ T=%5.0f", i, rec.TDCAmp[itdc][i], rec.TDCVal[itdc][i]);
1904#endif
1905 // There is a TDC value in the search zone around main-main position
1906 // NB: by definition main-main collision has zero time
1907 if (std::abs(rec.TDCVal[itdc][i]) < mRopt->tdc_search[itdc]) {
1908 rec.pattern[itdc] = 1;
1909 }
1910#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
1911 if (rec.pattern[itdc] == 1) {
1912 printf(" in_r");
1913 } else {
1914 printf(" out_r");
1915 }
1916#endif
1917 }
1918#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
1919 if (msg) {
1920 printf("\n");
1921 }
1922#endif
1923 }
1924
1925#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
1926 printf("%d %u.%-4u TDC PATTERN: ", ibun, mReco[ibun].ir.orbit, mReco[ibun].ir.bc);
1927 for (int itdc = 0; itdc < NTDCChannels; itdc++) {
1928 printf("%d", rec.pattern[itdc]);
1929 }
1930 for (int itdc = 0; itdc < NTDCChannels; itdc++) {
1931 printf(" %s", rec.pattern[itdc] ? ChannelNames[TDCSignal[itdc]].data() : " ");
1932 }
1933 printf("\n");
1934#endif
1935
1936 // Check if coincidence of common PM and sum of towers is satisfied
1937 // Missing coincidence can be due for example by PM noise
1938 // TODO: for the moment the map of fired TDC is in RecEventAux
1939 // One should transmit it or introduce a way to recompute while reading back
1940 // the reconstructed events
1941 // Side A
1942 if ((rec.pattern[TDCZNAC] || mRopt->bitset[TDCZNAC]) && (rec.pattern[TDCZNAS] || mRopt->bitset[TDCZNAS])) {
1943 for (int ich = IdZNAC; ich <= IdZNASum; ich++) {
1944 rec.chfired[ich] = true;
1945 }
1946 }
1947 if ((rec.pattern[TDCZPAC] || mRopt->bitset[TDCZPAC]) && (rec.pattern[TDCZPAS] || mRopt->bitset[TDCZPAS])) {
1948 for (int ich = IdZPAC; ich <= IdZPASum; ich++) {
1949 rec.chfired[ich] = true;
1950 }
1951 }
1952 // ZEM1 and ZEM2 are not in coincidence
1953 rec.chfired[IdZEM1] = rec.pattern[TDCZEM1];
1954 rec.chfired[IdZEM2] = rec.pattern[TDCZEM2];
1955 // Side C
1956 if ((rec.pattern[TDCZNCC] || mRopt->bitset[TDCZNCC]) && (rec.pattern[TDCZNCS] || mRopt->bitset[TDCZNCS])) {
1957 for (int ich = IdZNCC; ich <= IdZNCSum; ich++) {
1958 rec.chfired[ich] = true;
1959 }
1960 }
1961 if ((rec.pattern[TDCZPCC] || mRopt->bitset[TDCZPCC]) && (rec.pattern[TDCZPCS] || mRopt->bitset[TDCZPCS])) {
1962 for (int ich = IdZPCC; ich <= IdZPCSum; ich++) {
1963 rec.chfired[ich] = true;
1964 }
1965 }
1966 // TODO: option to use a less restrictive definition of "fired" using for example
1967 // just the autotrigger bits instead of using TDCs
1968#ifndef O2_ZDC_DEBUG
1969 if (mVerbosity >= DbgFull) {
1970 printf("%d %u.%-4u TDC FIRED ", ibun, rec.ir.orbit, rec.ir.bc);
1971 printf("ZNA:%d%d%d%d%d%d ZPA:%d%d%d%d%d%d ZEM:%d%d ZNC:%d%d%d%d%d%d ZPC:%d%d%d%d%d%d\n",
1972 rec.chfired[IdZNAC], rec.chfired[IdZNA1], rec.chfired[IdZNA2], rec.chfired[IdZNA3], rec.chfired[IdZNA4], rec.chfired[IdZNASum],
1973 rec.chfired[IdZPAC], rec.chfired[IdZPA1], rec.chfired[IdZPA2], rec.chfired[IdZPA3], rec.chfired[IdZPA4], rec.chfired[IdZPASum],
1974 rec.chfired[IdZEM1], rec.chfired[IdZEM2],
1975 rec.chfired[IdZNCC], rec.chfired[IdZNC1], rec.chfired[IdZNC2], rec.chfired[IdZNC3], rec.chfired[IdZNC4], rec.chfired[IdZNCSum],
1976 rec.chfired[IdZPCC], rec.chfired[IdZPC1], rec.chfired[IdZPC2], rec.chfired[IdZPC3], rec.chfired[IdZPC4], rec.chfired[IdZPCSum]);
1977 }
1978#endif
1979 } // loop on bunches
1980} // findSignals
1981
1982void DigiReco::correctTDCPile()
1983{
1984 // Pile-up correction for TDCs
1985 // FEE acquires data in two modes: triggered and continuous
1986 // In triggered mode minimum two and up to four bunches are transferred for each ALICE trigger
1987 // In continuous mode two bunches are transferred for each autotrigger
1988 // Reconstruction is performed for consecutive bunches
1989 // There is no issue with gaps for triggered mode because the trigger condition
1990 // A0 || A1 || (A2 && (T0 || TM)) || (A3 && T0) one can have the following sequences
1991 // where: T is autotrigger, A is ALICE trigger, P is a previous bunch, - is skipped
1992 // ---PA A0 || A1
1993 // ---TA A0 || A1
1994 // --TPA A2 && T0
1995 // -TPPA A2 && TM A3 && T0
1996 // On the other hand, in autotrigger mode one can have a gap
1997 // ---PT
1998 // --PTT
1999 // -PTPT
2000 // PT-PT
2001 // therefore we have to look for an interaction outside the range of consecutive bunch
2002 // crossings that is used in reconstruction. Therefore the correction is done outside
2003 // reconstruction loop
2004 // In case TDC correction parameters are missing (e.g. mTDCCorr==0) then
2005 // pile-up is flagged but not corrected for
2006
2007#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
2008 LOG(info) << "________________________________________________________________________________";
2009 LOG(info) << __func__;
2010#endif
2011
2012 // TODO: Perform actual pile-up correction for TDCs.. this is still work in progress..
2013 // For the moment this function has pile-up detection
2014
2015 for (int itdc = 0; itdc < NTDCChannels; itdc++) {
2016 // Queue is empty at first event of the time frame
2017 // TODO: collect information from previous time frame
2018 std::deque<DigiRecoTDC> tdc;
2019 for (int ibc = 0; ibc < mNBC; ibc++) {
2020 // Bunch to be corrected
2021 auto rec = &mReco[ibc];
2022 // Count the number of hits in preceding bunch crossings
2023 // N.B. it is initialized at every bunch crossing
2024 int ntdc[NBCAn] = {0};
2025 for (auto it = tdc.begin(); it != tdc.end(); ++it) {
2026 auto bcd = (rec->ir).differenceInBC((*it).ir);
2027 if (bcd > NBCAn) {
2028 // Remove early events
2029 tdc.pop_front();
2030 } else if (bcd > 0) {
2031 ntdc[bcd - 1]++;
2032 }
2033 }
2034 if (rec->ntdc[itdc] > 1) {
2035 // In-bunch pile-up: cannot correct
2036 rec->tdcPileEvE[TDCSignal[itdc]] = true;
2037 if (mRopt->doStoreEvPileup) {
2038 // Store also multiple hits
2039 for (int ihit = 0; ihit < rec->ntdc[itdc]; ihit++) {
2040 tdc.emplace_back(rec->TDCVal[itdc][ihit], rec->TDCAmp[itdc][ihit], rec->ir);
2041 }
2042 }
2043 } else if (rec->ntdc[itdc] == 1) {
2044 // A single TDC hit is present in current bunch
2045 if (tdc.size() > 0) {
2046 if (mCorrBackground) {
2047 correctTDCBackground(ibc, itdc, tdc);
2048 } else {
2049 // Identify pile-up and assign error flags
2050 std::deque<DigiRecoTDC>::reverse_iterator rit = tdc.rbegin();
2051 // Here we should start the loop on signal bucket position
2052 for (rit = tdc.rbegin(); rit != tdc.rend(); ++rit) {
2053 auto bcd = (rec->ir).differenceInBC((*rit).ir);
2054 // Check if background can be corrected
2055 if (bcd > 0 && bcd <= NBCAn) {
2056 if (ntdc[bcd - 1] > 0) {
2057 // We flag pile-up
2058 if (bcd == 1) {
2059 rec->tdcPileM1E[TDCSignal[itdc]] = true;
2060 } else if (bcd == 2) {
2061 rec->tdcPileM2E[TDCSignal[itdc]] = true;
2062 } else if (bcd == 3) {
2063 rec->tdcPileM3E[TDCSignal[itdc]] = true;
2064 }
2065 }
2066 }
2067 }
2068 }
2069 }
2070 // Add current event at the end of the queue
2071 for (int ihit = 0; ihit < rec->ntdc[itdc]; ihit++) {
2072 tdc.emplace_back(rec->TDCVal[itdc][ihit], rec->TDCAmp[itdc][ihit], rec->ir);
2073 }
2074 } // Single hit in bunch
2075#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
2076 if (rec->tdcPileEvE[TDCSignal[itdc]] || rec->tdcPileM1C[TDCSignal[itdc]] || rec->tdcPileM1E[TDCSignal[itdc]] ||
2077 rec->tdcPileM2C[TDCSignal[itdc]] || rec->tdcPileM2E[TDCSignal[itdc]] || rec->tdcPileM3C[TDCSignal[itdc]] || rec->tdcPileM3E[TDCSignal[itdc]]) {
2078 printf("%d %u.%-4u %s:%s%s%s%s\n", ibc, rec->ir.orbit, rec->ir.bc, ChannelNames[TDCSignal[itdc]].data(),
2079 rec->tdcPileEvE[TDCSignal[itdc]] ? " PileEvE" : "",
2080 rec->tdcPileM1C[TDCSignal[itdc]] ? " PileM1C" : "",
2081 rec->tdcPileM1E[TDCSignal[itdc]] ? " PileM1E" : "",
2082 rec->tdcPileM2C[TDCSignal[itdc]] ? " PileM2C" : "",
2083 rec->tdcPileM2E[TDCSignal[itdc]] ? " PileM2E" : "",
2084 rec->tdcPileM3C[TDCSignal[itdc]] ? " PileM3C" : "",
2085 rec->tdcPileM3E[TDCSignal[itdc]] ? " PileM3E" : "");
2086 }
2087#endif
2088 }
2089 }
2090} // correctTDCPile
2091
2092// #define O2_ZDC_DEBUG_CORR
2093
2094int DigiReco::correctTDCSignal(int itdc, int16_t TDCVal, float TDCAmp, float& fTDCVal, float& fTDCAmp, bool isbeg, bool isend)
2095{
2096 // Correction of single TDC signals
2097 // This function takes into account the position of the signal in the sequence
2098 // TDCVal is before recentering
2099 constexpr int TDCRange = TSN * NTimeBinsPerBC;
2100 constexpr int TDCMax = TDCRange - TSNH - 1;
2101
2102 // Fallback is no correction appliead
2103 fTDCVal = TDCVal;
2104 fTDCAmp = TDCAmp;
2105
2106 if (mTDCCorr == nullptr) {
2107#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
2108 printf("%21s itdc=%d TDC=%d AMP=%d MISSING mTDCCorr\n", __func__, itdc, TDCVal, TDCAmp);
2109#endif
2110 return 1;
2111 }
2112
2113 if (isbeg == false && isend == false) {
2114 // Mid bunch
2115 fTDCAmp = TDCAmp / mTDCCorr->mAFMidC[itdc][0];
2116 fTDCVal = TDCVal - mTDCCorr->mTSMidC[itdc][0];
2117 } else if (isbeg == true) {
2118 {
2119 auto p0 = mTDCCorr->mTSBegC[itdc][0];
2120 auto p1 = mTDCCorr->mTSBegC[itdc][1];
2121 if (TDCVal > TSNH && TDCVal < p0) {
2122 auto diff = TDCVal - p0;
2123 auto p2 = mTDCCorr->mTSBegC[itdc][2];
2124 auto p3 = mTDCCorr->mTSBegC[itdc][3];
2125 fTDCVal = TDCVal - (p1 + p2 * diff + p3 * diff * diff);
2126 } else {
2127 fTDCVal = TDCVal - p1;
2128 }
2129 }
2130 {
2131 auto p0 = mTDCCorr->mAFBegC[itdc][0];
2132 auto p1 = mTDCCorr->mAFBegC[itdc][1];
2133 if (TDCVal > TSNH && TDCVal < p0) {
2134 auto diff = TDCVal - p0;
2135 auto p2 = mTDCCorr->mAFBegC[itdc][2];
2136 auto p3 = mTDCCorr->mAFBegC[itdc][3];
2137 fTDCAmp = TDCAmp / (p1 + p2 * diff + p3 * diff * diff);
2138 } else {
2139 fTDCAmp = TDCAmp / p1;
2140 }
2141 }
2142 } else if (isend == true) {
2143 {
2144 auto p0 = mTDCCorr->mTSEndC[itdc][0];
2145 auto p1 = mTDCCorr->mTSEndC[itdc][1];
2146 if (TDCVal > p0 && TDCVal < TDCMax) {
2147 auto diff = TDCVal - p0;
2148 auto p2 = mTDCCorr->mTSEndC[itdc][2];
2149 auto p3 = mTDCCorr->mTSEndC[itdc][3];
2150 fTDCVal = TDCVal - (p1 + p2 * diff + p3 * diff * diff);
2151 } else {
2152 fTDCVal = TDCVal - p1;
2153 }
2154 }
2155 {
2156 auto p0 = mTDCCorr->mAFEndC[itdc][0];
2157 auto p1 = mTDCCorr->mAFEndC[itdc][1];
2158 if (TDCVal > p0 && TDCVal < TDCMax) {
2159 auto diff = TDCVal - p0;
2160 auto p2 = mTDCCorr->mAFEndC[itdc][2];
2161 auto p3 = mTDCCorr->mAFEndC[itdc][3];
2162 fTDCAmp = TDCAmp / (p1 + p2 * diff + p3 * diff * diff);
2163 } else {
2164 fTDCAmp = TDCAmp / p1;
2165 }
2166 }
2167 } else {
2168#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
2169 printf("%21s itdc=%d TDC=%d AMP=%d LONELY BUNCH\n", __func__, itdc, TDCVal, TDCAmp);
2170#endif
2171 return 1;
2172 }
2173 return 0;
2174} // correctTDCSignal
2175
2176int DigiReco::correctTDCBackground(int ibc, int itdc, std::deque<DigiRecoTDC>& tdc)
2177{
2178 constexpr int TDCRange = TSN * NTimeBinsPerBC;
2179 constexpr int BucketS = TDCRange / NBucket;
2180 constexpr int BucketSH = BucketS / 2;
2181 auto rec = &mReco[ibc];
2182 auto isig = TDCSignal[itdc];
2183 // With this function we are able to correct a single hit in a bunch
2184 // therefore we refer just to TDC hit in position [0]
2185 float TDCValUnc = rec->TDCVal[itdc][0];
2186 float TDCAmpUnc = rec->TDCAmp[itdc][0];
2187#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
2188 auto TDCValUncBck = rec->TDCVal[itdc][0];
2189 auto TDCAmpUncBck = rec->TDCAmp[itdc][0];
2190#endif
2191 float TDCValBest = TDCValUnc;
2192 float TDCAmpBest = TDCAmpUnc;
2193 int TDCSigBest = -1;
2194 float dtime = std::numeric_limits<float>::infinity();
2195 // Try every bucket position
2196 for (int ibuks = 0; ibuks < NBucket; ibuks++) {
2197 std::deque<DigiRecoTDC>::reverse_iterator rit = tdc.rbegin();
2198 // Correct amplitude
2199 float sum[3] = {0};
2200 // Loop on background signals to sum the effects on reconstructed amplitude
2201 int32_t nbkg = 0;
2202 for (rit = tdc.rbegin(); rit != tdc.rend(); ++rit) {
2203 auto bcd = (rec->ir).differenceInBC((*rit).ir);
2204 if (bcd > 0 && bcd <= NBCAn) {
2205 // Flag error if correction object does not exists
2206 if (mTDCCorr == nullptr) {
2207 if (bcd == 1) {
2208 rec->tdcPileM1E[isig] = true;
2209 } else if (bcd == 2) {
2210 rec->tdcPileM2E[isig] = true;
2211 } else if (bcd == 3) {
2212 rec->tdcPileM3E[isig] = true;
2213 }
2214 } else {
2215 int16_t TDCBkgVal = (*rit).val;
2216 int16_t TDCBkgAmp = (*rit).amp;
2217 // Get bucket number for background with floor division (divisor is positive)
2218 int arg1 = TDCBkgVal + BucketSH;
2219 int q1 = arg1 / BucketS;
2220 int r1 = arg1 % BucketS;
2221 if (r1 < 0) {
2222 q1--;
2223 r1 += TSN;
2224 }
2225 int arg2 = q1 + NBKZero;
2226 int q2 = arg2 / NBucket;
2227 int ibukb = arg2 % NBucket;
2228 if (ibukb < 0) {
2229 q2--;
2230 ibukb += NBucket;
2231 }
2232 // Correction parameters for amplitude
2233 int32_t ibun = NBCAn - bcd;
2234 auto p0 = mTDCCorr->mAmpCorr[itdc][ibun][ibukb][ibuks][0];
2235 auto p1 = mTDCCorr->mAmpCorr[itdc][ibun][ibukb][ibuks][1];
2236 auto p2 = mTDCCorr->mAmpCorr[itdc][ibun][ibukb][ibuks][2];
2237#ifdef O2_ZDC_DEBUG_CORR
2238 printf("%+e,%+e,%+e, // as%d_bc%+d_bk%d_sn%d\n", p0, p1, p2, itdc, -bcd, ibukb, ibuks);
2239#endif
2240 // Flag error if parameters are NaN
2241 if (std::isnan(p0) || std::isnan(p1) || std::isnan(p2)) {
2242 if (bcd == 1) {
2243 rec->tdcPileM1E[isig] = true;
2244 } else if (bcd == 2) {
2245 rec->tdcPileM2E[isig] = true;
2246 } else if (bcd == 3) {
2247 rec->tdcPileM3E[isig] = true;
2248 }
2249 } else {
2250 nbkg++;
2251 sum[0] += p0;
2252 sum[1] += p1;
2253 sum[2] += p2 * TDCBkgAmp;
2254 // Flag application of correction and flag error if
2255 // there are multiple signals in a bunch
2256 if (ibuks == 0) { // Make the test just once
2257 if (bcd == 1) {
2258 if (rec->tdcPileM1C[isig]) {
2259 rec->tdcPileM1E[isig] = true;
2260 } else {
2261 rec->tdcPileM1C[isig] = true;
2262 }
2263 } else if (bcd == 2) {
2264 if (rec->tdcPileM2C[isig]) {
2265 rec->tdcPileM2E[isig] = true;
2266 } else {
2267 rec->tdcPileM2C[isig] = true;
2268 }
2269 } else if (bcd == 3) {
2270 if (rec->tdcPileM3C[isig]) {
2271 rec->tdcPileM3E[isig] = true;
2272 } else {
2273 rec->tdcPileM3C[isig] = true;
2274 }
2275 }
2276 }
2277 }
2278 }
2279 }
2280 }
2281 if (mTDCCorr == nullptr) {
2282 // Cannot correct and error conditions has been already set above
2283 return 1;
2284 }
2285 if (nbkg > 0) { // Cross check.. should always be true
2286 float TDCAmpUpd = (TDCAmpUnc - sum[0] - sum[2]) / (1. - float(nbkg) + sum[1]);
2287 // Compute time correction assuming that time shift is additive
2288 float tshift = 0;
2289 for (rit = tdc.rbegin(); rit != tdc.rend(); ++rit) {
2290 auto bcd = (rec->ir).differenceInBC((*rit).ir);
2291 if (bcd > 0 && bcd <= NBCAn) {
2292 int16_t TDCBkgVal = (*rit).val;
2293 int16_t TDCBkgAmp = (*rit).amp;
2294 // Get bucket number for background with floor division (divisor is positive)
2295 int arg1 = TDCBkgVal + BucketSH;
2296 int q1 = arg1 / BucketS;
2297 int r1 = arg1 % BucketS;
2298 if (r1 < 0) {
2299 q1--;
2300 r1 += TSN;
2301 }
2302 int arg2 = q1 + NBKZero;
2303 int q2 = arg2 / NBucket;
2304 int ibukb = arg2 % NBucket;
2305 if (ibukb < 0) {
2306 q2--;
2307 ibukb += NBucket;
2308 }
2309 // Parameters for time correction
2310 int32_t ibun = NBCAn - bcd;
2311 auto p0 = mTDCCorr->mTDCCorr[itdc][ibun][ibukb][ibuks][0];
2312 auto p1 = mTDCCorr->mTDCCorr[itdc][ibun][ibukb][ibuks][1];
2313 auto p2 = mTDCCorr->mTDCCorr[itdc][ibun][ibukb][ibuks][2];
2314#ifdef O2_ZDC_DEBUG_CORR
2315 printf("%+e,%+e,%+e, // ts%d_bc%d_bk%d_sn%d\n", p0, p1, p2, itdc, -bcd, ibukb, ibuks);
2316#endif
2317 // Flag error if parameters are NaN
2318 if (std::isnan(p0) || std::isnan(p1)) {
2319 if (bcd == 1) {
2320 rec->tdcPileM1E[isig] = true;
2321 } else if (bcd == 2) {
2322 rec->tdcPileM2E[isig] = true;
2323 } else if (bcd == 3) {
2324 rec->tdcPileM3E[isig] = true;
2325 }
2326 } else {
2327 tshift += p0 + p1 / (TDCAmpUpd / TDCBkgAmp);
2328#ifdef O2_ZDC_DEBUG_CORR
2329 printf("ibuk = b.%d s.%d = %8.2f ts=%8.2f\n", ibukb, ibuks, TDCAmpUpd, TDCBkgAmp, tshift);
2330#endif
2331 }
2332 }
2333 }
2334 // Update signal arrival time
2335 float TDCValUpd = TDCValUnc - tshift;
2336 float TDCBucket = (ibuks - NBKZero) * BucketS;
2337 // Compare updated arrival time with assumed bucket assignment
2338 // Take into account the possibility that the TDC has been assigned to preceding
2339 // or successive bunch
2340 float mydtime = std::min(std::abs(TDCBucket - TDCValUpd), std::min(std::abs(TDCBucket - TDCValUpd - TDCRange), std::abs(TDCBucket - TDCValUpd + TDCRange)));
2341#ifdef O2_ZDC_DEBUG_CORR
2342 printf("ibuks = %d = %8.2f AS=%8.2f ts=%8.2f TDC %8.2f -> %8.2f delta=%8.2f\n", ibuks, TDCBucket, TDCAmpUpd, tshift, TDCValUnc, TDCValUpd, mydtime);
2343#endif
2344 if (mydtime < dtime) {
2345 dtime = mydtime;
2346 TDCValBest = TDCValUpd;
2347 TDCAmpBest = TDCAmpUpd;
2348 TDCSigBest = ibuks;
2349 }
2350 }
2351 } // Loop on signal bucket position (ibuks)
2352 rec->TDCVal[itdc][0] = TDCValBest;
2353 rec->TDCAmp[itdc][0] = TDCAmpBest;
2354#ifdef ALICEO2_ZDC_DIGI_RECO_DEBUG
2355 if (rec->TDCVal[itdc][0] != TDCValUnc || rec->TDCAmp[itdc][0] != TDCAmpUnc) {
2356 printf("%21s ibc=%d itdc=%d sn = %d", __func__, ibc, itdc, TDCSigBest);
2357 printf(" TDC=%f -> %f", TDCValUncBck, rec->TDCVal[itdc][0]);
2358 printf(" AMP=%f -> %f\n", TDCAmpUncBck, rec->TDCAmp[itdc][0]);
2359 }
2360#endif
2361 return 0;
2362}
2363} // namespace zdc
2364} // namespace o2
uint64_t orbit
Definition RawEventData.h:6
int32_t i
const GPUTPCGMMerger::trackCluster & b1
constexpr int p2()
constexpr int p1()
constexpr to accelerate the coordinates changing
uint32_t j
Definition RawData.h:0
ZDC reconstruction parameters.
void prepareInterpolation()
Definition DigiReco.cxx:463
int process(const gsl::span< const o2::zdc::OrbitData > &orbitdata, const gsl::span< const o2::zdc::BCData > &bcdata, const gsl::span< const o2::zdc::ChannelData > &chdata)
Definition DigiReco.cxx:493
float sum(float s, o2::dcs::DataPointValue v)
Definition dcs-ccdb.cxx:39
GLdouble n
Definition glcorearb.h:1982
const GLfloat * m
Definition glcorearb.h:4066
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat s1
Definition glcorearb.h:5034
GLint y
Definition glcorearb.h:270
GLboolean * data
Definition glcorearb.h:298
GLuint GLfloat * val
Definition glcorearb.h:1582
GLint ref
Definition glcorearb.h:291
uint8_t itsSharedClusterMap uint8_t
constexpr int LHCMaxBunches
std::array< int, 23 > p3
std::array< int, 24 > p0
std::mt19937 mt(rd())
struct o2::upgrades_utils::@463 zdc
structure to keep FT0 information
constexpr int IdZNCSum
constexpr int IdZPC4
constexpr int IdZPA2
constexpr int NModules
Definition Constants.h:68
constexpr int IdZNA3
constexpr int IdZNA1
constexpr int DummyIntRange
Definition Constants.h:303
const int TDCSignal[NTDCChannels]
Definition Constants.h:181
constexpr int16_t Int16MaxVal
Definition Constants.h:56
constexpr int IdZPA1
constexpr int IdZPCSum
constexpr int IdZNC2
constexpr int IdZEM1
constexpr int NBucket
Definition Constants.h:119
constexpr std::array< int, 17 > ChTowerCalib
Definition Constants.h:290
constexpr float FInfty
Definition Constants.h:77
float O2_ZDC_DIGIRECO_FLT
Definition DigiReco.h:49
constexpr int NTimeBinsPerBC
Definition Constants.h:53
constexpr uint32_t ZDCRefInitVal
Definition Constants.h:91
constexpr int NChPerModule
Definition Constants.h:69
constexpr std::array< int, 10 > ChEnergyCalib
Definition Constants.h:286
constexpr int NTDCChannels
Definition Constants.h:90
constexpr int IdZNCC
constexpr int IdZPAC
const int SignalTDC[NChannels]
Definition Constants.h:195
constexpr int NChannels
Definition Constants.h:65
constexpr int IdZPC2
constexpr int NBCAn
Definition Constants.h:123
constexpr int NBCReadOut
Definition Constants.h:54
constexpr int IdZPA4
constexpr int DbgFull
Definition Constants.h:210
constexpr int NBKZero
Definition Constants.h:120
constexpr int IdZNC1
constexpr int IdZNC3
constexpr int TSN
Definition Constants.h:94
constexpr int IdZPCC
constexpr int ADCRange
Definition Constants.h:76
constexpr int NTS
Definition Constants.h:96
constexpr int IdZPC1
constexpr int IdZPA3
constexpr int DbgMinimal
Definition Constants.h:208
constexpr int IdZNC4
constexpr int IdZEM2
constexpr int IdZNA2
constexpr int TSL
Definition Constants.h:93
constexpr int DbgZero
Definition Constants.h:207
constexpr int IdZNASum
constexpr int IdDummy
constexpr int ADCMin
Definition Constants.h:76
constexpr int IdZPC3
constexpr int MaxTDCValues
Definition Constants.h:89
constexpr int IdZNA4
constexpr int IdZNAC
constexpr std::string_view ChannelNames[]
Definition Constants.h:147
constexpr int DbgMedium
Definition Constants.h:209
constexpr int TSNH
Definition Constants.h:95
constexpr int ADCMax
Definition Constants.h:76
constexpr int IdZPASum
constexpr float FTDCVal
Definition Constants.h:103
constexpr std::array< int, NChannels > CaloCommonPM
Definition Constants.h:296
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
GPUReconstruction * rec
uint32_t orbit
LHC orbit.
void print(bool printall=true) const
float getCalib(uint32_t ich) const
uint32_t getTriggerMask() const
std::array< Module, MaxNModules > modules
int32_t beg_ped_int[NChannels]
float ped_thr_hi[NChannels]
int32_t beg_int[NChannels]
int32_t tsh[NTDCChannels]
int32_t tth[NTDCChannels]
int32_t end_int[NChannels]
float ped_thr_lo[NChannels]
int32_t end_ped_int[NChannels]
int tdc_search[NTDCChannels]
int32_t amod[NChannels]
float energy_calib[NChannels]
float tdc_shift[NTDCChannels]
int32_t tmod[NTDCChannels]
float tdc_calib[NTDCChannels]
float ped_thr_hi[NChannels]
float adc_offset[NChannels]
float tdc_offset[NTDCChannels]
int32_t ach[NChannels]
int32_t end_int[NChannels]
bool bitset[NTDCChannels]
float tdc_search[NTDCChannels]
int32_t tsh[NTDCChannels]
int32_t beg_int[NChannels]
float ped_thr_lo[NChannels]
int32_t tth[NTDCChannels]
int32_t end_ped_int[NChannels]
float tower_calib[NChannels]
int32_t tch[NTDCChannels]
int32_t beg_ped_int[NChannels]
float energy_calib[NChannels]
float adc_offset[NChannels]
std::array< std::array< std::array< std::array< std::array< float, NFParA >, NBucket >, NBucket >, NBCAn >, NTDCChannels > mAmpCorr
Definition ZDCTDCCorr.h:33
std::array< std::array< float, NParExtC >, NTDCChannels > mTSBegC
Definition ZDCTDCCorr.h:35
std::array< std::array< float, NParExtC >, NTDCChannels > mTSEndC
Definition ZDCTDCCorr.h:37
std::array< std::array< std::array< std::array< std::array< float, NFParT >, NBucket >, NBucket >, NBCAn >, NTDCChannels > mTDCCorr
Definition ZDCTDCCorr.h:32
std::array< std::array< float, NParMidC >, NTDCChannels > mAFMidC
Definition ZDCTDCCorr.h:39
void print() const
std::array< std::array< float, NParExtC >, NTDCChannels > mAFEndC
Definition ZDCTDCCorr.h:40
std::array< std::array< float, NParMidC >, NTDCChannels > mTSMidC
Definition ZDCTDCCorr.h:36
std::array< std::array< float, NParExtC >, NTDCChannels > mAFBegC
Definition ZDCTDCCorr.h:38
static void print()
Definition ZDCTDCData.h:35
float getOffset(uint32_t ich) const
float getFactor(uint32_t ich) const
float getShift(uint32_t ich) const
float tower_calib[NChannels]
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
o2::InteractionRecord ir(0, 0)
std::vector< ChannelData > channels
uint64_t const void const *restrict const msg
Definition x9.h:153