Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-06 08:17:21

0001 
0002 /*!
0003  * \file MicromegasBcoMatchingInformation_v1.cc
0004  * \author Hugo Pereira Da Costa <hugo.pereira-da-costa@cea.fr>
0005  * \brief handles matching between GTM and FEE BCO clock
0006  */
0007 
0008 #include "MicromegasBcoMatchingInformation_v1.h"
0009 
0010 #include <Event/packet.h>
0011 
0012 #include <algorithm>
0013 #include <vector>
0014 
0015 namespace
0016 {
0017 
0018   // streamer for lists
0019   template <class T>
0020   std::ostream& operator<<(std::ostream& o, const std::list<T>& list)
0021   {
0022     if (list.empty())
0023     {
0024       o << "{}";
0025     }
0026     else
0027     {
0028       const bool is_hex = (o.flags() & std::ios_base::hex);
0029       o << "{ ";
0030       bool first = true;
0031       for (const auto& value : list)
0032       {
0033         if (!first)
0034         {
0035           o << ", ";
0036         }
0037         if (is_hex)
0038         {
0039           o << "0x";
0040         }
0041         o << value;
0042         first = false;
0043       }
0044       o << " }";
0045     }
0046     return o;
0047   }
0048 
0049   template <class T>
0050   std::ostream& operator<<(std::ostream& o, const std::vector<T>& list)
0051   {
0052     if (list.empty())
0053     {
0054       o << "{}";
0055     }
0056     else
0057     {
0058       const bool is_hex = (o.flags() & std::ios_base::hex);
0059       o << "{ ";
0060       bool first = true;
0061       for (const auto& value : list)
0062       {
0063         if (!first)
0064         {
0065           o << ", ";
0066         }
0067         if (is_hex)
0068         {
0069           o << "0x";
0070         }
0071         o << value;
0072         first = false;
0073       }
0074       o << " }";
0075     }
0076     return o;
0077   }
0078 
0079   // get the difference between two BCO.
0080   template <class T>
0081   inline static constexpr T get_bco_diff(const T& first, const T& second)
0082   {
0083     return first < second ? (second - first) : (first - second);
0084   }
0085 
0086   // define limit for matching two fee_bco
0087   static constexpr unsigned int m_max_multiplier_adjustment_count = 1000;
0088 
0089   // define limit for matching two fee_bco
0090   static constexpr unsigned int m_max_fee_bco_diff = 10;
0091 
0092   // define limit for matching fee_bco to fee_bco_predicted
0093   static constexpr unsigned int m_max_gtm_bco_diff = 100;
0094 
0095   // needed to avoid memory leak. Assumes that we will not be assembling more than 50 events at the same time
0096   static constexpr unsigned int m_max_matching_data_size = 50;
0097 
0098   //! copied from micromegas/MicromegasDefs.h, not available here
0099   static constexpr int m_nchannels_fee = 256;
0100 
0101   /* see: https://git.racf.bnl.gov/gitea/Instrumentation/sampa_data/src/branch/fmtv2/README.md */
0102   enum SampaDataType
0103   {
0104     HEARTBEAT_T = 0b000,
0105     TRUNCATED_DATA_T = 0b001,
0106     TRUNCATED_TRIG_EARLY_DATA_T = 0b011,
0107     NORMAL_DATA_T = 0b100,
0108     LARGE_DATA_T = 0b101,
0109     TRIG_EARLY_DATA_T = 0b110,
0110     TRIG_EARLY_LARGE_DATA_T = 0b111,
0111   };
0112 
0113   /* see: https://git.racf.bnl.gov/gitea/Instrumentation/sampa_data/src/branch/fmtv2/README.md */
0114   enum ModeBitType
0115   {
0116     BX_COUNTER_SYNC_T = 0,
0117     ELINK_HEARTBEAT_T = 1,
0118     SAMPA_EVENT_TRIGGER_T = 2,
0119     CLEAR_LV1_LAST_T = 6,
0120     CLEAR_LV1_ENDAT_T = 7
0121   };
0122 }  // namespace
0123 
0124 // this is the clock multiplier from lvl1 to fee clock
0125 /* todo: should replace with actual rational number for John K. */
0126 double MicromegasBcoMatchingInformation_v1::m_multiplier = 4.262916255;
0127 
0128 //___________________________________________________
0129 std::optional<uint32_t> MicromegasBcoMatchingInformation_v1::get_predicted_fee_bco(uint64_t gtm_bco) const
0130 {
0131   // check proper initialization
0132   if (!is_verified())
0133   {
0134     return std::nullopt;
0135   }
0136 
0137   // get gtm bco difference with proper rollover accounting
0138   const uint64_t gtm_bco_difference = (gtm_bco >= m_gtm_bco_first) ? (gtm_bco - m_gtm_bco_first) : (gtm_bco + (1ULL << 40U) - m_gtm_bco_first);
0139 
0140   // convert to fee bco, and truncate to 20 bits
0141   const uint64_t fee_bco_predicted = m_fee_bco_first + get_adjusted_multiplier() * gtm_bco_difference;
0142   return uint32_t(fee_bco_predicted & 0xFFFFFU);
0143 }
0144 
0145 //___________________________________________________
0146 void MicromegasBcoMatchingInformation_v1::print_gtm_bco_information() const
0147 {
0148   if (!m_gtm_bco_list.empty())
0149   {
0150     std::cout
0151         << "MicromegasBcoMatchingInformation_v1::print_gtm_bco_information -"
0152         << " gtm_bco: " << std::hex << m_gtm_bco_list << std::dec
0153         << std::endl;
0154 
0155     // also print predicted fee bco
0156     if (is_verified())
0157     {
0158       std::list<uint32_t> fee_bco_predicted_list;
0159       std::transform(
0160           m_gtm_bco_list.begin(),
0161           m_gtm_bco_list.end(),
0162           std::back_inserter(fee_bco_predicted_list),
0163           [this](const uint64_t& gtm_bco)
0164           { return get_predicted_fee_bco(gtm_bco).value(); });
0165 
0166       std::cout
0167           << "MicromegasBcoMatchingInformation_v1::print_gtm_bco_information -"
0168           << " fee_bco_predicted: " << std::hex << fee_bco_predicted_list << std::dec
0169           << std::endl;
0170     }
0171   }
0172 }
0173 
0174 //___________________________________________________
0175 void MicromegasBcoMatchingInformation_v1::save_gtm_bco_information(Packet* packet)
0176 {
0177   // append gtm_bco from taggers in this event to packet-specific list of available lv1_bco
0178   const int n_tagger = packet->lValue(0, "N_TAGGER");
0179   for (int t = 0; t < n_tagger; ++t)
0180   {
0181     // save level1 trigger bco
0182     const bool is_lvl1 = static_cast<uint8_t>(packet->lValue(t, "IS_LEVEL1_TRIGGER"));
0183     if (is_lvl1)
0184     {
0185       const uint64_t gtm_bco = static_cast<uint64_t>(packet->lValue(t, "BCO"));
0186       m_gtm_bco_list.push_back(gtm_bco);
0187       continue;
0188     }
0189 
0190     // also save ENDDAT bco
0191     const bool is_endat = static_cast<uint8_t>(packet->lValue(t, "IS_ENDAT"));
0192     if( is_endat )
0193     {
0194       const uint64_t gtm_bco = static_cast<uint64_t>(packet->lValue(t, "BCO"));
0195 
0196       // add to list if difference to last entry is big enough
0197       if( m_gtm_bco_list.empty() || (gtm_bco-m_gtm_bco_list.back()) > 10 )
0198       {  m_gtm_bco_list.push_back(gtm_bco); }
0199 
0200       continue;
0201     }
0202 
0203     // also save hearbeat bco
0204     const bool is_modebit = static_cast<uint8_t>(packet->lValue(t, "IS_MODEBIT"));
0205     if (is_modebit)
0206     {
0207       // get modebits
0208       uint64_t modebits = static_cast<uint8_t>(packet->lValue(t, "MODEBITS"));
0209       if (modebits & (1U << ELINK_HEARTBEAT_T))
0210       {
0211         const uint64_t gtm_bco = static_cast<uint64_t>(packet->lValue(t, "BCO"));
0212         m_gtm_bco_list.push_back(gtm_bco);
0213         continue;
0214       }
0215     }
0216   }
0217 }
0218 
0219 //___________________________________________________
0220 bool MicromegasBcoMatchingInformation_v1::find_reference_from_modebits(Packet* packet)
0221 {
0222   // append gtm_bco from taggers in this event to packet-specific list of available lv1_bco
0223   const int n_tagger = packet->lValue(0, "N_TAGGER");
0224   for (int t = 0; t < n_tagger; ++t)
0225   {
0226     const bool is_modebit = static_cast<uint8_t>(packet->lValue(t, "IS_MODEBIT"));
0227     if (is_modebit)
0228     {
0229       // get modebits
0230       uint64_t modebits = static_cast<uint8_t>(packet->lValue(t, "MODEBITS"));
0231       if (modebits & (1U << BX_COUNTER_SYNC_T))
0232       {
0233         std::cout << "MicromegasBcoMatchingInformation_v1::find_reference_from_modebits"
0234                   << " - packet: " << packet->getIdentifier()
0235                   << " found reference from modebits"
0236                   << std::endl;
0237 
0238         // get BCO and assign
0239         const uint64_t gtm_bco = static_cast<uint64_t>(packet->lValue(t, "BCO"));
0240         m_gtm_bco_first = gtm_bco;
0241         m_fee_bco_first = 0;
0242         m_verified_from_modebits = true;
0243         return true;
0244       }
0245     }
0246   }
0247 
0248   return false;
0249 }
0250 
0251 //___________________________________________________
0252 bool MicromegasBcoMatchingInformation_v1::find_reference_from_data(Packet* packet)
0253 {
0254   // store gtm bco and diff to previous in an array
0255   std::vector<uint64_t> gtm_bco_list;
0256   std::vector<uint64_t> gtm_bco_diff_list;
0257   for (const auto& gtm_bco : m_gtm_bco_list)
0258   {
0259     if (!gtm_bco_list.empty())
0260     {
0261       // add difference to last
0262       // get gtm bco difference with proper rollover accounting
0263       const uint64_t gtm_bco_difference = (gtm_bco >= gtm_bco_list.back()) ? (gtm_bco - gtm_bco_list.back()) : (gtm_bco + (1ULL << 40U) - gtm_bco_list.back());
0264 
0265       gtm_bco_diff_list.push_back(get_adjusted_multiplier() * gtm_bco_difference);
0266     }
0267 
0268     // append to list
0269     gtm_bco_list.push_back(gtm_bco);
0270   }
0271 
0272   // print all differences
0273   if (verbosity())
0274   {
0275     std::cout << "MicromegasBcoMatchingInformation_v1::find_reference_from_data - gtm_bco_diff_list: " << gtm_bco_diff_list << std::endl;
0276   }
0277 
0278   uint32_t fee_bco_prev = 0;
0279   bool has_fee_bco_prev = false;
0280 
0281   // get number of waveforms
0282   const int n_waveform = packet->iValue(0, "NR_WF");
0283   for (int iwf = 0; iwf < n_waveform; ++iwf)
0284   {
0285     // check type
0286     const unsigned short type = packet->iValue(iwf, "TYPE");
0287 
0288     // skip heartbeat waveforms
0289     if (type == HEARTBEAT_T)
0290     {
0291       continue;
0292     }
0293 
0294     // check channel
0295     const unsigned short channel = packet->iValue(iwf, "CHANNEL");
0296 
0297     // bound check
0298     if (channel >= m_nchannels_fee)
0299     {
0300       continue;
0301     }
0302 
0303     const uint32_t fee_bco = static_cast<uint32_t>(packet->iValue(iwf, "BCO"));
0304     if (!has_fee_bco_prev)
0305     {
0306       fee_bco_prev = fee_bco;
0307       has_fee_bco_prev = true;
0308     }
0309 
0310     // calculate difference
0311     const uint64_t fee_bco_diff = get_bco_diff(fee_bco, fee_bco_prev);
0312 
0313     // discard identical fee_bco
0314     if (fee_bco_diff < m_max_fee_bco_diff)
0315     {
0316       continue;
0317     }
0318 
0319     std::cout << "MicromegasBcoMatchingInformation_v1::find_reference_from_data - fee_bco_diff: " << fee_bco_diff << std::endl;
0320 
0321     // look for matching diff in gtm_bco array
0322     for (size_t i = 0; i < gtm_bco_diff_list.size(); ++i)
0323     {
0324       uint64_t sum = 0;
0325       for (size_t j = i; j < gtm_bco_diff_list.size(); ++j)
0326       {
0327         sum += gtm_bco_diff_list[j];
0328         if (get_bco_diff(sum, fee_bco_diff) < m_max_fee_bco_diff)
0329         {
0330           m_verified_from_data = true;
0331           m_gtm_bco_first = gtm_bco_list[i];
0332           m_fee_bco_first = fee_bco_prev;
0333 
0334           if (verbosity())
0335           {
0336             std::cout << "MicromegasBcoMatchingInformation_v1::find_reference_from_data - matching is verified" << std::endl;
0337             std::cout
0338                 << "MicromegasBcoMatchingInformation_v1::find_reference_from_data -"
0339                 << " m_gtm_bco_first: " << std::hex << m_gtm_bco_first << std::dec
0340                 << std::endl;
0341             std::cout
0342                 << "MicromegasBcoMatchingInformation_v1::find_reference_from_data -"
0343                 << " m_fee_bco_first: " << std::hex << m_fee_bco_first << std::dec
0344                 << std::endl;
0345           }
0346           return true;
0347         }
0348       }
0349     }
0350 
0351     // update previous fee_bco
0352     fee_bco_prev = fee_bco;
0353   }
0354   return false;
0355 }
0356 
0357 //___________________________________________________
0358 std::optional<uint64_t> MicromegasBcoMatchingInformation_v1::find_gtm_bco(uint32_t fee_bco)
0359 {
0360   // make sure the bco matching is properly initialized
0361   if (!is_verified())
0362   {
0363     return std::nullopt;
0364   }
0365   // find matching gtm bco in map
0366   const auto bco_matching_iter = std::find_if(
0367       m_bco_matching_list.begin(),
0368       m_bco_matching_list.end(),
0369       [fee_bco](const m_bco_matching_pair_t& pair)
0370       { return get_bco_diff(pair.first, fee_bco) < m_max_fee_bco_diff; });
0371 
0372   if (bco_matching_iter != m_bco_matching_list.end())
0373   {
0374     return bco_matching_iter->second;
0375   }
0376   else
0377   {
0378     // find element for which predicted fee_bco matches fee_bco, within limit
0379     const auto iter = std::find_if(
0380         m_gtm_bco_list.begin(),
0381         m_gtm_bco_list.end(),
0382         [this, fee_bco](const uint64_t& gtm_bco)
0383         { return get_bco_diff(get_predicted_fee_bco(gtm_bco).value(), fee_bco) < m_max_gtm_bco_diff; });
0384 
0385     // check
0386     if (iter != m_gtm_bco_list.end())
0387     {
0388       const auto gtm_bco = *iter;
0389       if (verbosity())
0390       {
0391         const auto fee_bco_predicted = get_predicted_fee_bco(gtm_bco).value();
0392         const auto fee_bco_diff = get_bco_diff(fee_bco_predicted, fee_bco);
0393 
0394         std::cout << "MicromegasBcoMatchingInformation_v1::find_gtm_bco -"
0395                   << std::hex
0396                   << " fee_bco: 0x" << fee_bco
0397                   << " predicted: 0x" << fee_bco_predicted
0398                   << " gtm_bco: 0x" << gtm_bco
0399                   << std::dec
0400                   << " difference: " << fee_bco_diff
0401                   << std::endl;
0402       }
0403 
0404       // save fee_bco and gtm_bco matching in map
0405       m_bco_matching_list.emplace_back(fee_bco, gtm_bco);
0406 
0407       // remove gtm bco from runing list
0408       m_gtm_bco_list.erase(iter);
0409 
0410       // update clock adjustment
0411       update_multiplier_adjustment(gtm_bco, fee_bco);
0412 
0413       return gtm_bco;
0414     }
0415     else
0416     {
0417       if (m_orphans.insert(fee_bco).second)
0418       {
0419         if (verbosity())
0420         {
0421           // find element for which predicted fee_bco is the closest to request
0422           const auto iter2 = std::min_element(
0423               m_gtm_bco_list.begin(),
0424               m_gtm_bco_list.end(),
0425               [this, fee_bco](const uint64_t& first, const uint64_t& second)
0426               { return get_bco_diff(get_predicted_fee_bco(first).value(), fee_bco) < get_bco_diff(get_predicted_fee_bco(second).value(), fee_bco); });
0427 
0428           const int fee_bco_diff = (iter2 != m_gtm_bco_list.end()) ? get_bco_diff(get_predicted_fee_bco(*iter2).value(), fee_bco) : -1;
0429 
0430           std::cout << "MicromegasBcoMatchingInformation_v1::find_gtm_bco -"
0431                     << std::hex
0432                     << " fee_bco: 0x" << fee_bco
0433                     << std::dec
0434                     << " gtm_bco: none"
0435                     << " difference: " << fee_bco_diff
0436                     << std::endl;
0437         }
0438       }
0439       return std::nullopt;
0440     }
0441   }
0442 
0443   // never reached
0444   return std::nullopt;
0445 }
0446 
0447 //___________________________________________________
0448 void MicromegasBcoMatchingInformation_v1::cleanup()
0449 {
0450   // remove old gtm_bco and matching
0451   while (m_gtm_bco_list.size() > m_max_matching_data_size)
0452   {
0453     m_gtm_bco_list.pop_front();
0454   }
0455   while (m_bco_matching_list.size() > m_max_matching_data_size)
0456   {
0457     m_bco_matching_list.pop_front();
0458   }
0459 
0460   // clear orphans
0461   m_orphans.clear();
0462 }
0463 
0464 //___________________________________________________
0465 void MicromegasBcoMatchingInformation_v1::cleanup(uint64_t ref_bco)
0466 {
0467   // erase all elements from bco_list that are less than or equal to ref_bco
0468   m_gtm_bco_list.erase( std::remove_if( m_gtm_bco_list.begin(), m_gtm_bco_list.end(), [ref_bco](const uint64_t& bco) { return bco<=ref_bco; }), m_gtm_bco_list.end() );
0469 
0470   // erase all elements from bco_list that are less than or equal to ref_bco
0471   m_bco_matching_list.erase( std::remove_if( m_bco_matching_list.begin(), m_bco_matching_list.end(), [ref_bco](const m_bco_matching_pair_t& pair) { return pair.second<=ref_bco; }), m_bco_matching_list.end() );
0472 
0473   // clear orphans
0474   m_orphans.clear();
0475 }
0476 
0477 //___________________________________________________
0478 double MicromegasBcoMatchingInformation_v1::get_adjusted_multiplier() const
0479 {
0480   return m_multiplier + m_multiplier_adjustment;
0481 }
0482 
0483 //___________________________________________________
0484 void MicromegasBcoMatchingInformation_v1::update_multiplier_adjustment(uint64_t gtm_bco, uint32_t fee_bco)
0485 {
0486   // check that references are valid
0487   if (!is_verified())
0488   {
0489     return;
0490   }
0491 
0492   // skip if trivial
0493   if (gtm_bco == m_gtm_bco_first)
0494   {
0495     return;
0496   }
0497 
0498   const uint32_t fee_bco_predicted = get_predicted_fee_bco(gtm_bco).value();
0499   const double delta_fee_bco = double(fee_bco) - double(fee_bco_predicted);
0500   const double gtm_bco_difference = (gtm_bco >= m_gtm_bco_first) ? (gtm_bco - m_gtm_bco_first) : (gtm_bco + (1ULL << 40U) - m_gtm_bco_first);
0501 
0502   m_multiplier_adjustment_numerator += gtm_bco_difference * delta_fee_bco;
0503   m_multiplier_adjustment_denominator += gtm_bco_difference * gtm_bco_difference;
0504   ++m_multiplier_adjustment_count;
0505 
0506   if (verbosity())
0507   {
0508     const auto default_precision{std::cout.precision()};
0509     std::cout << "MicromegasBcoMatchingInformation_v1::update_multiplier_adjustment -"
0510               << " m_multiplier_adjustment_count: " << m_multiplier_adjustment_count
0511               << std::setprecision(10)
0512               << " m_multiplier: " << get_adjusted_multiplier()
0513               << " adjustment: " << m_multiplier_adjustment_numerator / m_multiplier_adjustment_denominator
0514               << " m_multiplier_adjusted: " << get_adjusted_multiplier() + m_multiplier_adjustment_numerator / m_multiplier_adjustment_denominator
0515               << std::setprecision(default_precision)
0516               << std::endl;
0517   }
0518 
0519   // update multiplier
0520   if (m_multiplier_adjustment_count > m_max_multiplier_adjustment_count)
0521   {
0522     m_multiplier_adjustment += m_multiplier_adjustment_numerator / m_multiplier_adjustment_denominator;
0523     m_multiplier_adjustment_numerator = 0;
0524     m_multiplier_adjustment_denominator = 0;
0525     m_multiplier_adjustment_count = 0;
0526   }
0527 }