diff --git a/algo/CMakeLists.txt b/algo/CMakeLists.txt
index a70b0872c98516d229715c67c7f86b14d8102a83..b07afd74581e2d9a6b58b7d04629f39902ac9acf 100644
--- a/algo/CMakeLists.txt
+++ b/algo/CMakeLists.txt
@@ -143,6 +143,9 @@ set(SRCS
   detectors/rich/ReadoutConfig.cxx
   detectors/rich/Unpack.cxx
   detectors/rich/UnpackMS.cxx
+  detectors/must/Unpack.cxx
+  detectors/must/UnpackMS.cxx
+  detectors/must/ReadoutConfig.cxx
   global/ParFiles.cxx
   global/StorableRecoResults.cxx
   global/Reco.cxx
@@ -159,6 +162,7 @@ set(SRCS
   qa/hitfind/BmonHitfindQa.cxx
   qa/hitfind/BmonHitfindQaParameters.cxx
   qa/unpack/StsDigiQa.cxx
+  qa/unpack/MustDigiQa.cxx
   ca/TrackingSetup.cxx
   ca/TrackingChain.cxx
   ca/qa/CaQa.cxx
@@ -337,6 +341,7 @@ install(DIRECTORY base/gpu TYPE INCLUDE FILES_MATCHING PATTERN "*.h")
 install(DIRECTORY data/sts TYPE INCLUDE FILES_MATCHING PATTERN "*.h")
 install(DIRECTORY detectors/bmon TYPE INCLUDE FILES_MATCHING PATTERN "*.h")
 install(DIRECTORY detectors/much TYPE INCLUDE FILES_MATCHING PATTERN "*.h")
+install(DIRECTORY detectors/must TYPE INCLUDE FILES_MATCHING PATTERN "*.h")
 install(DIRECTORY detectors/sts TYPE INCLUDE FILES_MATCHING PATTERN "*.h")
 install(DIRECTORY detectors/tof TYPE INCLUDE FILES_MATCHING PATTERN "*.h")
 install(DIRECTORY detectors/trd TYPE INCLUDE FILES_MATCHING PATTERN "*.h")
diff --git a/algo/base/AuxDigiData.h b/algo/base/AuxDigiData.h
index e464da7e281fbb57a33557ccf43ff98947fd2767..a2bb3201615d5f2886dc68f56bc3debc3fc6c9e3 100644
--- a/algo/base/AuxDigiData.h
+++ b/algo/base/AuxDigiData.h
@@ -12,6 +12,7 @@
 #include "CommonUnpacker.h"
 #include "bmon/UnpackMS.h"
 #include "much/UnpackMS.h"
+#include "must/UnpackMS.h"
 #include "rich/UnpackMS.h"
 #include "sts/UnpackMS.h"
 #include "tof/UnpackMS.h"
@@ -28,6 +29,7 @@ namespace cbm::algo
     UnpackAux<bmon::UnpackAuxData> fBmon;
     UnpackAux<much::UnpackAuxData> fMuch;
     UnpackAux<rich::UnpackAuxData> fRich;
+    UnpackAux<must::UnpackAuxData> fMust;
     UnpackAux<sts::UnpackAuxData> fSts;
     UnpackAux<tof::UnpackAuxData> fTof;
     UnpackAux<trd::UnpackAuxData> fTrd;
diff --git a/algo/base/Definitions.h b/algo/base/Definitions.h
index b1ff4c9ccc8e804a33a67fe0ab40d81d74dd616e..74b2f532bb0c9f1c0bbb03a8bc10d69d4e3cc824 100644
--- a/algo/base/Definitions.h
+++ b/algo/base/Definitions.h
@@ -67,6 +67,7 @@ namespace cbm::algo
     UnpackMuch,
     UnpackTof,
     UnpackFsd,
+    UnpackMust,
     EventBuilding,
     RecoBmon,
     RecoSts,
@@ -139,6 +140,7 @@ CBM_ENUM_DICT(cbm::algo::QaStep,
   {"UnpackMuch", cbm::algo::QaStep::UnpackMuch},
   {"UnpackTof", cbm::algo::QaStep::UnpackTof},
   {"UnpackFsd", cbm::algo::QaStep::UnpackFsd},
+  {"UnpackMust", cbm::algo::QaStep::UnpackMust},
   {"EventBuilding", cbm::algo::QaStep::EventBuilding},
   {"RecoBmon", cbm::algo::QaStep::RecoBmon},
   {"RecoSts", cbm::algo::QaStep::RecoSts},
diff --git a/algo/base/DigiData.cxx b/algo/base/DigiData.cxx
index 79fd5a4836022d2caae819e0715f3fcf0a1ecfe7..2277a0dac279df30c9f64a8593a67807e3b37cf5 100644
--- a/algo/base/DigiData.cxx
+++ b/algo/base/DigiData.cxx
@@ -21,6 +21,7 @@ DigiData::DigiData(const CbmDigiData& storable)
   , fRich(ToPODVector(storable.fRich.fDigis))
   , fPsd(ToPODVector(storable.fPsd.fDigis))
   , fFsd(ToPODVector(storable.fFsd.fDigis))
+  , fMust(ToPODVector(storable.fMust.fDigis))
 {
 }
 
@@ -36,6 +37,7 @@ size_t DigiData::Size(ECbmModuleId system) const
     case ECbmModuleId::kRich: return fRich.size();
     case ECbmModuleId::kPsd: return fPsd.size();
     case ECbmModuleId::kFsd: return fFsd.size();
+    case ECbmModuleId::kMust: return fMust.size();
     default: throw std::runtime_error("DigiData: Invalid system Id " + ::ToString(system));
   }
 }
@@ -43,14 +45,15 @@ size_t DigiData::Size(ECbmModuleId system) const
 size_t DigiData::TotalSize() const
 {
   return fSts.size() + fMuch.size() + fTof.size() + fBmon.size() + fTrd.size() + fTrd2d.size() + fRich.size()
-         + fPsd.size() + fFsd.size();
+         + fPsd.size() + fFsd.size() + fMust.size();
 }
 
 size_t DigiData::TotalSizeBytes() const
 {
   return sizeof(CbmStsDigi) * fSts.size() + sizeof(CbmMuchDigi) * fMuch.size() + sizeof(CbmTofDigi) * fTof.size()
          + sizeof(CbmBmonDigi) * fBmon.size() + sizeof(CbmTrdDigi) * fTrd.size() + sizeof(CbmTrdDigi) * fTrd2d.size()
-         + sizeof(CbmRichDigi) * fRich.size() + sizeof(CbmPsdDigi) * fPsd.size() + sizeof(CbmFsdDigi) * fFsd.size();
+         + sizeof(CbmRichDigi) * fRich.size() + sizeof(CbmPsdDigi) * fPsd.size() + sizeof(CbmFsdDigi) * fFsd.size()
+         + sizeof(CbmMustDigi) * fMust.size();
 }
 
 CbmDigiData DigiData::ToStorable() const
@@ -92,6 +95,10 @@ CbmDigiData DigiData::ToStorable() const
       {
         .fDigis = ToStdVector(fFsd),
       },
+    .fMust =
+      {
+        .fDigis = ToStdVector(fMust),
+      },
   };
 }
 
diff --git a/algo/base/DigiData.h b/algo/base/DigiData.h
index 09b82d933478c57b65087b93e1d0a799120d287b..93475796344854d95709906bf219b04235c14af6 100644
--- a/algo/base/DigiData.h
+++ b/algo/base/DigiData.h
@@ -10,6 +10,7 @@
 #include "CbmEventTriggers.h"
 #include "CbmFsdDigi.h"
 #include "CbmMuchDigi.h"
+#include "CbmMustDigi.h"
 #include "CbmPsdDigi.h"
 #include "CbmRichDigi.h"
 #include "CbmStsDigi.h"
@@ -39,6 +40,7 @@ namespace cbm::algo
     PODVector<CbmRichDigi> fRich;  ///< Unpacked RICH digis
     PODVector<CbmPsdDigi> fPsd;    ///< Unpacked PSD digis
     PODVector<CbmFsdDigi> fFsd;    ///< Unpacked FSD digis
+    PODVector<CbmMustDigi> fMust;  ///< Unpacked MUST digis
 
     DigiData();
     ~DigiData();
diff --git a/algo/data/CMakeLists.txt b/algo/data/CMakeLists.txt
index 717ba53896c880649c4c07c1ff010fad8c14551b..5dd6ae7c12a4bd9a87840c259dba8aee2a3ad251 100644
--- a/algo/data/CMakeLists.txt
+++ b/algo/data/CMakeLists.txt
@@ -32,6 +32,7 @@ set(SRCS
   ${OFFLINE_DATA_DIR}/fsd/CbmFsdDigi.cxx
   ${OFFLINE_DATA_DIR}/fsd/CbmFsdAddress.cxx
 
+  ${OFFLINE_DATA_DIR}/must/CbmMustDigi.cxx
 
   ${OFFLINE_DATA_DIR}/raw/CriGet4Mess001.cxx
   ${OFFLINE_DATA_DIR}/raw/StsXyterMessage.cxx
@@ -50,6 +51,7 @@ target_include_directories(OnlineData
   PUBLIC ${OFFLINE_DATA_DIR}/tof
   PUBLIC ${OFFLINE_DATA_DIR}/psd
   PUBLIC ${OFFLINE_DATA_DIR}/fsd
+  PUBLIC ${OFFLINE_DATA_DIR}/must
   PUBLIC ${OFFLINE_DATA_DIR}/global
   PUBLIC ${OFFLINE_DATA_DIR}/raw
 )
diff --git a/algo/detectors/must/ReadoutConfig.cxx b/algo/detectors/must/ReadoutConfig.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..5d059750006d72109296c06fd63a58bf59481f1d
--- /dev/null
+++ b/algo/detectors/must/ReadoutConfig.cxx
@@ -0,0 +1,29 @@
+/* Copyright (C) 2025 Jagiellonian University, Krakow
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Bartosz Sobol [committer] */
+
+#include "ReadoutConfig.h"
+
+namespace cbm::algo
+{
+  CBM_YAML_INSTANTIATE(must::ReadoutConfig);
+
+  namespace must
+  {
+
+    const std::vector<uint16_t>& ReadoutConfig::GetEquipmentIds() const { return fEquipmentIds; }
+
+    const std::vector<uint32_t>& ReadoutConfig::GetTrbAddresses() const { return fTRBAddresses; }
+
+    const std::array<float, 33>& ReadoutConfig::GetToTShifts(const uint32_t TRBId) const
+    {
+      return fToTShifts.at(TRBId);
+    }
+
+    uint32_t ReadoutConfig::GetTimeOffset() const { return fTimeOffset; }
+
+    uint8_t ReadoutConfig::GetSystemVersion() const { return fSystemVersion; }
+
+  }  // namespace must
+
+}  // namespace cbm::algo
diff --git a/algo/detectors/must/ReadoutConfig.h b/algo/detectors/must/ReadoutConfig.h
new file mode 100644
index 0000000000000000000000000000000000000000..1bc75edbae3004b20a2dfd89a12c2fdbe6d66215
--- /dev/null
+++ b/algo/detectors/must/ReadoutConfig.h
@@ -0,0 +1,46 @@
+/* Copyright (C) 2025 Jagiellonian University, Krakow
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Bartosz Sobol [committer] */
+
+#pragma once
+
+#include "yaml/Property.h"
+#include "yaml/Yaml.h"
+
+#include <array>
+#include <cstdint>
+
+namespace cbm::algo::must
+{
+
+  class ReadoutConfig {
+
+   public:
+    ReadoutConfig() = default;
+
+    const std::vector<uint16_t>& GetEquipmentIds() const;
+
+    const std::vector<uint32_t>& GetTrbAddresses() const;
+
+    const std::array<float, 33>& GetToTShifts(const uint32_t TRBId) const;
+
+    uint32_t GetTimeOffset() const;
+
+    uint8_t GetSystemVersion() const;
+
+    std::vector<uint16_t> fEquipmentIds{};
+    std::vector<uint32_t> fTRBAddresses{};
+    std::map<uint32_t, std::array<float, 33>> fToTShifts;
+    uint32_t fTimeOffset{};
+    uint8_t fSystemVersion{};
+
+    CBM_YAML_PROPERTIES(yaml::Property(&ReadoutConfig::fEquipmentIds, "equipmentIds", "List of equipment IDs", {}, YAML::Flow),
+                        yaml::Property(&ReadoutConfig::fTRBAddresses, "TRBAddresses", "List of TRB addresses",  {}, YAML::Flow),
+                        yaml::Property(&ReadoutConfig::fToTShifts, "ToTShifts", "Map from TRB to list of ToT shifts per channel",  {}, YAML::Flow),
+                        yaml::Property(&ReadoutConfig::fTimeOffset, "timeOffset", "System time offset"),
+                        yaml::Property(&ReadoutConfig::fSystemVersion, "systemVersion", "System version"));
+  };
+
+}  // namespace cbm::algo::must
+
+CBM_YAML_EXTERN_DECL(cbm::algo::must::ReadoutConfig);
diff --git a/algo/detectors/must/Unpack.cxx b/algo/detectors/must/Unpack.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..326ed5e31bd2a9abc036cfa0012d57b86238b4a3
--- /dev/null
+++ b/algo/detectors/must/Unpack.cxx
@@ -0,0 +1,29 @@
+/* Copyright (C) 2025 Jagiellonian University, Krakow
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Bartosz Sobol [committer] */
+
+#include "Unpack.h"
+
+#include "AlgoFairloggerCompat.h"
+
+
+namespace cbm::algo::must
+{
+  Unpack::Unpack(const ReadoutConfig& readout) : fReadout(readout)
+  {
+    const UnpackPar unpackPar{.fTRBAddresses = fReadout.fTRBAddresses,
+                              .fToTShifts    = fReadout.fToTShifts,
+                              .fTimeOffset   = fReadout.fTimeOffset};
+
+    for (const auto eqId : fReadout.GetEquipmentIds()) {
+      fAlgos.emplace(UnpackKey{eqId, fReadout.GetSystemVersion()}, std::make_unique<UnpackMS>(unpackPar));
+
+      L_(info) << "--- Configured equipment " << eqId << " of MUST@RICH.";
+    }
+
+    L_(info) << "--- Configured " << fAlgos.size() << " unpacker algorithms for MUST@RICH.";
+  }
+
+  Unpack::Result_t Unpack::operator()(const fles::Timeslice& ts) const { return DoUnpack(fles::Subsystem::RICH, ts); }
+
+}  // namespace cbm::algo::must
diff --git a/algo/detectors/must/Unpack.h b/algo/detectors/must/Unpack.h
new file mode 100644
index 0000000000000000000000000000000000000000..d98c8a77c06a899551b9454442c1b469f243330d
--- /dev/null
+++ b/algo/detectors/must/Unpack.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2025 Jagiellonian University, Krakow
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Bartosz Sobol [committer] */
+
+#pragma once
+
+#include "CommonUnpacker.h"
+#include "ReadoutConfig.h"
+#include "UnpackMS.h"
+
+namespace cbm::algo::must
+{
+
+  namespace detail
+  {
+    using UnpackBase = CommonUnpacker<CbmMustDigi, UnpackMonitorData, UnpackAuxData>;
+  }
+
+  class Unpack : public detail::UnpackBase {
+   public:
+    using Result_t = detail::UnpackBase::Result_t;
+
+    Unpack(const ReadoutConfig& readout);
+
+    Result_t operator()(const fles::Timeslice&) const;
+
+   private:
+    ReadoutConfig fReadout;
+  };
+
+}  // namespace cbm::algo::must
diff --git a/algo/detectors/must/UnpackMS.cxx b/algo/detectors/must/UnpackMS.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..92cc8d4de3d4f7d52517bb6a1fe1fee9bfb26cca
--- /dev/null
+++ b/algo/detectors/must/UnpackMS.cxx
@@ -0,0 +1,460 @@
+/* Copyright (C) 2021-2025 Jagiellonian University, Krakow; Goethe-University Frankfurt, Frankfurt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Bartosz Sobol [committer], Pascal Raisig, Dominik Smith */
+
+#include "UnpackMS.h"
+
+#include "AlgoFairloggerCompat.h"
+
+#include <cstdint>
+
+namespace cbm::algo::must
+{
+  UnpackMS::UnpackMS(const UnpackPar& pars) : fParams(pars) {}
+
+  UnpackMS::~UnpackMS() = default;
+
+  // ----   Algorithm execution   ---------------------------------------------
+  UnpackMS::Result_t UnpackMS::operator()(const uint8_t* msContent, const fles::MicrosliceDescriptor& msDescr,
+                                          const uint64_t tTimeslice) const
+  {
+    MSContext ctx;
+
+    // Clear CbmTime of MS. Used to get time offset of subtriggers to MS start
+    ctx.cbmTimeMS = 0;
+    ctx.digis.reserve(msDescr.size / sizeof(u32));
+
+    must::MicrosliceReader reader;
+    reader.SetData(msContent, msDescr.size);
+
+    const auto mstime = msDescr.idx;
+    ctx.refTime       = mstime - tTimeslice;
+
+    // There are a lot of MS  with 8 bytes size
+    // Does one need these MS?
+    if (reader.GetSize() <= 8) return {};
+
+    while (true) {
+      ProcessTrbPacket(reader, ctx);
+      ProcessHubBlock(reader, ctx);
+
+      // -4*2 for 2 last words which contain microslice index
+      if (reader.GetOffset() >= reader.GetSize() - 8) break;
+      // -4*3 for 0xffffffff padding and 2 last words which contain microslice index
+      if (reader.IsNextPadding() && reader.GetOffset() >= reader.GetSize() - 12) break;
+    }
+    [[maybe_unused]] uint32_t msIndexWord1 = reader.NextWord();
+    [[maybe_unused]] uint32_t msIndexWord2 = reader.NextWord();
+
+    return std::make_tuple(std::move(ctx.digis), std::move(ctx.monitor), UnpackAuxData());
+  }
+
+
+  void UnpackMS::ProcessTrbPacket(must::MicrosliceReader& reader, MSContext& ctx) const
+  {
+    //process CBM time
+    const uint32_t word_MSB = reader.NextWord();  // CBM 63:32
+    const uint32_t word_LSB = reader.NextWord();  // CBM 31: 0
+    ctx.cbmTimePacket       = (uint64_t) word_MSB << 32 | word_LSB;
+    if (ctx.cbmTimeMS == 0) ctx.cbmTimeMS = ctx.cbmTimePacket;
+
+    //discard unused words
+    for (auto l = 0; l < 10; ++l) {
+      reader.NextWord();
+    }
+
+    //process remaining words
+    for (auto l = 0; l < 14; ++l) {
+      const uint32_t wordEpoch = reader.NextWord();
+      const uint32_t epoch     = ProcessEpoch(wordEpoch);
+      const uint32_t wordTime  = reader.NextWord();
+      const TdcTimeData td     = ProcessTimeData(wordTime);
+      const double fullTime    = CalculateTime(epoch, td.fCoarse, td.fFine);
+
+      if (l == 0) ctx.prevLastCh0ReTime[12] = fullTime;
+      if (l == 1) ctx.mbsCorr = fullTime - ctx.prevLastCh0ReTime[12];  // = MbsPrevTimeCh1 - MbsPrevTimeCh0
+      if (l > 1) ctx.prevLastCh0ReTime[l - 2] = fullTime;
+    }
+
+    [[maybe_unused]] const uint32_t trbNum = reader.NextWord();  // TRB trigger number
+  }
+
+
+  void UnpackMS::ProcessHubBlock(must::MicrosliceReader& reader, MSContext& ctx) const
+  {
+    uint32_t word = reader.NextWord();
+
+    [[maybe_unused]] const uint32_t hubId = word & 0xffff;          // 16 bits
+    const uint32_t hubSize                = (word >> 16) & 0xffff;  // 16 bits
+
+    bool isLast            = false;  // if true then it is CTS sub-sub-event
+    size_t totalSize       = 0;
+    ctx.currentSubSubEvent = 0;
+
+    uint32_t subSubEventId = 0, subSubEventSize = 0;
+
+    //loop over events in hub block
+    while (totalSize < hubSize) {
+      word            = reader.NextWord();
+      subSubEventId   = word & 0xffff;                              // 16 bits
+      subSubEventSize = (word >> 16) & 0xffff;                      // 16 bits
+      isLast          = reader.IsLastSubSubEvent(subSubEventSize);  // if true then it is CTS sub-sub-event
+      totalSize += (1 + subSubEventSize);
+
+      if (!isLast) {                                 // all except last are DiRICH events
+        if (((subSubEventId >> 12) & 0xF) != 0x9) {  // catch invalid ids
+          ctx.monitor.fNumErrInvalidHubId++;
+        }
+        if (totalSize == hubSize) {
+          ctx.monitor.fNumErrInvalidHubSize++;
+        }
+        ProcessSubSubEvent(reader, subSubEventSize, subSubEventId, ctx);
+        ctx.currentSubSubEvent++;
+      }
+    }
+
+    //last event ist expected to be CTS
+    if (totalSize != hubSize || !isLast) {
+      ctx.monitor.fNumErrInvalidHubSize++;
+    }
+    subSubEventSize = ProcessCtsHeader(reader, subSubEventSize, subSubEventId);
+    ProcessSubSubEvent(reader, subSubEventSize, subSubEventId, ctx);
+
+    // read last words
+    int lastWordsCounter = 0;
+    while (true) {
+      lastWordsCounter++;
+      word = reader.NextWord();
+      if (word == 0x600dda7a) {
+        if (reader.IsNextPadding()) word = reader.NextWord();
+        break;
+      }
+      if (lastWordsCounter >= 7) {
+        ctx.monitor.fNumErrExcessLastWords++;
+      }
+    }
+  }
+
+  int UnpackMS::ProcessCtsHeader(must::MicrosliceReader& reader, uint32_t subSubEventSize,
+                                 uint32_t /*subSubEventId*/) const
+  {
+    const uint32_t word = reader.NextWord();
+
+    [[maybe_unused]] const uint32_t ctsState = word & 0xffff;  // 16 bits
+
+    const uint32_t nofInputs    = (word >> 16) & 0xf;   // 4 bits
+    const uint32_t nofTrigCh    = (word >> 20) & 0x1f;  // 5 bits
+    const uint32_t inclLastIdle = (word >> 25) & 0x1;   // 1 bit
+    const uint32_t inclTrigInfo = (word >> 26) & 0x1;   // 1 bit
+    const uint32_t inclTime     = (word >> 27) & 0x1;   // 1 bit
+    const uint32_t ETM          = (word >> 28) & 0x3;   // 2 bits
+
+    uint32_t ctsInfoSize = 2 * nofInputs + 2 * nofTrigCh + 2 * inclLastIdle + 3 * inclTrigInfo + inclTime;  // in words
+    switch (ETM) {
+      case 0: break;
+      case 1: ctsInfoSize += 1; break;
+      case 2: ctsInfoSize += 4; break;
+      case 3: break;
+    }
+    for (uint32_t i = 0; i < ctsInfoSize; i++) {
+      reader.NextWord();  // do nothing?
+    }
+    const int nofTimeWords = subSubEventSize - ctsInfoSize - 1;
+    return nofTimeWords;
+  }
+
+  void UnpackMS::ProcessSubSubEvent(must::MicrosliceReader& reader, int nofTimeWords, uint32_t subSubEventId,
+                                    MSContext& ctx) const
+  {
+    bool wasHeader  = false;
+    bool wasEpoch   = false;
+    bool wasTime    = false;
+    bool wasTrailer = false;
+
+    uint32_t epoch = 0;  // store last epoch obtained in sub-sub-event
+
+    // Store last raising edge time for each channel or -1. if no time
+    // this array is used to match raising and falling edges
+    std::vector<double> raisingTime(33, -1.);
+
+    // check if DiRICH (SubSubEvId) is masked and skip to end if so
+    if (CheckMaskedDiRICH(subSubEventId)) {
+      for (int i = 0; i < nofTimeWords; i++) {
+        reader.NextWord();
+      }
+      return;
+    }
+
+    // Skip SubSubEvent for CTS and not MUST addresses
+    if (std::find(fParams.fTRBAddresses.cbegin(), fParams.fTRBAddresses.cend(), subSubEventId)
+        == fParams.fTRBAddresses.end()) {
+      ctx.monitor.fNumCtsAndUnmappedDirich++;
+      ctx.monitor.fNumSkippedSubsubevent++;
+      for (int i = 0; i < nofTimeWords; i++) {
+        reader.NextWord();
+      }
+      return;
+    }
+
+    // Catch Subsubevents where some of the 3 "mandatory words" is missing
+    // but this lack was properly accounted/detected by the TRBnet/CTS
+    // => Otherwise the final readout of the Trailer word offsets the next
+    //    subsubevent
+    if (nofTimeWords < 3) {
+      ctx.monitor.fNumSkippedSubsubevent++;
+      for (int i = 0; i < nofTimeWords; i++) {
+        reader.NextWord();
+      }
+      return;
+    }
+
+    // First word is expected to be of type "header"
+    if (GetTdcWordType(reader.NextWord()) != TdcWordType::Header) {
+      L_(debug) << "MUST DiRICH 0x" << std::hex << subSubEventId << std::dec
+                << ": 1st message is not a TDC Header, skipping subsubevent";
+      ctx.monitor.fNumErrInvalidFirstMessage++;
+      ctx.monitor.fNumSkippedSubsubevent++;
+      for (int i = 1; i < nofTimeWords; i++) {
+        reader.NextWord();
+      }
+      return;
+    }
+    else {
+      wasHeader = true;
+    }
+
+    // Second word is expected to be of type "epoch"
+    uint32_t word = reader.NextWord();
+    if (GetTdcWordType(word) != TdcWordType::Epoch) {
+      L_(debug) << "MUST DiRICH 0x" << std::hex << subSubEventId << std::dec
+                << ": 2nd message is not an epoch, skipping subsubevent";
+      ctx.monitor.fNumErrInvalidSecondMessage++;
+      ctx.monitor.fNumSkippedSubsubevent++;
+      // Currently skip the subsubevent
+      // TODO: Check if possible to only skip to next Epoch and continue from there
+      for (int i = 2; i < nofTimeWords; i++) {
+        reader.NextWord();
+      }
+      return;
+    }
+    else {
+      epoch    = ProcessEpoch(word);
+      wasEpoch = true;
+    }
+
+    // Loop over words
+    for (int i = 2; i < nofTimeWords - 1; i++) {
+      word = reader.NextWord();
+      switch (GetTdcWordType(word)) {
+        case TdcWordType::TimeData: {
+          if (!wasHeader || !wasEpoch || wasTrailer) {
+            L_(debug) << "MUST DiRICH 0x" << std::hex << subSubEventId << std::dec
+                      << ": illegal position of TDC Time (before header/epoch or after trailer)";
+            ctx.monitor.fNumErrWildTdcTime++;
+            ctx.monitor.fNumSkippedSubsubevent++;
+            // Currently skip the subsubevent
+            // TODO: Check if possible to only skip to next Epoch and continue from there
+            for (; i < nofTimeWords - 1; i++) {
+              reader.NextWord();
+            }
+            return;
+          }
+          wasTime = true;
+          ProcessTimeDataWord(epoch, word, subSubEventId, raisingTime, ctx);
+          break;
+        }
+        case TdcWordType::Epoch: {
+          if (!wasHeader || wasTrailer) {
+            L_(debug) << "MUST DiRICH 0x" << std::hex << subSubEventId << std::dec
+                      << ": illegal position of TDC Epoch (before header or after trailer)";
+            ctx.monitor.fNumErrWildEpoch++;
+            ctx.monitor.fNumSkippedSubsubevent++;
+            for (; i < nofTimeWords - 1; i++) {
+              reader.NextWord();
+            }
+            return;
+          }
+          wasEpoch = true;
+          epoch    = ProcessEpoch(word);
+          break;
+        }
+        case TdcWordType::Header: {
+          if (wasEpoch || wasTime || wasTrailer) {
+            L_(debug) << "MUST DiRICH 0x" << std::hex << subSubEventId << std::dec
+                      << ": illegal position of TDC Header (after time/epoch/trailer)";
+            ctx.monitor.fNumErrWildHeaderMessage++;
+            ctx.monitor.fNumSkippedSubsubevent++;
+            for (; i < nofTimeWords - 1; i++) {
+              reader.NextWord();
+            }
+            return;
+          }
+          ctx.monitor.fNumErrWildHeaderMessage++;
+          break;
+        }
+        case TdcWordType::Trailer: {
+          if (!wasEpoch || !wasTime || !wasHeader) {
+            L_(debug) << "MUST DiRICH 0x" << std::hex << subSubEventId << std::dec
+                      << ": illegal position of TDC Trailer (before time/epoch/header)";
+            ctx.monitor.fNumErrWildTrailerMessage++;
+            ctx.monitor.fNumSkippedSubsubevent++;
+            for (; i < nofTimeWords - 1; i++) {
+              reader.NextWord();
+            }
+            return;
+          }
+          L_(debug) << "MUST DiRICH 0x" << std::hex << subSubEventId << std::dec << ": TDC Trailer too early, "
+                    << (nofTimeWords - 1 - i) << " word(s) before last";
+          ctx.monitor.fNumErrWildTrailerMessage++;
+          wasTrailer = true;
+          break;
+        }
+        case TdcWordType::Debug: {
+          // for the moment do nothing
+          ctx.monitor.fNumDebugMessage++;
+          break;
+        }
+        case TdcWordType::Error: {
+          ctx.monitor.fNumErrTdcErrorWord++;
+          break;
+        }
+      }
+    }
+
+    // Last word is expected to be of type "trailer"
+    word = reader.NextWord();
+    if (GetTdcWordType(word) != TdcWordType::Trailer) {
+      L_(debug) << "MUST DiRICH 0x" << std::hex << subSubEventId << std::dec << ": Last word not a TDC trailer";
+      ctx.monitor.fNumErrInvalidLastMessage++;
+      if (GetTdcWordType(word) == TdcWordType::TimeData && !wasTrailer) {
+        if (ProcessTimeDataWord(epoch, word, subSubEventId, raisingTime, ctx)) {
+          ctx.monitor.fNumWarnRecoveredLastDigi++;
+          L_(debug) << "MUST DiRICH 0x" << std::hex << subSubEventId << std::dec
+                    << ": Rescuing TimeData in subsubevent with missing Trailer leading to saved Digi";
+        }
+        else {
+          ctx.monitor.fNumErrOrphanRecovTimeData++;
+          L_(debug) << "MUST DiRICH 0x" << std::hex << subSubEventId << std::dec
+                    << ": Rescuing TimeData in subsubevent with missing Trailer w/o extra digi";
+        }
+      }
+    }
+  }
+
+  // ---- ProcessTimeDataWord ----
+  bool UnpackMS::ProcessTimeDataWord(uint32_t epoch, uint32_t tdcWord, uint32_t subSubEventId,
+                                     std::vector<double>& raisingTime, MSContext& ctx) const
+  {
+    const TdcTimeData td  = ProcessTimeData(tdcWord);
+    const double fullTime = CalculateTime(epoch, td.fCoarse, td.fFine);
+
+    bool madeDigi = false;
+    if (td.fChannel != 0) {
+      const double dT = fullTime - ctx.prevLastCh0ReTime[ctx.currentSubSubEvent];
+      const double subtrigOffset =
+        (ctx.cbmTimePacket - ctx.cbmTimeMS) * 25.0;  // offset of SubTrigger to MS start in ns
+      const double fullTimeCorr = dT - ctx.mbsCorr + subtrigOffset;
+
+      if (td.fChannel < 1 || td.fChannel >= raisingTime.size()) {
+        ctx.monitor.fNumErrChannelOutOfBounds++;
+        return false;
+      }
+      if (td.fIsRisingEdge) {
+        // always store the latest raising edge. It means that in case RRFF situation only middle RF will be matched.
+        raisingTime[td.fChannel] = fullTimeCorr;
+      }
+      else {
+        if (raisingTime[td.fChannel] != -1.) {
+          // Matching was found, calculate ToT, if tot is in a good range -> create digi
+          const double ToT = fullTimeCorr - raisingTime[td.fChannel];
+          if (ToT >= fToTMin && ToT <= fToTMax) {
+            if (fullTimeCorr >= 0.0) {
+              WriteOutputDigi(subSubEventId, td.fChannel, raisingTime[td.fChannel], ToT, ctx);
+              L_(info) << "[MUST DIGI] "
+                       << "TRBId: 0x" << std::hex << subSubEventId << std::dec << "; channel: 0x" << std::hex
+                       << td.fChannel << std::dec << "; raising time: " << raisingTime[td.fChannel] << "; ToT: " << ToT;
+              madeDigi = true;
+            }
+          }
+          // pair was created, set raising edge to -1.
+          raisingTime[td.fChannel] = -1.;
+        }
+      }
+    }
+    else {
+      ctx.monitor.fNumErrChannelOutOfBounds++;
+    }
+    return madeDigi;
+  }
+
+  double UnpackMS::CalculateTime(uint32_t epoch, uint32_t coarse, uint32_t fine) const
+  {
+    return ((double) epoch) * 2048. * 5. + ((double) coarse) * 5. - ((double) fine) * 0.005;
+  }
+
+  void UnpackMS::WriteOutputDigi(uint32_t trbId, uint16_t channel, double time, double tot, MSContext& ctx) const
+  {
+    const double ToTCorrection = fbDoToTCorr ? fParams.fToTShifts.at(trbId)[channel] : 0.;
+    const double finalToT      = tot - ToTCorrection;
+
+    const double finalTime = time + static_cast<double>(ctx.refTime) - fParams.fTimeOffset;
+
+    ctx.digis.emplace_back(trbId, channel, finalTime, finalToT);
+  }
+
+  bool UnpackMS::CheckMaskedDiRICH(int32_t subSubEventId) const
+  {
+    for (unsigned int i = 0; i < fMaskedDiRICHes.size(); ++i) {
+      if (fMaskedDiRICHes.at(i) == subSubEventId) return true;
+    }
+    return false;
+  }
+
+  TdcWordType UnpackMS::GetTdcWordType(uint32_t tdcWord) const
+  {
+    uint32_t tdcTimeDataMarker = (tdcWord >> 31) & 0x1;  // 1 bit
+    uint32_t tdcMarker         = (tdcWord >> 29) & 0x7;  // 3 bits
+
+    if (tdcTimeDataMarker == 0x1) {
+      // TODO: I also include tdcMarker == 0x5, some tdc time data words have this marker. Is it correct?
+      if (tdcMarker == 0x4 || tdcMarker == 0x5) {
+        return TdcWordType::TimeData;
+      }
+      else {
+        return TdcWordType::Error;
+      }
+    }
+    if (tdcMarker == 0x0) return TdcWordType::Trailer;
+    if (tdcMarker == 0x1) return TdcWordType::Header;
+    if (tdcMarker == 0x2) return TdcWordType::Debug;
+    if (tdcMarker == 0x3) return TdcWordType::Epoch;
+    return TdcWordType::Error;
+  }
+
+  uint32_t UnpackMS::GetStrawUID(uint32_t fpgaID, uint32_t ch) const
+  {
+    // First 16 bits are used for the FPGA ID, then
+    // 8 bits unused and then 8 bits are used for the channel
+    return ((fpgaID << 16) | (ch & 0x00FF));
+  }
+
+  TdcTimeData UnpackMS::ProcessTimeData(uint32_t tdcWord) const
+  {
+    TdcTimeData out;
+    out.fCoarse       = static_cast<uint32_t>(tdcWord & 0x7ff);          // 11 bits
+    out.fIsRisingEdge = static_cast<uint32_t>((tdcWord >> 11) & 0x1);    // 1 bit
+    out.fFine         = static_cast<uint32_t>((tdcWord >> 12) & 0x3ff);  // 10 bits
+    out.fChannel      = static_cast<uint32_t>((tdcWord >> 22) & 0x7f);   // 7 bits
+    return out;
+  }
+
+  uint32_t UnpackMS::ProcessEpoch(uint32_t tdcWord) const { return static_cast<uint32_t>(tdcWord & 0xfffffff); }
+
+  uint16_t UnpackMS::ProcessHeader(uint32_t tdcWord) const
+  {
+    return static_cast<uint16_t>(tdcWord & 0xff);  //8 bits
+  }
+
+  uint16_t UnpackMS::ProcessTrailer(uint32_t tdcWord) const { return static_cast<uint16_t>(tdcWord & 0xffff); }
+
+}  // namespace cbm::algo::must
diff --git a/algo/detectors/must/UnpackMS.h b/algo/detectors/must/UnpackMS.h
new file mode 100644
index 0000000000000000000000000000000000000000..1532e22c28977e974073f0144232f70904c80ef0
--- /dev/null
+++ b/algo/detectors/must/UnpackMS.h
@@ -0,0 +1,275 @@
+/* Copyright (C) 2021-2025 Jagiellonian University, Krakow; Goethe-University Frankfurt, Frankfurt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Bartosz Sobol [committer], Pascal Raisig, Dominik Smith */
+
+#pragma once
+
+#include "CbmMustDigi.h"
+#include "Definitions.h"
+#include "ReadoutConfig.h"
+#include "Timeslice.hpp"
+#include "UnpackMSBase.h"
+
+#include <cstddef>
+#include <cstdint>
+#include <iomanip>
+#include <memory>
+#include <sstream>
+#include <utility>
+
+namespace cbm::algo::must
+{
+
+  class MicrosliceReader;
+
+  enum class TdcWordType
+  {
+    TimeData,
+    Header,
+    Epoch,
+    Trailer,
+    Debug,
+    Error
+  };
+
+  struct TdcTimeData {
+    uint32_t fCoarse       = 0;  // 11 bits
+    uint32_t fIsRisingEdge = 0;  // 1 bit
+    uint32_t fFine         = 0;  // 10 bits
+    uint32_t fChannel      = 0;  // 7 bits
+  };
+
+
+  /** @struct UnpackElinkPar
+   ** @author Dominik Smith <d.smith@gsi.de>
+   ** @since 26 May 2023
+   ** @brief RICH Unpacking parameters for one eLink / ASIC
+   **/
+  struct UnpackElinkPar {
+    std::vector<double> fToTshift;  ///< TOT shift for different channels
+    uint64_t fTimeOffset = 0.;      ///< Time calibration parameter
+  };
+
+
+  /** @struct UnpackPar
+   ** @author Dominik Smith <d.smith@gsi.de>
+   ** @since 26 May 2023
+   ** @brief Parameters required for the RICH unpacking (specific to one component)
+   **/
+  struct UnpackPar {
+    std::vector<uint32_t> fTRBAddresses;
+    std::map<uint32_t, std::array<float, 33>> fToTShifts;
+    uint32_t fTimeOffset;
+  };
+
+
+  /** @struct UnpackMonitorData
+   ** @author Dominik Smith <d.smith@gsi.de>
+   ** @since 26 May 2023
+   ** @brief Monitoring data for RICH unpacking
+   **/
+  struct UnpackMonitorData {
+    uint32_t fNumDebugMessage            = 0;  ///< Received debug messages
+    uint32_t fNumCtsAndUnmappedDirich    = 0;  ///< Dirich address is unknown (or is of a CTS)
+    uint32_t fNumErrInvalidFirstMessage  = 0;  ///< First message is not HEADER
+    uint32_t fNumErrInvalidSecondMessage = 0;  ///< Second message is not EPOCH
+    uint32_t fNumErrInvalidLastMessage   = 0;  ///< Last message is not TRAILER
+    uint32_t fNumErrWildHeaderMessage    = 0;  ///< TDC header in invalid position
+    uint32_t fNumErrWildTrailerMessage   = 0;  ///< TDC trailer in invalid position
+    uint32_t fNumErrWildEpoch            = 0;  ///< TDC epoch in invalid position
+    uint32_t fNumErrWildTdcTime          = 0;  ///< TDC time (digi) in invalid position
+    uint32_t fNumErrTdcErrorWord         = 0;  ///< TDC word of error type
+    uint32_t fNumErrChannelOutOfBounds   = 0;  ///< TDC channel out of bounds
+    uint32_t fNumErrOrphanRecovTimeData  = 0;  ///< TimeData in last position (not TRAILER) but not creating a Digi
+    uint32_t fNumWarnRecoveredLastDigi   = 0;  ///< TimeData in last position (not TRAILER) led to recovered Digi
+    uint32_t fNumErrInvalidHubId         = 0;  ///< "SubSubEvent" has invalid ID
+    uint32_t fNumErrInvalidHubSize       = 0;  ///< Premature end of hub block
+    uint32_t fNumErrExcessLastWords      = 0;  ///< More than expected trailing words
+    uint32_t fNumSkippedSubsubevent      = 0;  ///< Whenever subsubevent skipped due to error (but not mask)
+
+    bool HasErrors() const
+    {
+      uint32_t numErrors = fNumDebugMessage + fNumErrInvalidFirstMessage + fNumErrInvalidSecondMessage
+                           + fNumErrInvalidLastMessage + fNumErrWildHeaderMessage + fNumErrWildTrailerMessage
+                           + fNumErrWildEpoch + fNumErrWildTdcTime + fNumErrTdcErrorWord + fNumErrChannelOutOfBounds
+                           + fNumErrOrphanRecovTimeData + fNumErrInvalidHubId + fNumErrInvalidHubSize
+                           + fNumErrExcessLastWords;
+      return (numErrors > 0 ? true : false);
+    }
+    std::string print() const
+    {
+      std::stringstream ss;
+      ss << "errors " << fNumDebugMessage << " | " << fNumCtsAndUnmappedDirich << " | " << fNumErrInvalidFirstMessage
+         << " | " << fNumErrInvalidSecondMessage << " | " << fNumErrInvalidLastMessage << " | "
+         << fNumErrWildHeaderMessage << " | " << fNumErrWildTrailerMessage << " | " << fNumErrWildEpoch << " | "
+         << fNumErrWildTdcTime << " | " << fNumErrTdcErrorWord << " | " << fNumErrChannelOutOfBounds << " | "
+         << fNumErrOrphanRecovTimeData << " | " << fNumWarnRecoveredLastDigi << " | " << fNumErrInvalidHubId << " | "
+         << fNumErrInvalidHubSize << " | " << fNumErrExcessLastWords << " | " << fNumSkippedSubsubevent;
+      return ss.str();
+    }
+  };
+
+  /** @struct UnpackAux
+   ** @author Dominik Smith <d.smith@gsi.de>
+   ** @since 24 May 2024
+   ** @brief Auxiliary data for BMON unpacking
+   **/
+  struct UnpackAuxData {
+    ///// TO BE FILLED
+  };
+
+  class UnpackMS : public UnpackMSBase<CbmMustDigi, UnpackMonitorData, UnpackAuxData> {
+
+   public:
+    UnpackMS() = delete;
+
+    /** @brief Construct from parameters **/
+    UnpackMS(const UnpackPar& pars);
+
+
+    /** @brief Destructor **/
+    ~UnpackMS() override;
+
+
+    /** @brief Algorithm execution
+     ** @param  msContent  Microslice payload
+     ** @param  msDescr    Microslice descriptor
+     ** @param  tTimeslice Unix start time of timeslice [ns]
+     ** @return RICH digi data
+     **/
+    Result_t operator()(const uint8_t* msContent, const fles::MicrosliceDescriptor& msDescr,
+                        const uint64_t tTimeslice) const override;
+
+    /** @brief Set the parameter container
+     ** @param params Pointer to parameter container
+     **/
+    void SetParams(std::unique_ptr<UnpackPar> params) { fParams = *(std::move(params)); }
+
+   private:
+    struct MSContext {
+      u64 cbmTimeMS     = 0;  // CbmTime of MS. Used to get time offset of subtriggers to MS start
+      u64 cbmTimePacket = 0;
+      u64 refTime       = 0;
+
+      // FIXME: Magic number, should be named.
+      //
+      // Comment from RICH expert (Martin Beyer):
+      //  > This number will also be valid for the Cbm Rich, 1 Rich backplane contains max. 12 DiRICHes.
+      //  > For the Cbm Rich the number of backplanes will be scaled
+      //  > up. Nevertheless it is very likely that fewer DiRiches(maybe not constant)
+      //  > per backplane are used in the day1 setup.
+      double prevLastCh0ReTime[13];  // 12 DiRICHes chnl0 + 1 CTS chnl0
+      double mbsCorr = 0.;
+
+      uint16_t currentSubSubEvent = 0;
+
+      std::vector<CbmMustDigi> digis;
+      UnpackMonitorData monitor;
+    };
+
+    void ProcessTrbPacket(MicrosliceReader& reader, MSContext& ctx) const;
+
+    void ProcessHubBlock(MicrosliceReader& reader, MSContext& ctx) const;
+
+    int ProcessCtsHeader(MicrosliceReader& reader, uint32_t subSubEventSize, uint32_t subSubEventId) const;
+
+    void ProcessSubSubEvent(MicrosliceReader& reader, int nofTimeWords, uint32_t subSubEventId, MSContext& ctx) const;
+
+    bool ProcessTimeDataWord(uint32_t epoch, uint32_t tdcWord, uint32_t subSubEventId, std::vector<double>& raisingTime,
+                             MSContext& ctx) const;
+
+    TdcWordType GetTdcWordType(uint32_t tdcWord) const;
+
+    TdcTimeData ProcessTimeData(uint32_t tdcWord) const;
+
+    uint32_t ProcessEpoch(uint32_t tdcWord) const;
+
+    uint16_t ProcessHeader(uint32_t tdcWord) const;
+
+    uint16_t ProcessTrailer(uint32_t tdcWord) const;
+
+    void WriteOutputDigi(uint32_t trbId, uint16_t channel, double time, double tot, MSContext& ctx) const;
+
+    double CalculateTime(uint32_t epoch, uint32_t coarse, uint32_t fine) const;
+
+    uint32_t GetStrawUID(uint32_t fpgaID, uint32_t ch) const;
+
+    bool CheckMaskedDiRICH(int32_t subSubEventId) const;
+
+    UnpackPar fParams;  ///< Parameter container
+
+    std::vector<int32_t> fMaskedDiRICHes;
+
+    bool fbDoToTCorr = true;  //activates ToT correction from parameter file
+
+    // uint64_t fCbmTimeMS;
+    // uint64_t fCbmTimePacket;
+
+    // double fMbsCorr = 0.;
+    // double fPrevLastCh0ReTime[13];  // 12 DiRICHes chnl0 + 1 CTS chnl0
+    // uint16_t fCurrentSubSubEvent = 0;
+
+    double fToTMin = -20.;
+    double fToTMax = 100.;
+  };
+
+
+  class MicrosliceReader {
+   private:
+    const uint8_t* fData = nullptr;
+    size_t fSize         = 0;
+    size_t fOffset       = 0;  // offset in bytes
+    size_t fWordCounter  = 0;
+    uint32_t fCurWord    = 0;
+
+   public:
+    void SetData(const uint8_t* data, size_t size)
+    {
+      fData        = data;
+      fSize        = size;
+      fOffset      = 0;
+      fWordCounter = 0;
+      fCurWord     = 0;
+    }
+
+    const uint8_t* GetData() { return fData; }
+    size_t GetSize() { return fSize; }
+    size_t GetOffset() { return fOffset; }
+    size_t GetWordCounter() { return fWordCounter; }
+    uint32_t GetCurWord() { return fCurWord; }
+
+    std::string GetWordAsHexString(uint32_t word)
+    {
+      std::stringstream stream;
+      stream << "0x" << std::setfill('0') << std::setw(sizeof(uint32_t) * 2) << std::hex << word;
+      return stream.str();
+    }
+
+    uint32_t NextWord()
+    {
+      uint32_t i = ((uint32_t*) (fData + fOffset))[0];
+      //swap bytes
+      i = (i >> 24) | ((i << 8) & 0x00FF0000) | ((i >> 8) & 0x0000FF00) | (i << 24);
+      fOffset += 4;
+      fWordCounter++;
+      fCurWord = i;
+      return i;
+    }
+
+    bool IsNextPadding()
+    {
+      uint32_t nextWord = ((uint32_t*) (fData + fOffset))[0];
+      if (nextWord == 0xffffffff) return true;
+      return false;
+    }
+
+    bool IsLastSubSubEvent(uint32_t subSubEventSize)
+    {
+      uint32_t i = ((uint32_t*) (fData + fOffset + 4 * subSubEventSize))[0];
+      i          = (i >> 24) | ((i << 8) & 0x00ff0000) | ((i >> 8) & 0x0000ff00) | (i << 24);
+      if (i == 0x00015555) return true;
+      return false;
+    }
+  };
+
+}  // namespace cbm::algo::must
diff --git a/algo/detectors/rich/ReadoutConfig.cxx b/algo/detectors/rich/ReadoutConfig.cxx
index cddc2ed1cd7da9b05650381257b185372b702f34..5d74b68d130cf7230b08852fb188c362ecfeae89 100644
--- a/algo/detectors/rich/ReadoutConfig.cxx
+++ b/algo/detectors/rich/ReadoutConfig.cxx
@@ -65,7 +65,7 @@ namespace cbm::algo::rich
 
     // Constants
     const uint16_t numComp          = 1;   // Number of components
-    const uint16_t numElinksPerComp = 87;  // Number of elinks per component (74 mRICH, 5 FSD/NCAL, 8 PASTA)
+    const uint16_t numElinksPerComp = 79;  // Number of elinks per component (74 mRICH, 5 FSD/NCAL, 8 MUST)
     const uint16_t numChanPerElink  = 33;  // Number of channels per Elink
 
     // Equipment IDs for each component
@@ -73,15 +73,13 @@ namespace cbm::algo::rich
     uint16_t eqId[numComp] = {12289};
 
     uint32_t TRBaddresses[numElinksPerComp] = {
-      0xc000, 0xc001, 0x7000, 0x7001, 0x7010, 0x7011, 0x7020, 0x7021, 0x7030, 0x7031, 0x7040, 0x7041, 0x7050,
-      0x7051, 0x7060, 0x7061, 0x7070, 0x7071, 0x7080, 0x7081, 0x7100, 0x7101, 0x7110, 0x7111, 0x7120, 0x7121,
-      0x7130, 0x7131, 0x7140, 0x7141, 0x7150, 0x7151, 0x7160, 0x7161, 0x7170, 0x7171, 0x7180, 0x7181, 0x7200,
-      0x7201, 0x7210, 0x7211, 0x7220, 0x7221, 0x7230, 0x7231, 0x7240, 0x7241, 0x7250, 0x7251, 0x7260, 0x7261,
-      0x7270, 0x7271, 0x7280, 0x7281, 0x7300, 0x7301, 0x7310, 0x7311, 0x7320, 0x7321, 0x7330, 0x7331, 0x7340,
-      0x7341, 0x7350, 0x7351, 0x7360, 0x7361, 0x7370, 0x7371, 0x7380, 0x7381, 0x7901, 0x7902, 0x7903, 0x7904,
-      0x7905, 0x9991, 0x9992, 0x9993, 0x9994, 0x9995, 0x9996, 0x9997, 0x9998};
+      0xc000, 0xc001, 0x7000, 0x7001, 0x7010, 0x7011, 0x7020, 0x7021, 0x7030, 0x7031, 0x7040, 0x7041, 0x7050, 0x7051,
+      0x7060, 0x7061, 0x7070, 0x7071, 0x7080, 0x7081, 0x7100, 0x7101, 0x7110, 0x7111, 0x7120, 0x7121, 0x7130, 0x7131,
+      0x7140, 0x7141, 0x7150, 0x7151, 0x7160, 0x7161, 0x7170, 0x7171, 0x7180, 0x7181, 0x7200, 0x7201, 0x7210, 0x7211,
+      0x7220, 0x7221, 0x7230, 0x7231, 0x7240, 0x7241, 0x7250, 0x7251, 0x7260, 0x7261, 0x7270, 0x7271, 0x7280, 0x7281,
+      0x7300, 0x7301, 0x7310, 0x7311, 0x7320, 0x7321, 0x7330, 0x7331, 0x7340, 0x7341, 0x7350, 0x7351, 0x7360, 0x7361,
+      0x7370, 0x7371, 0x7380, 0x7381, 0x7901, 0x7902, 0x7903, 0x7904, 0x7905};
     // TRBaddresses 0x7901 and 0x7902 are for FSD/NCAL
-    // TRBaddresses 0x9991 to 0x9998 are for PASTA
 
     double ToTshifts[numElinksPerComp][numChanPerElink] = {
       {0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00,
@@ -316,30 +314,6 @@ namespace cbm::algo::rich
       {0.00,  10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00,
        10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00,
        10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00},
-      {0.00,  10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00,
-       10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00,
-       10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00},
-      {0.00,  10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00,
-       10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00,
-       10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00},
-      {0.00,  10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00,
-       10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00,
-       10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00},
-      {0.00,  10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00,
-       10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00,
-       10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00},
-      {0.00,  10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00,
-       10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00,
-       10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00},
-      {0.00,  10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00,
-       10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00,
-       10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00},
-      {0.00,  10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00,
-       10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00,
-       10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00},
-      {0.00,  10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00,
-       10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00,
-       10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00},
       {0.00,  10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00,
        10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00,
        10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00, 10.00}};
diff --git a/algo/global/ParFiles.cxx b/algo/global/ParFiles.cxx
index 9d4e8ee2cde9139b0cc765ea92b5f97d62025d66..0c37f2781d2b7430472f9e35b8b142a116908499 100644
--- a/algo/global/ParFiles.cxx
+++ b/algo/global/ParFiles.cxx
@@ -119,6 +119,8 @@ ParFiles::ParFiles(uint32_t runId)
       trd.hitfinder   = "mcbm2025_02/TrdHitfinderPar.yaml";
       trd.hitfinder2d = "mcbm2025_02/TrdHitfinder2DPar.yaml";
 
+      must.readout = "mcbm2025_02/MustReadout_mcbm2025_02.yaml";
+
       ca.mainConfig = "mcbm2025_02/TrackingChainConfig.yaml";
 
       kfp.V0FinderConfig = "mcbm2025_02/kfp_lambda_v25a.yaml";
diff --git a/algo/global/ParFiles.h b/algo/global/ParFiles.h
index 0fe7b3094b1e25eaa0d7f3ccb207a6977f5d2836..b95f695782607f47ef3be2b30c48f7039d04cd34 100644
--- a/algo/global/ParFiles.h
+++ b/algo/global/ParFiles.h
@@ -55,6 +55,10 @@ namespace cbm::algo
       fs::path mainConfig;
     } ca;
 
+    struct {
+      fs::path readout;
+    } must;
+
     struct {
       fs::path V0FinderConfig;
     } kfp;
diff --git a/algo/global/Reco.cxx b/algo/global/Reco.cxx
index 0f192df1b6c68934269a1eea7e5561b73dd6aa5d..e5f5a0375ae4ec8855300dbb31801ac5d9732d21 100644
--- a/algo/global/Reco.cxx
+++ b/algo/global/Reco.cxx
@@ -10,6 +10,7 @@
 #include "EventbuildChain.h"
 #include "Exceptions.h"
 #include "HistogramSender.h"
+#include "MustDigiQa.h"
 #include "ParFiles.h"
 #include "RecoGeneralQa.h"
 #include "StsDigiQa.h"
@@ -24,6 +25,7 @@
 #include "evbuild/Config.h"
 #include "kfp/KfpV0FinderChain.h"
 #include "much/Unpack.h"
+#include "must/Unpack.h"
 #include "qa/QaManager.h"
 #include "qa/hitfind/BmonHitfindQa.h"
 #include "rich/Unpack.h"
@@ -140,8 +142,18 @@ void Reco::Init(const Options& opts)
   }
 
   if (Opts().Has(Subsystem::RICH) && Opts().Has(Step::Unpack)) {
-    rich::ReadoutConfig cfg{};
-    fRichUnpack = std::make_unique<rich::Unpack>(cfg);
+    rich::ReadoutConfig richCfg{};
+    fRichUnpack = std::make_unique<rich::Unpack>(richCfg);
+
+    //TODO handle under fles::Subsystem::MUST when available
+    if (parFiles.setup == Setup::mCBM2025_02) {
+      must::ReadoutConfig mustCfg = yaml::ReadFromFile<must::ReadoutConfig>(Opts().ParamsDir() / parFiles.must.readout);
+      fMustUnpack                 = std::make_unique<must::Unpack>(mustCfg);
+      if (fSender != nullptr && Opts().Has(QaStep::UnpackMust)) {
+        fMustDigiQa = std::make_unique<must::DigiQa>(fSender, mustCfg);
+        fMustDigiQa->Init();
+      }
+    }
   }
 
   if (Opts().Has(Subsystem::STS) && Opts().Has(Step::Unpack)) {
@@ -320,6 +332,7 @@ RecoResults Reco::Run(const fles::Timeslice& ts)
       std::tie(digis.fBmon, auxDigis.fBmon)   = RunUnpacker(fBmonUnpack, ts);
       std::tie(digis.fMuch, auxDigis.fMuch)   = RunUnpacker(fMuchUnpack, ts);
       std::tie(digis.fRich, auxDigis.fRich)   = RunUnpacker(fRichUnpack, ts);
+      std::tie(digis.fMust, auxDigis.fMust)   = RunUnpacker(fMustUnpack, ts);
       std::tie(digis.fSts, auxDigis.fSts)     = RunUnpacker(fStsUnpack, ts);
       std::tie(digis.fTof, auxDigis.fTof)     = RunUnpacker(fTofUnpack, ts);
       std::tie(digis.fTrd, auxDigis.fTrd)     = RunUnpacker(fTrdUnpack, ts);
@@ -332,8 +345,13 @@ RecoResults Reco::Run(const fles::Timeslice& ts)
       L_(info) << "TS contains Digis: STS=" << digis.fSts.size() << " MUCH=" << digis.fMuch.size()
                << " TOF=" << digis.fTof.size() << " BMON=" << digis.fBmon.size() << " TRD=" << digis.fTrd.size()
                << " TRD2D=" << digis.fTrd2d.size() << " RICH=" << digis.fRich.size() << " PSD=" << digis.fPsd.size()
-               << " FSD=" << digis.fFsd.size();
+               << " FSD=" << digis.fFsd.size() << " MUST=" << digis.fMust.size();
       // --- Raw digi QAs
+
+      if (fSender != nullptr && Opts().Has(QaStep::UnpackMust)) {
+        fMustDigiQa->Exec(digis.fMust, ts.index());
+      }
+
       if (fSender != nullptr && Opts().Has(Subsystem::STS)) {
         fStsDigiQa->RegisterDigiData(&digis.fSts);
         fStsDigiQa->RegisterAuxDigiData(&auxDigis.fSts);
@@ -444,6 +462,7 @@ RecoResults Reco::Run(const fles::Timeslice& ts)
       results.trdDigis   = std::move(digis.fTrd);
       results.tofDigis   = std::move(digis.fTof);
       results.richDigis  = std::move(digis.fRich);
+      results.mustDigis  = std::move(digis.fMust);
     }
     if (Opts().HasOutput(RecoData::Track)) {
       results.tracks             = std::move(recoData.tracks);
diff --git a/algo/global/Reco.h b/algo/global/Reco.h
index 35cb5c34626c8dc84ba994478e83a305522cfd98..ad2a272d2e5a23194f7daa4f22af9fb7f6b4ce7c 100644
--- a/algo/global/Reco.h
+++ b/algo/global/Reco.h
@@ -46,6 +46,12 @@ namespace cbm::algo
     class Unpack;
   }
 
+  namespace must
+  {
+    class Unpack;
+    class DigiQa;
+  }  // namespace must
+
   namespace sts
   {
     class Unpack;
@@ -166,6 +172,10 @@ namespace cbm::algo
     // RICH
     std::unique_ptr<rich::Unpack> fRichUnpack;
 
+    // MUST
+    std::unique_ptr<must::Unpack> fMustUnpack;
+    std::unique_ptr<must::DigiQa> fMustDigiQa;  ///< Raw MUST-digis QA
+
     // STS
     std::unique_ptr<sts::Unpack> fStsUnpack;
     std::unique_ptr<sts::DigiQa> fStsDigiQa;  ///< Raw STS-digis QA
diff --git a/algo/global/RecoResults.h b/algo/global/RecoResults.h
index 3bcee8be6acd66388ab8e0043313888c0f35c615..4a0cf6574dccdc2df468de8ffb3b6ee6ccac0c0c 100644
--- a/algo/global/RecoResults.h
+++ b/algo/global/RecoResults.h
@@ -36,6 +36,7 @@ namespace cbm::algo
     PODVector<CbmTrdDigi> trdDigis;
     PODVector<CbmTofDigi> tofDigis;
     PODVector<CbmRichDigi> richDigis;
+    PODVector<CbmMustDigi> mustDigis;
 
     std::vector<DigiEvent> events;
 
diff --git a/algo/global/StorableRecoResults.h b/algo/global/StorableRecoResults.h
index 5560b7d1ad1998bfacc2b35e0f64335acc571820..31e08876a961e4663b585e918a960b63374f4f49 100644
--- a/algo/global/StorableRecoResults.h
+++ b/algo/global/StorableRecoResults.h
@@ -70,6 +70,9 @@ namespace cbm::algo
     std::vector<CbmRichDigi>& RichDigis() { return fRichDigis; }
     const std::vector<CbmRichDigi>& RichDigis() const { return fRichDigis; }
 
+    std::vector<CbmMustDigi>& MustDigis() { return fMustDigis; }
+    const std::vector<CbmMustDigi>& MustDigis() const { return fMustDigis; }
+
     std::vector<CbmDigiEvent>& DigiEvents() { return fDigiEvents; }
     const std::vector<CbmDigiEvent>& DigiEvents() const { return fDigiEvents; }
 
@@ -106,6 +109,7 @@ namespace cbm::algo
     std::vector<CbmTrdDigi> fTrdDigis;
     std::vector<CbmTofDigi> fTofDigis;
     std::vector<CbmRichDigi> fRichDigis;
+    std::vector<CbmMustDigi> fMustDigis;
 
     // Event builder/filter output
     std::vector<CbmDigiEvent> fDigiEvents;
@@ -142,6 +146,7 @@ namespace cbm::algo
       ar& fTrdDigis;
       ar& fTofDigis;
       ar& fRichDigis;
+      ar& fMustDigis;
 
       ar& fDigiEvents;
 
diff --git a/algo/qa/unpack/MustDigiQa.cxx b/algo/qa/unpack/MustDigiQa.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..3d9530c63604c1036d76a18db9830251beb13ccd
--- /dev/null
+++ b/algo/qa/unpack/MustDigiQa.cxx
@@ -0,0 +1,93 @@
+/* Copyright (C) 2025 Jagiellonian University, Krakow
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Bartosz Sobol [committer] */
+
+#include "MustDigiQa.h"
+
+#include <fmt/format.h>
+
+namespace cbm::algo::must
+{
+  DigiQa::DigiQa(std::shared_ptr<HistogramSender> pSender, const ReadoutConfig& readoutConfig)
+    : fQaData{"RawDigi/MUST"}
+    , fpSender{pSender}
+    , fReadoutConfig(readoutConfig)
+  {
+  }
+
+  void DigiQa::Init()
+  {
+    if (fpSender == nullptr) {
+      return;
+    }
+
+    const auto& TRBIds = fReadoutConfig.GetTrbAddresses();
+
+    for (auto iTRB = 0; iTRB < TRBIds.size(); ++iTRB) {
+      const auto TRBId = TRBIds.at(iTRB);
+
+      auto canvas = qa::CanvasConfig(fmt::format("must_digi/must_digi_vs_channel_tot_{:x}", TRBId),
+                                     fmt::format("MUST digis per channel and ToT for TRB 0x{:x}", TRBId), 2, 1);
+
+      fChannelHists.emplace_back(fQaData.MakeObj<qa::H1D>(
+        fmt::format("must_digi_{:x}_channel", TRBId),
+        fmt::format("Digis per channel for TRB 0x{:x};channel;N_{{digis}}", TRBId), 33, 0, 33));
+      auto padChannels = qa::PadConfig();
+      padChannels.RegisterHistogram(fChannelHists[iTRB], "hist");
+      canvas.AddPadConfig(padChannels);
+
+      fTotHists.emplace_back(fQaData.MakeObj<qa::H1D>(fmt::format("must_digi_{:x}_tot", TRBId),
+                                                      fmt::format("ToT for TRB 0x{:x};ToT;N_{{digis}}", TRBId), 30, 10,
+                                                      40));
+      auto padTot = qa::PadConfig();
+      padTot.RegisterHistogram(fTotHists[iTRB], "hist");
+      canvas.AddPadConfig(padTot);
+
+      fQaData.AddCanvasConfig(canvas);
+    }
+
+    auto canvas    = qa::CanvasConfig("must_digi/must_digi_vs_trb_channel", "MUST digis per channel in TRBs", 1, 1);
+    fChannel2DHist = fQaData.MakeObj<qa::H2D>("must_digi_trb_channel", "Digis per channel in TRBs", TRBIds.size(), 0,
+                                              *std::max_element(TRBIds.cbegin(), TRBIds.cend())
+                                                - *std::min_element(TRBIds.cbegin(), TRBIds.cend()) + 1,
+                                              33, 0, 33);
+
+    auto padChannels2D = qa::PadConfig();
+    padChannels2D.RegisterHistogram(fChannel2DHist, "hist");
+    canvas.AddPadConfig(padChannels2D);
+
+    fQaData.AddCanvasConfig(canvas);
+
+    fQaData.Init(fpSender);
+  }
+
+  void DigiQa::Exec(const PODVector<CbmMustDigi>& digis, const uint64_t tsIndex)
+  {
+    if (fpSender == nullptr) {
+      return;
+    }
+
+    const auto minTRB =
+      *std::min_element(fReadoutConfig.GetTrbAddresses().cbegin(), fReadoutConfig.GetTrbAddresses().cend());
+
+    fQaData.SetTimesliceId(tsIndex);
+
+    for (const auto& digi : digis) {
+      const auto iHist = digi.fTRBId - fReadoutConfig.GetTrbAddresses()[0];
+
+      if (iHist >= fChannelHists.size()) {
+        L_(error) << fmt::format("must::DigiQa: digi TRBId {:x} out of range", digi.fTRBId);
+        continue;
+      }
+
+      fChannelHists[iHist]->Fill(digi.fChannel);
+      fTotHists[iHist]->Fill(digi.fToT);
+      fChannel2DHist->Fill(digi.fTRBId - minTRB, digi.fChannel);
+    }
+
+    fQaData.Send(fpSender);
+  }
+
+  void DigiQa::operator()(const PODVector<CbmMustDigi>& digis, const uint64_t tsIndex) { Exec(digis, tsIndex); }
+
+}  // namespace cbm::algo::must
diff --git a/algo/qa/unpack/MustDigiQa.h b/algo/qa/unpack/MustDigiQa.h
new file mode 100644
index 0000000000000000000000000000000000000000..a14fd667b9dcfe60539ab62f0fe07f0d5b106b8e
--- /dev/null
+++ b/algo/qa/unpack/MustDigiQa.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 2025 Jagiellonian University, Krakow
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Bartosz Sobol [committer] */
+
+#pragma once
+
+#include "CbmMustDigi.h"
+#include "QaBase.h"
+
+#include <CommonUnpacker.h>
+#include <must/ReadoutConfig.h>
+
+namespace cbm::algo::must
+{
+  /* TODO probably cbm::algo::sts::QaBase can be used here as a base class, but for some reason it is in sts namespace.
+   * IMO it should be only in cbm::algo and be a base for all QA classes?
+   * For now I'm doing similar impl/interface, but without inheritance.
+  */
+  /// \class cbm::algo::must::DigiQa
+  /// \brief QA module for MUST raw digis
+  class DigiQa {
+   public:
+    /// \brief Main constructor with HistogramSender
+    DigiQa(std::shared_ptr<HistogramSender> pSender, const ReadoutConfig& readoutConfig);
+
+    DigiQa() = delete;
+
+    DigiQa(const DigiQa&) = delete;
+
+    DigiQa(DigiQa&&) = delete;
+
+    DigiQa& operator=(const DigiQa&) = delete;
+
+    DigiQa& operator=(DigiQa&&) = delete;
+
+    /// \brief Initializes QA (initialization of histograms and canvases)
+    void Init();
+
+    /// \brief Executes QA (filling histograms)
+    void Exec(const PODVector<CbmMustDigi>& digis, uint64_t tsIndex);
+
+    /// \brief Executes QA (filling histograms), see Exec()
+    void operator()(const PODVector<CbmMustDigi>& digis, uint64_t tsIndex);
+
+   private:
+    std::vector<qa::H1D*> fChannelHists{};  ///< histograms: nDigis per channel per TRB
+    std::vector<qa::H1D*> fTotHists{};      ///< histograms: ToT per TRB
+    qa::H2D* fChannel2DHist{};              ///< histogram: nDigis per channel per TRB
+
+    qa::Data fQaData;
+    std::shared_ptr<HistogramSender> fpSender;
+    ReadoutConfig fReadoutConfig;
+  };
+
+}  // namespace cbm::algo::must
diff --git a/core/data/CMakeLists.txt b/core/data/CMakeLists.txt
index b5b59dd07855d9e7f8fb17bbd151c570757e7563..4c5f8f974da3c5d763f63ba76a381314b0c2de19 100644
--- a/core/data/CMakeLists.txt
+++ b/core/data/CMakeLists.txt
@@ -15,6 +15,7 @@ set(INCLUDE_DIRECTORIES
   ${CMAKE_CURRENT_SOURCE_DIR}/rich
   ${CMAKE_CURRENT_SOURCE_DIR}/psd
   ${CMAKE_CURRENT_SOURCE_DIR}/fsd
+  ${CMAKE_CURRENT_SOURCE_DIR}/must
   ${CMAKE_CURRENT_SOURCE_DIR}/global
   )
 
@@ -66,6 +67,8 @@ set(SRCS
   rich/CbmRichTrbDigi.cxx
   rich/CbmRichDigi.cxx
 
+  must/CbmMustDigi.cxx
+
   much/CbmMuchPixelHit.cxx
   much/CbmMuchPoint.cxx
   much/CbmMuchCluster.cxx
@@ -134,8 +137,8 @@ SET_SOURCE_FILES_PROPERTIES(tof/etof/star_rhicf.c PROPERTIES COMPILE_FLAGS -Wno-
 
 
 list(APPEND HEADERS base/CbmDigiData.h global/CbmDigiEvent.h global/CbmDigiTimeslice.h
-bmon/CbmBmonDigiData.h sts/CbmStsDigiData.h much/CbmMuchDigiData.h rich/CbmRichDigiData.h trd/CbmTrdDigiData.h
-tof/CbmTofDigiData.h psd/CbmPsdDigiData.h fsd/CbmFsdDigiData.h trd/CbmTrdFexMessageSpadic.h)
+bmon/CbmBmonDigiData.h sts/CbmStsDigiData.h much/CbmMuchDigiData.h rich/CbmRichDigiData.h must/CbmMustDigiData.h
+trd/CbmTrdDigiData.h tof/CbmTofDigiData.h psd/CbmPsdDigiData.h fsd/CbmFsdDigiData.h trd/CbmTrdFexMessageSpadic.h)
 
 set(LIBRARY_NAME CbmData)
 set(LINKDEF ${LIBRARY_NAME}LinkDef.h)
@@ -166,6 +169,7 @@ Install(FILES
         psd/CbmPsdDigiData.h
         fsd/CbmFsdDigiData.h
         rich/CbmRichDigiData.h rich/CbmRichRingLight.h
+        must/CbmMustDigiData.h
         sts/CbmStsDigiData.h
         trd/CbmTrdDigiData.h
         tof/CbmTofDigiData.h
diff --git a/core/data/CbmDefs.h b/core/data/CbmDefs.h
index 3b89430788a80b85447bd31f14ab5f24206a1501..59b25b519145bb7fa024710e3bbdfa923421309c 100644
--- a/core/data/CbmDefs.h
+++ b/core/data/CbmDefs.h
@@ -60,7 +60,8 @@ enum class ECbmModuleId
   kPlatform   = 21,  ///< RICH rail platform
   kCave       = 22,  ///< Cave
   kLastModule = 23,  ///< For loops over all modules
-  kNotExist   = -1   ///< If not found
+  kNotExist   = -1,  ///< If not found
+  kMust       = 100  ///< PANDA Straws //TODO
 };
 
 // operator ++ for ECbmModuleId for convenient usage in loops
diff --git a/core/data/base/CbmDigiData.h b/core/data/base/CbmDigiData.h
index c864e8ba6fcef44cbdfc3a1f5f4a5886e4d2428f..efae0b8cb9a4a1f5a223beddd237bbc99d201739 100644
--- a/core/data/base/CbmDigiData.h
+++ b/core/data/base/CbmDigiData.h
@@ -9,6 +9,7 @@
 #include "CbmBmonDigiData.h"
 #include "CbmFsdDigiData.h"
 #include "CbmMuchDigiData.h"
+#include "CbmMustDigiData.h"
 #include "CbmPsdDigiData.h"
 #include "CbmRichDigiData.h"
 #include "CbmStsDigiData.h"
@@ -41,6 +42,7 @@ class CbmDigiData {
   CbmTofDigiData fTof;    ///< TOF data
   CbmPsdDigiData fPsd;    ///< PSD data
   CbmFsdDigiData fFsd;    ///< FSD data
+  CbmMustDigiData fMust;  ///< MUST data
 
   friend class boost::serialization::access;
   /** @brief BOOST serializer**/
@@ -57,6 +59,7 @@ class CbmDigiData {
     ar& fPsd;
     ar& fFsd;
     ar& fRich;
+    ar& fMust;
   }
 
   // --- ROOT serializer
@@ -76,6 +79,7 @@ class CbmDigiData {
     fPsd.Clear();
     fFsd.Clear();
     fRich.Clear();
+    fMust.Clear();
   }
 
   /** @brief Size of detector data **/
@@ -91,6 +95,7 @@ class CbmDigiData {
       case ECbmModuleId::kPsd: return fPsd.Size(); break;
       case ECbmModuleId::kRich: return fRich.Size(); break;
       case ECbmModuleId::kFsd: return fFsd.Size(); break;
+      case ECbmModuleId::kMust: return fMust.Size(); break;
       default: return 0; break;
     }
   }
@@ -108,6 +113,7 @@ class CbmDigiData {
     size += fPsd.Size() * sizeof(CbmPsdDigi);
     size += fFsd.Size() * sizeof(CbmFsdDigi);
     size += fRich.Size() * sizeof(CbmRichDigi);
+    size += fMust.Size() * sizeof(CbmMustDigi);
     return size;
   }
 };
diff --git a/core/data/must/CbmMustDigi.cxx b/core/data/must/CbmMustDigi.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..2f8332a4133af53079974767f8922f242bb598df
--- /dev/null
+++ b/core/data/must/CbmMustDigi.cxx
@@ -0,0 +1,5 @@
+/* Copyright (C) 2025 Jagiellonian University, Krakow
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Bartosz Sobol [committer] */
+
+#include "CbmMustDigi.h"
diff --git a/core/data/must/CbmMustDigi.h b/core/data/must/CbmMustDigi.h
new file mode 100644
index 0000000000000000000000000000000000000000..b25c335ad2f5ab67d36c0b62922087f41c29f93f
--- /dev/null
+++ b/core/data/must/CbmMustDigi.h
@@ -0,0 +1,40 @@
+/* Copyright (C) 2025 Jagiellonian University, Krakow
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Bartosz Sobol [committer] */
+
+#ifndef CBMMUSTDIGI_H
+#define CBMMUSTDIGI_H
+#include <cstdint>
+
+
+class CbmMustDigi {
+ public:
+  CbmMustDigi() = default;
+
+  CbmMustDigi(const uint32_t trbId, const uint32_t channel, const double time, const double tot)
+    : fTRBId{trbId}
+    , fChannel{channel}
+    , fTime{time}
+    , fToT{tot}
+  {
+  }
+
+  template<class Archive>
+  void serialize(Archive& ar, const unsigned int /*version*/)
+  {
+    ar& fTRBId;
+    ar& fChannel;
+    ar& fTime;
+    ar& fToT;
+  }
+
+  double GetTime() const { return fTime; }
+
+  uint32_t fTRBId;
+  uint32_t fChannel;
+  double fTime;
+  double fToT;
+};
+
+
+#endif  //CBMMUSTDIGI_H
diff --git a/core/data/must/CbmMustDigiData.h b/core/data/must/CbmMustDigiData.h
new file mode 100644
index 0000000000000000000000000000000000000000..39b8f8267add05183af914437d43ff5e02856747
--- /dev/null
+++ b/core/data/must/CbmMustDigiData.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2025 Jagiellonian University, Krakow
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Bartosz Sobol [committer] */
+
+#ifndef CBMMUSTDIGIDATA_H
+#define CBMMUSTDIGIDATA_H
+
+#include "CbmMustDigi.h"
+
+#ifndef NO_ROOT
+#include <Rtypes.h>  // for ClassDef
+#endif
+
+#include <boost/serialization/vector.hpp>
+
+class CbmMustDigiData {
+ public:
+  std::vector<CbmMustDigi> fDigis{};
+
+  template<class Archive>
+  void serialize(Archive& ar, const unsigned int /*version*/)
+  {
+    ar& fDigis;
+  }
+
+  void Clear() { fDigis.clear(); }
+
+  size_t Size() const { return fDigis.size(); }
+
+#ifndef NO_ROOT
+  ClassDefNV(CbmMustDigiData, 1);
+#endif
+};
+
+#endif  //CBMMUSTDIGIDATA_H
diff --git a/reco/app/cbmreco/main.cxx b/reco/app/cbmreco/main.cxx
index 6dd23efc4481d3854e2f76af8d715f66528521a4..7c24c76ee7a9de28a64be731a8fe0482b051e5a2 100644
--- a/reco/app/cbmreco/main.cxx
+++ b/reco/app/cbmreco/main.cxx
@@ -43,6 +43,7 @@ std::shared_ptr<StorableRecoResults> makeStorableRecoResults(const fles::Timesli
   storable->TrdDigis()   = ToStdVector(results.trdDigis);
   storable->TofDigis()   = ToStdVector(results.tofDigis);
   storable->RichDigis()  = ToStdVector(results.richDigis);
+  storable->MustDigis()  = ToStdVector(results.mustDigis);
 
   storable->StsClusters() = results.stsClusters;
   storable->StsHits()     = results.stsHits;
diff --git a/reco/detectors/rich/mcbm/CbmRichMCbmHitProducer.cxx b/reco/detectors/rich/mcbm/CbmRichMCbmHitProducer.cxx
index 5f3cd3ad47cb84757b62384dc11a30e4dc6926d0..f445694be85cd1439bba9d87f61b7f977091eff5 100644
--- a/reco/detectors/rich/mcbm/CbmRichMCbmHitProducer.cxx
+++ b/reco/detectors/rich/mcbm/CbmRichMCbmHitProducer.cxx
@@ -204,7 +204,7 @@ void CbmRichMCbmHitProducer::ProcessDigi(CbmEvent* event, Int_t digiIndex)
   Int_t DiRICH_Add = (digi->GetAddress() >> 16) & 0xFFFF;
   if ((0x7901 <= DiRICH_Add && DiRICH_Add <= 0x7905) || (0x9991 <= DiRICH_Add && DiRICH_Add <= 0x9998)) {
     // TRBaddresses 0x7901 to 0x7905 are for FSD/NCAL
-    // TRBaddresses 0x9991 to 0x9998 are for PASTA
+    // TRBaddresses 0x9991 to 0x9998 are for MUST
     return;
   }
   fNofDigis++;