diff --git a/algo/CMakeLists.txt b/algo/CMakeLists.txt
index 58ac4397e40de296b66547e77c29a6497636ba0d..bc9883747a0e9aad120c691a3071046e81d13912 100644
--- a/algo/CMakeLists.txt
+++ b/algo/CMakeLists.txt
@@ -96,6 +96,11 @@ set(SRCS
   evselector/DigiEventSelectorConfig.cxx
   unpack/CommonUnpacker.cxx
   detectors/bmon/Calibrate.cxx
+  detectors/bmon/Clusterizer.cxx
+  detectors/bmon/Hitfind.cxx
+  detectors/bmon/ReadoutConfig.cxx
+  detectors/bmon/Unpack.cxx
+  detectors/bmon/UnpackMS.cxx
   detectors/sts/ChannelMaskSet.cxx
   detectors/sts/ReadoutConfig.cxx
   detectors/sts/HitfinderChain.cxx
@@ -114,9 +119,6 @@ set(SRCS
   detectors/tof/UnpackMS.cxx
   detectors/tof/Hitfind.cxx
   detectors/tof/TrackingInterface.cxx
-  detectors/bmon/ReadoutConfig.cxx
-  detectors/bmon/Unpack.cxx
-  detectors/bmon/UnpackMS.cxx
   detectors/trd/Cluster.cxx
   detectors/trd/Clusterizer.cxx
   detectors/trd/Cluster2D.cxx
diff --git a/algo/detectors/bmon/Calibrate.cxx b/algo/detectors/bmon/Calibrate.cxx
index 53b40e6b13fdec26b63778780af01d1a0d696ff8..1a2a6af79d13a522eaf08ffcf6072ccefdc70852 100644
--- a/algo/detectors/bmon/Calibrate.cxx
+++ b/algo/detectors/bmon/Calibrate.cxx
@@ -47,7 +47,7 @@ Calibrate::Calibrate(CalibrateSetup setup) : fSetup(setup), fSelectionBitsOffset
 
   // Remove the channel information from the reference address
   for (auto& diamond : fSetup.diamonds) {
-    diamond.refAddress = CbmTofAddress::GetModFullId(diamond.refAddress);
+    diamond.refAddress &= ~CbmTofAddress::GetChannelIdBitmask();
   }
 
   // Calculate the channel offsets, needed for the dead time
@@ -90,7 +90,7 @@ Calibrate::resultType Calibrate::operator()(gsl::span<const CbmBmonDigi> digiIn)
     int32_t iChannel = CbmTofAddress::GetChannelId(address);
     size_t iDiamond  = GetDiamondIndex(address);
     if (iDiamond >= diamonds.size()
-        || static_cast<uint32_t>(CbmTofAddress::GetModFullId(address)) != diamonds[iDiamond].refAddress) {
+        || (address & ~CbmTofAddress::GetChannelIdBitmask()) != diamonds[iDiamond].refAddress) {
       monitor.fDigiCalibUnknownRPC++;
       L_(error) << "Unknown BMON digi address: " << CbmTofAddress::ToString(address) << ", iDiamond = " << iDiamond;
       continue;
diff --git a/algo/detectors/bmon/Calibrate.h b/algo/detectors/bmon/Calibrate.h
index 72d3914794d5bfa1161e1ee37052e4d658625b25..0e602e6b4cc1b626c768b006c001c541aef5b8db 100644
--- a/algo/detectors/bmon/Calibrate.h
+++ b/algo/detectors/bmon/Calibrate.h
@@ -41,7 +41,8 @@ namespace cbm::algo::bmon
     resultType operator()(gsl::span<const CbmBmonDigi> digiIn);
 
    private:
-    /// \brief  A digi address
+    /// \brief Returns an index of the diamond by the address
+    /// \param address  A hardware address of the digi
     size_t GetDiamondIndex(uint32_t address) const
     {
       return ((fSetup.selectionMask & address) >> fSelectionBitsOffset);
@@ -49,7 +50,7 @@ namespace cbm::algo::bmon
 
     CalibrateSetup fSetup;                 ///< Parameters of calibrator
     std::vector<size_t> fChannelOffset;    ///< Channel offset: offset for the channel index of each diamond
-    std::vector<double> fChannelDeadTime;  ///< Channel dead time
+    std::vector<double> fChannelDeadTime;  ///< Dead time, stored for a channel
     uint32_t fSelectionBitsOffset;         ///< Number of bits to ther right from the first bit in the selection mask
   };
 }  // namespace cbm::algo::bmon
diff --git a/algo/detectors/bmon/CalibrateSetup.h b/algo/detectors/bmon/CalibrateSetup.h
index 26803bd010da857a78908b48da369e043566dba7..eeeb58e1784c7ff8896bb5050157171aff162e79 100644
--- a/algo/detectors/bmon/CalibrateSetup.h
+++ b/algo/detectors/bmon/CalibrateSetup.h
@@ -40,7 +40,6 @@ namespace cbm::algo::bmon
       int32_t numClWalkBinX;
       double TOTMax;
       double TOTMin;
-      bool swapChannelSides;
       double channelDeadtime;
       std::vector<Channel> chanPar;
 
@@ -49,7 +48,6 @@ namespace cbm::algo::bmon
                         yaml::Property(&Diamond::numClWalkBinX, "numClWalkBinX", "number of walk correction bins"),
                         yaml::Property(&Diamond::TOTMax, "TOTMax", "maximum time over threshold"),
                         yaml::Property(&Diamond::TOTMin, "TOTMin", "minimum time over threshold"),
-                        yaml::Property(&Diamond::swapChannelSides, "swapChannelSides", "flag for swapping channel sides"),
                         yaml::Property(&Diamond::channelDeadtime, "channelDeadtime", "channel dead time"),
                         yaml::Property(&Diamond::chanPar, "chanPar", "channel parameters"));
     };
@@ -59,7 +57,7 @@ namespace cbm::algo::bmon
     std::vector<Diamond> diamonds;
 
     CBM_YAML_PROPERTIES(
-      yaml::Property(&CalibrateSetup::selectionMask, "selectionMask", "A mask to distinguish between different diamonds"),
+      yaml::Property(&CalibrateSetup::selectionMask, "selectionMask", "A bit mask to distinguish between different diamonds"),
       yaml::Property(&CalibrateSetup::diamonds, "diamonds", "Parameters of each diamond"));
   };
 
diff --git a/algo/detectors/bmon/Clusterizer.cxx b/algo/detectors/bmon/Clusterizer.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..2d48416e35b526462ee7b8ef9bcb2d15086015cf
--- /dev/null
+++ b/algo/detectors/bmon/Clusterizer.cxx
@@ -0,0 +1,86 @@
+/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergei Zharko [committer] */
+
+/// \file   Clusterizer.cxx
+/// \brief  A clusterizer algorithm for BMON (implementation)
+/// \since  07.02.2025
+/// \author Sergei Zharko <s.zharko@gsi.de>
+
+
+#include "bmon/Clusterizer.h"
+
+#include "AlgoFairloggerCompat.h"
+#include "CbmBmonDigi.h"
+#include "CbmTofAddress.h"
+
+#include <algorithm>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+
+using cbm::algo::bmon::Clusterizer;
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+Clusterizer::Output_t Clusterizer::operator()(const Clusterizer::Input_t& digis)
+{
+  // Description:
+  // The input array of digis is traced through until the last element. If the current digi and the next digi are
+  // close in time and have neighboring channels, a single hit is produced. Otherwise a hit is produced from a
+  // single digi. The complexity of the algorithm is O(nDigis). Requirements: digis must be sorted in time
+  Output_t res;
+  if (digis.empty()) {
+    return res;
+  }
+
+  auto& hits        = std::get<0>(res);
+  auto& digiIndices = std::get<1>(res);
+  hits.reserve(digis.size());
+  digiIndices.reserve(digis.size());
+  auto itLast = std::prev(digis.end());  // iterator pointing to the last digi
+  bool bUsedWithPrevious{false};         // A flag: if the current digi was used together with the previous one
+  for (auto it = digis.begin(), in = it; it != itLast; ++it) {
+    if (bUsedWithPrevious) {
+      // skip a digi, if it was already used
+      bUsedWithPrevious = false;
+      continue;
+    }
+    in                = std::next(it);
+    const auto& digiT = it->first;
+    const auto& digiN = in->first;
+    if (digiN.GetTime() - digiT.GetTime() < fParams.fdMaxTimeDist
+        && abs(digiN.GetChannel() - digiT.GetChannel()) == 1) {
+      // A hit consisting from two digis is found
+      hits.emplace_back(fParams.fAddress, digiT, digiN);
+      digiIndices.emplace_back(it->second);
+      bUsedWithPrevious = true;
+    }
+    else {
+      // A hit consisting from a single digi
+      hits.emplace_back(fParams.fAddress, digiT);
+      digiIndices.emplace_back(it->second);
+    }
+  }
+  if (!bUsedWithPrevious) {
+    // Create a hit from the last digi
+    hits.emplace_back(fParams.fAddress, itLast->first);
+    digiIndices.emplace_back(itLast->second);
+  }
+  return res;
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+bool Clusterizer::SelectDigi(const CbmBmonDigi& digi) const
+{
+  // Dead channel cut
+  if (fParams.fDeadStrips & (1 << CbmTofAddress::GetChannelId(digi.GetAddress()))) {
+    return false;
+  }
+
+  // ??? Other cuts ??? Charge threshold? Also dead strips can be accounted already on the calibrator level, together
+  //     with the cuts
+
+  return true;
+}
diff --git a/algo/detectors/bmon/Clusterizer.h b/algo/detectors/bmon/Clusterizer.h
new file mode 100644
index 0000000000000000000000000000000000000000..86b3c76e3ba74d1595e7ae0282ac1f10e0e22a09
--- /dev/null
+++ b/algo/detectors/bmon/Clusterizer.h
@@ -0,0 +1,57 @@
+/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergei Zharko [committer] */
+
+/// \file   Clusterizer.h
+/// \brief  A clusterizer algorithm for BMON
+/// \since  07.02.2025
+/// \author Sergei Zharko <s.zharko@gsi.de>
+
+#pragma once
+
+#include "base/PODVector.h"
+#include "bmon/ClusterizerPars.h"
+#include "bmon/Hit.h"
+
+#include <cmath>
+#include <cstdint>
+#include <memory>
+#include <utility>
+#include <vector>
+
+class CbmBmonDigi;
+
+namespace cbm::algo::bmon
+{
+  /// \class Clusterizer
+  /// \brief A clusterizer algorithm for a BMON
+  ///
+  /// The algorithm is executed on a single hardware module
+  class Clusterizer {
+   public:
+    using Input_t  = std::vector<std::pair<CbmBmonDigi, int32_t>>;     ///< Input type
+    using Output_t = std::pair<std::vector<Hit>, PODVector<int32_t>>;  ///< Output type
+
+    /// \brief Constructor
+    /// \param params RPC parameters
+    explicit Clusterizer(ClusterizerPars params) : fParams(params) {}
+
+    /// \brief Hit building function
+    Output_t operator()(const Input_t& digisInput);
+
+    /// \brief  Applies selection on a digis
+    /// \return true   Digi is selected
+    /// \return false  Digi is cut out
+    bool SelectDigi(const CbmBmonDigi& digi) const;
+
+   private:
+    /// \brief Creates a hit from a single digi
+    Hit CreateHit(const CbmBmonDigi& digi) const;
+
+    /// \brief Creates a hit from two digis
+    Hit CreateHit(const CbmBmonDigi& digiL, const CbmBmonDigi& digiR) const;
+
+
+    ClusterizerPars fParams;  ///< parameters container
+  };
+}  // namespace cbm::algo::bmon
diff --git a/algo/detectors/bmon/ClusterizerPars.h b/algo/detectors/bmon/ClusterizerPars.h
new file mode 100644
index 0000000000000000000000000000000000000000..0e2e65ecf98814f9650e17f9d0c54298441e347d
--- /dev/null
+++ b/algo/detectors/bmon/ClusterizerPars.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergei Zharko [committer] */
+
+/// \file   ClusterizerPars.h
+/// \brief  BMON clusterizer parameters
+/// \since  07.02.2025
+/// \author Sergei Zharko <s.zharko@gsi.de>
+
+#pragma once
+
+#include <cstdint>
+#include <vector>
+
+namespace cbm::algo::bmon
+{
+  /// \struct ClusterizerPars
+  /// \brief  Clusterizer parameters for Diamond
+  struct ClusterizerPars {
+    uint32_t fAddress;     ///< Address of the diamond (the channel bit field is 0)
+    uint32_t fDeadStrips;  ///< Dead strip bitmask
+    double fdMaxTimeDist;  ///< Maximum time difference between two consecutive digis to form a single hit
+    double fTimeRes;       ///< Time resolution
+  };
+}  // namespace cbm::algo::bmon
diff --git a/algo/detectors/bmon/Hit.h b/algo/detectors/bmon/Hit.h
new file mode 100644
index 0000000000000000000000000000000000000000..e4e1aef243769e409fc0b8c06466b78548a80461
--- /dev/null
+++ b/algo/detectors/bmon/Hit.h
@@ -0,0 +1,103 @@
+/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergei Zharko [committer] */
+
+/// \file   Hitfind.h
+/// \brief  A BMON hit class
+/// \since  07.02.2025
+/// \author Sergei Zharko <s.zharko@gsi.de>
+
+#pragma once
+
+#include "CbmBmonDigi.h"
+
+#include <boost/serialization/access.hpp>
+
+#include <cstdint>
+#include <vector>
+
+namespace cbm::algo::bmon
+{
+  /// \class  Hit
+  /// \brief  A BMON hit
+  class Hit {
+   public:
+    /// \brief Constructor from a single digi
+    /// \param address  Address of the diamond
+    /// \param digi     A digi
+    Hit(uint32_t address, const CbmBmonDigi& digi) : fAddress(address), fNofChannels(1)
+    {
+      fTime      = digi.GetTime();
+      fTimeError = 0.;  // FIXME: provide a rule to set an error
+    }
+
+    /// \brief Constructor from two digis
+    /// \param address  Address of the diamond
+    /// \param digiL    First digi
+    /// \param digiR    Second digi
+    Hit(uint32_t address, const CbmBmonDigi& digiL, const CbmBmonDigi& digiR) : fAddress(address), fNofChannels(2)
+    {
+      double weightSum = digiL.GetCharge() + digiR.GetCharge();
+      fTime            = (digiL.GetTime() * digiL.GetCharge() + digiR.GetTime() * digiR.GetCharge()) / weightSum;
+      fTimeError       = 0.;  // FIXME: provide a rule to set an error
+    }
+
+    /// \brief Constructor
+    /// \param address   Address of diamond (the channel is not stored)
+    /// \param time      Time of the hit [ns]
+    /// \param timeError Time error of the hit [ns]
+    /// \param nChannels Number of channels used (either one or two)
+    Hit(uint32_t address, double time, double timeError, uint8_t nChannels)
+      : fTime(time)
+      , fTimeError(timeError)
+      , fAddress(address)
+      , fNofChannels(nChannels)
+    {
+    }
+
+    /// \brief Gets hardware address
+    uint32_t GetAddress() const { return fAddress; }
+
+    /// \brief Gets number of channels
+    uint8_t GetNofChannels() const { return fNofChannels; }
+
+    /// \brief Gets time [ns]
+    double GetTime() const { return fTime; }
+
+    /// \brief Gets time error [ns]
+    double GetTimeError() const { return fTimeError; }
+
+    /// \brief Sets address
+    /// \param address Hardware address
+    void SetAddress(uint32_t address) { fAddress = address; }
+
+    /// \brief Sets number of channels
+    /// \param nofChannels Number of channels (digis), used to create a hit
+    void SetNofChannels(uint8_t nofChannels) { fNofChannels = nofChannels; }
+
+    /// \brief Sets time
+    /// \param  time  Hit time [ns]
+    void SetTime(double time) { fTime = time; }
+
+    /// \brief Sets time error
+    /// \param  timeError  Hit time error [ns]
+    void SetTimeError(double timeError) { fTimeError = timeError; }
+
+   private:
+    double fTime{0.};         ///< Time [ns]
+    double fTimeError{0.};    ///< Time error [ns]
+    uint32_t fAddress{0};     ///< Assigned hit address
+    uint8_t fNofChannels{0};  ///< Number of channels used
+
+    /// \brief Boost serialization function
+    friend class boost::serialization::access;
+    template<class Archive>
+    void serialize(Archive& ar, unsigned int /*version*/)
+    {
+      ar& fTime;
+      ar& fTimeError;
+      ar& fAddress;
+      ar& fNofChannels;
+    }
+  };
+}  // namespace cbm::algo::bmon
diff --git a/algo/detectors/bmon/Hitfind.cxx b/algo/detectors/bmon/Hitfind.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..b1e6794c8e0291231e2fb2e87d51d6ce92f0bb38
--- /dev/null
+++ b/algo/detectors/bmon/Hitfind.cxx
@@ -0,0 +1,112 @@
+/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergei Zharko [committer] */
+
+/// \file   Hitfind.cxx
+/// \brief  A BMON hitfinder steering class (implementation)
+/// \since  07.02.2025
+/// \author Sergei Zharko <s.zharko@gsi.de>
+
+
+#include "Hitfind.h"
+
+#include "AlgoFairloggerCompat.h"
+#include "compat/OpenMP.h"
+#include "util/TimingsFormat.h"
+
+#include <chrono>
+
+using cbm::algo::bmon::ClusterizerPars;
+using cbm::algo::bmon::Hitfind;
+using cbm::algo::bmon::HitfindSetup;
+using fles::Subsystem;
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+Hitfind::Hitfind(HitfindSetup setup, uint32_t nThreads) : fNofThreads(nThreads)
+{
+  // Create one algorithm per diamond and per thread
+  size_t nDiamondsInSetup{setup.diamonds.size()};
+  if (nDiamondsInSetup == 0) {
+    throw std::runtime_error("No diamonds found in the BMON calibration config");
+  }
+  if (!(setup.selectionMask) != (nDiamondsInSetup == 1)) {
+    throw std::runtime_error("Wrong diamond selection mask: for a single diamond it must be zero, and for multiple"
+                             " diamonds it must be non-zero");
+  }
+
+  if (nDiamondsInSetup > 1) {
+    // Define the selection bit offset
+    while (!((setup.selectionMask >> fSelectionBitsOffset) % 2)) {
+      ++fSelectionBitsOffset;
+    }
+
+    // Sort the diamonds in the setup by their SM or Side or other distinguishing index
+    std::sort(setup.diamonds.begin(), setup.diamonds.end(), [&](const auto& lhs, const auto& rhs) {
+      return GetDiamondIndex(lhs.refAddress) < GetDiamondIndex(rhs.refAddress);
+    });
+  }
+
+  fSelectionBitmask = setup.selectionMask;
+  fDiamondAddress   = PODVector<uint32_t>(nDiamondsInSetup, 0);
+
+  // Store diamond address
+  for (size_t iDiamond = 0; iDiamond < nDiamondsInSetup; ++iDiamond) {
+    fDiamondAddress[iDiamond] = setup.diamonds[iDiamond].refAddress & ~CbmTofAddress::GetChannelIdBitmask();
+  }
+
+  // Create and configure clusterizer algorithms per thread and per diamond
+  fAlgo = std::vector<std::vector<Clusterizer>>(fNofThreads, std::vector<Clusterizer>());
+  for (auto& algoPerThread : fAlgo) {
+    algoPerThread.reserve(nDiamondsInSetup);
+    for (size_t iDiamond = 0; iDiamond < nDiamondsInSetup; ++iDiamond) {
+      auto par               = std::make_unique<ClusterizerPars>();
+      const auto& diamondPar = setup.diamonds[iDiamond];
+      par->fAddress          = diamondPar.refAddress;
+      par->fDeadStrips       = diamondPar.deadStrips;
+      par->fdMaxTimeDist     = diamondPar.maxTimeDist;
+      par->fTimeRes          = diamondPar.timeRes;
+      algoPerThread.emplace_back(std::move(*par));
+    }
+  }
+  L_(info) << "--- Configured hitfinder algorithms for BMON.";
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+Hitfind::Output_t Hitfind::operator()(gsl::span<CbmBmonDigi> digisIn, uint32_t iThread)
+{
+  Output_t res     = {};
+  auto& resHits    = std::get<0>(res);
+  auto& resMoni    = std::get<1>(res);
+  auto& resDigiIds = std::get<2>(res);
+
+  auto& algoPerThread = fAlgo[iThread];
+
+  // Distribute digis over diamonds, apply cuts on this level (maybe the Calibrator is a more proper place for it)
+  size_t nDiamonds  = algoPerThread.size();
+  auto vDigiStorage = std::vector<Clusterizer::Input_t>(nDiamonds, Clusterizer::Input_t(0));
+  for (int32_t iDigi = 0; iDigi < static_cast<int32_t>(digisIn.size()); ++iDigi) {
+    const auto& digi = digisIn[iDigi];
+    size_t iDiamond  = GetDiamondIndex(digi.GetAddress());
+    if (algoPerThread[iDiamond].SelectDigi(digi)) {
+      vDigiStorage[iDiamond].emplace_back(digi, iDigi);
+    }
+  }
+
+  // NOTE: I see no sense in storing a full channel address for different BMON hits,
+  //       so for each hit the diamond address will be assigned: address & ~CbmTofAddress::GetChannelIdBitmask()
+  PODVector<Hit> vHitsFlat;                                    // storage for clusters
+  PODVector<size_t> vNhitsPerDiamond(fDiamondAddress.size());  // number of hits per diamond
+
+  for (size_t iDiamond = 0; iDiamond < algoPerThread.size(); ++iDiamond) {
+    auto [hits, digiIds]       = algoPerThread[iDiamond](vDigiStorage[iDiamond]);
+    vNhitsPerDiamond[iDiamond] = hits.size();
+    vHitsFlat.insert(vHitsFlat.end(), std::make_move_iterator(hits.begin()), std::make_move_iterator(hits.end()));
+    resDigiIds.insert(resDigiIds.end(), std::make_move_iterator(digiIds.begin()),
+                      std::make_move_iterator(digiIds.end()));
+  }
+
+  resHits = PartitionedVector(std::move(vHitsFlat), vNhitsPerDiamond, fDiamondAddress);
+  return res;
+}
diff --git a/algo/detectors/bmon/Hitfind.h b/algo/detectors/bmon/Hitfind.h
new file mode 100644
index 0000000000000000000000000000000000000000..68d7d3a9b57e80099a6a4ce3854445622cca5384
--- /dev/null
+++ b/algo/detectors/bmon/Hitfind.h
@@ -0,0 +1,60 @@
+/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergei Zharko [committer] */
+
+/// \file   Hitfind.h
+/// \brief  BMON hitfinder steering class
+/// \since  07.02.2025
+/// \author Sergei Zharko <s.zharko@gsi.de>
+
+#pragma once
+
+#include "CbmBmonDigi.h"
+#include "PODVector.h"
+#include "PartitionedVector.h"
+#include "bmon/Clusterizer.h"
+#include "bmon/HitfindSetup.h"
+#include "tof/Hitfind.h"  // for tof::HitfindMonitorData
+
+#include <gsl/span>
+#include <optional>
+#include <sstream>
+#include <vector>
+
+#include <xpu/host.h>
+
+namespace cbm::algo::bmon
+{
+  /// \brief TOF hit-finder monitor, re-used for BMON
+  using HitfindMonitorData = tof::HitfindMonitorData;
+
+  /// \class Hitfind
+  /// \brief Hit-finder steering class for BMON
+  class Hitfind {
+   public:
+    /// \brief Output format
+    using Output_t = std::tuple<PartitionedVector<Hit>, HitfindMonitorData, PODVector<int32_t>>;
+
+    /// \brief Constructor
+    /// \param  setup     Setup configuration
+    /// \param  nThreads  Number of threads (for event-based mode)
+    explicit Hitfind(HitfindSetup setup, uint32_t nThreads = 1);
+
+    /// \brief Algorithm execution operator
+    /// \param digiIn   A portion of digis in TS/event
+    /// \param iThread  Index of thread
+    Output_t operator()(gsl::span<CbmBmonDigi> digisIn, uint32_t iThread = 0);
+
+   private:  // members
+    /// \brief Returns an index of the diamond by the address
+    /// \param address  A hardware address of the digi
+    size_t GetDiamondIndex(uint32_t address) const { return ((fSelectionBitmask & address) >> fSelectionBitsOffset); }
+
+    uint32_t fNofThreads;           ///< Number of threads
+    uint32_t fSelectionBitsOffset;  ///< Number of bits to ther right from the first bit in the selection mask
+    uint32_t fSelectionBitmask;     ///< Selection bitmask
+
+    std::vector<std::vector<Clusterizer>> fAlgo;  ///< Clusterizer algorithms [thread][diamond]
+    PODVector<uint32_t> fDiamondAddress;          ///< Diamond address
+  };
+}  // namespace cbm::algo::bmon
diff --git a/algo/detectors/bmon/HitfindSetup.h b/algo/detectors/bmon/HitfindSetup.h
new file mode 100644
index 0000000000000000000000000000000000000000..c98cc163ff616f0f0e0b8a55158ea224ebf9cbb6
--- /dev/null
+++ b/algo/detectors/bmon/HitfindSetup.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergei Zharko [committer] */
+
+/// \file   HitfindSetup.h
+/// \brief  Parameters of the BMON hitfinder
+/// \since  06.02.2025
+/// \author Sergei Zharko <s.zharko@gsi.de>
+
+
+#include "Definitions.h"
+#include "yaml/Property.h"
+
+#include <string>
+#include <vector>
+
+namespace cbm::algo::bmon
+{
+  /// \struct HitfindSetup
+  /// \brief  Parameters for the BMON hitfinder
+  struct HitfindSetup {
+    struct Diamond {
+      u32 refAddress;
+      u32 deadStrips;
+      double maxTimeDist;
+      double timeRes;
+
+      CBM_YAML_PROPERTIES(
+                        yaml::Property(&Diamond::refAddress, "refAddress", "reference address of the diamond"),
+                        yaml::Property(&Diamond::deadStrips, "deadStrips", "bit mask for dead strips"),
+                        yaml::Property(&Diamond::maxTimeDist, "maxTimeDist", "maximum time distance"),
+                        yaml::Property(&Diamond::timeRes, "timeRes", "time resolution"));
+    };
+
+    uint32_t selectionMask;
+    std::vector<Diamond> diamonds;
+
+    CBM_YAML_PROPERTIES(
+      yaml::Property(&HitfindSetup::selectionMask, "selectionMask", "A bit mask to distinguish between different diamonds"),
+      yaml::Property(&HitfindSetup::diamonds, "diamonds", "Parameters of diamonds"));
+  };
+}  // namespace cbm::algo::bmon
diff --git a/algo/global/Reco.cxx b/algo/global/Reco.cxx
index b16a463d0758b0af1d13c8c5a2154028e754df18..5b2698cb3f2f0c2928165baaa30abfcf298040fe 100644
--- a/algo/global/Reco.cxx
+++ b/algo/global/Reco.cxx
@@ -15,6 +15,7 @@
 #include "StsDigiQa.h"
 #include "TrackingSetup.h"
 #include "bmon/Calibrate.h"
+#include "bmon/Hitfind.h"
 #include "bmon/ReadoutConfig.h"
 #include "bmon/Unpack.h"
 #include "ca/TrackingChain.h"
@@ -214,8 +215,8 @@ void Reco::Init(const Options& opts)
     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);
+    auto hitfindSetup = yaml::ReadFromFile<bmon::HitfindSetup>(opts.ParamsDir() / parFiles.bmon.hitfinder);
+    fBmonHitFinder    = std::make_unique<bmon::Hitfind>(hitfindSetup);
   }
 
   // TOF Hitfinder
@@ -386,60 +387,26 @@ RecoResults Reco::Run(const fles::Timeslice& ts)
     }
 
     // ***** 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
-
+    if constexpr (0) {
       int nEvents = events.size();
+      size_t nBmonHitsOneChannel{0};
+      size_t nBmonHitsTwoChannels{0};
       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());
+        auto [bmonDigis, bmonCalMonitor]         = (*fBmonCalibrator)(event.fBmon);
+        auto [bmonHits, hitmonitor, digiindices] = (*fBmonHitFinder)(bmonDigis);
+        for (const auto& hit : bmonHits.Data()) {
+          if (hit.GetNofChannels() == 1) {
+            ++nBmonHitsOneChannel;
+          }
+          else {
+            ++nBmonHitsTwoChannels;
+          }
         }
-        //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);
       }
+      L_(info) << "!!!! BMON hits with two channels: " << nBmonHitsTwoChannels << " / "
+               << (nBmonHitsTwoChannels + nBmonHitsOneChannel);
     }
     // ***** DEBUG: END
 
diff --git a/algo/global/Reco.h b/algo/global/Reco.h
index 982a95cfa49cf83dce88485fc3b80118a58b5042..19027c8368b9fe496282f40eca52eaff22f339d6 100644
--- a/algo/global/Reco.h
+++ b/algo/global/Reco.h
@@ -28,6 +28,7 @@ namespace cbm::algo
   {
     class Unpack;
     class Calibrate;
+    class Hitfind;
   }
 
   namespace much
@@ -148,6 +149,7 @@ namespace cbm::algo
     // BMON
     std::unique_ptr<bmon::Unpack> fBmonUnpack;
     std::unique_ptr<bmon::Calibrate> fBmonCalibrator;
+    std::unique_ptr<bmon::Hitfind> fBmonHitFinder;
 
     // MUCH
     std::unique_ptr<much::Unpack> fMuchUnpack;
diff --git a/core/data/bmon/CbmBmonDigi.h b/core/data/bmon/CbmBmonDigi.h
index 7f0584e2fcb7f64727c7d0cf24734bdbd86191c9..fffed5ca6118e8ff51e2c3bdfbc9d619392094e6 100644
--- a/core/data/bmon/CbmBmonDigi.h
+++ b/core/data/bmon/CbmBmonDigi.h
@@ -1,12 +1,13 @@
-/* Copyright (C) 2022 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+/* Copyright (C) 2022-2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
    SPDX-License-Identifier: GPL-3.0-only
-   Authors: Pierre-Alain Loizeau, Volker Friese [committer] */
+   Authors: Pierre-Alain Loizeau, Volker Friese [committer], Sergei Zharko */
 
 
 #ifndef CBMBMONDIGI_H
 #define CBMBMONDIGI_H 1
 
 #include "CbmDefs.h"
+#include "CbmTofAddress.h"
 
 #ifndef NO_ROOT
 #include <Rtypes.h>  // for ClassDef
@@ -85,6 +86,11 @@ public:
    **/
   double GetTime() const { return fTime; }
 
+  /**
+   **  @brief Gets channel ID
+   **  @return Channel ID
+   **/
+  int32_t GetChannel() const { return CbmTofAddress::GetChannelId(fAddress); }
 
   /** @brief Charge
    ** @return Charge
diff --git a/core/data/tof/CbmTofAddress.cxx b/core/data/tof/CbmTofAddress.cxx
index 068504c39956e2424aea4d82707a896fefdd9f28..29c1573452a1713f962b5a4347e47577000aca0e 100644
--- a/core/data/tof/CbmTofAddress.cxx
+++ b/core/data/tof/CbmTofAddress.cxx
@@ -1,6 +1,6 @@
-/* Copyright (C) 2013-2020 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+/* Copyright (C) 2013-2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
    SPDX-License-Identifier: GPL-3.0-only
-   Authors: Pierre-Alain Loizeau, Florian Uhlig [committer], Norbert Herrmann */
+   Authors: Pierre-Alain Loizeau, Florian Uhlig [committer], Norbert Herrmann, Segei Zharko */
 
 /** @file CbmTofAddress.cxx
  ** @author Pierre-Alain Loizeau <loizeau@physi.uni-heidelberg.de>
@@ -44,6 +44,14 @@ const int32_t CbmTofAddress::fgkiStripFullIdMask =
   + (((1 << fgkSmTypeBits) - 1) << fgkSmTypeOffset) + (((1 << fgkRpcIdBits) - 1) << fgkRpcIdOffset)
   + (((1 << fgkChannelIdBits) - 1) << fgkChannelIdOffset);
 
+const int32_t CbmTofAddress::fgkSystemIdBitmask    = (1 << CbmAddress::fgkSystemBits) - 1;
+const int32_t CbmTofAddress::fgkSmIdBitmask        = ((1 << fgkSmIdBits) - 1) << fgkSmIdOffset;
+const int32_t CbmTofAddress::fgkSmTypeBitmask      = ((1 << fgkSmTypeBits) - 1) << fgkSmTypeOffset;
+const int32_t CbmTofAddress::fgkRpcIdBitmask       = ((1 << fgkRpcIdBits) - 1) << fgkRpcIdOffset;
+const int32_t CbmTofAddress::fgkChannelSideBitmask = ((1 << fgkChannelSideBits) - 1) << fgkChannelSideOffset;
+const int32_t CbmTofAddress::fgkChannelIdBitmask   = ((1 << fgkChannelIdBits) - 1) << fgkChannelIdOffset;
+const int32_t CbmTofAddress::fgkRpcTypeBitmask     = ((1 << fgkRpcTypeBits) - 1) << fgkRpcTypeOffset;
+
 std::string CbmTofAddress::ToString(int32_t address)
 {
   using std::setfill;
diff --git a/core/data/tof/CbmTofAddress.h b/core/data/tof/CbmTofAddress.h
index d174064bf263428eb7fb2f6a8cc87a4b9bf9351f..dfa1a18e89a74bd6037206079cf23e60011c956d 100644
--- a/core/data/tof/CbmTofAddress.h
+++ b/core/data/tof/CbmTofAddress.h
@@ -1,6 +1,6 @@
-/* Copyright (C) 2013-2020 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+/* Copyright (C) 2013-2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
    SPDX-License-Identifier: GPL-3.0-only
-   Authors: Pierre-Alain Loizeau, Florian Uhlig [committer], Norbert Herrmann */
+   Authors: Pierre-Alain Loizeau, Florian Uhlig [committer], Norbert Herrmann, Sergei Zharko */
 
 /** @file CbmTofAddress.h
  ** @author Pierre-Alain Loizeau <loizeau@physi.uni-heidelberg.de>
@@ -54,6 +54,37 @@ public:
   /** Destructor  **/
   virtual ~CbmTofAddress() {};
 
+  /** Component bitmasks **/
+  /** Gets a bitmask for the System bit field
+   ** @return a bitmask with the bits set only for the System bit field
+   **/
+  static int32_t GetSystemIdBitmask() { return fgkSystemIdBitmask; }
+  /** Gets a bitmask for the Super Module ID bit field
+   ** @return a bitmask with the bits set only for the Super Module ID bit field
+   **/
+  static int32_t GetSmIdBitmask() { return fgkSmIdBitmask; }
+  /** Gets a bitmask for the Super Module Type bit field
+   ** @return a bitmask with the bits set only for the Super Module Type bit field
+   **/
+  static int32_t GetSmTypeBitmask() { return fgkSmTypeBitmask; }
+  /** Gets a bitmask for the RPC ID bit field
+   ** @return a bitmask with the bits set only for the RPC ID bit field
+   **/
+  static int32_t GetRpcIdBitmask() { return fgkRpcIdBitmask; }
+  /** Gets a bitmask for the Channel Side bit field
+   ** @return a bitmask with the bits set only for the Channel Side bit field
+   **/
+  static int32_t GetChannelSideBitmask() { return fgkChannelSideBitmask; }
+  /** Gets a bitmask for the Channel ID bit field
+   ** @return a bitmask with the bits set only for the Channel ID bit field
+   **/
+  static int32_t GetChannelIdBitmask() { return fgkChannelIdBitmask; }
+  /** Gets a bitmask for the RPC Type bit field
+   ** @return a bitmask with the bits set only for the RPC Type bit field
+   **/
+  static int32_t GetRpcTypeBitmask() { return fgkRpcTypeBitmask; }
+
+
   /** Field size accessors **/
   /** Number of bits for Super Module Id in the address field
    ** @return Number of bits
@@ -76,6 +107,33 @@ public:
    **/
   static int32_t GetNofChSideBits() { return fgkChannelSideBits; };
 
+
+  /** Bit field offsets **/
+  /** Gets and offset for the System bit field
+   ** @return and offset with the bits set only for the System bit field
+   **/
+  static int32_t GetSmIdOffset() { return fgkSmIdOffset; }
+  /** Gets and offset for the Super Module Type bit field
+   ** @return and offset with the bits set only for the Super Module Type bit field
+   **/
+  static int32_t GetSmTypeOffset() { return fgkSmTypeOffset; }
+  /** Gets and offset for the RPC ID bit field
+   ** @return and offset with the bits set only for the RPC ID bit field
+   **/
+  static int32_t GetRpcIdOffset() { return fgkRpcIdOffset; }
+  /** Gets and offset for the Channel Side bit field
+   ** @return and offset with the bits set only for the Channel Side bit field
+   **/
+  static int32_t GetChannelSideOffset() { return fgkChannelSideOffset; }
+  /** Gets and offset for the Channel ID bit field
+   ** @return and offset with the bits set only for the Channel ID bit field
+   **/
+  static int32_t GetChannelIdOffset() { return fgkChannelIdOffset; }
+  /** Gets and offset for the RPC Type bit field
+   ** @return and offset with the bits set only for the RPC Type bit field
+   **/
+  static int32_t GetRpcTypeOffset() { return fgkRpcTypeOffset; }
+
   /** Maskers **/
   /** Get the Super Module Id from the address
    ** @param address  Unique address
@@ -154,7 +212,9 @@ public:
   {
     return (GetModFullId(addressA) == GetModFullId(addressB)) ? true : false;
   };
-
+  /**
+   ** @brief Sets the channel ID to the address 
+   **/
   static uint32_t ConvertCbmTofDetectorInfo(CbmTofDetectorInfo infoInput)
   {
     // For now assume that the system ID will always be correct
@@ -251,6 +311,16 @@ public:
    ** For the detector strip Id determination (ignore RPC type and side)
    **/
   static const int32_t fgkiStripFullIdMask;
+
+
+  /** Individual bitmasks for each bit field **/
+  static const int32_t fgkSystemIdBitmask;     //<  A bitmask for System ID
+  static const int32_t fgkSmIdBitmask;         //<  A bitmask for SM
+  static const int32_t fgkSmTypeBitmask;       //<  A bitmask for SM Type
+  static const int32_t fgkRpcIdBitmask;        //<  A bitmask for RPC ID
+  static const int32_t fgkChannelSideBitmask;  //<  A bitmask for Channel Side
+  static const int32_t fgkChannelIdBitmask;    //<  A bitmask for Channel ID
+  static const int32_t fgkRpcTypeBitmask;      //<  A bitmask for RPC Type
 };
 
 #endif  // CBMTOFADDRESS_H