From 3159c8bb7a9a38510b3637ceceea9338b6f2b150 Mon Sep 17 00:00:00 2001
From: Ajit Kumar <a.kumar@physik.uni-frankfurt.de>
Date: Fri, 7 Mar 2025 18:31:11 +0100
Subject: [PATCH] initial mMVD unpacker

---
 algo/CMakeLists.txt                  |   4 +
 algo/base/AuxDigiData.h              |   2 +
 algo/base/DigiData.cxx               |  12 +-
 algo/base/DigiData.h                 |   2 +
 algo/data/CMakeLists.txt             |   5 +
 algo/detectors/mvd/ReadoutConfig.cxx | 222 +++++++++++++++++++++++++++
 algo/detectors/mvd/ReadoutConfig.h   |  76 +++++++++
 algo/detectors/mvd/Unpack.cxx        |  36 +++++
 algo/detectors/mvd/Unpack.h          |  32 ++++
 algo/detectors/mvd/UnpackMS.cxx      | 168 ++++++++++++++++++++
 algo/detectors/mvd/UnpackMS.h        | 149 ++++++++++++++++++
 algo/global/ParFiles.cxx             |   4 +
 algo/global/ParFiles.h               |   6 +
 algo/global/Reco.cxx                 |  30 +++-
 algo/global/Reco.h                   |   8 +
 algo/global/RecoResults.h            |   1 +
 algo/global/StorableRecoResults.h    |   5 +
 core/data/CMakeLists.txt             |   3 +-
 core/data/base/CbmDigiData.h         |   6 +
 core/data/mvd/CbmMvdDigiData.h       |  55 +++++++
 reco/app/cbmreco/main.cxx            |   1 +
 21 files changed, 822 insertions(+), 5 deletions(-)
 create mode 100644 algo/detectors/mvd/ReadoutConfig.cxx
 create mode 100644 algo/detectors/mvd/ReadoutConfig.h
 create mode 100644 algo/detectors/mvd/Unpack.cxx
 create mode 100644 algo/detectors/mvd/Unpack.h
 create mode 100644 algo/detectors/mvd/UnpackMS.cxx
 create mode 100644 algo/detectors/mvd/UnpackMS.h
 create mode 100644 core/data/mvd/CbmMvdDigiData.h

diff --git a/algo/CMakeLists.txt b/algo/CMakeLists.txt
index 5eff2e560..a04c55b1f 100644
--- a/algo/CMakeLists.txt
+++ b/algo/CMakeLists.txt
@@ -114,6 +114,9 @@ set(SRCS
   detectors/much/ReadoutConfig.cxx
   detectors/much/Unpack.cxx
   detectors/much/UnpackMS.cxx
+  detectors/mvd/ReadoutConfig.cxx
+  detectors/mvd/Unpack.cxx
+  detectors/mvd/UnpackMS.cxx
   detectors/tof/HitFinder.cxx
   detectors/tof/Calibrate.cxx
   detectors/tof/Clusterizer.cxx
@@ -346,6 +349,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/mvd 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 e464da7e2..25b221e8a 100644
--- a/algo/base/AuxDigiData.h
+++ b/algo/base/AuxDigiData.h
@@ -17,6 +17,7 @@
 #include "tof/UnpackMS.h"
 #include "trd/UnpackMS.h"
 #include "trd2d/UnpackMS.h"
+#include "mvd/UnpackMS.h"
 
 #include <vector>
 
@@ -32,5 +33,6 @@ namespace cbm::algo
     UnpackAux<tof::UnpackAuxData> fTof;
     UnpackAux<trd::UnpackAuxData> fTrd;
     UnpackAux<trd2d::UnpackAuxData> fTrd2d;
+    UnpackAux<mvd::UnpackAuxData> fMvd;
   };
 }  // namespace cbm::algo
diff --git a/algo/base/DigiData.cxx b/algo/base/DigiData.cxx
index 79fd5a483..fab5281a5 100644
--- a/algo/base/DigiData.cxx
+++ b/algo/base/DigiData.cxx
@@ -14,6 +14,7 @@ DigiData::~DigiData() {}
 DigiData::DigiData(const CbmDigiData& storable)
   : fSts(ToPODVector(storable.fSts.fDigis))
   , fMuch(ToPODVector(storable.fMuch.fDigis))
+  , fMvd(ToPODVector(storable.fMvd.fDigis))
   , fTof(ToPODVector(storable.fTof.fDigis))
   , fBmon(ToPODVector(storable.fBmon.fDigis))
   , fTrd(ToPODVector(storable.fTrd.fDigis))
@@ -29,6 +30,7 @@ size_t DigiData::Size(ECbmModuleId system) const
   switch (system) {
     case ECbmModuleId::kSts: return fSts.size();
     case ECbmModuleId::kMuch: return fMuch.size();
+    case ECbmModuleId::kMvd: return fMvd.size();
     case ECbmModuleId::kTof: return fTof.size();
     case ECbmModuleId::kBmon: return fBmon.size();
     case ECbmModuleId::kTrd: return fTrd.size();
@@ -42,13 +44,13 @@ 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()
+  return fSts.size() + fMuch.size() + fMvd.size() + fTof.size() + fBmon.size() + fTrd.size() + fTrd2d.size() + fRich.size()
          + fPsd.size() + fFsd.size();
 }
 
 size_t DigiData::TotalSizeBytes() const
 {
-  return sizeof(CbmStsDigi) * fSts.size() + sizeof(CbmMuchDigi) * fMuch.size() + sizeof(CbmTofDigi) * fTof.size()
+  return sizeof(CbmStsDigi) * fSts.size() + sizeof(CbmMuchDigi) * fMuch.size() + sizeof(CbmMvdDigi) * fMvd.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();
 }
@@ -64,10 +66,14 @@ CbmDigiData DigiData::ToStorable() const
       {
         .fDigis = ToStdVector(fSts),
       },
-    .fMuch =
+      .fMuch =
       {
         .fDigis = ToStdVector(fMuch),
       },
+      .fMvd =
+      {
+        .fDigis = ToStdVector(fMvd),
+      },
     .fRich =
       {
         .fDigis = ToStdVector(fRich),
diff --git a/algo/base/DigiData.h b/algo/base/DigiData.h
index 09b82d933..1b4376a6a 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 "CbmMvdDigi.h"
 #include "CbmPsdDigi.h"
 #include "CbmRichDigi.h"
 #include "CbmStsDigi.h"
@@ -32,6 +33,7 @@ namespace cbm::algo
   struct DigiData {
     PODVector<CbmStsDigi> fSts;    ///< Unpacked STS digis
     PODVector<CbmMuchDigi> fMuch;  ///< Unpacked MUCH digis
+    PODVector<CbmMvdDigi> fMvd;  ///< Unpacked MVD digis
     PODVector<CbmTofDigi> fTof;    ///< Unpacked TOF digis
     PODVector<CbmBmonDigi> fBmon;  ///< Unpacked Bmon digis
     PODVector<CbmTrdDigi> fTrd;    ///< Unpacked TRD digis
diff --git a/algo/data/CMakeLists.txt b/algo/data/CMakeLists.txt
index 717ba5389..c1e3e6339 100644
--- a/algo/data/CMakeLists.txt
+++ b/algo/data/CMakeLists.txt
@@ -18,6 +18,9 @@ set(SRCS
   ${OFFLINE_DATA_DIR}/much/CbmMuchDigi.cxx
   ${OFFLINE_DATA_DIR}/much/CbmMuchAddress.cxx
 
+  ${OFFLINE_DATA_DIR}/mvd/CbmMvdDigi.cxx
+  # ${OFFLINE_DATA_DIR}/mvd/CbmMvdAddress.cxx
+
   ${OFFLINE_DATA_DIR}/trd/CbmTrdDigi.cxx
   ${OFFLINE_DATA_DIR}/trd/CbmTrdRawMessageSpadic.cxx
 
@@ -35,6 +38,7 @@ set(SRCS
 
   ${OFFLINE_DATA_DIR}/raw/CriGet4Mess001.cxx
   ${OFFLINE_DATA_DIR}/raw/StsXyterMessage.cxx
+  # ${OFFLINE_DATA_DIR}/raw/MimosisMessage.cxx // implement this for mimosis
 )
 
 add_library(OnlineData SHARED ${SRCS})
@@ -46,6 +50,7 @@ target_include_directories(OnlineData
   PUBLIC ${OFFLINE_DATA_DIR}/sts
   PUBLIC ${OFFLINE_DATA_DIR}/rich
   PUBLIC ${OFFLINE_DATA_DIR}/much
+  PUBLIC ${OFFLINE_DATA_DIR}/mvd
   PUBLIC ${OFFLINE_DATA_DIR}/trd
   PUBLIC ${OFFLINE_DATA_DIR}/tof
   PUBLIC ${OFFLINE_DATA_DIR}/psd
diff --git a/algo/detectors/mvd/ReadoutConfig.cxx b/algo/detectors/mvd/ReadoutConfig.cxx
new file mode 100644
index 000000000..ca4555cae
--- /dev/null
+++ b/algo/detectors/mvd/ReadoutConfig.cxx
@@ -0,0 +1,222 @@
+/* Copyright (C) 2017-2021 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pierre-Alain Loizeau, Ajit Kumar, Florian Uhlig [committer] */
+
+#include "ReadoutConfig.h"
+
+#include "AlgoFairloggerCompat.h"
+// #include "CbmMvdAddress.h" // not needed for the time being
+
+#include <bitset>
+#include <unordered_set>
+
+using namespace std;
+
+namespace cbm::algo::mvd
+{
+  // ---  Constructor  ------------------------------------------------------------------
+  ReadoutConfig::ReadoutConfig() { Init(); }
+  // ------------------------------------------------------------------------------------
+
+  // ---   Destructor   -----------------------------------------------------------------
+  ReadoutConfig::~ReadoutConfig() {}
+  // ------------------------------------------------------------------------------------
+
+  // ---   Equipment IDs   --------------------------------------------------------------
+  std::vector<uint16_t> ReadoutConfig::GetEquipmentIds()
+  {
+    std::vector<uint16_t> result;
+    for (auto& entry : fReadoutMap)
+      result.push_back(entry.first);
+    return result;
+  }
+  // ------------------------------------------------------------------------------------
+
+  // ---   Number of elinks for a component / equipment   -------------------------------
+  size_t ReadoutConfig::GetNumElinks(uint16_t equipmentId)
+  {
+    size_t result = 0;
+    auto it       = fReadoutMap.find(equipmentId);
+    if (it != fReadoutMap.end()) result = fReadoutMap[equipmentId].size();
+    return result;
+  }
+  // ------------------------------------------------------------------------------------
+
+  // ---  Mapping (equimentId, elink) -> address[channel]  ------------------------------
+  std::vector<uint32_t> ReadoutConfig::Map(uint16_t equipmentId, uint16_t elinkId)
+  {
+    std::vector<uint32_t> result;
+    auto equipIter = fReadoutMap.find(equipmentId);
+    if (equipIter != fReadoutMap.end()) {
+      if (elinkId < equipIter->second.size()) {
+        result = equipIter->second.at(elinkId);
+      }
+    }
+    return result;
+  }
+  // ------------------------------------------------------------------------------------
+
+  // ---  Mapping (equimentId, elink) -> (vector of mask flags)  ------------------------
+  std::vector<bool> ReadoutConfig::MaskMap(uint16_t equipmentId, uint16_t elinkId)
+  {
+    std::vector<bool> result;
+    auto equipIter = fMaskMap.find(equipmentId);
+    if (equipIter != fMaskMap.end()) {
+      auto elinkMap  = equipIter->second;
+      auto elinkIter = elinkMap.find(elinkId);
+      if (elinkIter != elinkMap.end()) {
+        result = elinkIter->second;
+      }
+    }
+    return result;
+  }
+  // ------------------------------------------------------------------------------------
+
+
+  void ReadoutConfig::Init()
+  {
+    // This here refers to the mCBM 2022 setup.
+    // Taken from CbmMvdUnpackPar in combination with macro/beamtime/mcbm2022/mMuchParUpto26052022.par
+
+    // Array to hold the unique IDs (equipment ID) for all MUCH DPBs
+    uint16_t eqId[numComp] = {0x2001, 0x2002};
+
+    // Mapping of eLink to FEB number within CROB. If -1, elink not used.
+    // This mapping is the same for each component.
+    const int16_t elink2Sensor[numElinksPerCrob] = {0};
+
+    // Constructing the map (equipmentId, eLink, channel) -> (MUCH address)
+    uint16_t numElinksPerComp = numCrobPerComp * numElinksPerCrob;
+
+    for (uint16_t comp = 0; comp < numComp; comp++) {
+      uint16_t equipment = eqId[comp];
+      fReadoutMap[equipment].resize(numElinksPerComp);
+      for (uint16_t crob = 0; crob < numCrobPerComp; crob++) {
+        for (uint16_t elink = 0; elink < numElinksPerCrob; elink++) {
+
+          // fReadoutMap[equipment][elink].resize(numChanPerAsic);
+          // uint16_t elinkId   = numElinksPerCrob * crob + elink;    // elink within component
+
+          // Skip unconnected FEB slots
+        }  //# elink
+      }    //# CROB
+    }      //# component
+  }
+
+  //To do address need to be checked carefully
+  // uint32_t ReadoutConfig::CreateMuchAddress(uint32_t dpbidx, int32_t iFebId, uint32_t usChan)
+  // {
+  //   // For generating Station number (GEM1 station = 0, GEM2 station = 1 and RPC station = 2)
+  //   /// Below FebID is according to FEB Position in Module GEM A or Module GEM B (Carefully write MUCH Par file)
+  //   int32_t station = -1;
+  //   int32_t layer   = -1;
+  //   if (iFebId == -7)  //Pulser FEB
+  //   {
+  //     station = 6;  // for Pulser
+  //     layer   = 0;  //
+  //   }
+  //   else if (dpbidx == 0 || dpbidx == 1 || dpbidx == 2)  //First 3 DPBs are for GEM-1
+  //   {
+  //     station = 0;  // for mCBM setup
+  //     layer   = 0;  // Station 0 for GEM-A and station 1 for Module GEM-B
+  //   }
+  //   else if (dpbidx == 4 || dpbidx == 5 || dpbidx == 6)  //Last 3 DPBs are for GEM-2 after 10/04/2022
+  //   {
+  //     station = 1;  // for mCBM setup  station
+  //     layer   = 0;  // 0 for Module GEM-A and 1 for Module GEM-B
+  //   }
+  //   else if (dpbidx == 3) {
+  //     station = 2;  // for mCBM setup only one station
+  //     layer   = 0;  //
+  //   }
+  //   else {
+  //     LOG(warning) << "Wrong DPB Id x " << dpbidx;
+  //     return 0;
+  //   }  //No address generated.
+
+  //   //Common layer side module will be 0 only for mCBM 2022
+  //   int32_t layerside = 0;  // 0 in mCBM
+  //   int32_t sModule   = 0;  // 0 in mCBM
+
+  //   int32_t sSector  = -9;  //channel values are from 0-96 therefore as per CbmMuchAddress it is sector
+  //   int32_t sChannel = -9;  //sector values are from 0-22 therefore as per CbmMuchAddress it is channel
+
+  //   // Channel flip in stsXYTER v2.1 : 0<->1, 2<->3, 3<->4 and so on...
+  //   auto fiFlag = 1;  //! Switch to smx2.0/smx2.1 data-> fiFlag = 0 for 2.0 and fiFlag = 1 for 2.1
+  //   if (fiFlag == 1) {
+  //     if (usChan % 2 == 0)
+  //       usChan = usChan + 1;
+  //     else
+  //       usChan = usChan - 1;
+  //   }
+
+  //   // PadX means CHANNEL for CbmMuchAddress
+  //   // PadY means SECTOR for CbmMuchAddress
+  //   // Due to two FLEX cable connected to single FEB; First Flex Connector number
+  //   // 1 - 63 and second flex connector number 64 - 127
+  //   //in few FEB positioned these flex connectors are flipped so below correction applied.
+  //   if (station == 0 && fiFlag == 1 && layer == 0) {  // First layer (GEM1) has old readout PCB
+  //     if (iFebId == 0 || iFebId == 1 || iFebId == 2 || iFebId == 3 || iFebId == 4 || iFebId == 8 || iFebId == 9
+  //         || iFebId == 10 || iFebId == 11 || iFebId == 17) {
+  //       sChannel = GetPadXA(iFebId, 127 - usChan);
+  //       sSector  = GetPadYA(iFebId, 127 - usChan);
+  //     }
+  //     else {
+  //       sChannel = GetPadXA(iFebId, usChan);
+  //       sSector  = GetPadYA(iFebId, usChan);
+  //     }
+  //   }
+  //   else if (station == 1 && fiFlag == 1 && layer == 0) {  // second layer (GEM2) has new readout PCB
+  //     if (iFebId == 0 || iFebId == 1 || iFebId == 2 || iFebId == 3 || iFebId == 4 || iFebId == 8 || iFebId == 9
+  //         || iFebId == 10 || iFebId == 11 || iFebId == 17) {
+  //       sChannel = GetPadXB(iFebId, 127 - usChan);
+  //       sSector  = GetPadYB(iFebId, 127 - usChan);
+  //     }
+  //     else {
+  //       sChannel = GetPadXB(iFebId, usChan);
+  //       sSector  = GetPadYB(iFebId, usChan);
+  //     }
+  //   }
+  //   else if (station == 0 || station == 1) {  // Both layer with same type of PCB (Probably below is not necessary)
+  //     if (iFebId == 0 || iFebId == 1 || iFebId == 2 || iFebId == 3 || iFebId == 4 || iFebId == 8 || iFebId == 9
+  //         || iFebId == 10 || iFebId == 11 || iFebId == 17) {
+  //       sChannel = GetPadXA(iFebId, 127 - usChan);
+  //       sSector  = GetPadYA(iFebId, 127 - usChan);
+  //     }
+  //     else {
+  //       sChannel = GetPadXA(iFebId, usChan);
+  //       sSector  = GetPadYA(iFebId, usChan);
+  //     }
+  //   }
+  //   else if (station == 2) {  //For RPC
+  //     sChannel = GetPadXRpc(iFebId, usChan);
+  //     sSector  = GetPadYRpc(iFebId, usChan);
+  //   }
+  //   else  // Checking for the not connected or misconnected pads
+  //   {
+  //     LOG(debug) << "Sector " << sSector << " channel " << sChannel << " is not connected or misconnected to pad. "
+  //                << " corresponding Feb is " << iFebId << " and uschan " << usChan << " DPB id " << dpbidx
+  //                << " Skipping this hit message.";
+  //     return 0;
+  //   }
+
+  //   //Creating Unique address of the particular channel of GEM
+  //   uint32_t address = CbmMuchAddress::GetAddress(station, layer, layerside, sModule, sSector, sChannel);
+  //   // LOG(debug) << "Raw address " << address;
+  //   address = CbmMuchAddress::SetElementId(address, 1, station);
+  //   // LOG(debug) << "After adding station " << address << " binary " << std::bitset<32>(address);
+  //   address = CbmMuchAddress::SetElementId(address, 2, layer);
+  //   // LOG(debug) << "After adding Layer " << address << " binary " << std::bitset<32>(address);
+  //   address = CbmMuchAddress::SetElementId(address, 3, layerside);
+  //   // LOG(debug) << "After adding Layer Side " << address << " binary " << std::bitset<32>(address);
+  //   address = CbmMuchAddress::SetElementId(address, 4, sModule);
+  //   // LOG(debug) << "After adding module " << address << " binary " << std::bitset<32>(address);
+  //   address = CbmMuchAddress::SetElementId(address, 5, sSector);
+  //   // LOG(debug) << "After adding sector " << address << " binary " << std::bitset<32>(address);
+  //   address = CbmMuchAddress::SetElementId(address, 6, sChannel);
+  //   // LOG(debug) << "After adding channel " << address << " binary " << std::bitset<32>(address);
+
+  //   return address;
+  // }
+
+}  // namespace cbm::algo::mvd
diff --git a/algo/detectors/mvd/ReadoutConfig.h b/algo/detectors/mvd/ReadoutConfig.h
new file mode 100644
index 000000000..7bb63dc16
--- /dev/null
+++ b/algo/detectors/mvd/ReadoutConfig.h
@@ -0,0 +1,76 @@
+/* Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Dominik Smith [committer] */
+
+#ifndef ALGO_DETECTORS_MVD_READOUTCONFIG_H
+#define ALGO_DETECTORS_MVD_READOUTCONFIG_H
+
+#include <cstddef>
+#include <cstdint>
+#include <map>
+#include <vector>
+
+namespace cbm::algo::mvd
+{
+  class ReadoutConfig {
+
+   public:
+    /** @brief Constructor **/
+    ReadoutConfig();
+
+    /** @brief Destructor **/
+    virtual ~ReadoutConfig();
+
+    /** @brief Equipment in the configuration
+     ** @return Vector of equipment IDs
+     **/
+    std::vector<uint16_t> GetEquipmentIds();
+
+    /** @brief Number of elinks of a component
+     ** @param Equipment ID
+     ** @return Number of elinks
+     **/
+    size_t GetNumElinks(uint16_t equipmentId);
+
+    /** @brief API: Mapping from component and elink to addresses per channel
+     ** @param equipId     Equipment identifier (component)
+     ** @param elink       Elink number within component
+     ** @return Vector of MVD addresses, indexed via channel number
+     */
+    std::vector<uint32_t> Map(uint16_t equipId, uint16_t elink);
+
+    /** @brief API: Mapping from component and elink to channel mask flags
+     ** @param equipId     Equipment identifier (component)
+     ** @param elink       Elink number within component
+     ** @return (vector of mask flags for channels per asic)
+     */
+    std::vector<bool> MaskMap(uint16_t equipId, uint16_t elink);
+
+   private:
+    // --- MVD readout map
+    // --- Map index: (equipment, elink, channel), map value: (MVD address)
+    std::map<uint16_t, std::vector<std::vector<uint32_t>>> fReadoutMap = {};
+
+    // --- MVD channel mask  map
+    // --- Map index: (equipment, elink), map value: (vector of mask flags for channels per asic)
+    std::map<uint16_t, std::map<size_t, std::vector<bool>>> fMaskMap = {};  //!
+
+    /** @brief Initialisation of readout map **/
+    void Init();
+
+    /** @brief Maps component index, Fed Id and channel number to MVD Address **/
+    // uint32_t CreateMvdAddress(uint32_t dpbidx, int32_t iFebId, uint32_t usChan); // not needed for the time being
+
+    /// Constants
+    static const uint16_t numCrobPerComp   = 2;    ///< Number of CROBs possible per DPB
+    static const uint16_t numElinksPerCrob = 1;   ///< Number of elinks in each CROB ?
+    static const uint16_t numComp          = 2;    ///< Total number of Mvd DPBs in system
+
+    /// Variables
+
+
+  };
+
+}  // namespace cbm::algo::Mvd
+
+#endif  // ALGO_DETECTORS_MVD_READOUTCONFIG_H
diff --git a/algo/detectors/mvd/Unpack.cxx b/algo/detectors/mvd/Unpack.cxx
new file mode 100644
index 000000000..a5cb5ded2
--- /dev/null
+++ b/algo/detectors/mvd/Unpack.cxx
@@ -0,0 +1,36 @@
+/* Copyright (C) 2024 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Felix Weiglhofer [committer], Dominik Smith */
+
+#include "Unpack.h"
+
+#include "AlgoFairloggerCompat.h"
+
+using namespace cbm::algo::mvd;
+using fles::Subsystem;
+
+Unpack::Unpack(const ReadoutConfig& readout) : fReadout(readout)
+{
+  constexpr i64 SystemTimeOffset = -980;
+  constexpr u8 SystemVersion     = 0x20;
+
+  // Create one algorithm per component for MVD and configure it with parameters
+  auto equipIdsMvd = fReadout.GetEquipmentIds();
+  for (auto& equip : equipIdsMvd) {
+    mvd::UnpackPar par{};
+    const size_t numElinks = fReadout.GetNumElinks(equip);
+    for (size_t elink = 0; elink < numElinks; elink++) {
+      mvd::UnpackElinkPar elinkPar;
+      elinkPar.fAddress    = fReadout.Map(equip, elink);  // Vector of MVD addresses for this elink
+      elinkPar.fTimeOffset = SystemTimeOffset;
+      elinkPar.fChanMask   = fReadout.MaskMap(equip, elink);
+      par.fElinkParams.push_back(elinkPar);
+    }
+    auto algo                      = std::make_unique<UnpackMS>(par);
+    fAlgos[{equip, SystemVersion}] = std::move(algo);
+    L_(debug) << "--- Configured equipment " << equip << " with " << numElinks << " elinks";
+  }
+  L_(info) << "--- Configured " << fAlgos.size() << " unpacker algorithms for MVD.";
+}
+
+Unpack::Result_t Unpack::operator()(const fles::Timeslice& ts) const { return DoUnpack(Subsystem::MVD, ts); }
diff --git a/algo/detectors/mvd/Unpack.h b/algo/detectors/mvd/Unpack.h
new file mode 100644
index 000000000..6112ded20
--- /dev/null
+++ b/algo/detectors/mvd/Unpack.h
@@ -0,0 +1,32 @@
+/* Copyright (C) 2024 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Felix Weiglhofer [committer], Dominik Smith */
+
+#pragma once
+
+#include "CommonUnpacker.h"
+#include "ReadoutConfig.h"
+#include "UnpackMS.h"
+
+namespace cbm::algo::mvd
+{
+
+  namespace detail
+  {
+    using UnpackBase = CommonUnpacker<CbmMvdDigi, 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::mvd
diff --git a/algo/detectors/mvd/UnpackMS.cxx b/algo/detectors/mvd/UnpackMS.cxx
new file mode 100644
index 000000000..e2daa3699
--- /dev/null
+++ b/algo/detectors/mvd/UnpackMS.cxx
@@ -0,0 +1,168 @@
+/* Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pierre-Alain Loizeau, Volker Friese [committer] */
+
+#include "UnpackMS.h"
+
+// #include "MimosisMessage.h" // implement this
+
+#include <cassert>
+#include <cmath>
+#include <utility>
+#include <vector>
+
+using std::unique_ptr;
+using std::vector;
+
+namespace cbm::algo::mvd
+{
+
+  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
+  {
+
+    // --- Output data
+    Result_t result = {};
+
+    TimeSpec time;
+
+    // --- Current Timeslice start time in epoch units. Note that it is always a multiple of epochs
+    // --- and the epoch is a multiple of ns.
+    // const uint64_t epochLengthInNs = fkEpochLength * fkClockCycleNom / fkClockCycleDen;
+    const uint64_t epochLengthInNs = fTimeStamp;
+    time.currentTsTime             = tTimeslice / epochLengthInNs;
+
+    // --- Current TS_MSB epoch cycle
+    auto const msTime = msDescr.idx;  // Unix time of MS in ns
+    // time.currentCycle = std::ldiv(msTime, fkCycleLength).quot;
+    time.currentEpoch = 0;  // Needed to make each MS independent of the previous! Will be updated in message 1 if MS OK
+    time.currentEpochTime =
+      0;  // Needed to make each MS independent of the previous! Will be updated in message 1 if MS OK
+
+    // --- Number of messages in microslice
+    // auto msSize = msDescr.size;
+    // if (msSize % sizeof(mimosis::Message) != 0) {
+    //   std::get<1>(result).fNumErrInvalidMsSize++;
+    //   return result;
+    // }
+    // const uint32_t numMessages = msSize / sizeof(mimosis::Message);
+    // if (numMessages < 2) {
+    //   std::get<1>(result).fNumErrInvalidMsSize++;
+    //   return result;
+    // }
+
+    // const uint32_t maxDigis = numMessages - 2;  // -2 for the TS_MSB and EPOCH messages
+    // std::get<0>(result).reserve(maxDigis);
+
+    // --- Interpret MS content as sequence of SMX messages
+    // auto message = reinterpret_cast<const mimosis::Message*>(msContent);
+
+    // --- The first message in the MS is expected to be of type EPOCH and can be ignored.
+    // if (message[0].GetMessType() != mimosis::MessType::Epoch) {
+    //   std::get<1>(result).fNumErrInvalidFirstMessage++;
+    //   return result;
+    // }
+
+    // --- The second message must be of type ts_msb.
+    // if (message[1].GetMessType() != mimosis::MessType::TsMsb) {
+    //   std::get<1>(result).fNumErrInvalidFirstMessage++;
+    //   return result;
+    // }
+    // ProcessTsmsbMessage(message[1], time);
+
+    // --- Message loop
+    // for (uint32_t messageNr = 2; messageNr < numMessages; messageNr++) {
+
+    //   // --- Action depending on message type
+    //   switch (message[messageNr].GetMessType()) {
+
+    //     case mimosis::MessType::Hit: {
+    //       ProcessHitMessage(message[messageNr], time, std::get<0>(result), std::get<1>(result));
+    //       break;
+    //     }
+    //     case mimosis::MessType::TsMsb: {
+    //       ProcessTsmsbMessage(message[messageNr], time);
+    //       break;
+    //     }
+    //     default: {
+    //       std::get<1>(result).fNumNonHitOrTsbMessage++;
+    //       break;
+    //     }
+
+    //   }  //? Message type
+
+    // }  //# Messages
+
+    return result;
+  }
+  // --------------------------------------------------------------------------
+
+
+  // -----   Process hit message   --------------------------------------------
+  // inline void UnpackMS::ProcessHitMessage(const mimosis::Message& message, const TimeSpec& time,
+  //                                         vector<CbmMuchDigi>& digiVec, UnpackMonitorData& monitor) const
+  // {
+  //   // --- Check eLink and get parameters
+  //   uint16_t elink = message.GetLinkIndexHitBinning();
+  //   if (elink >= fParams.fElinkParams.size()) {
+  //     monitor.fNumErrElinkOutOfRange++;
+  //     return;
+  //   }
+  //   const UnpackElinkPar& elinkPar = fParams.fElinkParams.at(elink);
+  //   uint16_t channel               = message.GetHitChannel();
+
+  //   // --- Check for masked channel
+  //   if (!elinkPar.fChanMask.empty() && elinkPar.fChanMask[channel] == true) {
+  //     return;
+  //   }
+
+  //   uint32_t address = (elinkPar.fAddress)[channel];
+
+  //   // --- Expand time stamp to time within timeslice (in clock cycle)
+  //   uint64_t messageTime = message.GetHitTimeBinning() + time.currentEpochTime;
+
+  //   // --- Convert time stamp from clock cycles to ns. Round to nearest full ns.
+  //   messageTime = (messageTime * fkClockCycleNom + fkClockCycleDen / 2) / fkClockCycleDen;
+
+  //   // --- Correct ASIC-wise offsets
+  //   messageTime -= elinkPar.fTimeOffset;
+
+  //   // --- Charge
+  //   double charge = message.GetHitAdc();
+
+  //   // --- Create output digi
+  //   digiVec.emplace_back(address, charge, messageTime);
+  // }
+  // --------------------------------------------------------------------------
+
+
+  // -----   Process an epoch (TS_MSB) message   ------------------------------
+  // inline void UnpackMS::ProcessTsmsbMessage(const mimosis::Message& message, TimeSpec& time) const
+  // {
+  //   // The compression of time is based on the hierarchy epoch cycle - epoch - message time.
+  //   // Cycles are counted from the start of Unix time and are multiples of an epoch (ts_msb).
+  //   // The epoch number is counted within each cycle. The time in the hit message is expressed
+  //   // in units of the readout clock cycle relative to the current epoch.
+  //   // The ts_msb message indicates the start of a new epoch. Its basic information is the epoch
+  //   // number within the current cycle. A cycle wrap resets the epoch number to zero, so it is
+  //   // indicated by the epoch number being smaller than the previous one (epoch messages are
+  //   // seemingly not consecutively in the data stream, but only if there are hit messages in between).
+  //   auto epoch = message.GetTsMsbValBinning();
+
+  //   // --- Cycle wrap
+  //   if (epoch < time.currentEpoch) time.currentCycle++;
+
+  //   // --- Update current epoch counter
+  //   time.currentEpoch = epoch;
+
+  //   // --- Calculate epoch time in clocks cycles relative to timeslice start time
+  //   time.currentEpochTime = (time.currentCycle * fkEpochsPerCycle + epoch - time.currentTsTime) * fkEpochLength;
+  // }
+  // --------------------------------------------------------------------------
+
+
+}  // namespace cbm::algo::much
diff --git a/algo/detectors/mvd/UnpackMS.h b/algo/detectors/mvd/UnpackMS.h
new file mode 100644
index 000000000..ea386bf4e
--- /dev/null
+++ b/algo/detectors/mvd/UnpackMS.h
@@ -0,0 +1,149 @@
+/* Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pierre-Alain Loizeau, Volker Friese [committer] */
+#pragma once
+
+#include "CbmMvdDigi.h"
+#include "MicrosliceDescriptor.hpp"
+// #include "MimosisMessage.h" // create this class
+#include "Timeslice.hpp"
+#include "UnpackMSBase.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <sstream>
+#include <vector>
+
+namespace cbm::algo::mvd
+{
+
+
+  /** @struct UnpackElinkPar
+   ** @author Volker Friese <v.friese@gsi.de>
+   ** @since 25 November 2021
+   ** @brief STS Unpacking parameters for one eLink / ASIC
+   **/
+  struct UnpackElinkPar {
+    // std::vector<uint32_t> fAddress;  ///< CbmMuchAddress for different channels
+    std::vector<bool> fPixX;     ///< col masking flags
+    std::vector<bool> fPixY;     ///< row masking flags
+    uint64_t fTimeOffset = 0.;       ///< Time calibration parameter
+  };
+
+
+  /** @struct UnpackPar
+   ** @author Volker Friese <v.friese@gsi.de>
+   ** @since 25 November 2021
+   ** @brief Parameters required for the STS unpacking (specific to one component)
+   **/
+  struct UnpackPar {
+    std::vector<UnpackElinkPar> fElinkParams = {};  ///< Parameters for each eLink
+  };
+
+
+  /** @struct UnpackMoni
+   ** @author Volker Friese <v.friese@gsi.de>
+   ** @since 2 December 2021
+   ** @brief Monitoring data for STS unpacking
+   **/
+  struct UnpackMonitorData {
+    uint32_t fNumNonHitOrTsbMessage     = 0;
+    uint32_t fNumErrElinkOutOfRange     = 0;  ///< Elink not contained in parameters
+    uint32_t fNumErrInvalidFirstMessage = 0;  ///< First message is not TS_MSB or second is not EPOCH
+    uint32_t fNumErrInvalidMsSize       = 0;  ///< Microslice size is not multiple of message size
+    uint32_t fNumErrTimestampOverflow   = 0;  ///< Overflow in 64 bit time stamp
+    bool HasErrors()
+    {
+      uint32_t numErrors = fNumNonHitOrTsbMessage + fNumErrElinkOutOfRange + fNumErrInvalidFirstMessage
+                           + fNumErrInvalidMsSize + fNumErrTimestampOverflow;
+      return (numErrors > 0 ? true : false);
+    }
+    std::string print()
+    {
+      std::stringstream ss;
+      ss << "errors " << fNumNonHitOrTsbMessage << " | " << fNumErrElinkOutOfRange << " | "
+         << fNumErrInvalidFirstMessage << " | " << fNumErrInvalidMsSize << " | " << fNumErrTimestampOverflow << " | ";
+      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
+   ** @author Pierre-Alain Loizeau <p.-a.loizeau@gsi.de>
+   ** @author Volker Friese <v.friese@gsi.de>
+   ** @since 25 November 2021
+   ** @brief Unpack algorithm for STS
+   **/
+  class UnpackMS : public UnpackMSBase<CbmMvdDigi, UnpackMonitorData, UnpackAuxData> {
+
+   public:
+    /** @brief Construct with 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 STS digi data
+     **/
+    Result_t operator()(const uint8_t* msContent, const fles::MicrosliceDescriptor& msDescr,
+                        const uint64_t tTimeslice) const override;
+
+   private:  // datatypes
+    struct TimeSpec {
+      uint64_t currentTsTime    = 0;  ///< Unix time of timeslice in units of epoch length
+      uint64_t currentCycle     = 0;  ///< Current epoch cycle
+      uint32_t currentEpoch     = 0;  ///< Current epoch number within epoch cycle
+      uint64_t currentEpochTime = 0;  ///< Current epoch time relative to timeslice in clock cycles
+    };
+
+
+   private:  // methods
+    /** @brief Process a hit message
+     ** @param message SMX message (32-bit word)
+     ** @param digiVec Vector to append the created digi to
+     ** @param monitor Reference to monitor object
+     **/
+    // void ProcessHitMessage(const mimosis::Message& message, const TimeSpec& time, std::vector<CbmMvdDigi>& digiVec,
+    //                        UnpackMonitorData& monitor) const;
+
+    /** @brief Process an epoch message (TS_MSB)
+     ** @param message SMX message (32-bit word)
+     **/
+    // void ProcessTsmsbMessage(const mimosis::Message& message, TimeSpec& time) const; // to do
+
+
+   private:                  // members
+    UnpackPar fParams = {};  ///< Parameter container
+
+    /** Number of TS_MSB epochs per cycle **/
+    // static constexpr uint64_t fkEpochsPerCycle = mimosis::kuTsMsbNbTsBinsBinning;
+
+    /** Length of TS_MSB epoch in clock cycles **/
+    // static constexpr uint64_t fTimeStamp = mimosis::Time;
+    static constexpr uint64_t fTimeStamp = 0;
+
+    /** Clock cycle nominator [ns] and denominator. The clock cycle in ns is nominator / denominator. **/
+    // static constexpr uint32_t fkClockCycleNom = mimosis::kulClockCycleNom;
+    // static constexpr uint32_t fkClockCycleDen = mimosis::kulClockCycleDen;
+
+    /** Epoch cycle length in ns **/
+    // static constexpr uint64_t fkCycleLength = (fkEpochsPerCycle * fkEpochLength * fkClockCycleNom) / fkClockCycleDen;
+  };
+
+
+}  // namespace cbm::algo::much
diff --git a/algo/global/ParFiles.cxx b/algo/global/ParFiles.cxx
index 9d4e8ee2c..e936debe0 100644
--- a/algo/global/ParFiles.cxx
+++ b/algo/global/ParFiles.cxx
@@ -34,6 +34,10 @@ ParFiles::ParFiles(uint32_t runId)
       sts.walkMap   = "StsWalkMap_mcbm2022.yaml";
       sts.hitfinder = "StsHitfinder_mcbm2022.yaml";
 
+      mvd.readout   = "MvdReadout_mcbm2025.yaml";
+      // mvd.pixMask  = "MvdChannelMaskSet_mcbm2025.yaml";
+      // mvd.walkMap   = "MvdWalkMap_mcbm2022.yaml";
+
       tof.readout   = "TofReadout_mcbm2022.yaml";
       tof.calibrate = "TofCalibratePar_mcbm2022.yaml";
       tof.hitfinder = "TofHitfinderPar_mcbm2022.yaml";
diff --git a/algo/global/ParFiles.h b/algo/global/ParFiles.h
index 0fe7b3094..cdcf88a04 100644
--- a/algo/global/ParFiles.h
+++ b/algo/global/ParFiles.h
@@ -37,6 +37,12 @@ namespace cbm::algo
       fs::path hitfinder;
     } sts;
 
+    struct {
+      fs::path readout;
+      fs::path pixMask;
+      fs::path walkMap;
+    } mvd;
+
     struct {
       fs::path readout;
       fs::path calibrate;
diff --git a/algo/global/Reco.cxx b/algo/global/Reco.cxx
index 79784707e..7da16cfc5 100644
--- a/algo/global/Reco.cxx
+++ b/algo/global/Reco.cxx
@@ -13,6 +13,7 @@
 #include "ParFiles.h"
 #include "RecoGeneralQa.h"
 #include "StsDigiQa.h"
+// #include "MvdDigiQa.h"  ToDo
 #include "TrackingSetup.h"
 #include "bmon/Calibrate.h"
 #include "bmon/Hitfind.h"
@@ -31,6 +32,9 @@
 #include "sts/ChannelMaskSet.h"
 #include "sts/HitfinderChain.h"
 #include "sts/Unpack.h"
+// #include "mvd/ChannelMaskSet.h" // todo
+// #include "mvd/HitfinderChain.h" // todo
+#include "mvd/Unpack.h"
 #include "tof/Calibrate.h"
 #include "tof/Hitfind.h"
 #include "tof/Unpack.h"
@@ -161,6 +165,27 @@ void Reco::Init(const Options& opts)
     }
   }
 
+  if (Opts().Has(Subsystem::MVD) && Opts().Has(Step::Unpack)) {
+    mvd::ReadoutConfig cfg{};
+    fMvdUnpack = std::make_unique<mvd::Unpack>(cfg);
+  }
+
+  // if (Opts().Has(Subsystem::MVD) && Opts().Has(Step::Unpack)) {
+  //   mvd::ReadoutSetup readoutSetup = yaml::ReadFromFile<mvd::ReadoutSetup>(Opts().ParamsDir() / parFiles.mvd.readout);
+  //   auto pixMask = yaml::ReadFromFile<mvd::ChannelMaskSet>(Opts().ParamsDir() / parFiles.mvd.chanMask);
+  //   auto walkMap  = yaml::ReadFromFile<mvd::WalkMap>(Opts().ParamsDir() / parFiles.mvd.walkMap);
+  //   bool bCollectAux               = (fSender != nullptr && Opts().CollectAuxData());
+  //   mvd::ReadoutConfig readout{readoutSetup, pixMask};
+  //   mvd::Unpack::Config cfg{.readout = readout, .walkMap = walkMap, .bCollectAuxData = bCollectAux};
+  //   fMvdUnpack = std::make_unique<mvd::Unpack>(cfg);
+  //   // if (fSender != nullptr && Opts().Has(QaStep::UnpackMvd)) {
+  //   //   fMvdDigiQa = std::make_unique<mvd::DigiQa>(fSender);
+  //   //   fMvdDigiQa->SetUseAuxData(bCollectAux);
+  //   //   fMvdDigiQa->RegisterReadoutSetup(readoutSetup);
+  //   //   fMvdDigiQa->Init();
+  //   // }
+  // }
+
   if (Opts().Has(Subsystem::TOF) && Opts().Has(Step::Unpack)) {
     tof::ReadoutSetup readoutSetup = yaml::ReadFromFile<tof::ReadoutSetup>(Opts().ParamsDir() / parFiles.tof.readout);
     tof::ReadoutConfig cfg{readoutSetup};
@@ -332,6 +357,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.fMvd, auxDigis.fMvd)     = RunUnpacker(fMvdUnpack, ts);
       std::tie(digis.fRich, auxDigis.fRich)   = RunUnpacker(fRichUnpack, ts);
       std::tie(digis.fSts, auxDigis.fSts)     = RunUnpacker(fStsUnpack, ts);
       std::tie(digis.fTof, auxDigis.fTof)     = RunUnpacker(fTofUnpack, ts);
@@ -342,7 +368,7 @@ RecoResults Reco::Run(const fles::Timeslice& ts)
       // digis.fPsd   = RunUnpacker(fPsdUnpack, ts);
       // digis.fFsd   = RunUnpacker(fFsdUnpack, ts);
 
-      L_(info) << "TS contains Digis: STS=" << digis.fSts.size() << " MUCH=" << digis.fMuch.size()
+      L_(info) << "TS contains Digis: STS=" << digis.fSts.size() << " MUCH=" << digis.fMuch.size() << " MVD=" << digis.fMvd.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();
@@ -457,6 +483,7 @@ RecoResults Reco::Run(const fles::Timeslice& ts)
       results.bmonDigis  = std::move(digis.fBmon);
       results.stsDigis   = std::move(digis.fSts);
       results.muchDigis  = std::move(digis.fMuch);
+      results.mvdDigis  = std::move(digis.fMvd);
       results.trd2dDigis = std::move(digis.fTrd2d);
       results.trdDigis   = std::move(digis.fTrd);
       results.tofDigis   = std::move(digis.fTof);
@@ -736,6 +763,7 @@ void Reco::QueueEvbuildMetrics(const evbuild::EventbuildChainMonitorData& mon)
 
   queueDetMetrics("sts", mon.evbuild.sts);
   queueDetMetrics("much", mon.evbuild.much);
+  queueDetMetrics("mvd", mon.evbuild.mvd);
   queueDetMetrics("tof", mon.evbuild.tof);
   queueDetMetrics("bmon", mon.evbuild.bmon);
   queueDetMetrics("trd", mon.evbuild.trd);
diff --git a/algo/global/Reco.h b/algo/global/Reco.h
index e5a841215..1f21990ef 100644
--- a/algo/global/Reco.h
+++ b/algo/global/Reco.h
@@ -41,6 +41,11 @@ namespace cbm::algo
     class Unpack;
   }
 
+  namespace mvd
+  {
+    class Unpack;
+  }
+
   namespace rich
   {
     class Unpack;
@@ -164,6 +169,9 @@ namespace cbm::algo
     // MUCH
     std::unique_ptr<much::Unpack> fMuchUnpack;
 
+    // MVD
+    std::unique_ptr<mvd::Unpack> fMvdUnpack;
+    
     // RICH
     std::unique_ptr<rich::Unpack> fRichUnpack;
 
diff --git a/algo/global/RecoResults.h b/algo/global/RecoResults.h
index 3bcee8be6..2653d3488 100644
--- a/algo/global/RecoResults.h
+++ b/algo/global/RecoResults.h
@@ -32,6 +32,7 @@ namespace cbm::algo
     PODVector<CbmBmonDigi> bmonDigis;
     PODVector<CbmStsDigi> stsDigis;
     PODVector<CbmMuchDigi> muchDigis;
+    PODVector<CbmMvdDigi> mvdDigis;
     PODVector<CbmTrdDigi> trd2dDigis;
     PODVector<CbmTrdDigi> trdDigis;
     PODVector<CbmTofDigi> tofDigis;
diff --git a/algo/global/StorableRecoResults.h b/algo/global/StorableRecoResults.h
index 5560b7d1a..27b23a9d7 100644
--- a/algo/global/StorableRecoResults.h
+++ b/algo/global/StorableRecoResults.h
@@ -58,6 +58,9 @@ namespace cbm::algo
     std::vector<CbmMuchDigi>& MuchDigis() { return fMuchDigis; }
     const std::vector<CbmMuchDigi>& MuchDigis() const { return fMuchDigis; }
 
+    std::vector<CbmMvdDigi>& MvdDigis() { return fMvdDigis; }
+    const std::vector<CbmMvdDigi>& MvdDigis() const { return fMvdDigis; }
+
     std::vector<CbmTrdDigi>& Trd2dDigis() { return fTrd2dDigis; }
     const std::vector<CbmTrdDigi>& Trd2dDigis() const { return fTrd2dDigis; }
 
@@ -102,6 +105,7 @@ namespace cbm::algo
     std::vector<CbmBmonDigi> fBmonDigis;
     std::vector<CbmStsDigi> fStsDigis;
     std::vector<CbmMuchDigi> fMuchDigis;
+    std::vector<CbmMvdDigi> fMvdDigis;
     std::vector<CbmTrdDigi> fTrd2dDigis;
     std::vector<CbmTrdDigi> fTrdDigis;
     std::vector<CbmTofDigi> fTofDigis;
@@ -138,6 +142,7 @@ namespace cbm::algo
       ar& fBmonDigis;
       ar& fStsDigis;
       ar& fMuchDigis;
+      ar& fMvdDigis;
       ar& fTrd2dDigis;
       ar& fTrdDigis;
       ar& fTofDigis;
diff --git a/core/data/CMakeLists.txt b/core/data/CMakeLists.txt
index b5b59dd07..c5c6e3d0d 100644
--- a/core/data/CMakeLists.txt
+++ b/core/data/CMakeLists.txt
@@ -134,7 +134,7 @@ 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
+bmon/CbmBmonDigiData.h sts/CbmStsDigiData.h much/CbmMuchDigiData.h mvd/CbmMvdDigiData.h rich/CbmRichDigiData.h trd/CbmTrdDigiData.h
 tof/CbmTofDigiData.h psd/CbmPsdDigiData.h fsd/CbmFsdDigiData.h trd/CbmTrdFexMessageSpadic.h)
 
 set(LIBRARY_NAME CbmData)
@@ -163,6 +163,7 @@ Install(FILES
         base/CbmDigiData.h base/CbmDigiVector.h
         bmon/CbmBmonDigiData.h
         much/CbmMuchDigiData.h
+        mvd/CbmMvdDigiData.h
         psd/CbmPsdDigiData.h
         fsd/CbmFsdDigiData.h
         rich/CbmRichDigiData.h rich/CbmRichRingLight.h
diff --git a/core/data/base/CbmDigiData.h b/core/data/base/CbmDigiData.h
index c864e8ba6..6c5183a27 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 "CbmMvdDigiData.h"
 #include "CbmPsdDigiData.h"
 #include "CbmRichDigiData.h"
 #include "CbmStsDigiData.h"
@@ -35,6 +36,7 @@ class CbmDigiData {
   CbmBmonDigiData fBmon;  ///< Beam monitor data
   CbmStsDigiData fSts;    ///< STS data
   CbmMuchDigiData fMuch;  ///< MUCH data
+  CbmMvdDigiData fMvd;  ///< MVD data
   CbmRichDigiData fRich;  ///< RICH data
   CbmTrdDigiData fTrd;    ///< TRD data
   CbmTrdDigiData fTrd2d;  ///< TRD2D data
@@ -51,6 +53,7 @@ class CbmDigiData {
     ar& fBmon;
     ar& fSts;
     ar& fMuch;
+    ar& fMvd;
     ar& fTrd;
     ar& fTrd2d;
     ar& fTof;
@@ -70,6 +73,7 @@ class CbmDigiData {
     fBmon.Clear();
     fSts.Clear();
     fMuch.Clear();
+    fMvd.Clear();
     fTrd.Clear();
     fTrd2d.Clear();
     fTof.Clear();
@@ -85,6 +89,7 @@ class CbmDigiData {
       case ECbmModuleId::kBmon: return fBmon.Size(); break;
       case ECbmModuleId::kSts: return fSts.Size(); break;
       case ECbmModuleId::kMuch: return fMuch.Size(); break;
+      case ECbmModuleId::kMvd: return fMvd.Size(); break;
       case ECbmModuleId::kTrd: return fTrd.Size(); break;
       case ECbmModuleId::kTrd2d: return fTrd2d.Size(); break;
       case ECbmModuleId::kTof: return fTof.Size(); break;
@@ -102,6 +107,7 @@ class CbmDigiData {
     size += fBmon.Size() * sizeof(CbmBmonDigi);
     size += fSts.Size() * sizeof(CbmStsDigi);
     size += fMuch.Size() * sizeof(CbmMuchDigi);
+    size += fMvd.Size() * sizeof(CbmMvdDigi);
     size += fTrd.Size() * sizeof(CbmTrdDigi);
     size += fTrd2d.Size() * sizeof(CbmTrdDigi);
     size += fTof.Size() * sizeof(CbmTofDigi);
diff --git a/core/data/mvd/CbmMvdDigiData.h b/core/data/mvd/CbmMvdDigiData.h
new file mode 100644
index 000000000..798bf760a
--- /dev/null
+++ b/core/data/mvd/CbmMvdDigiData.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 2022 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Volker Friese [committer] */
+
+#ifndef CBMMVDDIGIDATA_H
+#define CBMMVDDIGIDATA_H 1
+
+
+#include "CbmMvdDigi.h"
+
+#ifndef NO_ROOT
+#include <Rtypes.h>  // for ClassDef
+#endif
+
+#include <boost/serialization/access.hpp>
+#include <boost/serialization/base_object.hpp>
+
+#include <vector>
+
+/** @class CbmMvdDigiData
+ ** @brief Container class for CbmMvdDigi objects
+ ** @author Volker Friese <v.friese@gsi.de>
+ ** @since 7.12.2022
+ ** @version 1.0
+ **
+ ** Container class for transporting CbmMvdDigi objects.
+ ** The current implementation is the simplest one: a std::vector.
+ **/
+class CbmMvdDigiData {
+
+public:
+  std::vector<CbmMvdDigi> fDigis = {};  ///< Data vector
+
+  friend class boost::serialization::access;
+
+  /** @brief BOOST serializer**/
+  template<class Archive>
+  void serialize(Archive& ar, const unsigned int /*version*/)
+  {
+    ar& fDigis;
+  }
+
+  /** @brief Clear content **/
+  void Clear() { fDigis.clear(); }
+
+  /** @brief Size */
+  size_t Size() const { return fDigis.size(); }
+
+  // --- ROOT serializer
+#ifndef NO_ROOT
+  ClassDefNV(CbmMvdDigiData, 1);
+#endif
+};
+
+#endif
diff --git a/reco/app/cbmreco/main.cxx b/reco/app/cbmreco/main.cxx
index 6dd23efc4..884c4142e 100644
--- a/reco/app/cbmreco/main.cxx
+++ b/reco/app/cbmreco/main.cxx
@@ -39,6 +39,7 @@ std::shared_ptr<StorableRecoResults> makeStorableRecoResults(const fles::Timesli
   storable->BmonDigis()  = ToStdVector(results.bmonDigis);
   storable->StsDigis()   = ToStdVector(results.stsDigis);
   storable->MuchDigis()  = ToStdVector(results.muchDigis);
+  storable->MvdDigis()  = ToStdVector(results.mvdDigis);
   storable->Trd2dDigis() = ToStdVector(results.trd2dDigis);
   storable->TrdDigis()   = ToStdVector(results.trdDigis);
   storable->TofDigis()   = ToStdVector(results.tofDigis);
-- 
GitLab