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
0049
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
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
0866
0867
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
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
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
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
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
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
1366
1367
1368
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();
1376 assert(h_PacketLength);
1377 h_PacketLength->Fill(data_length);
1378
1379 int data_padding = packet->getPadding();
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
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
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
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
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
1498 m_hNorm->Fill("DMA_WORD_INVALID", 1);
1499 }
1500 }
1501
1502
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
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
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
1628
1629 bool is_digital_current = false;
1630
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 }
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
1671 const uint16_t pkt_length = data_buffer[0];
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 }
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
1718 payload.fee_id = fee;
1719 payload.adc_length = data_buffer[0] - HEADER_LENGTH;
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
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
1759 }
1760 }
1761
1762 assert(fee < m_bcoMatchingInformation_vec.size());
1763 BcoMatchingInformation& m_bcoMatchingInformation = m_bcoMatchingInformation_vec[fee];
1764
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
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
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
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
1804 }
1805 if (m_verbosity > 2)
1806 {
1807 m_bcoMatchingInformation.print_gtm_bco_information();
1808 }
1809 }
1810 }
1811 else if (!m_fastBCOSkip)
1812 {
1813 m_hFEEChannelPacketCount->Fill(fee * MAX_CHANNELS + payload.channel, 1);
1814
1815
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
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;
1904 }
1905 payload.waveforms.emplace_back(start_t, std::move(adc));
1906
1907
1908
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
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
1940 hit->set_checksumerror(payload.data_crc != payload.calc_crc);
1941
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 }
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;
1982
1983 payload.channel = data_buffer[4] & 0x1ffU;
1984
1985 payload.bx_timestamp = ((data_buffer[6] & 0x3ffU) << 10U) | (data_buffer[5] & 0x3ff);
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
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
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
2089 PHTFileServer::open(m_name, "RECREATE");
2090
2091
2092
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
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*>(>m_word);
2122
2123 gtm_payload payload;
2124
2125 payload.pkt_type = gtm[0] | static_cast<uint16_t>((unsigned short) gtm[1] << 8U);
2126
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 }
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
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
2237 if (i >= HEADER_LENGTH)
2238 {
2239
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
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 }
2316
2317 TpcTimeFrameBuilderRun3::BcoMatchingInformation::BcoMatchingInformation(const std::string& name)
2318 : m_name(name)
2319 {
2320 Fun4AllHistoManager* hm = QAHistManagerDef::getHistoManager();
2321 assert(hm);
2322
2323
2324
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
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384 }
2385
2386
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
2472 if (!is_verified() || !m_bco_reference)
2473 {
2474 return std::nullopt;
2475 }
2476
2477
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;
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
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
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
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
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
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
2564 gtm_bco_corrected += last_bco_rollover;
2565
2566
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
2596
2597
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
2617 else if (is_endat)
2618 {
2619 assert(m_hNorm);
2620 m_hNorm->Fill("EnDATGTM", 1);
2621
2622
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
2638 else if (is_modebit)
2639 {
2640
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 }
2696
2697 if (modebits == BX_COUNTER_SYNC_T)
2698 {
2699 assert(m_hNorm);
2700 m_hNorm->Fill("SyncGTM", 1);
2701
2702
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 }
2722
2723 if (modebits == DC_STOP_SEND_T)
2724 {
2725 assert(m_hNorm);
2726 m_hNorm->Fill("DC_STOP_SEND_GTM", 1);
2727
2728
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();
2733 }
2734 else
2735 {
2736 m_gtm_bco_dc_read.second = 0;
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
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
2770 if (get_fee_bco_diff(fee_bco_predicted, fee_bco) < m_max_fee_bco_diff)
2771 {
2772
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
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
2809 << "\t- previous reference fee_bco: 0x" << m_bco_reference.value().second
2810 << std::dec
2811 << std::endl;
2812 }
2813
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 }
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
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
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
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 }