From a4734cb8d3c05a926cd2702ecde44162cd2fed5c Mon Sep 17 00:00:00 2001
From: "s.zharko@gsi.de" <s.zharko@gsi.de>
Date: Wed, 5 Feb 2025 20:03:41 +0100
Subject: [PATCH] online: BMON digi calibrator

---
 algo/CMakeLists.txt                  |   1 +
 algo/detectors/bmon/Calibrate.cxx    | 166 +++++++++++++++++++++++++++
 algo/detectors/bmon/Calibrate.h      |  50 ++++++++
 algo/detectors/bmon/CalibrateSetup.h |  67 +++++++++++
 algo/detectors/bmon/UnpackMS.cxx     |   1 +
 algo/global/ParFiles.cxx             |  12 +-
 algo/global/ParFiles.h               |   2 +
 algo/global/Reco.cxx                 |  71 ++++++++++++
 algo/global/Reco.h                   |   2 +
 core/data/bmon/CbmBmonDigi.h         |  27 ++---
 core/data/tof/CbmTofAddress.cxx      |  18 +++
 core/data/tof/CbmTofAddress.h        |  13 ++-
 core/data/tof/CbmTofDigi.h           |  29 ++---
 external/InstallParameter.cmake      |   2 +
 14 files changed, 431 insertions(+), 30 deletions(-)
 create mode 100644 algo/detectors/bmon/Calibrate.cxx
 create mode 100644 algo/detectors/bmon/Calibrate.h
 create mode 100644 algo/detectors/bmon/CalibrateSetup.h

diff --git a/algo/CMakeLists.txt b/algo/CMakeLists.txt
index 09ee075af5..58ac4397e4 100644
--- a/algo/CMakeLists.txt
+++ b/algo/CMakeLists.txt
@@ -95,6 +95,7 @@ set(SRCS
   evselector/DigiEventSelector.cxx
   evselector/DigiEventSelectorConfig.cxx
   unpack/CommonUnpacker.cxx
+  detectors/bmon/Calibrate.cxx
   detectors/sts/ChannelMaskSet.cxx
   detectors/sts/ReadoutConfig.cxx
   detectors/sts/HitfinderChain.cxx
diff --git a/algo/detectors/bmon/Calibrate.cxx b/algo/detectors/bmon/Calibrate.cxx
new file mode 100644
index 0000000000..70d99daeb4
--- /dev/null
+++ b/algo/detectors/bmon/Calibrate.cxx
@@ -0,0 +1,166 @@
+/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergei Zharko [committer] */
+
+/// \file   Calibrate.h
+/// \brief  Calibrator for the BMON digis (implementation)
+/// \since  04.02.2025
+/// \author Sergei Zharko <s.zharko@gsi.de>
+
+#include "Calibrate.h"
+
+#include "AlgoFairloggerCompat.h"
+#include "CbmTofAddress.h"
+#include "util/TimingsFormat.h"
+
+#include <chrono>
+
+using cbm::algo::bmon::Calibrate;
+using cbm::algo::bmon::CalibrateSetup;
+using fles::Subsystem;
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+Calibrate::Calibrate(CalibrateSetup setup) : fSetup(setup), fSmOffset(1, 0), fRpcOffset(1, 0)
+{
+  // Initialize offset arrays for channel deadtime check
+  int32_t NbSm  = fSetup.NbSm;
+  int32_t NbRpc = fSetup.NbRpc;
+  for (int32_t Sm = 0; Sm < NbSm; Sm++) {
+    fSmOffset.push_back(fSmOffset.back() + NbRpc);
+    for (int32_t Rpc = 0; Rpc < NbRpc; Rpc++) {
+      int32_t NbChan = fSetup.rpcs[Sm * NbRpc + Rpc].chanPar.size();
+      fRpcOffset.push_back(fRpcOffset.back() + 2 * NbChan);  //Factor 2 for channel sides
+    }
+  }
+
+  // **** DEBUG: BEGIN
+  for (size_t iO = 0; iO < fSmOffset.size(); ++iO) {
+    L_(info) << "SM offset: " << iO << ", " << fSmOffset[iO];
+  }
+  for (size_t iO = 0; iO < fRpcOffset.size(); ++iO) {
+    L_(info) << "RPC offset: " << iO << ", " << fRpcOffset[iO];
+  }
+  // **** DEBUG: END
+
+  fChannelDeadTime = std::vector<double>(fRpcOffset.back(), std::numeric_limits<double>::quiet_NaN());
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+Calibrate::resultType Calibrate::operator()(gsl::span<const CbmBmonDigi> digiIn)
+{
+  xpu::push_timer("TofCalibrate");
+  xpu::t_add_bytes(digiIn.size_bytes());
+
+  // --- Output data
+  resultType result = {};
+
+  auto& calDigiOut = result.first;
+  auto& monitor    = result.second;
+  calDigiOut.reserve(digiIn.size());
+
+  std::fill(fChannelDeadTime.begin(), fChannelDeadTime.end(), std::numeric_limits<double>::quiet_NaN());
+
+  for (const auto& digi : digiIn) {
+    uint32_t address = static_cast<uint32_t>(digi.GetAddress());
+    const int32_t SmType{CbmTofAddress::GetSmType(address)};
+    const int32_t Sm{CbmTofAddress::GetSmId(address)};
+    const int32_t Rpc{CbmTofAddress::GetRpcId(address)};
+    const int NbRpc{fSetup.NbRpc};
+
+    int32_t Chan{CbmTofAddress::GetChannelId(address)};
+    int32_t Side{CbmTofAddress::GetChannelSide(address)};
+
+    // NOTE: Follow the hack in the CbmTofEventClusterizer::Exec() for the BMON digis and change the address
+    //       of calibrated digis. In general it is incorrect and needs further investigation.
+    uint32_t calAddress = address;
+    if (Side == 1) {
+      Side = 0;
+      Chan += 6;
+      calAddress = CbmTofAddress::GetUniqueAddress(Sm, Rpc, Chan, Side, SmType);
+    }
+
+    auto& rpcs = fSetup.rpcs;
+    if (SmType != fSetup.SmType || Sm * NbRpc + Rpc >= static_cast<int>(rpcs.size())) {
+      monitor.fDigiCalibUnknownRPC++;
+      L_(error) << "Unknown BMON digi address: " << CbmTofAddress::ToString(calAddress);
+      continue;
+    }
+
+    CalibrateSetup::Rpc& rpcPar      = fSetup.rpcs[Sm * NbRpc + Rpc];
+    CalibrateSetup::Channel& chanPar = rpcPar.chanPar[Chan];
+
+    // Check dead time
+    // FIXME: BMON digis (must) have only one side -> Fix the offsets, when mcbm2024_05 will be fixed
+    const size_t chanIdx  = fRpcOffset[fSmOffset[Sm] + Rpc] + Chan + Side * rpcPar.chanPar.size();
+    const double deadTime = fChannelDeadTime[chanIdx];
+
+    if (!std::isnan(deadTime) && digi.GetTime() <= deadTime) {
+      fChannelDeadTime[chanIdx] = digi.GetTime() + rpcPar.channelDeadtime;
+      monitor.fDigiDeadTimeCount++;
+      continue;
+    }
+    fChannelDeadTime[chanIdx] = digi.GetTime() + rpcPar.channelDeadtime;
+
+    // Create calibrated digi
+    CbmBmonDigi& pCalDigi = calDigiOut.emplace_back(digi);
+    pCalDigi.SetAddress(calAddress);
+
+    // Check if channel sides need to be swapped
+    if (rpcPar.swapChannelSides) {
+      pCalDigi.SetAddress(CbmTofAddress::GetUniqueAddress(Sm, Rpc, Chan, (0 == Side) ? 1 : 0, SmType));
+    }
+
+    // calibrate Digi Time
+    pCalDigi.SetTime(pCalDigi.GetTime() - chanPar.vCPTOff[Side]);
+
+    // subtract Offset
+    const double dTot = std::max(pCalDigi.GetCharge() - chanPar.vCPTotOff[Side], 0.001);
+
+    // calibrate Digi charge (corresponds to TOF ToT)
+    pCalDigi.SetCharge(dTot * chanPar.vCPTotGain[Side]);
+
+    // walk correction
+    std::vector<double>& walk = chanPar.vCPWalk[Side];
+    const double dTotBinSize  = (rpcPar.TOTMax - rpcPar.TOTMin) / rpcPar.numClWalkBinX;
+    int32_t iWx               = std::max((int32_t)((pCalDigi.GetCharge() - rpcPar.TOTMin) / dTotBinSize), 0);
+    iWx                       = std::min(iWx, rpcPar.numClWalkBinX - 1);
+
+    const double dDTot = (pCalDigi.GetCharge() - rpcPar.TOTMin) / dTotBinSize - (double) iWx - 0.5;
+    double dWT         = walk[iWx];
+
+    // linear interpolation to next bin
+    if (dDTot > 0) {
+      if (iWx < rpcPar.numClWalkBinX - 1) {
+        dWT += dDTot * (walk[iWx + 1] - walk[iWx]);
+      }
+    }
+    else {
+      if (0 < iWx) {
+        dWT -= dDTot * (walk[iWx - 1] - walk[iWx]);
+      }
+    }
+    pCalDigi.SetTime(pCalDigi.GetTime() - dWT);  // calibrate Digi Time
+  }
+
+  /// Sort the buffers of hits due to the time offsets applied
+  // (insert-sort faster than std::sort due to pre-sorting)
+  for (std::size_t i = 1; i < calDigiOut.size(); ++i) {
+    CbmTofDigi digi = calDigiOut[i];
+    std::size_t j   = i;
+    while (j > 0 && calDigiOut[j - 1].GetTime() > digi.GetTime()) {
+      calDigiOut[j] = calDigiOut[j - 1];
+      --j;
+    }
+    calDigiOut[j] = digi;
+  }
+
+  //  Kept for possible unsorted input
+  //  std::sort(calDigiOut.begin(), calDigiOut.end(),
+  //  [](const CbmTofDigi& a, const CbmTofDigi& b) -> bool { return a.GetTime() < b.GetTime(); });
+
+  monitor.fTime     = xpu::pop_timer();
+  monitor.fNumDigis = digiIn.size();
+  return result;
+}
diff --git a/algo/detectors/bmon/Calibrate.h b/algo/detectors/bmon/Calibrate.h
new file mode 100644
index 0000000000..291a6882b4
--- /dev/null
+++ b/algo/detectors/bmon/Calibrate.h
@@ -0,0 +1,50 @@
+/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergei Zharko [committer] */
+
+/// \file   Calibrate.h
+/// \brief  Calibratior for the BMON digis
+/// \since  04.02.2025
+/// \author Sergei Zharko <s.zharko@gsi.de>
+
+#pragma once
+
+#include "CbmBmonDigi.h"
+#include "PartitionedVector.h"
+#include "bmon/CalibrateSetup.h"
+#include "tof/Calibrate.h"  // for the monitor data
+#include "tof/Clusterizer.h"
+
+#include <gsl/span>
+#include <optional>
+#include <sstream>
+#include <vector>
+
+#include <xpu/host.h>
+
+namespace cbm::algo::bmon
+{
+  // NOTE: reusing TOF monitor
+  using CalibrateMonitorData = tof::CalibrateMonitorData;
+
+  /// \class Calibrate
+  /// \brief Algorithm to calibrate BMon digis
+  class Calibrate {
+   public:
+    using resultType = std::pair<std::vector<CbmBmonDigi>, CalibrateMonitorData>;
+
+    /// \brief Constructor
+    /// \param params  Calibration parameters
+    explicit Calibrate(CalibrateSetup params);
+
+    /// \brief Calibrates a portion of digis
+    /// \param digiIn  A portion of digis to calibrate
+    resultType operator()(gsl::span<const CbmBmonDigi> digiIn);
+
+   private:
+    CalibrateSetup fSetup;                 ///< Parameters of calibrator
+    std::vector<double> fChannelDeadTime;  ///< Storage for dead time check
+    std::vector<size_t> fSmOffset;         ///< Super module offset
+    std::vector<size_t> fRpcOffset;        ///< RPC offset
+  };
+}  // namespace cbm::algo::bmon
diff --git a/algo/detectors/bmon/CalibrateSetup.h b/algo/detectors/bmon/CalibrateSetup.h
new file mode 100644
index 0000000000..0d9dedd93c
--- /dev/null
+++ b/algo/detectors/bmon/CalibrateSetup.h
@@ -0,0 +1,67 @@
+/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergei Zharko [committer] */
+
+/// \file   CalibrateSetup.h
+/// \brief  Configuration of the calibrator for the BMON digis
+/// \since  04.02.2025
+/// \author Sergei Zharko <s.zharko@gsi.de>
+
+#pragma once
+
+#include "Definitions.h"
+#include "yaml/Property.h"
+
+#include <array>
+#include <map>
+#include <string>
+#include <vector>
+
+namespace cbm::algo::bmon
+{
+  /// \struct  CalibrateSetup
+  /// \brief   BMON calibration per channel
+  struct CalibrateSetup {
+
+    struct Channel {
+      std::vector<double> vCPTOff;
+      std::vector<double> vCPTotGain;
+      std::vector<double> vCPTotOff;
+      std::vector<std::vector<double>> vCPWalk;
+
+      CBM_YAML_PROPERTIES(yaml::Property(&Channel::vCPTOff, "vCPTOff", "CPT offset"),
+                        yaml::Property(&Channel::vCPTotGain, "vCPTotGain", "CP time over threshold gain"),
+                        yaml::Property(&Channel::vCPTotOff, "vCPTotOff", "CP time over threshold offset"),
+                        yaml::Property(&Channel::vCPWalk, "vCPWalk", "CP walk correction", YAML::Block, YAML::Flow));
+    };
+
+    struct Rpc {
+      int32_t numClWalkBinX;
+      double TOTMax;
+      double TOTMin;
+      bool swapChannelSides;
+      double channelDeadtime;
+      std::vector<Channel> chanPar;
+
+      CBM_YAML_PROPERTIES(yaml::Property(&Rpc::numClWalkBinX, "numClWalkBinX", "number of walk correction bins"),
+                        yaml::Property(&Rpc::TOTMax, "TOTMax", "maximum time over threshold"),
+                        yaml::Property(&Rpc::TOTMin, "TOTMin", "minimum time over threshold"),
+                        yaml::Property(&Rpc::swapChannelSides, "swapChannelSides", "flag for swapping channel sides"),
+                        yaml::Property(&Rpc::channelDeadtime, "channelDeadtime", "channel dead time"),
+                        yaml::Property(&Rpc::chanPar, "chanPar", "channel parameters"));
+    };
+
+    /* Members */
+    int32_t SmType;
+    int32_t NbSm;
+    int32_t NbRpc;
+    std::vector<Rpc> rpcs;
+
+    CBM_YAML_PROPERTIES(
+      yaml::Property(&CalibrateSetup::SmType, "SmType", "Super module type"),
+      yaml::Property(&CalibrateSetup::NbSm, "NbSm", "Number of SMs"),
+      yaml::Property(&CalibrateSetup::NbRpc, "NbRpc", "Number of RPCs"),
+      yaml::Property(&CalibrateSetup::rpcs, "rpcs", "Parameters of RPCs"));
+  };
+
+}  // namespace cbm::algo::bmon
diff --git a/algo/detectors/bmon/UnpackMS.cxx b/algo/detectors/bmon/UnpackMS.cxx
index b4fbf123e1..22d56e90d1 100644
--- a/algo/detectors/bmon/UnpackMS.cxx
+++ b/algo/detectors/bmon/UnpackMS.cxx
@@ -5,6 +5,7 @@
 #include "UnpackMS.h"
 
 #include "AlgoFairloggerCompat.h"
+#include "CbmTofAddress.h"
 
 #include <cassert>
 #include <cmath>
diff --git a/algo/global/ParFiles.cxx b/algo/global/ParFiles.cxx
index 8eb07742a6..eabec37b4b 100644
--- a/algo/global/ParFiles.cxx
+++ b/algo/global/ParFiles.cxx
@@ -25,7 +25,9 @@ ParFiles::ParFiles(uint32_t runId)
   switch (setup) {
 
     case Setup::mCBM2022:
-      bmon.readout = "BmonReadout_mcbm2022.yaml";
+      bmon.readout   = "BmonReadout_mcbm2022.yaml";
+      bmon.calibrate = "BmonCalibratePar_mcbm2022.yaml";
+      bmon.hitfinder = "BmonHitfinderPar_mcbm2022.yaml";
 
       sts.readout   = "StsReadout_mcbm2022.yaml";
       sts.chanMask  = "StsChannelMaskSet_mcbm2022.yaml";
@@ -46,7 +48,9 @@ ParFiles::ParFiles(uint32_t runId)
       break;
 
     case Setup::mCBM2024_03:
-      bmon.readout = "BmonReadout_mcbm2024.yaml";
+      bmon.readout   = "BmonReadout_mcbm2024.yaml";
+      bmon.calibrate = "BmonCalibratePar_mcbm2024.yaml";
+      bmon.hitfinder = "BmonHitfinderPar_mcbm2024.yaml";
 
       sts.readout   = "StsReadout_mcbm2024.yaml";
       sts.chanMask  = "StsChannelMaskSet_mcbm2024.yaml";
@@ -67,7 +71,9 @@ ParFiles::ParFiles(uint32_t runId)
       break;
 
     case Setup::mCBM2024_05:
-      bmon.readout = "BmonReadout_mcbm2024.yaml";
+      bmon.readout   = "BmonReadout_mcbm2024.yaml";
+      bmon.calibrate = "mcbm2024_05/BmonCalibratePar.yaml";
+      bmon.hitfinder = "mcbm2024_05/BmonHitfinderPar.yaml";
 
       sts.readout   = "StsReadout_mcbm2024.yaml";
       sts.chanMask  = "StsChannelMaskSet_mcbm2024.yaml";
diff --git a/algo/global/ParFiles.h b/algo/global/ParFiles.h
index 9c966ac637..691e27b39b 100644
--- a/algo/global/ParFiles.h
+++ b/algo/global/ParFiles.h
@@ -26,6 +26,8 @@ namespace cbm::algo
 
     struct {
       fs::path readout;
+      fs::path calibrate;
+      fs::path hitfinder;
     } bmon;
 
     struct {
diff --git a/algo/global/Reco.cxx b/algo/global/Reco.cxx
index e9a0a7a9a0..9bdc35bb6b 100644
--- a/algo/global/Reco.cxx
+++ b/algo/global/Reco.cxx
@@ -14,6 +14,7 @@
 #include "RecoGeneralQa.h"
 #include "StsDigiQa.h"
 #include "TrackingSetup.h"
+#include "bmon/Calibrate.h"
 #include "bmon/ReadoutConfig.h"
 #include "bmon/Unpack.h"
 #include "ca/TrackingChain.h"
@@ -41,6 +42,10 @@
 
 #include <xpu/host.h>
 
+// DEBUG: BEGIN
+#include <set>
+// DEBUG: END
+
 using namespace cbm::algo;
 using fles::Subsystem;
 
@@ -204,6 +209,14 @@ void Reco::Init(const Options& opts)
     fStsHitFinder->SetParameters(hitFinderPars);
   }
 
+  // BMON Hitfinder
+  if (Opts().Has(fles::Subsystem::BMON) && Opts().Has(Step::LocalReco)) {
+    auto calibSetup = yaml::ReadFromFile<bmon::CalibrateSetup>(opts.ParamsDir() / parFiles.bmon.calibrate);
+    fBmonCalibrator = std::make_unique<bmon::Calibrate>(calibSetup);
+
+    //auto hitfindSetup = yaml::ReadFromFile<bmon::HitfindSetup>(opts.ParamsDir() / parFiles.bmon.hitfinder);
+    //fBmonHitFinder     = std::make_unique<bmon::Hitfind>(hitfindSetup);
+  }
 
   // TOF Hitfinder
   if (Opts().Has(fles::Subsystem::TOF) && Opts().Has(Step::LocalReco)) {
@@ -372,6 +385,64 @@ RecoResults Reco::Run(const fles::Timeslice& ts)
       QueueEvbuildMetrics(evbuildMonitor);
     }
 
+    // ***** DEBUG: BEGIN
+    if constexpr (1) {
+      std::set<uint32_t> bmonFoundAddressesUnp;  // found addresses among unpacked bmon digis
+      std::set<uint32_t> bmonFoundAddressesCal;  // found addresses among clalibrated bmon digis
+
+      int nEvents = 20;  //events.size();
+      for (int iE = 0; iE < nEvents; ++iE) {
+        //L_(info) << "-------------- event #" << iE;
+        const auto& event = events[iE];
+        for (const auto& digi : event.fBmon) {
+          bmonFoundAddressesUnp.insert(digi.GetAddress());
+        }
+
+        // Calibrate TOF digis:
+        auto [tofDigis, tofCalMonitor]   = (*fTofCalibrator)(event.fTof);
+        auto [bmonDigis, bmonCalMonitor] = (*fBmonCalibrator)(event.fBmon);
+
+        for (const auto& digi : bmonDigis) {
+          bmonFoundAddressesCal.insert(digi.GetAddress());
+        }
+
+        double bmonMinT = 1e+20;
+        double bmonMaxT = -1e+20;
+        for (const auto& digi : bmonDigis) {
+          //L_(info) << "BMON: " << digi.GetTime();
+          bmonMinT = std::min(bmonMinT, digi.GetTime());
+          bmonMaxT = std::max(bmonMaxT, digi.GetTime());
+        }
+        double tofMinT = 1e+20;
+        double tofMaxT = -1e+20;
+        for (const auto& digi : tofDigis) {
+          //L_(info) << "TOF: " << digi.GetTime();
+          tofMinT = std::min(tofMinT, digi.GetTime());
+          tofMaxT = std::max(tofMaxT, digi.GetTime());
+        }
+        double stsMinT       = 1e+20;
+        double stsMaxT       = -1e+20;
+        const auto& stsDigis = event.fSts;
+        for (const auto& digi : stsDigis) {
+          //L_(info) << "STS: " << digi.GetTime();
+          stsMinT = std::min(stsMinT, digi.GetTime());
+          stsMaxT = std::max(stsMaxT, digi.GetTime());
+        }
+        L_(info) << "BMON time: " << bmonMinT << ", " << bmonMaxT << ", " << (bmonMaxT - bmonMinT);
+        L_(info) << "TOF time: " << tofMinT << ", " << tofMaxT << ", " << (tofMaxT - tofMinT);
+        L_(info) << "STS time: " << stsMinT << ", " << stsMaxT << ", " << (stsMaxT - stsMinT);
+      }
+      L_(info) << "!!!! Found addresses in BMON after unpacking: ";
+      for (auto address : bmonFoundAddressesUnp) {
+        L_(info) << " - " << CbmTofAddress::ToString(address);
+      }
+      L_(info) << "!!!! Found addresses in BMON after calibration: ";
+      for (auto address : bmonFoundAddressesCal) {
+        L_(info) << " - " << CbmTofAddress::ToString(address);
+      }
+    }
+    // ***** DEBUG: END
+
     // --- Filter data for output
     if (Opts().HasOutput(RecoData::DigiTimeslice)) {
       results.bmonDigis  = std::move(digis.fBmon);
diff --git a/algo/global/Reco.h b/algo/global/Reco.h
index 8e26d35271..982a95cfa4 100644
--- a/algo/global/Reco.h
+++ b/algo/global/Reco.h
@@ -27,6 +27,7 @@ namespace cbm::algo
   namespace bmon
   {
     class Unpack;
+    class Calibrate;
   }
 
   namespace much
@@ -146,6 +147,7 @@ namespace cbm::algo
 
     // BMON
     std::unique_ptr<bmon::Unpack> fBmonUnpack;
+    std::unique_ptr<bmon::Calibrate> fBmonCalibrator;
 
     // MUCH
     std::unique_ptr<much::Unpack> fMuchUnpack;
diff --git a/core/data/bmon/CbmBmonDigi.h b/core/data/bmon/CbmBmonDigi.h
index 200e72d0ce..7f0584e2fc 100644
--- a/core/data/bmon/CbmBmonDigi.h
+++ b/core/data/bmon/CbmBmonDigi.h
@@ -111,19 +111,20 @@ public:
 
 
 private:
-  int32_t fAddress = ToIntegralType<ECbmModuleId>(ECbmModuleId::kBmon);  ///< Unique CBM address
-  double fTime     = -1.;                                                ///< Time of signal in BMON [ns]
-  float fCharge    = -1.;                                                ///< Charge
-
-  friend class boost::serialization::access;
-
-  template<class Archive>
-  void serialize(Archive& ar, const unsigned int /*version*/)
-  {
-    ar& fAddress;
-    ar& fTime;
-    ar& fCharge;
-  }
+ // FIXME: SZh 5.2.2025: change address type int32_t -> uint32_t
+ int32_t fAddress = ToIntegralType<ECbmModuleId>(ECbmModuleId::kBmon);  ///< Unique CBM address
+ double fTime     = -1.;                                                ///< Time of signal in BMON [ns]
+ float fCharge    = -1.;                                                ///< Charge
+
+ friend class boost::serialization::access;
+
+ template<class Archive>
+ void serialize(Archive& ar, const unsigned int /*version*/)
+ {
+   ar& fAddress;
+   ar& fTime;
+   ar& fCharge;
+ }
 
 #ifndef NO_ROOT
   ClassDefNV(CbmBmonDigi, 1);
diff --git a/core/data/tof/CbmTofAddress.cxx b/core/data/tof/CbmTofAddress.cxx
index 0ed83293f2..068504c399 100644
--- a/core/data/tof/CbmTofAddress.cxx
+++ b/core/data/tof/CbmTofAddress.cxx
@@ -11,6 +11,9 @@
 
 #include "CbmTofAddress.h"
 
+#include <iomanip>
+#include <sstream>
+
 // It seems C++ standard force the initialization to be in cxx/cpp file (outside of class definition)
 // When not trivial constant values => To check if it should apply also to simple values maybe?
 /** Offset in bits for Super Module Id in the address field  **/
@@ -40,3 +43,18 @@ const int32_t CbmTofAddress::fgkiStripFullIdMask =
   (((1 << fgkSystemBits) - 1)) + (((1 << fgkSmIdBits) - 1) << fgkSmIdOffset)
   + (((1 << fgkSmTypeBits) - 1) << fgkSmTypeOffset) + (((1 << fgkRpcIdBits) - 1) << fgkRpcIdOffset)
   + (((1 << fgkChannelIdBits) - 1) << fgkChannelIdOffset);
+
+std::string CbmTofAddress::ToString(int32_t address)
+{
+  using std::setfill;
+  using std::setw;
+  std::stringstream msg;
+  msg << std::hex << "0x" << setw(8) << setfill('0') << address << std::dec << setfill(' ');
+  msg << ": SmType=" << setw(3) << CbmTofAddress::GetSmType(address);
+  msg << ", Sm=" << setw(2) << CbmTofAddress::GetSmId(address);
+  msg << ", Rpc=" << setw(2) << CbmTofAddress::GetRpcId(address);
+  msg << ", Ch=" << setw(2) << CbmTofAddress::GetChannelId(address);
+  msg << ", Side=" << setw(1) << CbmTofAddress::GetChannelSide(address);
+  msg << ", RpcType=" << setw(2) << CbmTofAddress::GetRpcType(address);
+  return msg.str();
+}
diff --git a/core/data/tof/CbmTofAddress.h b/core/data/tof/CbmTofAddress.h
index f8954d532e..3abf9b1703 100644
--- a/core/data/tof/CbmTofAddress.h
+++ b/core/data/tof/CbmTofAddress.h
@@ -44,6 +44,7 @@
 #include "CbmTofDetectorId_v12b.h"  // for CbmTofDetectorId_v12b
 
 #include <cstdint>
+#include <string>
 
 class CbmTofAddress : public CbmAddress {
 public:
@@ -91,6 +92,11 @@ public:
    ** @return  systemId
    **/
   static int32_t GetRpcId(uint32_t address) { return ((address >> fgkRpcIdOffset) & ((1 << fgkRpcIdBits) - 1)); };
+  /** Get the Rpc Type from the address
+   ** @param address  Unique address
+   ** @return  systemId
+   **/
+  static int32_t GetRpcType(uint32_t address) { return ((address >> fgkRpcTypeOffset) & ((1 << fgkRpcTypeBits) - 1)); };
   /** Get the Channel Id from the address
    ** @param address  Unique address
    ** @return  systemId
@@ -169,8 +175,13 @@ public:
     return GetUniqueAddress(detId.GetSModule(detIdInput), detId.GetCounter(detIdInput), detId.GetCell(detIdInput), 0,
                             detId.GetSMType(detIdInput));
   };
+  /** String representation of the address
+   ** @param address  Unique address
+   ** @return String representation of the address
+   **/
+  static std::string ToString(int32_t address);
 
-private:
+ private:
   /**
    ** To adapt the address sub-fields repartition in size,
    ** you just need to change number of bits of the two sub-fields changing length.
diff --git a/core/data/tof/CbmTofDigi.h b/core/data/tof/CbmTofDigi.h
index 942c6edb18..7448ad1a72 100644
--- a/core/data/tof/CbmTofDigi.h
+++ b/core/data/tof/CbmTofDigi.h
@@ -109,6 +109,7 @@ public:
   /**
           ** @brief Inherited from CbmDigi.
           **/
+  // FIXME: SZh 5.2.2025: change address type int32_t -> uint32_t
   int32_t GetAddress() const { return fuAddress; };
 
 
@@ -160,6 +161,8 @@ public:
   double GetSide() const { return CbmTofAddress::GetChannelSide(GetAddress()); };
 
   /** Modifiers **/
+
+  // FIXME: SZh 5.2.2025: change address type int32_t -> uint32_t
   void SetAddress(int32_t address) { fuAddress = address; };
   void SetAddress(uint32_t Sm, uint32_t Rpc, uint32_t Channel, uint32_t Side = 0, uint32_t SmType = 0);
   void SetTime(double time) { fdTime = time; };
@@ -169,19 +172,19 @@ public:
 
 
 private:
-  double fdTime;       ///< Absolute time [ps]
-  double fdTot;        ///< Tot [ps]
-  uint32_t fuAddress;  ///< Unique channel address
-
-  friend class boost::serialization::access;
-
-  template<class Archive>
-  void serialize(Archive& ar, const unsigned int /*version*/)
-  {
-    ar& fuAddress;
-    ar& fdTime;
-    ar& fdTot;
-  }
+ double fdTime;       ///< Absolute time [ns]
+ double fdTot;        ///< Tot [ns?]
+ uint32_t fuAddress;  ///< Unique channel address
+
+ friend class boost::serialization::access;
+
+ template<class Archive>
+ void serialize(Archive& ar, const unsigned int /*version*/)
+ {
+   ar& fuAddress;
+   ar& fdTime;
+   ar& fdTot;
+ }
 
 #ifndef NO_ROOT
   ClassDefNV(CbmTofDigi, 3);
diff --git a/external/InstallParameter.cmake b/external/InstallParameter.cmake
index 74e6f43126..b368899baf 100644
--- a/external/InstallParameter.cmake
+++ b/external/InstallParameter.cmake
@@ -1,5 +1,7 @@
 set(PARAMETER_VERSION ab9972f137bc52efd58bf3672e670a2d4fbcb1be) # 2025/02/11
 set(PARAMETER_SRC_URL "https://git.cbm.gsi.de/CbmSoft/cbmroot_parameter.git")
+#set(PARAMETER_VERSION 96b2189ac86b3d49c32f63da76e68f66544bba55) # 2025/05/02, BMON hitfinder parameters for online
+#set(PARAMETER_SRC_URL "https://git.cbm.gsi.de/s.zharko/cbmroot_parameter.git")
 
 download_project_if_needed(PROJECT         Parameter_source
                            GIT_REPOSITORY  ${PARAMETER_SRC_URL}
-- 
GitLab