Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-05 08:16:18

0001 #include "TpcTimeFrameBuilder.h"
0002 
0003 #include <Event/oncsSubConstants.h>
0004 #include <Event/packet.h>
0005 
0006 #include <ffarawobjects/TpcRawHitv2.h>
0007 #include <ffarawobjects/TpcRawHitv3.h>
0008 #include <phool/PHTimer.h>  // for PHTimer
0009 
0010 #include <fun4all/Fun4AllHistoManager.h>
0011 #include <fun4all/Fun4AllReturnCodes.h>
0012 #include <qautils/QAHistManagerDef.h>
0013 #include <fun4all/PHTFileServer.h>
0014 
0015 #include <TAxis.h>
0016 #include <TH1.h>
0017 #include <TH2.h>
0018 #include <TNamed.h>
0019 #include <TTree.h>
0020 #include <TString.h>
0021 #include <TVector3.h>
0022 
0023 #include <cassert>
0024 #include <cstdint>
0025 #include <limits>
0026 #include <memory>
0027 #include <string>
0028 #include <tuple>   // For std::tie
0029 
0030 using namespace std;
0031 
0032 TpcTimeFrameBuilder::TpcTimeFrameBuilder(const int packet_id)
0033   : m_packet_id(packet_id)
0034   , m_HistoPrefix("TpcTimeFrameBuilder_Packet" + to_string(packet_id))
0035 {
0036   for (int fee = 0; fee < MAX_FEECOUNT; ++fee)
0037   {
0038     m_bcoMatchingInformation_vec.emplace_back(
0039         std::string("BcoMatchingInformation_Packet") + to_string(packet_id) + "_FEE" + std::to_string(fee));
0040   }
0041 
0042   m_feeData.resize(MAX_FEECOUNT);
0043 
0044   // cppcheck-suppress noCopyConstructor
0045   // cppcheck-suppress noOperatorEq
0046   m_packetTimer = new PHTimer("TpcTimeFrameBuilder_Packet" + to_string(packet_id));
0047 
0048   Fun4AllHistoManager* hm = QAHistManagerDef::getHistoManager();
0049   assert(hm);
0050 
0051   m_hNorm = new TH1D(TString(m_HistoPrefix.c_str()) + "_Normalization",  //
0052                      TString(m_HistoPrefix.c_str()) + " Normalization;Items;Count",
0053                      20, .5, 20.5);
0054   int i = 1;
0055   m_hNorm->GetXaxis()->SetBinLabel(i++, "Packet");
0056   m_hNorm->GetXaxis()->SetBinLabel(i++, "Lv1-Taggers");
0057   m_hNorm->GetXaxis()->SetBinLabel(i++, "EnDat-Taggers");
0058   m_hNorm->GetXaxis()->SetBinLabel(i++, "ChannelPackets");
0059   m_hNorm->GetXaxis()->SetBinLabel(i++, "Waveforms");
0060 
0061   m_hNorm->GetXaxis()->SetBinLabel(i++, "DMA_WORD_GTM");
0062   m_hNorm->GetXaxis()->SetBinLabel(i++, "DMA_WORD_FEE");
0063   m_hNorm->GetXaxis()->SetBinLabel(i++, "DMA_WORD_FEE_INVALID");
0064   m_hNorm->GetXaxis()->SetBinLabel(i++, "DMA_WORD_INVALID");
0065 
0066   m_hNorm->GetXaxis()->SetBinLabel(i++, "DMA_WORD_GTM_HEARTBEAT");
0067   m_hNorm->GetXaxis()->SetBinLabel(i++, "DMA_WORD_GTM_DC_STOP_SEND");
0068 
0069   m_hNorm->GetXaxis()->SetBinLabel(i++, "TimeFrameSizeLimitError");
0070 
0071   m_hNorm->GetXaxis()->SetBinLabel(i++, "GTM_TimeFrame_Matched");
0072   m_hNorm->GetXaxis()->SetBinLabel(i++, "GTM_TimeFrame_Unmatched");
0073   m_hNorm->GetXaxis()->SetBinLabel(i++, "GTM_TimeFrame_Matched_Hit_Sum");
0074   m_hNorm->GetXaxis()->SetBinLabel(i++, "GTM_TimeFrame_Dropped_Hit_Sum");
0075 
0076   assert(i <= 20);
0077   m_hNorm->GetXaxis()->LabelsOption("v");
0078   hm->registerHisto(m_hNorm);
0079 
0080   h_PacketLength = new TH1I(TString(m_HistoPrefix.c_str()) + "_PacketLength",  //
0081                             TString(m_HistoPrefix.c_str()) + " PacketLength;PacketLength [32bit Words];Count", 1000, .5, 5e6);
0082   hm->registerHisto(h_PacketLength);
0083 
0084   h_PacketLength_Residual = new TH1I(TString(m_HistoPrefix.c_str()) + "_PacketLength_Residual",  //
0085                                      TString(m_HistoPrefix.c_str()) +
0086                                          " PacketLength that does not fit into DMA transfer;PacketLength [16bit Words];Count",
0087                                      16, -.5, 15.5);
0088   hm->registerHisto(h_PacketLength_Residual);
0089 
0090   h_PacketLength_Padding = new TH1I(TString(m_HistoPrefix.c_str()) + "_PacketLength_Padding",  //
0091                                     TString(m_HistoPrefix.c_str()) +
0092                                         " padding within PacketLength;PacketLength [32bit Words];Count",
0093                                     16, -.5, 15.5);
0094   hm->registerHisto(h_PacketLength_Padding);
0095 
0096   m_hFEEDataStream = new TH2I(TString(m_HistoPrefix.c_str()) + "_FEE_DataStream_WordCount",  //
0097                               TString(m_HistoPrefix.c_str()) +
0098                                   " FEE Data Stream Word Count;FEE ID;Type;Count",
0099                               MAX_FEECOUNT, -.5, MAX_FEECOUNT - .5, 25, .5, 25.5);
0100   i = 1;
0101   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "WordValid");
0102   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "WordSkipped");
0103   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "WordDigitalCurrentKeyWord");
0104   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "InvalidLength");
0105   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "RawHit");
0106   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "HitFormatErrorOverLength");
0107   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "HitFormatErrorMismatchedLength");
0108   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "HitCRCError");
0109   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "DigitalCurrent");
0110   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "DigitalCurrentFormatErrorMismatchedLength");
0111   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "DigitalCurrentCRCError");
0112   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "ParityError");
0113   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "HitUnusedBeforeCleanup");
0114   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "PacketHeartBeat");
0115   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "PacketHeartBeatClockSyncUnavailable");
0116   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "PacketHeartBeatClockSyncError");
0117   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "PacketHeartBeatClockSyncOK");
0118   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "PacketClockSyncUnavailable");
0119   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "PacketClockSyncError");
0120   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "PacketClockSyncOK");
0121   assert(i <= 25);
0122   hm->registerHisto(m_hFEEDataStream);
0123 
0124   m_hFEEChannelPacketCount = new TH1I(TString(m_HistoPrefix.c_str()) + "_FEEChannelPacketCount",  //
0125                                       TString(m_HistoPrefix.c_str()) +
0126                                           " Count of waveform packet per channel;FEE*256 + Channel;Count",
0127                                       MAX_FEECOUNT * MAX_CHANNELS, -.5, MAX_FEECOUNT * MAX_CHANNELS - .5);
0128   hm->registerHisto(m_hFEEChannelPacketCount);
0129 
0130   m_hFEESAMPAADC = new TH2I(TString(m_HistoPrefix.c_str()) + "_FEE_SAMPA_ADC",  //
0131                             TString(m_HistoPrefix.c_str()) +
0132                                 " ADC distribution in 2D;ADC Time Bin [0...1023];FEE*8+SAMPA;Sum ADC",
0133                             MAX_PACKET_LENGTH, -.5, MAX_PACKET_LENGTH - .5,
0134                             MAX_FEECOUNT * MAX_SAMPA, -.5, MAX_FEECOUNT * MAX_SAMPA - .5);
0135   hm->registerHisto(m_hFEESAMPAADC);
0136 
0137   m_hFEESAMPAHeartBeatSync = new TH1I(TString(m_HistoPrefix.c_str()) + "_FEE_SAMPA_HEARTBEAT_SYNC",  //
0138                                       TString(m_HistoPrefix.c_str()) +
0139                                           " FEE/SAMPA Sync Heartbeat Count;FEE*8+SAMPA;Sync Heartbeat Count",
0140                                       MAX_FEECOUNT * MAX_SAMPA, -.5, MAX_FEECOUNT * MAX_SAMPA - .5);
0141   hm->registerHisto(m_hFEESAMPAHeartBeatSync);
0142 
0143   h_GTMClockDiff_Matched = new TH1I(TString(m_HistoPrefix.c_str()) + "_GTMClockDiff_Matched",  //
0144                                     TString(m_HistoPrefix.c_str()) +
0145                                         " GTM BCO Diff for Matched Time Frame;Trigger BCO Diff [BCO];Count",
0146                                     1024, -512 - .5, 512 - .5);
0147   hm->registerHisto(h_GTMClockDiff_Matched);
0148   h_GTMClockDiff_Unmatched = new TH1I(TString(m_HistoPrefix.c_str()) + "_GTMClockDiff_Unmatched",  //
0149                                       TString(m_HistoPrefix.c_str()) +
0150                                           " GTM BCO Diff for Unmatched Time Frame;Trigger BCO Diff [BCO];Count",
0151                                       1024, -512 - .5, 512 - .5);
0152   hm->registerHisto(h_GTMClockDiff_Unmatched);
0153   h_GTMClockDiff_Dropped = new TH1I(TString(m_HistoPrefix.c_str()) + "_GTMClockDiff_Dropped",  //
0154                                     TString(m_HistoPrefix.c_str()) +
0155                                         " GTM BCO Diff for Dropped Time Frame;Trigger BCO Diff [BCO];Count",
0156                                     16384, -16384 - .5, 0 - .5);
0157   hm->registerHisto(h_GTMClockDiff_Dropped);
0158   h_TimeFrame_Matched_Size = new TH1I(TString(m_HistoPrefix.c_str()) + "_TimeFrame_Matched_Size",  //
0159                                       TString(m_HistoPrefix.c_str()) +
0160                                           " Time frame size for Matched Time Frame ;Size [TPC raw hits];Count",
0161                                       3328, -.5, 3328 - .5);
0162   hm->registerHisto(h_TimeFrame_Matched_Size);
0163 
0164   h_ProcessPacket_Time = new TH2I(TString(m_HistoPrefix.c_str()) + "_ProcessPacket_Time",  //
0165                                   TString(m_HistoPrefix.c_str()) +
0166                                       " Time cost to run ProcessPacket();Call counts;Time elapsed per call [ms];Count",
0167                                   100, 0, 30e6, 100, 0, 10);
0168   hm->registerHisto(h_ProcessPacket_Time);
0169 }
0170 
0171 TpcTimeFrameBuilder::~TpcTimeFrameBuilder()
0172 {
0173   for (auto& timeFrameEntry : m_timeFrameMap)
0174   {
0175     while (!timeFrameEntry.second.empty())
0176     {
0177       delete timeFrameEntry.second.back();
0178       timeFrameEntry.second.pop_back();
0179     }
0180   }
0181 
0182   if (m_packetTimer)
0183   {
0184     delete m_packetTimer;
0185   }
0186 
0187   if (m_digitalCurrentDebugTTree)
0188   {
0189     delete m_digitalCurrentDebugTTree;
0190   }
0191 }
0192 
0193 void TpcTimeFrameBuilder::setVerbosity(const int i)
0194 {
0195   m_verbosity = i;
0196 
0197   for (BcoMatchingInformation& bcoMatchingInformation : m_bcoMatchingInformation_vec)
0198   {
0199     bcoMatchingInformation.set_verbosity(i);
0200   }
0201 }
0202 
0203 bool TpcTimeFrameBuilder::isMoreDataRequired(const uint64_t& gtm_bco) const
0204 {
0205   for (const BcoMatchingInformation& bcoMatchingInformation : m_bcoMatchingInformation_vec)
0206   {
0207     // if (not bcoMatchingInformation.is_verified())
0208     // {
0209     //   continue;
0210     // }
0211 
0212     if (bcoMatchingInformation.isMoreDataRequired(gtm_bco))
0213     {
0214       return true;
0215     }
0216   }
0217 
0218   if (m_verbosity > 1)
0219   {
0220     std::cout << __PRETTY_FUNCTION__ << "\t- packet " << m_packet_id
0221               << ":PASS: All FEEs satisfied for gtm_bco: 0x" << std::hex << gtm_bco << std::dec << ". Return false."
0222               << std::endl;
0223   }
0224   return false;
0225 }
0226 
0227 std::vector<TpcRawHit*>& TpcTimeFrameBuilder::getTimeFrame(const uint64_t& gtm_bco)
0228 {
0229   assert(m_hNorm);
0230   uint64_t bclk_rollover_corrected = m_bcoMatchingInformation_vec[0].get_gtm_rollover_correction(gtm_bco);
0231 
0232   // cleanup old unused matching info after completion of a time frame
0233   for (BcoMatchingInformation& bcoMatchingInformation : m_bcoMatchingInformation_vec)
0234   {
0235     bcoMatchingInformation.cleanup(bclk_rollover_corrected);
0236   }
0237 
0238   if (m_verbosity > 2)
0239   {
0240     std::cout << __PRETTY_FUNCTION__ << "\t- packet " << m_packet_id
0241               << ": getTimeFrame for gtm_bco: 0x" << std::hex << gtm_bco << std::dec
0242               << ": bclk_rollover_corrected: 0x" << std::hex << bclk_rollover_corrected << std::dec
0243               << std::endl;
0244   }
0245 
0246   for (auto it = m_timeFrameMap.begin(); it != m_timeFrameMap.end();)
0247   {
0248     if (it->first + GL1_BCO_MATCH_WINDOW < bclk_rollover_corrected)
0249     {
0250       if (m_verbosity >= 2)
0251       {
0252         std::cout << __PRETTY_FUNCTION__ << "\t- packet " << m_packet_id
0253                   << ":DROPPED: BCO " << std::hex << it->first << std::dec
0254                   << " dropped for gtm_bco: 0x" << std::hex << gtm_bco << std::dec
0255                   << " and bclk_rollover_corrected 0x" << std::hex
0256                   << bclk_rollover_corrected << std::dec << ". m_timeFrameMap:" << std::endl;
0257 
0258         // if (m_verbosity >= 3)
0259         {
0260           for (const auto& timeframe : m_timeFrameMap)
0261           {
0262             std::cout << "- BCO in map: 0x" << std::hex << timeframe.first << std::dec
0263                       << "(Diff:" << int64_t(timeframe.first) - int64_t(bclk_rollover_corrected)
0264                       << ")"
0265                       << " size: " << timeframe.second.size()
0266                       << std::endl;
0267           }
0268         }
0269       }
0270 
0271       m_hNorm->Fill("GTM_TimeFrame_Dropped_Hit_Sum", it->second.size());
0272       assert(h_GTMClockDiff_Dropped);
0273       h_GTMClockDiff_Dropped->Fill(int64_t(it->first) - int64_t(bclk_rollover_corrected));
0274       for (const auto& hit : it->second)
0275       {
0276         delete hit;
0277       }
0278       it = m_timeFrameMap.erase(it);
0279     }
0280     else if (it->first < bclk_rollover_corrected + GL1_BCO_MATCH_WINDOW)
0281     {
0282       if (m_verbosity > 1)
0283       {
0284         std::cout << __PRETTY_FUNCTION__ << "\t- packet " << m_packet_id
0285                   << ":PASS: BCO " << std::hex << it->first << std::dec
0286                   << " matched for gtm_bco: 0x" << std::hex << gtm_bco << std::dec
0287                   << " and bclk_rollover_corrected 0x" << std::hex
0288                   << bclk_rollover_corrected << std::dec << ". m_timeFrameMap:" << std::endl;
0289 
0290         for (const auto& timeframe : m_timeFrameMap)
0291         {
0292           std::cout << "- BCO in map: 0x" << std::hex << timeframe.first << std::dec
0293                     << "(Diff:" << int64_t(timeframe.first) - int64_t(bclk_rollover_corrected)
0294                     << ")"
0295                     << " size: " << timeframe.second.size()
0296                     << std::endl;
0297         }
0298       }
0299 
0300       m_hNorm->Fill("GTM_TimeFrame_Matched", 1);
0301       assert(h_GTMClockDiff_Matched);
0302       h_GTMClockDiff_Matched->Fill(int64_t(it->first) - int64_t(bclk_rollover_corrected));
0303       assert(h_TimeFrame_Matched_Size);
0304       h_TimeFrame_Matched_Size->Fill(it->second.size());
0305       m_hNorm->Fill("GTM_TimeFrame_Matched_Hit_Sum", it->second.size());
0306       m_UsedTimeFrameSet.push(it->first);
0307       return it->second;
0308     }
0309     else
0310     {
0311       break;
0312     }
0313   }
0314 
0315   if (m_verbosity >= 1)
0316   {
0317     std::cout << __PRETTY_FUNCTION__ << "\t- packet " << m_packet_id
0318               << ":WARNING: BCO no match for gtm_bco: 0x" << std::hex << gtm_bco << std::dec
0319               << "and bclk_rollover_corrected 0x" << std::hex
0320               << bclk_rollover_corrected << std::dec << ". m_timeFrameMap:" << std::endl;
0321 
0322     if (m_verbosity >= 2)
0323     {
0324       for (const auto& timeframe : m_timeFrameMap)
0325       {
0326         std::cout << "- BCO in map: 0x" << std::hex << timeframe.first << std::dec
0327                   << "(Diff:" << int64_t(timeframe.first) - int64_t(bclk_rollover_corrected) << ")" << std::endl;
0328       }
0329     }
0330   }
0331 
0332   m_hNorm->Fill("GTM_TimeFrame_Unmatched", 1);
0333   assert(h_GTMClockDiff_Unmatched);
0334   for (const auto& timeframe : m_timeFrameMap)
0335   {
0336     h_GTMClockDiff_Unmatched->Fill(int64_t(timeframe.first) - int64_t(bclk_rollover_corrected));
0337   }
0338   static std::vector<TpcRawHit*> empty;
0339   return empty;
0340 }
0341 
0342 void TpcTimeFrameBuilder::CleanupUsedPackets(const uint64_t& bclk)
0343 {
0344   if (m_verbosity > 2)
0345   {
0346     std::cout << __PRETTY_FUNCTION__ << "\t- packet " << m_packet_id << ": cleaning up bcos < 0x" << std::hex
0347               << bclk << std::dec << std::endl;
0348   }
0349 
0350   while (not m_UsedTimeFrameSet.empty())
0351   {
0352     uint64_t bco_completed = m_UsedTimeFrameSet.front();
0353     m_UsedTimeFrameSet.pop();
0354 
0355     if (m_verbosity > 1)
0356     {
0357       std::cout << __PRETTY_FUNCTION__ << "\t- packet " << m_packet_id
0358                 << ": cleaning up previous processed packet in m_timeFrameMap at clock 0x"
0359                 << std::hex
0360                 << bco_completed << std::dec
0361                 << " for CleanupUsedPackets(const uint64_t& bclk) call at bclk 0x" << std::hex
0362                 << bclk << std::dec
0363                 << " Diff:" << int64_t(bco_completed) - int64_t(bclk) << std::endl;
0364     }
0365 
0366     auto it = m_timeFrameMap.find(bco_completed);
0367 
0368     // assert(it != m_timeFrameMap.end());  // strong workflow check disabled
0369     // this can happen if TPC GL1 tagger is shifted slight ahead of the GTM BCO, but within GL1_BCO_MATCH_WINDOW
0370 
0371     if (it != m_timeFrameMap.end())
0372     {
0373       while (!it->second.empty())
0374       {
0375         delete it->second.back();
0376         it->second.pop_back();
0377       }
0378       m_timeFrameMap.erase(it);
0379     }
0380   }
0381 
0382   uint64_t bclk_rollover_corrected = m_bcoMatchingInformation_vec[0].get_gtm_rollover_correction(bclk);
0383 
0384   assert(m_hFEEDataStream);
0385 
0386   for (auto it = m_timeFrameMap.begin(); it != m_timeFrameMap.end();)
0387   {
0388     if (it->first <= bclk_rollover_corrected)
0389     {
0390       int count = 0;
0391       while (!it->second.empty())
0392       {
0393         m_hFEEDataStream->Fill(it->second.back()->get_fee(), "HitUnusedBeforeCleanup", 1);
0394         delete it->second.back();
0395         it->second.pop_back();
0396         ++count;
0397       }
0398 
0399       if (m_verbosity >= 1)
0400       {
0401         std::cout << __PRETTY_FUNCTION__ << "\t- packet " << m_packet_id
0402                   << ": cleaning up " << count << " TPC hits in m_timeFrameMap at clock 0x"
0403                   << std::hex
0404                   << it->first << std::dec
0405                   << " for <= bclk_rollover_corrected 0x" << std::hex
0406                   << bclk_rollover_corrected << std::dec
0407                   << " Diff:" << int64_t(it->first) - int64_t(bclk_rollover_corrected) << std::endl;
0408       }
0409       m_timeFrameMap.erase(it++);
0410     }
0411     else
0412     {
0413       break;
0414     }
0415   }  //   for (auto it = m_timeFrameMap.begin(); it != m_timeFrameMap.end();)
0416 }
0417 
0418 int TpcTimeFrameBuilder::ProcessPacket(Packet* packet)
0419 {
0420   static size_t call_count = 0;
0421   ++call_count;
0422 
0423   if (m_verbosity > 1)
0424   {
0425     std::cout << "TpcTimeFrameBuilder::ProcessPacket: " << m_packet_id
0426               << "\t- Entry " << std::endl;
0427   }
0428 
0429   if (!packet)
0430   {
0431     cout << __PRETTY_FUNCTION__ << "\t- Error : Invalid packet, doing nothing" << endl;
0432     assert(packet);
0433     return 0;
0434   }
0435 
0436   if (m_hitFormat <0 )
0437   {
0438     if (packet->getHitFormat() != IDTPCFEEV4 and packet->getHitFormat() != IDTPCFEEV5 and packet->getHitFormat() != IDTPCFEEV6 )
0439     {
0440       cout << __PRETTY_FUNCTION__ << "\t- Error : expect packet format " << IDTPCFEEV4 
0441           << " or "<< IDTPCFEEV5<< " or "<< IDTPCFEEV6
0442           << "\t- but received packet format " << packet->getHitFormat() << ":" << endl;
0443       packet->identify();
0444       assert(packet->getHitFormat() == IDTPCFEEV4 or packet->getHitFormat() == IDTPCFEEV5 or packet->getHitFormat() == IDTPCFEEV6);
0445       return 0;
0446     }
0447 
0448     m_hitFormat = packet->getHitFormat();
0449 
0450     double clock_multiplier (0);
0451     if (m_hitFormat == IDTPCFEEV4)
0452     {
0453       // this is the clock multiplier from lvl1 to fee clock
0454       // Tested with Run24 data. Could be changable in future runs
0455       clock_multiplier = 4.262916255;
0456     }
0457     else if (m_hitFormat == IDTPCFEEV5 or packet->getHitFormat() == IDTPCFEEV6)
0458     {
0459       // version 46 FEE firmware 
0460       clock_multiplier = 30./8.;
0461     }
0462     else
0463     {
0464       assert(clock_multiplier>0);
0465     }
0466 
0467     if (m_verbosity >= 1)
0468     {
0469       cout << __PRETTY_FUNCTION__ << " set clock sync for hit format " << m_hitFormat 
0470       <<" with clock_multiplier = "<< clock_multiplier << endl;
0471     }
0472     for (BcoMatchingInformation& bcoMatchingInformation : m_bcoMatchingInformation_vec)
0473     {
0474       bcoMatchingInformation.set_gtm_clock_multiplier(clock_multiplier);
0475     }
0476   }
0477   else
0478   {
0479     if (packet->getHitFormat() != m_hitFormat)
0480     {
0481       cout << __PRETTY_FUNCTION__ << "\t- Error : expect the last packet format " << m_hitFormat
0482           << "\t- but received packet format " << packet->getHitFormat() << ":" << endl;
0483       packet->identify();
0484       assert((packet->getHitFormat() == m_hitFormat));
0485       return 0;
0486     }
0487   }
0488   assert((packet->getHitFormat() == m_hitFormat));
0489 
0490   if (m_packet_id != packet->getIdentifier())
0491   {
0492     cout << __PRETTY_FUNCTION__ << "\t- Error : mismatched packet with packet ID expectation of " << m_packet_id << ", but received";
0493     packet->identify();
0494     assert(m_packet_id == packet->getIdentifier());
0495     return 0;
0496   }
0497 
0498   assert(m_packetTimer);
0499   if ((m_verbosity == 1 and (call_count % 1000) == 0) or (m_verbosity > 1))
0500   {
0501     cout << __PRETTY_FUNCTION__ << "\t- : received packet ";
0502     packet->identify();
0503 
0504     m_packetTimer->print_stat();
0505   }
0506   m_packetTimer->restart();
0507 
0508   // //remove after testing
0509   // ;
0510   // cout <<"packet->lValue(0, N_TAGGER) = "<<packet->lValue(0, "N_TAGGER")<<endl;
0511   // cout <<"packet->lValue(0, NR_WF) = "<<packet->iValue(0, "NR_WF")<<endl;
0512 
0513   Fun4AllHistoManager* hm = QAHistManagerDef::getHistoManager();
0514   assert(hm);
0515   assert(m_hNorm);
0516   m_hNorm->Fill("Packet", 1);
0517 
0518   int data_length = packet->getDataLength();  // 32bit length
0519   assert(h_PacketLength);
0520   h_PacketLength->Fill(data_length);
0521 
0522   int data_padding = packet->getPadding();  // 32bit padding
0523   assert(h_PacketLength_Padding);
0524   h_PacketLength_Padding->Fill(data_padding);
0525   if (data_padding != 0)
0526   {
0527     cout << __PRETTY_FUNCTION__ << "\t- : Warning : suspecious padding "
0528          << data_padding << "\t- in packet " << m_packet_id <<":" << endl;
0529     packet->identify();
0530     // packet->dump();
0531   }
0532 
0533   size_t dma_words_buffer = static_cast<size_t>(data_length) * 2 / DAM_DMA_WORD_LENGTH + 1;
0534   vector<dma_word> buffer(dma_words_buffer);
0535 
0536   int l2 = 0;
0537   packet->fillIntArray(reinterpret_cast<int*>(buffer.data()), data_length + DAM_DMA_WORD_LENGTH / 2, &l2, "DATA");
0538   
0539   if (data_padding != 0)
0540   {
0541     cout << __PRETTY_FUNCTION__ << "\t- :  data_length = " << data_length
0542          << "\t- data_padding = " << data_padding <<"\t l2 = "<<l2 << "\t- in packet " << m_packet_id <<":" << endl;
0543   }
0544 
0545   assert(l2 <= data_length);
0546 
0547   if(l2 < data_padding)
0548   {
0549     cout << __PRETTY_FUNCTION__ << "\t- : Error : l2 from fillIntArray() is smaller than padding suggesting an invalid data: " << l2
0550          << "\t- in packet " << m_packet_id << ". Data length: " << data_length
0551          << ", data padding: " << data_padding <<". Ignore this packet: "<< endl;
0552     packet->identify();
0553     return Fun4AllReturnCodes::DISCARDEVENT;
0554   }
0555   l2 -= data_padding;
0556 
0557   assert(l2 >= 0);
0558 
0559   size_t dma_words = static_cast<size_t>(l2) * 2 / DAM_DMA_WORD_LENGTH;
0560   size_t dma_residual = (static_cast<size_t>(l2) * 2) % DAM_DMA_WORD_LENGTH;
0561   assert(dma_words <= buffer.size());
0562   assert(h_PacketLength_Residual);
0563   h_PacketLength_Residual->Fill(dma_residual);
0564   if (dma_residual > 0)
0565   {
0566     cout << __PRETTY_FUNCTION__ << "\t- : Warning : mismatch of RCDAQ data to DMA transfer. Dropping mismatched data: "
0567          << dma_residual << "\t- in packet " << m_packet_id << ". Dropping residual data : " << endl;
0568 
0569     assert(dma_words + 1 < buffer.size());
0570     const dma_word& last_dma_word_data = buffer[dma_words + 1];
0571     const uint16_t* last_dma_word = reinterpret_cast<const uint16_t*>(&last_dma_word_data);
0572 
0573     for (size_t i = 0; i < dma_residual; ++i)
0574     {
0575       cout << "\t- 0x" << hex << last_dma_word[i] << dec;
0576     }
0577     cout << endl;
0578   }
0579 
0580   if (m_verbosity > 1)
0581   {
0582     cout << __PRETTY_FUNCTION__ << "\t- : packet" << m_packet_id << endl
0583          << "\t-   data_length = " << data_length << endl
0584          << "\t-   data_padding = " << data_padding << endl
0585          << "\t-   dma_words_buffer = " << dma_words_buffer << endl
0586          << "\t-   l2 = " << l2 << endl
0587          << "\t-   dma_words = " << dma_words << endl;
0588   }
0589 
0590   // demultiplexer
0591   for (size_t index = 0; index < dma_words; ++index)
0592   {
0593     const dma_word& dma_word_data = buffer[index];
0594 
0595     if (m_verbosity > 2)
0596     {
0597       cout << __PRETTY_FUNCTION__ << "\t- : processing DMA word "
0598            << index << "/" << dma_words << "\t- with header 0x"
0599            << hex << dma_word_data.dma_header << dec << endl;
0600     }
0601 
0602     if ((dma_word_data.dma_header & 0xFF00U) == FEE_MAGIC_KEY)
0603     {
0604       unsigned int fee_id = dma_word_data.dma_header & 0xffU;
0605 
0606       if (fee_id < MAX_FEECOUNT)
0607       {
0608         for (const uint16_t& i : dma_word_data.data)
0609         {
0610           m_feeData[fee_id].push_back(i);
0611         }
0612         m_hNorm->Fill("DMA_WORD_FEE", 1);
0613 
0614         // immediate fee buffer processing to reduce memory consuption
0615         process_fee_data(fee_id);
0616       }
0617       else
0618       {
0619         cout << __PRETTY_FUNCTION__ << "\t- : Error : Invalid FEE ID " << fee_id << "\t- at position " << index << endl;
0620         index += DAM_DMA_WORD_LENGTH - 1;
0621         m_hNorm->Fill("DMA_WORD_FEE_INVALID", 1);
0622       }
0623     }
0624 
0625     else if ((dma_word_data.dma_header & 0xFF00U) == GTM_MAGIC_KEY)
0626     {
0627       decode_gtm_data(dma_word_data);
0628       m_hNorm->Fill("DMA_WORD_GTM", 1);
0629     }
0630     else
0631     {
0632       cout << __PRETTY_FUNCTION__ << "\t- : Error : Unknown data type at position " << index << ": "
0633            << hex << buffer[index].dma_header << dec << endl;
0634       // not FEE data, e.g. GTM data or other stream, to be decoded
0635       m_hNorm->Fill("DMA_WORD_INVALID", 1);
0636     }
0637   }
0638 
0639   // sanity check for the timeframe size
0640   for (auto& timeframe : m_timeFrameMap)
0641   {
0642     if (timeframe.second.size() > kMaxRawHitLimit)
0643     {
0644       cout << __PRETTY_FUNCTION__ << "\t- : Warning : impossible amount of hits in the same timeframe at BCO "
0645            << timeframe.first << "\t- : " << timeframe.second.size() << ", limit is " << kMaxRawHitLimit
0646            << ". Dropping this time frame!"
0647            << endl;
0648       m_hNorm->Fill("TimeFrameSizeLimitError", 1);
0649 
0650       while (!timeframe.second.empty())
0651       {
0652         delete timeframe.second.back();
0653         timeframe.second.pop_back();
0654       }
0655     }
0656   }
0657 
0658   m_packetTimer->stop();
0659   assert(h_ProcessPacket_Time);
0660   h_ProcessPacket_Time->Fill(call_count, m_packetTimer->elapsed());
0661 
0662   return Fun4AllReturnCodes::EVENT_OK;
0663 }
0664 
0665 int TpcTimeFrameBuilder::process_fee_data(unsigned int fee)
0666 {
0667   assert(m_hFEEDataStream);
0668 
0669   if (m_verbosity > 2)
0670   {
0671     cout << __PRETTY_FUNCTION__ << "\t- : processing FEE " << fee << "\t- with " << m_feeData[fee].size() << "\t- words" << endl;
0672   }
0673 
0674   assert(fee < m_feeData.size());
0675   std::deque<uint16_t>& data_buffer = m_feeData[fee];
0676 
0677   while (HEADER_LENGTH <= data_buffer.size())
0678   {
0679     // packet loop
0680 
0681     bool is_digital_current = false;
0682     // test if digital current packet
0683     if (data_buffer[3] == FEE_PACKET_MAGIC_KEY_3_DC)
0684     {
0685       if (m_verbosity > 2)
0686       {
0687         cout << __PRETTY_FUNCTION__ 
0688         << "\t- : processing FEE " << fee 
0689         << "\t- with digital packet" << endl;
0690       }
0691       
0692       m_hFEEDataStream->Fill(fee, "WordDigitalCurrentKeyWord", 1);
0693       is_digital_current = true;
0694     } //     if (data_buffer[3] == FEE_PACKET_MAGIC_KEY_3)
0695     else
0696     {
0697 
0698       if (data_buffer[1] != FEE_PACKET_MAGIC_KEY_1)
0699       {
0700         if (m_verbosity > 1)
0701         {
0702           cout << __PRETTY_FUNCTION__ << "\t- : Error : Invalid FEE magic key at position 1 0x" << hex << data_buffer[1] << dec << endl;
0703         }
0704         m_hFEEDataStream->Fill(fee, "WordSkipped", 1);
0705         data_buffer.pop_front();
0706         continue;
0707       }
0708       assert(data_buffer[1] == FEE_PACKET_MAGIC_KEY_1);
0709 
0710       if (data_buffer[2] != FEE_PACKET_MAGIC_KEY_2)
0711       {
0712         if (m_verbosity > 1)
0713         {
0714           cout << __PRETTY_FUNCTION__ << "\t- : Error : Invalid FEE magic key at position 2 0x" << hex << data_buffer[2] << dec << endl;
0715         }
0716         m_hFEEDataStream->Fill(fee, "WordSkipped", 1);
0717         data_buffer.pop_front();
0718         continue;
0719       }
0720       assert(data_buffer[2] == FEE_PACKET_MAGIC_KEY_2);
0721 
0722     }
0723 
0724     // valid packet
0725     const uint16_t & pkt_length = data_buffer[0];  // this is indeed the number of 10-bit words + 5 in this packet
0726     if (pkt_length > MAX_PACKET_LENGTH)
0727     {
0728       if (m_verbosity > 1)
0729       {
0730         cout << __PRETTY_FUNCTION__ << "\t- : Error : Invalid FEE pkt_length " << pkt_length << endl;
0731       }
0732       m_hFEEDataStream->Fill(fee, "InvalidLength", 1);
0733       data_buffer.pop_front();
0734       continue;
0735     }
0736 
0737     if (pkt_length + 1U > data_buffer.size())
0738     {
0739       if (m_verbosity > 2)
0740       {
0741         cout << __PRETTY_FUNCTION__ << "\t- : packet over buffer boundary for now, skip decoding and wait for more data: "
0742                                         " pkt_length = "
0743               << pkt_length
0744               << "\t- data_buffer.size() = " << data_buffer.size()
0745               << endl;
0746       }
0747       break;
0748     }
0749 
0750     if (is_digital_current)
0751     {
0752       process_fee_data_digital_current(fee, data_buffer);
0753     }
0754     else
0755     {
0756       process_fee_data_waveform(fee, data_buffer);
0757     }
0758     data_buffer.erase(data_buffer.begin(), data_buffer.begin() + pkt_length + 1);
0759     m_hFEEDataStream->Fill(fee, "WordValid", pkt_length + 1);
0760 
0761   }  //     while (HEADER_LENGTH < data_buffer.size())
0762 
0763   return Fun4AllReturnCodes::EVENT_OK;
0764 }
0765 
0766 void TpcTimeFrameBuilder::process_fee_data_waveform(const unsigned int & fee, std::deque<uint16_t>& data_buffer)
0767 {
0768   const uint16_t & pkt_length = data_buffer[0];
0769 
0770   fee_payload payload;
0771   // continue the decoding
0772   payload.fee_id = fee;
0773   payload.adc_length = data_buffer[0] - HEADER_LENGTH;  // this is indeed the number of 10-bit words in this packet
0774   payload.data_parity = data_buffer[4] >> 9U;
0775   payload.sampa_address = static_cast<uint16_t>(data_buffer[4] >> 5U) & 0xfU;
0776   payload.sampa_channel = data_buffer[4] & 0x1fU;
0777   payload.channel = data_buffer[4] & 0x1ffU;
0778   payload.type = static_cast<uint16_t>(data_buffer[3] >> 7U) & 0x7U;
0779   payload.user_word = data_buffer[3] & 0x7fU;
0780   payload.bx_timestamp = static_cast<uint32_t>(static_cast<uint32_t>(data_buffer[6] & 0x3ffU) << 10U) | (data_buffer[5] & 0x3ffU);
0781   payload.data_crc = data_buffer[pkt_length];
0782 
0783   if (not m_fastBCOSkip)
0784   {
0785     auto crc_parity = crc16_parity(fee, pkt_length);
0786     payload.calc_crc = crc_parity.first;
0787     payload.calc_parity = crc_parity.second;
0788 
0789     if (payload.data_crc != payload.calc_crc)
0790     {
0791       if (m_verbosity > 2)
0792       {
0793         cout << __PRETTY_FUNCTION__ << "\t- : CRC error in FEE "
0794               << fee << "\t- at position " << pkt_length - 1
0795               << ": data_crc = " << payload.data_crc
0796               << "\t- calc_crc = " << payload.calc_crc << endl;
0797       }
0798       m_hFEEDataStream->Fill(fee, "HitCRCError", 1);
0799       // continue;
0800     }
0801 
0802     if (payload.data_parity != payload.calc_parity)
0803     {
0804       if (m_verbosity > 2)
0805       {
0806         cout << __PRETTY_FUNCTION__ << "\t- : parity error in FEE "
0807               << fee << "\t- at position " << pkt_length - 1
0808               << ": data_parity = " << payload.data_parity
0809               << "\t- calc_parity = " << payload.calc_parity << endl;
0810       }
0811       m_hFEEDataStream->Fill(fee, "ParityError", 1);
0812       // continue;
0813     }
0814   }  //     if (not m_fastBCOSkip)
0815 
0816   assert(fee < m_bcoMatchingInformation_vec.size());
0817   BcoMatchingInformation& m_bcoMatchingInformation = m_bcoMatchingInformation_vec[fee];
0818   // gtm_bco matching
0819   if (payload.type == m_bcoMatchingInformation.HEARTBEAT_T)
0820   {
0821     if (m_verbosity > 1)
0822     {
0823       cout << __PRETTY_FUNCTION__
0824             << "\t- : received heartbeat packet from FEE " << fee << endl;
0825     }
0826 
0827     // if bco matching information is still not verified, drop the packet
0828     if (not m_bcoMatchingInformation.is_verified())
0829     {
0830       m_hFEEDataStream->Fill(fee, "PacketHeartBeatClockSyncUnavailable", 1);
0831 
0832       if (m_verbosity > 1)
0833       {
0834         std::cout << "TpcTimeFrameBuilder::process_fee_data - bco_matching not verified for heart beat, dropping packet" << std::endl;
0835         m_bcoMatchingInformation.print_gtm_bco_information();
0836       }
0837     }
0838     else  //       if (not m_bcoMatchingInformation.is_verified())
0839     {
0840       const optional<uint64_t> result = m_bcoMatchingInformation.find_reference_heartbeat(payload);
0841       m_hFEEDataStream->Fill(fee, "PacketHeartBeat", 1);
0842 
0843       if (result)
0844       {
0845         // assign gtm bco
0846         payload.gtm_bco = result.value();
0847         m_hFEEDataStream->Fill(fee, "PacketHeartBeatClockSyncOK", 1);
0848 
0849         assert(m_hFEESAMPAHeartBeatSync);
0850         m_hFEESAMPAHeartBeatSync->Fill(fee * MAX_SAMPA + payload.sampa_address, 1);
0851       }
0852       else
0853       {
0854         m_hFEEDataStream->Fill(fee, "PacketHeartBeatClockSyncError", 1);
0855 
0856         // skip the waverform
0857       }
0858       if (m_verbosity > 2)
0859       {
0860         m_bcoMatchingInformation.print_gtm_bco_information();
0861       }
0862     }
0863   }
0864   else if (not m_fastBCOSkip)  //     if (payload.type == m_bcoMatchingInformation.HEARTBEAT_T)
0865   {
0866     m_hFEEChannelPacketCount->Fill(fee * MAX_CHANNELS + payload.channel, 1);
0867 
0868     // if bco matching information is still not verified, drop the packet
0869     if (not m_bcoMatchingInformation.is_verified())
0870     {
0871       m_hFEEDataStream->Fill(fee, "PacketClockSyncUnavailable", 1);
0872 
0873       if (m_verbosity > 1)
0874       {
0875         std::cout << "TpcTimeFrameBuilder::process_fee_data - bco_matching not verified, dropping packet" << std::endl;
0876         m_bcoMatchingInformation.print_gtm_bco_information();
0877       }
0878     }
0879     else
0880     {
0881       const optional<uint64_t> result = m_bcoMatchingInformation.find_gtm_bco(payload.bx_timestamp);
0882 
0883       if (result)
0884       {
0885         // assign gtm bco
0886         payload.gtm_bco = result.value();
0887         m_hFEEDataStream->Fill(fee, "PacketClockSyncOK", 1);
0888       }
0889       else
0890       {
0891         if (m_verbosity > 1)
0892         {
0893           std::cout << "TpcTimeFrameBuilder::process_fee_data - WARNING: bco_matching failed!" << std::endl;
0894           m_bcoMatchingInformation.print_gtm_bco_information();
0895         }
0896         m_hFEEDataStream->Fill(fee, "PacketClockSyncError", 1);
0897 
0898         // skip the waverform
0899       }
0900     }
0901   }
0902 
0903   if (m_verbosity > 2)
0904   {
0905     cout << __PRETTY_FUNCTION__ << "\t- : received data packet "
0906           << "\t- from FEE " << fee << endl
0907           << "\t- pkt_length = " << pkt_length << endl
0908           << "\t- type = " << payload.type << endl
0909           << "\t- adc_length = " << payload.adc_length << endl
0910           << "\t- sampa_address = " << payload.sampa_address << endl
0911           << "\t- sampa_channel = " << payload.sampa_channel << endl
0912           << "\t- channel = " << payload.channel << endl
0913           << "\t- bx_timestamp = 0x" << hex << payload.bx_timestamp << dec << endl
0914           << "\t- bco = 0x" << hex << payload.gtm_bco << dec << endl
0915           << "\t- data_crc = 0x" << hex << payload.data_crc << dec << endl
0916           << "\t- calc_crc = 0x" << hex << payload.calc_crc << dec << endl
0917           << "\t- data_parity = 0x" << hex << payload.data_parity << dec << endl
0918           << "\t- calc_parity = 0x" << hex << payload.calc_parity << dec << endl;
0919   }
0920 
0921   if ((not m_fastBCOSkip) and payload.gtm_bco > 0)
0922   {
0923     m_hFEEDataStream->Fill(fee, "RawHit", 1);
0924 
0925     // Format is (N sample) (start time), (1st sample)... (Nth sample)
0926     size_t pos = HEADER_LENGTH;
0927     std::deque<uint16_t>::const_iterator data_buffer_iterator = data_buffer.cbegin();
0928     std::advance(data_buffer_iterator, pos);
0929     while (pos + 2 < pkt_length)
0930     {
0931       const uint16_t& nsamp = *data_buffer_iterator;
0932       ++pos;
0933       ++data_buffer_iterator;
0934       const uint16_t& start_t = *data_buffer_iterator;
0935       ++pos;
0936       ++data_buffer_iterator;
0937       if (m_verbosity > 3)
0938       {
0939         cout << __PRETTY_FUNCTION__ << ": nsamp: " << nsamp
0940               << "+ pos: " << pos
0941               << " pkt_length: " << pkt_length << " start_t:" << start_t << endl;
0942       }
0943 
0944       if (pos + nsamp > pkt_length)
0945       {
0946         if (m_verbosity > 1)
0947         {
0948           cout << __PRETTY_FUNCTION__ << ": WARNING : nsamp: " << nsamp
0949                 << "+ pos: " << pos
0950                 << " > pkt_length: " << pkt_length << ", format error over length: " << endl;
0951 
0952           for (int print_pos = 0; print_pos <= pkt_length; ++print_pos)
0953           {
0954             cout << "\t[" << print_pos << "]=0x" << hex << data_buffer[print_pos] << dec << "(" << data_buffer[print_pos] << ")";
0955           }
0956           cout << endl;
0957         }
0958         m_hFEEDataStream->Fill(fee, "HitFormatErrorOverLength", 1);
0959 
0960         break;
0961       }
0962 
0963       const unsigned int fee_sampa_address = fee * MAX_SAMPA + payload.sampa_address;
0964       std::vector<uint16_t> adc(nsamp);
0965       for (int j = 0; j < nsamp; j++)
0966       {
0967         const uint16_t& adc_value = *data_buffer_iterator;
0968 
0969         adc[j] = adc_value;
0970         m_hFEESAMPAADC->Fill(start_t + j, fee_sampa_address, adc_value);
0971 
0972         ++pos;
0973         ++data_buffer_iterator;  //data_buffer[pos++];
0974       }
0975       payload.waveforms.emplace_back(start_t, std::move(adc));
0976 
0977       //   // an exception to deal with the last sample that is missing in the current hit format
0978       //   if (pos + 1 == pkt_length) break;
0979     }
0980 
0981     if (pos != pkt_length)
0982     {
0983       if (m_verbosity > 1)
0984       {
0985         cout << __PRETTY_FUNCTION__ << ": WARNING : residual data at the end of decoding:"
0986               << " pos: " << pos
0987               << " <pkt_length: " << pkt_length << ", format error under length" << endl;
0988       }
0989       m_hFEEDataStream->Fill(fee, "HitFormatErrorMismatchedLength", 1);
0990     }
0991 
0992     // valid packet in the buffer, create a new hit
0993     if (payload.type != m_bcoMatchingInformation.HEARTBEAT_T)
0994     {
0995       TpcRawHitv3* hit = new TpcRawHitv3();
0996       m_timeFrameMap[payload.gtm_bco].push_back(hit);
0997 
0998       hit->set_bco(payload.bx_timestamp);
0999       hit->set_packetid(m_packet_id);
1000       hit->set_fee(fee);
1001       hit->set_channel(payload.channel);
1002       hit->set_type(payload.type);
1003       // hit->set_checksum(payload.data_crc);
1004       hit->set_checksumerror(payload.data_crc != payload.calc_crc);
1005       // hit->set_parity(payload.data_parity);
1006       hit->set_parityerror(payload.data_parity != payload.calc_parity);
1007 
1008       for (pair<uint16_t, std::vector<uint16_t>>& waveform : payload.waveforms)
1009       {
1010         hit->move_adc_waveform(waveform.first, std::move(waveform.second));
1011       }
1012     }
1013   }  //     if (not m_fastBCOSkip)
1014 
1015   return  ;
1016 }
1017 
1018 void TpcTimeFrameBuilder::process_fee_data_digital_current(const unsigned int & fee, std::deque<uint16_t>& data_buffer)
1019 {
1020   if (m_verbosity > 2)
1021   {
1022     cout << __PRETTY_FUNCTION__ << "\t- : processing digital_current data " << endl;
1023   }
1024   m_hFEEDataStream->Fill(fee, "DigitalCurrent", 1);
1025   const uint16_t & pkt_length = data_buffer[0];
1026 
1027   if (pkt_length != HEADER_LENGTH + digital_current_payload::MAX_CHANNELS * 2 * 2)
1028   {
1029     if (m_verbosity > 1)
1030     {
1031       cout << __PRETTY_FUNCTION__ << "\t- : Error : Invalid FEE pkt_length " << pkt_length
1032            << ", expected at least " << HEADER_LENGTH + digital_current_payload::MAX_CHANNELS * 2 * 2
1033            << endl;
1034     }
1035     m_hFEEDataStream->Fill(fee, "DigitalCurrentFormatErrorMismatchedLength", 1);
1036     return;
1037   }
1038 
1039   digital_current_payload payload;
1040 
1041   payload.fee           = fee;
1042   payload.pkt_length    = pkt_length;
1043   payload.sampa_address = (data_buffer[4] >> 5U) & 0xfU;
1044   // payload.sampa_max_channel = data_buffer[4] & 0x1fU;
1045   payload.channel       = data_buffer[4] & 0x1ffU;
1046   // payload.type          = data_buffer[3];
1047   payload.bx_timestamp  = ((data_buffer[6] & 0x3ffU) << 10U) | (data_buffer[5] & 0x3ff);
1048 
1049   uint16_t  pos = HEADER_LENGTH;
1050   for(int ich = 0; ich<digital_current_payload::MAX_CHANNELS; ich++)
1051   {
1052     payload.current[ich] = ((unsigned int)data_buffer[pos])<<16U | ((unsigned int)data_buffer[pos+1U]);
1053     pos++; pos++;
1054     payload.nsamples[ich] = ((unsigned int)data_buffer[pos])<<16U | ((unsigned int)data_buffer[pos+1U]);
1055     pos++; pos++;
1056   }
1057 
1058   if (pos != pkt_length)
1059   {
1060     if (m_verbosity> 1)
1061     {
1062       cout << __PRETTY_FUNCTION__ << "\t- : Warning : residual data at the end of decoding:"
1063            << " pos: " << pos
1064            << " <pkt_length: " << pkt_length << ", format error under length" << endl;
1065     }
1066   }
1067 
1068   payload.data_crc = data_buffer[pkt_length];
1069   auto crc_parity = crc16_parity(fee, pkt_length);
1070   payload.calc_crc = crc_parity.first;
1071   // payload.calc_parity = crc_parity.second;
1072 
1073   if (payload.data_crc != payload.calc_crc)
1074   {
1075     if (m_verbosity > 2)
1076     {
1077       cout << __PRETTY_FUNCTION__ << "\t- : CRC error in FEE "
1078            << fee << "\t- at position " << pkt_length - 1
1079            << ": data_crc = " << payload.data_crc
1080            << "\t- calc_crc = " << payload.calc_crc << endl;
1081     }
1082     m_hFEEDataStream->Fill(fee, "DigitalCurrentCRCError", 1);
1083     // continue;
1084   }
1085 
1086   assert(fee < m_bcoMatchingInformation_vec.size());
1087   BcoMatchingInformation& m_bcoMatchingInformation = m_bcoMatchingInformation_vec[fee];
1088   std::tie(payload.gtm_bco, payload.bx_timestamp_predicted) = m_bcoMatchingInformation.find_dc_read_bco();
1089 
1090   if (m_verbosity>2)
1091   {
1092     cout << __PRETTY_FUNCTION__ << "\t- : received digital current packet "
1093          << "\t- from FEE " << fee << endl
1094          << "\t- pkt_length = " << pkt_length << endl
1095          << "\t- sampa_address = " << payload.sampa_address << endl
1096          << "\t- channel = " << payload.channel << endl
1097          << "\t- bx_timestamp = 0x" << hex << payload.bx_timestamp << dec << endl
1098          << "\t- gtm_bco = 0x" << hex << payload.gtm_bco << dec << endl
1099          << "\t- bx_timestamp_predicted = 0x" << hex << payload.bx_timestamp_predicted << dec << endl;
1100 
1101     cout << "\t- current:" ;
1102     for (int ich = 0; ich < digital_current_payload::MAX_CHANNELS; ich++)
1103     {
1104       cout << "\t[" << ich << "] = " << payload.current[ich]  ;
1105     }
1106     cout   << endl;
1107     cout << "\t- nsamples:" ;
1108     for (int ich = 0; ich < digital_current_payload::MAX_CHANNELS; ich++)
1109     {
1110       cout << "\t[" << ich << "] = " << payload.nsamples[ich] ;
1111     }
1112     cout   << endl;
1113     cout<< "\t- data_crc = 0x" << hex << payload.data_crc << dec << endl
1114        << "\t- calc_crc = 0x" << hex << payload.calc_crc << dec << endl;
1115   }
1116 
1117   if (m_digitalCurrentDebugTTree)
1118   {
1119     m_digitalCurrentDebugTTree->fill(payload);
1120   }
1121 
1122   return  ;
1123 }
1124 
1125 void TpcTimeFrameBuilder::SaveDigitalCurrentDebugTTree(const std::string &name)
1126 {
1127   if (m_verbosity >= 1)
1128   {
1129     cout << __PRETTY_FUNCTION__ << "\t- : Saving digital current debug TTree to " << name << endl;
1130   }
1131 
1132   m_digitalCurrentDebugTTree = new TpcTimeFrameBuilder::DigitalCurrentDebugTTree(name); 
1133 }
1134 
1135 TpcTimeFrameBuilder::DigitalCurrentDebugTTree::DigitalCurrentDebugTTree(const std::string &name)
1136 : m_name(name) 
1137 {  
1138   // open TFile
1139   PHTFileServer::get().open(m_name, "RECREATE");
1140 
1141   // cppcheck-suppress noCopyConstructor
1142   // cppcheck-suppress noOperatorEq
1143   m_tDigitalCurrent = new TTree("T_DigitalCurrent", "DigitalCurrent Debug TTree");
1144   assert(m_tDigitalCurrent);
1145   
1146   m_tDigitalCurrent->Branch("dc", &m_payload, 
1147     "gtm_bco/l:bx_timestamp_predicted/i:fee/s:pkt_length/s:channel/s:sampa_address/s:bx_timestamp/i:current[8]/i:nsamples[8]/i:data_crc/s:calc_crc/s");
1148 }
1149 
1150 TpcTimeFrameBuilder::DigitalCurrentDebugTTree::~DigitalCurrentDebugTTree()
1151 {  
1152   // open TFile
1153   PHTFileServer::get().write(m_name);
1154 }
1155 
1156 void TpcTimeFrameBuilder::DigitalCurrentDebugTTree::fill(const TpcTimeFrameBuilder::digital_current_payload &payload)
1157 {
1158   assert(m_tDigitalCurrent);
1159 
1160   m_payload = payload;
1161   m_tDigitalCurrent->Fill();
1162 }
1163 
1164 int TpcTimeFrameBuilder::decode_gtm_data(const TpcTimeFrameBuilder::dma_word& gtm_word)
1165 {
1166   if (m_verbosity > 2)
1167   {
1168     cout << __PRETTY_FUNCTION__ << "\t- : processing GTM data " << endl;
1169   }
1170 
1171   const uint8_t* gtm = reinterpret_cast<const uint8_t*>(&gtm_word);
1172 
1173   gtm_payload payload;
1174 
1175   payload.pkt_type = gtm[0] | static_cast<uint16_t>((unsigned short) gtm[1] << 8U);
1176   //    if (payload.pkt_type != GTM_LVL1_ACCEPT_MAGIC_KEY && payload.pkt_type != GTM_ENDAT_MAGIC_KEY)
1177   if (payload.pkt_type != GTM_LVL1_ACCEPT_MAGIC_KEY && payload.pkt_type != GTM_ENDAT_MAGIC_KEY && payload.pkt_type != GTM_MODEBIT_MAGIC_KEY)
1178   {
1179     return -1;
1180   }
1181 
1182   payload.is_lvl1 = payload.pkt_type == GTM_LVL1_ACCEPT_MAGIC_KEY;
1183   payload.is_endat = payload.pkt_type == GTM_ENDAT_MAGIC_KEY;
1184   payload.is_modebit = payload.pkt_type == GTM_MODEBIT_MAGIC_KEY;
1185 
1186   payload.bco = ((unsigned long long) gtm[2] << 0U) | ((unsigned long long) gtm[3] << 8U) | ((unsigned long long) gtm[4] << 16U) | ((unsigned long long) gtm[5] << 24U) | ((unsigned long long) gtm[6] << 32U) | (((unsigned long long) gtm[7]) << 40U);
1187   payload.lvl1_count = ((unsigned int) gtm[8] << 0U) | ((unsigned int) gtm[9] << 8U) | ((unsigned int) gtm[10] << 16U) | ((unsigned int) gtm[11] << 24U);
1188   payload.endat_count = ((unsigned int) gtm[12] << 0U) | ((unsigned int) gtm[13] << 8U) | ((unsigned int) gtm[14] << 16U) | ((unsigned int) gtm[15] << 24U);
1189   payload.last_bco = ((unsigned long long) gtm[16] << 0U) | ((unsigned long long) gtm[17] << 8U) | ((unsigned long long) gtm[18] << 16U) | ((unsigned long long) gtm[19] << 24U) | ((unsigned long long) gtm[20] << 32U) | (((unsigned long long) gtm[21]) << 40U);
1190   payload.modebits = gtm[22];
1191   payload.userbits = gtm[23];
1192 
1193   if (m_verbosity >= 2)
1194   {
1195     cout << __PRETTY_FUNCTION__ << "\t- GTM data : "
1196          << "\t- pkt_type = " << payload.pkt_type << endl
1197          << "\t- is_lvl1 = " << payload.is_lvl1 << endl
1198          << "\t- is_endat = " << payload.is_endat << endl
1199          << "\t- is_modebit = " << payload.is_modebit << endl
1200          << "\t- bco = 0x" << hex << payload.bco << dec << endl
1201          << "\t- lvl1_count = " << payload.lvl1_count << endl
1202          << "\t- endat_count = " << payload.endat_count << endl
1203          << "\t- last_bco = 0x" << hex << payload.last_bco << dec << endl
1204          << "\t- modebits =  0x" << hex << (int) payload.modebits << dec << endl
1205          << "\t- userbits =  0x" << hex << (int) payload.userbits << dec << endl;
1206   }
1207 
1208   if (payload.is_modebit)
1209   {
1210     if (payload.modebits == BcoMatchingInformation::ELINK_HEARTBEAT_T)
1211     {
1212       if (m_verbosity > 2)
1213       {
1214         cout << "\t- (Heartbeat modebit)" << endl;
1215       }
1216       assert(m_hNorm);
1217       m_hNorm->Fill("DMA_WORD_GTM_HEARTBEAT", 1);
1218     }
1219 
1220     if (payload.modebits == BcoMatchingInformation::DC_STOP_SEND_T)
1221     {
1222       if (m_verbosity > 2)
1223       {
1224         cout << "\t- (DC stop send modebit)" << endl;
1225       }
1226       assert(m_hNorm);
1227       m_hNorm->Fill("DMA_WORD_GTM_DC_STOP_SEND", 1);
1228     }
1229   }
1230 
1231   if (not(m_fastBCOSkip and (payload.is_lvl1 or payload.is_endat)))
1232   {
1233     int fee = -1;
1234     for (BcoMatchingInformation& bcoMatchingInformation : m_bcoMatchingInformation_vec)
1235     {
1236       ++fee;
1237 
1238       if (m_verbosity > 2)
1239       {
1240         cout << __PRETTY_FUNCTION__ << "\t- : processing GTM data for FEE " << fee << endl;
1241       }
1242 
1243       bcoMatchingInformation.save_gtm_bco_information(payload);
1244 
1245       if (m_verbosity > 2)
1246       {
1247         bcoMatchingInformation.print_gtm_bco_information();
1248       }
1249     }
1250   }  //   if (not m_fastBCOSkip)
1251 
1252   return 0;
1253 }
1254 
1255 uint16_t TpcTimeFrameBuilder::reverseBits(const uint16_t x) const
1256 {
1257   uint16_t n = x;
1258   n = (static_cast<uint16_t>(n >> 1U) & 0x55555555U) | (static_cast<uint16_t>(n << 1U) & 0xaaaaaaaaU);
1259   n = (static_cast<uint16_t>(n >> 2U) & 0x33333333U) | (static_cast<uint16_t>(n << 2U) & 0xccccccccU);
1260   n = (static_cast<uint16_t>(n >> 4U) & 0x0f0f0f0fU) | (static_cast<uint16_t>(n << 4U) & 0xf0f0f0f0U);
1261   n = (static_cast<uint16_t>(n >> 8U) & 0x00ff00ffU) | (static_cast<uint16_t>(n << 8U) & 0xff00ff00U);
1262   // n = (n >> 16U) & 0x0000ffffU | (n << 16U) & 0xffff0000U;
1263   return n;
1264 }
1265 
1266 std::pair<uint16_t, uint16_t> TpcTimeFrameBuilder::crc16_parity(const uint32_t fee, const uint16_t l) const
1267 {
1268   const std::deque<uint16_t>& data_buffer = m_feeData[fee];
1269   assert(l < data_buffer.size());
1270 
1271   std::deque<uint16_t>::const_iterator it = data_buffer.begin();
1272 
1273   uint16_t crc = 0xffffU;
1274   uint16_t data_parity = 0U;
1275 
1276   for (int i = 0; i < l; ++i, ++it)
1277   {
1278     const uint16_t& x = *it;
1279 
1280     crc ^= reverseBits(x);
1281     for (uint16_t k = 0; k < 16U; k++)
1282     {
1283       crc = crc & 1U ? static_cast<uint16_t>(crc >> 1U) ^ 0xa001U : crc >> 1U;
1284     }
1285 
1286     // parity on data payload only
1287     if (i >= HEADER_LENGTH)
1288     {
1289       // fast parity
1290       uint16_t word = x & uint16_t((1U << 10U) - 1U);
1291       word = word ^ static_cast<uint16_t>(word >> 1U);
1292       word = word ^ static_cast<uint16_t>(word >> 2U);
1293       word = word ^ static_cast<uint16_t>(word >> 4U);
1294       word = word ^ static_cast<uint16_t>(word >> 8U);
1295       data_parity ^= word & 1U;
1296     }
1297   }
1298   crc = reverseBits(crc);
1299   return make_pair(crc, data_parity);
1300 }
1301 
1302 namespace
1303 {
1304   // streamer for lists
1305   template <class T>
1306   std::ostream& operator<<(std::ostream& o, const std::list<T>& list)
1307   {
1308     if (list.empty())
1309     {
1310       o << "{}";
1311     }
1312     else
1313     {
1314       const bool is_hex = (o.flags() & std::ios_base::hex);
1315       o << "{ ";
1316       bool first = true;
1317       for (const auto& value : list)
1318       {
1319         if (!first)
1320         {
1321           o << ", ";
1322         }
1323         if (is_hex)
1324         {
1325           o << "0x";
1326         }
1327         o << value;
1328         first = false;
1329       }
1330       o << "\t- }";
1331     }
1332     return o;
1333   }
1334 
1335   template <class T>
1336   std::ostream& operator<<(std::ostream& o, const std::vector<T>& list)
1337   {
1338     if (list.empty())
1339     {
1340       o << "{}";
1341     }
1342     else
1343     {
1344       const bool is_hex = (o.flags() & std::ios_base::hex);
1345       o << "{ ";
1346       bool first = true;
1347       for (const auto& value : list)
1348       {
1349         if (!first)
1350         {
1351           o << ", ";
1352         }
1353         if (is_hex)
1354         {
1355           o << "0x";
1356         }
1357         o << value;
1358         first = false;
1359       }
1360       o << "\t- }";
1361     }
1362     return o;
1363   }
1364 
1365 }  // namespace
1366 
1367 TpcTimeFrameBuilder::BcoMatchingInformation::BcoMatchingInformation(const std::string& name)
1368   : m_name(name)
1369 {
1370   Fun4AllHistoManager* hm = QAHistManagerDef::getHistoManager();
1371   assert(hm);
1372 
1373   // cppcheck-suppress noCopyConstructor
1374   // cppcheck-suppress noOperatorEq
1375   m_hNorm = new TH1D(TString(m_name.c_str()) + "_Normalization",  //
1376                      TString(m_name.c_str()) + " Normalization;Items;Count",
1377                      20, .5, 20.5);
1378   int i = 1;
1379   m_hNorm->GetXaxis()->SetBinLabel(i++, "SyncGTM");
1380   m_hNorm->GetXaxis()->SetBinLabel(i++, "DC_STOP_SEND_GTM");
1381   m_hNorm->GetXaxis()->SetBinLabel(i++, "HeartBeatGTM");
1382   m_hNorm->GetXaxis()->SetBinLabel(i++, "HeartBeatFEE");
1383   m_hNorm->GetXaxis()->SetBinLabel(i++, "HeartBeatFEEMatchedReference");
1384   m_hNorm->GetXaxis()->SetBinLabel(i++, "HeartBeatFEEMatchedNew");
1385   m_hNorm->GetXaxis()->SetBinLabel(i++, "HeartBeatFEEUnMatched");
1386   m_hNorm->GetXaxis()->SetBinLabel(i++, "TriggerGTM");
1387   m_hNorm->GetXaxis()->SetBinLabel(i++, "EnDATGTM");
1388   m_hNorm->GetXaxis()->SetBinLabel(i++, "UnmatchedEnDATGTM");
1389   m_hNorm->GetXaxis()->SetBinLabel(i++, "FindGTMBCO");
1390   m_hNorm->GetXaxis()->SetBinLabel(i++, "FindGTMBCOMatchedExisting");
1391   m_hNorm->GetXaxis()->SetBinLabel(i++, "FindGTMBCOMatchedNew");
1392   m_hNorm->GetXaxis()->SetBinLabel(i++, "FindGTMBCOMatchedFailed");
1393 
1394   assert(i <= 20);
1395   m_hNorm->GetXaxis()->LabelsOption("v");
1396   hm->registerHisto(m_hNorm);
1397 
1398   m_hFEEClockAdjustment_MatchedReference = new TH1I(TString(m_name.c_str()) + "_FEEClockAdjustment_MatchedReference",  //
1399                                                     TString(m_name.c_str()) +
1400                                                         " FEEClockAdjustment for Matched Reference;Clock Adjustment [FEE Clock Cycle];Count",
1401                                                     512, -256 - .5, +256 - .5);
1402   hm->registerHisto(m_hFEEClockAdjustment_MatchedReference);
1403   m_hFEEClockAdjustment_MatchedNew = new TH1I(TString(m_name.c_str()) + "_FEEClockAdjustment_MatchedNew",  //
1404                                               TString(m_name.c_str()) +
1405                                                   " FEEClockAdjustment for Matched New;Clock Adjustment [FEE Clock Cycle];Count",
1406                                               512, -256 - .5, +256 - .5);
1407   hm->registerHisto(m_hFEEClockAdjustment_MatchedNew);
1408 
1409   m_hFEEClockAdjustment_Unmatched = new TH1I(TString(m_name.c_str()) + "_FEEClockAdjustment_Unmatched",  //
1410                                              TString(m_name.c_str()) +
1411                                                  " FEEClock Diff for unmatched;Clock Adjustment [FEE Clock Cycle];Count",
1412                                              512,
1413                                              -(1UL << m_FEE_CLOCK_BITS) - .5,
1414                                              +(1UL << m_FEE_CLOCK_BITS) - .5);
1415   hm->registerHisto(m_hFEEClockAdjustment_Unmatched);
1416 
1417   m_hGTMNewEventSpacing = new TH1I(TString(m_name.c_str()) +
1418                                        "_GTM_NewEventSpacing",  //
1419                                    TString(m_name.c_str()) +
1420                                        " Spacing between two events;Clock Diff [RHIC Clock Cycle];Count",
1421                                    1024, -.5, +1024 - .5);
1422   hm->registerHisto(m_hGTMNewEventSpacing);
1423 
1424   m_hFindGTMBCO_MatchedExisting_BCODiff = new TH1I(TString(m_name.c_str()) + "_FindGTMBCO_MatchedExisting_BCODiff",  //
1425                                                    TString(m_name.c_str()) +
1426                                                        " find_gtm_bco matched to existing event clock diff;Clock Difference [FEE Clock Cycle];Count",
1427                                                    512, -256 - .5, +256 - .5);
1428   hm->registerHisto(m_hFindGTMBCO_MatchedExisting_BCODiff);
1429   m_hFindGTMBCO_MatchedNew_BCODiff = new TH1I(TString(m_name.c_str()) + "_FindGTMBCO_MatchedNew_BCODiff",  //
1430                                               TString(m_name.c_str()) +
1431                                                   " find_gtm_bco matched to new event clock diff;Clock Difference [FEE Clock Cycle];Count",
1432                                               512, -256 - .5, +256 - .5);
1433   hm->registerHisto(m_hFindGTMBCO_MatchedNew_BCODiff);
1434 }
1435 
1436 //! whether reference bco has moved pass the given gtm_bco
1437 bool TpcTimeFrameBuilder::BcoMatchingInformation::isMoreDataRequired(const uint64_t& gtm_bco) const
1438 {
1439   const uint64_t bco_correction = get_gtm_rollover_correction(gtm_bco);
1440 
1441   if (m_verbosity>=2)
1442   {
1443     std::cout << "TpcTimeFrameBuilder[" << m_name << "]::BcoMatchingInformation::isMoreDataRequired entry"
1444               << " at gtm_bco = 0x" << hex << gtm_bco << dec
1445               << " bco_correction = 0x" << hex << bco_correction << dec
1446               << std::endl;
1447   }
1448 
1449   if (m_bco_reference)
1450   {
1451     if (m_bco_reference.value().first > bco_correction + m_max_fee_sync_time)
1452     {
1453       if (m_verbosity >= 2)
1454       {
1455         std::cout << "TpcTimeFrameBuilder[" << m_name << "]::BcoMatchingInformation::isMoreDataRequired"
1456                   << " at gtm_bco = 0x" << hex << gtm_bco << dec
1457                   << ". m_bco_reference.value().first = 0x" << hex << m_bco_reference.value().first << dec
1458                   << " bco_correction = 0x" << hex << bco_correction << dec
1459                   << ". satisified m_max_fee_sync_time = " << m_max_fee_sync_time
1460                   << std::endl;
1461       }
1462 
1463       return false;
1464     }
1465   }
1466 
1467   if (m_bco_reference_candidate_list.size() > 0)
1468   {
1469     if (m_bco_reference_candidate_list.back().first > bco_correction + m_max_fee_sync_time)
1470     {
1471       if (m_verbosity >= 2)
1472       {
1473         std::cout << "TpcTimeFrameBuilder[" << m_name << "]::BcoMatchingInformation::isMoreDataRequired"
1474                   << "at gtm_bco = 0x" << hex << gtm_bco << dec
1475                   << ". m_bco_reference_candidate_list.back().first = 0x" << hex << m_bco_reference_candidate_list.back().first << dec
1476                   << " bco_correction = 0x" << hex << bco_correction << dec
1477                   << ". satisified m_max_fee_sync_time = " << m_max_fee_sync_time
1478                   << std::endl;
1479       }
1480 
1481       return false;
1482     }
1483     else
1484     {
1485       if (m_verbosity > 4)
1486       {
1487         std::cout << "TpcTimeFrameBuilder[" << m_name << "]::BcoMatchingInformation::isMoreDataRequired"
1488                   << "at gtm_bco = 0x" << hex << gtm_bco << dec
1489                   << ". m_bco_reference_candidate_list.back().first = 0x" << hex << m_bco_reference_candidate_list.back().first << dec
1490                   << " bco_correction = 0x" << hex << bco_correction << dec
1491                   << ". not yet satisified m_max_fee_sync_time = " << m_max_fee_sync_time
1492                   << std::endl;
1493       }
1494     }
1495   }
1496 
1497   if (m_verbosity > 3)
1498   {
1499     std::cout << "TpcTimeFrameBuilder[" << m_name << "]::BcoMatchingInformation::isMoreDataRequired"
1500               << "at gtm_bco = 0x" << hex << gtm_bco << dec
1501               << " bco_correction = 0x" << hex << bco_correction << dec << ": more data required"
1502               << " as their is NO m_bco_reference nor m_bco_reference_candidate_list"
1503               << std::endl;
1504 
1505     std::cout << "  m_gtm_bco_trigger_map:" << std::endl;
1506     for (const auto& trig : m_gtm_bco_trigger_map)
1507     {
1508       std::cout << " - 0x" << hex << trig.first << dec << "(Diff = " << trig.first - bco_correction << ") " << std::endl;
1509     }
1510 
1511     std::cout << "  m_bco_matching_list:" << std::endl;
1512     for (const auto& trig : m_bco_matching_list)
1513     {
1514       std::cout << " - 0x" << hex << trig.second << dec << "(Diff = " << trig.second - bco_correction << ") " << std::endl;
1515     }
1516   }
1517   return true;
1518 }
1519 
1520 //___________________________________________________
1521 std::optional<uint32_t> TpcTimeFrameBuilder::BcoMatchingInformation::get_predicted_fee_bco(uint64_t gtm_bco) const
1522 {
1523   // check proper initialization
1524   if (!is_verified())
1525   {
1526     return std::nullopt;
1527   }
1528 
1529   // get gtm bco difference with proper rollover accounting
1530   const int64_t gtm_bco_difference = int64_t(gtm_bco) - int64_t(m_bco_reference.value().first);
1531 
1532   assert(m_multiplier>0);
1533 
1534   // convert to fee bco, and truncate to 20 bits
1535   const int64_t fee_bco_predicted = int64_t(m_bco_reference.value().second) + int64_t(m_multiplier * gtm_bco_difference);
1536   return uint32_t(static_cast<uint64_t>(fee_bco_predicted) & 0xFFFFFU);
1537 }
1538 
1539 //___________________________________________________
1540 void TpcTimeFrameBuilder::BcoMatchingInformation::print_gtm_bco_information() const
1541 {
1542   if (!m_gtm_bco_trig_list.empty())
1543   {
1544     std::cout
1545         << "TpcTimeFrameBuilder[" << m_name << "]::BcoMatchingInformation::print_gtm_bco_information -"
1546         << "\t- m_gtm_bco_trig_list: " << std::hex << m_gtm_bco_trig_list << std::dec
1547         << std::endl;
1548 
1549     // also print predicted fee bco
1550     if (is_verified())
1551     {
1552       std::list<uint32_t> fee_bco_predicted_list;
1553       std::transform(
1554           m_gtm_bco_trig_list.begin(),
1555           m_gtm_bco_trig_list.end(),
1556           std::back_inserter(fee_bco_predicted_list),
1557           [this](const uint64_t& gtm_bco) { return get_predicted_fee_bco(gtm_bco).value(); });
1558 
1559       std::cout
1560           << "TpcTimeFrameBuilder[" << m_name << "]::BcoMatchingInformation::print_gtm_bco_information -"
1561           << "\t- m_gtm_bco_trig_list fee predicted: " << std::hex << fee_bco_predicted_list << std::dec
1562           << std::endl;
1563     }
1564   }
1565 
1566   std::cout <<"\t m_gtm_bco_dc_read = " << std::hex 
1567     << m_gtm_bco_dc_read.first <<" -> 0x" << m_gtm_bco_dc_read.second 
1568     << std::dec << std::endl;
1569 }
1570 
1571 uint64_t TpcTimeFrameBuilder::BcoMatchingInformation::
1572     get_gtm_rollover_correction(const uint64_t& gtm_bco) const
1573 {
1574   // start with 40bit clock, enforced
1575   uint64_t gtm_bco_corrected = gtm_bco & ((uint64_t(1) << m_GTM_CLOCK_BITS) - 1);
1576 
1577   if (not m_bco_reference)
1578   {
1579     return gtm_bco_corrected;
1580   }
1581 
1582   // get the last GTM clock roll over
1583   const uint64_t& last_bco = m_bco_reference.value().first;
1584   const uint64_t last_bco_rollover = last_bco &
1585                                      (std::numeric_limits<uint64_t>::max() << m_GTM_CLOCK_BITS);
1586 
1587   // use the roll over of the last GTM clock reading
1588   gtm_bco_corrected += last_bco_rollover;
1589 
1590   // check if the rollover has advanced
1591   if (gtm_bco_corrected + (uint64_t(1) << (m_GTM_CLOCK_BITS - 1)) < last_bco)
1592   {
1593     gtm_bco_corrected += uint64_t(1) << m_GTM_CLOCK_BITS;
1594   }
1595 
1596   return gtm_bco_corrected;
1597 }
1598 
1599 //___________________________________________________
1600 void TpcTimeFrameBuilder::BcoMatchingInformation::save_gtm_bco_information(const TpcTimeFrameBuilder::gtm_payload& gtm_tagger)
1601 {
1602   // append gtm_bco from taggers in this event to packet-specific list of available lv1_bco
1603 
1604   // save level1 trigger bco
1605   const bool& is_lvl1 = gtm_tagger.is_lvl1;
1606   const bool& is_endat = gtm_tagger.is_endat;
1607   const bool& is_modebit = gtm_tagger.is_modebit;
1608   const uint64_t gtm_bco = get_gtm_rollover_correction(gtm_tagger.bco);
1609 
1610   if (is_lvl1)
1611   {
1612     assert(m_hNorm);
1613     m_hNorm->Fill("TriggerGTM", 1);
1614 
1615     assert(m_hGTMNewEventSpacing);
1616     if (not m_gtm_bco_trig_list.empty())
1617     {
1618       m_hGTMNewEventSpacing->Fill(gtm_bco - m_gtm_bco_trig_list.back());
1619     }
1620     m_gtm_bco_trig_list.push_back(gtm_bco);
1621   }
1622 
1623   // also save ENDDAT bco
1624   else if (is_endat)
1625   {
1626     assert(m_hNorm);
1627     m_hNorm->Fill("EnDATGTM", 1);
1628 
1629     // add to list if difference to last entry is big enough
1630     if (m_gtm_bco_trig_list.empty() || (gtm_bco - m_gtm_bco_trig_list.back()) > m_max_lv1_endat_bco_diff)
1631     {
1632       assert(m_hNorm);
1633       m_hNorm->Fill("UnmatchedEnDATGTM", 1);
1634 
1635       if (not m_gtm_bco_trig_list.empty())
1636       {
1637         assert(m_hGTMNewEventSpacing);
1638         m_hGTMNewEventSpacing->Fill(gtm_bco - m_gtm_bco_trig_list.back());
1639       }
1640       m_gtm_bco_trig_list.push_back(gtm_bco);
1641     }
1642   }
1643 
1644   // also save hearbeat bco
1645   else if (is_modebit)
1646   {
1647     // get modebits
1648     const uint64_t& modebits = gtm_tagger.modebits;
1649     if (modebits == ELINK_HEARTBEAT_T)
1650     {
1651       assert(m_hNorm);
1652       m_hNorm->Fill("HeartBeatGTM", 1);
1653 
1654       auto predicted_fee_bco = get_predicted_fee_bco(gtm_bco);
1655       if (predicted_fee_bco)
1656       {
1657         m_bco_reference_candidate_list.emplace_back(gtm_bco, predicted_fee_bco.value());
1658       }
1659       else
1660       {
1661         if (m_verbosity > 1)
1662         {
1663           std::cout << "TpcTimeFrameBuilder[" << m_name << "]::BcoMatchingInformation::save_gtm_bco_information"
1664                     << "\t- Warning: predicted_fee_bco is not available for gtm_bco = 0x" << hex << gtm_bco << dec
1665                     << ". Skipping heartbeat candidate." << std::endl;
1666         }
1667       }
1668 
1669       if (m_verbosity > 1)
1670       {
1671         std::cout << "TpcTimeFrameBuilder[" << m_name << "]::BcoMatchingInformation::save_gtm_bco_information"
1672                   << "\t- found heartbeat candidate "
1673                   << "at gtm_bco = 0x" << hex << gtm_bco << dec
1674                   << ". Current m_bco_reference_candidate_list:"
1675                   << std::endl;
1676 
1677         for (const m_gtm_fee_bco_matching_pair_t& bco : m_bco_reference_candidate_list)
1678         {
1679           std::cout << "\t- gtm_bco = 0x" << hex << bco.first << dec
1680                     << "\t- fee_bco = 0x" << hex << bco.second << dec
1681                     << std::endl;
1682         }
1683       }
1684 
1685       while (m_bco_reference_candidate_list.size() > m_max_bco_reference_candidate_list_size)
1686       {
1687         if (m_verbosity > 1)
1688         {
1689           uint64_t bco = m_bco_reference_candidate_list.begin()->first;
1690           std::cout << "TpcTimeFrameBuilder[" << m_name << "]::BcoMatchingInformation::find_reference_from_modebits"
1691                     << "Warning: m_bco_reference_candidate_list is full"
1692                     << "\t- drop unprocessed heart beat in queue "
1693                     << "at gtm_bco = 0x" << hex << bco
1694                     << dec
1695                     << ". Unprocessed heartbeats in queue with size of " << m_bco_reference_candidate_list.size()
1696                     << std::endl;
1697         }
1698 
1699         m_bco_reference_candidate_list.pop_front();
1700       }
1701 
1702     }  //     if (modebits & (1U << ELINK_HEARTBEAT_T))
1703 
1704     if (modebits == BX_COUNTER_SYNC_T)  // initiate synchronization of clock sync
1705     {
1706       assert(m_hNorm);
1707       m_hNorm->Fill("SyncGTM", 1);
1708 
1709       // get BCO and assign
1710       m_verified_from_modebits = true;
1711       m_bco_reference = make_pair(gtm_bco, 0);
1712       m_bco_reference_candidate_list.clear();
1713 
1714       if (m_verbosity)
1715       {
1716         std::cout << "TpcTimeFrameBuilder[" << m_name << "]::BcoMatchingInformation::find_reference_from_modebits"
1717                   << "\t- found reference from modebits BX_COUNTER_SYNC_T "
1718                   << "at gtm_bco = 0x" << hex << gtm_bco << dec
1719                   << std::endl;
1720       }
1721     } //     if (modebits == BX_COUNTER_SYNC_T)  // initiate synchronization of clock sync
1722 
1723     if (modebits == DC_STOP_SEND_T)
1724     {
1725       assert(m_hNorm);
1726       m_hNorm->Fill("DC_STOP_SEND_GTM", 1);
1727 
1728       // save the gtm_bco for the digital current readout
1729       m_gtm_bco_dc_read.first = gtm_bco;
1730       if (is_verified())
1731       {
1732         m_gtm_bco_dc_read.second = get_predicted_fee_bco(gtm_bco).value();
1733       }
1734       else
1735       {
1736         m_gtm_bco_dc_read.second = 0;  // not verified, so no reference clock sync available
1737       }
1738 
1739       if (m_verbosity > 2)
1740       {
1741         std::cout << "TpcTimeFrameBuilder[" << m_name << "]::BcoMatchingInformation::save_gtm_bco_information"
1742                   << "\t- found DC stop send modebit "
1743                   << "at gtm_bco = 0x" << hex << gtm_bco << dec
1744                   << std::endl;
1745       }
1746     }
1747   }
1748 }
1749 
1750 //___________________________________________________
1751 std::optional<uint64_t> TpcTimeFrameBuilder::BcoMatchingInformation::find_reference_heartbeat(const TpcTimeFrameBuilder::fee_payload& HeartBeatPacket)
1752 {
1753   assert(m_hNorm);
1754   m_hNorm->Fill("HeartBeatFEE", 1);
1755 
1756   // make sure the bco matching is properly initialized and historical valid
1757   if (!is_verified())
1758   {
1759     return std::nullopt;
1760   }
1761 
1762   assert(HeartBeatPacket.type == HEARTBEAT_T);
1763   const uint32_t& fee_bco = HeartBeatPacket.bx_timestamp;
1764 
1765   if (m_bco_reference)
1766   {
1767     const uint64_t& gtm_bco = m_bco_reference.value().first;
1768     const uint32_t& fee_bco_predicted = m_bco_reference.value().second;
1769     // check if the predicted fee bco matches the actual fee bco
1770     if (get_fee_bco_diff(fee_bco_predicted, fee_bco) < m_max_fee_bco_diff)
1771     {
1772       // assign gtm bco
1773       m_bco_reference.value().second = fee_bco;
1774 
1775       if (verbosity() > 1)
1776       {
1777         std::cout << "TpcTimeFrameBuilder[" << m_name << "]::BcoMatchingInformation::find_reference_heartbeat - found an updated reference heartbeat and updated reference clock sync: "
1778                   << std::hex
1779                   << "\t- fee_bco: 0x" << fee_bco
1780                   << "\t- predicted: 0x" << fee_bco_predicted
1781                   << "\t- gtm_bco: 0x" << gtm_bco
1782                   << std::dec
1783                   << std::endl;
1784       }
1785 
1786       assert(m_hFEEClockAdjustment_MatchedReference);
1787       m_hFEEClockAdjustment_MatchedReference->Fill(int64_t(fee_bco) - int64_t(fee_bco_predicted), 1);
1788 
1789       m_hNorm->Fill("HeartBeatFEEMatchedReference", 1);
1790 
1791       return gtm_bco;
1792     }
1793   }
1794 
1795   for (const m_gtm_fee_bco_matching_pair_t& bco : m_bco_reference_candidate_list)
1796   {
1797     const uint64_t gtm_bco = bco.first;
1798     const uint32_t fee_bco_predicted = bco.second;
1799 
1800     // check if the predicted fee bco matches the actual fee bco
1801     if (get_fee_bco_diff(fee_bco_predicted, fee_bco) < m_max_fee_bco_diff)
1802     {
1803       if (verbosity() > 1)
1804       {
1805         std::cout << "TpcTimeFrameBuilder[" << m_name << "]::BcoMatchingInformation::find_reference_heartbeat - found a new reference canidate heartbeat and replaced reference clock sync: "
1806                   << std::hex
1807                   << "\t- fee_bco: 0x" << fee_bco
1808                   << "\t- predicted: 0x" << fee_bco_predicted
1809                   << "\t- gtm_bco: 0x" << gtm_bco
1810                   << "\t- previous reference gtm_bco: 0x" << m_bco_reference.value().first
1811                   << "\t- previous reference fee_bco: 0x" << m_bco_reference.value().second
1812                   << std::dec
1813                   << std::endl;
1814       }
1815       // assign gtm bco
1816       m_bco_reference = make_pair(gtm_bco, fee_bco);
1817 
1818       if (m_verbosity > 1)
1819       {
1820         std::cout << "\t- trimming m_bco_reference_candidate_list from size " << m_bco_reference_candidate_list.size() << std::endl;
1821 
1822         for (const m_gtm_fee_bco_matching_pair_t& bco_tmp : m_bco_reference_candidate_list)
1823         {
1824           std::cout << "\t\t- gtm_bco = 0x" << hex << bco_tmp.first << dec
1825                     << "\t\t- fee_bco = 0x" << hex << bco_tmp.second << dec
1826                     << std::endl;
1827         }
1828       }
1829 
1830       // remove the older candidate from the list
1831       while (m_bco_reference_candidate_list.begin()->first != gtm_bco)
1832       {
1833         m_bco_reference_candidate_list.pop_front();
1834       }
1835       m_bco_reference_candidate_list.pop_front();
1836 
1837       if (m_verbosity > 1)
1838       {
1839         std::cout << "\t- to size " << m_bco_reference_candidate_list.size() << std::endl;
1840 
1841         for (const m_gtm_fee_bco_matching_pair_t& bco_tmp : m_bco_reference_candidate_list)
1842         {
1843           std::cout << "\t\t- gtm_bco = 0x" << hex << bco_tmp.first << dec
1844                     << "\t\t- fee_bco = 0x" << hex << bco_tmp.second << dec
1845                     << std::endl;
1846         }
1847       }
1848 
1849       assert(m_hFEEClockAdjustment_MatchedNew);
1850       m_hFEEClockAdjustment_MatchedNew->Fill(int64_t(fee_bco) - int64_t(fee_bco_predicted), 1);
1851 
1852       m_hNorm->Fill("HeartBeatFEEMatchedNew", 1);
1853       return gtm_bco;
1854     }
1855 
1856     if (verbosity() > 1)
1857     {
1858       std::cout << "TpcTimeFrameBuilder[" << m_name << "]::BcoMatchingInformation::find_reference_heartbeat - unmatched heartbeat: "
1859                 << std::hex
1860                 << "\t- fee_bco: 0x" << fee_bco
1861                 << "\t- predicted: 0x" << fee_bco_predicted
1862                 << "\t- gtm_bco: 0x" << gtm_bco
1863                 << std::dec
1864                 << std::endl;
1865     }
1866 
1867     assert(m_hFEEClockAdjustment_Unmatched);
1868     m_hFEEClockAdjustment_Unmatched->Fill(int64_t(fee_bco) - int64_t(fee_bco_predicted), 1);
1869   }  //   for (const auto& bco : m_bco_reference_candidate_list)
1870 
1871   if (verbosity() > 1)
1872   {
1873     std::cout << "TpcTimeFrameBuilder[" << m_name << "]::BcoMatchingInformation::find_reference_heartbeat - WARNING: failed match for fee_bco = 0x" << hex << fee_bco << dec << std::endl;
1874   }
1875   m_hNorm->Fill("HeartBeatFEEUnMatched", 1);
1876   return std::nullopt;
1877 }
1878 
1879 //___________________________________________________
1880 std::optional<uint64_t> TpcTimeFrameBuilder::BcoMatchingInformation::find_gtm_bco(uint32_t fee_bco)
1881 {
1882   if (verbosity() > 5)
1883   {
1884     std::cout << "TpcTimeFrameBuilder[" << m_name << "]::BcoMatchingInformation::find_gtm_bco - entry: "
1885               << std::hex
1886               << "\t- fee_bco: 0x" << fee_bco
1887               << std::dec
1888               << "\t- is_verified(): " << (is_verified() ? "true" : "false")
1889               << std::endl;
1890   }
1891 
1892   // make sure the bco matching is properly initialized
1893   if (!is_verified())
1894   {
1895     return std::nullopt;
1896   }
1897 
1898   assert(m_hNorm);
1899   m_hNorm->Fill("FindGTMBCO", 1);
1900 
1901   // find matching gtm bco in map
1902   const auto bco_matching_iter = std::find_if(
1903       m_bco_matching_list.begin(),
1904       m_bco_matching_list.end(),
1905       [fee_bco](const m_fee_gtm_bco_matching_pair_t& pair) { return get_fee_bco_diff(pair.first, fee_bco) < m_max_fee_bco_diff; });
1906 
1907   if (bco_matching_iter != m_bco_matching_list.end())
1908   {
1909     m_hNorm->Fill("FindGTMBCOMatchedExisting", 1);
1910     assert(m_hFindGTMBCO_MatchedExisting_BCODiff);
1911     m_hFindGTMBCO_MatchedExisting_BCODiff->Fill(int64_t(fee_bco) - int64_t(bco_matching_iter->first));
1912 
1913     if (verbosity() > 3)
1914     {
1915       std::cout << "TpcTimeFrameBuilder[" << m_name << "]::BcoMatchingInformation::find_gtm_bco - found existing FEE BCO: "
1916                 << std::hex
1917                 << "\t- fee_bco: 0x" << fee_bco
1918                 << "\t- predicted: 0x" << bco_matching_iter->first
1919                 << "\t- gtm_bco: 0x" << bco_matching_iter->second
1920                 << std::dec
1921                 << std::endl;
1922     }
1923 
1924     return bco_matching_iter->second;
1925   }
1926   else
1927   {
1928     // find element for which predicted fee_bco matches fee_bco, within limit
1929     const auto iter = std::find_if(
1930         m_gtm_bco_trig_list.begin(),
1931         m_gtm_bco_trig_list.end(),
1932         [this, fee_bco](const uint64_t& gtm_bco) { return get_fee_bco_diff(get_predicted_fee_bco(gtm_bco).value(), fee_bco) < m_max_gtm_bco_diff; });
1933 
1934     // check
1935     if (iter != m_gtm_bco_trig_list.end())
1936     {
1937       const uint64_t gtm_bco = *iter;
1938 
1939       m_hNorm->Fill("FindGTMBCOMatchedNew", 1);
1940       assert(m_hFindGTMBCO_MatchedNew_BCODiff);
1941       m_hFindGTMBCO_MatchedNew_BCODiff->Fill(int64_t(fee_bco) - int64_t(gtm_bco));
1942 
1943       if (verbosity() > 2)
1944       {
1945         const uint32_t fee_bco_predicted = get_predicted_fee_bco(gtm_bco).value();
1946         const uint32_t fee_bco_diff = get_bco_diff(fee_bco_predicted, fee_bco);
1947 
1948         std::cout << "TpcTimeFrameBuilder[" << m_name << "]::BcoMatchingInformation::find_gtm_bco - new GL1 match: "
1949                   << std::hex
1950                   << "\t- fee_bco: 0x" << fee_bco
1951                   << "\t- predicted: 0x" << fee_bco_predicted
1952                   << "\t- gtm_bco: 0x" << gtm_bco
1953                   << std::dec
1954                   << "\t- difference: " << fee_bco_diff
1955                   << std::endl;
1956       }
1957 
1958       // save fee_bco and gtm_bco matching in map
1959       m_bco_matching_list.emplace_back(fee_bco, gtm_bco);
1960 
1961       // remove gtm bco from runing list
1962       m_gtm_bco_trig_list.erase(iter);
1963 
1964       // // update clock adjustment not applied for non HEARTBEAT_T
1965       // update_multiplier_adjustment(gtm_bco, fee_bco);
1966 
1967       return gtm_bco;
1968     }
1969     else
1970     {
1971       m_hNorm->Fill("FindGTMBCOMatchedFailed", 1);
1972 
1973       bool new_orphan = m_orphans.insert(fee_bco).second;
1974 
1975       if ((new_orphan and verbosity()) or (verbosity() > 3))
1976       {
1977         // find element for which predicted fee_bco is the closest to request
1978         const auto iter2 = std::min_element(
1979             m_gtm_bco_trig_list.begin(),
1980             m_gtm_bco_trig_list.end(),
1981             [this, fee_bco](const uint64_t& first, const uint64_t& second) { return get_bco_diff(get_predicted_fee_bco(first).value(), fee_bco) < get_bco_diff(get_predicted_fee_bco(second).value(), fee_bco); });
1982 
1983         const int fee_bco_diff = (iter2 != m_gtm_bco_trig_list.end()) ? get_bco_diff(get_predicted_fee_bco(*iter2).value(), fee_bco) : -1;
1984 
1985         if (m_verbosity >=2)
1986         {
1987           std::cout << "TpcTimeFrameBuilder[" << m_name << "]::BcoMatchingInformation::find_gtm_bco - match failed!"
1988                     << std::hex
1989                     << "\t- fee_bco: 0x" << fee_bco
1990                     << std::dec
1991                     << "\t- gtm_bco: 0x" << *iter2
1992                     << "\t- difference: " << fee_bco_diff
1993                     << std::endl;
1994         }
1995       }  //       if ((new_orphan and verbosity()) or (verbosity()>3))
1996 
1997       if (verbosity() > 3)
1998       {
1999         std::cout << "\t- m_gtm_bco_trig_list : " << std::endl;
2000         for (const auto& gtm_bco : m_gtm_bco_trig_list)
2001         {
2002           std::cout << "\t\t- 0x" << hex << gtm_bco << " -> 0x" << get_predicted_fee_bco(gtm_bco).value() << dec << std::endl;
2003         }
2004 
2005         std::cout << "\t- m_bco_matching_list : " << std::endl;
2006         for (const auto& iter_m_bco_matching_list : m_bco_matching_list)
2007         {
2008           std::cout << "\t\t- 0x" << hex << iter_m_bco_matching_list.first << " -> 0x" << iter_m_bco_matching_list.second << dec << std::endl;
2009         }
2010 
2011       }  //       if (verbosity()>3)
2012 
2013       return std::nullopt;
2014     }  // else
2015   }
2016 
2017   // never reached
2018   return std::nullopt;
2019 }
2020 
2021 //___________________________________________________
2022 void TpcTimeFrameBuilder::BcoMatchingInformation::cleanup()
2023 {
2024   // remove old gtm_bco and matching
2025   while (m_gtm_bco_trig_list.size() > m_max_matching_data_size)
2026   {
2027     m_gtm_bco_trig_list.pop_front();
2028   }
2029   while (m_bco_matching_list.size() > m_max_matching_data_size)
2030   {
2031     m_bco_matching_list.pop_front();
2032   }
2033 
2034   // clear orphans
2035   m_orphans.clear();
2036 }
2037 
2038 //___________________________________________________
2039 void TpcTimeFrameBuilder::BcoMatchingInformation::cleanup(uint64_t ref_bco)
2040 {
2041   // erase all elements from bco_list that are less than or equal to ref_bco
2042   m_gtm_bco_trig_list.erase(std::remove_if(m_gtm_bco_trig_list.begin(), m_gtm_bco_trig_list.end(),
2043                                            [ref_bco](const uint64_t& bco) { return bco <= ref_bco; }),
2044                             m_gtm_bco_trig_list.end());
2045 
2046   // erase all elements from bco_list that are less than or equal to ref_bco
2047   m_bco_matching_list.erase(std::remove_if(m_bco_matching_list.begin(), m_bco_matching_list.end(),
2048                                            [ref_bco](const m_fee_gtm_bco_matching_pair_t& pair) {
2049                                              return pair.second <= ref_bco;
2050                                            }),
2051                             m_bco_matching_list.end());
2052 
2053   // clear orphans
2054   m_orphans.clear();
2055 }