Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-04-06 12:11:01

0001 #ifndef EventFilter_SiStripRawToDigi_SiStripFEDBuffer_H
0002 #define EventFilter_SiStripRawToDigi_SiStripFEDBuffer_H
0003 
0004 #include <string>
0005 #include <vector>
0006 #include <memory>
0007 #include <ostream>
0008 #include <cstring>
0009 #include <cmath>
0010 #include "EventFilter/SiStripRawToDigi/interface/SiStripFEDBufferComponents.h"
0011 #include "DataFormats/SiStripDigi/interface/SiStripRawDigi.h"
0012 #include "DataFormats/SiStripDigi/interface/SiStripDigi.h"
0013 
0014 #include <cstdint>
0015 
0016 namespace sistrip {
0017   constexpr uint16_t BITS_PER_BYTE = 8;
0018 
0019   //
0020   // Class definitions
0021   //
0022 
0023   //class representing standard (non-spy channel) FED buffers
0024   class FEDBuffer final : public FEDBufferBase {
0025   public:
0026     /**
0027      * constructor from a FEDRawData buffer
0028      *
0029      * The sistrip::preconstructCheckFEDBuffer() method should be used
0030      * (with the same value of allowBadBuffer) to check the validity of
0031      * fedBuffer before constructing a sistrip::FEDBuffer.
0032      * If allowBadBuffer is set to true, the initialization proceeds
0033      * even if the event format is not recognized.
0034      * To initialize also the channel information, the FEDBuffer::findChannels()
0035      * method should be called as well, and its return status checked
0036      * (unless bad buffers, with an unrecognized event format or channel lengths
0037      * that do not make sense, should also be included).
0038      *
0039      * @see sistrip::preconstructCheckFEDBuffer() sistrip::FEDBuffer::findChannels()
0040      */
0041     explicit FEDBuffer(const FEDRawData& fedBuffer, const bool allowBadBuffer = false);
0042     ~FEDBuffer() override {}
0043 
0044     /**
0045      * Read the channel lengths from the payload
0046      *
0047      * This method should be called to after the constructor
0048      * (and should not be called more than once for the same sistrip::FEDBuffer).
0049      * In case any check fails, a value different from sistrip::FEDBufferStatusCode::SUCCESS
0050      * is returned, and detailed information printed to LogDebug("FEDBuffer"), if relevant.
0051      *
0052      * @see sistrip::FEDBuffer::FEDBuffer()
0053      */
0054     FEDBufferStatusCode findChannels();
0055 
0056     void print(std::ostream& os) const override;
0057     const FEDFEHeader* feHeader() const;
0058     //check that a FE unit is enabled, has a good majority address and, if in full debug mode, that it is present
0059     bool feGood(const uint8_t internalFEUnitNum) const;
0060     bool feGoodWithoutAPVEmulatorCheck(const uint8_t internalFEUnitNum) const;
0061     //check that a FE unit is present in the data.
0062     //The high order byte of the FEDStatus register in the tracker special header is used in APV error mode.
0063     //The FE length from the full debug header is used in full debug mode.
0064     bool fePresent(uint8_t internalFEUnitNum) const;
0065     //check that a channel is present in data, found, on a good FE unit and has no errors flagged in status bits
0066     using sistrip::FEDBufferBase::channelGood;
0067     bool channelGood(const uint8_t internalFEDannelNum, const bool doAPVeCheck) const;
0068     void setLegacyMode(bool legacy) { legacyUnpacker_ = legacy; }
0069 
0070     //functions to check buffer. All return true if there is no problem.
0071     //minimum checks to do before using buffer
0072     using sistrip::FEDBufferBase::doChecks;
0073     bool doChecks(bool doCRC) const;
0074 
0075     //additional checks to check for corrupt buffers
0076     //check channel lengths fit inside to buffer length
0077     bool checkChannelLengths() const;
0078     //check that channel lengths add up to buffer length (this does the previous check as well)
0079     bool checkChannelLengthsMatchBufferLength() const;
0080     //check channel packet codes match readout mode
0081     bool checkChannelPacketCodes() const;
0082     //check FE unit lengths in FULL DEBUG header match the lengths of their channels
0083     bool checkFEUnitLengths() const;
0084     //check FE unit APV addresses in FULL DEBUG header are equal to the APVe address if the majority was good
0085     bool checkFEUnitAPVAddresses() const;
0086     //do all corrupt buffer checks
0087     virtual bool doCorruptBufferChecks() const;
0088 
0089     //check that there are no errors in channel, APV or FEUnit status bits
0090     //these are done by channelGood(). Channels with bad status bits may be disabled so bad status bits do not usually indicate an error
0091     bool checkStatusBits(const uint8_t internalFEDChannelNum) const;
0092     bool checkStatusBits(const uint8_t internalFEUnitNum, const uint8_t internalChannelNum) const;
0093     //same but for all channels on enabled FE units
0094     bool checkAllChannelStatusBits() const;
0095 
0096     //check that all FE unit payloads are present
0097     bool checkFEPayloadsPresent() const;
0098 
0099     //print a summary of all checks
0100     std::string checkSummary() const override;
0101 
0102   private:
0103     uint8_t nFEUnitsPresent() const;
0104     inline uint8_t getCorrectPacketCode() const { return packetCode(legacyUnpacker_); }
0105     uint16_t calculateFEUnitLength(const uint8_t internalFEUnitNumber) const;
0106     std::unique_ptr<FEDFEHeader> feHeader_;
0107     const uint8_t* payloadPointer_;
0108     uint16_t payloadLength_;
0109     uint8_t validChannels_;
0110     bool fePresent_[FEUNITS_PER_FED];
0111     bool legacyUnpacker_ = false;
0112   };
0113 
0114   //
0115   // Inline function definitions
0116   //
0117 
0118   /**
0119    * Check if a FEDRawData object satisfies the requirements for constructing a sistrip::FEDBuffer
0120    *
0121    * These are:
0122    *   - those from sistrip::preconstructCheckFEDBufferBase()
0123    *   - the readout mode should not be sistrip::READOUT_MODE_SPY
0124    *   - (unless allowBadBuffers is true) the header type should not be sistrip::HEADER_TYPE_INVALID or HEADER_TYPE_NONE
0125    *
0126    * In case any check fails, a value different from sistrip::FEDBufferStatusCode::SUCCESS
0127    * is returned, and detailed information printed to LogDebug("FEDBuffer"), if relevant.
0128    *
0129    * @see sistrip::preconstructCheckFEDBufferBase()
0130    */
0131   inline FEDBufferStatusCode preconstructCheckFEDBuffer(const FEDRawData& fedBuffer, bool allowBadBuffer = false) {
0132     const auto st_base = preconstructCheckFEDBufferBase(fedBuffer, !allowBadBuffer);
0133     if (FEDBufferStatusCode::SUCCESS != st_base)
0134       return st_base;
0135     const TrackerSpecialHeader hdr{fedBuffer.data() + 8};
0136     const auto hdr_type = hdr.headerType();
0137     if ((!allowBadBuffer) && ((hdr_type == sistrip::HEADER_TYPE_INVALID) || (hdr_type == sistrip::HEADER_TYPE_NONE))) {
0138 #ifdef EDM_ML_DEBUG
0139       std::ostringstream msg;
0140       msg << "Header type is invalid. Header type nibble is ";
0141       const auto headerTypeNibble = hdr.headerTypeNibble();
0142       printHex(&headerTypeNibble, 1, msg);
0143       LogDebug("FEDBuffer") << msg.str();
0144 #endif
0145       return FEDBufferStatusCode::WRONG_HEADERTYPE;
0146     }
0147     if (READOUT_MODE_SPY == hdr.readoutMode())
0148       return FEDBufferStatusCode::EXPECT_NOT_SPY;
0149     return FEDBufferStatusCode::SUCCESS;
0150   }
0151 
0152   //FEDBuffer
0153 
0154   inline const FEDFEHeader* FEDBuffer::feHeader() const { return feHeader_.get(); }
0155 
0156   inline bool FEDBuffer::channelGood(const uint8_t internalFEDChannelNum, const bool doAPVeCheck) const {
0157     return ((internalFEDChannelNum < validChannels_) &&
0158             ((doAPVeCheck && feGood(internalFEDChannelNum / FEDCH_PER_FEUNIT)) ||
0159              (!doAPVeCheck && feGoodWithoutAPVEmulatorCheck(internalFEDChannelNum / FEDCH_PER_FEUNIT))) &&
0160             (this->readoutMode() == sistrip::READOUT_MODE_SCOPE || checkStatusBits(internalFEDChannelNum)));
0161   }
0162 
0163   inline bool FEDBuffer::feGood(const uint8_t internalFEUnitNum) const {
0164     return (!majorityAddressErrorForFEUnit(internalFEUnitNum) && !feOverflow(internalFEUnitNum) &&
0165             fePresent(internalFEUnitNum));
0166   }
0167 
0168   inline bool FEDBuffer::feGoodWithoutAPVEmulatorCheck(const uint8_t internalFEUnitNum) const {
0169     return (!feOverflow(internalFEUnitNum) && fePresent(internalFEUnitNum));
0170   }
0171 
0172   inline bool FEDBuffer::fePresent(uint8_t internalFEUnitNum) const { return fePresent_[internalFEUnitNum]; }
0173 
0174   inline bool FEDBuffer::checkStatusBits(const uint8_t internalFEDChannelNum) const {
0175     return feHeader_->checkChannelStatusBits(internalFEDChannelNum);
0176   }
0177 
0178   inline bool FEDBuffer::checkStatusBits(const uint8_t internalFEUnitNum, const uint8_t internalChannelNum) const {
0179     return checkStatusBits(internalFEDChannelNum(internalFEUnitNum, internalChannelNum));
0180   }
0181 
0182   inline bool FEDBuffer::doChecks(bool doCRC) const {
0183     //check that all channels were unpacked properly
0184     return (validChannels_ == FEDCH_PER_FED) &&
0185            //do checks from base class
0186            (FEDBufferBase::doChecks()) &&
0187            // check crc if required
0188            (!doCRC || checkCRC());
0189   }
0190 
0191   namespace fedchannelunpacker {
0192     enum class StatusCode { SUCCESS = 0, BAD_CHANNEL_LENGTH, UNORDERED_DATA, BAD_PACKET_CODE, ZERO_PACKET_CODE };
0193 
0194     namespace detail {
0195 
0196       template <uint8_t num_words>
0197       uint16_t getADC_W(const uint8_t* data, uint_fast16_t offset, uint8_t bits_shift) {
0198         // get ADC from one or two bytes (at most 10 bits), and shift if needed
0199         return (data[offset ^ 7] + (num_words == 2 ? ((data[(offset + 1) ^ 7] & 0x03) << 8) : 0)) << bits_shift;
0200       }
0201 
0202       template <uint16_t mask>
0203       uint16_t getADC_B2(const uint8_t* data, uint_fast16_t wOffset, uint_fast8_t bOffset) {
0204         // get ADC from two bytes, from wOffset until bOffset bits from the next byte (maximum decided by mask)
0205         return (((data[wOffset ^ 7]) << bOffset) + (data[(wOffset + 1) ^ 7] >> (BITS_PER_BYTE - bOffset))) & mask;
0206       }
0207       template <uint16_t mask>
0208       uint16_t getADC_B1(const uint8_t* data, uint_fast16_t wOffset, uint_fast8_t bOffset) {
0209         // get ADC from one byte, until bOffset into the byte at wOffset (maximum decided by mask)
0210         return (data[wOffset ^ 7] >> (BITS_PER_BYTE - bOffset)) & mask;
0211       }
0212 
0213       // Unpack Raw with ADCs in whole 8-bit words (8bit and 10-in-16bit)
0214       template <uint8_t num_bits, typename OUT>
0215       StatusCode unpackRawW(const FEDChannel& channel, OUT&& out, uint8_t bits_shift = 0) {
0216         constexpr auto num_words = num_bits / 8;
0217         static_assert(((num_bits % 8) == 0) && (num_words > 0) && (num_words < 3));
0218         if ((num_words > 1) && ((channel.length() - 3) % num_words)) {
0219           LogDebug("FEDBuffer") << "Channel length is invalid. Raw channels have 3 header bytes and " << num_words
0220                                 << " bytes per sample. "
0221                                 << "Channel length is " << uint16_t(channel.length()) << ".";
0222           return StatusCode::BAD_CHANNEL_LENGTH;
0223         }
0224         const uint8_t* const data = channel.data();
0225         const uint_fast16_t end = channel.offset() + channel.length();
0226         for (uint_fast16_t offset = channel.offset() + 3; offset != end; offset += num_words) {
0227           *out++ = SiStripRawDigi(getADC_W<num_words>(data, offset, bits_shift));
0228         }
0229         return StatusCode::SUCCESS;
0230       }
0231 
0232       // Generic implementation for non-whole words (10bit, essentially)
0233       template <uint_fast8_t num_bits, typename OUT>
0234       StatusCode unpackRawB(const FEDChannel& channel, OUT&& out) {
0235         static_assert(num_bits <= 16, "Word length must be between 0 and 16.");
0236         if (channel.length() & 0xF000) {
0237           LogDebug("FEDBuffer") << "Channel length is invalid. Channel length is " << uint16_t(channel.length()) << ".";
0238           return StatusCode::BAD_CHANNEL_LENGTH;
0239         }
0240         constexpr uint16_t mask = (1 << num_bits) - 1;
0241         const uint8_t* const data = channel.data();
0242         const uint_fast16_t chEnd = channel.offset() + channel.length();
0243         uint_fast16_t wOffset = channel.offset() + 3;
0244         uint_fast8_t bOffset = 0;
0245         while (((wOffset + 1) < chEnd) || ((chEnd - wOffset) * BITS_PER_BYTE - bOffset >= num_bits)) {
0246           bOffset += num_bits;
0247           if ((num_bits > BITS_PER_BYTE) || (bOffset > BITS_PER_BYTE)) {
0248             bOffset -= BITS_PER_BYTE;
0249             **out++ = SiStripRawDigi(getADC_B2<mask>(data, wOffset, bOffset));
0250             ++wOffset;
0251           } else {
0252             **out++ = SiStripRawDigi(getADC_B1<mask>(data, wOffset, bOffset));
0253           }
0254           if (bOffset == BITS_PER_BYTE) {
0255             bOffset = 0;
0256             ++wOffset;
0257           }
0258         }
0259         return StatusCode::SUCCESS;
0260       }
0261 
0262       template <uint8_t num_bits, typename OUT>
0263       StatusCode unpackZSW(
0264           const FEDChannel& channel, OUT&& out, uint8_t headerLength, uint16_t stripStart, uint8_t bits_shift = 0) {
0265         constexpr auto num_words = num_bits / 8;
0266         static_assert(((num_bits % 8) == 0) && (num_words > 0) && (num_words < 3));
0267         if (channel.length() & 0xF000) {
0268           LogDebug("FEDBuffer") << "Channel length is invalid. Channel length is " << uint16_t(channel.length()) << ".";
0269           return StatusCode::BAD_CHANNEL_LENGTH;
0270         }
0271         const uint8_t* const data = channel.data();
0272         uint_fast16_t offset = channel.offset() + headerLength;  // header is 2 (lite) or 7
0273         uint_fast8_t firstStrip{0}, nInCluster{0}, inCluster{0};
0274         const uint_fast16_t end = channel.offset() + channel.length();
0275         while (offset != end) {
0276           if (inCluster == nInCluster) {
0277             if (offset + 2 >= end) {
0278               // offset should already be at end then (empty cluster)
0279               break;
0280             }
0281             const uint_fast8_t newFirstStrip = data[(offset++) ^ 7];
0282             if (newFirstStrip < (firstStrip + inCluster)) {
0283               LogDebug("FEDBuffer") << "First strip of new cluster is not greater than last strip of previous cluster. "
0284                                     << "Last strip of previous cluster is " << uint16_t(firstStrip + inCluster) << ". "
0285                                     << "First strip of new cluster is " << uint16_t(newFirstStrip) << ".";
0286               return StatusCode::UNORDERED_DATA;
0287             }
0288             firstStrip = newFirstStrip;
0289             nInCluster = data[(offset++) ^ 7];
0290             inCluster = 0;
0291           }
0292           *out++ = SiStripDigi(stripStart + firstStrip + inCluster, getADC_W<num_words>(data, offset, bits_shift));
0293           offset += num_words;
0294           ++inCluster;
0295         }
0296         return StatusCode::SUCCESS;
0297       }
0298 
0299       // Generic implementation (for 10bit, essentially)
0300       template <uint_fast8_t num_bits, typename OUT>
0301       StatusCode unpackZSB(const FEDChannel& channel, OUT&& out, uint8_t headerLength, uint16_t stripStart) {
0302         constexpr uint16_t mask = (1 << num_bits) - 1;
0303         if (channel.length() & 0xF000) {
0304           LogDebug("FEDBuffer") << "Channel length is invalid. Channel length is " << uint16_t(channel.length()) << ".";
0305           return StatusCode::BAD_CHANNEL_LENGTH;
0306         }
0307         const uint8_t* const data = channel.data();
0308         uint_fast16_t wOffset = channel.offset() + headerLength;  // header is 2 (lite) or 7
0309         uint_fast8_t bOffset{0}, firstStrip{0}, nInCluster{0}, inCluster{0};
0310         const uint_fast16_t chEnd = channel.offset() + channel.length();
0311         while (((wOffset + 1) < chEnd) ||
0312                ((inCluster != nInCluster) && ((chEnd - wOffset) * BITS_PER_BYTE - bOffset >= num_bits))) {
0313           if (inCluster == nInCluster) {
0314             if (wOffset + 2 >= chEnd) {
0315               // offset should already be at end then (empty cluster)
0316               break;
0317             }
0318             if (bOffset) {
0319               ++wOffset;
0320               bOffset = 0;
0321             }
0322             const uint_fast8_t newFirstStrip = data[(wOffset++) ^ 7];
0323             if (newFirstStrip < (firstStrip + inCluster)) {
0324               LogDebug("FEDBuffer") << "First strip of new cluster is not greater than last strip of previous cluster. "
0325                                     << "Last strip of previous cluster is " << uint16_t(firstStrip + inCluster) << ". "
0326                                     << "First strip of new cluster is " << uint16_t(newFirstStrip) << ".";
0327               return StatusCode::UNORDERED_DATA;
0328             }
0329             firstStrip = newFirstStrip;
0330             nInCluster = data[(wOffset++) ^ 7];
0331             inCluster = 0;
0332             bOffset = 0;
0333           }
0334           bOffset += num_bits;
0335           if ((num_bits > BITS_PER_BYTE) || (bOffset > BITS_PER_BYTE)) {
0336             bOffset -= BITS_PER_BYTE;
0337             *out++ = SiStripDigi(stripStart + firstStrip + inCluster, getADC_B2<mask>(data, wOffset, bOffset));
0338             ++wOffset;
0339           } else {
0340             *out++ = SiStripDigi(stripStart + firstStrip + inCluster, getADC_B1<mask>(data, wOffset, bOffset));
0341           }
0342           ++inCluster;
0343           if (bOffset == BITS_PER_BYTE) {
0344             bOffset = 0;
0345             ++wOffset;
0346           }
0347         }
0348         return StatusCode::SUCCESS;
0349       }
0350 
0351       inline uint16_t readoutOrder(uint16_t physical_order) {
0352         return (4 * ((static_cast<uint16_t>((static_cast<float>(physical_order) / 8.0))) % 4) +
0353                 static_cast<uint16_t>(static_cast<float>(physical_order) / 32.0) + 16 * (physical_order % 8));
0354       }
0355     };  // namespace detail
0356 
0357     inline bool isZeroSuppressed(FEDReadoutMode mode,
0358                                  bool legacy = false,
0359                                  FEDLegacyReadoutMode lmode = READOUT_MODE_LEGACY_INVALID) {
0360       if (!legacy) {
0361         switch (mode) {
0362           case READOUT_MODE_ZERO_SUPPRESSED_LITE10:
0363           case READOUT_MODE_ZERO_SUPPRESSED_LITE10_CMOVERRIDE:
0364           case READOUT_MODE_ZERO_SUPPRESSED_LITE8_TOPBOT:
0365           case READOUT_MODE_PREMIX_RAW:
0366           case READOUT_MODE_ZERO_SUPPRESSED_LITE8_TOPBOT_CMOVERRIDE:
0367           case READOUT_MODE_ZERO_SUPPRESSED_LITE8_CMOVERRIDE:
0368           case READOUT_MODE_ZERO_SUPPRESSED_LITE8_BOTBOT:
0369           case READOUT_MODE_ZERO_SUPPRESSED:
0370           case READOUT_MODE_ZERO_SUPPRESSED_FAKE:
0371           case READOUT_MODE_ZERO_SUPPRESSED_LITE8:
0372           case READOUT_MODE_ZERO_SUPPRESSED_LITE8_BOTBOT_CMOVERRIDE:
0373             return true;
0374             break;
0375           default:
0376             return false;
0377         }
0378       } else {
0379         switch (lmode) {
0380           case READOUT_MODE_LEGACY_ZERO_SUPPRESSED_REAL:
0381           case READOUT_MODE_LEGACY_ZERO_SUPPRESSED_FAKE:
0382           case READOUT_MODE_LEGACY_ZERO_SUPPRESSED_LITE_REAL:
0383           case READOUT_MODE_LEGACY_ZERO_SUPPRESSED_LITE_FAKE:
0384           case READOUT_MODE_LEGACY_PREMIX_RAW:
0385             return true;
0386           default:
0387             return false;
0388         }
0389       }
0390     }
0391     inline bool isNonLiteZS(FEDReadoutMode mode,
0392                             bool legacy = false,
0393                             FEDLegacyReadoutMode lmode = READOUT_MODE_LEGACY_INVALID) {
0394       return (!legacy) ? (mode == READOUT_MODE_ZERO_SUPPRESSED || mode == READOUT_MODE_ZERO_SUPPRESSED_FAKE)
0395                        : (lmode == READOUT_MODE_LEGACY_ZERO_SUPPRESSED_REAL ||
0396                           lmode == READOUT_MODE_LEGACY_ZERO_SUPPRESSED_FAKE);
0397     }
0398     inline bool isVirginRaw(FEDReadoutMode mode,
0399                             bool legacy = false,
0400                             FEDLegacyReadoutMode lmode = READOUT_MODE_LEGACY_INVALID) {
0401       return (!legacy) ? mode == READOUT_MODE_VIRGIN_RAW
0402                        : (lmode == READOUT_MODE_LEGACY_VIRGIN_RAW_REAL || lmode == READOUT_MODE_LEGACY_VIRGIN_RAW_FAKE);
0403     }
0404     inline bool isProcessedRaw(FEDReadoutMode mode,
0405                                bool legacy = false,
0406                                FEDLegacyReadoutMode lmode = READOUT_MODE_LEGACY_INVALID) {
0407       return (!legacy) ? mode == READOUT_MODE_PROC_RAW
0408                        : (lmode == READOUT_MODE_LEGACY_PROC_RAW_REAL || lmode == READOUT_MODE_LEGACY_PROC_RAW_FAKE);
0409     }
0410     inline bool isScopeMode(FEDReadoutMode mode,
0411                             bool legacy = false,
0412                             FEDLegacyReadoutMode lmode = READOUT_MODE_LEGACY_INVALID) {
0413       return (!legacy) ? mode == READOUT_MODE_SCOPE : lmode == READOUT_MODE_LEGACY_SCOPE;
0414     }
0415 
0416     template <typename OUT>
0417     StatusCode unpackScope(const FEDChannel& channel, OUT&& out) {
0418       return detail::unpackRawW<16>(channel, out);
0419     }
0420     template <typename OUT>
0421     StatusCode unpackProcessedRaw(const FEDChannel& channel, OUT&& out) {
0422       return detail::unpackRawW<16>(channel, out);
0423     }
0424 
0425     template <typename OUT>
0426     StatusCode unpackVirginRaw(const FEDChannel& channel, OUT&& out, uint8_t packetCode) {
0427       std::vector<SiStripRawDigi> samples;
0428       auto st = StatusCode::SUCCESS;
0429       if (PACKET_CODE_VIRGIN_RAW == packetCode) {
0430         samples.reserve((channel.length() - 3) / 2);
0431         st = detail::unpackRawW<16>(channel, std::back_inserter(samples));
0432       } else if (PACKET_CODE_VIRGIN_RAW10 == packetCode) {
0433         samples.reserve((channel.length() - 3) * 10 / 8);
0434         st = detail::unpackRawB<10>(channel, std::back_inserter(samples));
0435       } else if (PACKET_CODE_VIRGIN_RAW8_BOTBOT == packetCode || PACKET_CODE_VIRGIN_RAW8_TOPBOT == packetCode) {
0436         samples.reserve(channel.length() - 3);
0437         st = detail::unpackRawW<8>(
0438             channel, std::back_inserter(samples), (PACKET_CODE_VIRGIN_RAW8_BOTBOT == packetCode ? 2 : 1));
0439       }
0440       if (!samples.empty()) {  // reorder
0441         for (uint_fast16_t i{0}; i != samples.size(); ++i) {
0442           const auto physical = i % 128;
0443           const auto readout = (detail::readoutOrder(physical) * 2  // convert index from physical to readout order
0444                                 + (i >= 128 ? 1 : 0));              // un-multiplex data
0445           *out++ = samples[readout];
0446         }
0447       }
0448       return st;
0449     }
0450     template <typename OUT>
0451     StatusCode unpackZeroSuppressed(const FEDChannel& channel,
0452                                     OUT&& out,
0453                                     uint16_t stripStart,
0454                                     bool isNonLite,
0455                                     FEDReadoutMode mode,
0456                                     bool legacy = false,
0457                                     FEDLegacyReadoutMode lmode = READOUT_MODE_LEGACY_INVALID,
0458                                     uint8_t packetCode = 0) {
0459       if ((isNonLite && packetCode == PACKET_CODE_ZERO_SUPPRESSED10) ||
0460           ((!legacy) &&
0461            (mode == READOUT_MODE_ZERO_SUPPRESSED_LITE10 || mode == READOUT_MODE_ZERO_SUPPRESSED_LITE10_CMOVERRIDE))) {
0462         return detail::unpackZSB<10>(channel, out, (isNonLite ? 7 : 2), stripStart);
0463       } else if ((!legacy) ? mode == READOUT_MODE_PREMIX_RAW : lmode == READOUT_MODE_LEGACY_PREMIX_RAW) {
0464         return detail::unpackZSW<16>(channel, out, 7, stripStart);
0465       } else {  // 8bit
0466         uint8_t bits_shift = 0;
0467         if (isNonLite) {
0468           if (packetCode == PACKET_CODE_ZERO_SUPPRESSED8_TOPBOT)
0469             bits_shift = 1;
0470           else if (packetCode == PACKET_CODE_ZERO_SUPPRESSED8_BOTBOT)
0471             bits_shift = 2;
0472         } else {  // lite
0473           if (mode == READOUT_MODE_ZERO_SUPPRESSED_LITE8_TOPBOT ||
0474               mode == READOUT_MODE_ZERO_SUPPRESSED_LITE8_TOPBOT_CMOVERRIDE)
0475             bits_shift = 1;
0476           else if (mode == READOUT_MODE_ZERO_SUPPRESSED_LITE8_BOTBOT ||
0477                    mode == READOUT_MODE_ZERO_SUPPRESSED_LITE8_BOTBOT_CMOVERRIDE)
0478             bits_shift = 2;
0479         }
0480         auto st = detail::unpackZSW<8>(channel, out, (isNonLite ? 7 : 2), stripStart, bits_shift);
0481         if (isNonLite && packetCode == 0 && StatusCode::SUCCESS == st) {
0482           // workaround for a pre-2015 bug in the packer: assume default ZS packing
0483           return StatusCode::ZERO_PACKET_CODE;
0484         }
0485         return st;
0486       }
0487     }
0488   };  // namespace fedchannelunpacker
0489   std::string toString(fedchannelunpacker::StatusCode status);
0490 }  // namespace sistrip
0491 
0492 #endif  //ndef EventFilter_SiStripRawToDigi_SiStripFEDBuffer_H