CMS 3D CMS Logo

SiStripFEDBuffer.h
Go to the documentation of this file.
1 #ifndef EventFilter_SiStripRawToDigi_SiStripFEDBuffer_H
2 #define EventFilter_SiStripRawToDigi_SiStripFEDBuffer_H
3 
4 #include <string>
5 #include <vector>
6 #include <memory>
7 #include <ostream>
8 #include <cstring>
9 #include <cmath>
13 
14 #include <cstdint>
15 
16 namespace sistrip {
17  constexpr uint16_t BITS_PER_BYTE = 8;
18 
19  //
20  // Class definitions
21  //
22 
23  //class representing standard (non-spy channel) FED buffers
24  class FEDBuffer final : public FEDBufferBase {
25  public:
41  explicit FEDBuffer(const FEDRawData& fedBuffer, const bool allowBadBuffer = false);
42  ~FEDBuffer() override {}
43 
55 
56  void print(std::ostream& os) const override;
57  const FEDFEHeader* feHeader() const;
58  //check that a FE unit is enabled, has a good majority address and, if in full debug mode, that it is present
59  bool feGood(const uint8_t internalFEUnitNum) const;
60  bool feGoodWithoutAPVEmulatorCheck(const uint8_t internalFEUnitNum) const;
61  //check that a FE unit is present in the data.
62  //The high order byte of the FEDStatus register in the tracker special header is used in APV error mode.
63  //The FE length from the full debug header is used in full debug mode.
64  bool fePresent(uint8_t internalFEUnitNum) const;
65  //check that a channel is present in data, found, on a good FE unit and has no errors flagged in status bits
67  bool channelGood(const uint8_t internalFEDannelNum, const bool doAPVeCheck) const;
68  void setLegacyMode(bool legacy) { legacyUnpacker_ = legacy; }
69 
70  //functions to check buffer. All return true if there is no problem.
71  //minimum checks to do before using buffer
73  bool doChecks(bool doCRC) const;
74 
75  //additional checks to check for corrupt buffers
76  //check channel lengths fit inside to buffer length
77  bool checkChannelLengths() const;
78  //check that channel lengths add up to buffer length (this does the previous check as well)
80  //check channel packet codes match readout mode
81  bool checkChannelPacketCodes() const;
82  //check FE unit lengths in FULL DEBUG header match the lengths of their channels
83  bool checkFEUnitLengths() const;
84  //check FE unit APV addresses in FULL DEBUG header are equal to the APVe address if the majority was good
85  bool checkFEUnitAPVAddresses() const;
86  //do all corrupt buffer checks
87  virtual bool doCorruptBufferChecks() const;
88 
89  //check that there are no errors in channel, APV or FEUnit status bits
90  //these are done by channelGood(). Channels with bad status bits may be disabled so bad status bits do not usually indicate an error
91  bool checkStatusBits(const uint8_t internalFEDChannelNum) const;
92  bool checkStatusBits(const uint8_t internalFEUnitNum, const uint8_t internalChannelNum) const;
93  //same but for all channels on enabled FE units
94  bool checkAllChannelStatusBits() const;
95 
96  //check that all FE unit payloads are present
97  bool checkFEPayloadsPresent() const;
98 
99  //print a summary of all checks
100  std::string checkSummary() const override;
101 
102  private:
103  uint8_t nFEUnitsPresent() const;
104  inline uint8_t getCorrectPacketCode() const { return packetCode(legacyUnpacker_); }
105  uint16_t calculateFEUnitLength(const uint8_t internalFEUnitNumber) const;
106  std::unique_ptr<FEDFEHeader> feHeader_;
107  const uint8_t* payloadPointer_;
108  uint16_t payloadLength_;
109  uint8_t validChannels_;
111  bool legacyUnpacker_ = false;
112  };
113 
114  //
115  // Inline function definitions
116  //
117 
131  inline FEDBufferStatusCode preconstructCheckFEDBuffer(const FEDRawData& fedBuffer, bool allowBadBuffer = false) {
132  const auto st_base = preconstructCheckFEDBufferBase(fedBuffer, !allowBadBuffer);
133  if (FEDBufferStatusCode::SUCCESS != st_base)
134  return st_base;
135  const TrackerSpecialHeader hdr{fedBuffer.data() + 8};
136  const auto hdr_type = hdr.headerType();
137  if ((!allowBadBuffer) && ((hdr_type == sistrip::HEADER_TYPE_INVALID) || (hdr_type == sistrip::HEADER_TYPE_NONE))) {
138 #ifdef EDM_ML_DEBUG
139  std::ostringstream msg;
140  msg << "Header type is invalid. Header type nibble is ";
141  const auto headerTypeNibble = hdr.headerTypeNibble();
142  printHex(&headerTypeNibble, 1, msg);
143  LogDebug("FEDBuffer") << msg.str();
144 #endif
146  }
147  if (READOUT_MODE_SPY == hdr.readoutMode())
150  }
151 
152  //FEDBuffer
153 
154  inline const FEDFEHeader* FEDBuffer::feHeader() const { return feHeader_.get(); }
155 
156  inline bool FEDBuffer::channelGood(const uint8_t internalFEDChannelNum, const bool doAPVeCheck) const {
158  ((doAPVeCheck && feGood(internalFEDChannelNum / FEDCH_PER_FEUNIT)) ||
160  (this->readoutMode() == sistrip::READOUT_MODE_SCOPE || checkStatusBits(internalFEDChannelNum)));
161  }
162 
163  inline bool FEDBuffer::feGood(const uint8_t internalFEUnitNum) const {
164  return (!majorityAddressErrorForFEUnit(internalFEUnitNum) && !feOverflow(internalFEUnitNum) &&
165  fePresent(internalFEUnitNum));
166  }
167 
168  inline bool FEDBuffer::feGoodWithoutAPVEmulatorCheck(const uint8_t internalFEUnitNum) const {
169  return (!feOverflow(internalFEUnitNum) && fePresent(internalFEUnitNum));
170  }
171 
172  inline bool FEDBuffer::fePresent(uint8_t internalFEUnitNum) const { return fePresent_[internalFEUnitNum]; }
173 
174  inline bool FEDBuffer::checkStatusBits(const uint8_t internalFEDChannelNum) const {
175  return feHeader_->checkChannelStatusBits(internalFEDChannelNum);
176  }
177 
178  inline bool FEDBuffer::checkStatusBits(const uint8_t internalFEUnitNum, const uint8_t internalChannelNum) const {
179  return checkStatusBits(internalFEDChannelNum(internalFEUnitNum, internalChannelNum));
180  }
181 
182  inline bool FEDBuffer::doChecks(bool doCRC) const {
183  //check that all channels were unpacked properly
184  return (validChannels_ == FEDCH_PER_FED) &
185  //do checks from base class
187  // check crc if required
188  (!doCRC || checkCRC());
189  }
190 
191  namespace fedchannelunpacker {
193 
194  namespace detail {
195 
196  template <uint8_t num_words>
197  uint16_t getADC_W(const uint8_t* data, uint_fast16_t offset, uint8_t bits_shift) {
198  // get ADC from one or two bytes (at most 10 bits), and shift if needed
199  return (data[offset ^ 7] + (num_words == 2 ? ((data[(offset + 1) ^ 7] & 0x03) << 8) : 0)) << bits_shift;
200  }
201 
202  template <uint16_t mask>
203  uint16_t getADC_B2(const uint8_t* data, uint_fast16_t wOffset, uint_fast8_t bOffset) {
204  // get ADC from two bytes, from wOffset until bOffset bits from the next byte (maximum decided by mask)
205  return (((data[wOffset ^ 7]) << bOffset) + (data[(wOffset + 1) ^ 7] >> (BITS_PER_BYTE - bOffset))) & mask;
206  }
207  template <uint16_t mask>
208  uint16_t getADC_B1(const uint8_t* data, uint_fast16_t wOffset, uint_fast8_t bOffset) {
209  // get ADC from one byte, until bOffset into the byte at wOffset (maximum decided by mask)
210  return (data[wOffset ^ 7] >> (BITS_PER_BYTE - bOffset)) & mask;
211  }
212 
213  // Unpack Raw with ADCs in whole 8-bit words (8bit and 10-in-16bit)
214  template <uint8_t num_bits, typename OUT>
215  StatusCode unpackRawW(const FEDChannel& channel, OUT&& out, uint8_t bits_shift = 0) {
216  constexpr auto num_words = num_bits / 8;
217  static_assert(((num_bits % 8) == 0) && (num_words > 0) && (num_words < 3));
218  if ((num_words > 1) && ((channel.length() - 3) % num_words)) {
219  LogDebug("FEDBuffer") << "Channel length is invalid. Raw channels have 3 header bytes and " << num_words
220  << " bytes per sample. "
221  << "Channel length is " << uint16_t(channel.length()) << ".";
223  }
224  const uint8_t* const data = channel.data();
225  const uint_fast16_t end = channel.offset() + channel.length();
226  for (uint_fast16_t offset = channel.offset() + 3; offset != end; offset += num_words) {
227  *out++ = SiStripRawDigi(getADC_W<num_words>(data, offset, bits_shift));
228  }
229  return StatusCode::SUCCESS;
230  }
231 
232  // Generic implementation for non-whole words (10bit, essentially)
233  template <uint_fast8_t num_bits, typename OUT>
234  StatusCode unpackRawB(const FEDChannel& channel, OUT&& out) {
235  static_assert(num_bits <= 16, "Word length must be between 0 and 16.");
236  if (channel.length() & 0xF000) {
237  LogDebug("FEDBuffer") << "Channel length is invalid. Channel length is " << uint16_t(channel.length()) << ".";
239  }
240  constexpr uint16_t mask = (1 << num_bits) - 1;
241  const uint8_t* const data = channel.data();
242  const uint_fast16_t chEnd = channel.offset() + channel.length();
243  uint_fast16_t wOffset = channel.offset() + 3;
244  uint_fast8_t bOffset = 0;
245  while (((wOffset + 1) < chEnd) || ((chEnd - wOffset) * BITS_PER_BYTE - bOffset >= num_bits)) {
246  bOffset += num_bits;
247  if ((num_bits > BITS_PER_BYTE) || (bOffset > BITS_PER_BYTE)) {
248  bOffset -= BITS_PER_BYTE;
249  **out++ = SiStripRawDigi(getADC_B2<mask>(data, wOffset, bOffset));
250  ++wOffset;
251  } else {
252  **out++ = SiStripRawDigi(getADC_B1<mask>(data, wOffset, bOffset));
253  }
254  if (bOffset == BITS_PER_BYTE) {
255  bOffset = 0;
256  ++wOffset;
257  }
258  }
259  return StatusCode::SUCCESS;
260  }
261 
262  template <uint8_t num_bits, typename OUT>
264  const FEDChannel& channel, OUT&& out, uint8_t headerLength, uint16_t stripStart, uint8_t bits_shift = 0) {
265  constexpr auto num_words = num_bits / 8;
266  static_assert(((num_bits % 8) == 0) && (num_words > 0) && (num_words < 3));
267  if (channel.length() & 0xF000) {
268  LogDebug("FEDBuffer") << "Channel length is invalid. Channel length is " << uint16_t(channel.length()) << ".";
270  }
271  const uint8_t* const data = channel.data();
272  uint_fast16_t offset = channel.offset() + headerLength; // header is 2 (lite) or 7
273  uint_fast8_t firstStrip{0}, nInCluster{0}, inCluster{0};
274  const uint_fast16_t end = channel.offset() + channel.length();
275  while (offset != end) {
276  if (inCluster == nInCluster) {
277  if (offset + 2 >= end) {
278  // offset should already be at end then (empty cluster)
279  break;
280  }
281  const uint_fast8_t newFirstStrip = data[(offset++) ^ 7];
282  if (newFirstStrip < (firstStrip + inCluster)) {
283  LogDebug("FEDBuffer") << "First strip of new cluster is not greater than last strip of previous cluster. "
284  << "Last strip of previous cluster is " << uint16_t(firstStrip + inCluster) << ". "
285  << "First strip of new cluster is " << uint16_t(newFirstStrip) << ".";
287  }
288  firstStrip = newFirstStrip;
289  nInCluster = data[(offset++) ^ 7];
290  inCluster = 0;
291  }
292  *out++ = SiStripDigi(stripStart + firstStrip + inCluster, getADC_W<num_words>(data, offset, bits_shift));
293  offset += num_words;
294  ++inCluster;
295  }
296  return StatusCode::SUCCESS;
297  }
298 
299  // Generic implementation (for 10bit, essentially)
300  template <uint_fast8_t num_bits, typename OUT>
301  StatusCode unpackZSB(const FEDChannel& channel, OUT&& out, uint8_t headerLength, uint16_t stripStart) {
302  constexpr uint16_t mask = (1 << num_bits) - 1;
303  if (channel.length() & 0xF000) {
304  LogDebug("FEDBuffer") << "Channel length is invalid. Channel length is " << uint16_t(channel.length()) << ".";
306  }
307  const uint8_t* const data = channel.data();
308  uint_fast16_t wOffset = channel.offset() + headerLength; // header is 2 (lite) or 7
309  uint_fast8_t bOffset{0}, firstStrip{0}, nInCluster{0}, inCluster{0};
310  const uint_fast16_t chEnd = channel.offset() + channel.length();
311  while (((wOffset + 1) < chEnd) ||
312  ((inCluster != nInCluster) && ((chEnd - wOffset) * BITS_PER_BYTE - bOffset >= num_bits))) {
313  if (inCluster == nInCluster) {
314  if (wOffset + 2 >= chEnd) {
315  // offset should already be at end then (empty cluster)
316  break;
317  }
318  if (bOffset) {
319  ++wOffset;
320  bOffset = 0;
321  }
322  const uint_fast8_t newFirstStrip = data[(wOffset++) ^ 7];
323  if (newFirstStrip < (firstStrip + inCluster)) {
324  LogDebug("FEDBuffer") << "First strip of new cluster is not greater than last strip of previous cluster. "
325  << "Last strip of previous cluster is " << uint16_t(firstStrip + inCluster) << ". "
326  << "First strip of new cluster is " << uint16_t(newFirstStrip) << ".";
328  }
329  firstStrip = newFirstStrip;
330  nInCluster = data[(wOffset++) ^ 7];
331  inCluster = 0;
332  bOffset = 0;
333  }
334  bOffset += num_bits;
335  if ((num_bits > BITS_PER_BYTE) || (bOffset > BITS_PER_BYTE)) {
336  bOffset -= BITS_PER_BYTE;
337  *out++ = SiStripDigi(stripStart + firstStrip + inCluster, getADC_B2<mask>(data, wOffset, bOffset));
338  ++wOffset;
339  } else {
340  *out++ = SiStripDigi(stripStart + firstStrip + inCluster, getADC_B1<mask>(data, wOffset, bOffset));
341  }
342  ++inCluster;
343  if (bOffset == BITS_PER_BYTE) {
344  bOffset = 0;
345  ++wOffset;
346  }
347  }
348  return StatusCode::SUCCESS;
349  }
350 
351  inline uint16_t readoutOrder(uint16_t physical_order) {
352  return (4 * ((static_cast<uint16_t>((static_cast<float>(physical_order) / 8.0))) % 4) +
353  static_cast<uint16_t>(static_cast<float>(physical_order) / 32.0) + 16 * (physical_order % 8));
354  }
355  }; // namespace detail
356 
358  bool legacy = false,
360  if (!legacy) {
361  switch (mode) {
373  return true;
374  break;
375  default:
376  return false;
377  }
378  } else {
379  switch (lmode) {
385  return true;
386  default:
387  return false;
388  }
389  }
390  }
392  bool legacy = false,
397  }
399  bool legacy = false,
401  return (!legacy) ? mode == READOUT_MODE_VIRGIN_RAW
403  }
405  bool legacy = false,
407  return (!legacy) ? mode == READOUT_MODE_PROC_RAW
409  }
411  bool legacy = false,
413  return (!legacy) ? mode == READOUT_MODE_SCOPE : lmode == READOUT_MODE_LEGACY_SCOPE;
414  }
415 
416  template <typename OUT>
417  StatusCode unpackScope(const FEDChannel& channel, OUT&& out) {
418  return detail::unpackRawW<16>(channel, out);
419  }
420  template <typename OUT>
422  return detail::unpackRawW<16>(channel, out);
423  }
424 
425  template <typename OUT>
426  StatusCode unpackVirginRaw(const FEDChannel& channel, OUT&& out, uint8_t packetCode) {
427  std::vector<SiStripRawDigi> samples;
428  auto st = StatusCode::SUCCESS;
429  if (PACKET_CODE_VIRGIN_RAW == packetCode) {
430  samples.reserve((channel.length() - 3) / 2);
431  st = detail::unpackRawW<16>(channel, std::back_inserter(samples));
432  } else if (PACKET_CODE_VIRGIN_RAW10 == packetCode) {
433  samples.reserve((channel.length() - 3) * 10 / 8);
434  st = detail::unpackRawB<10>(channel, std::back_inserter(samples));
435  } else if (PACKET_CODE_VIRGIN_RAW8_BOTBOT == packetCode || PACKET_CODE_VIRGIN_RAW8_TOPBOT == packetCode) {
436  samples.reserve(channel.length() - 3);
437  st = detail::unpackRawW<8>(
438  channel, std::back_inserter(samples), (PACKET_CODE_VIRGIN_RAW8_BOTBOT == packetCode ? 2 : 1));
439  }
440  if (!samples.empty()) { // reorder
441  for (uint_fast16_t i{0}; i != samples.size(); ++i) {
442  const auto physical = i % 128;
443  const auto readout = (detail::readoutOrder(physical) * 2 // convert index from physical to readout order
444  + (i >= 128 ? 1 : 0)); // un-multiplex data
445  *out++ = samples[readout];
446  }
447  }
448  return st;
449  }
450  template <typename OUT>
452  OUT&& out,
453  uint16_t stripStart,
454  bool isNonLite,
456  bool legacy = false,
458  uint8_t packetCode = 0) {
459  if ((isNonLite && packetCode == PACKET_CODE_ZERO_SUPPRESSED10) ||
460  ((!legacy) &&
462  return detail::unpackZSB<10>(channel, out, (isNonLite ? 7 : 2), stripStart);
463  } else if ((!legacy) ? mode == READOUT_MODE_PREMIX_RAW : lmode == READOUT_MODE_LEGACY_PREMIX_RAW) {
464  return detail::unpackZSW<16>(channel, out, 7, stripStart);
465  } else { // 8bit
466  uint8_t bits_shift = 0;
467  if (isNonLite) {
468  if (packetCode == PACKET_CODE_ZERO_SUPPRESSED8_TOPBOT)
469  bits_shift = 1;
470  else if (packetCode == PACKET_CODE_ZERO_SUPPRESSED8_BOTBOT)
471  bits_shift = 2;
472  } else { // lite
475  bits_shift = 1;
478  bits_shift = 2;
479  }
480  auto st = detail::unpackZSW<8>(channel, out, (isNonLite ? 7 : 2), stripStart, bits_shift);
481  if (isNonLite && packetCode == 0 && StatusCode::SUCCESS == st) {
482  // workaround for a pre-2015 bug in the packer: assume default ZS packing
484  }
485  return st;
486  }
487  }
488  }; // namespace fedchannelunpacker
490 } // namespace sistrip
491 
492 #endif //ndef EventFilter_SiStripRawToDigi_SiStripFEDBuffer_H
FEDReadoutMode readoutMode() const
bool checkChannelLengthsMatchBufferLength() const
static const uint8_t PACKET_CODE_ZERO_SUPPRESSED8_BOTBOT
bool feOverflow(const uint8_t internalFEUnitNum) const
const uint8_t * data() const
bool feGoodWithoutAPVEmulatorCheck(const uint8_t internalFEUnitNum) const
StatusCode unpackZeroSuppressed(const FEDChannel &channel, OUT &&out, uint16_t stripStart, bool isNonLite, FEDReadoutMode mode, bool legacy=false, FEDLegacyReadoutMode lmode=READOUT_MODE_LEGACY_INVALID, uint8_t packetCode=0)
std::string checkSummary() const override
StatusCode unpackRawW(const FEDChannel &channel, OUT &&out, uint8_t bits_shift=0)
const FEDFEHeader * feHeader() const
FEDBuffer(const FEDRawData &fedBuffer, const bool allowBadBuffer=false)
uint8_t internalFEDChannelNum(const uint8_t internalFEUnitNum, const uint8_t internalFEUnitChannelNum)
bool checkFEUnitAPVAddresses() const
uint16_t calculateFEUnitLength(const uint8_t internalFEUnitNumber) const
bool isScopeMode(FEDReadoutMode mode, bool legacy=false, FEDLegacyReadoutMode lmode=READOUT_MODE_LEGACY_INVALID)
FEDBufferStatusCode findChannels()
StatusCode unpackZSW(const FEDChannel &channel, OUT &&out, uint8_t headerLength, uint16_t stripStart, uint8_t bits_shift=0)
uint8_t nFEUnitsPresent() const
bool checkFEUnitLengths() const
uint16_t getADC_B1(const uint8_t *data, uint_fast16_t wOffset, uint_fast8_t bOffset)
std::string toString(fedchannelunpacker::StatusCode status)
sistrip classes
constexpr uint32_t mask
Definition: gpuClustering.h:24
bool majorityAddressErrorForFEUnit(const uint8_t internalFEUnitNum) const
virtual bool doCorruptBufferChecks() const
bool fePresent_[FEUNITS_PER_FED]
StatusCode unpackZSB(const FEDChannel &channel, OUT &&out, uint8_t headerLength, uint16_t stripStart)
static const uint8_t PACKET_CODE_ZERO_SUPPRESSED8_TOPBOT
bool checkChannelPacketCodes() const
static const uint8_t PACKET_CODE_ZERO_SUPPRESSED10
void setLegacyMode(bool legacy)
static const uint8_t PACKET_CODE_VIRGIN_RAW10
bool isProcessedRaw(FEDReadoutMode mode, bool legacy=false, FEDLegacyReadoutMode lmode=READOUT_MODE_LEGACY_INVALID)
const uint8_t * payloadPointer_
static const uint16_t FEUNITS_PER_FED
bool checkFEPayloadsPresent() const
uint16_t getADC_W(const uint8_t *data, uint_fast16_t offset, uint8_t bits_shift)
constexpr uint16_t BITS_PER_BYTE
bool isVirginRaw(FEDReadoutMode mode, bool legacy=false, FEDLegacyReadoutMode lmode=READOUT_MODE_LEGACY_INVALID)
StatusCode unpackVirginRaw(const FEDChannel &channel, OUT &&out, uint8_t packetCode)
A Digi for the silicon strip detector, containing both strip and adc information, and suitable for st...
Definition: SiStripDigi.h:12
bool checkAllChannelStatusBits() const
static const uint8_t PACKET_CODE_VIRGIN_RAW8_TOPBOT
uint8_t getCorrectPacketCode() const
bool channelGood(const uint8_t internalFEDannelNum, const bool doAPVeCheck) const
StatusCode unpackScope(const FEDChannel &channel, OUT &&out)
static const uint8_t PACKET_CODE_VIRGIN_RAW8_BOTBOT
void printHex(const void *pointer, const size_t length, std::ostream &os)
bool isZeroSuppressed(FEDReadoutMode mode, bool legacy=false, FEDLegacyReadoutMode lmode=READOUT_MODE_LEGACY_INVALID)
static const uint16_t FEDCH_PER_FEUNIT
bool checkStatusBits(const uint8_t internalFEDChannelNum) const
bool checkChannelLengths() const
std::unique_ptr< FEDFEHeader > feHeader_
StatusCode unpackRawB(const FEDChannel &channel, OUT &&out)
tuple msg
Definition: mps_check.py:285
bool isNonLiteZS(FEDReadoutMode mode, bool legacy=false, FEDLegacyReadoutMode lmode=READOUT_MODE_LEGACY_INVALID)
virtual bool channelGood(const uint8_t internalFEDChannelNum) const
char data[epos_bytes_allocation]
Definition: EPOS_Wrapper.h:79
static const uint16_t FEDCH_PER_FED
uint16_t getADC_B2(const uint8_t *data, uint_fast16_t wOffset, uint_fast8_t bOffset)
bool feGood(const uint8_t internalFEUnitNum) const
FEDBufferStatusCode preconstructCheckFEDBuffer(const FEDRawData &fedBuffer, bool allowBadBuffer=false)
uint16_t readoutOrder(uint16_t physical_order)
const unsigned char * data() const
Return a const pointer to the beginning of the data buffer.
Definition: FEDRawData.cc:24
void print(std::ostream &os) const override
FEDBufferStatusCode preconstructCheckFEDBufferBase(const FEDRawData &fedBuffer, bool checkRecognizedFormat=true)
A Digi for the silicon strip detector, containing only adc information, and suitable for storing raw ...
bool fePresent(uint8_t internalFEUnitNum) const
static const uint8_t PACKET_CODE_VIRGIN_RAW
StatusCode unpackProcessedRaw(const FEDChannel &channel, OUT &&out)
#define LogDebug(id)
uint8_t packetCode(bool legacy=false, const uint8_t internalFEDChannelNum=0) const