CbmRoot
Loading...
Searching...
No Matches
trd2d/UnpackMS.cxx
Go to the documentation of this file.
1/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
2 SPDX-License-Identifier: GPL-3.0-only
3 Authors: Pascal Raisig, Alexandru Bercuci, Dominik Smith [committer] */
4
5#include "UnpackMS.h"
6
8
9#include <algorithm>
10#include <cassert>
11#include <iomanip>
12#include <iostream>
13#include <sstream>
14#include <vector>
15
16using std::unique_ptr;
17
18namespace cbm::algo::trd2d
19{
20 // ---- Fasp message constructor ----------------------------------------
21 FaspMessage::FaspMessage(uint8_t c, uint8_t typ, uint8_t t, uint16_t d, uint8_t asic)
22 : ch(c)
23 , type(eMessageType::kNone)
24 , tlab(t)
25 , data(d)
26 , fasp(asic)
27 {
28 if (typ == uint8_t(eMessageType::kData))
30 else if (typ == uint8_t(eMessageType::kEpoch))
32 }
33
34 std::string FaspMessage::print() const
35 {
36 std::stringstream ss;
37 switch (type) {
39 ss << " DATA : fasp=" << std::setw(2) << (int) fasp << " ch=" << std::setw(2) << (int) ch
40 << " t=" << std::setw(3) << (int) tlab << " data=" << std::setw(4) << (int) data << std::endl;
41 break;
42 case eMessageType::kEpoch: ss << " EPOCH: ch=" << (int) ch << " epoch=" << (int) epoch << std::endl; break;
43 default: ss << " MTYPE: unknown" << std::endl; break;
44 }
45
46 return ss.str();
47 }
48
49 // ---- Data type descriptor ---------------------------------------------
50 template<std::uint8_t mess_ver>
55
56 template<>
57 eMessageType FaspMessage::getType<uint8_t(eMessageVersion::kMess24)>(uint32_t w)
58 {
61 if ((w >> 31) & 0x1) return eMessageType::kEpoch;
63 }
64
65 // ---- Unpacking a DATA WORD ---------------------------------------------
66 template<std::uint8_t mess_ver>
67 void FaspMessage::readDW(uint32_t)
68 {
69 return;
70 }
71
72 template<>
73 void FaspMessage::readDW<uint8_t(eMessageVersion::kMess24)>(uint32_t w)
74 {
77 uint8_t shift(0);
78 uint16_t adc_data = (w >> shift) & 0x3fff;
79 // TODO This data format version delivers the ADC value as bit_sgn + 13 significant bits
80 // TODO The CbmTrdDigi supports digi data with only 12bits unsigned. The first tests will
81 // TODO convert the measurement to the old format leaving the implementation of the new storage to // TODO later time. (AB 14.06.2024)
82 uint16_t sign = adc_data >> 13; // sign
83 int value_i;
84 if (!sign)
85 value_i = adc_data;
86 else
87 value_i = (-1) * ((adc_data ^ 0xffff) & 0x1fff);
88 // convert to 12bit unsigned
89 data = (value_i + 0x1fff) >> 2;
90 shift += uint8_t(eMessageLength::kMessData);
91 tlab = (w >> shift) & 0x7f;
92 shift += uint8_t(eMessageLength::kMessTlab);
93 ch = (w >> shift) & 0xf;
94 shift += uint8_t(eMessageLength::kMessCh);
95 fasp = ((w >> shift) & 0x3f);
96 shift += uint8_t(eMessageLength::kMessFasp);
97 type = eMessageType((w >> shift) & 0x1);
98 shift += uint8_t(eMessageLength::kMessType);
99
100 // if (VERBOSE >= 2) {
101 // printf("v06.24Mess_readDW[%x] signed charge = %+d\n", w, value_i);
102 // print();
103 // }
104 return;
105 }
106
107 // ---- Unpacking an EPOCH WORD ---------------------------------------------
108 template<std::uint8_t mess_ver>
109 void FaspMessage::readEW(uint32_t)
110 {
111 return;
112 }
113
114 template<>
115 void FaspMessage::readEW<uint8_t(eMessageVersion::kMess24)>(uint32_t w)
116 {
119 uint8_t shift(0);
120 epoch = (w >> shift) & 0x1fffff;
121 shift += uint8_t(eMessageLength::kMessEpoch);
122 ch = (w >> shift) & 0xf;
123 shift += uint8_t(eMessageLength::kMessCh);
124 fasp = (w >> shift) & 0x3f;
125 shift += uint8_t(eMessageLength::kMessFasp);
126 type = eMessageType((w >> shift) & 0x1);
127 shift += uint8_t(eMessageLength::kMessType);
128
129 // if (VERBOSE >= 2) {
130 // printf("v06.24Mess_readEW[%x]\n", w);
131 // print();
132 // }
133 return;
134 }
135
136 void UnpackPar::dump() const
137 {
138 L_(debug) << "UnpackPar::dump()";
139 L_(debug) << "mod=" << fModId << " elink[" << (int) fEqId << "]=0x" << std::hex << (int) fEqAdd
140 << " nAsics=" << std::dec << fAsicParams.size();
141 for (const auto& [fasp, par] : fAsicParams) {
142 L_(debug) << " fasp=" << int(fasp) << " par=" << std::hex << &par;
143 }
144 L_(debug) << "UnpackPar::dump(-----------------------)";
145 }
146
147 template<>
148 uint8_t UnpackPar::mapFaspId2Mod<uint8_t(eMessageVersion::kMessLegacy)>(uint8_t fasp_id) const
149 {
153 L_(debug) << "<vLegacy> Eq[" << (int) fEqId << "] = 0x" << std::hex << fEqAdd;
154 return fEqId * NFASPROB + fasp_id;
155 }
156
157 template<>
158 uint8_t UnpackPar::mapFaspId2Mod<uint8_t(eMessageVersion::kMess24)>(uint8_t fasp_id) const
159 {
164 int rob = fEqId / 2; // ROB on the current chamber
165 //L_(debug) << "<v24> ROB=" << rob << " Eq[" << (int)fEqId << "] = 0x" << std::hex << fEqAdd;
166
167 return rob * NFASPROB + fasp_id;
168 }
169
170 // ---- Algorithm execution ---------------------------------------------
171 template<>
172 typename UnpackMS<uint8_t(eMessageVersion::kMessLegacy)>::Result_t
173 UnpackMS<uint8_t(eMessageVersion::kMessLegacy)>::operator()(const uint8_t* msContent,
174 const fles::MicrosliceDescriptor& msDescr,
175 const uint64_t tTimeslice) const
176 {
180 // --- Output data
181 Result_t result = {};
182
183 MsContext ctx = {};
184
185 // define time wrt start of time slice in TRD/FASP clks [80 MHz]. Contains:
186 // - relative offset of the MS wrt the TS
187 // - FASP epoch offset for current CROB
188 // - TRD2D system offset wrt to experiment time (e.g. Bmon)
189 uint64_t time = uint64_t((msDescr.idx - tTimeslice - fParams.fSystemTimeOffset) / 12.5);
190
191 // Get parameters for current eq id.
192 //const uint8_t crob_id = fParams.fEqId;
193
194 // Get the number of complete words in the input MS buffer.
195 const uint32_t nwords = msDescr.size / 4;
196 L_(debug) << "UnpackMS<kMessLegacy>::op() param.olink[" << (int) fParams.fEqId << "]=0x" << std::hex
197 << (int) fParams.fEqAdd << " data.rob=0x" << int(msDescr.eq_id) << " words=" << std::dec << nwords;
198 // We have 32 bit FASP frames in this readout version
199 const uint32_t* wd = reinterpret_cast<const uint32_t*>(msContent);
200
201 unsigned char lFaspOld(0xff);
202 std::vector<FaspMessage> vMess;
203 for (uint64_t j = 0; j < nwords; j++, wd++) {
204 uint32_t w = *wd;
205 uint8_t ch_id = w & 0xf;
206 uint8_t isaux = (w >> 4) & 0x1;
207 uint8_t slice = (w >> 5) & 0x7f;
208 uint16_t data = (w >> 12) & 0x3fff;
209 uint8_t fasp_id = ((w >> 26) & 0x3f);
210
211 if (isaux) {
212 if (ch_id == 0) {
213 // clear buffer
214 if (vMess.size()) {
215 pushDigis(vMess, time, ctx);
216 }
217 vMess.clear();
218
219 lFaspOld = 0xff;
220 time += FASP_EPOCH_LENGTH;
221 }
222 continue;
223 }
224 if (lFaspOld != fasp_id) {
225 if (vMess.size()) {
226 pushDigis(vMess, time, ctx);
227 }
228 vMess.clear();
229 lFaspOld = fasp_id;
230 }
231 if (data & 0x1) {
233 continue;
234 }
235 if (data & 0x2000) {
237 data &= 0x1fff;
238 }
239 vMess.emplace_back(ch_id, (uint8_t) eMessageType::kData, slice, data >> 1, lFaspOld);
240 }
241 std::get<0>(result) = FinalizeComponent(ctx); //TO DO: Original (non-algo) version calls this after MS loop!!
242 std::get<1>(result) = ctx.fMonitor;
243
244 return result;
245 }
246 //_________________________________________________________________________________
247 template<uint8_t sys_ver>
248 bool UnpackMS<sys_ver>::pushDigis(std::vector<FaspMessage> messes, const uint64_t time, MsContext& ctx) const
249 {
250 constexpr uint8_t mLegacy =
251 uint8_t(eMessageVersion::kMessLegacy); // message versions compatible with the current algo specialization
252 const uint16_t mod_id = fParams.fModId;
253 const uint8_t fasp_mod_id = fParams.mapFaspId2Mod<mLegacy>(messes[0].fasp);
254 const UnpackAsicPar& asicPar = fParams.fAsicParams.at(fasp_mod_id);
255
256 for (auto imess : messes) {
257 const UnpackChannelPar& chPar = asicPar.fChanParams[imess.ch];
258 //std::cout << imess.print();
259 const int32_t pad = std::abs(chPar.fPadAddress) / 2;
260 const bool hasPairingR = bool(chPar.fPadAddress > 0);
261 const uint8_t tdaqOffset = chPar.fDaqOffset;
262 const uint64_t lTime = time + tdaqOffset + imess.tlab;
263 const uint16_t lchR = hasPairingR ? imess.data : 0;
264 const uint16_t lchT = hasPairingR ? 0 : imess.data;
265 std::vector<CbmTrdDigi>& digiBuffer = ctx.fDigiBuffer[pad];
266
267 if (digiBuffer.size() == 0) { // init pad position in map and build digi for message
268 digiBuffer.emplace_back(pad, lchT, lchR, lTime);
269 digiBuffer.back().SetAddressModule(mod_id);
270 continue;
271 }
272
273 // check if last digi has both R/T message components. Update if not and is within time window
274 auto id = digiBuffer.rbegin(); // Should always be valid here.
275 // No need to extra check
276 double r, t;
277 int32_t dt;
278 const int32_t dtime = (*id).GetTimeDAQ() - lTime;
279 bool use(false);
280
281 if (abs(dtime) < 5) { // test message part of (last) digi
282 r = (*id).GetCharge(t, dt);
283 if (lchR && r < 0.1) { // set R charge on an empty slot
284 (*id).SetCharge(t, lchR, -dtime);
285 use = true;
286 }
287 else if (lchT && t < 0.1) { // set T charge on an empty slot
288 (*id).SetCharge(lchT, r, +dtime);
289 (*id).SetTimeDAQ(uint64_t((*id).GetTimeDAQ() - dtime));
290 use = true;
291 }
292 }
293
294 // build digi for message when update failed
295 if (!use) {
296 digiBuffer.emplace_back(pad, lchT, lchR, lTime);
297 digiBuffer.back().SetAddressModule(mod_id);
298 id = digiBuffer.rbegin();
299 }
300
301 // update charge for previously allocated digis to account for FASPRO ADC buffering and read-out feature
302 for (++id; id != digiBuffer.rend(); ++id) {
303 r = (*id).GetCharge(t, dt);
304 if (lchR && int(r)) { // update R charge and mark on digi
305 (*id).SetCharge(t, lchR, dt);
306 (*id).SetFlag(1);
307 break;
308 }
309 else if (lchT && int(t)) { // update T charge and mark on digi
310 (*id).SetCharge(lchT, r, dt);
311 (*id).SetFlag(0);
312 break;
313 }
314 }
315 }
316 messes.clear();
317
318 return true;
319 }
320
321 template<uint8_t sys_ver>
322 std::vector<CbmTrdDigi> UnpackMS<sys_ver>::FinalizeComponent(MsContext& ctx) const
323 {
324 std::vector<CbmTrdDigi> outputDigis;
325
326 for (uint16_t ipad(0); ipad < NFASPMOD * NFASPPAD; ipad++) {
327 if (!ctx.fDigiBuffer[ipad].size()) continue;
328 uint nIncomplete(0);
329 for (auto id = ctx.fDigiBuffer[ipad].begin(); id != ctx.fDigiBuffer[ipad].end(); id++) {
330 double r, t;
331 int32_t dt;
332 r = (*id).GetCharge(t, dt);
333 // check if digi has all signals CORRECTED
334 if (((t > 0) != (*id).IsFlagged(0)) || ((r > 0) != (*id).IsFlagged(1))) {
335 nIncomplete++;
336 continue;
337 }
338 // reset flags as they were used only to mark the correctly setting of the charge/digi
339 (*id).SetFlag(0, false);
340 (*id).SetFlag(1, false);
341 outputDigis.emplace_back(std::move((*id)));
342 }
343 // clear digi buffer wrt the digi which was forwarded to higher structures
344 ctx.fDigiBuffer[ipad].clear();
345 if (nIncomplete > 2) {
346 ctx.fMonitor.fNumIncompleteDigis++; //TO DO: This must be moved if finalization is done after MS loop
347 }
348 }
349 return outputDigis;
350 }
351
352 // ------------- Specialization kMess24 --------------------------------
353 typename UnpackMS<uint8_t(eMessageVersion::kMess24)>::Result_t
354 UnpackMS<uint8_t(eMessageVersion::kMess24)>::operator()(const uint8_t* msContent,
355 const fles::MicrosliceDescriptor& msDescr,
356 const uint64_t tTimeslice) const
357 {
362 constexpr uint8_t m24 =
363 uint8_t(eMessageVersion::kMess24); // message versions compatible with the current algo specialization
364 Result_t result = {};
365 MsContext ctx = {};
366
367 // define time wrt start of time slice in TRD/FASP clks [80 MHz]. Contains:
368 // - relative offset of the MS wrt the TS
369 // - FASP epoch offset for current CROB
370 // - TRD2D system offset wrt to experiment time
371 uint64_t time = uint64_t((msDescr.idx - tTimeslice - fParams.fSystemTimeOffset) * fAsicClockFreq);
372
373 // Get the number of complete words in the input MS buffer.
374 const uint32_t nwords = msDescr.size / 4;
375 L_(debug) << "UnpackMS<kMess24>::op() param.olink[" << (int) fParams.fEqId << "]=0x" << std::hex
376 << (int) fParams.fEqAdd << " data.rob=0x" << int(msDescr.eq_id) << " words=" << std::dec << nwords;
377
378 // We have 32 bit FASP frames in this readout version
379 const uint32_t* wd = reinterpret_cast<const uint32_t*>(msContent);
380
381 unsigned char lFaspOld(0xff);
382 std::vector<FaspMessage> vMess;
383 for (uint32_t j = 0; j < nwords; j++, wd++) {
384 uint32_t w = *wd;
385 // Select the appropriate conversion type of the word according to
386 // the current message version and type
387 switch (FaspMessage::getType<m24>(w)) {
388 case eMessageType::kData: ctx.fMess.readDW<m24>(w); break;
389 case eMessageType::kEpoch: ctx.fMess.readEW<m24>(w); break;
390 default: break; // no way to reach this line
391 }
392
393 // PROCESS EPOCH MESSAGES
394 if (ctx.fMess.type == eMessageType::kEpoch) {
395 if (ctx.fMess.ch == 0) { // check word integrity
396 // clear buffer
397 if (vMess.size()) {
398 pushDigis(vMess, time, ctx);
399 }
400 vMess.clear();
401
402 lFaspOld = 0xff;
403 time += FASP_EPOCH_LENGTH;
404 }
405 else {
406 L_(error) << "FASP message[Epoch] with wrong signature.";
408 }
409 continue;
410 }
411
412 // PROCESS DATA MESSAGES
413 // clear buffer when switching to other FASP
414 if (ctx.fMess.fasp != lFaspOld) {
415 if (vMess.size()) pushDigis(vMess, time, ctx);
416 vMess.clear();
417 lFaspOld = ctx.fMess.fasp;
418 }
419 if (ctx.fMess.data & 0x2000) { // kept for backward compatibility TODO
421 ctx.fMess.data &= 0x1fff;
422 }
423 vMess.emplace_back(ctx.fMess);
424 }
425
426 // combine all digis from this ROB
427 std::vector<CbmTrdDigi> outputDigis;
428 for (uint16_t ipad(0); ipad < NFASPMOD * NFASPPAD; ipad++) {
429 if (!ctx.fRobDigi[ipad].size()) continue;
430 for (auto id : ctx.fRobDigi[ipad]) {
431 // L_(debug) << id.ToString();
432 outputDigis.emplace_back(std::move(id));
433 }
434 }
435 std::get<0>(result) = outputDigis;
436 std::get<1>(result) = ctx.fMonitor;
437
438 return result;
439 }
440 bool UnpackMS<uint8_t(eMessageVersion::kMess24)>::pushDigis(std::vector<FaspMessage> messes, const uint64_t time,
441 MsContext& ctx) const
442 {
443 constexpr uint8_t m24 =
444 uint8_t(eMessageVersion::kMess24); // message versions compatible with the current algo specialization
445 const uint8_t fasp_mod_id = fParams.mapFaspId2Mod<m24>(messes[0].fasp);
446 L_(debug) << "pushDigis<v24> fasp=" << (int) messes[0].fasp << "/" << (int) fasp_mod_id;
447 if (fParams.fAsicParams.find(fasp_mod_id) == fParams.fAsicParams.end()) {
448 L_(error) << "pushDigis<v24> fasp=" << (int) messes[0].fasp << "/" << (int) fasp_mod_id
449 << " not mapped to param.olink[" << (int) fParams.fEqId << "]=0x" << std::hex << (int) fParams.fEqAdd;
450 return false;
451 }
452 const UnpackAsicPar& asicPar = fParams.fAsicParams.at(fasp_mod_id);
453
454 for (auto imess : messes) {
455 const UnpackChannelPar& chPar = asicPar.fChanParams[imess.ch];
456 // skip message if threshold set and signal under
457 if (chPar.fSignalThres && imess.data <= chPar.fSignalThres) continue;
458 const int32_t pad = std::abs(chPar.fPadAddress) / 2;
459 const bool hasPairingR = bool(chPar.fPadAddress > 0);
460 const uint64_t lTime = time + chPar.fDaqOffset + imess.tlab;
461 const uint16_t lchR = hasPairingR ? imess.data : 0;
462 const uint16_t lchT = hasPairingR ? 0 : imess.data;
463 std::vector<CbmTrdDigi>& digiBuffer = ctx.fRobDigi[pad];
464
465 // init pad position in array and build digi for message
466 if (digiBuffer.size() == 0) {
467 digiBuffer.emplace_back(pad, lchT, lchR, lTime);
468 digiBuffer.back().SetAddressModule(fParams.fModId);
469 continue;
470 }
471
472 // check if last digi has both R/T message components.
473 // Update if not and is within time window
474 auto& id = digiBuffer.back(); // Should always be valid here.
475 // No need to extra check
476 double r, t;
477 int32_t dt;
478 const int32_t dtime = id.GetTimeDAQ() - lTime;
479 bool use(false);
480 if (abs(dtime) < 5) { // test message part of (last) digi
481 r = id.GetCharge(t, dt);
482 if (lchR && r < 0.1) { // set R charge on an empty slot
483 id.SetCharge(t, lchR, -dtime);
484 use = true;
485 }
486 else if (lchT && t < 0.1) { // set T charge on an empty slot
487 id.SetCharge(lchT, r, +dtime);
488 id.SetTimeDAQ(uint64_t(id.GetTimeDAQ() - dtime));
489 use = true;
490 }
491 }
492
493 // build digi for message when update failed
494 if (!use) {
495 digiBuffer.emplace_back(pad, lchT, lchR, lTime);
496 digiBuffer.back().SetAddressModule(fParams.fModId);
497 }
498 }
499 messes.clear();
500
501 return true;
502 }
503} // namespace cbm::algo::trd2d
#define L_(level)
std::tuple< std::vector< Digi_t >, Monitor_t, Aux_t > Result_t
Unpack algorithm for TRD2D.
std::vector< CbmTrdDigi > FinalizeComponent(MsContext &ctx) const
Finalize component (e.g. copy from temp buffers)
bool pushDigis(std::vector< FaspMessage > messages, const uint64_t time, MsContext &ctx) const
@ kMess24
unpacker version for 2-board FASPRO+GETS HW
uint8_t tlab
time of the digi inside the epoch
void readDW(uint32_t w)
Read DATA WORD and store the content locally.
uint32_t epoch
epoch id (not used for the moment)
uint8_t ch
ch id in the FASP
void readEW(uint32_t w)
Read EPOCH WORD and store the content locally.
uint8_t fasp
FASP id in the module.
eMessageType type
message type 0 = data, 1 = epoch (not used for the moment)
static eMessageType getType(uint32_t w)
Implementation of message type descriptor according to message version.
TRD2D Unpacking parameters for one Asic.
TRD2D Unpacking parameters for one Asic channel.
int32_t fPadAddress
Pad address for channel.
uint8_t fDaqOffset
Time calibration parameter.
UnpackMonitorData fMonitor
Container for monitoring data.
std::array< std::vector< CbmTrdDigi >, NFASPMOD *NFASPCH > fDigiBuffer
std::array< std::vector< CbmTrdDigi >, NFASPMOD *NFASPPAD > fRobDigi
uint32_t fNumSelfTriggeredData
word fulfills data & 0x2000
uint32_t fNumIncompleteDigis
incomplete digis left in pads after finalization
uint32_t fNumErrEndBitSet
Corrupted data with end bit set.
void dump() const
Write to the debug stream the content of the current param object.
uint8_t fEqId
Equipment (optical fiber) ID of component.
uint16_t fModId
Module ID of component.
std::map< uint8_t, UnpackAsicPar > fAsicParams
Parameters for each ASIC.
uint16_t fEqAdd
Equipment (optical fiber) address [HEX].
#define NFASPROB
#define NFASPPAD
#define NFASPMOD
#define FASP_EPOCH_LENGTH