Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2026-07-16 08:13:56

0001 #include "TpcTimeFrameBuilderRun3.h"
0002 
0003 #include <Event/oncsSubConstants.h>
0004 #include <Event/packet.h>
0005 
0006 #include <qautils/QAHistManagerDef.h>
0007 
0008 #include <ffarawobjects/TpcRawHitv2.h>
0009 #include <ffarawobjects/TpcRawHitv3.h>
0010 
0011 #include <cdbobjects/CDBTTree.h>
0012 #include <ffamodules/CDBInterface.h>
0013 
0014 #include <phool/PHTimer.h>  // for PHTimer
0015 
0016 #include <fun4all/Fun4AllHistoManager.h>
0017 #include <fun4all/Fun4AllReturnCodes.h>
0018 #include <fun4all/PHTFileServer.h>
0019 
0020 #include <TAxis.h>
0021 #include <TH1.h>
0022 #include <TH2.h>
0023 #include <TNamed.h>
0024 #include <TTree.h>
0025 #include <TVector3.h>
0026 
0027 #include <cassert>
0028 #include <cstdint>
0029 #include <limits>
0030 #include <memory>
0031 #include <string>
0032 #include <tuple>  // For std::tie
0033 
0034 TpcTimeFrameBuilderRun3::TpcTimeFrameBuilderRun3(const int packet_id)
0035   : m_packet_id(packet_id)
0036   , m_HistoPrefix("TpcTimeFrameBuilderRun3_Packet" + std::to_string(packet_id))
0037   , m_bxCounterSyncCDBTTreeName(m_HistoPrefix + "_BXCounterSyncCDBTTree.root")
0038 {
0039   for (int fee = 0; fee < MAX_FEECOUNT; ++fee)
0040   {
0041     m_bcoMatchingInformation_vec.emplace_back(
0042         std::string("BcoMatchingInformation_Packet") + std::to_string(packet_id) + "_FEE" + std::to_string(fee));
0043   }
0044 
0045   m_feeData.resize(MAX_FEECOUNT);
0046   m_timeHitMap.resize(MAX_FEECOUNT);
0047 
0048   // cppcheck-suppress noCopyConstructor
0049   // cppcheck-suppress noOperatorEq
0050   m_packetTimer = new PHTimer("TpcTimeFrameBuilderRun3_Packet" + std::to_string(packet_id));
0051 
0052   Fun4AllHistoManager* hm = QAHistManagerDef::getHistoManager();
0053   assert(hm);
0054 
0055   m_hNorm = new TH1D(TString(m_HistoPrefix.c_str()) + "_Normalization",  //
0056                      TString(m_HistoPrefix.c_str()) + " Normalization;Items;Count",
0057                       kRun3NormalizationBinCount, .5, kRun3NormalizationBinCount + .5);
0058   int i = 1;
0059   m_hNorm->GetXaxis()->SetBinLabel(i++, "Packet");
0060   m_hNorm->GetXaxis()->SetBinLabel(i++, "Lv1-Taggers");
0061   m_hNorm->GetXaxis()->SetBinLabel(i++, "EnDat-Taggers");
0062   m_hNorm->GetXaxis()->SetBinLabel(i++, "ChannelPackets");
0063   m_hNorm->GetXaxis()->SetBinLabel(i++, "Waveforms");
0064 
0065   m_hNorm->GetXaxis()->SetBinLabel(i++, "DMA_WORD_GTM");
0066   m_hNorm->GetXaxis()->SetBinLabel(i++, "DMA_WORD_FEE");
0067   m_hNorm->GetXaxis()->SetBinLabel(i++, "DMA_WORD_FEE_INVALID");
0068   m_hNorm->GetXaxis()->SetBinLabel(i++, "DMA_WORD_INVALID");
0069 
0070   m_hNorm->GetXaxis()->SetBinLabel(i++, "DMA_WORD_GTM_HEARTBEAT");
0071   m_hNorm->GetXaxis()->SetBinLabel(i++, "DMA_WORD_GTM_DC_STOP_SEND");
0072 
0073   m_hNorm->GetXaxis()->SetBinLabel(i++, "TimeFrameSizeLimitError");
0074 
0075   m_hNorm->GetXaxis()->SetBinLabel(i++, "GTM_TimeFrame_Matched");
0076   m_hNorm->GetXaxis()->SetBinLabel(i++, "GTM_TimeFrame_Unmatched");
0077   m_hNorm->GetXaxis()->SetBinLabel(i++, "GTM_TimeFrame_Matched_Hit_Sum");
0078   m_hNorm->GetXaxis()->SetBinLabel(i++, "GTM_TimeFrame_Dropped_Hit_Sum");
0079   m_hNorm->GetXaxis()->SetBinLabel(i++, "Run3_TimeFrame_Exact_Matched");
0080   m_hNorm->GetXaxis()->SetBinLabel(i++, "Run3_TimeFrame_FuzzyFallback");
0081   m_hNorm->GetXaxis()->SetBinLabel(i++, "Run3_TimeFrame_FuzzyFallback_Hit_Sum");
0082   m_hNorm->GetXaxis()->SetBinLabel(i++, "Run3_TimeFrame_MatchFailed");
0083 
0084   m_hNormTruncatedWaveformRecoveryFeeFirstBin = i;
0085   for (uint16_t fee = 0; fee < MAX_FEECOUNT; ++fee)
0086   {
0087     m_hNorm->GetXaxis()->SetBinLabel(i++, ("Run3_TruncatedWaveformRecover_FEE" + std::to_string(fee)).c_str());
0088   }
0089 
0090   assert(i <= kRun3NormalizationBinCount + 1);
0091   m_hNorm->GetXaxis()->LabelsOption("v");
0092   hm->registerHisto(m_hNorm);
0093 
0094   h_PacketLength = new TH1I(TString(m_HistoPrefix.c_str()) + "_PacketLength",  //
0095                             TString(m_HistoPrefix.c_str()) + " PacketLength;PacketLength [32bit Words];Count", 1000, .5, 5e6);
0096   hm->registerHisto(h_PacketLength);
0097 
0098   h_PacketLength_Residual = new TH1I(TString(m_HistoPrefix.c_str()) + "_PacketLength_Residual",  //
0099                                      TString(m_HistoPrefix.c_str()) +
0100                                          " PacketLength that does not fit into DMA transfer;PacketLength [16bit Words];Count",
0101                                      16, -.5, 15.5);
0102   hm->registerHisto(h_PacketLength_Residual);
0103 
0104   h_PacketLength_Padding = new TH1I(TString(m_HistoPrefix.c_str()) + "_PacketLength_Padding",  //
0105                                     TString(m_HistoPrefix.c_str()) +
0106                                         " padding within PacketLength;PacketLength [32bit Words];Count",
0107                                     16, -.5, 15.5);
0108   hm->registerHisto(h_PacketLength_Padding);
0109 
0110   m_hFEEDataStream = new TH2I(TString(m_HistoPrefix.c_str()) + "_FEE_DataStream_WordCount",  //
0111                               TString(m_HistoPrefix.c_str()) +
0112                                   " FEE Data Stream Word Count;FEE ID;Type;Count",
0113                               MAX_FEECOUNT, -.5, MAX_FEECOUNT - .5, 25, .5, 25.5);
0114   i = 1;
0115   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "WordValid");
0116   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "WordSkipped");
0117   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "WordDigitalCurrentKeyWord");
0118   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "InvalidLength");
0119   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "RawHit");
0120   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "HitFormatErrorOverLength");
0121   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "HitFormatErrorMismatchedLength");
0122   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "HitCRCError");
0123   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "DigitalCurrent");
0124   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "DigitalCurrentFormatErrorMismatchedLength");
0125   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "DigitalCurrentCRCError");
0126   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "ParityError");
0127   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "HitUnusedBeforeCleanup");
0128   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "PacketHeartBeat");
0129   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "PacketHeartBeatClockSyncUnavailable");
0130   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "PacketHeartBeatClockSyncError");
0131   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "PacketHeartBeatClockSyncOK");
0132   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "PacketClockSyncUnavailable");
0133   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "PacketClockSyncError");
0134   m_hFEEDataStream->GetYaxis()->SetBinLabel(i++, "PacketClockSyncOK");
0135   assert(i <= 25);
0136   hm->registerHisto(m_hFEEDataStream);
0137 
0138   m_hFEEChannelPacketCount = new TH1I(TString(m_HistoPrefix.c_str()) + "_FEEChannelPacketCount",  //
0139                                       TString(m_HistoPrefix.c_str()) +
0140                                           " Count of waveform packet per channel;FEE*256 + Channel;Count",
0141                                       MAX_FEECOUNT * MAX_CHANNELS, -.5, MAX_FEECOUNT * MAX_CHANNELS - .5);
0142   hm->registerHisto(m_hFEEChannelPacketCount);
0143 
0144   m_hFEESAMPAADC = new TH2I(TString(m_HistoPrefix.c_str()) + "_FEE_SAMPA_ADC",  //
0145                             TString(m_HistoPrefix.c_str()) +
0146                                 " ADC distribution in 2D;ADC Time Bin [0...1023];FEE*8+SAMPA;Sum ADC",
0147                             MAX_PACKET_LENGTH, -.5, MAX_PACKET_LENGTH - .5,
0148                             MAX_FEECOUNT * MAX_SAMPA, -.5, MAX_FEECOUNT * MAX_SAMPA - .5);
0149   hm->registerHisto(m_hFEESAMPAADC);
0150 
0151   m_hFEESAMPAHeartBeatSync = new TH1I(TString(m_HistoPrefix.c_str()) + "_FEE_SAMPA_HEARTBEAT_SYNC",  //
0152                                       TString(m_HistoPrefix.c_str()) +
0153                                           " FEE/SAMPA Sync Heartbeat Count;FEE*8+SAMPA;Sync Heartbeat Count",
0154                                       MAX_FEECOUNT * MAX_SAMPA, -.5, MAX_FEECOUNT * MAX_SAMPA - .5);
0155   hm->registerHisto(m_hFEESAMPAHeartBeatSync);
0156 
0157   h_GTMClockDiff_Matched = new TH1I(TString(m_HistoPrefix.c_str()) + "_GTMClockDiff_Matched",  //
0158                                     TString(m_HistoPrefix.c_str()) +
0159                                         " GTM BCO Diff for Matched Time Frame;Trigger BCO Diff [BCO];Count",
0160                                     1024, -512 - .5, 512 - .5);
0161   hm->registerHisto(h_GTMClockDiff_Matched);
0162   h_GTMClockDiff_Unmatched = new TH1I(TString(m_HistoPrefix.c_str()) + "_GTMClockDiff_Unmatched",  //
0163                                       TString(m_HistoPrefix.c_str()) +
0164                                           " GTM BCO Diff for Unmatched Time Frame;Trigger BCO Diff [BCO];Count",
0165                                       1024, -512 - .5, 512 - .5);
0166   hm->registerHisto(h_GTMClockDiff_Unmatched);
0167   h_GTMClockDiff_Dropped = new TH1I(TString(m_HistoPrefix.c_str()) + "_GTMClockDiff_Dropped",  //
0168                                     TString(m_HistoPrefix.c_str()) +
0169                                         " GTM BCO Diff for Dropped Time Frame;Trigger BCO Diff [BCO];Count",
0170                                     16384, -16384 - .5, 0 - .5);
0171   hm->registerHisto(h_GTMClockDiff_Dropped);
0172   h_TimeFrame_Matched_Size = new TH1I(TString(m_HistoPrefix.c_str()) + "_TimeFrame_Matched_Size",  //
0173                                       TString(m_HistoPrefix.c_str()) +
0174                                           " Time frame size for Matched Time Frame ;Size [TPC raw hits];Count",
0175                                       3328, -.5, 3328 - .5);
0176   hm->registerHisto(h_TimeFrame_Matched_Size);
0177 
0178   h_Run3_FEE_GTMMatching_ClockDiff = new TH2I(TString(m_HistoPrefix.c_str()) + "_Run3_FEE_GTMMatching_ClockDiff",  //
0179                                                    TString(m_HistoPrefix.c_str()) +
0180                                                        " Run3 FEE GTM matching clock diff by FEE;Clock Difference [FEE Clock Cycle];FEE;Matched hits",
0181                                                    2048, -1024 - .5, 1024 - .5,
0182                                                    MAX_FEECOUNT, -.5, MAX_FEECOUNT - .5);
0183   hm->registerHisto(h_Run3_FEE_GTMMatching_ClockDiff);
0184 
0185   h_Run3TimeFrameExactHit_FEE = new TH1I(TString(m_HistoPrefix.c_str()) + "_Run3TimeFrameExactHit_FEE",  //
0186                                           TString(m_HistoPrefix.c_str()) +
0187                                               " Run3 exact matched hit sum by FEE;FEE;Exact matched hits",
0188                                           MAX_FEECOUNT, -.5, MAX_FEECOUNT - .5);
0189   hm->registerHisto(h_Run3TimeFrameExactHit_FEE);
0190 
0191   h_Run3TimeFrameFuzzyHit_FEE = new TH1I(TString(m_HistoPrefix.c_str()) + "_Run3TimeFrameFuzzyHit_FEE",  //
0192                                           TString(m_HistoPrefix.c_str()) +
0193                                               " Run3 fuzzy fallback matched hit sum by FEE;FEE;Fuzzy fallback matched hits",
0194                                           MAX_FEECOUNT, -.5, MAX_FEECOUNT - .5);
0195   hm->registerHisto(h_Run3TimeFrameFuzzyHit_FEE);
0196 
0197   static constexpr uint32_t kRun3TruncatedWaveformRecoveryWindowPlotingRange = 1200U;
0198   h_Run3Waveform_GL1Spacing = new TH2I(TString(m_HistoPrefix.c_str()) + "_Run3Waveform_GL1Spacing",  //
0199                                           TString(m_HistoPrefix.c_str()) +
0200                                               " Run3 matched waveform ADC sum before truncated waveform recovery vs GL1 spacing;ADC Time Bin [0...1199];Current - previous GL1 GTM BCO [BCO]",
0201                                           kRun3TruncatedWaveformRecoveryWindowPlotingRange, -.5, static_cast<double>(kRun3TruncatedWaveformRecoveryWindowPlotingRange) - .5, 1001, -.5, 1000.5);
0202   hm->registerHisto(h_Run3Waveform_GL1Spacing);
0203 
0204   h_Run3WaveformRecovered_GL1Spacing = new TH2I(TString(m_HistoPrefix.c_str()) + "_Run3WaveformRecovered_GL1Spacing",  //
0205                                                    TString(m_HistoPrefix.c_str()) +
0206                                                        " Run3 matched waveform ADC sum after truncated waveform recovery vs GL1 spacing;ADC Time Bin [0...1199];Current - previous GL1 GTM BCO [BCO]",
0207                                                    kRun3TruncatedWaveformRecoveryWindowPlotingRange, -.5, static_cast<double>(kRun3TruncatedWaveformRecoveryWindowPlotingRange) - .5, 1001, -.5, 1000.5);
0208   hm->registerHisto(h_Run3WaveformRecovered_GL1Spacing);
0209 
0210   h_Run3FEE_TimeFrameCount_GL1Spacing = new TH2I(TString(m_HistoPrefix.c_str()) + "_Run3FEE_TimeFrameCount_GL1Spacing",  //
0211                                                  TString(m_HistoPrefix.c_str()) +
0212                                                      " Run3 exact timeframe count by FEE vs GL1 spacing;Current - previous GL1 GTM BCO [BCO];FEE",
0213                                                  1001, -.5, 1000.5, MAX_FEECOUNT, -.5, MAX_FEECOUNT - .5);
0214   hm->registerHisto(h_Run3FEE_TimeFrameCount_GL1Spacing);
0215 
0216   h_Run3FEE_TimeFrameRecoveredCount_GL1Spacing = new TH2I(TString(m_HistoPrefix.c_str()) + "_Run3FEE_TimeFrameRecoveredCount_GL1Spacing",  //
0217                                                           TString(m_HistoPrefix.c_str()) +
0218                                                               " Run3 timeframe count by FEE after truncated waveform recovery vs GL1 spacing;Current - previous GL1 GTM BCO [BCO];FEE",
0219                                                           1001, -.5, 1000.5, MAX_FEECOUNT, -.5, MAX_FEECOUNT - .5);
0220   hm->registerHisto(h_Run3FEE_TimeFrameRecoveredCount_GL1Spacing);
0221 
0222   h_Run3FEE_TriggerCount_GL1Spacing = new TH2I(TString(m_HistoPrefix.c_str()) + "_Run3FEE_TriggerCount_GL1Spacing",  //
0223                                                TString(m_HistoPrefix.c_str()) +
0224                                                    " Run3 GL1 trigger count by FEE vs GL1 spacing;Current - previous GL1 GTM BCO [BCO];FEE",
0225                                                1001, -.5, 1000.5, MAX_FEECOUNT, -.5, MAX_FEECOUNT - .5);
0226   hm->registerHisto(h_Run3FEE_TriggerCount_GL1Spacing);
0227 
0228   h_Run3PreviousTimeFrameWaveformADC = new TH1I(TString(m_HistoPrefix.c_str()) + "_Run3PreviousTimeFrameWaveformADCCache",  //
0229                                                 TString(m_HistoPrefix.c_str()) +
0230                                                     " Run3 previous matched waveform ADC cache;ADC Time Bin [0...1199];Sum ADC",
0231                                                 kRun3TruncatedWaveformRecoveryWindowPlotingRange, -.5, static_cast<double>(kRun3TruncatedWaveformRecoveryWindowPlotingRange) - .5);
0232   h_Run3PreviousTimeFrameWaveformADC->SetDirectory(nullptr);
0233 
0234   h_Run3PreviousTimeFrameRecoveredWaveformADC = new TH1I(TString(m_HistoPrefix.c_str()) + "_Run3PreviousTimeFrameRecoveredWaveformADCCache",  //
0235                                                          TString(m_HistoPrefix.c_str()) +
0236                                                              " Run3 previous matched waveform ADC cache after truncated waveform recovery;ADC Time Bin [0...1199];Sum ADC",
0237                                                          kRun3TruncatedWaveformRecoveryWindowPlotingRange, -.5, static_cast<double>(kRun3TruncatedWaveformRecoveryWindowPlotingRange) - .5);
0238   h_Run3PreviousTimeFrameRecoveredWaveformADC->SetDirectory(nullptr);
0239 
0240   h_ProcessPacket_Time = new TH2I(TString(m_HistoPrefix.c_str()) + "_ProcessPacket_Time",  //
0241                                   TString(m_HistoPrefix.c_str()) +
0242                                       " Time cost to run ProcessPacket();Call counts;Time elapsed per call [ms];Count",
0243                                   100, 0, 30e6, 100, 0, 10);
0244   hm->registerHisto(h_ProcessPacket_Time);
0245 }
0246 
0247 TpcTimeFrameBuilderRun3::~TpcTimeFrameBuilderRun3()
0248 {
0249   for (auto& feeTimeHitMap : m_timeHitMap)
0250   {
0251     for (auto& timeHitEntry : feeTimeHitMap)
0252     {
0253       for (TpcRawHit* hit : timeHitEntry.second)
0254       {
0255         delete hit;
0256       }
0257       timeHitEntry.second.clear();
0258     }
0259   }
0260 
0261   for (auto& timeFrameEntry : m_timeFrameMap)
0262   {
0263     while (!timeFrameEntry.second.empty())
0264     {
0265       TpcRawHit* hit = timeFrameEntry.second.back();
0266       delete hit;
0267       timeFrameEntry.second.pop_back();
0268     }
0269   }
0270 
0271   write_bx_counter_sync_cdb_tree();
0272 
0273   delete h_Run3PreviousTimeFrameWaveformADC;
0274   delete h_Run3PreviousTimeFrameRecoveredWaveformADC;
0275 
0276   delete m_packetTimer;
0277 
0278   delete m_digitalCurrentDebugTTree;
0279 }
0280 
0281 void TpcTimeFrameBuilderRun3::setVerbosity(const int i)
0282 {
0283   m_verbosity = i;
0284 
0285   for (BcoMatchingInformation& bcoMatchingInformation : m_bcoMatchingInformation_vec)
0286   {
0287     bcoMatchingInformation.set_verbosity(i);
0288   }
0289 }
0290 
0291 void TpcTimeFrameBuilderRun3::write_bx_counter_sync_cdb_tree() const
0292 {
0293   if (m_bxCounterSyncCDBTTreeName.empty())
0294   {
0295     return;
0296   }
0297 
0298   CDBTTree cdbtree(m_bxCounterSyncCDBTTreeName);
0299 
0300   int entry_count = 0;
0301   for (size_t fee = 0; fee < m_bcoMatchingInformation_vec.size(); ++fee)
0302   {
0303     const BcoMatchingInformation& bco_info = m_bcoMatchingInformation_vec[fee];
0304     const size_t observation_count = std::min(bco_info.get_bx_counter_sync_observation_count(),
0305                                               BcoMatchingInformation::kMaxBXCounterSyncObservations);
0306     const auto& observations = bco_info.get_bx_counter_sync_observations();
0307     for (size_t observation_index = 0; observation_index < observation_count; ++observation_index)
0308     {
0309       const BcoMatchingInformation::BXCounterSyncObservation& observation = observations[observation_index];
0310       const int channel = static_cast<int>(fee * BcoMatchingInformation::kMaxBXCounterSyncObservations + observation_index);
0311       cdbtree.SetIntValue(channel, "packet_id", m_packet_id);
0312       cdbtree.SetIntValue(channel, "fee", static_cast<int>(fee));
0313       cdbtree.SetIntValue(channel, "observation", static_cast<int>(observation_index));
0314       cdbtree.SetUInt64Value(channel, "bx_counter_sync_gtm_bco", observation.bx_counter_sync_gtm_bco);
0315       cdbtree.SetUInt64Value(channel, "bco_reference_gtm_bco", observation.bco_reference_gtm_bco);
0316       cdbtree.SetUInt64Value(channel, "m_bco_reference_gtm_bco", observation.m_bco_reference.first);
0317       cdbtree.SetIntValue(channel, "m_bco_reference_fee_bco", static_cast<int>(observation.m_bco_reference.second));
0318       ++entry_count;
0319     }
0320   }
0321 
0322   if (entry_count == 0)
0323   {
0324     return;
0325   }
0326 
0327   cdbtree.SetSingleIntValue("packet_id", m_packet_id);
0328   cdbtree.SetSingleIntValue("n_bx_counter_sync_observations", entry_count);
0329   cdbtree.SetSingleIntValue("max_fee_count", MAX_FEECOUNT);
0330   cdbtree.SetSingleIntValue("max_observations_per_fee", static_cast<int>(BcoMatchingInformation::kMaxBXCounterSyncObservations));
0331   cdbtree.CommitSingle();
0332   cdbtree.Commit();
0333   cdbtree.WriteCDBTTree();
0334 
0335   if (m_verbosity >= 0)
0336   {
0337     std::cout << __PRETTY_FUNCTION__ << " - saved " << entry_count
0338               << " BX_COUNTER_SYNC_T observations to " << m_bxCounterSyncCDBTTreeName << std::endl;
0339   }
0340 }
0341 
0342 void TpcTimeFrameBuilderRun3::fill_waveform_gl1_spacing(TH1 *waveform_adc_cache, TH2 *waveform_gl1_spacing, uint64_t gtm_bco_spacing) const
0343 {
0344   assert(waveform_adc_cache);
0345   assert(waveform_gl1_spacing);
0346 
0347   const int waveform_ybin = waveform_gl1_spacing->GetYaxis()->FindFixBin(static_cast<double>(gtm_bco_spacing));
0348   double waveform_entries = 0;
0349   for (int xbin = 1; xbin <= waveform_adc_cache->GetNbinsX(); ++xbin)
0350   {
0351     const double adc_sum = waveform_adc_cache->GetBinContent(xbin);
0352     if (adc_sum == 0)
0353     {
0354       continue;
0355     }
0356 
0357     waveform_gl1_spacing->AddBinContent(waveform_gl1_spacing->GetBin(xbin, waveform_ybin), adc_sum);
0358     ++waveform_entries;
0359   }
0360   waveform_gl1_spacing->SetEntries(waveform_gl1_spacing->GetEntries() + waveform_entries);
0361 }
0362 
0363 void TpcTimeFrameBuilderRun3::flush_previous_timeframe_qa_cache(uint64_t current_gtm_bco)
0364 {
0365   assert(h_Run3PreviousTimeFrameWaveformADC);
0366   assert(h_Run3PreviousTimeFrameRecoveredWaveformADC);
0367   assert(h_Run3Waveform_GL1Spacing);
0368   assert(h_Run3WaveformRecovered_GL1Spacing);
0369   assert(h_Run3FEE_TimeFrameCount_GL1Spacing);
0370   assert(h_Run3FEE_TimeFrameRecoveredCount_GL1Spacing);
0371   assert(h_Run3FEE_TriggerCount_GL1Spacing);
0372 
0373   if (!m_previousTimeFrameGtmBco)
0374   {
0375     return;
0376   }
0377 
0378   const uint64_t previous_gtm_bco = *m_previousTimeFrameGtmBco;
0379   static constexpr uint64_t gtm_clock_range = uint64_t(1) << 40U;
0380   const uint64_t current_gtm_bco_rollover_corrected = current_gtm_bco >= previous_gtm_bco
0381                                                           ? current_gtm_bco
0382                                                           : current_gtm_bco + gtm_clock_range;
0383   const uint64_t gtm_bco_spacing = current_gtm_bco_rollover_corrected - previous_gtm_bco;
0384 
0385   fill_waveform_gl1_spacing(h_Run3PreviousTimeFrameWaveformADC, h_Run3Waveform_GL1Spacing, gtm_bco_spacing);
0386   fill_waveform_gl1_spacing(h_Run3PreviousTimeFrameRecoveredWaveformADC, h_Run3WaveformRecovered_GL1Spacing, gtm_bco_spacing);
0387 
0388   const int fee_xbin = h_Run3FEE_TriggerCount_GL1Spacing->GetXaxis()->FindFixBin(static_cast<double>(gtm_bco_spacing));
0389   double timeframe_entries = 0;
0390   double recovered_timeframe_entries = 0;
0391   for (uint16_t fee = 0; fee < MAX_FEECOUNT; ++fee)
0392   {
0393     const int fee_ybin = static_cast<int>(fee) + 1;
0394     h_Run3FEE_TriggerCount_GL1Spacing->AddBinContent(h_Run3FEE_TriggerCount_GL1Spacing->GetBin(fee_xbin, fee_ybin));
0395 
0396     if (m_previousTimeFrameExactFees.test(fee))
0397     {
0398       h_Run3FEE_TimeFrameCount_GL1Spacing->AddBinContent(h_Run3FEE_TimeFrameCount_GL1Spacing->GetBin(fee_xbin, fee_ybin));
0399       ++timeframe_entries;
0400     }
0401 
0402     if (m_previousTimeFrameRecoveredFees.test(fee))
0403     {
0404       h_Run3FEE_TimeFrameRecoveredCount_GL1Spacing->AddBinContent(h_Run3FEE_TimeFrameRecoveredCount_GL1Spacing->GetBin(fee_xbin, fee_ybin));
0405       ++recovered_timeframe_entries;
0406     }
0407   }
0408   h_Run3FEE_TriggerCount_GL1Spacing->SetEntries(h_Run3FEE_TriggerCount_GL1Spacing->GetEntries() + MAX_FEECOUNT);
0409   h_Run3FEE_TimeFrameCount_GL1Spacing->SetEntries(h_Run3FEE_TimeFrameCount_GL1Spacing->GetEntries() + timeframe_entries);
0410   h_Run3FEE_TimeFrameRecoveredCount_GL1Spacing->SetEntries(h_Run3FEE_TimeFrameRecoveredCount_GL1Spacing->GetEntries() + recovered_timeframe_entries);
0411 
0412   h_Run3PreviousTimeFrameWaveformADC->Reset();
0413   h_Run3PreviousTimeFrameRecoveredWaveformADC->Reset();
0414   m_previousTimeFrameExactFees.reset();
0415   m_previousTimeFrameRecoveredFees.reset();
0416   m_previousTimeFrameGtmBco.reset();
0417 }
0418 
0419 void TpcTimeFrameBuilderRun3::cache_waveform_adc(TH1 *waveform_adc_cache, const std::vector<TpcRawHit*>& timeframe) const
0420 {
0421   assert(waveform_adc_cache);
0422 
0423   waveform_adc_cache->Reset();
0424   for (const TpcRawHit* hit : timeframe)
0425   {
0426     if (!hit)
0427     {
0428       continue;
0429     }
0430 
0431     std::unique_ptr<TpcRawHit::AdcIterator> adc_iter(hit->CreateAdcIterator());
0432     if (!adc_iter)
0433     {
0434       continue;
0435     }
0436 
0437     for (adc_iter->First(); !adc_iter->IsDone(); adc_iter->Next())
0438     {
0439       const uint16_t time_bin = adc_iter->CurrentTimeBin();
0440       const uint16_t adc = adc_iter->CurrentAdc();
0441       if (adc == 0 || time_bin >= kRun3TruncatedWaveformRecoveryWindow)
0442       {
0443         continue;
0444       }
0445 
0446       waveform_adc_cache->AddBinContent(static_cast<int>(time_bin) + 1, adc);
0447     }
0448   }
0449 }
0450 
0451 void TpcTimeFrameBuilderRun3::cache_timeframe_qa(uint64_t gtm_bco, const std::vector<TpcRawHit*>& timeframe, const std::bitset<MAX_FEECOUNT>& exact_matched_fees)
0452 {
0453   cache_waveform_adc(h_Run3PreviousTimeFrameRecoveredWaveformADC, timeframe);
0454 
0455   m_previousTimeFrameExactFees = exact_matched_fees;
0456   m_previousTimeFrameRecoveredFees.reset();
0457   for (const TpcRawHit* hit : timeframe)
0458   {
0459     if (!hit || hit->get_fee() >= MAX_FEECOUNT)
0460     {
0461       continue;
0462     }
0463     m_previousTimeFrameRecoveredFees.set(hit->get_fee());
0464   }
0465   m_previousTimeFrameGtmBco = gtm_bco;
0466 }
0467 
0468 int64_t TpcTimeFrameBuilderRun3::get_signed_fee_bco_diff(uint32_t first, uint32_t second)
0469 {
0470   static constexpr int64_t fee_clock_range = static_cast<int64_t>(uint64_t{1} << 20U);
0471   static constexpr int64_t fee_clock_half_range = static_cast<int64_t>(uint64_t{1} << 19U);
0472 
0473   int64_t diff = static_cast<int64_t>(first & kFEEClockMask) - static_cast<int64_t>(second & kFEEClockMask);
0474   if (diff > fee_clock_half_range)
0475   {
0476     diff -= fee_clock_range;
0477   }
0478   else if (diff < -fee_clock_half_range)
0479   {
0480     diff += fee_clock_range;
0481   }
0482   return diff;
0483 }
0484 
0485 uint32_t TpcTimeFrameBuilderRun3::get_fee_bco_diff(uint32_t first, uint32_t second)
0486 {
0487   const int64_t diff = get_signed_fee_bco_diff(first, second);
0488   return static_cast<uint32_t>(diff < 0 ? -diff : diff);
0489 }
0490 
0491 size_t TpcTimeFrameBuilderRun3::move_time_hits(uint32_t fee_bco, uint16_t fee, std::vector<TpcRawHit*>& timeframe)
0492 {
0493   if (fee >= m_timeHitMap.size())
0494   {
0495     return 0;
0496   }
0497 
0498   auto& fee_time_hits = m_timeHitMap[fee];
0499   auto it = fee_time_hits.find(fee_bco & kFEEClockMask);
0500   if (it == fee_time_hits.end())
0501   {
0502     return 0;
0503   }
0504 
0505   std::vector<TpcRawHit*>& hits = it->second;
0506   const size_t moved = hits.size();
0507   if (moved == 0)
0508   {
0509     fee_time_hits.erase(it);
0510     return 0;
0511   }
0512 
0513   timeframe.reserve(timeframe.size() + moved);
0514   timeframe.insert(timeframe.end(), hits.begin(), hits.end());
0515   fee_time_hits.erase(it);
0516   return moved;
0517 }
0518 
0519 size_t TpcTimeFrameBuilderRun3::count_time_hits(uint32_t fee_bco, uint16_t fee) const
0520 {
0521   if (fee >= m_timeHitMap.size())
0522   {
0523     return 0;
0524   }
0525 
0526   const auto& fee_time_hits = m_timeHitMap[fee];
0527   auto it = fee_time_hits.find(fee_bco & kFEEClockMask);
0528   if (it == fee_time_hits.end())
0529   {
0530     return 0;
0531   }
0532 
0533   return it->second.size();
0534 }
0535 
0536 size_t TpcTimeFrameBuilderRun3::time_hit_bucket_count() const
0537 {
0538   size_t count = 0;
0539   for (const auto& fee_time_hits : m_timeHitMap)
0540   {
0541     count += fee_time_hits.size();
0542   }
0543   return count;
0544 }
0545 
0546 std::optional<uint32_t> TpcTimeFrameBuilderRun3::find_fuzzy_fee_bco(uint32_t predicted_fee_bco, uint16_t fee) const
0547 {
0548   if (fee >= m_timeHitMap.size())
0549   {
0550     return std::nullopt;
0551   }
0552 
0553   const auto& fee_time_hits = m_timeHitMap[fee];
0554   if (fee_time_hits.empty())
0555   {
0556     return std::nullopt;
0557   }
0558 
0559   predicted_fee_bco &= kFEEClockMask;
0560   uint32_t best_fee_bco = 0;
0561   uint32_t best_diff = std::numeric_limits<uint32_t>::max();
0562   bool found = false;
0563 
0564   auto consider_fee_bco = [&](uint32_t fee_bco, const std::vector<TpcRawHit *>& hits)
0565   {
0566     if (hits.empty())
0567     {
0568       return;
0569     }
0570 
0571     const uint32_t diff = get_fee_bco_diff(fee_bco, predicted_fee_bco);
0572     if (diff <= kRun3FeeMatchWindow && (!found || diff < best_diff || (diff == best_diff && fee_bco < best_fee_bco)))
0573     {
0574       found = true;
0575       best_diff = diff;
0576       best_fee_bco = fee_bco;
0577     }
0578   };
0579 
0580   auto scan_range = [&](uint32_t first_fee_bco, uint32_t last_fee_bco)
0581   {
0582     for (auto it = fee_time_hits.lower_bound(first_fee_bco); it != fee_time_hits.end() && it->first <= last_fee_bco; ++it)
0583     {
0584       consider_fee_bco(it->first, it->second);
0585     }
0586   };
0587 
0588   const uint32_t lower_fee_bco = (predicted_fee_bco - kRun3FeeMatchWindow) & kFEEClockMask;
0589   const uint32_t upper_fee_bco = (predicted_fee_bco + kRun3FeeMatchWindow) & kFEEClockMask;
0590   if (lower_fee_bco <= upper_fee_bco)
0591   {
0592     scan_range(lower_fee_bco, upper_fee_bco);
0593   }
0594   else
0595   {
0596     scan_range(lower_fee_bco, kFEEClockMask);
0597     scan_range(0, upper_fee_bco);
0598   }
0599 
0600   if (found)
0601   {
0602     return best_fee_bco;
0603   }
0604   return std::nullopt;
0605 }
0606 
0607 size_t TpcTimeFrameBuilderRun3::append_shifted_waveforms(TpcRawHitRun3_typ *target, const TpcRawHit &source, uint32_t fee_clock_shift) const
0608 {
0609   if (!target || fee_clock_shift >= kRun3TruncatedWaveformRecoveryWindow)
0610   {
0611     return 0;
0612   }
0613 
0614   const auto& source_run3 = static_cast<const TpcRawHitRun3_typ&>(source);
0615   const TpcRawHitRun3_typ::AdcWaveformVector_t& source_waveforms = source_run3.get_adc_waveforms();
0616   if (source_waveforms.empty())
0617   {
0618     return 0;
0619   }
0620 
0621   size_t appended_waveforms = 0;
0622   std::vector<uint16_t> adc_values;
0623   uint16_t waveform_start = 0;
0624   uint32_t expected_time_bin = std::numeric_limits<uint32_t>::max();
0625 
0626   auto flush_waveform = [&]()
0627   {
0628     if (adc_values.empty())
0629     {
0630       return;
0631     }
0632 
0633     std::vector<uint16_t> waveform_adc;
0634     waveform_adc.swap(adc_values);
0635     target->move_adc_waveform(waveform_start, std::move(waveform_adc));
0636     expected_time_bin = std::numeric_limits<uint32_t>::max();
0637     ++appended_waveforms;
0638   };
0639 
0640   for (const TpcRawHitRun3_typ::AdcWaveform_t& source_waveform : source_waveforms)
0641   {
0642     const std::vector<uint16_t>& source_adc_values = source_waveform.second;
0643     if (source_adc_values.empty())
0644     {
0645       continue;
0646     }
0647 
0648     const uint32_t shifted_waveform_start = static_cast<uint32_t>(source_waveform.first) + fee_clock_shift;
0649     if (shifted_waveform_start >= kRun3TruncatedWaveformRecoveryWindow)
0650     {
0651       flush_waveform();
0652       continue;
0653     }
0654 
0655     for (size_t adc_index = 0; adc_index < source_adc_values.size(); ++adc_index)
0656     {
0657       const uint32_t shifted_time_bin = shifted_waveform_start + static_cast<uint32_t>(adc_index);
0658       if (shifted_time_bin >= kRun3TruncatedWaveformRecoveryWindow)
0659       {
0660         flush_waveform();
0661         break;
0662       }
0663 
0664       if (adc_values.empty() || shifted_time_bin != expected_time_bin)
0665       {
0666         flush_waveform();
0667         waveform_start = static_cast<uint16_t>(shifted_time_bin);
0668       }
0669 
0670       adc_values.push_back(source_adc_values[adc_index]);
0671       expected_time_bin = shifted_time_bin + 1U;
0672     }
0673   }
0674   flush_waveform();
0675 
0676   return appended_waveforms;
0677 }
0678 
0679 size_t TpcTimeFrameBuilderRun3::recover_truncated_waveforms(uint32_t predicted_fee_bco, uint16_t fee, std::vector<TpcRawHit*>& timeframe)
0680 {
0681   if (fee >= m_timeHitMap.size())
0682   {
0683     return 0;
0684   }
0685 
0686   predicted_fee_bco &= kFEEClockMask;
0687   std::array<TpcRawHitRun3_typ*, MAX_CHANNELS> current_hits{};
0688   std::array<int64_t, MAX_CHANNELS> current_hit_diff{};
0689   current_hit_diff.fill(std::numeric_limits<int64_t>::max());
0690 
0691   for (TpcRawHit* hit : timeframe)
0692   {
0693     if (!hit || hit->get_fee() != fee || hit->get_channel() >= MAX_CHANNELS)
0694     {
0695       continue;
0696     }
0697 
0698     TpcRawHitRun3_typ* hit_v3 = dynamic_cast<TpcRawHitRun3_typ*>(hit);
0699     if (!hit_v3)
0700     {
0701       continue;
0702     }
0703 
0704     const uint16_t channel = hit_v3->get_channel();
0705     const int64_t signed_diff = get_signed_fee_bco_diff(static_cast<uint32_t>(hit_v3->get_bco()), predicted_fee_bco);
0706     const int64_t abs_diff = signed_diff < 0 ? -signed_diff : signed_diff;
0707     if (abs_diff < current_hit_diff[channel])
0708     {
0709       current_hit_diff[channel] = abs_diff;
0710       current_hits[channel] = hit_v3;
0711     }
0712   }
0713 
0714   size_t recovered_hits = 0;
0715   auto& fee_time_hits = m_timeHitMap[fee];
0716   if (fee_time_hits.empty())
0717   {
0718     return 0;
0719   }
0720 
0721   auto recover_from_bucket = [&](const std::pair<const uint32_t, std::vector<TpcRawHit*>>& bucket)
0722   {
0723     for (const TpcRawHit* source_hit : bucket.second)
0724     {
0725       if (!source_hit || source_hit->get_channel() >= MAX_CHANNELS)
0726       {
0727         continue;
0728       }
0729 
0730       const uint16_t channel = source_hit->get_channel();
0731       TpcRawHitRun3_typ* target_hit = current_hits[channel];
0732       const uint32_t target_fee_bco = target_hit ? static_cast<uint32_t>(target_hit->get_bco()) & kFEEClockMask : predicted_fee_bco;
0733       const int64_t fee_bco_diff = get_signed_fee_bco_diff(static_cast<uint32_t>(source_hit->get_bco()), target_fee_bco);
0734       if (fee_bco_diff <= 0 || fee_bco_diff >= static_cast<int64_t>(kRun3TruncatedWaveformRecoveryFEEWindow))
0735       {
0736         continue;
0737       }
0738 
0739       const int64_t fee_clock_shift = fee_bco_diff / static_cast<int64_t>(kRun3FEEClockPerADCClock);
0740       // FEE BCO is 2x ADC clock, so the waveform shift is half the FEE BCO difference.
0741       if (fee_clock_shift <= 0)
0742       {
0743         continue;
0744       }
0745 
0746       bool created_target = false;
0747       if (!target_hit)
0748       {
0749         target_hit = new TpcRawHitRun3_typ();
0750         target_hit->set_bco(predicted_fee_bco);
0751         target_hit->set_packetid(m_packet_id);
0752         target_hit->set_fee(fee);
0753         target_hit->set_channel(channel);
0754         target_hit->set_type(source_hit->get_type());
0755         target_hit->set_checksumerror(source_hit->get_checksumerror());
0756         target_hit->set_parityerror(source_hit->get_parityerror());
0757         timeframe.push_back(target_hit);
0758         current_hits[channel] = target_hit;
0759         current_hit_diff[channel] = 0;
0760         created_target = true;
0761       }
0762 
0763       const size_t appended_waveforms = append_shifted_waveforms(target_hit, *source_hit, static_cast<uint32_t>(fee_clock_shift));
0764       if (appended_waveforms == 0)
0765       {
0766         if (created_target)
0767         {
0768           current_hits[channel] = nullptr;
0769           current_hit_diff[channel] = std::numeric_limits<int64_t>::max();
0770           assert(!timeframe.empty() && timeframe.back() == target_hit);
0771           timeframe.pop_back();
0772           delete target_hit;
0773         }
0774         continue;
0775       }
0776 
0777       target_hit->set_checksumerror(target_hit->get_checksumerror() || source_hit->get_checksumerror());
0778       target_hit->set_parityerror(target_hit->get_parityerror() || source_hit->get_parityerror());
0779       ++recovered_hits;
0780     }
0781   };
0782 
0783   const uint32_t lower_fee_bco = (predicted_fee_bco + 1U) & kFEEClockMask;
0784   const uint32_t upper_fee_bco = (predicted_fee_bco + kRun3TruncatedWaveformRecoveryFEEWindow - 1U) & kFEEClockMask;
0785   auto scan_range = [&](uint32_t first_fee_bco, uint32_t last_fee_bco)
0786   {
0787     for (auto it = fee_time_hits.lower_bound(first_fee_bco); it != fee_time_hits.end() && it->first <= last_fee_bco; ++it)
0788     {
0789       recover_from_bucket(*it);
0790     }
0791   };
0792 
0793   if (lower_fee_bco <= upper_fee_bco)
0794   {
0795     scan_range(lower_fee_bco, upper_fee_bco);
0796   }
0797   else
0798   {
0799     scan_range(lower_fee_bco, kFEEClockMask);
0800     scan_range(0, upper_fee_bco);
0801   }
0802 
0803   return recovered_hits;
0804 }
0805 
0806 
0807 void TpcTimeFrameBuilderRun3::cleanup_time_hit_map(uint64_t bclk_rollover_corrected, uint32_t fee_clock_window)
0808 {
0809   assert(m_hFEEDataStream);
0810 
0811   const size_t nfees = std::min(m_timeHitMap.size(), m_bcoMatchingInformation_vec.size());
0812   for (size_t fee_index = 0; fee_index < nfees; ++fee_index)
0813   {
0814     const uint16_t fee = static_cast<uint16_t>(fee_index);
0815     auto& fee_time_hits = m_timeHitMap[fee_index];
0816 
0817     const std::optional<uint32_t> predicted_fee_bco = m_bcoMatchingInformation_vec[fee_index].get_predicted_fee_bco(bclk_rollover_corrected);
0818     if (!predicted_fee_bco)
0819     {
0820       if (m_verbosity >= 2)
0821       {
0822         std::cout << __PRETTY_FUNCTION__ << " - packet " << m_packet_id
0823             << ": WARNING: No predicted FEE BCO for fee index " << fee_index
0824             << " with bclk_rollover_corrected: 0x" << std::hex << bclk_rollover_corrected << std::dec
0825             << ". Clearing time hit map for this fee." << std::endl;
0826       }
0827 
0828       for (auto map_it = fee_time_hits.begin(); map_it != fee_time_hits.end();)
0829       {
0830         for (TpcRawHit* hit : map_it->second)
0831         {
0832           m_hFEEDataStream->Fill(fee, "HitUnusedBeforeCleanup", 1);
0833           delete hit;
0834         }
0835         map_it = fee_time_hits.erase(map_it);        
0836       }
0837 
0838       continue;
0839     }
0840 
0841     for (auto map_it = fee_time_hits.begin(); map_it != fee_time_hits.end();)
0842     {
0843       const int64_t diff = get_signed_fee_bco_diff(map_it->first, *predicted_fee_bco);
0844       if ((fee_clock_window == 0 && diff <= 0) || (fee_clock_window > 0 && diff < -static_cast<int64_t>(fee_clock_window)))
0845       {
0846         for (TpcRawHit* hit : map_it->second)
0847         {
0848           m_hFEEDataStream->Fill(fee, "HitUnusedBeforeCleanup", 1);
0849           delete hit;
0850         }
0851         map_it = fee_time_hits.erase(map_it);
0852       }
0853       else
0854       {
0855         ++map_it;
0856       }
0857     }
0858   }
0859 }
0860 
0861 bool TpcTimeFrameBuilderRun3::isMoreDataRequired(const uint64_t& gtm_bco) const
0862 {
0863   for (const BcoMatchingInformation& bcoMatchingInformation : m_bcoMatchingInformation_vec)
0864   {
0865     // if (not bcoMatchingInformation.is_verified())
0866     // {
0867     //   continue;
0868     // }
0869 
0870     if (bcoMatchingInformation.isMoreDataRequired(gtm_bco))
0871     {
0872       return true;
0873     }
0874   }
0875 
0876   if (m_verbosity > 1)
0877   {
0878     std::cout << __PRETTY_FUNCTION__ << "\t- packet " << m_packet_id
0879               << ":PASS: All FEEs satisfied for gtm_bco: 0x" << std::hex << gtm_bco << std::dec << ". Return false."
0880               << std::endl;
0881   }
0882   return false;
0883 }
0884 
0885 std::vector<TpcRawHit*>& TpcTimeFrameBuilderRun3::getTimeFrame(const uint64_t& gtm_bco)
0886 {
0887   assert(m_hNorm);
0888   const uint64_t bclk_rollover_corrected = m_bcoMatchingInformation_vec[0].get_gtm_rollover_correction(gtm_bco);
0889 
0890   for (BcoMatchingInformation& bcoMatchingInformation : m_bcoMatchingInformation_vec)
0891   {
0892     bcoMatchingInformation.cleanup(bclk_rollover_corrected);
0893   }
0894 
0895   cleanup_time_hit_map(bclk_rollover_corrected, kRun3FeeMatchWindow);
0896 
0897   if (auto cached = m_timeFrameMap.find(bclk_rollover_corrected); cached != m_timeFrameMap.end())
0898   {
0899     return cached->second;
0900   }
0901 
0902   flush_previous_timeframe_qa_cache(bclk_rollover_corrected);
0903 
0904   if (m_verbosity > 2)
0905   {
0906     std::cout << __PRETTY_FUNCTION__ << "   - packet " << m_packet_id
0907               << ": getTimeFrame for gtm_bco: 0x" << std::hex << gtm_bco << std::dec
0908               << ": bclk_rollover_corrected: 0x" << std::hex << bclk_rollover_corrected << std::dec
0909               << std::endl;
0910   }
0911 
0912   // Track initial buffer usage
0913   if (m_verbosity >= 2)
0914   {
0915     size_t total_time_hits = 0;
0916     size_t time_hit_map_buckets = 0;
0917     for (const auto& fee_time_hits : m_timeHitMap)
0918     {
0919       time_hit_map_buckets += fee_time_hits.size();
0920       for (const auto& bucket : fee_time_hits)
0921       {
0922         total_time_hits += bucket.second.size();
0923       }
0924     }
0925     size_t total_gtm_bco_trig = 0;
0926     size_t total_bco_heartbeat = 0;
0927     size_t total_gtm_bco_trigger = 0;
0928     size_t total_bco_matching = 0;
0929     for (const auto& bco_info : m_bcoMatchingInformation_vec)
0930     {
0931       total_gtm_bco_trig += bco_info.get_gtm_bco_trig_list_size();
0932       total_bco_heartbeat += bco_info.get_bco_heartbeat_list_size();
0933       total_gtm_bco_trigger += bco_info.get_gtm_bco_trigger_map_size();
0934       total_bco_matching += bco_info.get_bco_matching_list_size();
0935     }
0936     std::cout << __PRETTY_FUNCTION__ << " - packet " << m_packet_id
0937               << ": [INITIAL] STL buffer usage - m_timeFrameMap: " << m_timeFrameMap.size()
0938               << " frames, m_UsedTimeFrameSet: " << m_UsedTimeFrameSet.size()
0939               << ", m_timeHitMap: " << time_hit_map_buckets << " FEE-BCO buckets, "
0940               << total_time_hits << " total hits"
0941               << ", BcoMatchingInfo[gtm_trig: " << total_gtm_bco_trig
0942               << ", bco_heartbeat: " << total_bco_heartbeat
0943               << ", gtm_trigger_map: " << total_gtm_bco_trigger
0944               << ", bco_matching: " << total_bco_matching
0945               << std::endl;
0946   }
0947 
0948   auto inserted_frame = m_timeFrameMap.emplace(bclk_rollover_corrected, std::vector<TpcRawHit*>{});
0949   auto frame_it = inserted_frame.first;
0950   std::vector<TpcRawHit*>& timeframe = frame_it->second;
0951 
0952   size_t exact_hit_count = 0;
0953   size_t fallback_hit_count = 0;
0954   std::bitset<MAX_FEECOUNT> exact_matched_fees;
0955   std::array<uint32_t, MAX_FEECOUNT> predicted_fee_bcos{};
0956   std::bitset<MAX_FEECOUNT> predicted_fee_bco_available;
0957 
0958   for (size_t fee_index = 0; fee_index < std::min(m_bcoMatchingInformation_vec.size(), static_cast<size_t>(MAX_FEECOUNT)); ++fee_index)
0959   {
0960     const uint16_t fee = static_cast<uint16_t>(fee_index);
0961     const std::optional<uint32_t> predicted_fee_bco = m_bcoMatchingInformation_vec[fee_index].get_predicted_fee_bco(bclk_rollover_corrected);
0962     if (!predicted_fee_bco)
0963     {
0964       continue;
0965     }
0966     predicted_fee_bcos[fee] = *predicted_fee_bco;
0967     predicted_fee_bco_available.set(fee);
0968 
0969     size_t exact_hits = 0;
0970     for (int32_t fee_clock_offset = -kRun3ExactMatchWindow; fee_clock_offset <= kRun3ExactMatchWindow; ++fee_clock_offset)
0971     {
0972       const uint32_t exact_fee_bco = static_cast<uint32_t>(static_cast<uint64_t>(static_cast<int64_t>(*predicted_fee_bco) + fee_clock_offset) & kFEEClockMask);
0973       const size_t exact_hits_for_bco = move_time_hits(exact_fee_bco, fee, timeframe);
0974       if (exact_hits_for_bco == 0)
0975       {
0976         continue;
0977       }
0978 
0979       exact_hits += exact_hits_for_bco;
0980       assert(h_Run3_FEE_GTMMatching_ClockDiff);
0981       h_Run3_FEE_GTMMatching_ClockDiff->Fill(static_cast<double>(get_signed_fee_bco_diff(exact_fee_bco, *predicted_fee_bco)),
0982                                              static_cast<double>(fee),
0983                                              static_cast<double>(exact_hits_for_bco));
0984     }
0985 
0986     exact_hit_count += exact_hits;
0987     if (exact_hits > 0)
0988     {
0989       assert(h_Run3TimeFrameExactHit_FEE);
0990       h_Run3TimeFrameExactHit_FEE->Fill(fee, exact_hits);
0991       exact_matched_fees.set(fee);
0992       continue;
0993     }
0994 
0995     const std::optional<uint32_t> fuzzy_fee_bco = find_fuzzy_fee_bco(*predicted_fee_bco, fee);
0996     if (!fuzzy_fee_bco)
0997     {
0998       continue;
0999     }
1000 
1001 
1002     const size_t fuzzy_hits = count_time_hits(*fuzzy_fee_bco, fee);
1003     if (fuzzy_hits == 0)
1004     {
1005       continue;
1006     }
1007 
1008     fallback_hit_count += fuzzy_hits;
1009     assert(h_Run3TimeFrameFuzzyHit_FEE);
1010     h_Run3TimeFrameFuzzyHit_FEE->Fill(fee, fuzzy_hits);
1011     assert(h_Run3_FEE_GTMMatching_ClockDiff);
1012     h_Run3_FEE_GTMMatching_ClockDiff->Fill(static_cast<double>(get_signed_fee_bco_diff(*fuzzy_fee_bco, *predicted_fee_bco)),
1013                                            static_cast<double>(fee),
1014                                            static_cast<double>(fuzzy_hits));
1015 
1016     if (m_verbosity >= 2)
1017     {
1018       std::cout << __PRETTY_FUNCTION__ << " - packet " << m_packet_id
1019                 << ": Run3 fuzzy FEE-clock fallback for fee " << fee
1020                 << " predicted 0x" << std::hex << *predicted_fee_bco
1021                 << " matched 0x" << *fuzzy_fee_bco << std::dec
1022                 << " diff " << get_signed_fee_bco_diff(*fuzzy_fee_bco, *predicted_fee_bco)
1023                 << " hits " << fuzzy_hits << std::endl;
1024     }
1025   }
1026 
1027   if (fallback_hit_count > 0)
1028   {
1029     m_hNorm->Fill("Run3_TimeFrame_FuzzyFallback", 1);
1030     m_hNorm->Fill("Run3_TimeFrame_FuzzyFallback_Hit_Sum", fallback_hit_count);
1031   }
1032 
1033   cache_waveform_adc(h_Run3PreviousTimeFrameWaveformADC, timeframe);
1034 
1035   // Track buffer usage after exact and fuzzy hit processing
1036   if (m_verbosity >= 2)
1037   {
1038     size_t total_time_hits_post_exact_fuzzy = 0;
1039     size_t time_hit_map_buckets_post_exact_fuzzy = 0;
1040     for (const auto& fee_time_hits : m_timeHitMap)
1041     {
1042       time_hit_map_buckets_post_exact_fuzzy += fee_time_hits.size();
1043       for (const auto& bucket : fee_time_hits)
1044       {
1045         total_time_hits_post_exact_fuzzy += bucket.second.size();
1046       }
1047     }
1048     size_t total_gtm_bco_trig_post = 0;
1049     size_t total_bco_heartbeat_post = 0;
1050     for (const auto& bco_info : m_bcoMatchingInformation_vec)
1051     {
1052       total_gtm_bco_trig_post += bco_info.get_gtm_bco_trig_list_size();
1053       total_bco_heartbeat_post += bco_info.get_bco_heartbeat_list_size();
1054     }
1055     std::cout << __PRETTY_FUNCTION__ << " - packet " << m_packet_id
1056               << ": [AFTER EXACT/FUZZY] STL buffer usage - exact_hits: " << exact_hit_count
1057               << ", fuzzy_hits: " << fallback_hit_count
1058               << ", timeframe size: " << timeframe.size()
1059               << ", m_timeFrameMap: " << m_timeFrameMap.size()
1060               << ", m_UsedTimeFrameSet: " << m_UsedTimeFrameSet.size()
1061               << ", m_timeHitMap: " << time_hit_map_buckets_post_exact_fuzzy << " buckets, "
1062               << total_time_hits_post_exact_fuzzy << " hits"
1063               << ", BcoMatchingInfo[gtm_trig: " << total_gtm_bco_trig_post
1064               << ", bco_heartbeat: " << total_bco_heartbeat_post << "]"
1065               << std::endl;
1066   }
1067 
1068   size_t recovered_hit_count = 0;
1069   for (uint16_t fee = 0; fee < MAX_FEECOUNT; ++fee)
1070   {
1071     if (!predicted_fee_bco_available.test(fee))
1072     {
1073       continue;
1074     }
1075 
1076     const size_t recovered_hits = recover_truncated_waveforms(predicted_fee_bcos[fee], fee, timeframe);
1077     if (recovered_hits == 0)
1078     {
1079       continue;
1080     }
1081 
1082     recovered_hit_count += recovered_hits;
1083     if (m_hNormTruncatedWaveformRecoveryFeeFirstBin > 0)
1084     {
1085       m_hNorm->Fill(static_cast<double>(m_hNormTruncatedWaveformRecoveryFeeFirstBin + fee), static_cast<double>(recovered_hits));
1086     }
1087   }
1088 
1089   if (m_verbosity >= 2 && recovered_hit_count > 0)
1090   {
1091     std::cout << __PRETTY_FUNCTION__ << "   - packet " << m_packet_id
1092               << ": Run3 truncated waveform recovery appended " << recovered_hit_count
1093               << " later hit segments for gtm_bco: 0x" << std::hex << gtm_bco << std::dec << std::endl;
1094   }
1095 
1096   // Track buffer usage after recovery
1097   if (m_verbosity >= 2)
1098   {
1099     size_t total_time_hits_post_recovery = 0;
1100     size_t time_hit_map_buckets_post_recovery = 0;
1101     for (const auto& fee_time_hits : m_timeHitMap)
1102     {
1103       time_hit_map_buckets_post_recovery += fee_time_hits.size();
1104       for (const auto& bucket : fee_time_hits)
1105       {
1106         total_time_hits_post_recovery += bucket.second.size();
1107       }
1108     }
1109     size_t total_gtm_bco_trig_recovery = 0;
1110     size_t total_bco_heartbeat_recovery = 0;
1111     for (const auto& bco_info : m_bcoMatchingInformation_vec)
1112     {
1113       total_gtm_bco_trig_recovery += bco_info.get_gtm_bco_trig_list_size();
1114       total_bco_heartbeat_recovery += bco_info.get_bco_heartbeat_list_size();
1115     }
1116     std::cout << __PRETTY_FUNCTION__ << " - packet " << m_packet_id
1117               << ": [AFTER RECOVERY] STL buffer usage - timeframe size: " << timeframe.size()
1118               << ", recovered_hits: " << recovered_hit_count
1119               << ", m_timeFrameMap: " << m_timeFrameMap.size()
1120               << ", m_UsedTimeFrameSet: " << m_UsedTimeFrameSet.size()
1121               << ", m_timeHitMap: " << time_hit_map_buckets_post_recovery << " buckets, "
1122               << total_time_hits_post_recovery << " hits"
1123               << ", BcoMatchingInfo[gtm_trig: " << total_gtm_bco_trig_recovery
1124               << ", bco_heartbeat: " << total_bco_heartbeat_recovery << "]"
1125               << std::endl;
1126   }
1127 
1128   if (timeframe.empty())
1129   {
1130     if (m_verbosity >= 1)
1131     {
1132       std::cout << __PRETTY_FUNCTION__ << " - packet " << m_packet_id
1133                 << ":ERROR: Run3 FEE-clock match failed for gtm_bco: 0x" << std::hex << gtm_bco << std::dec
1134                 << " bclk_rollover_corrected 0x" << std::hex << bclk_rollover_corrected << std::dec
1135                 << ". m_timeHitMap size: " << time_hit_bucket_count() << std::endl;
1136     }
1137 
1138     if (m_verbosity >= 2)
1139     {
1140       size_t total_time_hits_empty = 0;
1141       size_t time_hit_map_buckets_empty = 0;
1142       for (const auto& fee_time_hits : m_timeHitMap)
1143       {
1144         time_hit_map_buckets_empty += fee_time_hits.size();
1145         for (const auto& bucket : fee_time_hits)
1146         {
1147           total_time_hits_empty += bucket.second.size();
1148         }
1149       }
1150       size_t total_gtm_bco_trig_empty = 0;
1151       size_t total_bco_heartbeat_empty = 0;
1152       for (const auto& bco_info : m_bcoMatchingInformation_vec)
1153       {
1154         total_gtm_bco_trig_empty += bco_info.get_gtm_bco_trig_list_size();
1155         total_bco_heartbeat_empty += bco_info.get_bco_heartbeat_list_size();
1156       }
1157       std::cout << __PRETTY_FUNCTION__ << " - packet " << m_packet_id
1158                 << ": [EMPTY-FRAME ERROR] STL buffer usage - m_timeFrameMap: " << m_timeFrameMap.size()
1159                 << ", m_UsedTimeFrameSet: " << m_UsedTimeFrameSet.size()
1160                 << ", m_timeHitMap: " << time_hit_map_buckets_empty << " buckets, "
1161                 << total_time_hits_empty << " hits"
1162                 << ", BcoMatchingInfo[gtm_trig: " << total_gtm_bco_trig_empty
1163                 << ", bco_heartbeat: " << total_bco_heartbeat_empty << "]"
1164                 << std::endl;
1165     }
1166 
1167     m_hNorm->Fill("Run3_TimeFrame_MatchFailed", 1);
1168     m_hNorm->Fill("GTM_TimeFrame_Unmatched", 1);
1169     cache_timeframe_qa(bclk_rollover_corrected, timeframe, exact_matched_fees);
1170     m_timeFrameMap.erase(frame_it);
1171     static std::vector<TpcRawHit*> empty;
1172     return empty;
1173   }
1174 
1175   if (exact_hit_count > 0)
1176   {
1177     m_hNorm->Fill("Run3_TimeFrame_Exact_Matched", 1);
1178   }
1179   m_hNorm->Fill("GTM_TimeFrame_Matched", 1);
1180   assert(h_TimeFrame_Matched_Size);
1181   h_TimeFrame_Matched_Size->Fill(timeframe.size());
1182   m_hNorm->Fill("GTM_TimeFrame_Matched_Hit_Sum", timeframe.size());
1183   cache_timeframe_qa(bclk_rollover_corrected, timeframe, exact_matched_fees);
1184   m_UsedTimeFrameSet.push(bclk_rollover_corrected);
1185 
1186   // Track final buffer usage
1187   if (m_verbosity >= 2)
1188   {
1189     size_t total_time_hits_final = 0;
1190     size_t time_hit_map_buckets_final = 0;
1191     for (const auto& fee_time_hits : m_timeHitMap)
1192     {
1193       time_hit_map_buckets_final += fee_time_hits.size();
1194       for (const auto& bucket : fee_time_hits)
1195       {
1196         total_time_hits_final += bucket.second.size();
1197       }
1198     }
1199     size_t total_gtm_bco_trig_final = 0;
1200     size_t total_bco_heartbeat_final = 0;
1201     size_t total_gtm_bco_trigger_final = 0;
1202     size_t total_bco_matching_final = 0;
1203     for (const auto& bco_info : m_bcoMatchingInformation_vec)
1204     {
1205       total_gtm_bco_trig_final += bco_info.get_gtm_bco_trig_list_size();
1206       total_bco_heartbeat_final += bco_info.get_bco_heartbeat_list_size();
1207       total_gtm_bco_trigger_final += bco_info.get_gtm_bco_trigger_map_size();
1208       total_bco_matching_final += bco_info.get_bco_matching_list_size();
1209     }
1210     std::cout << __PRETTY_FUNCTION__ << " - packet " << m_packet_id
1211               << ": [FINAL] STL buffer usage - timeframe size: " << timeframe.size()
1212               << ", exact_hits: " << exact_hit_count
1213               << ", fuzzy_hits: " << fallback_hit_count
1214               << ", recovered_hits: " << recovered_hit_count
1215               << ", m_timeFrameMap: " << m_timeFrameMap.size()
1216               << ", m_UsedTimeFrameSet: " << m_UsedTimeFrameSet.size()
1217               << ", m_timeHitMap: " << time_hit_map_buckets_final << " buckets, "
1218               << total_time_hits_final << " hits"
1219               << ", BcoMatchingInfo[gtm_trig: " << total_gtm_bco_trig_final
1220               << ", bco_heartbeat: " << total_bco_heartbeat_final
1221               << ", gtm_trigger_map: " << total_gtm_bco_trigger_final
1222               << ", bco_matching: " << total_bco_matching_final
1223               << std::endl;
1224   }
1225 
1226   return timeframe;
1227 }
1228 
1229 void TpcTimeFrameBuilderRun3::CleanupUsedPackets(const uint64_t& bclk)
1230 {
1231   if (m_verbosity > 2)
1232   {
1233     std::cout << __PRETTY_FUNCTION__ << "   - packet " << m_packet_id << ": cleaning up bcos < 0x" << std::hex
1234               << bclk << std::dec 
1235               << " and m_UsedTimeFrameSet size: " << m_UsedTimeFrameSet.size()
1236               << std::endl;
1237   }
1238 
1239   while (!m_UsedTimeFrameSet.empty())
1240   {
1241     const uint64_t bco_completed = m_UsedTimeFrameSet.front();
1242     m_UsedTimeFrameSet.pop();
1243 
1244     auto it = m_timeFrameMap.find(bco_completed);
1245     if (it != m_timeFrameMap.end())
1246     {
1247       while (!it->second.empty())
1248       {
1249         TpcRawHit* hit = it->second.back();
1250         delete hit;
1251         it->second.pop_back();
1252       }
1253       m_timeFrameMap.erase(it);
1254     }
1255   }
1256 
1257   const uint64_t bclk_rollover_corrected = m_bcoMatchingInformation_vec[0].get_gtm_rollover_correction(bclk);
1258   cleanup_time_hit_map(bclk_rollover_corrected, 0);
1259 }
1260 
1261 int TpcTimeFrameBuilderRun3::ProcessPacket(Packet* packet)
1262 {
1263   static size_t call_count = 0;
1264   ++call_count;
1265 
1266   if (m_verbosity > 1)
1267   {
1268     std::cout << "TpcTimeFrameBuilderRun3::ProcessPacket: " << m_packet_id
1269               << "\t- Entry " << std::endl;
1270   }
1271 
1272   if (!packet)
1273   {
1274     std::cout << __PRETTY_FUNCTION__ << "\t- Error : Invalid packet, doing nothing" << std::endl;
1275     assert(packet);
1276     return 0;
1277   }
1278 
1279   const int packet_hit_format = packet->getHitFormat();
1280   if (packet_hit_format != IDTPCFEEV5 && packet_hit_format != IDTPCFEEV6)
1281   {
1282     std::cout << __PRETTY_FUNCTION__
1283               << "\t- Error : TpcTimeFrameBuilderRun3 only supports packet formats " << IDTPCFEEV5
1284               << " or " << IDTPCFEEV6
1285               << " but received packet format " << packet_hit_format
1286               << ". Aborting run." << std::endl;
1287     packet->identify();
1288     return Fun4AllReturnCodes::ABORTRUN;
1289   }
1290 
1291   if (m_hitFormat < 0)
1292   {
1293     m_hitFormat = packet_hit_format;
1294   }
1295   else if (packet_hit_format != m_hitFormat)
1296   {
1297     std::cout << __PRETTY_FUNCTION__ << "\t- Error : packet format changed for packet " << m_packet_id
1298               << " from " << m_hitFormat << " to " << packet_hit_format
1299               << ". Aborting run." << std::endl;
1300     packet->identify();
1301     return Fun4AllReturnCodes::ABORTRUN;
1302   }
1303   assert((packet_hit_format == m_hitFormat));
1304 
1305 
1306   if (m_packet_id != packet->getIdentifier())
1307   {
1308     std::cout << __PRETTY_FUNCTION__ << "\t- Error : mismatched packet with packet ID expectation of " << m_packet_id << ", but received";
1309     packet->identify();
1310     assert(m_packet_id == packet->getIdentifier());
1311     return 0;
1312   }
1313 
1314   assert(m_packetTimer);
1315   if ((m_verbosity == 1 && (call_count % 1000) == 0) || (m_verbosity > 1))
1316   {
1317     std::cout << __PRETTY_FUNCTION__ << "\t- : received packet ";
1318     packet->identify();
1319 
1320     m_packetTimer->print_stat();
1321   }
1322   m_packetTimer->restart();
1323 
1324   // Track initial buffer usage at start of ProcessPacket
1325   if (m_verbosity >= 2)
1326   {
1327     size_t total_time_hits = 0;
1328     size_t time_hit_map_buckets = 0;
1329     for (const auto& fee_time_hits : m_timeHitMap)
1330     {
1331       time_hit_map_buckets += fee_time_hits.size();
1332       for (const auto& bucket : fee_time_hits)
1333       {
1334         total_time_hits += bucket.second.size();
1335       }
1336     }
1337     size_t total_fee_data = 0;
1338     for (const auto& fee_data_deque : m_feeData)
1339     {
1340       total_fee_data += fee_data_deque.size();
1341     }
1342     size_t total_gtm_bco_trig = 0;
1343     size_t total_bco_heartbeat = 0;
1344     size_t total_gtm_bco_trigger = 0;
1345     size_t total_bco_matching = 0;
1346     for (const auto& bco_info : m_bcoMatchingInformation_vec)
1347     {
1348       total_gtm_bco_trig += bco_info.get_gtm_bco_trig_list_size();
1349       total_bco_heartbeat += bco_info.get_bco_heartbeat_list_size();
1350       total_gtm_bco_trigger += bco_info.get_gtm_bco_trigger_map_size();
1351       total_bco_matching += bco_info.get_bco_matching_list_size();
1352     }
1353     std::cout << __PRETTY_FUNCTION__ << " - packet " << m_packet_id
1354               << ": [INITIAL] STL buffer usage - m_timeFrameMap: " << m_timeFrameMap.size()
1355               << ", m_UsedTimeFrameSet: " << m_UsedTimeFrameSet.size()
1356               << ", m_feeData total: " << total_fee_data
1357               << ", m_timeHitMap: " << time_hit_map_buckets << " buckets, " << total_time_hits << " hits"
1358               << ", BcoMatchingInfo[gtm_trig: " << total_gtm_bco_trig
1359               << ", bco_heartbeat: " << total_bco_heartbeat
1360               << ", gtm_trigger_map: " << total_gtm_bco_trigger
1361               << ", bco_matching: " << total_bco_matching
1362               << std::endl;
1363   }
1364 
1365   // //remove after testing
1366   // ;
1367   // std::cout <<"packet->lValue(0, N_TAGGER) = "<<packet->lValue(0, "N_TAGGER")<<std::endl;
1368   // std::cout <<"packet->lValue(0, NR_WF) = "<<packet->iValue(0, "NR_WF")<<std::endl;
1369 
1370   Fun4AllHistoManager* hm = QAHistManagerDef::getHistoManager();
1371   assert(hm);
1372   assert(m_hNorm);
1373   m_hNorm->Fill("Packet", 1);
1374 
1375   int data_length = packet->getDataLength();  // 32bit length
1376   assert(h_PacketLength);
1377   h_PacketLength->Fill(data_length);
1378 
1379   int data_padding = packet->getPadding();  // 32bit padding
1380   assert(h_PacketLength_Padding);
1381   h_PacketLength_Padding->Fill(data_padding);
1382   if (data_padding != 0)
1383   {
1384     std::cout << __PRETTY_FUNCTION__ << "\t- : Warning : suspecious padding "
1385               << data_padding << "\t- in packet " << m_packet_id << ":" << std::endl;
1386     packet->identify();
1387     // packet->dump();
1388   }
1389 
1390   size_t dma_words_buffer = static_cast<size_t>(data_length) * 2 / DAM_DMA_WORD_LENGTH + 1;
1391   std::vector<dma_word> buffer(dma_words_buffer);
1392 
1393   int l2 = 0;
1394   packet->fillIntArray(reinterpret_cast<int*>(buffer.data()), data_length + DAM_DMA_WORD_LENGTH / 2, &l2, "DATA");
1395 
1396   if (data_padding != 0)
1397   {
1398     std::cout << __PRETTY_FUNCTION__ << "\t- :  data_length = " << data_length
1399               << "\t- data_padding = " << data_padding << "\t l2 = " << l2 << "\t- in packet " << m_packet_id << ":" << std::endl;
1400   }
1401 
1402   assert(l2 <= data_length);
1403 
1404   if (l2 < data_padding)
1405   {
1406     std::cout << __PRETTY_FUNCTION__ << "\t- : Error : l2 from fillIntArray() is smaller than padding suggesting an invalid data: " << l2
1407               << "\t- in packet " << m_packet_id << ". Data length: " << data_length
1408               << ", data padding: " << data_padding << ". Ignore this packet: " << std::endl;
1409     packet->identify();
1410     return Fun4AllReturnCodes::DISCARDEVENT;
1411   }
1412   l2 -= data_padding;
1413 
1414   assert(l2 >= 0);
1415 
1416   size_t dma_words = static_cast<size_t>(l2) * 2 / DAM_DMA_WORD_LENGTH;
1417   size_t dma_residual = (static_cast<size_t>(l2) * 2) % DAM_DMA_WORD_LENGTH;
1418   assert(dma_words <= buffer.size());
1419   assert(h_PacketLength_Residual);
1420   h_PacketLength_Residual->Fill(dma_residual);
1421   if (dma_residual > 0)
1422   {
1423     std::cout << __PRETTY_FUNCTION__ << "\t- : Warning : mismatch of RCDAQ data to DMA transfer. Dropping mismatched data: "
1424               << dma_residual << "\t- in packet " << m_packet_id << ". Dropping residual data : " << std::endl;
1425 
1426     assert(dma_words + 1 < buffer.size());
1427     const dma_word& last_dma_word_data = buffer[dma_words + 1];
1428     const uint16_t* last_dma_word = reinterpret_cast<const uint16_t*>(&last_dma_word_data);
1429 
1430     for (size_t i = 0; i < dma_residual; ++i)
1431     {
1432       std::cout << "\t- 0x" << std::hex << last_dma_word[i] << std::dec;
1433     }
1434     std::cout << std::endl;
1435   }
1436 
1437   if (m_verbosity > 1)
1438   {
1439     std::cout << __PRETTY_FUNCTION__ << "\t- : packet" << m_packet_id << std::endl
1440               << "\t-   data_length = " << data_length << std::endl
1441               << "\t-   data_padding = " << data_padding << std::endl
1442               << "\t-   dma_words_buffer = " << dma_words_buffer << std::endl
1443               << "\t-   l2 = " << l2 << std::endl
1444               << "\t-   dma_words = " << dma_words << std::endl;
1445   }
1446 
1447   // demultiplexer
1448   for (size_t index = 0; index < dma_words; ++index)
1449   {
1450     const dma_word& dma_word_data = buffer[index];
1451 
1452     if (m_verbosity > 2)
1453     {
1454       std::cout << __PRETTY_FUNCTION__ << "\t- : processing DMA word "
1455                 << index << "/" << dma_words << "\t- with header 0x"
1456                 << std::hex << dma_word_data.dma_header << std::dec << std::endl;
1457     }
1458 
1459     if ((dma_word_data.dma_header & 0xFF00U) == FEE_MAGIC_KEY)
1460     {
1461       unsigned int fee_id = dma_word_data.dma_header & 0xffU;
1462 
1463       // for packet id 4XYZ ebdc is XY, endpoint is Z
1464       if (m_maskedFEEs[((m_packet_id / 10) % 100)].contains(fee_id))
1465       {
1466         continue;
1467       }
1468 
1469       if (fee_id < MAX_FEECOUNT)
1470       {
1471         for (const uint16_t& i : dma_word_data.data)
1472         {
1473           m_feeData[fee_id].push_back(i);
1474         }
1475         m_hNorm->Fill("DMA_WORD_FEE", 1);
1476 
1477         // immediate fee buffer processing to reduce memory consuption
1478         process_fee_data(fee_id);
1479       }
1480       else
1481       {
1482         std::cout << __PRETTY_FUNCTION__ << "\t- : Error : Invalid FEE ID " << fee_id << "\t- at position " << index << std::endl;
1483         index += DAM_DMA_WORD_LENGTH - 1;
1484         m_hNorm->Fill("DMA_WORD_FEE_INVALID", 1);
1485       }
1486     }
1487 
1488     else if ((dma_word_data.dma_header & 0xFF00U) == GTM_MAGIC_KEY)
1489     {
1490       decode_gtm_data(dma_word_data);
1491       m_hNorm->Fill("DMA_WORD_GTM", 1);
1492     }
1493     else
1494     {
1495       std::cout << __PRETTY_FUNCTION__ << "\t- : Error : Unknown data type at position " << index << ": "
1496                 << std::hex << buffer[index].dma_header << std::dec << std::endl;
1497       // not FEE data, e.g. GTM data or other stream, to be decoded
1498       m_hNorm->Fill("DMA_WORD_INVALID", 1);
1499     }
1500   }
1501 
1502   // Track buffer usage after DMA word processing
1503   if (m_verbosity >= 2)
1504   {
1505     size_t total_time_hits_post = 0;
1506     size_t time_hit_map_buckets_post = 0;
1507     for (const auto& fee_time_hits : m_timeHitMap)
1508     {
1509       time_hit_map_buckets_post += fee_time_hits.size();
1510       for (const auto& bucket : fee_time_hits)
1511       {
1512         total_time_hits_post += bucket.second.size();
1513       }
1514     }
1515     size_t total_fee_data_post = 0;
1516     for (const auto& fee_data_deque : m_feeData)
1517     {
1518       total_fee_data_post += fee_data_deque.size();
1519     }
1520     size_t total_gtm_bco_trig_post = 0;
1521     size_t total_bco_heartbeat_post = 0;
1522     for (const auto& bco_info : m_bcoMatchingInformation_vec)
1523     {
1524       total_gtm_bco_trig_post += bco_info.get_gtm_bco_trig_list_size();
1525       total_bco_heartbeat_post += bco_info.get_bco_heartbeat_list_size();
1526     }
1527     std::cout << __PRETTY_FUNCTION__ << " - packet " << m_packet_id
1528               << ": [AFTER DMA PROCESSING] STL buffer usage - m_feeData total: " << total_fee_data_post
1529               << ", m_timeHitMap: " << time_hit_map_buckets_post << " buckets, " << total_time_hits_post << " hits"
1530               << ", m_timeFrameMap: " << m_timeFrameMap.size()
1531               << ", m_UsedTimeFrameSet: " << m_UsedTimeFrameSet.size()
1532               << ", BcoMatchingInfo[gtm_trig: " << total_gtm_bco_trig_post
1533               << ", bco_heartbeat: " << total_bco_heartbeat_post << "]"
1534               << std::endl;
1535   }
1536 
1537   // sanity check for the cached FEE-clock hit size
1538   for (size_t fee = 0; fee < m_timeHitMap.size(); ++fee)
1539   {
1540     auto& fee_time_hits = m_timeHitMap[fee];
1541     for (auto timehit = fee_time_hits.begin(); timehit != fee_time_hits.end();)
1542     {
1543       if (timehit->second.size() > kMaxRawHitLimit)
1544       {
1545         std::cout << __PRETTY_FUNCTION__ << "\t- : Warning : impossible amount of hits for FEE "
1546                   << fee << " at FEE BCO " << timehit->first << "\t- : " << timehit->second.size()
1547                   << ", limit is " << kMaxRawHitLimit
1548                   << ". Dropping this FEE-clock cache!"
1549                   << std::endl;
1550         m_hNorm->Fill("TimeFrameSizeLimitError", 1);
1551 
1552         for (TpcRawHit* hit : timehit->second)
1553         {
1554           delete hit;
1555         }
1556         timehit = fee_time_hits.erase(timehit);
1557       }
1558       else
1559       {
1560         ++timehit;
1561       }
1562     }
1563   }
1564 
1565   m_packetTimer->stop();
1566   assert(h_ProcessPacket_Time);
1567   h_ProcessPacket_Time->Fill(call_count, m_packetTimer->elapsed());
1568 
1569   // Track final buffer usage at end of ProcessPacket
1570   if (m_verbosity >= 1)
1571   {
1572     size_t total_time_hits_final = 0;
1573     size_t time_hit_map_buckets_final = 0;
1574     for (const auto& fee_time_hits : m_timeHitMap)
1575     {
1576       time_hit_map_buckets_final += fee_time_hits.size();
1577       for (const auto& bucket : fee_time_hits)
1578       {
1579         total_time_hits_final += bucket.second.size();
1580       }
1581     }
1582     size_t total_fee_data_final = 0;
1583     for (const auto& fee_data_deque : m_feeData)
1584     {
1585       total_fee_data_final += fee_data_deque.size();
1586     }
1587     size_t total_gtm_bco_trig_final = 0;
1588     size_t total_bco_heartbeat_final = 0;
1589     size_t total_gtm_bco_trigger_final = 0;
1590     size_t total_bco_matching_final = 0;
1591     for (const auto& bco_info : m_bcoMatchingInformation_vec)
1592     {
1593       total_gtm_bco_trig_final += bco_info.get_gtm_bco_trig_list_size();
1594       total_bco_heartbeat_final += bco_info.get_bco_heartbeat_list_size();
1595       total_gtm_bco_trigger_final += bco_info.get_gtm_bco_trigger_map_size();
1596       total_bco_matching_final += bco_info.get_bco_matching_list_size();
1597     }
1598     std::cout << __PRETTY_FUNCTION__ << " - packet " << m_packet_id
1599               << ": [FINAL] STL buffer usage - m_timeFrameMap: " << m_timeFrameMap.size()
1600               << ", m_UsedTimeFrameSet: " << m_UsedTimeFrameSet.size()
1601               << ", m_feeData total: " << total_fee_data_final
1602               << ", m_timeHitMap: " << time_hit_map_buckets_final << " buckets, " << total_time_hits_final << " hits"
1603               << ", BcoMatchingInfo[gtm_trig: " << total_gtm_bco_trig_final
1604               << ", bco_heartbeat: " << total_bco_heartbeat_final
1605               << ", gtm_trigger_map: " << total_gtm_bco_trigger_final
1606               << ", bco_matching: " << total_bco_matching_final
1607               << std::endl;
1608   }
1609 
1610   return Fun4AllReturnCodes::EVENT_OK;
1611 }
1612 
1613 int TpcTimeFrameBuilderRun3::process_fee_data(unsigned int fee)
1614 {
1615   assert(m_hFEEDataStream);
1616 
1617   if (m_verbosity > 2)
1618   {
1619     std::cout << __PRETTY_FUNCTION__ << "\t- : processing FEE " << fee << "\t- with " << m_feeData[fee].size() << "\t- words" << std::endl;
1620   }
1621 
1622   assert(fee < m_feeData.size());
1623   std::deque<uint16_t>& data_buffer = m_feeData[fee];
1624 
1625   while (HEADER_LENGTH <= data_buffer.size())
1626   {
1627     // packet loop
1628 
1629     bool is_digital_current = false;
1630     // test if digital current packet
1631     if (data_buffer[3] == FEE_PACKET_MAGIC_KEY_3_DC)
1632     {
1633       if (m_verbosity > 2)
1634       {
1635         std::cout << __PRETTY_FUNCTION__
1636                   << "\t- : processing FEE " << fee
1637                   << "\t- with digital packet" << std::endl;
1638       }
1639 
1640       m_hFEEDataStream->Fill(fee, "WordDigitalCurrentKeyWord", 1);
1641       is_digital_current = true;
1642     }  //     if (data_buffer[3] == FEE_PACKET_MAGIC_KEY_3)
1643     else
1644     {
1645       if (data_buffer[1] != FEE_PACKET_MAGIC_KEY_1)
1646       {
1647         if (m_verbosity > 1)
1648         {
1649           std::cout << __PRETTY_FUNCTION__ << "\t- : Error : Invalid FEE magic key at position 1 0x" << std::hex << data_buffer[1] << std::dec << std::endl;
1650         }
1651         m_hFEEDataStream->Fill(fee, "WordSkipped", 1);
1652         data_buffer.pop_front();
1653         continue;
1654       }
1655       assert(data_buffer[1] == FEE_PACKET_MAGIC_KEY_1);
1656 
1657       if (data_buffer[2] != FEE_PACKET_MAGIC_KEY_2)
1658       {
1659         if (m_verbosity > 1)
1660         {
1661           std::cout << __PRETTY_FUNCTION__ << "\t- : Error : Invalid FEE magic key at position 2 0x" << std::hex << data_buffer[2] << std::dec << std::endl;
1662         }
1663         m_hFEEDataStream->Fill(fee, "WordSkipped", 1);
1664         data_buffer.pop_front();
1665         continue;
1666       }
1667       assert(data_buffer[2] == FEE_PACKET_MAGIC_KEY_2);
1668     }
1669 
1670     // valid packet
1671     const uint16_t pkt_length = data_buffer[0];  // this is indeed the number of 10-bit words + 5 in this packet
1672     if (pkt_length > MAX_PACKET_LENGTH)
1673     {
1674       if (m_verbosity > 1)
1675       {
1676         std::cout << __PRETTY_FUNCTION__ << "\t- : Error : Invalid FEE pkt_length " << pkt_length << std::endl;
1677       }
1678       m_hFEEDataStream->Fill(fee, "InvalidLength", 1);
1679       data_buffer.pop_front();
1680       continue;
1681     }
1682 
1683     if (pkt_length + 1U > data_buffer.size())
1684     {
1685       if (m_verbosity > 2)
1686       {
1687         std::cout << __PRETTY_FUNCTION__ << "\t- : packet over buffer boundary for now, skip decoding and wait for more data: "
1688                                             " pkt_length = "
1689                   << pkt_length
1690                   << "\t- data_buffer.size() = " << data_buffer.size()
1691                   << std::endl;
1692       }
1693       break;
1694     }
1695 
1696     if (is_digital_current)
1697     {
1698       process_fee_data_digital_current(fee, data_buffer);
1699     }
1700     else
1701     {
1702       process_fee_data_waveform(fee, data_buffer);
1703     }
1704     data_buffer.erase(data_buffer.begin(), data_buffer.begin() + pkt_length + 1);
1705     m_hFEEDataStream->Fill(fee, "WordValid", pkt_length + 1);
1706 
1707   }  //     while (HEADER_LENGTH < data_buffer.size())
1708 
1709   return Fun4AllReturnCodes::EVENT_OK;
1710 }
1711 
1712 void TpcTimeFrameBuilderRun3::process_fee_data_waveform(const unsigned int& fee, std::deque<uint16_t>& data_buffer)
1713 {
1714   const uint16_t& pkt_length = data_buffer[0];
1715 
1716   fee_payload payload;
1717   // continue the decoding
1718   payload.fee_id = fee;
1719   payload.adc_length = data_buffer[0] - HEADER_LENGTH;  // this is indeed the number of 10-bit words in this packet
1720   payload.data_parity = data_buffer[4] >> 9U;
1721   payload.sampa_address = static_cast<uint16_t>(data_buffer[4] >> 5U) & 0xfU;
1722   payload.sampa_channel = data_buffer[4] & 0x1fU;
1723   payload.channel = data_buffer[4] & 0x1ffU;
1724   payload.type = static_cast<uint16_t>(data_buffer[3] >> 7U) & 0x7U;
1725   payload.user_word = data_buffer[3] & 0x7fU;
1726   payload.bx_timestamp = static_cast<uint32_t>(static_cast<uint32_t>(data_buffer[6] & 0x3ffU) << 10U) | (data_buffer[5] & 0x3ffU);
1727   payload.data_crc = data_buffer[pkt_length];
1728 
1729   if (!m_fastBCOSkip)
1730   {
1731     auto crc_parity = crc16_parity(fee, pkt_length);
1732     payload.calc_crc = crc_parity.first;
1733     payload.calc_parity = crc_parity.second;
1734 
1735     if (payload.data_crc != payload.calc_crc)
1736     {
1737       if (m_verbosity > 2)
1738       {
1739         std::cout << __PRETTY_FUNCTION__ << "\t- : CRC error in FEE "
1740                   << fee << "\t- at position " << pkt_length - 1
1741                   << ": data_crc = " << payload.data_crc
1742                   << "\t- calc_crc = " << payload.calc_crc << std::endl;
1743       }
1744       m_hFEEDataStream->Fill(fee, "HitCRCError", 1);
1745       // continue;
1746     }
1747 
1748     if (payload.data_parity != payload.calc_parity)
1749     {
1750       if (m_verbosity > 2)
1751       {
1752         std::cout << __PRETTY_FUNCTION__ << "\t- : parity error in FEE "
1753                   << fee << "\t- at position " << pkt_length - 1
1754                   << ": data_parity = " << payload.data_parity
1755                   << "\t- calc_parity = " << payload.calc_parity << std::endl;
1756       }
1757       m_hFEEDataStream->Fill(fee, "ParityError", 1);
1758       // continue;
1759     }
1760   }  //     if (not m_fastBCOSkip)
1761 
1762   assert(fee < m_bcoMatchingInformation_vec.size());
1763   BcoMatchingInformation& m_bcoMatchingInformation = m_bcoMatchingInformation_vec[fee];
1764   // gtm_bco matching
1765   if (payload.type == TpcTimeFrameBuilderRun3::BcoMatchingInformation::HEARTBEAT_T)
1766   {
1767     if (m_verbosity > 1)
1768     {
1769       std::cout << __PRETTY_FUNCTION__
1770                 << "\t- : received heartbeat packet from FEE " << fee << std::endl;
1771     }
1772 
1773     // if bco matching information is still not verified, drop the packet
1774     if (!m_bcoMatchingInformation.is_verified())
1775     {
1776       m_hFEEDataStream->Fill(fee, "PacketHeartBeatClockSyncUnavailable", 1);
1777 
1778       if (m_verbosity > 1)
1779       {
1780         std::cout << "TpcTimeFrameBuilderRun3::process_fee_data - bco_matching not verified for heart beat, dropping packet" << std::endl;
1781         m_bcoMatchingInformation.print_gtm_bco_information();
1782       }
1783     }
1784     else  //       if (not m_bcoMatchingInformation.is_verified())
1785     {
1786       const std::optional<uint64_t> result = m_bcoMatchingInformation.find_reference_heartbeat(payload);
1787       m_hFEEDataStream->Fill(fee, "PacketHeartBeat", 1);
1788 
1789       if (result)
1790       {
1791         // assign gtm bco
1792         payload.gtm_bco = result.value();
1793         payload.has_clock_sync = true;
1794         m_hFEEDataStream->Fill(fee, "PacketHeartBeatClockSyncOK", 1);
1795 
1796         assert(m_hFEESAMPAHeartBeatSync);
1797         m_hFEESAMPAHeartBeatSync->Fill(fee * MAX_SAMPA + payload.sampa_address, 1);
1798       }
1799       else
1800       {
1801         m_hFEEDataStream->Fill(fee, "PacketHeartBeatClockSyncError", 1);
1802 
1803         // skip the waverform
1804       }
1805       if (m_verbosity > 2)
1806       {
1807         m_bcoMatchingInformation.print_gtm_bco_information();
1808       }
1809     }
1810   }
1811   else if (!m_fastBCOSkip)  //     if (payload.type == m_bcoMatchingInformation.HEARTBEAT_T)
1812   {
1813     m_hFEEChannelPacketCount->Fill(fee * MAX_CHANNELS + payload.channel, 1);
1814 
1815     // if bco matching information is still not verified, drop the packet
1816     if (!m_bcoMatchingInformation.is_verified())
1817     {
1818       m_hFEEDataStream->Fill(fee, "PacketClockSyncUnavailable", 1);
1819 
1820       if (m_verbosity > 1)
1821       {
1822         std::cout << "TpcTimeFrameBuilderRun3::process_fee_data - bco_matching not verified, dropping packet" << std::endl;
1823         m_bcoMatchingInformation.print_gtm_bco_information();
1824       }
1825     }
1826     else
1827     {
1828       payload.has_clock_sync = true;
1829       m_hFEEDataStream->Fill(fee, "PacketClockSyncOK", 1);
1830     }
1831   }
1832 
1833   if (m_verbosity > 2)
1834   {
1835     std::cout << __PRETTY_FUNCTION__ << "\t- : received data packet "
1836               << "\t- from FEE " << fee << std::endl
1837               << "\t- pkt_length = " << pkt_length << std::endl
1838               << "\t- type = " << payload.type << std::endl
1839               << "\t- adc_length = " << payload.adc_length << std::endl
1840               << "\t- sampa_address = " << payload.sampa_address << std::endl
1841               << "\t- sampa_channel = " << payload.sampa_channel << std::endl
1842               << "\t- channel = " << payload.channel << std::endl
1843               << "\t- bx_timestamp = 0x" << std::hex << payload.bx_timestamp << std::dec << std::endl
1844               << "\t- bco = 0x" << std::hex << payload.gtm_bco << std::dec << std::endl
1845               << "\t- data_crc = 0x" << std::hex << payload.data_crc << std::dec << std::endl
1846               << "\t- calc_crc = 0x" << std::hex << payload.calc_crc << std::dec << std::endl
1847               << "\t- data_parity = 0x" << std::hex << payload.data_parity << std::dec << std::endl
1848               << "\t- calc_parity = 0x" << std::hex << payload.calc_parity << std::dec << std::endl;
1849   }
1850 
1851   if ((!m_fastBCOSkip) && payload.has_clock_sync)
1852   {
1853     m_hFEEDataStream->Fill(fee, "RawHit", 1);
1854 
1855     // Format is (N sample) (start time), (1st sample)... (Nth sample)
1856     size_t pos = HEADER_LENGTH;
1857     std::deque<uint16_t>::const_iterator data_buffer_iterator = data_buffer.cbegin();
1858     std::advance(data_buffer_iterator, pos);
1859     while (pos + 2 < pkt_length)
1860     {
1861       const uint16_t& nsamp = *data_buffer_iterator;
1862       ++pos;
1863       ++data_buffer_iterator;
1864       const uint16_t& start_t = *data_buffer_iterator;
1865       ++pos;
1866       ++data_buffer_iterator;
1867       if (m_verbosity > 3)
1868       {
1869         std::cout << __PRETTY_FUNCTION__ << ": nsamp: " << nsamp
1870                   << "+ pos: " << pos
1871                   << " pkt_length: " << pkt_length << " start_t:" << start_t << std::endl;
1872       }
1873 
1874       if (pos + nsamp > pkt_length)
1875       {
1876         if (m_verbosity > 1)
1877         {
1878           std::cout << __PRETTY_FUNCTION__ << ": WARNING : nsamp: " << nsamp
1879                     << "+ pos: " << pos
1880                     << " > pkt_length: " << pkt_length << ", format error over length: " << std::endl;
1881 
1882           for (int print_pos = 0; print_pos <= pkt_length; ++print_pos)
1883           {
1884             std::cout << "\t[" << print_pos << "]=0x" << std::hex << data_buffer[print_pos] << std::dec << "(" << data_buffer[print_pos] << ")";
1885           }
1886           std::cout << std::endl;
1887         }
1888         m_hFEEDataStream->Fill(fee, "HitFormatErrorOverLength", 1);
1889 
1890         break;
1891       }
1892 
1893       const unsigned int fee_sampa_address = fee * MAX_SAMPA + payload.sampa_address;
1894       std::vector<uint16_t> adc(nsamp);
1895       for (int j = 0; j < nsamp; j++)
1896       {
1897         const uint16_t& adc_value = *data_buffer_iterator;
1898 
1899         adc[j] = adc_value;
1900         m_hFEESAMPAADC->Fill(start_t + j, fee_sampa_address, adc_value);
1901 
1902         ++pos;
1903         ++data_buffer_iterator;  // data_buffer[pos++];
1904       }
1905       payload.waveforms.emplace_back(start_t, std::move(adc));
1906 
1907       //   // an exception to deal with the last sample that is missing in the current hit format
1908       //   if (pos + 1 == pkt_length) break;
1909     }
1910 
1911     if (pos != pkt_length)
1912     {
1913       if (m_verbosity > 1)
1914       {
1915         std::cout << __PRETTY_FUNCTION__ << ": WARNING : residual data at the end of decoding:"
1916                   << " pos: " << pos
1917                   << " <pkt_length: " << pkt_length << ", format error under length" << std::endl;
1918       }
1919       m_hFEEDataStream->Fill(fee, "HitFormatErrorMismatchedLength", 1);
1920     }
1921 
1922     // valid packet in the buffer, create a new hit
1923     if (payload.type != TpcTimeFrameBuilderRun3::BcoMatchingInformation::HEARTBEAT_T)
1924     {
1925       if (fee >= m_timeHitMap.size())
1926       {
1927         std::cout << __PRETTY_FUNCTION__ << ": ERROR : invalid FEE " << fee
1928                   << " for packet " << m_packet_id << ". Dropping waveform hit." << std::endl;
1929         return;
1930       }
1931 
1932       TpcRawHitRun3_typ* hit = new TpcRawHitRun3_typ();
1933 
1934       hit->set_bco(payload.bx_timestamp);
1935       hit->set_packetid(m_packet_id);
1936       hit->set_fee(fee);
1937       hit->set_channel(payload.channel);
1938       hit->set_type(payload.type);
1939       // hit->set_checksum(payload.data_crc);
1940       hit->set_checksumerror(payload.data_crc != payload.calc_crc);
1941       // hit->set_parity(payload.data_parity);
1942       hit->set_parityerror(payload.data_parity != payload.calc_parity);
1943 
1944       for (std::pair<uint16_t, std::vector<uint16_t>>& waveform : payload.waveforms)
1945       {
1946         hit->move_adc_waveform(waveform.first, std::move(waveform.second));
1947       }
1948 
1949       m_timeHitMap[fee][payload.bx_timestamp & kFEEClockMask].push_back(hit);
1950     }
1951   }  //     if (not m_fastBCOSkip)
1952 
1953   return;
1954 }
1955 
1956 void TpcTimeFrameBuilderRun3::process_fee_data_digital_current(const unsigned int& fee, std::deque<uint16_t>& data_buffer)
1957 {
1958   if (m_verbosity > 2)
1959   {
1960     std::cout << __PRETTY_FUNCTION__ << "\t- : processing digital_current data " << std::endl;
1961   }
1962   m_hFEEDataStream->Fill(fee, "DigitalCurrent", 1);
1963   const uint16_t& pkt_length = data_buffer[0];
1964 
1965   if (pkt_length != HEADER_LENGTH + digital_current_payload::MAX_CHANNELS * 2 * 2)
1966   {
1967     if (m_verbosity > 1)
1968     {
1969       std::cout << __PRETTY_FUNCTION__ << "\t- : Error : Invalid FEE pkt_length " << pkt_length
1970                 << ", expected at least " << HEADER_LENGTH + digital_current_payload::MAX_CHANNELS * 2 * 2
1971                 << std::endl;
1972     }
1973     m_hFEEDataStream->Fill(fee, "DigitalCurrentFormatErrorMismatchedLength", 1);
1974     return;
1975   }
1976 
1977   digital_current_payload payload;
1978 
1979   payload.fee = fee;
1980   payload.pkt_length = pkt_length;
1981   payload.sampa_address = (data_buffer[4] >> 5U) & 0xfU;  // NOLINT(hicpp-signed-bitwise)
1982   // payload.sampa_max_channel = data_buffer[4] & 0x1fU;
1983   payload.channel = data_buffer[4] & 0x1ffU;
1984   // payload.type          = data_buffer[3];
1985   payload.bx_timestamp = ((data_buffer[6] & 0x3ffU) << 10U) | (data_buffer[5] & 0x3ff);  // NOLINT(hicpp-signed-bitwise)
1986 
1987   uint16_t pos = HEADER_LENGTH;
1988   for (int ich = 0; ich < digital_current_payload::MAX_CHANNELS; ich++)
1989   {
1990     payload.current[ich] = ((unsigned int) data_buffer[pos]) << 16U | ((unsigned int) data_buffer[pos + 1U]);
1991     pos++;
1992     pos++;
1993     payload.nsamples[ich] = ((unsigned int) data_buffer[pos]) << 16U | ((unsigned int) data_buffer[pos + 1U]);
1994     pos++;
1995     pos++;
1996   }
1997 
1998   if (pos != pkt_length)
1999   {
2000     if (m_verbosity > 1)
2001     {
2002       std::cout << __PRETTY_FUNCTION__ << "\t- : Warning : residual data at the end of decoding:"
2003                 << " pos: " << pos
2004                 << " <pkt_length: " << pkt_length << ", format error under length" << std::endl;
2005     }
2006   }
2007 
2008   payload.data_crc = data_buffer[pkt_length];
2009   auto crc_parity = crc16_parity(fee, pkt_length);
2010   payload.calc_crc = crc_parity.first;
2011   // payload.calc_parity = crc_parity.second;
2012 
2013   if (payload.data_crc != payload.calc_crc)
2014   {
2015     if (m_verbosity > 2)
2016     {
2017       std::cout << __PRETTY_FUNCTION__ << "\t- : CRC error in FEE "
2018                 << fee << "\t- at position " << pkt_length - 1
2019                 << ": data_crc = " << payload.data_crc
2020                 << "\t- calc_crc = " << payload.calc_crc << std::endl;
2021     }
2022     m_hFEEDataStream->Fill(fee, "DigitalCurrentCRCError", 1);
2023     // continue;
2024   }
2025 
2026   assert(fee < m_bcoMatchingInformation_vec.size());
2027   BcoMatchingInformation& m_bcoMatchingInformation = m_bcoMatchingInformation_vec[fee];
2028   std::tie(payload.gtm_bco, payload.bx_timestamp_predicted) = m_bcoMatchingInformation.find_dc_read_bco();
2029 
2030   if (m_verbosity > 2)
2031   {
2032     std::cout << __PRETTY_FUNCTION__ << "\t- : received digital current packet "
2033               << "\t- from FEE " << fee << std::endl
2034               << "\t- pkt_length = " << pkt_length << std::endl
2035               << "\t- sampa_address = " << payload.sampa_address << std::endl
2036               << "\t- channel = " << payload.channel << std::endl
2037               << "\t- bx_timestamp = 0x" << std::hex << payload.bx_timestamp << std::dec << std::endl
2038               << "\t- gtm_bco = 0x" << std::hex << payload.gtm_bco << std::dec << std::endl
2039               << "\t- bx_timestamp_predicted = 0x" << std::hex << payload.bx_timestamp_predicted << std::dec << std::endl;
2040 
2041     std::cout << "\t- current:";
2042     for (int ich = 0; ich < digital_current_payload::MAX_CHANNELS; ich++)
2043     {
2044       std::cout << "\t[" << ich << "] = " << payload.current[ich];
2045     }
2046     std::cout << std::endl;
2047     std::cout << "\t- nsamples:";
2048     for (int ich = 0; ich < digital_current_payload::MAX_CHANNELS; ich++)
2049     {
2050       std::cout << "\t[" << ich << "] = " << payload.nsamples[ich];
2051     }
2052     std::cout << std::endl;
2053     std::cout << "\t- data_crc = 0x" << std::hex << payload.data_crc << std::dec << std::endl
2054               << "\t- calc_crc = 0x" << std::hex << payload.calc_crc << std::dec << std::endl;
2055   }
2056 
2057   if (m_digitalCurrentDebugTTree)
2058   {
2059     m_digitalCurrentDebugTTree->fill(payload);
2060   }
2061 
2062   return;
2063 }
2064 
2065 void TpcTimeFrameBuilderRun3::SaveBXCounterSyncCDBTTree(const std::string& name)
2066 {
2067   m_bxCounterSyncCDBTTreeName = name;
2068 
2069   if (m_verbosity >= 1)
2070   {
2071     std::cout << __PRETTY_FUNCTION__ << "\t- : Saving BX counter sync CDB TTree to " << m_bxCounterSyncCDBTTreeName << std::endl;
2072   }
2073 }
2074 
2075 void TpcTimeFrameBuilderRun3::SaveDigitalCurrentDebugTTree(const std::string& name)
2076 {
2077   if (m_verbosity >= 1)
2078   {
2079     std::cout << __PRETTY_FUNCTION__ << "\t- : Saving digital current debug TTree to " << name << std::endl;
2080   }
2081 
2082   m_digitalCurrentDebugTTree = new TpcTimeFrameBuilderRun3::DigitalCurrentDebugTTree(name);
2083 }
2084 
2085 TpcTimeFrameBuilderRun3::DigitalCurrentDebugTTree::DigitalCurrentDebugTTree(const std::string& name)
2086   : m_name(name)
2087 {
2088   // open TFile
2089   PHTFileServer::open(m_name, "RECREATE");
2090 
2091   // cppcheck-suppress noCopyConstructor
2092   // cppcheck-suppress noOperatorEq
2093   m_tDigitalCurrent = new TTree("T_DigitalCurrent", "DigitalCurrent Debug TTree");
2094   assert(m_tDigitalCurrent);
2095 
2096   m_tDigitalCurrent->Branch("dc", &m_payload,
2097                             "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");
2098 }
2099 
2100 TpcTimeFrameBuilderRun3::DigitalCurrentDebugTTree::~DigitalCurrentDebugTTree()
2101 {
2102   // open TFile
2103   PHTFileServer::write(m_name);
2104 }
2105 
2106 void TpcTimeFrameBuilderRun3::DigitalCurrentDebugTTree::fill(const TpcTimeFrameBuilderRun3::digital_current_payload& payload)
2107 {
2108   assert(m_tDigitalCurrent);
2109 
2110   m_payload = payload;
2111   m_tDigitalCurrent->Fill();
2112 }
2113 
2114 int TpcTimeFrameBuilderRun3::decode_gtm_data(const TpcTimeFrameBuilderRun3::dma_word& gtm_word)
2115 {
2116   if (m_verbosity > 2)
2117   {
2118     std::cout << __PRETTY_FUNCTION__ << "\t- : processing GTM data " << std::endl;
2119   }
2120 
2121   const uint8_t* gtm = reinterpret_cast<const uint8_t*>(&gtm_word);
2122 
2123   gtm_payload payload;
2124 
2125   payload.pkt_type = gtm[0] | static_cast<uint16_t>((unsigned short) gtm[1] << 8U);
2126   //    if (payload.pkt_type != GTM_LVL1_ACCEPT_MAGIC_KEY && payload.pkt_type != GTM_ENDAT_MAGIC_KEY)
2127   if (payload.pkt_type != GTM_LVL1_ACCEPT_MAGIC_KEY && payload.pkt_type != GTM_ENDAT_MAGIC_KEY && payload.pkt_type != GTM_MODEBIT_MAGIC_KEY)
2128   {
2129     return -1;
2130   }
2131 
2132   payload.is_lvl1 = payload.pkt_type == GTM_LVL1_ACCEPT_MAGIC_KEY;
2133   payload.is_endat = payload.pkt_type == GTM_ENDAT_MAGIC_KEY;
2134   payload.is_modebit = payload.pkt_type == GTM_MODEBIT_MAGIC_KEY;
2135 
2136   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);
2137   payload.lvl1_count = ((unsigned int) gtm[8] << 0U) | ((unsigned int) gtm[9] << 8U) | ((unsigned int) gtm[10] << 16U) | ((unsigned int) gtm[11] << 24U);
2138   payload.endat_count = ((unsigned int) gtm[12] << 0U) | ((unsigned int) gtm[13] << 8U) | ((unsigned int) gtm[14] << 16U) | ((unsigned int) gtm[15] << 24U);
2139   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);
2140   payload.modebits = gtm[22];
2141   payload.userbits = gtm[23];
2142 
2143   if (m_verbosity >= 2)
2144   {
2145     std::cout << __PRETTY_FUNCTION__ << "\t- GTM data : "
2146               << "\t- pkt_type = " << payload.pkt_type << std::endl
2147               << "\t- is_lvl1 = " << payload.is_lvl1 << std::endl
2148               << "\t- is_endat = " << payload.is_endat << std::endl
2149               << "\t- is_modebit = " << payload.is_modebit << std::endl
2150               << "\t- bco = 0x" << std::hex << payload.bco << std::dec << std::endl
2151               << "\t- lvl1_count = " << payload.lvl1_count << std::endl
2152               << "\t- endat_count = " << payload.endat_count << std::endl
2153               << "\t- last_bco = 0x" << std::hex << payload.last_bco << std::dec << std::endl
2154               << "\t- modebits =  0x" << std::hex << (int) payload.modebits << std::dec << std::endl
2155               << "\t- userbits =  0x" << std::hex << (int) payload.userbits << std::dec << std::endl;
2156   }
2157 
2158   if (payload.is_modebit)
2159   {
2160     if (payload.modebits == BcoMatchingInformation::ELINK_HEARTBEAT_T)
2161     {
2162       if (m_verbosity > 2)
2163       {
2164         std::cout << "\t- (Heartbeat modebit)" << std::endl;
2165       }
2166       assert(m_hNorm);
2167       m_hNorm->Fill("DMA_WORD_GTM_HEARTBEAT", 1);
2168     }
2169 
2170     if (payload.modebits == BcoMatchingInformation::DC_STOP_SEND_T)
2171     {
2172       if (m_verbosity > 2)
2173       {
2174         std::cout << "\t- (DC stop send modebit)" << std::endl;
2175       }
2176       assert(m_hNorm);
2177       m_hNorm->Fill("DMA_WORD_GTM_DC_STOP_SEND", 1);
2178     }
2179   }
2180 
2181   if (!(m_fastBCOSkip && (payload.is_lvl1 || payload.is_endat)))
2182   {
2183     int fee = -1;
2184     for (BcoMatchingInformation& bcoMatchingInformation : m_bcoMatchingInformation_vec)
2185     {
2186       ++fee;
2187 
2188       if (m_verbosity > 2)
2189       {
2190         std::cout << __PRETTY_FUNCTION__ << "\t- : processing GTM data for FEE " << fee << std::endl;
2191       }
2192 
2193       bcoMatchingInformation.save_gtm_bco_information(payload);
2194 
2195       if (m_verbosity > 2)
2196       {
2197         bcoMatchingInformation.print_gtm_bco_information();
2198       }
2199     }
2200   }  //   if (not m_fastBCOSkip)
2201 
2202   return 0;
2203 }
2204 
2205 uint16_t TpcTimeFrameBuilderRun3::reverseBits(const uint16_t x) const
2206 {
2207   uint16_t n = x;
2208   n = (static_cast<uint16_t>(n >> 1U) & 0x55555555U) | (static_cast<uint16_t>(n << 1U) & 0xaaaaaaaaU);
2209   n = (static_cast<uint16_t>(n >> 2U) & 0x33333333U) | (static_cast<uint16_t>(n << 2U) & 0xccccccccU);
2210   n = (static_cast<uint16_t>(n >> 4U) & 0x0f0f0f0fU) | (static_cast<uint16_t>(n << 4U) & 0xf0f0f0f0U);
2211   n = (static_cast<uint16_t>(n >> 8U) & 0x00ff00ffU) | (static_cast<uint16_t>(n << 8U) & 0xff00ff00U);
2212   // n = (n >> 16U) & 0x0000ffffU | (n << 16U) & 0xffff0000U;
2213   return n;
2214 }
2215 
2216 std::pair<uint16_t, uint16_t> TpcTimeFrameBuilderRun3::crc16_parity(const uint32_t fee, const uint16_t l) const
2217 {
2218   const std::deque<uint16_t>& data_buffer = m_feeData[fee];
2219   assert(l < data_buffer.size());
2220 
2221   std::deque<uint16_t>::const_iterator it = data_buffer.begin();
2222 
2223   uint16_t crc = 0xffffU;
2224   uint16_t data_parity = 0U;
2225 
2226   for (int i = 0; i < l; ++i, ++it)
2227   {
2228     const uint16_t& x = *it;
2229 
2230     crc ^= reverseBits(x);
2231     for (uint16_t k = 0; k < 16U; k++)
2232     {
2233       crc = crc & 1U ? static_cast<uint16_t>(crc >> 1U) ^ 0xa001U : crc >> 1U;
2234     }
2235 
2236     // parity on data payload only
2237     if (i >= HEADER_LENGTH)
2238     {
2239       // fast parity
2240       uint16_t word = x & uint16_t((1U << 10U) - 1U);
2241       word = word ^ static_cast<uint16_t>(word >> 1U);
2242       word = word ^ static_cast<uint16_t>(word >> 2U);
2243       word = word ^ static_cast<uint16_t>(word >> 4U);
2244       word = word ^ static_cast<uint16_t>(word >> 8U);
2245       data_parity ^= word & 1U;
2246     }
2247   }
2248   crc = reverseBits(crc);
2249   return std::make_pair(crc, data_parity);
2250 }
2251 
2252 namespace
2253 {
2254   // streamer for lists
2255   template <class T>
2256   std::ostream& operator<<(std::ostream& o, const std::list<T>& list)
2257   {
2258     if (list.empty())
2259     {
2260       o << "{}";
2261     }
2262     else
2263     {
2264       const bool is_hex = (o.flags() & std::ios_base::hex);
2265       o << "{ ";
2266       bool first = true;
2267       for (const auto& value : list)
2268       {
2269         if (!first)
2270         {
2271           o << ", ";
2272         }
2273         if (is_hex)
2274         {
2275           o << "0x";
2276         }
2277         o << value;
2278         first = false;
2279       }
2280       o << "\t- }";
2281     }
2282     return o;
2283   }
2284 
2285   template <class T>
2286   std::ostream& operator<<(std::ostream& o, const std::vector<T>& list)
2287   {
2288     if (list.empty())
2289     {
2290       o << "{}";
2291     }
2292     else
2293     {
2294       const bool is_hex = (o.flags() & std::ios_base::hex);
2295       o << "{ ";
2296       bool first = true;
2297       for (const auto& value : list)
2298       {
2299         if (!first)
2300         {
2301           o << ", ";
2302         }
2303         if (is_hex)
2304         {
2305           o << "0x";
2306         }
2307         o << value;
2308         first = false;
2309       }
2310       o << "\t- }";
2311     }
2312     return o;
2313   }
2314 
2315 }  // namespace
2316 
2317 TpcTimeFrameBuilderRun3::BcoMatchingInformation::BcoMatchingInformation(const std::string& name)
2318   : m_name(name)
2319 {
2320   Fun4AllHistoManager* hm = QAHistManagerDef::getHistoManager();
2321   assert(hm);
2322 
2323   // cppcheck-suppress noCopyConstructor
2324   // cppcheck-suppress noOperatorEq
2325   m_hNorm = new TH1D(TString(m_name.c_str()) + "_Normalization",  //
2326                      TString(m_name.c_str()) + " Normalization;Items;Count",
2327                      20, .5, 20.5);
2328   int i = 1;
2329   m_hNorm->GetXaxis()->SetBinLabel(i++, "SyncGTM");
2330   m_hNorm->GetXaxis()->SetBinLabel(i++, "DC_STOP_SEND_GTM");
2331   m_hNorm->GetXaxis()->SetBinLabel(i++, "HeartBeatGTM");
2332   m_hNorm->GetXaxis()->SetBinLabel(i++, "HeartBeatFEE");
2333   m_hNorm->GetXaxis()->SetBinLabel(i++, "HeartBeatFEEMatchedReference");
2334   m_hNorm->GetXaxis()->SetBinLabel(i++, "HeartBeatFEEMatchedNew");
2335   m_hNorm->GetXaxis()->SetBinLabel(i++, "HeartBeatFEEUnMatched");
2336   m_hNorm->GetXaxis()->SetBinLabel(i++, "TriggerGTM");
2337   m_hNorm->GetXaxis()->SetBinLabel(i++, "EnDATGTM");
2338   m_hNorm->GetXaxis()->SetBinLabel(i++, "UnmatchedEnDATGTM");
2339   m_hNorm->GetXaxis()->SetBinLabel(i++, "FindGTMBCO");
2340   m_hNorm->GetXaxis()->SetBinLabel(i++, "FindGTMBCOMatchedExisting");
2341   m_hNorm->GetXaxis()->SetBinLabel(i++, "FindGTMBCOMatchedNew");
2342   m_hNorm->GetXaxis()->SetBinLabel(i++, "FindGTMBCOMatchedFailed");
2343 
2344   assert(i <= 20);
2345   m_hNorm->GetXaxis()->LabelsOption("v");
2346   hm->registerHisto(m_hNorm);
2347 
2348   m_hFEEClockAdjustment_MatchedReference = new TH1I(TString(m_name.c_str()) + "_FEEClockAdjustment_MatchedReference",  //
2349                                                     TString(m_name.c_str()) +
2350                                                         " FEEClockAdjustment for Matched Reference;Clock Adjustment [FEE Clock Cycle];Count",
2351                                                     512, -256 - .5, +256 - .5);
2352   hm->registerHisto(m_hFEEClockAdjustment_MatchedReference);
2353   m_hFEEClockAdjustment_MatchedNew = new TH1I(TString(m_name.c_str()) + "_FEEClockAdjustment_MatchedNew",  //
2354                                               TString(m_name.c_str()) +
2355                                                   " FEEClockAdjustment for Matched New;Clock Adjustment [FEE Clock Cycle];Count",
2356                                               512, -256 - .5, +256 - .5);
2357   hm->registerHisto(m_hFEEClockAdjustment_MatchedNew);
2358 
2359   m_hFEEClockAdjustment_Unmatched = new TH1I(TString(m_name.c_str()) + "_FEEClockAdjustment_Unmatched",  //
2360                                              TString(m_name.c_str()) +
2361                                                  " FEEClock Diff for unmatched;Clock Adjustment [FEE Clock Cycle];Count",
2362                                              512,
2363                                              -(1UL << m_FEE_CLOCK_BITS) - .5,
2364                                              +(1UL << m_FEE_CLOCK_BITS) - .5);
2365   hm->registerHisto(m_hFEEClockAdjustment_Unmatched);
2366 
2367   m_hGTMNewEventSpacing = new TH1I(TString(m_name.c_str()) +
2368                                        "_GTM_NewEventSpacing",  //
2369                                    TString(m_name.c_str()) +
2370                                        " Spacing between two events;Clock Diff [RHIC Clock Cycle];Count",
2371                                    1024, -.5, +1024 - .5);
2372   hm->registerHisto(m_hGTMNewEventSpacing);
2373 
2374   // m_hFindGTMBCO_MatchedExisting_BCODiff = new TH1I(TString(m_name.c_str()) + "_FindGTMBCO_MatchedExisting_BCODiff",  //
2375   //                                                  TString(m_name.c_str()) +
2376   //                                                      " find_gtm_bco matched to existing event clock diff;Clock Difference [FEE Clock Cycle];Count",
2377   //                                                  512, -256 - .5, +256 - .5);
2378   // hm->registerHisto(m_hFindGTMBCO_MatchedExisting_BCODiff);
2379   // m_hFindGTMBCO_MatchedNew_BCODiff = new TH1I(TString(m_name.c_str()) + "_FindGTMBCO_MatchedNew_BCODiff",  //
2380   //                                             TString(m_name.c_str()) +
2381   //                                                 " find_gtm_bco matched to new event clock diff;Clock Difference [FEE Clock Cycle];Count",
2382   //                                             512, -256 - .5, +256 - .5);
2383   // hm->registerHisto(m_hFindGTMBCO_MatchedNew_BCODiff);
2384 }
2385 
2386 //! whether reference bco has moved pass the given gtm_bco
2387 bool TpcTimeFrameBuilderRun3::BcoMatchingInformation::isMoreDataRequired(const uint64_t& gtm_bco) const
2388 {
2389   const uint64_t bco_correction = get_gtm_rollover_correction(gtm_bco);
2390 
2391   if (m_verbosity >= 2)
2392   {
2393     std::cout << "TpcTimeFrameBuilderRun3[" << m_name << "]::BcoMatchingInformation::isMoreDataRequired entry"
2394               << " at gtm_bco = 0x" << std::hex << gtm_bco << std::dec
2395               << " bco_correction = 0x" << std::hex << bco_correction << std::dec
2396               << std::endl;
2397   }
2398 
2399   if (m_bco_reference)
2400   {
2401     if (m_bco_reference.value().first > bco_correction + m_max_fee_sync_time)
2402     {
2403       if (m_verbosity >= 2)
2404       {
2405         std::cout << "TpcTimeFrameBuilderRun3[" << m_name << "]::BcoMatchingInformation::isMoreDataRequired"
2406                   << " at gtm_bco = 0x" << std::hex << gtm_bco << std::dec
2407                   << ". m_bco_reference.value().first = 0x" << std::hex << m_bco_reference.value().first << std::dec
2408                   << " bco_correction = 0x" << std::hex << bco_correction << std::dec
2409                   << ". satisified m_max_fee_sync_time = " << m_max_fee_sync_time
2410                   << std::endl;
2411       }
2412 
2413       return false;
2414     }
2415   }
2416 
2417   if (!m_bco_heartbeat_list.empty())
2418   {
2419     if (m_bco_heartbeat_list.back().first > bco_correction + m_max_fee_sync_time)
2420     {
2421       if (m_verbosity >= 2)
2422       {
2423         std::cout << "TpcTimeFrameBuilderRun3[" << m_name << "]::BcoMatchingInformation::isMoreDataRequired"
2424                   << "at gtm_bco = 0x" << std::hex << gtm_bco << std::dec
2425                   << ". m_bco_heartbeat_list.back().first = 0x" << std::hex << m_bco_heartbeat_list.back().first << std::dec
2426                   << " bco_correction = 0x" << std::hex << bco_correction << std::dec
2427                   << ". satisified m_max_fee_sync_time = " << m_max_fee_sync_time
2428                   << std::endl;
2429       }
2430 
2431       return false;
2432     }
2433 
2434     if (m_verbosity > 4)
2435     {
2436       std::cout << "TpcTimeFrameBuilderRun3[" << m_name << "]::BcoMatchingInformation::isMoreDataRequired"
2437                 << "at gtm_bco = 0x" << std::hex << gtm_bco << std::dec
2438                 << ". m_bco_heartbeat_list.back().first = 0x" << std::hex << m_bco_heartbeat_list.back().first << std::dec
2439                 << " bco_correction = 0x" << std::hex << bco_correction << std::dec
2440                 << ". not yet satisified m_max_fee_sync_time = " << m_max_fee_sync_time
2441                 << std::endl;
2442     }
2443   }
2444 
2445   if (m_verbosity > 3)
2446   {
2447     std::cout << "TpcTimeFrameBuilderRun3[" << m_name << "]::BcoMatchingInformation::isMoreDataRequired"
2448               << "at gtm_bco = 0x" << std::hex << gtm_bco << std::dec
2449               << " bco_correction = 0x" << std::hex << bco_correction << std::dec << ": more data required"
2450               << " as their is NO m_bco_reference nor m_bco_heartbeat_list"
2451               << std::endl;
2452 
2453     std::cout << "  m_gtm_bco_trigger_map:" << std::endl;
2454     for (const auto& trig : m_gtm_bco_trigger_map)
2455     {
2456       std::cout << " - 0x" << std::hex << trig.first << std::dec << "(Diff = " << trig.first - bco_correction << ") " << std::endl;
2457     }
2458 
2459     std::cout << "  m_bco_matching_list:" << std::endl;
2460     for (const auto& trig : m_bco_matching_list)
2461     {
2462       std::cout << " - 0x" << std::hex << trig.second << std::dec << "(Diff = " << trig.second - bco_correction << ") " << std::endl;
2463     }
2464   }
2465   return true;
2466 }
2467 
2468 //___________________________________________________
2469 std::optional<uint32_t> TpcTimeFrameBuilderRun3::BcoMatchingInformation::get_predicted_fee_bco(uint64_t gtm_bco) const
2470 {
2471   // check proper initialization
2472   if (!is_verified() || !m_bco_reference)
2473   {
2474     return std::nullopt;
2475   }
2476 
2477   // check whether it is within the same FEE clock rollover window based on the reference candidate list
2478   {
2479     uint64_t latest_reference_bco = (*m_bco_reference).first;
2480     if (! m_bco_heartbeat_list.empty())  
2481     {
2482         latest_reference_bco = m_bco_heartbeat_list.back().first;  // get the latest heartbeat bco
2483     }
2484 
2485     if (get_bco_diff(gtm_bco , latest_reference_bco)*m_clock_ratio_numerator 
2486     > ((1U << (m_FEE_CLOCK_BITS -1))) * m_clock_ratio_denominator)
2487     {
2488       if (m_verbosity >= 3)
2489       {
2490         std::cout << "TpcTimeFrameBuilderRun3[" << m_name << "]::BcoMatchingInformation::get_predicted_fee_bco -"
2491                   << " GTM bco 0x" << std::hex << gtm_bco << std::dec
2492                   << " is too far from the latest heartbeat bco 0x" << std::hex << latest_reference_bco << std::dec
2493                   << " get_bco_diff(gtm_bco , latest_reference_bco) =" << get_bco_diff(gtm_bco , latest_reference_bco)
2494                   << " > " << ((1U << (m_FEE_CLOCK_BITS -1))) * m_clock_ratio_denominator / m_clock_ratio_numerator
2495                   << ", cannot predict fee bco" << std::endl;
2496       }
2497       return std::nullopt;
2498     }
2499   }
2500 
2501   // get gtm bco difference with proper rollover accounting
2502   const auto& bco_reference = *m_bco_reference;
2503   const int64_t gtm_bco_difference = int64_t(gtm_bco) - int64_t(bco_reference.first);
2504 
2505   static_assert(m_clock_ratio_numerator > 0);
2506   static_assert(m_clock_ratio_denominator > 0);
2507 
2508   // convert to fee bco with the exact Run3 30/8 ratio, and truncate to 20 bits
2509   const int64_t fee_bco_predicted = int64_t(bco_reference.second) +
2510                                     (gtm_bco_difference * m_clock_ratio_numerator) / m_clock_ratio_denominator;
2511   return uint32_t(static_cast<uint64_t>(fee_bco_predicted) & 0xFFFFFU);
2512 }
2513 
2514 //___________________________________________________
2515 void TpcTimeFrameBuilderRun3::BcoMatchingInformation::print_gtm_bco_information() const
2516 {
2517   if (!m_gtm_bco_trig_list.empty())
2518   {
2519     std::cout
2520         << "TpcTimeFrameBuilderRun3[" << m_name << "]::BcoMatchingInformation::print_gtm_bco_information -"
2521         << "\t- m_gtm_bco_trig_list: " << std::hex << m_gtm_bco_trig_list << std::dec
2522         << std::endl;
2523 
2524     // also print predicted fee bco
2525     if (is_verified())
2526     {
2527       std::list<uint32_t> fee_bco_predicted_list;
2528       std::transform(
2529           m_gtm_bco_trig_list.begin(),
2530           m_gtm_bco_trig_list.end(),
2531           std::back_inserter(fee_bco_predicted_list),
2532           [this](const uint64_t& gtm_bco)
2533           { return get_predicted_fee_bco(gtm_bco).value(); });
2534 
2535       std::cout
2536           << "TpcTimeFrameBuilderRun3[" << m_name << "]::BcoMatchingInformation::print_gtm_bco_information -"
2537           << "\t- m_gtm_bco_trig_list fee predicted: " << std::hex << fee_bco_predicted_list << std::dec
2538           << std::endl;
2539     }
2540   }
2541 
2542   std::cout << "\t m_gtm_bco_dc_read = " << std::hex
2543             << m_gtm_bco_dc_read.first << " -> 0x" << m_gtm_bco_dc_read.second
2544             << std::dec << std::endl;
2545 }
2546 
2547 uint64_t TpcTimeFrameBuilderRun3::BcoMatchingInformation::
2548     get_gtm_rollover_correction(const uint64_t& gtm_bco) const
2549 {
2550   // start with 40bit clock, enforced
2551   uint64_t gtm_bco_corrected = gtm_bco & ((uint64_t(1) << m_GTM_CLOCK_BITS) - 1);
2552 
2553   if (!m_bco_reference)
2554   {
2555     return gtm_bco_corrected;
2556   }
2557 
2558   // get the last GTM clock roll over
2559   const uint64_t& last_bco = m_bco_reference.value().first;
2560   const uint64_t last_bco_rollover = last_bco &
2561                                      (std::numeric_limits<uint64_t>::max() << m_GTM_CLOCK_BITS);
2562 
2563   // use the roll over of the last GTM clock reading
2564   gtm_bco_corrected += last_bco_rollover;
2565 
2566   // check if the rollover has advanced
2567   if (gtm_bco_corrected + (uint64_t(1) << (m_GTM_CLOCK_BITS - 1)) < last_bco)
2568   {
2569     gtm_bco_corrected += uint64_t(1) << m_GTM_CLOCK_BITS;
2570   }
2571 
2572   return gtm_bco_corrected;
2573 }
2574 
2575 //___________________________________________________
2576 void TpcTimeFrameBuilderRun3::BcoMatchingInformation::save_bx_counter_sync_observation(
2577     uint64_t bx_counter_sync_gtm_bco,
2578     uint64_t bco_reference_gtm_bco,
2579     const TpcTimeFrameBuilderRun3::BcoMatchingInformation::m_gtm_fee_bco_matching_pair_t& bco_reference)
2580 {
2581   if (m_bx_counter_sync_observation_count >= kMaxBXCounterSyncObservations)
2582   {
2583     return;
2584   }
2585 
2586   BXCounterSyncObservation& observation = m_bx_counter_sync_observations[m_bx_counter_sync_observation_count];
2587   observation.bx_counter_sync_gtm_bco = bx_counter_sync_gtm_bco;
2588   observation.bco_reference_gtm_bco = bco_reference_gtm_bco;
2589   observation.m_bco_reference = bco_reference;
2590   ++m_bx_counter_sync_observation_count;
2591 }
2592 
2593 void TpcTimeFrameBuilderRun3::BcoMatchingInformation::save_gtm_bco_information(const TpcTimeFrameBuilderRun3::gtm_payload& gtm_tagger)
2594 {
2595   // append gtm_bco from taggers in this event to packet-specific list of available lv1_bco
2596 
2597   // save level1 trigger bco
2598   const bool& is_lvl1 = gtm_tagger.is_lvl1;
2599   const bool& is_endat = gtm_tagger.is_endat;
2600   const bool& is_modebit = gtm_tagger.is_modebit;
2601   const uint64_t gtm_bco = get_gtm_rollover_correction(gtm_tagger.bco);
2602 
2603   if (is_lvl1)
2604   {
2605     assert(m_hNorm);
2606     m_hNorm->Fill("TriggerGTM", 1);
2607 
2608     assert(m_hGTMNewEventSpacing);
2609     if (!m_gtm_bco_trig_list.empty())
2610     {
2611       m_hGTMNewEventSpacing->Fill(gtm_bco - m_gtm_bco_trig_list.back());
2612     }
2613     m_gtm_bco_trig_list.push_back(gtm_bco);
2614   }
2615 
2616   // also save ENDDAT bco
2617   else if (is_endat)
2618   {
2619     assert(m_hNorm);
2620     m_hNorm->Fill("EnDATGTM", 1);
2621 
2622     // add to list if difference to last entry is big enough
2623     if (m_gtm_bco_trig_list.empty() || (gtm_bco - m_gtm_bco_trig_list.back()) > m_max_lv1_endat_bco_diff)
2624     {
2625       assert(m_hNorm);
2626       m_hNorm->Fill("UnmatchedEnDATGTM", 1);
2627 
2628       if (!m_gtm_bco_trig_list.empty())
2629       {
2630         assert(m_hGTMNewEventSpacing);
2631         m_hGTMNewEventSpacing->Fill(gtm_bco - m_gtm_bco_trig_list.back());
2632       }
2633       m_gtm_bco_trig_list.push_back(gtm_bco);
2634     }
2635   }
2636 
2637   // also save hearbeat bco
2638   else if (is_modebit)
2639   {
2640     // get modebits
2641     const uint64_t& modebits = gtm_tagger.modebits;
2642     if (modebits == ELINK_HEARTBEAT_T)
2643     {
2644       assert(m_hNorm);
2645       m_hNorm->Fill("HeartBeatGTM", 1);
2646 
2647       auto predicted_fee_bco = get_predicted_fee_bco(gtm_bco);
2648       if (predicted_fee_bco)
2649       {
2650         m_bco_heartbeat_list.emplace_back(gtm_bco, predicted_fee_bco.value());
2651       }
2652       else
2653       {
2654         if (m_verbosity > 1)
2655         {
2656           std::cout << "TpcTimeFrameBuilderRun3[" << m_name << "]::BcoMatchingInformation::save_gtm_bco_information"
2657                     << "\t- Warning: predicted_fee_bco is not available for gtm_bco = 0x" << std::hex << gtm_bco << std::dec
2658                     << ". Skipping heartbeat candidate." << std::endl;
2659         }
2660       }
2661 
2662       if (m_verbosity > 1)
2663       {
2664         std::cout << "TpcTimeFrameBuilderRun3[" << m_name << "]::BcoMatchingInformation::save_gtm_bco_information"
2665                   << "\t- found heartbeat candidate "
2666                   << "at gtm_bco = 0x" << std::hex << gtm_bco << std::dec
2667                   << ". Current m_bco_heartbeat_list:"
2668                   << std::endl;
2669 
2670         for (const m_gtm_fee_bco_matching_pair_t& bco : m_bco_heartbeat_list)
2671         {
2672           std::cout << "\t- gtm_bco = 0x" << std::hex << bco.first << std::dec
2673                     << "\t- fee_bco = 0x" << std::hex << bco.second << std::dec
2674                     << std::endl;
2675         }
2676       }
2677 
2678       while (m_bco_heartbeat_list.size() > m_max_bco_heartbeat_list_size)
2679       {
2680         if (m_verbosity > 1)
2681         {
2682           uint64_t bco = m_bco_heartbeat_list.begin()->first;
2683           std::cout << "TpcTimeFrameBuilderRun3[" << m_name << "]::BcoMatchingInformation::find_reference_from_modebits"
2684                     << "Warning: m_bco_heartbeat_list is full"
2685                     << "\t- drop unprocessed heart beat in queue "
2686                     << "at gtm_bco = 0x" << std::hex << bco
2687                     << std::dec
2688                     << ". Unprocessed heartbeats in queue with size of " << m_bco_heartbeat_list.size()
2689                     << std::endl;
2690         }
2691 
2692         m_bco_heartbeat_list.pop_front();
2693       }
2694 
2695     }  //     if (modebits & (1U << ELINK_HEARTBEAT_T))
2696 
2697     if (modebits == BX_COUNTER_SYNC_T)  // initiate synchronization of clock sync
2698     {
2699       assert(m_hNorm);
2700       m_hNorm->Fill("SyncGTM", 1);
2701 
2702       // get BCO and assign
2703       const uint64_t bco_reference_gtm_bco = gtm_bco + kBXCounterSyncGtmBcoOffset;
2704       const m_gtm_fee_bco_matching_pair_t bx_counter_sync_reference =
2705           std::make_pair(bco_reference_gtm_bco, static_cast<uint32_t>(kBXCounterSyncFEEBcoOffset));
2706       m_verified_from_modebits = true;
2707       m_bco_reference = bx_counter_sync_reference;
2708       save_bx_counter_sync_observation(gtm_bco, bco_reference_gtm_bco, bx_counter_sync_reference);
2709       m_bco_heartbeat_list.clear();
2710 
2711       if (m_verbosity)
2712       {
2713         std::cout << "TpcTimeFrameBuilderRun3[" << m_name << "]::BcoMatchingInformation::find_reference_from_modebits"
2714                   << "\t- found reference from modebits BX_COUNTER_SYNC_T "
2715                   << "at gtm_bco = 0x" << std::hex << gtm_bco
2716                   << " reference gtm_bco = 0x" << bco_reference_gtm_bco << std::dec
2717                   << " GTM sync offset = " << kBXCounterSyncGtmBcoOffset
2718                   << " FEE sync offset = " << kBXCounterSyncFEEBcoOffset
2719                   << std::endl;
2720       }
2721     }  //     if (modebits == BX_COUNTER_SYNC_T)  // initiate synchronization of clock sync
2722 
2723     if (modebits == DC_STOP_SEND_T)
2724     {
2725       assert(m_hNorm);
2726       m_hNorm->Fill("DC_STOP_SEND_GTM", 1);
2727 
2728       // save the gtm_bco for the digital current readout
2729       m_gtm_bco_dc_read.first = gtm_bco;
2730       if (is_verified())
2731       {
2732         m_gtm_bco_dc_read.second = get_predicted_fee_bco(gtm_bco).value();  // NOLINT(bugprone-unchecked-optional-access)
2733       }
2734       else
2735       {
2736         m_gtm_bco_dc_read.second = 0;  // not verified, so no reference clock sync available
2737       }
2738 
2739       if (m_verbosity > 2)
2740       {
2741         std::cout << "TpcTimeFrameBuilderRun3[" << m_name << "]::BcoMatchingInformation::save_gtm_bco_information"
2742                   << "\t- found DC stop send modebit "
2743                   << "at gtm_bco = 0x" << std::hex << gtm_bco << std::dec
2744                   << std::endl;
2745       }
2746     }
2747   }
2748 }
2749 
2750 //___________________________________________________
2751 std::optional<uint64_t> TpcTimeFrameBuilderRun3::BcoMatchingInformation::find_reference_heartbeat(const TpcTimeFrameBuilderRun3::fee_payload& HeartBeatPacket)
2752 {
2753   assert(m_hNorm);
2754   m_hNorm->Fill("HeartBeatFEE", 1);
2755 
2756   // make sure the bco matching is properly initialized and historical valid
2757   if (!is_verified())
2758   {
2759     return std::nullopt;
2760   }
2761 
2762   assert(HeartBeatPacket.type == HEARTBEAT_T);
2763   const uint32_t& fee_bco = HeartBeatPacket.bx_timestamp;
2764 
2765   if (m_bco_reference)
2766   {
2767     const uint64_t& gtm_bco = m_bco_reference.value().first;
2768     const uint32_t& fee_bco_predicted = m_bco_reference.value().second;
2769     // check if the predicted fee bco matches the actual fee bco
2770     if (get_fee_bco_diff(fee_bco_predicted, fee_bco) < m_max_fee_bco_diff)
2771     {
2772       // Keep QA for matched heartbeat, but do not update clock reference from heartbeat.
2773       if (verbosity() > 1)
2774       {
2775         std::cout << "TpcTimeFrameBuilderRun3[" << m_name << "]::BcoMatchingInformation::find_reference_heartbeat - found a matched reference heartbeat; clock reference update disabled: "
2776                   << std::hex
2777                   << "\t- fee_bco: 0x" << fee_bco
2778                   << "\t- predicted: 0x" << fee_bco_predicted
2779                   << "\t- gtm_bco: 0x" << gtm_bco
2780                   << std::dec
2781                   << std::endl;
2782       }
2783 
2784       assert(m_hFEEClockAdjustment_MatchedReference);
2785       m_hFEEClockAdjustment_MatchedReference->Fill(int64_t(fee_bco) - int64_t(fee_bco_predicted), 1);
2786 
2787       m_hNorm->Fill("HeartBeatFEEMatchedReference", 1);
2788 
2789       return gtm_bco;
2790     }
2791   }
2792 
2793   for (const m_gtm_fee_bco_matching_pair_t& bco : m_bco_heartbeat_list)
2794   {
2795     const uint64_t gtm_bco = bco.first;
2796     const uint32_t fee_bco_predicted = bco.second;
2797 
2798     // check if the predicted fee bco matches the actual fee bco
2799     if (get_fee_bco_diff(fee_bco_predicted, fee_bco) < m_max_fee_bco_diff)
2800     {
2801       if (verbosity() > 1)
2802       {
2803         std::cout << "TpcTimeFrameBuilderRun3[" << m_name << "]::BcoMatchingInformation::find_reference_heartbeat - found a new reference candidate heartbeat; clock reference update disabled: "
2804                   << std::hex
2805                   << "\t- fee_bco: 0x" << fee_bco
2806                   << "\t- predicted: 0x" << fee_bco_predicted
2807                   << "\t- gtm_bco: 0x" << gtm_bco
2808                   << "\t- previous reference gtm_bco: 0x" << m_bco_reference.value().first   // NOLINT(bugprone-unchecked-optional-access)
2809                   << "\t- previous reference fee_bco: 0x" << m_bco_reference.value().second  // NOLINT(bugprone-unchecked-optional-access)
2810                   << std::dec
2811                   << std::endl;
2812       }
2813       // Keep QA for matched candidate heartbeat, but do not replace the clock reference or trim candidates.
2814       if (m_verbosity > 1)
2815       {
2816         std::cout << "\t- clock reference update from heartbeat is disabled; candidate list retained at size "
2817                   << m_bco_heartbeat_list.size() << std::endl;
2818       }
2819 
2820       assert(m_hFEEClockAdjustment_MatchedNew);
2821       m_hFEEClockAdjustment_MatchedNew->Fill(int64_t(fee_bco) - int64_t(fee_bco_predicted), 1);
2822 
2823       m_hNorm->Fill("HeartBeatFEEMatchedNew", 1);
2824       return gtm_bco;
2825     }
2826 
2827     if (verbosity() > 1)
2828     {
2829       std::cout << "TpcTimeFrameBuilderRun3[" << m_name << "]::BcoMatchingInformation::find_reference_heartbeat - unmatched heartbeat: "
2830                 << std::hex
2831                 << "\t- fee_bco: 0x" << fee_bco
2832                 << "\t- predicted: 0x" << fee_bco_predicted
2833                 << "\t- gtm_bco: 0x" << gtm_bco
2834                 << std::dec
2835                 << std::endl;
2836     }
2837 
2838     assert(m_hFEEClockAdjustment_Unmatched);
2839     m_hFEEClockAdjustment_Unmatched->Fill(int64_t(fee_bco) - int64_t(fee_bco_predicted), 1);
2840   }  //   for (const auto& bco : m_bco_heartbeat_list)
2841 
2842   if (verbosity() > 1)
2843   {
2844     std::cout << "TpcTimeFrameBuilderRun3[" << m_name << "]::BcoMatchingInformation::find_reference_heartbeat - WARNING: failed match for fee_bco = 0x" << std::hex << fee_bco << std::dec << std::endl;
2845   }
2846   m_hNorm->Fill("HeartBeatFEEUnMatched", 1);
2847   return std::nullopt;
2848 }
2849 
2850 //___________________________________________________
2851 void TpcTimeFrameBuilderRun3::BcoMatchingInformation::cleanup()
2852 {
2853   // remove old gtm_bco and matching
2854   while (m_gtm_bco_trig_list.size() > m_max_matching_data_size)
2855   {
2856     m_gtm_bco_trig_list.pop_front();
2857   }
2858   while (m_bco_matching_list.size() > m_max_matching_data_size)
2859   {
2860     m_bco_matching_list.pop_front();
2861   }
2862 }
2863 
2864 //___________________________________________________
2865 void TpcTimeFrameBuilderRun3::BcoMatchingInformation::cleanup(uint64_t ref_bco)
2866 {
2867   // erase all elements from bco_list that are less than or equal to ref_bco
2868   m_gtm_bco_trig_list.erase(std::remove_if(m_gtm_bco_trig_list.begin(), m_gtm_bco_trig_list.end(),
2869                                            [ref_bco](const uint64_t& bco)
2870                                            { return bco <= ref_bco; }),
2871                             m_gtm_bco_trig_list.end());
2872 
2873   // erase all elements from bco_list that are less than or equal to ref_bco
2874   m_bco_matching_list.erase(std::remove_if(m_bco_matching_list.begin(), m_bco_matching_list.end(),
2875                                            [ref_bco](const m_fee_gtm_bco_matching_pair_t& pair)
2876                                            {
2877                                              return pair.second <= ref_bco;
2878                                            }),
2879                             m_bco_matching_list.end());
2880 }
2881 
2882 void TpcTimeFrameBuilderRun3::fillBadFeeMap()
2883 {
2884   const std::string filename = CDBInterface::instance()->getUrl("TPC_DECODER_BAD_FEE");
2885 
2886   if (filename.empty())
2887   {
2888     if (m_verbosity > 0)
2889     {
2890       std::cout << "TpcTimeFrameBuilderRun3::fillBadFeeMap - no file found for TPC_DECODER_BAD_FEE, not filling bad fee map" << std::endl;
2891     }
2892     return;
2893   }
2894 
2895   CDBTTree cdbtree(filename);
2896   cdbtree.LoadCalibrations();
2897 
2898   const int nentries = cdbtree.GetSingleIntValue("N_MASKED_FEES");
2899 
2900   for (int i = 0; i < nentries; i++)
2901   {
2902     m_maskedFEEs[cdbtree.GetIntValue(i, "EBDC")].insert(cdbtree.GetIntValue(i, "FEEID"));
2903   }
2904 }