Back to home page

sPhenix code displayed by LXR

 
 

    


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

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