diff --git a/algo/detectors/rich/UnpackMS.cxx b/algo/detectors/rich/UnpackMS.cxx
index 7460c6bfb915dd983819a7242529ec0c0fec274c..07c826e6d023c9f1f0dd3e11850bc302aeb8e37e 100644
--- a/algo/detectors/rich/UnpackMS.cxx
+++ b/algo/detectors/rich/UnpackMS.cxx
@@ -166,6 +166,11 @@ namespace cbm::algo::rich
   void UnpackMS::ProcessSubSubEvent(rich::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
@@ -179,19 +184,67 @@ namespace cbm::algo::rich
       }
       return;
     }
+    // Skip SubSubEvent for CTS and unknown DiRICH addresses
+    // TODO: properly handle/skip CTS subsubevents
+    if (fParams.fElinkParams.end() == fParams.fElinkParams.find(subSubEventId)) {
+      if (0x8000 != (subSubEventId & 0xF000)) {
+        // No log for CTS subsubevents
+        L_(debug) << "Unknown DiRICH ID 0x" << std::hex << subSubEventId << std::dec
+                  << ": Stopping processing of this subsubevent!!!";
+      }
+      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) << "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) << "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
@@ -199,23 +252,69 @@ namespace cbm::algo::rich
       word = reader.NextWord();
       switch (GetTdcWordType(word)) {
         case TdcWordType::TimeData: {
+          if (!wasHeader || !wasEpoch || wasTrailer) {
+            L_(debug) << "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) << "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) << "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) << "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) << "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: {
-          break;
           // for the moment do nothing
           ctx.monitor.fNumDebugMessage++;
           break;
@@ -228,18 +327,33 @@ namespace cbm::algo::rich
     }
 
     // Last word is expected to be of type "trailer"
-    if (GetTdcWordType(reader.NextWord()) != TdcWordType::Trailer) {
+    word = reader.NextWord();
+    if (GetTdcWordType(word) != TdcWordType::Trailer) {
+      L_(debug) << "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) << "DiRICH 0x" << std::hex << subSubEventId << std::dec
+                    << ": Rescuing TimeData in subsubevent with missing Trailer leading to saved Digi";
+        }
+        else {
+          ctx.monitor.fNumErrOrphanRecovTimeData++;
+          L_(debug) << "DiRICH 0x" << std::hex << subSubEventId << std::dec
+                    << ": Rescuing TimeData in subsubevent with missing Trailer w/o extra digi";
+        }
+      }
     }
   }
 
   // ---- ProcessTimeDataWord ----
-  void UnpackMS::ProcessTimeDataWord(uint32_t epoch, uint32_t tdcWord, uint32_t subSubEventId,
+  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 =
@@ -248,6 +362,7 @@ namespace cbm::algo::rich
 
       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.
@@ -260,6 +375,7 @@ namespace cbm::algo::rich
           if (ToT >= fToTMin && ToT <= fToTMax) {
             if (fullTimeCorr >= 0.0) {
               WriteOutputDigi(subSubEventId, td.fChannel, raisingTime[td.fChannel], ToT, ctx);
+              madeDigi = true;
             }
           }
           // pair was created, set raising edge to -1.
@@ -267,6 +383,10 @@ namespace cbm::algo::rich
         }
       }
     }
+    else {
+      ctx.monitor.fNumErrChannelOutOfBounds++;
+    }
+    return madeDigi;
   }
 
   double UnpackMS::CalculateTime(uint32_t epoch, uint32_t coarse, uint32_t fine) const
diff --git a/algo/detectors/rich/UnpackMS.h b/algo/detectors/rich/UnpackMS.h
index f89b2f58cf8a07000fe2a9b6cd67562ce6e99fa7..0dd092c3019de79a5f8627679b5b7ad4b86ee728 100644
--- a/algo/detectors/rich/UnpackMS.h
+++ b/algo/detectors/rich/UnpackMS.h
@@ -66,32 +66,41 @@ namespace cbm::algo::rich
    **/
   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 exprected trailing words
+    uint32_t fNumErrExcessLastWords      = 0;  ///< More than expected trailing words
+    uint32_t fNumSkippedSubsubevent      = 0;  ///< Whenever subsubevent skipped due to error (but not mask)
 
-    bool HasErrors()
+    bool HasErrors() const
     {
       uint32_t numErrors = fNumDebugMessage + fNumErrInvalidFirstMessage + fNumErrInvalidSecondMessage
                            + fNumErrInvalidLastMessage + fNumErrWildHeaderMessage + fNumErrWildTrailerMessage
-                           + fNumErrTdcErrorWord + fNumErrChannelOutOfBounds + fNumErrInvalidHubId
-                           + fNumErrInvalidHubSize + fNumErrExcessLastWords;
+                           + fNumErrWildEpoch + fNumErrWildTdcTime + fNumErrTdcErrorWord + fNumErrChannelOutOfBounds
+                           + fNumErrOrphanRecovTimeData + fNumErrInvalidHubId + fNumErrInvalidHubSize
+                           + fNumErrExcessLastWords;
       return (numErrors > 0 ? true : false);
     }
-    std::string print()
+    std::string print() const
     {
       std::stringstream ss;
-      ss << "errors " << fNumDebugMessage << " | " << fNumErrInvalidFirstMessage << " | " << fNumErrInvalidSecondMessage
-         << " | " << fNumErrInvalidLastMessage << " | " << fNumErrWildHeaderMessage << " | "
-         << fNumErrWildTrailerMessage << " | " << fNumErrTdcErrorWord << " | " << fNumErrChannelOutOfBounds << " | "
-         << fNumErrInvalidHubId << " | " << fNumErrInvalidHubSize << " | " << fNumErrExcessLastWords << " | ";
+      ss << "errors " << fNumDebugMessage << " | " << fNumCtsAndUnmappedDirich << " | " << fNumErrInvalidFirstMessage
+         << " | " << fNumErrInvalidSecondMessage << " | " << fNumErrInvalidLastMessage << " | "
+         << fNumErrWildHeaderMessage << " | " << fNumErrWildTrailerMessage << " | " << fNumErrWildEpoch << " | "
+         << fNumErrWildTdcTime << " | " << fNumErrTdcErrorWord << " | " << fNumErrChannelOutOfBounds << " | "
+         << fNumErrOrphanRecovTimeData << " | " << fNumWarnRecoveredLastDigi << " | " << fNumErrInvalidHubId << " | "
+         << fNumErrInvalidHubSize << " | " << fNumErrExcessLastWords << " | " << fNumSkippedSubsubevent;
       return ss.str();
     }
   };
@@ -153,7 +162,7 @@ namespace cbm::algo::rich
 
     void ProcessSubSubEvent(MicrosliceReader& reader, int nofTimeWords, uint32_t subSubEventId, MSContext& ctx) const;
 
-    void ProcessTimeDataWord(uint32_t epoch, uint32_t tdcWord, uint32_t subSubEventId, std::vector<double>& raisingTime,
+    bool ProcessTimeDataWord(uint32_t epoch, uint32_t tdcWord, uint32_t subSubEventId, std::vector<double>& raisingTime,
                              MSContext& ctx) const;
 
     TdcWordType GetTdcWordType(uint32_t tdcWord) const;
diff --git a/macro/beamtime/mcbm2024/data/.gitignore b/macro/beamtime/mcbm2024/data/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..4ea40f8315d27f24d50e2c0f6ac12ea00b5542f6
--- /dev/null
+++ b/macro/beamtime/mcbm2024/data/.gitignore
@@ -0,0 +1 @@
+*.root
diff --git a/macro/beamtime/mcbm2024/run_unpack_tsa_rich_only.C b/macro/beamtime/mcbm2024/run_unpack_tsa_rich_only.C
new file mode 100644
index 0000000000000000000000000000000000000000..651bfa2ff7fa0e6488a94f204f68e8f6b3df4c5e
--- /dev/null
+++ b/macro/beamtime/mcbm2024/run_unpack_tsa_rich_only.C
@@ -0,0 +1,808 @@
+/* Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Jan de Cuveland, Volker Friese, Pierre-Alain Loizeau, Pascal Raisig [committer], Dominik Smith, Adrian A. Weber  */
+
+/** @file run_unpack_tsa.C
+ ** @author Volker Friese <v.friese@gsi.de>
+ ** @since May 2021
+ **/
+
+
+// --- Includes needed for IDE
+#include <RtypesCore.h>
+
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+#if !defined(__CLING__)
+#include "CbmTrdRawMessageSpadic.h"
+#include "CbmTrdSpadic.h"
+
+#include <FairLogger.h>
+#include <FairRootFileSink.h>
+#include <FairRunOnline.h>
+#include <Logger.h>
+
+#include <TStopwatch.h>
+#include <TSystem.h>
+#endif
+
+std::shared_ptr<CbmTrdUnpackMonitor> GetTrdMonitor(std::string treefilename, bool fasp = false);
+std::shared_ptr<CbmTrdSpadic> GetTrdSpadic(bool useAvgBaseline = false);
+std::shared_ptr<CbmStsUnpackMonitor> GetStsMonitor(std::string treefilename, bool bDebugMode = false);
+std::shared_ptr<CbmMuchUnpackMonitor> GetMuchMonitor(std::string treefilename, bool bDebugMode = false);
+std::shared_ptr<CbmRichUnpackMonitor> GetRichMonitor(std::string treefilename, bool bDebugMode = false);
+std::shared_ptr<CbmTofUnpackMonitor> GetTofMonitor(std::string treefilename, bool bBmonMode = false);
+std::string defaultSetupName = "mcbm_beam_2021_07_surveyed";
+
+void run_unpack_tsa(std::vector<std::string> infile = {"test.tsa"}, UInt_t runid = 0,
+                    std::string setupName = defaultSetupName, std::int32_t nevents = -1, bool bBmoninTof = false,
+                    std::string outpath = "data/")
+{
+
+  // ========================================================================
+  //          Adjust this part according to your requirements
+
+  // --- Logger settings ----------------------------------------------------
+  TString logLevel     = "INFO";
+  TString logVerbosity = "LOW";
+  // ------------------------------------------------------------------------
+
+  // -----   Environment   --------------------------------------------------
+  TString myName = "run_unpack_tsa";               // this macro's name for screen output
+  TString srcDir = gSystem->Getenv("VMCWORKDIR");  // top source directory
+  // ------------------------------------------------------------------------
+
+
+  // -----   Potentially hardcode the infile   ------------------------------
+
+  // std::string infile = srcDir + "/input/mcbm_run399_first20Ts.tsa";
+
+  // ------------------------------------------------------------------------
+
+
+  // -----   Output filename   ----------------------------------------------
+  std::string outfilename = infile[0];
+  auto filenamepos        = infile[0].find_last_of("/");
+  filenamepos++;
+  std::string filename = infile[0].substr(filenamepos);
+  if (filename.find("*") != infile[0].npos) filename = std::to_string(runid) + ".tsa";
+  if (filename.find(";") != infile[0].npos) filename = std::to_string(runid) + "_merged" + ".tsa";
+  if (outpath.empty()) {
+    outpath = infile[0].substr(0, filenamepos);
+  }
+  outfilename = outpath + filename;
+  if (bBmoninTof) {  //
+    outfilename.replace(outfilename.find(".tsa"), 4, "_bmonintof.digi.root");
+  }
+  else {
+    outfilename.replace(outfilename.find(".tsa"), 4, ".digi.root");
+  }
+  std::cout << "-I- " << myName << ": Output file will be " << outfilename << std::endl;
+  // ------------------------------------------------------------------------
+
+
+  // -----   Performance profiling   ----------------------------------------
+  // Set to true if you want some minimal performance profiling output
+  bool doPerfProfiling = true;
+  // Define if you want a special path and name for the performance profiling output file
+  std::string perfProfFileName = outpath + filename;
+  perfProfFileName.replace(perfProfFileName.find(".tsa"), 4, ".perf.root");
+  // ------------------------------------------------------------------------
+
+
+  // -----   CbmSetup   -----------------------------------------------------
+  /// Do automatic mapping only if not overridden by user or empty
+  if (defaultSetupName == setupName || "" == setupName) {
+    cbm::mcbm::ToForceLibLoad dummy;  /// Needed to trigger loading of the library as no fct dict in ROOT6 and CLING
+    try {
+      setupName = cbm::mcbm::GetSetupFromRunId(runid);
+    }
+    catch (const std::invalid_argument& e) {
+      std::cout << "Error in mapping from runID to setup name: " << e.what() << std::endl;
+      return;
+    }
+    if (defaultSetupName != setupName) {
+      std::cout << "Automatic setup choice for run " << runid << ": " << setupName << std::endl;
+    }
+  }
+  auto cbmsetup = CbmSetup::Instance();
+  cbmsetup->LoadSetup(setupName.c_str());
+
+  // ------------------------------------------------------------------------
+
+  // -----   UnpackerConfigs   ----------------------------------------------
+
+  // ---- BMON ----
+  std::shared_ptr<CbmBmonUnpackConfig> bmonconfig = nullptr;
+
+  if (!bBmoninTof) {
+    bmonconfig = std::make_shared<CbmBmonUnpackConfig>("", runid);
+    if (bmonconfig) {
+      // bmonconfig->SetDebugState();
+      bmonconfig->SetDoWriteOutput();
+      // bmonconfig->SetDoWriteOptOutA("CbmBmonErrors");
+      std::string parfilesbasepathBmon = Form("%s/macro/beamtime/mcbm2022/", srcDir.Data());
+      if (2610 < runid) {
+        parfilesbasepathBmon = Form("%s/macro/beamtime/mcbm2023/", srcDir.Data());
+      }
+      if (2724 <= runid) {
+        parfilesbasepathBmon = Form("%s/macro/beamtime/mcbm2024/", srcDir.Data());
+      }
+      bmonconfig->SetParFilesBasePath(parfilesbasepathBmon);
+      bmonconfig->SetParFileName("mBmonCriPar.par");
+      bmonconfig->SetSystemTimeOffset(-1220);  // [ns] value to be updated
+      if (2160 <= runid) {
+        bmonconfig->SetSystemTimeOffset(-80);  // [ns] value to be updated
+      }
+      if (2350 <= runid) {
+        bmonconfig->SetSystemTimeOffset(0);  // [ns] value to be updated
+      }
+      /// Enable Monitor plots
+      // bmonconfig->SetMonitor(GetTofMonitor(outfilename, true));
+      // if (2337 <= runid) {
+      //   bmonconfig->GetMonitor()->SetSpillThreshold(250);
+      //   bmonconfig->GetMonitor()->SetSpillThresholdNonPulser(100);
+      // }
+    }
+  }
+  // -------------
+
+  // ---- STS ----
+  std::shared_ptr<CbmStsUnpackConfig> stsconfig = nullptr;
+
+  stsconfig = std::make_shared<CbmStsUnpackConfig>(std::string(setupName), runid);
+  if (stsconfig) {
+    // stsconfig->SetDebugState();
+    stsconfig->SetDoWriteOutput();
+    stsconfig->SetDoWriteOptOutA("StsDigiPulser");
+    std::string parfilesbasepathSts = Form("%s/macro/beamtime/mcbm2021/", srcDir.Data());
+    if (2060 <= runid) {
+      /// Starting to readout the U3 since 10/03/2022 Carbon run
+      parfilesbasepathSts = Form("%s/macro/beamtime/mcbm2022/", srcDir.Data());
+    }
+    stsconfig->SetParFilesBasePath(parfilesbasepathSts);
+    /// Enable duplicates rejection, Ignores the ADC for duplicates check
+    stsconfig->SetDuplicatesRejection(true, true);
+    /// Enable Monitor plots
+    //stsconfig->SetMonitor(GetStsMonitor(outfilename, true));
+    stsconfig->SetSystemTimeOffset(-2221);  // [ns] value to be updated
+    if (2160 <= runid) {
+      stsconfig->SetSystemTimeOffset(-1075);  // [ns] value to be updated
+    }
+    if (2350 <= runid) {
+      stsconfig->SetSystemTimeOffset(-970);  // [ns] value to be updated
+    }
+
+    // Noise removal
+    stsconfig->SetMinAdcCut(1, 1);  // ADC cut to Station 0 Ladder 0 Module 0
+    stsconfig->SetMinAdcCut(2, 1);  // ADC cut to Station 0 Ladder 0 Module 0
+    stsconfig->SetMinAdcCut(3, 1);  // ADC cut to Station 0 Ladder 0 Module 1
+    stsconfig->SetMinAdcCut(4, 1);  // ADC cut to Station 0 Ladder 0 Module 1
+
+    // Masking noisy channels
+    std::ifstream mask_channels(Form("%s/sts_mask_channels.par", parfilesbasepathSts.data()));
+    int feb_idx, feb_chn;
+    while (mask_channels >> feb_idx >> feb_chn) {
+      stsconfig->MaskNoisyChannel(feb_idx, feb_chn);
+    }
+
+    // Time Walk correction
+    std::map<uint32_t, CbmStsParModule> moduleWalkMap;
+    auto parAsic = new CbmStsParAsic(128, 31, 31., 1., 5., 800., 1000., 3.9789e-3);  // Default ASIC parameters
+    auto parMod  = new CbmStsParModule(2048, 128);  // Generic STS module parameter object
+
+    std::array<double, 31> tw_map = {};  // Default time walk map
+    parAsic->SetWalkCoef(tw_map);        // Set generic ASIC par with no time walk correction
+    parMod->SetAllAsics(*parAsic);       // Set generic module ASIC as default ASIC parameter configuration
+
+    moduleWalkMap[0x10107C02] = CbmStsParModule(*parMod);  // Make a copy for storage
+    moduleWalkMap[0x101FFC02] = CbmStsParModule(*parMod);  // Make a copy for storage
+
+    /// To be replaced by a storage in a new parameter class later
+    int sensor, asic;
+    std::ifstream asicTimeWalk_par(Form("%s/mStsAsicTimeWalk.par", parfilesbasepathSts.data()));
+    while (asicTimeWalk_par >> std::hex >> sensor >> std::dec >> asic) {  // Read module and ASIC
+      LOG(debug) << Form("[STS] Reading %x %u", sensor, asic);
+      for (int adc = 0; adc < 31; adc++) {  // Read time offset by ADC
+        asicTimeWalk_par >> tw_map[adc];    // Set time walk map offset value for given ADC
+        if (std::fabs(tw_map[adc]) > 100) {
+          LOG(warning)
+            << "[STS] Very large time walk parameter";  // Large offset values could indicate par file malformation
+        }
+      }                              // end ASIC idx loop
+      parAsic->SetWalkCoef(tw_map);  // Set time walk map for the ASIC par obj
+
+      if (!moduleWalkMap.count(sensor)) {                  // No parameters obj for given module
+        moduleWalkMap[sensor] = CbmStsParModule(*parMod);  // Create CbmStsParModule obj for the loaded module
+      }
+      moduleWalkMap[sensor].SetAsic(asic, *parAsic);  // Set ASIC parameter
+
+      LOG(debug) << Form("\n[STS] Time Walk parameters loaded for: module %x, ASIC %u\n", sensor, asic);
+    }
+
+    stsconfig->SetWalkMap(moduleWalkMap);
+    moduleWalkMap.clear();
+    delete parMod;
+    delete parAsic;
+  }
+  // -------------
+
+  // ---- MUCH ----
+  std::shared_ptr<CbmMuchUnpackConfig> muchconfig = nullptr;
+
+  muchconfig = std::make_shared<CbmMuchUnpackConfig>(std::string(setupName), runid);
+  if (muchconfig) {
+    // muchconfig->SetDebugState();
+    muchconfig->SetDoWriteOutput();
+    muchconfig->SetDoWriteOptOutA("MuchDigiPulser");
+    std::string parfilesbasepathMuch = Form("%s/macro/beamtime/mcbm2022/", srcDir.Data());
+    muchconfig->SetParFilesBasePath(parfilesbasepathMuch);
+    if (2060 <= runid && runid <= 2162) {
+      /// Starting to use CRI Based MUCH setup with 2GEM and 1 RPC since 09/03/2022 Carbon run
+      muchconfig->SetParFileName("mMuchParUpto26032022.par");
+    }
+    else if (2163 <= runid && runid <= 2291) {
+      ///
+      muchconfig->SetParFileName("mMuchParUpto03042022.par");
+    }
+    else if (2311 <= runid && runid <= 2315) {
+      ///
+      muchconfig->SetParFileName("mMuchParUpto10042022.par");
+    }
+    else if (2316 <= runid && runid <= 2366) {
+      ///
+      muchconfig->SetParFileName("mMuchParUpto23052022.par");
+    }
+    else if (2367 <= runid && runid <= 2397) {
+      /// Starting to use GEM 2 moved to CRI 0 on 24/05/2022
+      muchconfig->SetParFileName("mMuchParUpto26052022.par");
+    }
+    else {
+      /// Default file for all other runs (including 06/2022 Gold runs)
+      muchconfig->SetParFileName("mMuchPar.par");
+    }
+
+
+    /// Enable duplicates rejection, Ignores the ADC for duplicates check
+    muchconfig->SetDuplicatesRejection(true, true);
+    /// Enable Monitor plots
+    //muchconfig->SetMonitor(GetMuchMonitor(outfilename, true));
+    muchconfig->SetSystemTimeOffset(-2221);  // [ns] value to be updated
+    if (2160 <= runid) {
+      muchconfig->SetSystemTimeOffset(-1020);  // [ns] value to be updated
+    }
+    if (2350 <= runid) {
+      muchconfig->SetSystemTimeOffset(-980);  // [ns] value to be updated
+    }
+
+    // muchconfig->SetMinAdcCut(1, 1);
+
+    // muchconfig->MaskNoisyChannel(3, 56);
+    //List of noisy channels *.txt file based on CbmMuchAddress is placed at the same location as par files
+    //
+    std::string NoisyChannelFilePath = parfilesbasepathMuch + "mMuChNoisyChannelMarch2022.txt";
+    muchconfig->SetNoisyChannelFile(NoisyChannelFilePath);
+  }
+  // -------------
+
+  // ---- TRD ----
+  std::shared_ptr<CbmTrdUnpackConfig> trd1Dconfig = nullptr;
+
+  TString trdsetuptag = "";
+  cbmsetup->GetGeoTag(ECbmModuleId::kTrd, trdsetuptag);
+  // trd1Dconfig = std::make_shared<CbmTrdUnpackConfig>(trdsetuptag.Data(), runid);
+  trd1Dconfig = std::make_shared<CbmTrdUnpackConfig>(trdsetuptag.Data());
+  if (2724 <= runid) {
+    /// mCBM 2024
+    trdsetuptag = "v24b_mcbm";
+  }
+  if (trd1Dconfig) {
+    trd1Dconfig->SetDoWriteOutput();
+    // Activate the line below to write Trd1D digis to a separate "TrdSpadicDigi" branch. Can be used to separate between Fasp and Spadic digis
+    // trd1Dconfig->SetOutputBranchName("TrdSpadicDigi");
+    // trd1Dconfig->SetDoWriteOptOutA(CbmTrdRawMessageSpadic::GetBranchName());
+    // trd1Dconfig->SetDoWriteOptOutB("SpadicInfoMessages"); // SpadicInfoMessages
+
+    std::string parfilesbasepathTrd = Form("%s/parameters/trd", srcDir.Data());
+    trd1Dconfig->SetParFilesBasePath(parfilesbasepathTrd);
+    // trd1Dconfig->SetMonitor(GetTrdMonitor(outfilename));
+    // Get the spadic configuration true = avg baseline active / false plain sample 0
+    trd1Dconfig->SetSpadicObject(GetTrdSpadic(true));
+    trd1Dconfig->SetSystemTimeOffset(0);  // [ns] value to be updated
+    if (2160 <= runid) {
+      trd1Dconfig->SetSystemTimeOffset(1140);  // [ns] value to be updated
+    }
+    if (2350 <= runid) {
+      trd1Dconfig->SetSystemTimeOffset(1300);  // [ns] value to be updated
+    }
+  }
+  // -------------
+
+  // ---- TRDFASP2D ----
+  std::shared_ptr<CbmTrdUnpackFaspConfig> trdfasp2dconfig = nullptr;
+
+  trdfasp2dconfig = std::make_shared<CbmTrdUnpackFaspConfig>(trdsetuptag.Data());
+  if (trdfasp2dconfig) {
+    // trdfasp2dconfig->SetDebugState();
+    trdfasp2dconfig->SetDoWriteOutput();
+    // Activate the line below to write Trd1D digis to a separate "TrdFaspDigi" branch. Can be used to separate between Fasp and Spadic digis
+    // trdfasp2dconfig->SetOutputBranchName("TrdFaspDigi");
+    //   uint8_t map[NFASPMOD];
+    //   uint16_t crob_map[NCROBMOD];
+    //   for (uint32_t i(0); i < NFASPMOD; i++)
+    //     map[i] = i;
+    //   if (runid <= 1588) {
+    //     const size_t nfasps = 12;
+    //     uint8_t map21[] = {9, 2, 3, 11, 10, 7, 8, 0, 1, 4, 6, 5};
+    //     for (uint32_t i(0); i < nfasps; i++)
+    //       map[i] = map21[i];
+    //     uint16_t crob_map21[] = {0x00f0, 0, 0, 0, 0};
+    //     for (uint32_t i(0); i < NCROBMOD; i++)
+    //       crob_map[i] = crob_map21[i];
+    //   }
+    //   else if (runid >= 2335) {
+    //     uint16_t crob_map22[] = {0xffc2, 0xffc5, 0xffc1, 0, 0};
+    //     for (uint32_t i(0); i < NCROBMOD; i++)
+    //       crob_map[i] = crob_map22[i];
+    //   }
+    //   trdfasp2dconfig->SetCrobMapping(5, crob_map);
+    std::string parfilesbasepathTrdfasp2d = Form("%s/parameters/trd", srcDir.Data());
+    trdfasp2dconfig->SetParFilesBasePath(parfilesbasepathTrdfasp2d);
+    trdfasp2dconfig->SetSystemTimeOffset(-1800);  // [ns] value to be updated
+    if (2160 <= runid) {
+      trdfasp2dconfig->SetSystemTimeOffset(-570);  // [ns] value to be updated
+    }
+    if (2350 <= runid) {
+      trdfasp2dconfig->SetSystemTimeOffset(-510);  // [ns] value to be updated
+    }
+    trdfasp2dconfig->SetMonitor(dynamic_pointer_cast<CbmTrdUnpackFaspMonitor>(GetTrdMonitor(outfilename, 1)));
+  }
+  // -------------
+
+  // ---- TOF ----
+  std::shared_ptr<CbmTofUnpackConfig> tofconfig = nullptr;
+
+  tofconfig = std::make_shared<CbmTofUnpackConfig>("", runid);
+  if (tofconfig) {
+    // tofconfig->SetDebugState();
+    tofconfig->SetDoWriteOutput();
+    // tofconfig->SetDoWriteOptOutA("CbmTofErrors");
+    std::string parfilesbasepathTof = Form("%s/macro/beamtime/mcbm2021/", srcDir.Data());
+    std::string parFileNameTof      = "mTofCriPar.par";
+    if (2060 <= runid) {
+      /// Additional modules added just before the 10/03/2022 Carbon run
+      parfilesbasepathTof = Form("%s/macro/beamtime/mcbm2022/", srcDir.Data());
+      /// Setup changed multiple times between the 2022 carbon and uranium runs
+      if (runid <= 2065) {
+        /// Carbon runs: 2060 - 2065
+        parFileNameTof = "mTofCriParCarbon.par";
+      }
+      else if (2150 <= runid && runid <= 2160) {
+        /// Iron runs: 2150 - 2160
+        parFileNameTof = "mTofCriParIron.par";
+        if (bBmoninTof) {
+          /// Map the BMon components in the TOF par file
+          parFileNameTof = "mTofCriParIron_withBmon.par";
+        }
+      }
+      else if (2176 <= runid && runid <= 2310) {
+        /// Uranium runs: 2176 - 2310
+        parFileNameTof = "mTofCriParUranium.par";
+      }
+      else if (2335 <= runid && runid <= 2497) {
+        /// Nickel runs: 2335 - 2397
+        /// Gold runs: 2400 - 2497
+        parFileNameTof = "mTofCriParNickel.par";
+        if (bBmoninTof) {
+          /// Map the BMon components in the TOF par file
+          parFileNameTof = "mTofCriParNickel_withBmon.par";
+        }
+      }
+      else {
+        parFileNameTof = "mTofCriPar.par";
+      }
+    }
+    if (2724 <= runid) {
+      /// mCBM 2024
+      parfilesbasepathTof = Form("%s/macro/beamtime/mcbm2024/", srcDir.Data());
+    }
+
+    tofconfig->SetParFilesBasePath(parfilesbasepathTof);
+    tofconfig->SetParFileName(parFileNameTof);
+    tofconfig->SetSystemTimeOffset(-1220);  // [ns] value to be updated
+    if (2160 <= runid) {
+      tofconfig->SetSystemTimeOffset(0);  // [ns] value to be updated
+    }
+    if (2350 <= runid) {
+      tofconfig->SetSystemTimeOffset(40);  // [ns] value to be updated
+    }
+    if (runid <= 1659) {
+      /// Switch ON the -4 offset in epoch count (hack for Spring-Summer 2021)
+      tofconfig->SetFlagEpochCountHack2021();
+    }
+    /// Enable Monitor plots
+    // tofconfig->SetMonitor(GetTofMonitor(outfilename, false));
+  }
+  // -------------
+
+  // ---- RICH ----
+  std::shared_ptr<CbmRichUnpackConfig> richconfig = nullptr;
+
+  richconfig = std::make_shared<CbmRichUnpackConfig>("", runid);
+  if (richconfig) {
+    if (1904 < runid) {
+      /// Switch to new unpacking algo starting from first combined cosmics run in 2022
+      richconfig->SetUnpackerVersion(CbmRichUnpackerVersion::v03);
+      richconfig->SetMonitor(GetRichMonitor(outfilename, true));
+    }
+
+    richconfig->DoTotOffsetCorrection();  // correct ToT offset
+    richconfig->SetDebugState();
+    richconfig->SetDoWriteOutput();
+    std::string parfilesbasepathRich = Form("%s/macro/beamtime/mcbm2024/", srcDir.Data());
+    richconfig->SetParFilesBasePath(parfilesbasepathRich);
+    richconfig->SetSystemTimeOffset(256000 - 1200);  // [ns] 1 MS and additional correction
+    if (1904 < runid) richconfig->SetSystemTimeOffset(-1200);
+    if (2160 <= runid) {
+      richconfig->SetSystemTimeOffset(50);  // [ns] value to be updated
+    }
+    if (2350 <= runid) {
+      richconfig->SetSystemTimeOffset(100);  // [ns] value to be updated
+    }
+    if (runid == 1588) richconfig->MaskDiRICH(0x7150);
+  }
+  // -------------
+
+  // ---- PSD ----
+  std::shared_ptr<CbmPsdUnpackConfig> psdconfig = nullptr;
+
+  psdconfig = std::make_shared<CbmPsdUnpackConfig>("", runid);
+  if (psdconfig) {
+    // psdconfig->SetDebugState();
+    psdconfig->SetDoWriteOutput();
+    // psdconfig->SetDoWriteOptOutA("CbmPsdDsp");
+    std::string parfilesbasepathPsd = Form("%s/macro/beamtime/mcbm2021/", srcDir.Data());
+    psdconfig->SetParFilesBasePath(parfilesbasepathPsd);
+    psdconfig->SetSystemTimeOffset(0);  // [ns] value to be updated
+  }
+  // -------------
+
+
+  // ------------------------------------------------------------------------
+
+  // In general, the following parts need not be touched
+  // ========================================================================
+
+  // -----   Timer   --------------------------------------------------------
+  TStopwatch timer;
+  timer.Start();
+  // ------------------------------------------------------------------------
+
+  // -----   CbmSourceTsArchive   -------------------------------------------
+  std::unique_ptr<CbmSourceTsArchive> source = std::unique_ptr<CbmSourceTsArchive>(new CbmSourceTsArchive(infile));
+  auto unpack                                = source->GetRecoUnpack();
+  unpack->SetDoPerfProfiling(doPerfProfiling);
+  /// Uncomment following line to enable extra hists about unpacker and detectors performances (I/O, I/O shares, expan.)
+  // unpack->SetDoPerfProfilingPerTs(doPerfProfiling);
+  /// Uncomment following two line to enable the InfluxDb+Grafana based publication of unpacker performances
+  // unpack->SetPublishProfMoni();
+  unpack->SetOutputFilename(perfProfFileName);
+  // Enable full time sorting instead sorting per FLIM link
+  unpack->SetTimeSorting(true);
+
+  // if (bmonconfig) unpack->SetUnpackConfig(bmonconfig);
+  // if (stsconfig) unpack->SetUnpackConfig(stsconfig);
+  // if (muchconfig) unpack->SetUnpackConfig(muchconfig);
+  // if (trd1Dconfig) unpack->SetUnpackConfig(trd1Dconfig);
+  // if (trdfasp2dconfig) unpack->SetUnpackConfig(trdfasp2dconfig);
+  // if (tofconfig) unpack->SetUnpackConfig(tofconfig);
+  if (richconfig) unpack->SetUnpackConfig(richconfig);
+  // if (psdconfig) unpack->SetUnpackConfig(psdconfig);
+  // ------------------------------------------------------------------------
+
+
+  // -----   FairRunAna   ---------------------------------------------------
+  auto run  = new FairRunOnline(source.release());
+  auto sink = new FairRootFileSink(outfilename.data());
+  run->SetSink(sink);
+  auto eventheader = new CbmTsEventHeader();
+  run->SetRunId(runid);
+  run->SetEventHeader(eventheader);
+  // ------------------------------------------------------------------------
+
+
+  // -----   Logger settings   ----------------------------------------------
+  FairLogger::GetLogger()->SetLogScreenLevel(logLevel.Data());
+  FairLogger::GetLogger()->SetLogVerbosityLevel(logVerbosity.Data());
+  // ------------------------------------------------------------------------
+
+
+  // -----   Run initialisation   -------------------------------------------
+  std::cout << std::endl;
+  std::cout << "-I- " << myName << ": Initialise run" << std::endl;
+  run->Init();
+  // ------------------------------------------------------------------------
+
+
+  // -----   Start run   ----------------------------------------------------
+  std::cout << std::endl << std::endl;
+  std::cout << "-I- " << myName << ": Starting run" << std::endl;
+  if (nevents < 0)
+    run->Run(-1, 0);
+  else
+    run->Run(0, nevents);
+  // ------------------------------------------------------------------------
+
+
+  // -----   Finish   -------------------------------------------------------
+  timer.Stop();
+  std::cout << "Macro finished successfully." << std::endl;
+  std::cout << "After CpuTime = " << timer.CpuTime() << " s RealTime = " << timer.RealTime() << " s." << std::endl;
+  // ------------------------------------------------------------------------
+
+  // --   Release all shared pointers to config before ROOT destroys things -
+  // => We need to destroy things by hand because run->Finish calls (through the FairRootManager) Source->Close which
+  //    does call the Source destructor, so due to share pointer things stay alive until out of macro scope...
+  run->SetSource(nullptr);
+  delete run;
+
+  bmonconfig.reset();
+  stsconfig.reset();
+  muchconfig.reset();
+  trd1Dconfig.reset();
+  trdfasp2dconfig.reset();
+  tofconfig.reset();
+  richconfig.reset();
+  psdconfig.reset();
+  // ------------------------------------------------------------------------
+
+}  // End of main macro function
+
+
+/**
+ * @brief Get the Trd Monitor. Extra function to keep default macro part more silent.
+ * @return std::shared_ptr<CbmTrdUnpackMonitor>
+*/
+std::shared_ptr<CbmTrdUnpackMonitor> GetTrdMonitor(std::string treefilename, bool fasp = false)
+{
+  // -----   Output filename and path   -------------------------------------
+  std::string outpath  = "";
+  std::string filename = "";
+  auto filenamepos     = treefilename.find_last_of("/");
+  if (filenamepos != treefilename.npos) {
+    outpath  = treefilename.substr(0, filenamepos);
+    filename = treefilename.substr(filenamepos++);
+  }
+  if (outpath.empty()) outpath = gSystem->GetWorkingDirectory();
+  std::string mydir = "/qa";
+  outpath += mydir;
+
+  auto currentdir = gSystem->GetWorkingDirectory();
+
+  if (!gSystem->cd(outpath.data()))
+    gSystem->MakeDirectory(outpath.data());
+  else
+    gSystem->cd(currentdir.data());
+
+  std::string outfilename = outpath + filename;
+  auto filetypepos        = outfilename.find(".digi.root");
+  if (filetypepos != outfilename.npos)
+    outfilename.replace(filetypepos, 10, ".mon.trd.root");
+  else
+    outfilename += ".mon.trd.root";
+  // ------------------------------------------------------------------------
+
+  std::vector<CbmTrdUnpackMonitor::eDigiHistos> digihistovec = {
+    CbmTrdUnpackMonitor::eDigiHistos::kMap,        CbmTrdUnpackMonitor::eDigiHistos::kMap_St,
+    CbmTrdUnpackMonitor::eDigiHistos::kMap_Nt,     CbmTrdUnpackMonitor::eDigiHistos::kCharge,
+    CbmTrdUnpackMonitor::eDigiHistos::kCharge_St,  CbmTrdUnpackMonitor::eDigiHistos::kCharge_Nt,
+    CbmTrdUnpackMonitor::eDigiHistos::kChannel,    CbmTrdUnpackMonitor::eDigiHistos::kChannel_St,
+    CbmTrdUnpackMonitor::eDigiHistos::kChannel_Nt, CbmTrdUnpackMonitor::eDigiHistos::kTriggerType,
+    CbmTrdUnpackMonitor::eDigiHistos::kDigiDeltaT};
+
+  std::vector<CbmTrdUnpackMonitor::eRawHistos> rawhistovec = {
+    CbmTrdUnpackMonitor::eRawHistos::kSignalshape,      CbmTrdUnpackMonitor::eRawHistos::kSignalshape_St,
+    CbmTrdUnpackMonitor::eRawHistos::kSignalshape_Nt,   CbmTrdUnpackMonitor::eRawHistos::kElinkId,
+    CbmTrdUnpackMonitor::eRawHistos::kSampleDistStdDev, CbmTrdUnpackMonitor::eRawHistos::kSample0perChannel,
+    CbmTrdUnpackMonitor::eRawHistos::kHitType};
+
+  std::vector<CbmTrdUnpackMonitor::eOtherHistos> otherhistovec = {CbmTrdUnpackMonitor::eOtherHistos::kSpadic_Info_Types,
+                                                                  CbmTrdUnpackMonitor::eOtherHistos::kMs_Flags};
+  std::shared_ptr<CbmTrdUnpackMonitor> monitor(nullptr);
+  if (!fasp) {  // SPADIC monitor
+    monitor = std::make_shared<CbmTrdUnpackMonitor>();
+    monitor->SetActiveHistos(digihistovec);
+    monitor->SetActiveHistos(rawhistovec);
+    monitor->SetActiveHistos(otherhistovec);
+    monitor->SetWriteToFile(outfilename.data());
+  }
+  else {  // FASP monitoring settings
+    monitor = std::make_shared<CbmTrdUnpackFaspMonitor>();
+    monitor->SetActiveHistos(digihistovec);
+    monitor->SetWriteToFile(outfilename.data());
+  }
+  return monitor;
+}
+
+
+/**
+ * @brief Get the Trd Spadic
+ * @return std::shared_ptr<CbmTrdSpadic>
+*/
+std::shared_ptr<CbmTrdSpadic> GetTrdSpadic(bool useAvgBaseline)
+{
+  auto spadic = std::make_shared<CbmTrdSpadic>();
+  spadic->SetUseBaselineAverage(useAvgBaseline);
+  spadic->SetMaxAdcToEnergyCal(1.0);
+
+  return spadic;
+}
+
+/**
+ * @brief Get the Sts Monitor. Extra function to keep default macro part more silent.
+ * @return std::shared_ptr<CbmStsUnpackMonitor>
+*/
+std::shared_ptr<CbmStsUnpackMonitor> GetStsMonitor(std::string treefilename, bool bDebugMode = false)
+{
+  // -----   Output filename and path   -------------------------------------
+  std::string outpath  = "";
+  std::string filename = "";
+  auto filenamepos     = treefilename.find_last_of("/");
+  if (filenamepos != treefilename.npos) {
+    outpath  = treefilename.substr(0, filenamepos);
+    filename = treefilename.substr(filenamepos++);
+  }
+  if (outpath.empty()) outpath = gSystem->GetWorkingDirectory();
+  //std::string mydir = "/qa";
+  //outpath += mydir;
+
+  auto currentdir = gSystem->GetWorkingDirectory();
+
+  if (!gSystem->cd(outpath.data()))
+    gSystem->MakeDirectory(outpath.data());
+  else
+    gSystem->cd(currentdir.data());
+
+  std::string outfilename = outpath + filename;
+  auto filetypepos        = outfilename.find(".digi.root");
+  if (filetypepos != outfilename.npos)
+    outfilename.replace(filetypepos, 10, ".mon.sts.root");
+  else
+    outfilename += ".mon.sts.root";
+  // ------------------------------------------------------------------------
+
+  auto monitor = std::make_shared<CbmStsUnpackMonitor>();
+  monitor->SetHistoFileName(outfilename);
+  monitor->SetDebugMode(bDebugMode);
+  return monitor;
+}
+
+/**
+ * @brief Get the Much Monitor. Extra function to keep default macro part more silent.
+ * @return std::shared_ptr<CbmMuchUnpackMonitor>
+*/
+std::shared_ptr<CbmMuchUnpackMonitor> GetMuchMonitor(std::string treefilename, bool bDebugMode = false)
+{
+  // -----   Output filename and path   -------------------------------------
+  std::string outpath  = "";
+  std::string filename = "";
+  auto filenamepos     = treefilename.find_last_of("/");
+  if (filenamepos != treefilename.npos) {
+    outpath  = treefilename.substr(0, filenamepos);
+    filename = treefilename.substr(filenamepos++);
+  }
+  if (outpath.empty()) outpath = gSystem->GetWorkingDirectory();
+
+  auto currentdir = gSystem->GetWorkingDirectory();
+
+  if (!gSystem->cd(outpath.data()))
+    gSystem->MakeDirectory(outpath.data());
+  else
+    gSystem->cd(currentdir.data());
+
+  std::string outfilename = outpath + filename;
+  auto filetypepos        = outfilename.find(".digi.root");
+  if (filetypepos != outfilename.npos)
+    outfilename.replace(filetypepos, 10, ".mon.much.root");
+  else
+    outfilename += ".mon.much.root";
+  // ------------------------------------------------------------------------
+
+  auto monitor = std::make_shared<CbmMuchUnpackMonitor>();
+  monitor->SetHistoFileName(outfilename);
+  monitor->SetDebugMode(bDebugMode);
+  return monitor;
+}
+
+/**
+ * @brief Get the Rich Monitor. Extra function to keep default macro part more silent.
+ * @return std::shared_ptr<CbmRichUnpackMonitor>
+*/
+std::shared_ptr<CbmRichUnpackMonitor> GetRichMonitor(std::string treefilename, bool bDebugMode = false)
+{
+  // -----   Output filename and path   -------------------------------------
+  std::string outpath  = "";
+  std::string filename = "";
+  auto filenamepos     = treefilename.find_last_of("/");
+  if (filenamepos != treefilename.npos) {
+    outpath  = treefilename.substr(0, filenamepos);
+    filename = treefilename.substr(filenamepos++);
+  }
+  if (outpath.empty()) outpath = gSystem->GetWorkingDirectory();
+
+  auto currentdir = gSystem->GetWorkingDirectory();
+
+  if (!gSystem->cd(outpath.data()))
+    gSystem->MakeDirectory(outpath.data());
+  else
+    gSystem->cd(currentdir.data());
+
+  std::string outfilename = outpath + filename;
+  auto filetypepos        = outfilename.find(".digi.root");
+  if (filetypepos != outfilename.npos)
+    outfilename.replace(filetypepos, 10, ".mon.rich.root");
+  else
+    outfilename += ".mon.rich.root";
+  // ------------------------------------------------------------------------
+
+  auto monitor = std::make_shared<CbmRichUnpackMonitor>();
+  monitor->SetHistoFileName(outfilename);
+  monitor->SetDebugMode(bDebugMode);
+
+  return monitor;
+}
+
+/**
+ * @brief Get the Tof Monitor. Extra function to keep default macro part more silent.
+ * @return std::shared_ptr<CbmTofUnpackMonitor>
+*/
+std::shared_ptr<CbmTofUnpackMonitor> GetTofMonitor(std::string treefilename, bool bBmonMode = false)
+{
+  // -----   Output filename and path   -------------------------------------
+  std::string outpath  = "";
+  std::string filename = "";
+  auto filenamepos     = treefilename.find_last_of("/");
+  if (filenamepos != treefilename.npos) {
+    outpath  = treefilename.substr(0, filenamepos);
+    filename = treefilename.substr(filenamepos++);
+  }
+  if (outpath.empty()) outpath = gSystem->GetWorkingDirectory();
+  //std::string mydir = "/qa";
+  //outpath += mydir;
+
+  auto currentdir = gSystem->GetWorkingDirectory();
+
+  if (!gSystem->cd(outpath.data()))
+    gSystem->MakeDirectory(outpath.data());
+  else
+    gSystem->cd(currentdir.data());
+
+  std::string sSystemType = ".mon.tof.root";
+  if (bBmonMode) {
+    //
+    sSystemType = ".mon.bmon.root";
+  }
+
+  std::string outfilename = outpath + filename;
+  auto filetypepos        = outfilename.find(".digi.root");
+  if (filetypepos != outfilename.npos)
+    outfilename.replace(filetypepos, 10, sSystemType);
+  else
+    outfilename += sSystemType;
+  // ------------------------------------------------------------------------
+
+  auto monitor = std::make_shared<CbmTofUnpackMonitor>();
+  monitor->SetHistoFileName(outfilename);
+  monitor->SetBmonMode(bBmonMode);
+  return monitor;
+}
+
+void run_unpack_tsa_rich_only(std::string infile = "test.tsa", UInt_t runid = 0,
+                              std::string setupName = defaultSetupName, std::int32_t nevents = -1,
+                              bool bBmoninTof = false, std::string outpath = "data/")
+{
+  std::vector<std::string> vInFile = {infile};
+  return run_unpack_tsa(vInFile, runid, setupName, nevents, bBmoninTof, outpath);
+}