Project
Loading...
Searching...
No Matches
Digitizer.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
14#include "FT0Base/FT0DigParam.h"
18
20#include "FT0Base/Constants.h"
21#include <map>
22#include <array>
23#include <unordered_map>
24#include <regex>
25#include <string>
26
27#include "TMath.h"
28#include "TRandom.h"
29#include <TH1F.h>
30#include <algorithm>
31#include <cassert>
32#include <iostream>
33#include <optional>
34#include <Vc/Vc>
35
36using namespace o2::ft0;
37
39
40namespace o2::ft0
41{
42// signal shape function
43template <typename Float>
45{
46 float p0, p1, p2, p3, p4, p5, p6, p7;
47 p0 = 1.30853;
48 p1 = 0.516807;
49 p2 = 3.36714;
50 p3 = -1.01206;
51 p4 = 1.42832;
52 p5 = 1.1589;
53 p6 = 1.22019;
54 p7 = 0.426818;
55
56 Double_t val = 0;
57
58 if (x > p3) {
59 Double_t x1 = x - p3;
60 Double_t arg1 = (log(x1) - p0) / p1;
61 val += p2 * (1.0 / (x1 * p1 * sqrt(2 * TMath::Pi()))) * exp(-0.5 * arg1 * arg1);
62 }
63
64 if (x > p7) {
65 Double_t x2 = x - p7;
66 Double_t arg2 = (log(x2) - p4) / p5;
67 val += p6 * (1.0 / (x2 * p5 * sqrt(2 * TMath::Pi()))) * exp(-0.5 * arg2 * arg2);
68 }
69
70 return val;
71};
72
73// integrated signal shape function
74inline float signalForm_integral(float x)
75{
76 float p0, p1, p2, p3, p4, p5, p6, p7;
77 p0 = 1.30853;
78 p1 = 0.516807;
79 p2 = 3.36714;
80 p3 = -1.01206;
81 p4 = 1.42832;
82 p5 = 1.1589;
83 p6 = 1.22019;
84 p7 = 0.426818;
85 Double_t val = 0;
86
87 if (x > p3) {
88 Double_t x1 = x - p3;
89 Double_t z1 = (log(x1) - p0) / (sqrt(2) * p1);
90 val += p2 * 0.5 * (1 + TMath::Erf(z1)); // norm1 * CDF1
91 }
92
93 if (x > p7) {
94 Double_t x2 = x - p7;
95 Double_t z2 = (log(x2) - p4) / (sqrt(2) * p5);
96 val += p6 * 0.5 * (1 + TMath::Erf(z2)); // norm2 * CDF2
97 }
98
99 return val;
100};
101/*
102// signal shape function
103template <typename Float>
104Float signalForm_i(Float x)
105{
106using namespace std;
107Float const a = -0.45458;
108Float const b = -0.83344945;
109return x > Float(0) ? -(exp(b * x) - exp(a * x)) / Float(7.8446501) : Float(0);
110};
111
112// integrated signal shape function
113inline float signalForm_integral(float x)
114{
115using namespace std;
116double const a = -0.45458;
117double const b = -0.83344945;
118if (x < 0) {
119 x = 0;
120}
121return -(exp(b * x) / b - exp(a * x) / a) / 7.8446501;
122};
123*/
124// SIMD version of the integrated signal shape function
125inline Vc::float_v signalForm_integralVc(Vc::float_v x)
126{
127 auto const mask = (x >= 0.0f);
128 Vc::float_v arg(0);
129 arg.assign(x, mask); // branchless if
130 Vc::float_v const a(-0.45458f);
131 Vc::float_v const b(-0.83344945f);
132 Vc::float_v result = -(Vc::exp(b * arg) / b - Vc::exp(a * arg) / a) / 7.8446501f;
133 return result;
134};
135} // namespace o2::ft0
136
137Digitizer::CFDOutput Digitizer::get_time(const std::vector<float>& times, float deadTime)
138{
139 assert(std::is_sorted(std::begin(times), std::end(times)));
140
141 // get a new batch of random values
142 for (float& n : mNoiseSamples) {
143 n = mRndGaus.getNextValue();
144 }
145
146 // this lambda function evaluates the signal shape at a give time
147 auto value_at = [&](float time) {
148 float val = 0;
149 // (1) sum over individual hits
150 Vc::float_v acc(0);
151 Vc::float_v tableVal(0);
152 const float* tp = times.data();
153 size_t m = times.size() / Vc::float_v::size();
154 for (size_t i = 0; i < m; ++i) {
155 tableVal.load(tp);
156 tp += Vc::float_v::size();
157 Vc::prefetchForOneRead(tp);
158 acc += signalFormVc(time - tableVal);
159 }
160 val += acc.sum();
161 // non-SIMD tail
162 for (size_t i = Vc::float_v::size() * m; i < times.size(); ++i, ++tp) {
163 val += signalForm(time - (*tp));
164 }
165 // (2) add noise
166 // find the right indices into the sinc table
167 int timeIndex = std::lround(time / FT0DigParam::Instance().mNoisePeriod * mSincTable.size());
168 int timeOffset = timeIndex / mSincTable.size();
169 timeIndex %= mSincTable.size();
170 if (timeOffset >= mNumNoiseSamples) { // this happens when time >= 25 ns
171 timeOffset = mNumNoiseSamples - 1;
172 LOG(debug) << "timeOffset >= mNumNoiseSamples";
173 }
174 if (timeOffset <= -mNumNoiseSamples) { // this happens when time <= -25 ns
175 timeOffset = -mNumNoiseSamples + 1;
176 LOG(debug) << "timeOffset <= -mNumNoiseSamples";
177 }
178 Vc::float_v noiseVal(0);
179 const float* np = mNoiseSamples.data();
180 tp = mSincTable[timeIndex].data() + mNumNoiseSamples - timeOffset;
181 acc = 0.0f;
182 m = mNumNoiseSamples / Vc::float_v::Size;
183 for (size_t i = 0; i < m; ++i) {
184 tableVal.load(tp);
185 tp += Vc::float_v::Size;
186 Vc::prefetchForOneRead(tp);
187 noiseVal.load(np);
188 np += Vc::float_v::Size;
189 Vc::prefetchForOneRead(np);
190 acc += noiseVal * tableVal;
191 }
192 val += acc.sum(); // horizontal sum
193 // non-SIMD tail
194 for (size_t i = Vc::float_v::Size * m; i < mNumNoiseSamples; ++i, ++tp, ++np) {
195 val += (*np) * (*tp);
196 }
197 return val;
198 };
199 auto const min_time = std::max(deadTime, *std::min_element(std::begin(times),
200 std::end(times)));
201 CFDOutput result{std::nullopt, -0.5f * FT0DigParam::Instance().mBunchWidth};
202 bool is_positive = true;
203
204 // reset the chache
205 std::fill_n(std::begin(mSignalCache), std::size(mSignalCache), -1.0f);
206 const auto& params = FT0DigParam::Instance();
207 // we need double precision for time in order to match previous behaviour
208 for (double time = min_time; time < 0.5 * params.mBunchWidth; time += DP::SIGNAL_CACHE_DT) {
209 float const val = value_at(time);
210 int const index = std::lround((time + 0.5 * params.mBunchWidth) / DP::SIGNAL_CACHE_DT);
211 if (index >= 0 && index < mSignalCache.size()) { // save the value for later use
212 mSignalCache[index] = val;
213 }
214 // look up the time-shifted signal value from the past
215 float val_prev = 0.0f;
216 int const index_prev = std::lround((time - params.mCFDShiftPos + 0.5f * params.mBunchWidth) / DP::SIGNAL_CACHE_DT);
217 val_prev = ((index_prev < 0 || index_prev >= mSignalCache.size() || mSignalCache[index_prev] < 0.0f)
218 ? value_at(time - params.mCFDShiftPos) // was not computed before
219 : mSignalCache[index_prev]); // is available in the cache
220 float const cfd_val = 5.0f * val_prev - val;
221 if (std::abs(val) > params.mCFD_trsh && !is_positive && cfd_val > 0.0f) {
222 if (!result.particle) {
223 result.particle = time;
224 }
225 result.deadTime = time + params.mCFDdeadTime;
226 time += params.mCFDdeadTime - DP::SIGNAL_CACHE_DT;
227 is_positive = true;
228 } else {
229 is_positive = cfd_val > 0.0f;
230 }
231 }
232 return result;
233}
234
235double Digitizer::measure_amplitude(const std::vector<float>& times) const
236{
237 float const from = FT0DigParam::Instance().mAmpRecordLow;
238 float const to = from + FT0DigParam::Instance().mAmpRecordUp;
239 // SIMD version has a negligible effect on the total wall time
240 Vc::float_v acc(0);
241 Vc::float_v tv(0);
242 const float* tp = times.data();
243 size_t const m = times.size() / Vc::float_v::Size;
244 for (size_t i = 0; i < m; ++i) {
245 tv.load(tp);
246 tp += Vc::float_v::Size;
247 Vc::prefetchForOneRead(tp);
248 acc += signalForm_integralVc(to - tv) - signalForm_integralVc(from - tv);
249 }
250 float result = acc.sum(); // horizontal sum
251 // non-SIMD tail
252 for (size_t i = Vc::float_v::Size * m; i < times.size(); ++i, ++tp) {
253 result += signalForm_integral(to - (*tp)) - signalForm_integral(from - (*tp));
254 }
255 return result;
256}
257
258void Digitizer::process(const std::vector<o2::ft0::HitType>* hits,
259 std::vector<o2::ft0::Digit>& digitsBC,
260 std::vector<o2::ft0::ChannelData>& digitsCh,
261 std::vector<o2::ft0::DetTrigInput>& digitsTrig,
263{
264 ;
265 // Calculating signal time, amplitude in mean_time +- time_gate --------------
266 LOG(debug) << " process firstBCinDeque " << firstBCinDeque << " mIntRecord " << mIntRecord;
267 if (firstBCinDeque != mIntRecord) {
268 flush(digitsBC, digitsCh, digitsTrig, label);
269 }
270
271 Int_t parent = -10;
272 for (auto const& hit : *hits) {
273 if (hit.GetEnergyLoss() > 0) {
274 continue;
275 }
276
277 Int_t hit_ch = hit.GetDetectorID();
278
279 // If the dead channel map is used, and the channel with ID 'hit_ch' is dead, don't process this hit.
280 if (mDeadChannelMap && !mDeadChannelMap->isChannelAlive(hit_ch)) {
281 continue;
282 }
283
284 const auto& params = FT0DigParam::Instance();
285
286 Bool_t is_A_side = (hit_ch < 4 * mGeometry.NCellsA);
287
288 // Subtract time-of-flight from hit time
289 const Float_t timeOfFlight = hit.GetPos().R() / o2::constants::physics::LightSpeedCm2NS;
290 const Float_t timeOffset = is_A_side ? params.hitTimeOffsetA : params.hitTimeOffsetC;
291 Double_t hit_time = hit.GetTime() - timeOfFlight + timeOffset + mIntRecord.getTimeOffsetWrtBC();
292
293 if (hit_time > 150) {
294 continue; // not collect very slow particles
295 }
296
297 auto relBC = o2::InteractionRecord{hit_time};
298 if (mCache.size() <= relBC.bc) {
299 mCache.resize(relBC.bc + 1);
300 }
301 mCache[relBC.bc].hits.emplace_back(BCCache::particle{hit_ch, hit_time - relBC.bc2ns()});
302 // charge particles in MCLabel
303 Int_t parentID = hit.GetTrackID();
304 if (parentID != parent) {
305 mCache[relBC.bc].labels.emplace(parentID, mEventID, mSrcID, hit_ch);
306 parent = parentID;
307 }
308 }
309}
310
311void Digitizer::storeBC(BCCache& bc,
312 std::vector<o2::ft0::Digit>& digitsBC,
313 std::vector<o2::ft0::ChannelData>& digitsCh,
314 std::vector<o2::ft0::DetTrigInput>& digitsTrig,
316{
317 if (bc.hits.empty()) {
318 return;
319 }
320 // Initialize mapping channelID -> PM hash and PM side (A/C) using FT0 LUT
321 static bool pmLutInitialized = false;
322 static std::array<uint8_t, o2::ft0::Constants::sNCHANNELS_PM> mChID2PMhash{};
323 static std::map<uint8_t, bool> mMapPMhash2isAside; // hashed PM -> is A side
324
325 if (!pmLutInitialized) {
326 std::map<std::string, uint8_t> mapFEE2hash; // module name -> hashed PM id
327 uint8_t tcmHash = 0;
328
329 const auto& lut = o2::ft0::SingleLUT::Instance().getVecMetadataFEE();
330 auto lutSorted = lut;
331 std::sort(lutSorted.begin(), lutSorted.end(),
332 [](const auto& first, const auto& second) { return first.mModuleName < second.mModuleName; });
333
334 uint8_t binPos = 0;
335 for (const auto& lutEntry : lutSorted) {
336 const auto& moduleName = lutEntry.mModuleName;
337 const auto& moduleType = lutEntry.mModuleType;
338 const auto& strChID = lutEntry.mChannelID;
339
340 auto [it, inserted] = mapFEE2hash.insert({moduleName, binPos});
341 if (inserted) {
342 if (moduleName.find("PMA") != std::string::npos) {
343 mMapPMhash2isAside.insert({binPos, true});
344 } else if (moduleName.find("PMC") != std::string::npos) {
345 mMapPMhash2isAside.insert({binPos, false});
346 }
347 ++binPos;
348 }
349
350 if (std::regex_match(strChID, std::regex("^[0-9]{1,3}$"))) {
351 int chID = std::stoi(strChID);
353 mChID2PMhash[chID] = mapFEE2hash[moduleName];
354 } else {
355 LOG(fatal) << "Incorrect LUT entry: chID " << strChID << " | " << moduleName;
356 }
357 } else if (moduleType != "TCM") {
358 LOG(fatal) << "Non-TCM module w/o numerical chID: chID " << strChID << " | " << moduleName;
359 } else { // TCM
360 tcmHash = mapFEE2hash[moduleName];
361 }
362 }
363
364 pmLutInitialized = true;
365 }
366
367 int n_hit_A = 0, n_hit_C = 0, mean_time_A = 0, mean_time_C = 0;
368 int summ_ampl_A = 0, summ_ampl_C = 0;
369 int sum_A_ampl = 0, sum_C_ampl = 0;
370 int nPMTs = mGeometry.NCellsA * 4 + mGeometry.NCellsC * 4;
371 std::vector<int> sum_ampl_ipmt(nPMTs, 0);
372 // Per-PM summed charge (like in digits2trgFT0)
373 std::map<uint8_t, int> mapPMhash2sumAmpl;
374 for (const auto& entry : mMapPMhash2isAside) {
375 mapPMhash2sumAmpl.insert({entry.first, 0});
376 }
377
378 int vertex_time;
379 const auto& params = FT0DigParam::Instance();
380
381 static bool pmGroupsInitialized = false;
382 static std::vector<std::array<int, 4>> pmtChannelGroups;
383 if (!pmGroupsInitialized) {
384 std::unordered_map<uint8_t, std::vector<int>> tmpGroups;
385 for (int ch = 0; ch < o2::ft0::Constants::sNCHANNELS_PM; ++ch) {
386 tmpGroups[mChID2PMhash[static_cast<uint8_t>(ch)]].push_back(ch);
387 }
388
389 for (auto& [pmHash, chVec] : tmpGroups) {
390 std::sort(chVec.begin(), chVec.end());
391 if (chVec.size() % 4 != 0) {
392 LOG(fatal) << "PM hash " << int(pmHash) << " has " << chVec.size()
393 << " channels in LUT, expected multiplicity of 4";
394 }
395 for (size_t i = 0; i < chVec.size(); i += 4) {
396 std::array<int, 4> arr = {chVec[i + 0], chVec[i + 1], chVec[i + 2], chVec[i + 3]};
397 pmtChannelGroups.push_back(arr);
398 }
399 }
400 pmGroupsInitialized = true;
401 }
402
403 int first = digitsCh.size(), nStored = 0;
404 auto& particles = bc.hits;
405 std::sort(std::begin(particles), std::end(particles));
406 auto channel_end = particles.begin();
407 std::vector<float> channel_times;
408 std::vector<float> baseAmp(params.mMCPs, 0.f);
409 std::vector<float> finalAmp(params.mMCPs, 0.f);
410 std::vector<int> chTime(params.mMCPs, -5000);
411 std::vector<int> chChain(params.mMCPs, 0);
412 std::vector<bool> chValid(params.mMCPs, false);
413
414 static const std::array<std::array<int, 3>, 4> localNeighbours = {{{{1, 2, 3}},
415 {{0, 3, 2}},
416 {{0, 3, 1}},
417 {{1, 2, 0}}}};
418
419 // std::set<int> disabledChannels = {40, 41, 42, 43, 88, 89, 90, 91, 56, 57, 58, 59, 60, 61, 62, 63, 72, 73, 74, 75, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 164, 165, 166, 167, 184, 185, 186, 187, 160, 161, 162, 163, 188, 189, 190, 191, 156, 157, 158, 159, 192, 193, 194, 195, 152, 153, 154, 155, 196, 197, 198, 199, 148, 149, 150, 151, 144, 145, 146, 147, 204, 205, 206, 207, 200, 201, 202, 203}; // przykładowe kanały
420 for (Int_t ipmt = 0; ipmt < params.mMCPs; ++ipmt) {
421 auto channel_begin = channel_end;
422 channel_end = std::find_if(channel_begin, particles.end(),
423 [ipmt](BCCache::particle const& p) { return p.hit_ch != ipmt; });
424
425 if (channel_end - channel_begin < params.mAmp_trsh) {
426 continue;
427 }
428 channel_times.resize(channel_end - channel_begin);
429 std::transform(channel_begin, channel_end, channel_times.begin(), [](BCCache::particle const& p) { return p.hit_time; });
430 int chain = (std::rand() % 2) ? 1 : 0;
431 auto cfd = get_time(channel_times, mDeadTimes[ipmt].intrec.bc2ns() -
432 firstBCinDeque.bc2ns() +
433 mDeadTimes[ipmt].deadTime);
434 mDeadTimes[ipmt].intrec = firstBCinDeque;
435 mDeadTimes[ipmt].deadTime = cfd.deadTime;
436
437 if (!cfd.particle) {
438 continue;
439 }
440 // miscalibrate CFD with cahnnel offsets
441 int miscalib = 0;
442 if (mCalibOffset) {
443 miscalib = mCalibOffset->mTimeOffsets[ipmt];
444 }
445 int smeared_time = 1000. * (*cfd.particle - params.mCfdShift) * params.mChannelWidthInverse + miscalib;
446 bool is_time_in_signal_gate = (smeared_time > -params.mTime_trg_gate && smeared_time < params.mTime_trg_gate);
447 float charge = measure_amplitude(channel_times) * params.mCharge2amp;
448 float amp = is_time_in_signal_gate ? params.mMV_2_Nchannels * charge : 0.f;
449 if (amp > 4095.f) {
450 amp = 4095.f;
451 }
452 // if (!disabledChannels.count(ipmt)) {
453 // continue;
454 // }
455
456 LOG(debug) << mEventID << " bc " << firstBCinDeque.bc << " orbit " << firstBCinDeque.orbit
457 << ", ipmt " << ipmt << ", smeared_time " << smeared_time
458 << " nStored " << nStored << " offset " << miscalib
459 << " base amp " << amp;
460 if (is_time_in_signal_gate) {
463 }
464
465 baseAmp[ipmt] = amp;
466 finalAmp[ipmt] = amp;
467 chTime[ipmt] = smeared_time;
468 chChain[ipmt] = chain;
469 chValid[ipmt] = true;
470 }
471
472 for (const auto& channels : pmtChannelGroups) {
473 for (int localIdx = 0; localIdx < 4; ++localIdx) {
474 const int src = channels[localIdx];
475 if (!chValid[src] || baseAmp[src] <= 0.f) {
476 continue;
477 }
478
479 const int nb1 = channels[localNeighbours[localIdx][0]];
480 const int nb2 = channels[localNeighbours[localIdx][1]];
481 const int diag = channels[localNeighbours[localIdx][2]];
482
483 const float directXtalk = baseAmp[src] * params.Cross_Talk_Frac;
484 const float diagXtalk = baseAmp[src] * (params.Cross_Talk_Frac / 3.f);
485
486 finalAmp[nb1] += directXtalk;
487 finalAmp[nb2] += directXtalk;
488 finalAmp[diag] += diagXtalk;
489
490 if (!chValid[nb1] && directXtalk >= params.mAmpThresholdForCrossTalkDigit) {
491 chValid[nb1] = true;
492 chTime[nb1] = chTime[src];
493 chChain[nb1] = chChain[src];
494 }
495
496 if (!chValid[nb2] && directXtalk >= params.mAmpThresholdForCrossTalkDigit) {
497 chValid[nb2] = true;
498 chTime[nb2] = chTime[src];
499 chChain[nb2] = chChain[src];
500 }
501
502 if (!chValid[diag] && diagXtalk >= params.mAmpThresholdForCrossTalkDigit) {
503 chValid[diag] = true;
504 chTime[diag] = chTime[src];
505 chChain[diag] = chChain[src];
506 }
507 }
508 }
509
510 for (Int_t ipmt = 0; ipmt < params.mMCPs; ++ipmt) {
511 if (!chValid[ipmt]) {
512 continue;
513 }
514
515 float amp = finalAmp[ipmt];
516 if (amp > 4095.f) {
517 amp = 4095.f;
518 }
519 const bool hasPrimarySignal = (baseAmp[ipmt] > 0.f);
520 const bool isCrossTalkOnly = (!hasPrimarySignal && amp > 0.f);
521
522 if (isCrossTalkOnly && amp < params.mAmpThresholdForCrossTalkDigit) {
523 continue;
524 }
525
526 const int smeared_time = chTime[ipmt];
527 const int chain = chChain[ipmt];
528 const bool is_time_in_signal_gate = (smeared_time > -params.mTime_trg_gate && smeared_time < params.mTime_trg_gate);
529
530 if (is_time_in_signal_gate && ipmt < o2::ft0::Constants::sNCHANNELS_PM) {
531 mapPMhash2sumAmpl[mChID2PMhash[static_cast<uint8_t>(ipmt)]] += static_cast<int>(amp);
532 }
533
534 digitsCh.emplace_back(ipmt, smeared_time, int(amp), chain);
535 nStored++;
536
537 Bool_t is_A_side = (ipmt < 4 * mGeometry.NCellsA);
538 if (!is_time_in_signal_gate) {
539 continue;
540 }
541
542 sum_ampl_ipmt[ipmt] += amp;
543
544 if (is_A_side) {
545 n_hit_A++;
546 summ_ampl_A += amp;
547 mean_time_A += smeared_time;
548 } else {
549 n_hit_C++;
550 summ_ampl_C += amp;
551 mean_time_C += smeared_time;
552 }
553 }
554
555 for (size_t i = 0; i < sum_ampl_ipmt.size(); i++) {
556 sum_ampl_ipmt[i] = sum_ampl_ipmt[i] >> 3;
557 if (i < 4 * mGeometry.NCellsA) {
558 sum_A_ampl += sum_ampl_ipmt[i];
559 } else {
560 sum_C_ampl += sum_ampl_ipmt[i];
561 }
562 }
563
564 // Sum over PMs (using per-PM map) for debug/monitoring
565 int sum_PM_ampl_debug = 0;
566 int sum_PM_ampl_A_debug = 0;
567 int sum_PM_ampl_C_debug = 0;
568 for (const auto& entry : mapPMhash2sumAmpl) {
569 int pmAmpl = (entry.second >> 3);
570 sum_PM_ampl_debug += pmAmpl;
571 auto itSide = mMapPMhash2isAside.find(entry.first);
572 if (itSide != mMapPMhash2isAside.end()) {
573 if (itSide->second) {
574 sum_PM_ampl_A_debug += pmAmpl;
575 } else {
576 sum_PM_ampl_C_debug += pmAmpl;
577 }
578 }
579 }
580 LOG(debug) << "Sum PM amplitude (LUT-based): total=" << sum_PM_ampl_debug
581 << " A-side=" << sum_PM_ampl_A_debug
582 << " C-side=" << sum_PM_ampl_C_debug;
583
584 Bool_t is_A, is_C, isVertex, is_Central, is_SemiCentral = 0;
585 is_A = n_hit_A > 0;
586 is_C = n_hit_C > 0;
587 is_Central = sum_PM_ampl_A_debug + sum_PM_ampl_C_debug >= 2 * params.mtrg_central_trh;
588 is_SemiCentral = sum_PM_ampl_A_debug + sum_PM_ampl_C_debug >= 2 * params.mtrg_semicentral_trh && !is_Central;
589 uint32_t amplA = is_A ? summ_ampl_A * 0.125 : -5000; // sum amplitude A side / 8 (hardware)
590 uint32_t amplC = is_C ? summ_ampl_C * 0.125 : -5000; // sum amplitude C side / 8 (hardware)
591 int timeA = is_A ? mean_time_A / n_hit_A : -5000; // average time A side
592 int timeC = is_C ? mean_time_C / n_hit_C : -5000; // average time C side
593 vertex_time = (timeC - timeA) * 0.5;
594 isVertex = is_A && is_C && (vertex_time > -params.mTime_trg_vertex_gate && vertex_time < params.mTime_trg_vertex_gate);
595 LOG(debug) << " A " << is_A << " timeA " << timeA << " mean_time_A " << mean_time_A << " n_hit_A " << n_hit_A << " C " << is_C << " timeC " << timeC << " mean_time_C " << mean_time_C << " n_hit_C " << n_hit_C << " vertex_time " << vertex_time;
596 Triggers triggers;
597 bool isLaser = false;
598 bool isOutputsAreBlocked = false;
599 bool isDataValid = true;
600 if (nStored > 0) {
601 triggers.setTriggers(is_A, is_C, isVertex, is_Central, is_SemiCentral, int8_t(n_hit_A), int8_t(n_hit_C),
602 amplA, amplC, timeA, timeC, isLaser, isOutputsAreBlocked, isDataValid);
603 digitsBC.emplace_back(first, nStored, firstBCinDeque, triggers, mEventID - 1);
604 digitsTrig.emplace_back(firstBCinDeque, is_A, is_C, isVertex, is_Central, is_SemiCentral);
605 size_t const nBC = digitsBC.size();
606 for (auto const& lbl : bc.labels) {
607 labels.addElement(nBC - 1, lbl);
608 }
609 }
610 // Debug output -------------------------------------------------------------
611
612 LOG(debug) << "Event ID: " << mEventID << ", bc " << firstBCinDeque.bc << ", N hit " << bc.hits.size();
613 LOG(debug) << "N hit A: " << int(triggers.getNChanA()) << " N hit C: " << int(triggers.getNChanC()) << " summ ampl A: " << int(triggers.getAmplA())
614 << " summ ampl C: " << int(triggers.getAmplC()) << " mean time A: " << triggers.getTimeA()
615 << " mean time C: " << triggers.getTimeC() << " nStored " << nStored;
616
617 LOG(debug) << "IS A " << triggers.getOrA() << " IsC " << triggers.getOrC() << " vertex " << triggers.getVertex() << " is Central " << triggers.getCen() << " is SemiCentral " << triggers.getSCen();
618}
619
620//------------------------------------------------------------------------
621void Digitizer::flush(std::vector<o2::ft0::Digit>& digitsBC,
622 std::vector<o2::ft0::ChannelData>& digitsCh,
623 std::vector<o2::ft0::DetTrigInput>& digitsTrig,
625{
626
627 assert(firstBCinDeque <= mIntRecord);
628
629 while (firstBCinDeque < mIntRecord && !mCache.empty()) {
630 storeBC(mCache.front(), digitsBC, digitsCh, digitsTrig, labels);
631 mCache.pop_front();
632 ++firstBCinDeque;
633 }
634 firstBCinDeque = mIntRecord;
635}
636
637void Digitizer::flush_all(std::vector<o2::ft0::Digit>& digitsBC,
638 std::vector<o2::ft0::ChannelData>& digitsCh,
639 std::vector<o2::ft0::DetTrigInput>& digitsTrig,
641{
642
643 assert(firstBCinDeque <= mIntRecord);
644 ++mEventID;
645 while (!mCache.empty()) {
646 storeBC(mCache.front(), digitsBC, digitsCh, digitsTrig, labels);
647 mCache.pop_front();
648 ++firstBCinDeque;
649 }
650}
651
653{
654 auto const sinc = [](double x) { x *= TMath::Pi(); return (std::abs(x) < 1e-12) ? 1.0 : std::sin(x) / x; };
655
656 // number of noise samples in one BC
657 const auto& params = FT0DigParam::Instance();
658 mNumNoiseSamples = std::ceil(params.mBunchWidth / params.mNoisePeriod);
659 mNoiseSamples.resize(mNumNoiseSamples);
660
661 // set up tables with sinc function values (times noiseVar)
662 for (size_t i = 0, n = mSincTable.size(); i < n; ++i) {
663 float const time = i / float(n) * params.mNoisePeriod; // [0 .. 1/params.mNoisePeriod)
664 LOG(debug) << "initParameters " << i << "/" << n << " " << time;
665 // we make a table of sinc values between -num_noise_samples and 2*num_noise_samples
666 mSincTable[i].resize(3 * mNumNoiseSamples);
667 for (int j = -mNumNoiseSamples; j < 2 * mNumNoiseSamples; ++j) {
668 mSincTable[i][mNumNoiseSamples + j] = params.mNoiseVar * sinc((time + 0.5f * params.mBunchWidth) / params.mNoisePeriod - j);
669 }
670 }
671 // set up the lookup table for the signal form
672 for (size_t i = 0; i < DP::SIGNAL_TABLE_SIZE; ++i) {
673 float const x = float(i) / float(DP::SIGNAL_TABLE_SIZE) * params.mBunchWidth;
674 mSignalTable[i] = signalForm_i(x);
675 }
676
677 // cache for signal time series used by the CFD -BC/2 .. +3BC/2
678 mSignalCache.resize(std::lround(params.mBunchWidth / DP::SIGNAL_CACHE_DT));
679}
680//_______________________________________________________________________
682{
683 LOG(info) << " @@@ Digitizer::init " << std::endl;
684 mDeadTimes.fill({InteractionRecord(0), -100.});
686}
687//_______________________________________________________________________
689{
691}
692
693//_______________________________________________________________________
695{
696 const auto& params = FT0DigParam::Instance();
697 LOG(info) << " Run Digitzation with parametrs: \n"
698 << " CFD amplitude threshold \n " << params.mCFD_trsh << " CFD signal gate in ps \n"
699 << params.mTime_trg_gate << "shift to have signal around zero after CFD trancformation \n"
700 << params.mCfdShift << "CFD distance between 0.3 of max amplitude to max \n"
701 << params.mCFDShiftPos << "MIP -> mV " << params.mMip_in_V << " Pe in MIP \n"
702 << params.mPe_in_mip << "noise level " << params.mNoiseVar << " noise frequency \n"
703 << params.mNoisePeriod << " mMCPs " << params.mMCPs;
704}
705
std::vector< std::string > labels
std::vector< unsigned long > times
#define O2ParamImpl(classname)
uint64_t exp(uint64_t base, uint8_t exp) noexcept
General constants in FT0.
std::ostringstream debug
ClassImp(Digitizer)
int64_t timeC
int16_t charge
Definition RawEventData.h:5
int64_t amplA
int64_t timeA
int64_t amplC
uint64_t bc
Definition RawEventData.h:5
int16_t time
Definition RawEventData.h:4
Configurable digitization parameters.
int32_t i
GPUChain * chain
constexpr int p2()
constexpr int p1()
constexpr to accelerate the coordinates changing
Definition of a container to keep Monte Carlo truth external to simulation objects.
Header to collect physics constants.
uint32_t j
Definition RawData.h:0
A container to hold and manage MC truth information/labels.
static SingleLUT & Instance(const Table_t *table=nullptr, long timestamp=-1)
uint8_t getNChanC() const
Definition Triggers.h:97
int32_t getAmplC() const
Definition Triggers.h:99
bool getCen() const
Definition Triggers.h:85
int32_t getAmplA() const
Definition Triggers.h:98
bool getVertex() const
Definition Triggers.h:87
void setTriggers(uint8_t trgsig, uint8_t chanA, uint8_t chanC, int32_t aamplA, int32_t aamplC, int16_t atimeA, int16_t atimeC)
Definition Triggers.h:103
int16_t getTimeC() const
Definition Triggers.h:101
uint8_t getNChanA() const
Definition Triggers.h:96
bool getOrA() const
Definition Triggers.h:80
bool getOrC() const
Definition Triggers.h:81
int16_t getTimeA() const
Definition Triggers.h:100
bool getSCen() const
Definition Triggers.h:83
void flush(std::vector< o2::ft0::Digit > &digitsBC, std::vector< o2::ft0::ChannelData > &digitsCh, std::vector< o2::ft0::DetTrigInput > &digitsTrig, o2::dataformats::MCTruthContainer< o2::ft0::MCLabel > &label)
void printParameters() const
CFDOutput get_time(const std::vector< float > &times, float deadTime)
VcType signalFormVc(VcType x) const
Definition Digitizer.h:121
void process(const std::vector< o2::ft0::HitType > *hits, std::vector< o2::ft0::Digit > &digitsBC, std::vector< o2::ft0::ChannelData > &digitsCh, std::vector< o2::ft0::DetTrigInput > &digitsTrig, o2::dataformats::MCTruthContainer< o2::ft0::MCLabel > &label)
void flush_all(std::vector< o2::ft0::Digit > &digitsBC, std::vector< o2::ft0::ChannelData > &digitsCh, std::vector< o2::ft0::DetTrigInput > &digitsTrig, o2::dataformats::MCTruthContainer< o2::ft0::MCLabel > &label)
double measure_amplitude(const std::vector< float > &times) const
float signalForm(float x) const
Definition Digitizer.h:106
static constexpr int NCellsA
Definition Geometry.h:52
static constexpr int NCellsC
Definition Geometry.h:53
GLdouble n
Definition glcorearb.h:1982
GLint GLenum GLint x
Definition glcorearb.h:403
const GLfloat * m
Definition glcorearb.h:4066
GLenum src
Definition glcorearb.h:1767
GLuint64EXT * result
Definition glcorearb.h:5662
GLuint entry
Definition glcorearb.h:5735
GLuint GLfloat GLfloat GLfloat x1
Definition glcorearb.h:5034
GLuint index
Definition glcorearb.h:781
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLenum const GLfloat * params
Definition glcorearb.h:272
GLuint GLsizei const GLchar * label
Definition glcorearb.h:2519
GLuint GLfloat * val
Definition glcorearb.h:1582
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
GLint GLuint mask
Definition glcorearb.h:291
uint8_t itsSharedClusterMap uint8_t
constexpr float LightSpeedCm2NS
Float signalForm_i(Float x)
Definition Digitizer.cxx:44
Vc::float_v signalForm_integralVc(Vc::float_v x)
float signalForm_integral(float x)
Definition Digitizer.cxx:74
uint32_t orbit
LHC orbit.
uint16_t bc
bunch crossing ID of interaction
static double bc2ns(int bc, unsigned int orbit)
const bool isChannelAlive(const uint8_t &chId) const
static constexpr std::size_t sNCHANNELS_PM
Definition Constants.h:33
static constexpr double SIGNAL_CACHE_DT
std::array< int16_t, o2::ft0::Geometry::Nchannels > mTimeOffsets
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
std::vector< ChannelData > channels