From 0146c55f2b4334355a6119b0622ca382b829f58e Mon Sep 17 00:00:00 2001
From: Pascal Raisig <praisig@ikf.uni-frankfurt.de>
Date: Wed, 14 Jul 2021 21:15:13 +0200
Subject: [PATCH] Include all other mCBM2021 subsystems to the CbmRecoUnpack
 scheme.

Except for Psd the classes are empty dummies.
---
 core/data/CMakeLists.txt                      |   2 +
 core/data/CbmTsEventHeader.cxx                |   9 +
 core/data/CbmTsEventHeader.h                  |  65 ++++
 core/data/DataLinkDef.h                       |   1 +
 macro/run/run_unpack_tsa.C                    |  13 +-
 reco/base/CbmRecoUnpackAlgo.tmpl              |   8 +-
 reco/base/CbmRecoUnpackConfig.tmpl            |   4 +-
 reco/detectors/psd/CMakeLists.txt             |  16 +-
 reco/detectors/psd/CbmPsdRecoLinkDef.h        |   3 +
 .../detectors/psd/unpack/CbmPsdUnpackAlgo.cxx | 335 ++++++++++++++++++
 reco/detectors/psd/unpack/CbmPsdUnpackAlgo.h  | 177 +++++++++
 .../psd/unpack/CbmPsdUnpackConfig.cxx         |  64 ++++
 .../detectors/psd/unpack/CbmPsdUnpackConfig.h |  89 +++++
 reco/detectors/rich/CMakeLists.txt            |   9 +-
 reco/detectors/rich/CbmRichRecoLinkDef.h      |   5 +
 .../rich/unpack/CbmRichUnpackAlgo.cxx         |  20 ++
 .../detectors/rich/unpack/CbmRichUnpackAlgo.h | 124 +++++++
 .../rich/unpack/CbmRichUnpackConfig.cxx       |  64 ++++
 .../rich/unpack/CbmRichUnpackConfig.h         |  89 +++++
 reco/detectors/sts/CMakeLists.txt             |   6 +
 reco/detectors/sts/CbmRecoStsLinkDef.h        |   3 +
 .../detectors/sts/unpack/CbmStsUnpackAlgo.cxx |  20 ++
 reco/detectors/sts/unpack/CbmStsUnpackAlgo.h  | 124 +++++++
 .../sts/unpack/CbmStsUnpackConfig.cxx         |  64 ++++
 .../detectors/sts/unpack/CbmStsUnpackConfig.h |  89 +++++
 .../trd/unpack/CbmTrdUnpackAlgo2D.cxx         | 154 ++++++++
 .../detectors/trd/unpack/CbmTrdUnpackAlgo2D.h | 236 ++++++++++++
 .../unpack/CbmTrdUnpackAlgoLegacy2020R.cxx    |   3 +
 .../trd/unpack/CbmTrdUnpackAlgoR.cxx          |   6 +
 .../trd/unpack/CbmTrdUnpackConfig2D.cxx       |  79 +++++
 .../trd/unpack/CbmTrdUnpackConfig2D.h         | 122 +++++++
 .../trd/unpack/CbmTrdUnpackMonitor.cxx        |  78 ++--
 .../trd/unpack/CbmTrdUnpackMonitor.h          |  91 ++++-
 reco/steer/CMakeLists.txt                     |  18 +-
 reco/steer/CbmRecoUnpack.cxx                  |  76 +++-
 reco/steer/CbmRecoUnpack.h                    |  98 +++--
 36 files changed, 2279 insertions(+), 85 deletions(-)
 create mode 100644 core/data/CbmTsEventHeader.cxx
 create mode 100644 core/data/CbmTsEventHeader.h
 create mode 100644 reco/detectors/psd/unpack/CbmPsdUnpackAlgo.cxx
 create mode 100644 reco/detectors/psd/unpack/CbmPsdUnpackAlgo.h
 create mode 100644 reco/detectors/psd/unpack/CbmPsdUnpackConfig.cxx
 create mode 100644 reco/detectors/psd/unpack/CbmPsdUnpackConfig.h
 create mode 100644 reco/detectors/rich/unpack/CbmRichUnpackAlgo.cxx
 create mode 100644 reco/detectors/rich/unpack/CbmRichUnpackAlgo.h
 create mode 100644 reco/detectors/rich/unpack/CbmRichUnpackConfig.cxx
 create mode 100644 reco/detectors/rich/unpack/CbmRichUnpackConfig.h
 create mode 100644 reco/detectors/sts/unpack/CbmStsUnpackAlgo.cxx
 create mode 100644 reco/detectors/sts/unpack/CbmStsUnpackAlgo.h
 create mode 100644 reco/detectors/sts/unpack/CbmStsUnpackConfig.cxx
 create mode 100644 reco/detectors/sts/unpack/CbmStsUnpackConfig.h
 create mode 100644 reco/detectors/trd/unpack/CbmTrdUnpackAlgo2D.cxx
 create mode 100644 reco/detectors/trd/unpack/CbmTrdUnpackAlgo2D.h
 create mode 100644 reco/detectors/trd/unpack/CbmTrdUnpackConfig2D.cxx
 create mode 100644 reco/detectors/trd/unpack/CbmTrdUnpackConfig2D.h

diff --git a/core/data/CMakeLists.txt b/core/data/CMakeLists.txt
index 6121c9f1f4..d9937f36f5 100644
--- a/core/data/CMakeLists.txt
+++ b/core/data/CMakeLists.txt
@@ -52,6 +52,8 @@ set(SRCS
   CbmMatch.cxx
   CbmTrackMatchNew.cxx
 
+  CbmTsEventHeader.cxx
+
   base/CbmDigiBranchBase.cxx
   base/CbmDigiContainer.cxx
 
diff --git a/core/data/CbmTsEventHeader.cxx b/core/data/CbmTsEventHeader.cxx
new file mode 100644
index 0000000000..6c9724edf6
--- /dev/null
+++ b/core/data/CbmTsEventHeader.cxx
@@ -0,0 +1,9 @@
+/* Copyright (C) 2010 - 2021 Goethe-University Frankfurt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pascal Raisig */
+
+#include "CbmTsEventHeader.h"
+
+// -----   Constructor   ------------------------------------------------------
+CbmTsEventHeader::CbmTsEventHeader() {}
+// ----------------------------------------------------------------------------
diff --git a/core/data/CbmTsEventHeader.h b/core/data/CbmTsEventHeader.h
new file mode 100644
index 0000000000..16f0e8f535
--- /dev/null
+++ b/core/data/CbmTsEventHeader.h
@@ -0,0 +1,65 @@
+/* Copyright (C) 2010 - 2021 Goethe-University Frankfurt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pascal Raisig */
+
+#ifndef CbmTsEventHeader_H
+#define CbmTsEventHeader_H
+
+#include <FairEventHeader.h>
+
+#include <RtypesCore.h>
+
+#include <cstddef>
+
+class CbmTsEventHeader : public FairEventHeader {
+
+public:
+  /** Default constructor */
+  CbmTsEventHeader();
+
+  /** Default destructor */
+  virtual ~CbmTsEventHeader() {};
+
+  /** Get the Start time of the this Timeslice linked to this event header*/
+  ULong64_t GetTsStartTime() { return fTsStartTime; }
+
+  /** @brief Get the number of digis in this Ts */
+  ULong64_t GetNDigisPsd(ULong64_t) { return fNDigisPsd; }
+  /** @brief Get the number of digis in this Ts */
+  ULong64_t GetNDigisRich(ULong64_t) { return fNDigisRich; }
+  /** @brief Get the number of digis in this Ts */
+  ULong64_t GetNDigisSts(ULong64_t) { return fNDigisSts; }
+  /** @brief Get the number of digis in this Ts */
+  ULong64_t GetNDigisTrd(ULong64_t) { return fNDigisTrd; }
+  /** @brief Get the number of digis in this Ts */
+  ULong64_t GetNDigisTrd2D(ULong64_t) { return fNDigisTrd2D; }
+
+  /** @brief Set the Ts Start Time @param value Start time of the TS */
+  void SetTsStartTime(ULong64_t value) { fTsStartTime = value; }
+
+  /** @brief Set the number of digis in this Ts */
+  void SetNDigisPsd(ULong64_t value) { fNDigisPsd = value; }
+  /** @brief Set the number of digis in this Ts */
+  void SetNDigisRich(ULong64_t value) { fNDigisRich = value; }
+  /** @brief Set the number of digis in this Ts */
+  void SetNDigisSts(ULong64_t value) { fNDigisSts = value; }
+  /** @brief Set the number of digis in this Ts */
+  void SetNDigisTrd(ULong64_t value) { fNDigisTrd = value; }
+  /** @brief Set the number of digis in this Ts */
+  void SetNDigisTrd2D(ULong64_t value) { fNDigisTrd2D = value; }
+
+
+protected:
+  /** Run Id */
+  ULong64_t fTsStartTime = 0;
+
+  ULong64_t fNDigisPsd   = 0;
+  ULong64_t fNDigisRich  = 0;
+  ULong64_t fNDigisSts   = 0;
+  ULong64_t fNDigisTrd   = 0;
+  ULong64_t fNDigisTrd2D = 0;
+
+
+  ClassDef(CbmTsEventHeader, 3)
+};
+#endif
diff --git a/core/data/DataLinkDef.h b/core/data/DataLinkDef.h
index be8b44cb3c..40247ce74e 100644
--- a/core/data/DataLinkDef.h
+++ b/core/data/DataLinkDef.h
@@ -27,6 +27,7 @@
 #pragma link C++ class CbmModuleList;
 #pragma link C++ class CbmErrorMessage + ;
 #pragma link C++ class CbmRawEvent + ;
+#pragma link C++ class CbmTsEventHeader + ;
 
 // ---In base
 #pragma link C++ class CbmDigiBranchBase + ;
diff --git a/macro/run/run_unpack_tsa.C b/macro/run/run_unpack_tsa.C
index b10652c5a7..c30f704ef8 100644
--- a/macro/run/run_unpack_tsa.C
+++ b/macro/run/run_unpack_tsa.C
@@ -108,6 +108,15 @@ void run_unpack_tsa(std::string infile = "test.tsa", UInt_t runid = 0, const cha
 
   // -----   UnpackerConfigs   ----------------------------------------------
 
+  // ---- PSD ----
+  auto psdconfig = std::make_shared<CbmPsdUnpackConfig>("", runid);
+  // psdconfig->SetDebugState();
+  psdconfig->SetDoWriteOutput();
+  // psdconfig->SetDoWriteOptOutA("CbmPsdDsp");
+  std::string parfilesbasepath = Form("%s/macro/beamtime/mcbm2021/", srcDir.Data());
+  psdconfig->SetParFilesBasePath("");
+  // -------------
+
   // ---- TRD ----
   TString trdsetuptag = "";
   cbmsetup->GetGeoTag(ECbmModuleId::kTrd, trdsetuptag);
@@ -141,6 +150,7 @@ void run_unpack_tsa(std::string infile = "test.tsa", UInt_t runid = 0, const cha
   auto source = new CbmSourceTsArchive(infile.data());
   auto unpack = source->GetRecoUnpack();
   unpack->SetUnpackConfig(trdconfig);
+  unpack->SetUnpackConfig(psdconfig);
   // ------------------------------------------------------------------------
 
 
@@ -188,7 +198,6 @@ void run_unpack_tsa(std::string infile = "test.tsa", UInt_t runid = 0, const cha
 */
 std::shared_ptr<CbmTrdUnpackMonitor> GetTrdMonitor(std::string treefilename)
 {
-
   // -----   Output filename and path   -------------------------------------
   std::string outpath  = "";
   std::string filename = "";
@@ -218,6 +227,8 @@ std::shared_ptr<CbmTrdUnpackMonitor> GetTrdMonitor(std::string treefilename)
     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 = {
diff --git a/reco/base/CbmRecoUnpackAlgo.tmpl b/reco/base/CbmRecoUnpackAlgo.tmpl
index c3afb0b35b..e9393ff78f 100644
--- a/reco/base/CbmRecoUnpackAlgo.tmpl
+++ b/reco/base/CbmRecoUnpackAlgo.tmpl
@@ -21,8 +21,8 @@
 #ifndef CbmRecoUnpackAlgo_TMPL
 #define CbmRecoUnpackAlgo_TMPL
 
-#include "Timeslice.hpp"  // timeslice
 #include <MicrosliceDescriptor.hpp>
+#include <Timeslice.hpp>  // timeslice
 
 #include <FairParGenericSet.h>  // for par container vector
 #include <FairTask.h>           // for Bool_t
@@ -134,6 +134,9 @@ protected:
   /** @brief Time of the last succesful digest hit message */
   size_t fLastFulltime = 0;
 
+  /** @brief Start time of the current TS [ns] */
+  size_t fTsStartTime = 0;
+
   /** @brief Additional explicit finish function of the derived algo implementations. */
   virtual void finish() = 0;
 
@@ -349,6 +352,9 @@ public:
   */
   void SetParFilesBasePath(std::string value) { fParFilesBasePath = value; }
 
+  /** @brief Set the start time of the current TS */
+  void SetTsStartTime(size_t value) { fTsStartTime = value; }
+
   /**
    * @brief Actual unpacking function
    * 
diff --git a/reco/base/CbmRecoUnpackConfig.tmpl b/reco/base/CbmRecoUnpackConfig.tmpl
index bdbcaf908c..b6b0dd4497 100644
--- a/reco/base/CbmRecoUnpackConfig.tmpl
+++ b/reco/base/CbmRecoUnpackConfig.tmpl
@@ -239,7 +239,9 @@ protected:
       auto filepath = pair.first;
       auto parset   = pair.second;
       FairParAsciiFileIo asciiInput;
-      if (asciiInput.open(filepath.data())) { parset->init(&asciiInput); }
+      if (!filepath.empty()) {
+        if (asciiInput.open(filepath.data())) { parset->init(&asciiInput); }
+      }
     }
     return kTRUE;
   }
diff --git a/reco/detectors/psd/CMakeLists.txt b/reco/detectors/psd/CMakeLists.txt
index 2e97d28670..9d407cd539 100644
--- a/reco/detectors/psd/CMakeLists.txt
+++ b/reco/detectors/psd/CMakeLists.txt
@@ -1,16 +1,23 @@
 set(INCLUDE_DIRECTORIES
   ${CMAKE_CURRENT_SOURCE_DIR}
+  ${CMAKE_CURRENT_SOURCE_DIR}/unpack
+
   ${CBMDATA_DIR}
   ${CBMDATA_DIR}/global
   ${CBMDATA_DIR}/base
   ${CBMDATA_DIR}/psd
+  ${CBMDATA_DIR}/raw
   ${CBMBASE_DIR}
+  ${CBMDETECTORBASE_DIR}/psd
+
+  ${CBMROOT_SOURCE_DIR}/reco/base
 )
 
 include_directories(${INCLUDE_DIRECTORIES})
 
 set(SYSTEM_INCLUDE_DIRECTORIES
   ${BASE_INCLUDE_DIRECTORIES} 
+  ${IPC_INCLUDE_DIRECTORY} # for fles infos for unpacker
 )
 
 include_directories(SYSTEM ${SYSTEM_INCLUDE_DIRECTORIES})
@@ -26,12 +33,19 @@ link_directories(${LINK_DIRECTORIES})
 set(SRCS
   CbmPsdHitProducer.cxx
   CbmPsdMCbmHitProducer.cxx
+
+  unpack/CbmPsdUnpackAlgo.cxx
+  unpack/CbmPsdUnpackConfig.cxx  
 )
 
 set(LINKDEF CbmPsdRecoLinkDef.h)
 set(LIBRARY_NAME CbmPsdReco)
 set(DEPENDENCIES
-    CbmData CbmBase Base 
+    CbmData 
+    CbmBase 
+    Base 
+    CbmRecoBase 
+    fles_ipc # for unpacker
 )
 
 GENERATE_LIBRARY()
diff --git a/reco/detectors/psd/CbmPsdRecoLinkDef.h b/reco/detectors/psd/CbmPsdRecoLinkDef.h
index e81bfa68b8..803cb12a31 100644
--- a/reco/detectors/psd/CbmPsdRecoLinkDef.h
+++ b/reco/detectors/psd/CbmPsdRecoLinkDef.h
@@ -11,4 +11,7 @@
 #pragma link C++ class CbmPsdHitProducer + ;
 #pragma link C++ class CbmPsdMCbmHitProducer + ;
 
+#pragma link C++ class CbmPsdUnpackAlgo + ;
+#pragma link C++ class CbmPsdUnpackConfig + ;
+
 #endif
diff --git a/reco/detectors/psd/unpack/CbmPsdUnpackAlgo.cxx b/reco/detectors/psd/unpack/CbmPsdUnpackAlgo.cxx
new file mode 100644
index 0000000000..ca67b1904c
--- /dev/null
+++ b/reco/detectors/psd/unpack/CbmPsdUnpackAlgo.cxx
@@ -0,0 +1,335 @@
+/* Copyright (C) 2010 - 2021 Goethe-University Frankfurt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pascal Raisig */
+
+#include "CbmPsdUnpackAlgo.h"
+
+#include <FairParGenericSet.h>
+#include <FairTask.h>
+#include <Logger.h>
+
+#include <Rtypes.h>
+#include <RtypesCore.h>
+
+#include <numeric>
+
+#include "../../core/detectors/psd/PronyFitter.h"
+#include "raw/PsdGbtReader-v0.00.h"
+#include "raw/PsdGbtReader-v1.00.h"
+
+
+CbmPsdUnpackAlgo::CbmPsdUnpackAlgo() : CbmRecoUnpackAlgo(fgkFlesSubsystemIdTrdR, "CbmPsdUnpackAlgo") {}
+
+// ----- Channel address
+Int_t CbmPsdUnpackAlgo::getAddress(size_t channel)
+{
+  assert(channel < fChannelAddress.size());
+  return fChannelAddress[channel];
+}
+
+
+// ----- Energy calibration
+Double_t CbmPsdUnpackAlgo::getMipCalibration(uint8_t channel)
+{
+  assert(channel < fMipCalibration.size());
+  return fMipCalibration[channel];
+}
+
+
+// ---- GetParContainerRequest ----
+std::vector<std::pair<std::string, std::shared_ptr<FairParGenericSet>>>*
+  CbmPsdUnpackAlgo::GetParContainerRequest(std::string /*geoTag*/, std::uint32_t /*runId*/)
+{
+  // Basepath for default Trd parameter sets (those connected to a geoTag)
+  std::string basepath = Form("%s", fParFilesBasePath.data());
+  std::string temppath = "";
+
+  // // Get parameter container
+  temppath = basepath + "mPsdPar.par";
+  fParContVec.emplace_back(std::make_pair(temppath, std::make_shared<CbmMcbm2018PsdPar>()));
+
+  return &fParContVec;
+}
+
+// ---- init
+Bool_t CbmPsdUnpackAlgo::init() { return kTRUE; }
+
+// ---- initParSet(FairParGenericSet* parset) ----
+Bool_t CbmPsdUnpackAlgo::initParSet(FairParGenericSet* parset)
+{
+  LOG(info) << fName << "::initParSet - for container " << parset->ClassName();
+  if (parset->IsA() == CbmMcbm2018PsdPar::Class()) return initParSet(static_cast<CbmMcbm2018PsdPar*>(parset));
+
+  // If we do not know the derived ParSet class we return false
+  LOG(error)
+    << fName << "::initParSet - for container " << parset->ClassName()
+    << " failed, since CbmTrdUnpackAlgoBaseR::initParSet() does not know the derived ParSet and what to do with it!";
+  return kFALSE;
+}
+
+// ---- initParSet(CbmTrdParSetAsic* parset) ----
+Bool_t CbmPsdUnpackAlgo::initParSet(CbmMcbm2018PsdPar* parset)
+{
+  LOG(debug) << fName << "::initParSetAsic - ";
+
+  fuRawDataVersion = parset->GetDataVersion();
+  LOG(info) << "Data Version: " << fuRawDataVersion;
+
+  UInt_t uNrOfGdpbs = parset->GetNrOfGdpbs();
+  LOG(info) << "Nr. of Tof GDPBs: " << uNrOfGdpbs;
+
+  UInt_t uNrOfFeePerGdpb = parset->GetNrOfFeesPerGdpb();
+  LOG(info) << "Nr. of FEEs per Psd GDPB: " << uNrOfFeePerGdpb;
+
+  UInt_t uNrOfChannelsPerFee = parset->GetNrOfChannelsPerFee();
+  LOG(info) << "Nr. of channels per FEE: " << uNrOfChannelsPerFee;
+
+  auto uNrOfChannelsPerGdpb = uNrOfChannelsPerFee * uNrOfFeePerGdpb;
+  LOG(info) << "Nr. of channels per GDPB: " << uNrOfChannelsPerGdpb;
+
+  fGdpbIdIndexMap.clear();
+  for (UInt_t i = 0; i < uNrOfGdpbs; ++i) {
+    fGdpbIdIndexMap[parset->GetGdpbId(i)] = i;
+    LOG(info) << "GDPB Id of PSD  " << i << " : " << std::hex << parset->GetGdpbId(i) << std::dec;
+  }  // for( UInt_t i = 0; i < fuNrOfGdpbs; ++i )
+
+  fuNrOfGbtx = parset->GetNrOfGbtx();
+  LOG(info) << "Nr. of GBTx: " << fuNrOfGbtx;
+
+  //Temporary until creation of full psd map
+  UInt_t uNrOfModules  = 1;
+  UInt_t uNrOfSections = 32;
+  UInt_t uNrOfChannels = uNrOfModules * uNrOfSections;
+  LOG(info) << "Nr. of possible Psd channels: " << uNrOfChannels;
+  fviPsdChUId.resize(uNrOfChannels);
+
+  UInt_t iCh = 0;
+  for (UInt_t iModule = 0; iModule < uNrOfModules; ++iModule) {
+    for (UInt_t iSection = 0; iSection < uNrOfSections; ++iSection) {
+      iCh              = iModule * uNrOfSections + iSection;
+      fviPsdChUId[iCh] = CbmPsdAddress::GetAddress(iModule, iSection);
+    }
+  }
+
+  for (size_t ichannel = 0; ichannel < fviPsdChUId.size(); ++ichannel) {
+    fMipCalibration.emplace_back(parset->GetMipCalibration(ichannel));
+  }
+
+  return kTRUE;
+
+
+  LOG(info) << fName << "::initParSetPsdMcbm2018 - Successfully initialized Spadic hardware address map";
+
+  return kTRUE;
+}
+
+
+// ---- makeDigi
+void CbmPsdUnpackAlgo::makeDigi(CbmPsdDsp dsp)
+{
+  std::unique_ptr<CbmPsdDigi> digi(new CbmPsdDigi(dsp.GetAddress(), dsp.GetTime(), dsp.GetEdep()));
+
+  if (digi) fOutputVec.emplace_back(*std::move(digi));
+
+  if (fOptOutAVec) { fOptOutAVec->emplace_back(dsp); }
+}
+
+
+// ---- unpack
+bool CbmPsdUnpackAlgo::unpack(const fles::Timeslice* ts, std::uint16_t icomp, UInt_t imslice)
+{
+  auto msDescriptor = ts->descriptor(icomp, imslice);
+  auto eqid         = msDescriptor.eq_id;
+
+  const uint8_t* msContent = reinterpret_cast<const uint8_t*>(ts->content(icomp, imslice));
+
+  uint32_t uSize = msDescriptor.size;
+  auto msidx     = msDescriptor.idx;
+
+  auto mstime = static_cast<double>(msidx);
+  LOG(debug) << "Microslice: " << msidx << " from EqId " << std::hex << eqid << std::dec << " has size: " << uSize;
+
+  if (0 == fvbMaskedComponents.size()) fvbMaskedComponents.resize(ts->num_components(), kFALSE);
+
+  auto dpbid    = static_cast<uint16_t>(eqid & 0xFFFF);
+  UInt_t dpbidx = 0;
+
+  /// Check if this sDPB ID was declared in parameter file and stop there if not
+  auto it = fGdpbIdIndexMap.find(dpbid);
+  if (it == fGdpbIdIndexMap.end()) {
+    if (kFALSE == fvbMaskedComponents[icomp]) {
+      LOG(info) << "---------------------------------------------------------------";
+      LOG(warning) << "Could not find the gDPB index for AFCK id 0x" << std::hex << dpbid << std::dec
+                   << " in timeslice " << ts->index() << " in microslice " << imslice << " component " << icomp << "\n"
+                   << "If valid this index has to be added in the PSD "
+                      "parameter file in the DbpIdArray field";
+      fvbMaskedComponents[icomp] = kTRUE;
+    }  // if( kFALSE == fvbMaskedComponents[ uMsComp ] )
+    else
+      return kTRUE;
+
+    /// Try to get it from the second message in buffer (first is epoch cycle without gDPB ID)
+    /// TODO!!!!
+
+    return kFALSE;
+
+  }  // if( it == fGdpbIdIndexMap.end() )
+  else
+    dpbidx = fGdpbIdIndexMap[dpbid];
+
+
+  //   assert(dpbid == 0);  // mCBM201: only one GPDB
+
+  // If not integer number of message in input buffer, print warning/error
+  if (0 != (uSize % kuBytesPerMessage))
+    LOG(error) << "The input microslice buffer does NOT "
+               << "contain only complete nDPB messages!";
+
+  /// Save start time of first valid MS
+  /** @todo check if this is really needed */
+  Double_t dMsRelativeTime = mstime - fTsStartTime;
+
+  // Compute the number of complete messages in the input microslice buffer
+  uint32_t uNbMessages = (uSize - (uSize % kuBytesPerMessage)) / kuBytesPerMessage;
+
+  // Prepare variables for the loop on contents
+  const uint64_t* pInBuff = reinterpret_cast<const uint64_t*>(msContent);
+
+  // every 80bit gbt word is decomposed into two 64bit words
+  if (!(uNbMessages > 1)) return kTRUE;
+
+  PsdDataV100::PsdGbtReader PsdReader(pInBuff);
+  if (fair::Logger::Logging(fair::Severity::debug)) PsdReader.SetPrintOutMode(true);
+
+  while (PsdReader.GetTotalGbtWordsRead() < uNbMessages) {
+    int ReadResult = PsdReader.ReadMs();
+    if (fair::Logger::Logging(fair::Severity::debug)) {
+      printf("\nMicroslice idx: %lu\n", msidx);
+      PsdReader.PrintOut(); /*PsdReader.PrintSaveBuff();*/
+    }
+    if (ReadResult == 0) {
+
+      double prev_hit_time =
+        dMsRelativeTime + (double) PsdReader.VectPackHdr.at(0).uAdcTime * 12.5 - fdTimeOffsetNs;  //in ns
+
+      //hit loop
+      for (uint64_t hit_iter = 0; hit_iter < PsdReader.VectHitHdr.size(); hit_iter++) {
+        if (PsdReader.VectPackHdr.size() != PsdReader.VectHitHdr.size()) {
+          LOG(error) << "Different vector headers sizes!"
+                     << " in VectPackHdr " << PsdReader.VectPackHdr.size() << " in VectHitHdr "
+                     << PsdReader.VectHitHdr.size() << "\n";
+          break;
+        }
+
+        uint8_t uHitChannel = PsdReader.VectHitHdr.at(hit_iter).uHitChannel;
+        if (uHitChannel >= fviPsdChUId.size()) {
+          LOG(error) << "hit channel number out of range! channel index: " << uHitChannel
+                     << " max: " << fviPsdChUId.size();
+          break;
+        }
+        UInt_t uChanUId = fviPsdChUId[uHitChannel];  //unique ID
+
+        auto dTime = static_cast<double>(PsdReader.VectPackHdr.at(hit_iter).uAdcTime * 12.5);
+        dTime -= fdTimeOffsetNs;  // Subtract system-side offset
+        dTime += msidx;           // Add the microslice start time
+        dTime -= fTsStartTime;    // Get the time relative to the TS start time
+
+        Double_t dEdep = (double) PsdReader.VectHitHdr.at(hit_iter).uSignalCharge / getMipCalibration(uHitChannel);
+        /// Energy deposition from FPGA [MeV]
+
+        UInt_t uZL        = PsdReader.VectHitHdr.at(hit_iter).uZeroLevel;  /// ZeroLevel from waveform [adc counts]
+        Double_t dAccum   = (double) PsdReader.VectHitHdr.at(hit_iter).uFeeAccum;  /// FPGA FEE Accumulator
+        Double_t dAdcTime = (double) PsdReader.VectPackHdr.at(hit_iter).uAdcTime;  /// Adc time of measurement
+
+        Double_t dEdepWfm          = 0.;  /// Energy deposition from waveform [MeV]
+        Double_t dAmpl             = 0.;  /// Amplitude from waveform [mV]
+        UInt_t uMinimum            = 0;   /// Minimum of waveform [adc samples]
+        UInt_t uTimeMax            = 0;   /// Time of maximum in waveform [adc samples]
+        std::vector<uint16_t> uWfm = PsdReader.VectHitData.at(hit_iter).uWfm;
+
+        Double_t dFitAmpl    = 0.;    /// Amplitude from fit of waveform [mV]
+        Double_t dFitZL      = 0.;    /// ZeroLevel from fit of waveform [adc counts]
+        Double_t dFitEdep    = 0.;    /// Energy deposition from fit of waveform [MeV]
+        Double_t dFitR2      = 999.;  /// Quality of waveform fit [] -- good near 0
+        Double_t dFitTimeMax = -1.;   /// Time of maximum in fit of waveform [adc samples]
+        std::vector<uint16_t> uFitWfm;
+
+        if (!uWfm.empty()) {
+
+          int32_t iHitChargeWfm = std::accumulate(uWfm.begin(), uWfm.end(), -uZL * uWfm.size());
+          auto const max_iter   = std::max_element(uWfm.begin(), uWfm.end());
+          assert(max_iter != uWfm.end());
+          if (max_iter == uWfm.end()) break;
+          uint8_t uHitTimeMax   = std::distance(uWfm.begin(), max_iter);
+          int32_t iHitAmlpitude = *max_iter - uZL;
+          auto const min_iter   = std::min_element(uWfm.begin(), uWfm.end());
+          uint32_t uHitMinimum  = *min_iter;
+
+          dEdepWfm = (double) iHitChargeWfm / getMipCalibration(uHitChannel);  // ->now in MeV
+          dAmpl    = (double) iHitAmlpitude / 16.5;                            // -> now in mV
+          uTimeMax = uHitTimeMax;
+          uMinimum = uHitMinimum;
+
+          int gate_beg = 0;
+          int gate_end = (int) uWfm.size() - 1;
+          PsdSignalFitting::PronyFitter Pfitter(2, 2, gate_beg, gate_end);
+          Pfitter.SetDebugMode(0);
+          Pfitter.SetWaveform(uWfm, uZL);
+          int SignalBeg = 4;
+          //0.6, 0.2 // 0.72 0.38
+          std::complex<float> first_fit_harmonic  = {0.72, 0.0};
+          std::complex<float> second_fit_harmonic = {0.38, -0.0};
+          Pfitter.SetExternalHarmonics(first_fit_harmonic, second_fit_harmonic);
+          Int_t best_signal_begin = Pfitter.ChooseBestSignalBegin(SignalBeg - 1, SignalBeg + 1);
+          Pfitter.SetSignalBegin(best_signal_begin);
+          Pfitter.CalculateFitAmplitudes();
+
+          dFitEdep    = Pfitter.GetIntegral(gate_beg, gate_end) / getMipCalibration(uHitChannel);  // ->now in MeV
+          dFitAmpl    = (Pfitter.GetMaxAmplitude() - Pfitter.GetZeroLevel()) / 16.5;               // ->now in mV
+          dFitR2      = Pfitter.GetRSquare(gate_beg, gate_end);
+          dFitZL      = Pfitter.GetZeroLevel();
+          dFitTimeMax = Pfitter.GetSignalMaxTime();
+          uFitWfm     = Pfitter.GetFitWfm();
+        }
+
+        CbmPsdDsp dsp = CbmPsdDsp(uChanUId, dTime, dEdep, uZL, dAccum, dAdcTime,
+
+                                  dEdepWfm, dAmpl, uMinimum, uTimeMax, uWfm,
+
+                                  dFitAmpl, dFitZL, dFitEdep, dFitR2, dFitTimeMax, uFitWfm);
+
+        //DEBUG
+        //if (fdTime < prev_hit_time) printf("negative time btw hits! %f after %f \n", fdTime, prev_hit_time);
+        //DEBUG END
+        prev_hit_time = dTime;
+        //DEBUG
+
+      }  // for (uint64_t hit_iter = 0; hit_iter < PsdReader.VectHitHdr.size(); hit_iter++) {
+    }
+    else if (ReadResult == 1) {
+      LOG(error) << "no pack headers in message!";
+    }
+    else if (ReadResult == 2) {
+      LOG(error) << "wrong channel! In header: " << PsdReader.HitHdr.uHitChannel;
+    }
+    else if (ReadResult == 3) {
+      LOG(error) << "check number of waveform points! In header: " << PsdReader.HitHdr.uWfmWords - 1;
+    }
+    else {
+      LOG(error) << "PsdGbtReader.ReadEventFles() didn't return expected values";
+    }
+
+
+  }  // while(PsdReader.GetTotalGbtWordsRead()<uNbMessages)
+
+  if (uNbMessages != PsdReader.GetTotalGbtWordsRead())
+    LOG(error) << "Wrong amount of messages read!"
+               << " in microslice " << uNbMessages << " by PsdReader " << PsdReader.GetTotalGbtWordsRead() << "\n";
+
+  return kTRUE;
+}
+
+CbmPsdUnpackAlgo::~CbmPsdUnpackAlgo() {}
+
+
+ClassImp(CbmPsdUnpackAlgo)
diff --git a/reco/detectors/psd/unpack/CbmPsdUnpackAlgo.h b/reco/detectors/psd/unpack/CbmPsdUnpackAlgo.h
new file mode 100644
index 0000000000..e1643c7a80
--- /dev/null
+++ b/reco/detectors/psd/unpack/CbmPsdUnpackAlgo.h
@@ -0,0 +1,177 @@
+/* Copyright (C) 2010 - 2021 Goethe-University Frankfurt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pascal Raisig */
+
+/**
+ * @file CbmPsdUnpackAlgo.h
+ * @author Pascal Raisig (praisig@ikf.uni-frankfurt.de)
+ * @brief Baseclass for the TrdR unpacker algorithms
+ * @version 0.1
+ * @date 2021-04-21
+ * 
+ * @copyright Copyright (c) 2021
+ * 
+ * This is the base class for the algorithmic part of the tsa data unpacking 
+ * processes of the CbmTrd.
+ * The actual translation from tsa to digi happens in the derived classes. 
+ * 
+ * 
+*/
+
+#ifndef CbmPsdUnpackAlgo_H
+#define CbmPsdUnpackAlgo_H
+
+#include "../../core/detectors/psd/CbmMcbm2018PsdPar.h"
+#include "CbmPsdDigi.h"
+#include "CbmPsdDsp.h"
+#include "CbmRecoUnpackAlgo.tmpl"
+
+#include "Timeslice.hpp"  // timeslice
+
+#include <Rtypes.h>  // for types
+#include <RtypesCore.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <utility>
+
+class CbmPsdUnpackAlgo : public CbmRecoUnpackAlgo<CbmPsdDigi, CbmPsdDsp> {
+public:
+  /** @brief Create the Cbm Trd Unpack AlgoBase object */
+  CbmPsdUnpackAlgo();
+
+  /** @brief Destroy the Cbm Trd Unpack Task object */
+  virtual ~CbmPsdUnpackAlgo();
+
+  /** @brief Copy constructor - not implemented **/
+  CbmPsdUnpackAlgo(const CbmPsdUnpackAlgo&) = delete;
+
+  /** @brief Assignment operator - not implemented **/
+  CbmPsdUnpackAlgo& operator=(const CbmPsdUnpackAlgo&) = delete;
+
+  /**
+   * @brief Get the requested parameter containers. To be defined in the derived classes!
+   * Return the required parameter containers together with the paths to the ascii 
+   * files to.
+   *  
+   * @param[in] std::string geoTag as used in CbmSetup
+   * @param[in] std::uint32_t runId for runwise defined parameters
+   * @return fParContVec
+  */
+  virtual std::vector<std::pair<std::string, std::shared_ptr<FairParGenericSet>>>*
+  GetParContainerRequest(std::string geoTag, std::uint32_t runId);
+
+
+protected:
+  /** @brief Finish function for this algorithm base clase */
+  void finish()
+  {
+    finishDerived();
+    // Finish the monitor if we have one
+    // if (fMonitor) fMonitor->Finish();
+  }
+
+  /** @brief Function that allows special calls during Finish in the derived algos */
+  virtual void finishDerived() { return; }
+
+  /** @brief Get channel address
+   ** @param channel  Channel index
+   ** @return Channel address
+   **/
+  Int_t getAddress(size_t channel);
+
+  /** @brief Energy calibration constants
+   ** @param channel Channel number
+   ** @return Energy calibration constant
+   **/
+  double getMipCalibration(uint8_t channel);
+
+  /**
+   * @brief Intialisation at begin of run. Special inits of the derived algos.
+   * 
+   * @retval Bool_t initOk
+  */
+  virtual Bool_t init();
+
+  /** @brief Create a digi object from the signal
+   ** @param dsp  Signal object
+   **/
+  void makeDigi(CbmPsdDsp dsp);
+
+  /**
+   * @brief Handles the distribution of the hidden derived classes to their explicit functions.
+   * 
+   * @param parset 
+   * @return Bool_t initOk 
+  */
+  Bool_t initParSet(FairParGenericSet* parset);
+
+  /**
+   * @brief Handles the distribution of the hidden derived classes to their explicit functions.
+   * 
+   * @param parset 
+   * @return Bool_t initOk 
+  */
+  Bool_t initParSet(CbmMcbm2018PsdPar* parset);
+
+  /**
+   * @brief Set the Derived Ts Parameters
+   * 
+   * In this function parameters required by the explicit algo connected to the timeslice can be set.
+   * 
+   * @param itimeslice 
+   * @return true 
+   * @return false 
+  */
+  bool setDerivedTsParameters(size_t itimeslice) { return true; }
+
+  /**
+   * @brief Unpack a given microslice. To be implemented in the derived unpacker algos.
+   * 
+   * @param ts timeslice pointer
+   * @param icomp index to the component to be unpacked
+   * @param imslice index of the microslice to be unpacked
+   * @return true 
+   * @return false 
+   * 
+   * @remark The content of the µslice can only be accessed via the timeslice. Hence, we need to pass the pointer to the full timeslice
+  */
+  bool unpack(const fles::Timeslice* ts, std::uint16_t icomp, UInt_t imslice);
+
+  /** @brief Fles Subsystem Identifier for the TRD R data */
+  static constexpr int fgkFlesSubsystemIdTrdR = static_cast<int>(fles::SubsystemIdentifier::PSD);
+
+  // Settings from parameters
+  UInt_t fuRawDataVersion = 0;                    //! Raw data versioning
+                                                  /// Readout chain dimensions and mapping
+  std::map<UInt_t, UInt_t> fGdpbIdIndexMap = {};  //! gDPB ID to index map
+  std::vector<Bool_t> fvbMaskedComponents  = {};
+
+
+  /// Detector Mapping
+  UInt_t fuNrOfGbtx              = 0;
+  UInt_t fuNrOfModules           = 0;
+  std::vector<Int_t> fviPsdChUId = {};
+
+
+  // Channel-to-address mapping
+  std::vector<Int_t> fChannelAddress = {};
+
+  // Mip calibration vector
+  std::vector<double> fMipCalibration = {};  // Calibration array
+
+  /// User settings: Data correction parameters
+  Double_t fdTimeOffsetNs = 0.0;
+
+  /// Constants
+  static const Int_t kiMaxNbFlibLinks   = 32;
+  static const UInt_t kuBytesPerMessage = 8;
+  static const UInt_t kuDetMask         = 0x0001FFFF;
+
+
+private:
+  ClassDef(CbmPsdUnpackAlgo, 2)
+};
+
+#endif  // CbmPsdUnpackAlgo_H
diff --git a/reco/detectors/psd/unpack/CbmPsdUnpackConfig.cxx b/reco/detectors/psd/unpack/CbmPsdUnpackConfig.cxx
new file mode 100644
index 0000000000..4fa6254bf1
--- /dev/null
+++ b/reco/detectors/psd/unpack/CbmPsdUnpackConfig.cxx
@@ -0,0 +1,64 @@
+/* Copyright (C) 2010 - 2021 Goethe-University Frankfurt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pascal Raisig */
+
+#include "CbmPsdUnpackConfig.h"
+
+#include "CbmPsdUnpackAlgo.h"
+
+#include <Logger.h>
+
+#include <Rtypes.h>
+#include <RtypesCore.h>
+
+#include <memory>
+#include <vector>
+
+CbmPsdUnpackConfig::CbmPsdUnpackConfig(std::string detGeoSetupTag, UInt_t runid)
+  : CbmRecoUnpackConfig("CbmPsdUnpackConfig")
+  , fGeoSetupTag(detGeoSetupTag)
+  , fRunId(runid)
+{
+}
+
+CbmPsdUnpackConfig::~CbmPsdUnpackConfig() {}
+
+// ---- Init ----
+void CbmPsdUnpackConfig::InitUnpacker()
+{
+  LOG(info) << fName << "::Init -";
+
+  auto initOk = kTRUE;
+
+  // First choose the derived unpacking algorithm to be used and pass the raw to digi method
+  auto algo = chooseAlgo();
+
+  // Initialise the parameter containers required by the unpacker algo. Includes loading the corresponding ascii files
+  auto reqparvec = algo->GetParContainerRequest(fGeoSetupTag, fRunId);
+  initOk &= initParContainers(reqparvec);
+
+  // Now we have all information required to initialise the algorithm
+  algo->Init();
+
+  // Pass the algo to its member in the base class
+  fAlgo = algo;
+}
+
+// ---- chooseAlgo ----
+std::shared_ptr<CbmPsdUnpackAlgo> CbmPsdUnpackConfig::chooseAlgo()
+{
+  if (fDoLog) LOG(info) << fName << "::Init - chooseAlgo";
+
+  // Default unpacker selection
+  // Unpacker algo from mcbm 2021 on and hopefully default for a long time.
+  auto algo = std::make_shared<CbmPsdUnpackAlgo>();
+  LOG(info) << fName << "::chooseAlgo() - selected algo = " << algo->Class_Name();
+  return algo;
+
+  LOG(error) << fName
+             << "::Init - chooseAlgo() - no algorithm created something went wrong. We can not work like this!";
+  return nullptr;
+}
+
+
+ClassImp(CbmPsdUnpackConfig)
diff --git a/reco/detectors/psd/unpack/CbmPsdUnpackConfig.h b/reco/detectors/psd/unpack/CbmPsdUnpackConfig.h
new file mode 100644
index 0000000000..8be09af21b
--- /dev/null
+++ b/reco/detectors/psd/unpack/CbmPsdUnpackConfig.h
@@ -0,0 +1,89 @@
+/* Copyright (C) 2010 - 2021 Goethe-University Frankfurt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pascal Raisig */
+
+/**
+ * @file CbmPsdUnpackConfig.h
+ * @author Pascal Raisig (praisig@ikf.uni-frankfurt.de)
+ * @brief Configuration class for an unpacker algorithm
+ * @version 0.1
+ * @date 2021-04-21
+ * 
+ * @copyright Copyright (c) 2021
+ * 
+ * This is the common configuration class for unpacking algorithms
+ * 
+*/
+
+#ifndef CbmPsdUnpackConfig_H
+#define CbmPsdUnpackConfig_H
+
+#include "CbmPsdDigi.h"
+#include "CbmPsdUnpackAlgo.h"
+#include "CbmRecoUnpackConfig.tmpl"
+
+#include <FairLogger.h>
+#include <Logger.h>
+
+#include <Rtypes.h>
+#include <RtypesCore.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+class CbmPsdUnpackConfig : public CbmRecoUnpackConfig<CbmPsdUnpackAlgo, CbmPsdDigi, CbmPsdDsp> {
+
+public:
+  /**
+   * @brief Create the Cbm Trd Unpack Task object
+   * 
+   * @param geoSetupTag Geometry setup tag for the given detector as used by CbmSetup objects
+   * @param runid set if unpacker is rerun on a special run with special parameters
+   *@remark We use the string instead of CbmSetup here, to not having to link against sim/steer...
+  */
+  CbmPsdUnpackConfig(std::string detGeoSetupTag, UInt_t runid = 0);
+
+  /**
+   * @brief Destroy the Cbm Trd Unpack Task object
+   * 
+  */
+  virtual ~CbmPsdUnpackConfig();
+
+  /** @brief Copy constructor - not implemented **/
+  CbmPsdUnpackConfig(const CbmPsdUnpackConfig&) = delete;
+
+  /** @brief Assignment operator - not implemented **/
+  CbmPsdUnpackConfig& operator=(const CbmPsdUnpackConfig&) = delete;
+
+  // Getters
+
+
+  /**
+   * @brief Prepare the unpacker to be ready to run.
+   * In this function all initialization steps of the unpacker algorithms happen.
+  */
+  void InitUnpacker();
+
+  // Setters
+
+protected:
+  /**
+   * @brief Choose the derived unpacker algorithm to be used for the DAQ output to Digi translation. If algo was already set manually by the user this algorithm is used.
+   * 
+   * @return Bool_t initOk 
+  */
+  virtual std::shared_ptr<CbmPsdUnpackAlgo> chooseAlgo();
+
+  /** @brief Geometry setup tag for the given detector as used by CbmSetup objects */
+  std::string fGeoSetupTag = "";
+
+  /** @brief RunId of the current run, if not known 0 is a valid runtime case. Used runId based parameter loading. */
+  UInt_t fRunId = 0;
+
+private:
+  ClassDef(CbmPsdUnpackConfig, 2)
+};
+
+#endif  // CbmPsdUnpackConfig_H
diff --git a/reco/detectors/rich/CMakeLists.txt b/reco/detectors/rich/CMakeLists.txt
index 4335e7d438..b45f03e2a2 100644
--- a/reco/detectors/rich/CMakeLists.txt
+++ b/reco/detectors/rich/CMakeLists.txt
@@ -10,6 +10,7 @@ ${CMAKE_CURRENT_SOURCE_DIR}/utils
 ${CMAKE_CURRENT_SOURCE_DIR}/qa
 ${CMAKE_CURRENT_SOURCE_DIR}/alignment
 ${CMAKE_CURRENT_SOURCE_DIR}/mcbm
+${CMAKE_CURRENT_SOURCE_DIR}/unpack
 
 ${CBMDETECTORBASE_DIR}/rich
 
@@ -47,6 +48,7 @@ Include_Directories( ${INCLUDE_DIRECTORIES})
 Set(SYSTEM_INCLUDE_DIRECTORIES
   ${BASE_INCLUDE_DIRECTORIES}
   ${Boost_INCLUDE_DIR}
+  ${IPC_INCLUDE_DIRECTORY} # for fles for unpacker
 )
 
 Include_Directories(SYSTEM ${SYSTEM_INCLUDE_DIRECTORIES})
@@ -82,6 +84,9 @@ qa/CbmRichGeoTest.cxx
 qa/CbmRichUrqmdTest.cxx
 qa/CbmRichGeoTestOpt.cxx
 qa/CbmRichRecoQa.cxx
+
+unpack/CbmRichUnpackAlgo.cxx
+unpack/CbmRichUnpackConfig.cxx
 )
 
 set(NO_DICT_SRCS
@@ -126,7 +131,9 @@ set(LINKDEF  CbmRichRecoLinkDef.h)
 
 Set(LIBRARY_NAME CbmRichReco)
 Set(DEPENDENCIES
-    KF L1 CbmRichBase CbmRecoBase CbmBase CbmData Base MLP boost_regex Gdml)
+    KF L1 CbmRichBase CbmRecoBase CbmBase CbmData Base MLP boost_regex Gdml 
+    fles_ipc # for unpacker
+    )
 
 GENERATE_LIBRARY()
 
diff --git a/reco/detectors/rich/CbmRichRecoLinkDef.h b/reco/detectors/rich/CbmRichRecoLinkDef.h
index 8ff30913da..80a8254f47 100644
--- a/reco/detectors/rich/CbmRichRecoLinkDef.h
+++ b/reco/detectors/rich/CbmRichRecoLinkDef.h
@@ -32,4 +32,9 @@
 #pragma link C++ class CbmRichRecoQa + ;
 #pragma link C++ class CbmRichRecoTbQa + ;
 
+//unpack
+#pragma link C++ class CbmRichUnpackAlgo + ;
+#pragma link C++ class CbmRichUnpackConfig + ;
+
+
 #endif
diff --git a/reco/detectors/rich/unpack/CbmRichUnpackAlgo.cxx b/reco/detectors/rich/unpack/CbmRichUnpackAlgo.cxx
new file mode 100644
index 0000000000..55b597878a
--- /dev/null
+++ b/reco/detectors/rich/unpack/CbmRichUnpackAlgo.cxx
@@ -0,0 +1,20 @@
+/* Copyright (C) 2010 - 2021 Goethe-University Frankfurt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pascal Raisig */
+
+#include "CbmRichUnpackAlgo.h"
+
+#include <FairParGenericSet.h>
+#include <FairTask.h>
+#include <Logger.h>
+
+#include <Rtypes.h>
+#include <RtypesCore.h>
+
+
+CbmRichUnpackAlgo::CbmRichUnpackAlgo() : CbmRecoUnpackAlgo(fgkFlesSubsystemIdTrdR, "CbmRichUnpackAlgo") {}
+
+CbmRichUnpackAlgo::~CbmRichUnpackAlgo() {}
+
+
+ClassImp(CbmRichUnpackAlgo)
diff --git a/reco/detectors/rich/unpack/CbmRichUnpackAlgo.h b/reco/detectors/rich/unpack/CbmRichUnpackAlgo.h
new file mode 100644
index 0000000000..effe35cae3
--- /dev/null
+++ b/reco/detectors/rich/unpack/CbmRichUnpackAlgo.h
@@ -0,0 +1,124 @@
+/* Copyright (C) 2010 - 2021 Goethe-University Frankfurt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pascal Raisig */
+
+/**
+ * @file CbmRichUnpackAlgo.h
+ * @author Pascal Raisig (praisig@ikf.uni-frankfurt.de)
+ * @brief Baseclass for the TrdR unpacker algorithms
+ * @version 0.1
+ * @date 2021-04-21
+ * 
+ * @copyright Copyright (c) 2021
+ * 
+ * This is the base class for the algorithmic part of the tsa data unpacking 
+ * processes of the CbmTrd.
+ * The actual translation from tsa to digi happens in the derived classes. 
+ * 
+ * 
+*/
+
+#ifndef CbmRichUnpackAlgo_H
+#define CbmRichUnpackAlgo_H
+
+#include "CbmRecoUnpackAlgo.tmpl"
+#include "CbmRichDigi.h"
+
+#include "Timeslice.hpp"  // timeslice
+
+#include <Rtypes.h>  // for types
+#include <RtypesCore.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <utility>
+
+class CbmRichUnpackAlgo : public CbmRecoUnpackAlgo<CbmRichDigi> {
+public:
+  /** @brief Create the Cbm Trd Unpack AlgoBase object */
+  CbmRichUnpackAlgo();
+
+  /** @brief Destroy the Cbm Trd Unpack Task object */
+  virtual ~CbmRichUnpackAlgo();
+
+  /** @brief Copy constructor - not implemented **/
+  CbmRichUnpackAlgo(const CbmRichUnpackAlgo&) = delete;
+
+  /** @brief Assignment operator - not implemented **/
+  CbmRichUnpackAlgo& operator=(const CbmRichUnpackAlgo&) = delete;
+
+  /**
+   * @brief Get the requested parameter containers. To be defined in the derived classes!
+   * Return the required parameter containers together with the paths to the ascii 
+   * files to.
+   *  
+   * @param[in] std::string geoTag as used in CbmSetup
+   * @param[in] std::uint32_t runId for runwise defined parameters
+   * @return fParContVec
+  */
+  virtual std::vector<std::pair<std::string, std::shared_ptr<FairParGenericSet>>>*
+  GetParContainerRequest(std::string geoTag, std::uint32_t runId)
+  {
+    return {};
+  };
+
+protected:
+  /** @brief Finish function for this algorithm base clase */
+  void finish()
+  {
+    finishDerived();
+    // Finish the monitor if we have one
+    // if (fMonitor) fMonitor->Finish();
+  }
+
+  /** @brief Function that allows special calls during Finish in the derived algos */
+  virtual void finishDerived() { return; }
+
+  /**
+   * @brief Intialisation at begin of run. Special inits of the derived algos.
+   * 
+   * @retval Bool_t initOk
+  */
+  Bool_t init() { return kTRUE; }
+
+  /**
+   * @brief Handles the distribution of the hidden derived classes to their explicit functions.
+   * 
+   * @param parset 
+   * @return Bool_t initOk 
+  */
+  Bool_t initParSet(FairParGenericSet* parset) { return kTRUE; }
+
+  /**
+   * @brief Set the Derived Ts Parameters
+   * 
+   * In this function parameters required by the explicit algo connected to the timeslice can be set.
+   * 
+   * @param itimeslice 
+   * @return true 
+   * @return false 
+  */
+  bool setDerivedTsParameters(size_t itimeslice) { return true; }
+
+  /**
+   * @brief Unpack a given microslice. To be implemented in the derived unpacker algos.
+   * 
+   * @param ts timeslice pointer
+   * @param icomp index to the component to be unpacked
+   * @param imslice index of the microslice to be unpacked
+   * @return true 
+   * @return false 
+   * 
+   * @remark The content of the µslice can only be accessed via the timeslice. Hence, we need to pass the pointer to the full timeslice
+  */
+  bool unpack(const fles::Timeslice* ts, std::uint16_t icomp, UInt_t imslice) { return true; }
+
+  /** @brief Fles Subsystem Identifier for the TRD R data */
+  static constexpr int fgkFlesSubsystemIdTrdR = static_cast<int>(fles::SubsystemIdentifier::RICH);
+
+private:
+  ClassDef(CbmRichUnpackAlgo, 2)
+};
+
+#endif  // CbmRichUnpackAlgo_H
diff --git a/reco/detectors/rich/unpack/CbmRichUnpackConfig.cxx b/reco/detectors/rich/unpack/CbmRichUnpackConfig.cxx
new file mode 100644
index 0000000000..279e552fa8
--- /dev/null
+++ b/reco/detectors/rich/unpack/CbmRichUnpackConfig.cxx
@@ -0,0 +1,64 @@
+/* Copyright (C) 2010 - 2021 Goethe-University Frankfurt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pascal Raisig */
+
+#include "CbmRichUnpackConfig.h"
+
+#include "CbmRichUnpackAlgo.h"
+
+#include <Logger.h>
+
+#include <Rtypes.h>
+#include <RtypesCore.h>
+
+#include <memory>
+#include <vector>
+
+CbmRichUnpackConfig::CbmRichUnpackConfig(std::string detGeoSetupTag, UInt_t runid)
+  : CbmRecoUnpackConfig("CbmRichUnpackConfig")
+  , fGeoSetupTag(detGeoSetupTag)
+  , fRunId(runid)
+{
+}
+
+CbmRichUnpackConfig::~CbmRichUnpackConfig() {}
+
+// ---- Init ----
+void CbmRichUnpackConfig::InitUnpacker()
+{
+  LOG(info) << fName << "::Init -";
+
+  auto initOk = kTRUE;
+
+  // First choose the derived unpacking algorithm to be used and pass the raw to digi method
+  auto algo = chooseAlgo();
+
+  // Initialise the parameter containers required by the unpacker algo. Includes loading the corresponding ascii files
+  auto reqparvec = algo->GetParContainerRequest(fGeoSetupTag, fRunId);
+  initOk &= initParContainers(reqparvec);
+
+  // Now we have all information required to initialise the algorithm
+  algo->Init();
+
+  // Pass the algo to its member in the base class
+  fAlgo = algo;
+}
+
+// ---- chooseAlgo ----
+std::shared_ptr<CbmRichUnpackAlgo> CbmRichUnpackConfig::chooseAlgo()
+{
+  if (fDoLog) LOG(info) << fName << "::Init - chooseAlgo";
+
+  // Default unpacker selection
+  // Unpacker algo from mcbm 2021 on and hopefully default for a long time.
+  auto algo = std::make_shared<CbmRichUnpackAlgo>();
+  LOG(info) << fName << "::chooseAlgo() - selected algo = " << algo->Class_Name();
+  return algo;
+
+  LOG(error) << fName
+             << "::Init - chooseAlgo() - no algorithm created something went wrong. We can not work like this!";
+  return nullptr;
+}
+
+
+ClassImp(CbmRichUnpackConfig)
diff --git a/reco/detectors/rich/unpack/CbmRichUnpackConfig.h b/reco/detectors/rich/unpack/CbmRichUnpackConfig.h
new file mode 100644
index 0000000000..632226eb98
--- /dev/null
+++ b/reco/detectors/rich/unpack/CbmRichUnpackConfig.h
@@ -0,0 +1,89 @@
+/* Copyright (C) 2010 - 2021 Goethe-University Frankfurt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pascal Raisig */
+
+/**
+ * @file CbmRichUnpackConfig.h
+ * @author Pascal Raisig (praisig@ikf.uni-frankfurt.de)
+ * @brief Configuration class for an unpacker algorithm
+ * @version 0.1
+ * @date 2021-04-21
+ * 
+ * @copyright Copyright (c) 2021
+ * 
+ * This is the common configuration class for unpacking algorithms
+ * 
+*/
+
+#ifndef CbmRichUnpackConfig_H
+#define CbmRichUnpackConfig_H
+
+#include "CbmRecoUnpackConfig.tmpl"
+#include "CbmRichDigi.h"
+#include "CbmRichUnpackAlgo.h"
+
+#include <FairLogger.h>
+#include <Logger.h>
+
+#include <Rtypes.h>
+#include <RtypesCore.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+class CbmRichUnpackConfig : public CbmRecoUnpackConfig<CbmRichUnpackAlgo, CbmRichDigi, std::nullptr_t, std::nullptr_t> {
+
+public:
+  /**
+   * @brief Create the Cbm Trd Unpack Task object
+   * 
+   * @param geoSetupTag Geometry setup tag for the given detector as used by CbmSetup objects
+   * @param runid set if unpacker is rerun on a special run with special parameters
+   *@remark We use the string instead of CbmSetup here, to not having to link against sim/steer...
+  */
+  CbmRichUnpackConfig(std::string detGeoSetupTag, UInt_t runid = 0);
+
+  /**
+   * @brief Destroy the Cbm Trd Unpack Task object
+   * 
+  */
+  virtual ~CbmRichUnpackConfig();
+
+  /** @brief Copy constructor - not implemented **/
+  CbmRichUnpackConfig(const CbmRichUnpackConfig&) = delete;
+
+  /** @brief Assignment operator - not implemented **/
+  CbmRichUnpackConfig& operator=(const CbmRichUnpackConfig&) = delete;
+
+  // Getters
+
+
+  /**
+   * @brief Prepare the unpacker to be ready to run.
+   * In this function all initialization steps of the unpacker algorithms happen.
+  */
+  void InitUnpacker();
+
+  // Setters
+
+protected:
+  /**
+   * @brief Choose the derived unpacker algorithm to be used for the DAQ output to Digi translation. If algo was already set manually by the user this algorithm is used.
+   * 
+   * @return Bool_t initOk 
+  */
+  virtual std::shared_ptr<CbmRichUnpackAlgo> chooseAlgo();
+
+  /** @brief Geometry setup tag for the given detector as used by CbmSetup objects */
+  std::string fGeoSetupTag = "";
+
+  /** @brief RunId of the current run, if not known 0 is a valid runtime case. Used runId based parameter loading. */
+  UInt_t fRunId = 0;
+
+private:
+  ClassDef(CbmRichUnpackConfig, 2)
+};
+
+#endif  // CbmRichUnpackConfig_H
diff --git a/reco/detectors/sts/CMakeLists.txt b/reco/detectors/sts/CMakeLists.txt
index 0dc91bcde2..b1ab46c91e 100644
--- a/reco/detectors/sts/CMakeLists.txt
+++ b/reco/detectors/sts/CMakeLists.txt
@@ -19,6 +19,9 @@ CbmStsFindTracksEvents.cxx
 CbmStsFindTracksQa.cxx
 CbmStsRecoModule.cxx
 CbmStsTrackFinderIdeal.cxx
+
+unpack/CbmStsUnpackAlgo.cxx
+unpack/CbmStsUnpackConfig.cxx
 )
 # -----  End of sources   ---------------------------------
 
@@ -29,6 +32,7 @@ set(INCLUDE_DIRECTORIES
 
 # This directory
 ${CBMROOT_SOURCE_DIR}/reco/detectors/sts
+${CBMROOT_SOURCE_DIR}/reco/detectors/sts/unpack
 
 # Reco
 ${CBMROOT_SOURCE_DIR}/reco/base
@@ -52,6 +56,7 @@ ${CBMDETECTORBASE_DIR}/sts
 
 set(SYSTEM_INCLUDE_DIRECTORIES
 ${BASE_INCLUDE_DIRECTORIES}
+${IPC_INCLUDE_DIRECTORY} # for fles infos for unpacker
 )
 # ----  End of include directories ------------------------
 
@@ -68,6 +73,7 @@ ${Boost_LIBRARY_DIRS}
 # -----   Specify library dependences   -------------------
 Set(DEPENDENCIES
     CbmBase CbmData CbmRecoBase CbmStsBase
+    fles_ipc # for unpacker
 )
 # ---------------------------------------------------------
 
diff --git a/reco/detectors/sts/CbmRecoStsLinkDef.h b/reco/detectors/sts/CbmRecoStsLinkDef.h
index f3eb01e954..a1a0faab5b 100644
--- a/reco/detectors/sts/CbmRecoStsLinkDef.h
+++ b/reco/detectors/sts/CbmRecoStsLinkDef.h
@@ -19,4 +19,7 @@
 #pragma link C++ class CbmStsRecoModule + ;
 #pragma link C++ class CbmStsTrackFinderIdeal + ;
 
+#pragma link C++ class CbmStsUnpackAlgo + ;
+#pragma link C++ class CbmStsUnpackConfig + ;
+
 #endif /* __CINT__ */
diff --git a/reco/detectors/sts/unpack/CbmStsUnpackAlgo.cxx b/reco/detectors/sts/unpack/CbmStsUnpackAlgo.cxx
new file mode 100644
index 0000000000..6a1e6e5ac9
--- /dev/null
+++ b/reco/detectors/sts/unpack/CbmStsUnpackAlgo.cxx
@@ -0,0 +1,20 @@
+/* Copyright (C) 2010 - 2021 Goethe-University Frankfurt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pascal Raisig */
+
+#include "CbmStsUnpackAlgo.h"
+
+#include <FairParGenericSet.h>
+#include <FairTask.h>
+#include <Logger.h>
+
+#include <Rtypes.h>
+#include <RtypesCore.h>
+
+
+CbmStsUnpackAlgo::CbmStsUnpackAlgo() : CbmRecoUnpackAlgo(fgkFlesSubsystemIdTrdR, "CbmStsUnpackAlgo") {}
+
+CbmStsUnpackAlgo::~CbmStsUnpackAlgo() {}
+
+
+ClassImp(CbmStsUnpackAlgo)
diff --git a/reco/detectors/sts/unpack/CbmStsUnpackAlgo.h b/reco/detectors/sts/unpack/CbmStsUnpackAlgo.h
new file mode 100644
index 0000000000..862d89a39f
--- /dev/null
+++ b/reco/detectors/sts/unpack/CbmStsUnpackAlgo.h
@@ -0,0 +1,124 @@
+/* Copyright (C) 2010 - 2021 Goethe-University Frankfurt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pascal Raisig */
+
+/**
+ * @file CbmStsUnpackAlgo.h
+ * @author Pascal Raisig (praisig@ikf.uni-frankfurt.de)
+ * @brief Baseclass for the TrdR unpacker algorithms
+ * @version 0.1
+ * @date 2021-04-21
+ * 
+ * @copyright Copyright (c) 2021
+ * 
+ * This is the base class for the algorithmic part of the tsa data unpacking 
+ * processes of the CbmTrd.
+ * The actual translation from tsa to digi happens in the derived classes. 
+ * 
+ * 
+*/
+
+#ifndef CbmStsUnpackAlgo_H
+#define CbmStsUnpackAlgo_H
+
+#include "CbmRecoUnpackAlgo.tmpl"
+#include "CbmStsDigi.h"
+
+#include "Timeslice.hpp"  // timeslice
+
+#include <Rtypes.h>  // for types
+#include <RtypesCore.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <utility>
+
+class CbmStsUnpackAlgo : public CbmRecoUnpackAlgo<CbmStsDigi> {
+public:
+  /** @brief Create the Cbm Trd Unpack AlgoBase object */
+  CbmStsUnpackAlgo();
+
+  /** @brief Destroy the Cbm Trd Unpack Task object */
+  virtual ~CbmStsUnpackAlgo();
+
+  /** @brief Copy constructor - not implemented **/
+  CbmStsUnpackAlgo(const CbmStsUnpackAlgo&) = delete;
+
+  /** @brief Assignment operator - not implemented **/
+  CbmStsUnpackAlgo& operator=(const CbmStsUnpackAlgo&) = delete;
+
+  /**
+   * @brief Get the requested parameter containers. To be defined in the derived classes!
+   * Return the required parameter containers together with the paths to the ascii 
+   * files to.
+   *  
+   * @param[in] std::string geoTag as used in CbmSetup
+   * @param[in] std::uint32_t runId for runwise defined parameters
+   * @return fParContVec
+  */
+  virtual std::vector<std::pair<std::string, std::shared_ptr<FairParGenericSet>>>*
+  GetParContainerRequest(std::string geoTag, std::uint32_t runId)
+  {
+    return {};
+  };
+
+protected:
+  /** @brief Finish function for this algorithm base clase */
+  void finish()
+  {
+    finishDerived();
+    // Finish the monitor if we have one
+    // if (fMonitor) fMonitor->Finish();
+  }
+
+  /** @brief Function that allows special calls during Finish in the derived algos */
+  virtual void finishDerived() { return; }
+
+  /**
+   * @brief Intialisation at begin of run. Special inits of the derived algos.
+   * 
+   * @retval Bool_t initOk
+  */
+  Bool_t init() { return kTRUE; }
+
+  /**
+   * @brief Handles the distribution of the hidden derived classes to their explicit functions.
+   * 
+   * @param parset 
+   * @return Bool_t initOk 
+  */
+  Bool_t initParSet(FairParGenericSet* parset) { return kTRUE; }
+
+  /**
+   * @brief Set the Derived Ts Parameters
+   * 
+   * In this function parameters required by the explicit algo connected to the timeslice can be set.
+   * 
+   * @param itimeslice 
+   * @return true 
+   * @return false 
+  */
+  bool setDerivedTsParameters(size_t itimeslice) { return true; }
+
+  /**
+   * @brief Unpack a given microslice. To be implemented in the derived unpacker algos.
+   * 
+   * @param ts timeslice pointer
+   * @param icomp index to the component to be unpacked
+   * @param imslice index of the microslice to be unpacked
+   * @return true 
+   * @return false 
+   * 
+   * @remark The content of the µslice can only be accessed via the timeslice. Hence, we need to pass the pointer to the full timeslice
+  */
+  bool unpack(const fles::Timeslice* ts, std::uint16_t icomp, UInt_t imslice) { return true; }
+
+  /** @brief Fles Subsystem Identifier for the TRD R data */
+  static constexpr int fgkFlesSubsystemIdTrdR = static_cast<int>(fles::SubsystemIdentifier::STS);
+
+private:
+  ClassDef(CbmStsUnpackAlgo, 2)
+};
+
+#endif  // CbmStsUnpackAlgo_H
diff --git a/reco/detectors/sts/unpack/CbmStsUnpackConfig.cxx b/reco/detectors/sts/unpack/CbmStsUnpackConfig.cxx
new file mode 100644
index 0000000000..256521dc56
--- /dev/null
+++ b/reco/detectors/sts/unpack/CbmStsUnpackConfig.cxx
@@ -0,0 +1,64 @@
+/* Copyright (C) 2010 - 2021 Goethe-University Frankfurt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pascal Raisig */
+
+#include "CbmStsUnpackConfig.h"
+
+#include "CbmStsUnpackAlgo.h"
+
+#include <Logger.h>
+
+#include <Rtypes.h>
+#include <RtypesCore.h>
+
+#include <memory>
+#include <vector>
+
+CbmStsUnpackConfig::CbmStsUnpackConfig(std::string detGeoSetupTag, UInt_t runid)
+  : CbmRecoUnpackConfig("CbmStsUnpackConfig")
+  , fGeoSetupTag(detGeoSetupTag)
+  , fRunId(runid)
+{
+}
+
+CbmStsUnpackConfig::~CbmStsUnpackConfig() {}
+
+// ---- Init ----
+void CbmStsUnpackConfig::InitUnpacker()
+{
+  LOG(info) << fName << "::Init -";
+
+  auto initOk = kTRUE;
+
+  // First choose the derived unpacking algorithm to be used and pass the raw to digi method
+  auto algo = chooseAlgo();
+
+  // Initialise the parameter containers required by the unpacker algo. Includes loading the corresponding ascii files
+  auto reqparvec = algo->GetParContainerRequest(fGeoSetupTag, fRunId);
+  initOk &= initParContainers(reqparvec);
+
+  // Now we have all information required to initialise the algorithm
+  algo->Init();
+
+  // Pass the algo to its member in the base class
+  fAlgo = algo;
+}
+
+// ---- chooseAlgo ----
+std::shared_ptr<CbmStsUnpackAlgo> CbmStsUnpackConfig::chooseAlgo()
+{
+  if (fDoLog) LOG(info) << fName << "::Init - chooseAlgo";
+
+  // Default unpacker selection
+  // Unpacker algo from mcbm 2021 on and hopefully default for a long time.
+  auto algo = std::make_shared<CbmStsUnpackAlgo>();
+  LOG(info) << fName << "::chooseAlgo() - selected algo = " << algo->Class_Name();
+  return algo;
+
+  LOG(error) << fName
+             << "::Init - chooseAlgo() - no algorithm created something went wrong. We can not work like this!";
+  return nullptr;
+}
+
+
+ClassImp(CbmStsUnpackConfig)
diff --git a/reco/detectors/sts/unpack/CbmStsUnpackConfig.h b/reco/detectors/sts/unpack/CbmStsUnpackConfig.h
new file mode 100644
index 0000000000..d6a271c708
--- /dev/null
+++ b/reco/detectors/sts/unpack/CbmStsUnpackConfig.h
@@ -0,0 +1,89 @@
+/* Copyright (C) 2010 - 2021 Goethe-University Frankfurt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pascal Raisig */
+
+/**
+ * @file CbmStsUnpackConfig.h
+ * @author Pascal Raisig (praisig@ikf.uni-frankfurt.de)
+ * @brief Configuration class for an unpacker algorithm
+ * @version 0.1
+ * @date 2021-04-21
+ * 
+ * @copyright Copyright (c) 2021
+ * 
+ * This is the common configuration class for unpacking algorithms
+ * 
+*/
+
+#ifndef CbmStsUnpackConfig_H
+#define CbmStsUnpackConfig_H
+
+#include "CbmRecoUnpackConfig.tmpl"
+#include "CbmStsDigi.h"
+#include "CbmStsUnpackAlgo.h"
+
+#include <FairLogger.h>
+#include <Logger.h>
+
+#include <Rtypes.h>
+#include <RtypesCore.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+class CbmStsUnpackConfig : public CbmRecoUnpackConfig<CbmStsUnpackAlgo, CbmStsDigi> {
+
+public:
+  /**
+   * @brief Create the Cbm Trd Unpack Task object
+   * 
+   * @param geoSetupTag Geometry setup tag for the given detector as used by CbmSetup objects
+   * @param runid set if unpacker is rerun on a special run with special parameters
+   *@remark We use the string instead of CbmSetup here, to not having to link against sim/steer...
+  */
+  CbmStsUnpackConfig(std::string detGeoSetupTag, UInt_t runid = 0);
+
+  /**
+   * @brief Destroy the Cbm Trd Unpack Task object
+   * 
+  */
+  virtual ~CbmStsUnpackConfig();
+
+  /** @brief Copy constructor - not implemented **/
+  CbmStsUnpackConfig(const CbmStsUnpackConfig&) = delete;
+
+  /** @brief Assignment operator - not implemented **/
+  CbmStsUnpackConfig& operator=(const CbmStsUnpackConfig&) = delete;
+
+  // Getters
+
+
+  /**
+   * @brief Prepare the unpacker to be ready to run.
+   * In this function all initialization steps of the unpacker algorithms happen.
+  */
+  void InitUnpacker();
+
+  // Setters
+
+protected:
+  /**
+   * @brief Choose the derived unpacker algorithm to be used for the DAQ output to Digi translation. If algo was already set manually by the user this algorithm is used.
+   * 
+   * @return Bool_t initOk 
+  */
+  virtual std::shared_ptr<CbmStsUnpackAlgo> chooseAlgo();
+
+  /** @brief Geometry setup tag for the given detector as used by CbmSetup objects */
+  std::string fGeoSetupTag = "";
+
+  /** @brief RunId of the current run, if not known 0 is a valid runtime case. Used runId based parameter loading. */
+  UInt_t fRunId = 0;
+
+private:
+  ClassDef(CbmStsUnpackConfig, 2)
+};
+
+#endif  // CbmStsUnpackConfig_H
diff --git a/reco/detectors/trd/unpack/CbmTrdUnpackAlgo2D.cxx b/reco/detectors/trd/unpack/CbmTrdUnpackAlgo2D.cxx
new file mode 100644
index 0000000000..09adecbb9b
--- /dev/null
+++ b/reco/detectors/trd/unpack/CbmTrdUnpackAlgo2D.cxx
@@ -0,0 +1,154 @@
+/* Copyright (C) 2010 - 2021 Goethe-University Frankfurt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pascal Raisig */
+
+
+#include "CbmTrdUnpackAlgo2D.h"
+
+#include "CbmMcbm2020TrdTshiftPar.h"
+#include "CbmTrdDigi.h"
+#include "CbmTrdHardwareSetupR.h"  // for channel address parameters
+#include "CbmTrdParSetAsic.h"
+
+#include <FairParGenericSet.h>
+#include <FairTask.h>
+#include <Logger.h>
+
+#include <Rtypes.h>
+#include <RtypesCore.h>
+
+
+CbmTrdUnpackAlgo2D::CbmTrdUnpackAlgo2D() : CbmRecoUnpackAlgo(fgkFlesSubsystemIdTrdR, "CbmTrdUnpackAlgo2D") {}
+
+CbmTrdUnpackAlgo2D::~CbmTrdUnpackAlgo2D() {}
+
+// ---- digestOutput ----
+void CbmTrdUnpackAlgo2D::digestOutput(std::unique_ptr<CbmTrdDigi> digi)
+{
+  ++fNrCreatedDigis;
+
+  // If requested lets monitor something
+  if (fMonitor) { fMonitor->FillHistos(digi.get(), nullptr); }
+
+  // Save the digi
+  fOutputVec.emplace_back(*std::move(digi));
+}
+
+// ---- getAsicAddress ----
+std::uint32_t CbmTrdUnpackAlgo2D::getAsicAddress(std::uint32_t criid, std::uint32_t crobid, std::uint32_t elinkid)
+{
+  size_t spadicHwAddress = 0;
+  spadicHwAddress = elinkid + (CbmTrdParAsic::kCriIdPosition * criid) + (CbmTrdParAsic::kCrobIdPosition * crobid);
+  auto mapIt      = fSpadicAddressMap.find(spadicHwAddress);  // check if asic exists
+  if (mapIt == fSpadicAddressMap.end()) {
+    LOG(info) << fName
+              << "::makeDigi - No asic address "
+                 "found for Spadic hardware address "
+              << spadicHwAddress;
+    return 0;
+  }
+
+  return mapIt->second;
+}
+
+// ---- getAsicAddress ----
+std::uint32_t CbmTrdUnpackAlgo2D::getChannelId(std::uint32_t asicaddress, std::uint32_t elinkid,
+                                               std::uint32_t elinkchannelid)
+{
+  // GetChannelId per eLink add NSPADICCH / 2 to the second(first) eLink in the case we start with odd(even) eLinks, since, our mapping is based on odd eLinks
+  auto asicChannelId =
+    (elinkid % 2) == fIsFirstChannelsElinkEven ? elinkchannelid : elinkchannelid + (CbmTrdSpadic::GetNrChannels() / 2);
+
+  auto channelId = (fAsicChannelMap.find(asicaddress))->second.at(asicChannelId);
+
+  return channelId;
+}
+
+// ---- initParSet(FairParGenericSet* parset) ----
+Bool_t CbmTrdUnpackAlgo2D::initParSet(FairParGenericSet* parset)
+{
+  LOG(info) << fName << "::initParSet - for container " << parset->ClassName();
+  if (parset->IsA() == CbmTrdParSetAsic::Class()) return initParSet(static_cast<CbmTrdParSetAsic*>(parset));
+
+  if (parset->IsA() == CbmTrdParSetDigi::Class()) return initParSet(static_cast<CbmTrdParSetDigi*>(parset));
+
+  if (parset->IsA() == CbmMcbm2020TrdTshiftPar::Class())
+    return initParSet(static_cast<CbmMcbm2020TrdTshiftPar*>(parset));
+
+  // If we do not know the derived ParSet class we return false
+  LOG(error)
+    << fName << "::initParSet - for container " << parset->ClassName()
+    << " failed, since CbmTrdUnpackAlgo2D::initParSet() does not know the derived ParSet and what to do with it!";
+  return kFALSE;
+}
+
+// ---- initParSet(CbmTrdParSetAsic* parset) ----
+Bool_t CbmTrdUnpackAlgo2D::initParSet(CbmTrdParSetAsic* parset)
+{
+  LOG(debug) << fName << "::initParSetAsic - ";
+  CbmTrdHardwareSetupR hwSetup;
+  fSpadicAddressMap = hwSetup.CreateHwToSwAsicAddressTranslatorMap(parset);
+  if (fSpadicAddressMap.empty()) {
+    LOG(error) << fName << "::initParSetAsic - SpadicAddressMap creation failed, the map is empty";
+    return kFALSE;
+  }
+  // at least check that the loaded map is not empty
+
+  fAsicChannelMap = hwSetup.CreateAsicChannelMap(parset);
+  if (fAsicChannelMap.empty()) {
+    LOG(error) << fName << "::initParSetAsic - AsicChannelMap creation failed, the map is empty";
+    return kFALSE;
+  }
+
+
+  LOG(info) << fName << "::initParSetAsic - Successfully initialized Spadic hardware address map";
+
+  return kTRUE;
+}
+
+// ---- initParSet(CbmTrdParSetDigi* parset) ----
+Bool_t CbmTrdUnpackAlgo2D::initParSet(CbmTrdParSetDigi* parset)
+{
+  Bool_t initOk = kTRUE;
+  // The monitor needs the ParSetDigi to extract module informations and other stuff
+  if (fMonitor) {
+    LOG(info) << fName << "::initParSet(CbmTrdParSetDigi) - Forwarding ParSetDigi to the monitor";
+    initOk &= fMonitor->Init(parset);
+  }
+
+  return initOk;
+}
+
+// ---- initParSet(CbmMcbm2020TrdTshiftPar* parset) ----
+Bool_t CbmTrdUnpackAlgo2D::initParSet(CbmMcbm2020TrdTshiftPar* parset)
+{
+  auto maptimeshifts = parset->GetTimeshiftsMap();
+  fTimeshiftsMap.clear();
+  fTimeshiftsMap.insert(maptimeshifts->begin(), maptimeshifts->end());
+  LOG(info) << fName << "::initParSetTimeshift2020() - Parsing timeshift correction map to unpacker algo";
+
+  return (!fTimeshiftsMap.empty());
+}
+
+// ---- initR ----
+Bool_t CbmTrdUnpackAlgo2D::initR()
+{
+  auto initOk = kTRUE;
+  // Check if we have already a Spadic object. If not check if we have a raw to digi object, it has by default a Spadic object. If we do not have a rtd object we create a default spadic object on our own.
+  if (!fSpadic) {
+    // First we check if there is a spadic definition stored in the raw to digi method
+    if (fRTDMethod) fSpadic = fRTDMethod->GetSpadicObject();
+    // If we still do not have spadic, we create it on our own
+    if (!fSpadic) fSpadic = std::make_shared<CbmTrdSpadic>();
+  }
+  if (!fSpadic) {
+    LOG(error) << fName
+               << "::initR - We are missing a CbmTrdSpadic object, to extract the basic spadic functionalities!";
+    initOk &= kFALSE;
+  }
+  if (fMonitor) fMonitor->SetSpadicObject(fSpadic);
+
+  return initOk;
+}
+
+ClassImp(CbmTrdUnpackAlgo2D)
diff --git a/reco/detectors/trd/unpack/CbmTrdUnpackAlgo2D.h b/reco/detectors/trd/unpack/CbmTrdUnpackAlgo2D.h
new file mode 100644
index 0000000000..fe510cffb0
--- /dev/null
+++ b/reco/detectors/trd/unpack/CbmTrdUnpackAlgo2D.h
@@ -0,0 +1,236 @@
+/* Copyright (C) 2010 - 2021 Goethe-University Frankfurt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pascal Raisig */
+
+/**
+ * @file CbmTrdUnpackAlgo2D.h
+ * @author Pascal Raisig (praisig@ikf.uni-frankfurt.de)
+ * @brief Baseclass for the TrdR unpacker algorithms
+ * @version 0.1
+ * @date 2021-04-21
+ * 
+ * @copyright Copyright (c) 2021
+ * 
+ * This is the base class for the algorithmic part of the tsa data unpacking 
+ * processes of the CbmTrd.
+ * The actual translation from tsa to digi happens in the derived classes. 
+ * 
+ * 
+*/
+
+#ifndef CbmTrdUnpackAlgo2D_H
+#define CbmTrdUnpackAlgo2D_H
+
+#include "CbmMcbm2020TrdTshiftPar.h"
+#include "CbmRecoUnpackAlgo.tmpl"
+#include "CbmTrdDigi.h"
+#include "CbmTrdParSetAsic.h"
+#include "CbmTrdRawMessageSpadic.h"
+#include "CbmTrdRawToDigiBaseR.h"
+#include "CbmTrdSpadic.h"
+#include "CbmTrdUnpackMonitor.h"
+
+#include "Timeslice.hpp"  // timeslice
+
+#include <Rtypes.h>  // for types
+#include <RtypesCore.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <utility>
+
+class CbmTrdUnpackAlgo2D : public CbmRecoUnpackAlgo<CbmTrdDigi> {
+public:
+  /** @brief Create the Cbm Trd Unpack AlgoBase object */
+  CbmTrdUnpackAlgo2D();
+
+  /** @brief Destroy the Cbm Trd Unpack Task object */
+  virtual ~CbmTrdUnpackAlgo2D();
+
+  /** @brief Copy constructor - not implemented **/
+  CbmTrdUnpackAlgo2D(const CbmTrdUnpackAlgo2D&) = delete;
+
+  /** @brief Assignment operator - not implemented **/
+  CbmTrdUnpackAlgo2D& operator=(const CbmTrdUnpackAlgo2D&) = delete;
+
+  // Setters
+  /** @brief Set a predefined monitor @param monitor predefined unpacking monitor */
+  void SetMonitor(std::shared_ptr<CbmTrdUnpackMonitor> monitor) { fMonitor = monitor; }
+
+  /**
+   * @brief Set the Raw To Digi Method
+   * 
+   * @param value 
+  */
+  void SetRawToDigiMethod(std::shared_ptr<CbmTrdRawToDigiBaseR> value) { fRTDMethod = value; }
+
+  /**
+   * @brief Set the Spadic Object
+   * 
+   * @param value 
+  */
+  void SetSpadicObject(std::shared_ptr<CbmTrdSpadic> value) { fSpadic = value; }
+
+protected:
+  /**
+   * @brief Handle the output created by the explicit algorithms. E.g. write to output vectors.
+   * 
+   * @param digi 
+   * @param raw 
+  */
+  void digestOutput(std::unique_ptr<CbmTrdDigi> digi);
+
+  /** @brief Finish function for this algorithm base clase */
+  void finish()
+  {
+    finishDerived();
+    // Finish the monitor if we have one
+    if (fMonitor) fMonitor->Finish();
+  }
+
+  /** @brief Function that allows special calls during Finish in the derived algos */
+  // virtual void finishDerived() = 0;
+  virtual void finishDerived() { return; }
+
+  /**
+   * @brief Get the Asic Address (CbmAddress scheme) for the given hardware Ids
+   * 
+   * @param criid 
+   * @param crobid 
+   * @param elinkid 
+   * @return std::uint32_t 
+  */
+  virtual std::uint32_t getAsicAddress(std::uint32_t criid, std::uint32_t crobid, std::uint32_t elinkid);
+
+  /**
+   * @brief Get the Channel Id (CbmAddress scheme) for the given hardware Ids
+   * 
+   * @param asicaddress 
+   * @param elinkid 
+   * @param elinkchannelid 
+   * @return std::uint32_t 
+  */
+  std::uint32_t getChannelId(std::uint32_t asicaddress, std::uint32_t elinkid, std::uint32_t elinkchannelid);
+
+  /**
+   * @brief Additional initialisation function for all BaseR derived algorithms.
+   * 
+   * @return Bool_t initOk 
+  */
+  virtual Bool_t init() { return initR(); }
+
+  /**
+   * @brief Additional initialisation function for all BaseR derived algorithms.
+   * 
+   * @return Bool_t initOk 
+  */
+  virtual Bool_t initR();
+
+  // Initialise par set, the base function handles the casting to distribute the pointers to their explicit functions
+
+  /**
+   * @brief Handles the distribution of the hidden derived classes to their explicit functions.
+   * 
+   * @param parset 
+   * @return Bool_t initOk 
+  */
+  Bool_t initParSet(FairParGenericSet* parset);
+
+  /**
+   * @brief Transfer parameters from ParSetAsic container to members.
+   *  
+   * Currently we mainly transfer here the addressing, other parameters are yet to come.
+   *  
+   * @param parset 
+   * @return Bool_t initOk 
+  */
+  Bool_t initParSet(CbmTrdParSetAsic* parset);
+
+  /**
+   * @brief Transfer parameters from ParSetDigi container to members.
+   *  
+   * The monitor needs this to extract module informations, e.g. ncols and nrows.
+   *  
+   * @param parset 
+   * @return Bool_t initOk 
+  */
+  Bool_t initParSet(CbmTrdParSetDigi* parset);
+
+  /**
+   * @brief Transfer parameters from CbmMcbm2020TrdTshiftPar container to members.
+   *  
+   * These are mCBM 2020 specific parameters needed to correct in run timeshifts. 
+   *  
+   * @param parset 
+   * @return Bool_t initOk 
+  */
+  Bool_t initParSet(CbmMcbm2020TrdTshiftPar* parset);
+
+  /**
+   * @brief Unpack a given microslice.
+   * 
+   * @param ts timeslice pointer
+   * @param icomp index to the component to be unpacked
+   * @param imslice index of the microslice to be unpacked
+   * @return true 
+   * @return false 
+   * 
+   * @remark The content of the µslice can only be accessed via the timeslice. Hence, we need to pass the pointer to the full timeslice
+  */
+  bool unpack(const fles::Timeslice* ts, std::uint16_t icomp, UInt_t imslice)
+  {
+    LOG(info) << "HelloWorld";
+    return true;
+  }
+
+  // Monitoring
+  /** @brief Potential (online) monitor for the unpacking process */
+  std::shared_ptr<CbmTrdUnpackMonitor> fMonitor = nullptr;
+
+  // Parameter storage members
+  /** @brief Spadic software reprensentation object */
+  std::shared_ptr<CbmTrdSpadic> fSpadic = nullptr;
+
+  /** @brief raw to digi extraction method, set in the task */
+  std::shared_ptr<CbmTrdRawToDigiBaseR> fRTDMethod = nullptr;
+
+  /** @brief Map to retrieve asic address from CriId/CrobId/ElinkId (see CbmTrdHardwareSetupR) */
+  std::map<size_t, Int_t> fSpadicAddressMap = {};
+
+  /** @brief Map to retrieve module channelId from asicAddress and asicChannel */
+  std::map<Int_t, std::vector<Int_t>> fAsicChannelMap = {};
+
+  /** @brief Map containing the timeshift parameters for the correction of the µSlice timeshifts. The keys are the tsIdx, if no key is found, the shifts of the previous tsIdx are used again */
+  std::map<size_t, std::vector<Int_t>> fTimeshiftsMap = {};
+
+  /** @brief Define if the first 16 channels (00..15) are found on the even (set true) or odd (false) eLinkId.
+   * Default for mCbm2020 is false thus, initialized as false */
+  Bool_t fIsFirstChannelsElinkEven = kFALSE;
+
+  /** @brief Number of rda frames outside of a SOM frame range */
+  size_t fNrWildRda = 0;
+
+  /** @brief Number of eom frames outside of a SOM frame range */
+  size_t fNrWildEom = 0;
+
+  /** @brief Number of wild null words, should only appear at the end of a µSlice */
+  size_t fNrWildNul = 0;
+
+  /** @brief Number of unknown words */
+  size_t fNrUnknownWords = 0;
+
+  /** @brief length of one ts_msb in [ns] */
+  static constexpr std::uint16_t fTsMsbLength = 16000;
+
+  /** @brief length of one ts_msb in [cc] */
+  size_t fTsMsbLengthCC = fTsMsbLength / CbmTrdSpadic::GetClockCycle();
+
+  /** @brief Fles Subsystem Identifier for the TRD R data */
+  static constexpr int fgkFlesSubsystemIdTrdR = static_cast<int>(fles::SubsystemIdentifier::TRD);
+
+private:
+  ClassDef(CbmTrdUnpackAlgo2D, 2)
+};
+
+#endif  // CbmTrdUnpackAlgo2D_H
diff --git a/reco/detectors/trd/unpack/CbmTrdUnpackAlgoLegacy2020R.cxx b/reco/detectors/trd/unpack/CbmTrdUnpackAlgoLegacy2020R.cxx
index 7a592f93c2..2fbb3e4ef2 100644
--- a/reco/detectors/trd/unpack/CbmTrdUnpackAlgoLegacy2020R.cxx
+++ b/reco/detectors/trd/unpack/CbmTrdUnpackAlgoLegacy2020R.cxx
@@ -295,6 +295,9 @@ void CbmTrdUnpackAlgoLegacy2020R::makeDigi(CbmTrdRawMessageSpadic raw)
   }
   time -= fSystemTimeoffset;
 
+  // Set the time relative to the TS start
+  time -= fTsStartTime;
+
   auto digi = fRTDMethod->MakeDigi(raw.GetSamples(), padChNr, uniqueModuleId, time, triggerType, errClass);
 
   // Digest the output, i.e. write to return vector and optional pass to the monitor
diff --git a/reco/detectors/trd/unpack/CbmTrdUnpackAlgoR.cxx b/reco/detectors/trd/unpack/CbmTrdUnpackAlgoR.cxx
index b24e3c8cdd..87cafeb82a 100644
--- a/reco/detectors/trd/unpack/CbmTrdUnpackAlgoR.cxx
+++ b/reco/detectors/trd/unpack/CbmTrdUnpackAlgoR.cxx
@@ -290,8 +290,14 @@ void CbmTrdUnpackAlgoR::makeDigi(CbmTrdRawMessageSpadic raw)
   ULong64_t time = raw.GetTime();
   time -= fSystemTimeoffset;
 
+  // Set the time relative to the TS start
+  time -= fTsStartTime;
+
   auto digi = fRTDMethod->MakeDigi(raw.GetSamples(), padChNr, uniqueModuleId, time, triggerType, errClass);
 
+  // If the raw message was flagged as multi hit, forward this info to the digi
+  if (raw.GetMultiHit()) digi->SetTriggerType(CbmTrdDigi::eTriggerType::kMulti);
+
   // Digest the output, i.e. write to return vector and optional pass to the monitor
   digestOutput(std::move(digi), raw);
 }
diff --git a/reco/detectors/trd/unpack/CbmTrdUnpackConfig2D.cxx b/reco/detectors/trd/unpack/CbmTrdUnpackConfig2D.cxx
new file mode 100644
index 0000000000..c66a9964ec
--- /dev/null
+++ b/reco/detectors/trd/unpack/CbmTrdUnpackConfig2D.cxx
@@ -0,0 +1,79 @@
+/* Copyright (C) 2010 - 2021 Goethe-University Frankfurt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pascal Raisig */
+
+#include "CbmTrdUnpackConfig2D.h"
+
+#include "CbmTrdUnpackAlgo2D.h"
+
+#include <Logger.h>
+
+#include <Rtypes.h>
+#include <RtypesCore.h>
+
+#include <memory>
+#include <vector>
+
+CbmTrdUnpackConfig2D::CbmTrdUnpackConfig2D(std::string detGeoSetupTag, UInt_t runid)
+  : CbmRecoUnpackConfig("CbmTrdUnpackConfig2D")
+  , fGeoSetupTag(detGeoSetupTag)
+  , fRunId(runid)
+{
+}
+
+CbmTrdUnpackConfig2D::~CbmTrdUnpackConfig2D() {}
+
+// ---- Init ----
+void CbmTrdUnpackConfig2D::InitUnpacker()
+{
+  LOG(info) << fName << "::Init -";
+
+  auto initOk = kTRUE;
+
+  // First choose the derived unpacking algorithm to be used and pass the raw to digi method
+  auto algo = chooseAlgo();
+
+  // If we got a handmade spadic we pass it to the algorithm and the RTD method
+  if (fSpadic) {
+    algo->SetSpadicObject(fSpadic);
+    fRTDMethod->SetSpadicObject(fSpadic);
+  }
+
+  if (fDoLog) LOG(info) << fName << "::Init - SetRawToDigiMethod";
+  algo->SetRawToDigiMethod(fRTDMethod);
+
+  if (fDoLog) LOG(info) << fName << "::Init - SetParFilesBasePath";
+  algo->SetParFilesBasePath(fParFilesBasePath);
+
+  // If we have a monitor in the config add it to the algo
+  if (fMonitor) algo->SetMonitor(fMonitor);
+
+  // Initialise the parameter containers required by the unpacker algo. Includes loading the corresponding ascii files
+  auto reqparvec = algo->GetParContainerRequest(fGeoSetupTag, fRunId);
+  initOk &= initParContainers(reqparvec);
+
+  // Now we have all information required to initialise the algorithm
+  algo->Init();
+
+  // Pass the algo to its member in the base class
+  fAlgo = algo;
+}
+
+// ---- chooseAlgo ----
+std::shared_ptr<CbmTrdUnpackAlgo2D> CbmTrdUnpackConfig2D::chooseAlgo()
+{
+  if (fDoLog) LOG(info) << fName << "::Init - chooseAlgo";
+
+  // Default unpacker selection
+  // Unpacker algo from mcbm 2021 on and hopefully default for a long time.
+  auto algo = std::make_shared<CbmTrdUnpackAlgo2D>();
+  LOG(info) << fName << "::chooseAlgo() - selected algo = " << algo->Class_Name();
+  return algo;
+
+  LOG(error) << fName
+             << "::Init - chooseAlgo() - no algorithm created something went wrong. We can not work like this!";
+  return nullptr;
+}
+
+
+ClassImp(CbmTrdUnpackConfig2D)
diff --git a/reco/detectors/trd/unpack/CbmTrdUnpackConfig2D.h b/reco/detectors/trd/unpack/CbmTrdUnpackConfig2D.h
new file mode 100644
index 0000000000..f5a1137a0d
--- /dev/null
+++ b/reco/detectors/trd/unpack/CbmTrdUnpackConfig2D.h
@@ -0,0 +1,122 @@
+/* Copyright (C) 2010 - 2021 Goethe-University Frankfurt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pascal Raisig */
+
+/**
+ * @file CbmTrdUnpackConfig2D.h
+ * @author Pascal Raisig (praisig@ikf.uni-frankfurt.de)
+ * @brief Configuration class for an unpacker algorithm
+ * @version 0.1
+ * @date 2021-04-21
+ * 
+ * @copyright Copyright (c) 2021
+ * 
+ * This is the common configuration class for unpacking algorithms
+ * 
+*/
+
+#ifndef CbmTrdUnpackConfig2D_H
+#define CbmTrdUnpackConfig2D_H
+
+
+#include "CbmRecoUnpackConfig.tmpl"
+#include "CbmTrdRawToDigiBaseR.h"
+#include "CbmTrdRawToDigiMaxAdcR.h"
+#include "CbmTrdUnpackAlgo2D.h"
+#include "CbmTrdUnpackMonitor.h"
+
+#include <FairLogger.h>
+#include <Logger.h>
+
+#include <Rtypes.h>
+#include <RtypesCore.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+class CbmTrdUnpackConfig2D : public CbmRecoUnpackConfig<CbmTrdUnpackAlgo2D, CbmTrdDigi> {
+
+public:
+  /**
+   * @brief Create the Cbm Trd Unpack Task object
+   * 
+   * @param geoSetupTag Geometry setup tag for the given detector as used by CbmSetup objects
+   * @param runid set if unpacker is rerun on a special run with special parameters
+   *@remark We use the string instead of CbmSetup here, to not having to link against sim/steer...
+  */
+  CbmTrdUnpackConfig2D(std::string detGeoSetupTag, UInt_t runid = 0);
+
+  /**
+   * @brief Destroy the Cbm Trd Unpack Task object
+   * 
+  */
+  virtual ~CbmTrdUnpackConfig2D();
+
+  /** @brief Copy constructor - not implemented **/
+  CbmTrdUnpackConfig2D(const CbmTrdUnpackConfig2D&) = delete;
+
+  /** @brief Assignment operator - not implemented **/
+  CbmTrdUnpackConfig2D& operator=(const CbmTrdUnpackConfig2D&) = delete;
+
+  // Getters
+  /** @brief Get the potentially added monitor. */
+  std::shared_ptr<CbmTrdUnpackMonitor> GetMonitor() { return fMonitor; }
+
+  /** @brief Get the spadic object attached to this config. @return CbmTrdSpadic */
+  std::shared_ptr<CbmTrdSpadic> GetSpadicObject() { return fSpadic; }
+
+  /**
+   * @brief Prepare the unpacker to be ready to run.
+   * In this function all initialization steps of the unpacker algorithms happen.
+  */
+  void InitUnpacker();
+
+  // Setters
+
+  /** @brief Add a monitor to the unpacker. @param value CbmTrdUnpackMonitor */
+  void SetMonitor(std::shared_ptr<CbmTrdUnpackMonitor> value) { fMonitor = value; }
+
+  /**
+   * @brief Set the raw to digi method
+   * 
+   * @param value 
+  */
+  void SetRawToDigiMethod(std::shared_ptr<CbmTrdRawToDigiBaseR> value) { fRTDMethod = value; }
+
+  /**
+   * @brief Set the Spadic Object
+   * 
+   * @param value 
+  */
+  void SetSpadicObject(std::shared_ptr<CbmTrdSpadic> value) { fSpadic = value; }
+
+protected:
+  /**
+   * @brief Choose the derived unpacker algorithm to be used for the DAQ output to Digi translation. If algo was already set manually by the user this algorithm is used.
+   * 
+   * @return Bool_t initOk 
+  */
+  virtual std::shared_ptr<CbmTrdUnpackAlgo2D> chooseAlgo();
+
+  /** @brief pointer to the raw msg to digi method */
+  std::shared_ptr<CbmTrdRawToDigiBaseR> fRTDMethod = std::make_shared<CbmTrdRawToDigiMaxAdcR>();
+
+  /** @brief Spadic software reprensentation object */
+  std::shared_ptr<CbmTrdSpadic> fSpadic = nullptr;
+
+  /** @brief pointer to the monitor object */
+  std::shared_ptr<CbmTrdUnpackMonitor> fMonitor = nullptr;
+
+  /** @brief Geometry setup tag for the given detector as used by CbmSetup objects */
+  std::string fGeoSetupTag = "";
+
+  /** @brief RunId of the current run, if not known 0 is a valid runtime case. Used runId based parameter loading. */
+  UInt_t fRunId = 0;
+
+private:
+  ClassDef(CbmTrdUnpackConfig2D, 2)
+};
+
+#endif  // CbmTrdUnpackConfig2D_H
diff --git a/reco/detectors/trd/unpack/CbmTrdUnpackMonitor.cxx b/reco/detectors/trd/unpack/CbmTrdUnpackMonitor.cxx
index c15a838069..d5ff730b3f 100644
--- a/reco/detectors/trd/unpack/CbmTrdUnpackMonitor.cxx
+++ b/reco/detectors/trd/unpack/CbmTrdUnpackMonitor.cxx
@@ -92,32 +92,10 @@ void CbmTrdUnpackMonitor::Finish()
     // std::unique_ptr<TFile> histofile(new TFile(fOutfilename.data(), "RECREATE"));
     TFile histofile(fOutfilename.data(), "RECREATE");
 
-    // Move into the histofile
-    // histofile->cd();
-    histofile.cd();
-    /// Write all the histograms to the file.
-    for (auto histopair : fHistoVec) {
-      // Make sure we end up in chosen folder
-      if (nullptr == gDirectory->Get(histopair.first.data())) gDirectory->mkdir(histopair.first.data());
-      gDirectory->cd(histopair.first.data());
-
-      // Now lets extract whether we have a digi, raw or other histo and move (create) to the subdirectory
-      auto histotype = getHistoType(histopair.second);
-      // (Create and) Move to the directory of the type
-      if (nullptr == gDirectory->Get(histotype.data())) gDirectory->mkdir(histotype.data());
-      gDirectory->cd(histotype.data());
-
-
-      // Write histogram
-      LOG(debug) << Class_Name() << "::Finish() Write histo " << histopair.second->GetName() << " to file "
-                 << histofile.GetName() << " in folder " << histopair.first.data() << "/" << histotype.data();
-      histopair.second->Write();
-      // Move back to root directory of the output file
-      // histofile->cd();
-      histofile.cd();
-
-      nhistos++;
-    }
+    nhistos += writeHistosToFile(&fDigiHistoMap, &histofile);
+    nhistos += writeHistosToFile(&fRawHistoMap, &histofile);
+    nhistos += writeHistosToFile(&fOtherHistoMap, &histofile);
+
     /// Restore old global file and folder pointer to avoid messing with FairRoot
     gFile      = oldFile;
     gDirectory = oldDir;
@@ -170,7 +148,9 @@ void CbmTrdUnpackMonitor::createHisto(eDigiHistos kHisto)
     auto rotation  = fModuleOrientation.find(moduleid)->second;
 
     histoname = "ModuleId_" + std::to_string(moduleid) + "-";
+    histoname += getTypeName(kHisto) + "_";
     histoname += getHistoName(kHisto);
+
     switch (kHisto) {
       case eDigiHistos::kMap:
       case eDigiHistos::kMap_St:
@@ -209,6 +189,13 @@ void CbmTrdUnpackMonitor::createHisto(eDigiHistos kHisto)
         newhisto->GetXaxis()->SetBinLabel((static_cast<int>(CbmTrdDigi::eTriggerType::kNeighbor) + 1), "Nt");
         newhisto->GetXaxis()->SetBinLabel((static_cast<int>(CbmTrdDigi::eTriggerType::kMulti) + 1), "Multihit");
 
+        break;
+      case eDigiHistos::kChannel:
+      case eDigiHistos::kChannel_St:
+      case eDigiHistos::kChannel_Nt:
+        newhisto = std::make_shared<TH1I>(histoname.data(), histoname.data(), nchannels, -0.5, (nchannels - 0.5));
+        newhisto->SetXTitle("ChannelId");
+        newhisto->SetYTitle("Counts");
         break;
       case eDigiHistos::kDigiDeltaT:
         newhisto = std::make_shared<TH2I>(histoname.data(), histoname.data(), nchannels, -0.5, (nchannels - 0.5), 3500,
@@ -216,7 +203,7 @@ void CbmTrdUnpackMonitor::createHisto(eDigiHistos kHisto)
         newhisto->SetXTitle("ChannelId");
         newhisto->SetYTitle("#DeltaT(Digi(n)-Digi(n-1)) [ns]");
         break;
-      default: return; break;
+        // default: return; break;
     }
     LOG(debug) << Class_Name() << "::CreateHisto() HistoDigi " << static_cast<size_t>(kHisto) << " Module " << moduleid
                << " initialized as " << histoname.data();
@@ -236,6 +223,7 @@ void CbmTrdUnpackMonitor::createHisto(eRawHistos kHisto)
     auto nchannels = nrows * ncols;
 
     histoname = "ModuleId_" + std::to_string(moduleid) + "-";
+    histoname += getTypeName(kHisto) + "_";
     histoname += getHistoName(kHisto);
     switch (kHisto) {
       case eRawHistos::kSignalshape:
@@ -292,6 +280,7 @@ void CbmTrdUnpackMonitor::createHisto(eOtherHistos kHisto)
 
   for (auto moduleid : fModuleIdsVec) {
     histoname = "ModuleId_" + std::to_string(moduleid) + "-";
+    histoname += getTypeName(kHisto) + "_";
     histoname += getHistoName(kHisto);
     switch (kHisto) {
       case eOtherHistos::kSpadic_Info_Types: {
@@ -348,7 +337,9 @@ void CbmTrdUnpackMonitor::createHistos()
 void CbmTrdUnpackMonitor::fillHisto(CbmTrdDigi* digi, eDigiHistos kHisto, std::uint32_t moduleid,
                                     std::shared_ptr<TH1> histo)
 {
-  auto triggertype = static_cast<CbmTrdDigi::eTriggerType>(digi->GetTriggerType());
+  auto triggerpair = digi->GetTriggerPair(digi->GetTriggerType());
+  auto triggertype = triggerpair.first;
+  auto isMultihit  = triggerpair.second;
   switch (kHisto) {
     case eDigiHistos::kMap_St: {
       if (triggertype != CbmTrdDigi::eTriggerType::kSelf) { return; }
@@ -382,7 +373,21 @@ void CbmTrdUnpackMonitor::fillHisto(CbmTrdDigi* digi, eDigiHistos kHisto, std::u
       histo->Fill(digi->GetCharge());
       break;
 
-    case eDigiHistos::kTriggerType: histo->Fill(digi->GetTriggerType()); break;
+    case eDigiHistos::kChannel: histo->Fill(digi->GetAddressChannel()); break;
+    case eDigiHistos::kChannel_St:
+      if (triggertype != CbmTrdDigi::eTriggerType::kSelf) { return; }
+      histo->Fill(digi->GetAddressChannel());
+      break;
+    case eDigiHistos::kChannel_Nt:
+      if (triggertype != CbmTrdDigi::eTriggerType::kNeighbor) { return; }
+      histo->Fill(digi->GetAddressChannel());
+      break;
+
+    case eDigiHistos::kTriggerType: {
+      if (isMultihit) histo->Fill(static_cast<int>(CbmTrdDigi::eTriggerType::kMulti));
+      histo->Fill(static_cast<int>(triggertype));
+      break;
+    }
     case eDigiHistos::kDigiDeltaT: histo->Fill(digi->GetAddressChannel(), getDeltaT(digi)); break;
 
     default: return; break;
@@ -420,7 +425,7 @@ void CbmTrdUnpackMonitor::fillHisto(CbmTrdRawMessageSpadic* raw, eRawHistos kHis
       histo->Fill(digi->GetAddressChannel(), raw->GetSamples()->at(0));
       break;
     }
-    case eRawHistos::kHitType: histo->Fill(raw->GetHitType()); break;
+    case eRawHistos::kHitType: histo->Fill(static_cast<int>(raw->GetHitType())); break;
     default: return; break;
   }
 }
@@ -459,7 +464,7 @@ std::double_t CbmTrdUnpackMonitor::getDeltaT(CbmTrdDigi* digi)
 // ---- getHistoName ----
 std::string CbmTrdUnpackMonitor::getHistoName(eDigiHistos kHisto)
 {
-  std::string histoname = "Digi_";
+  std::string histoname = "";
 
   switch (kHisto) {
     case eDigiHistos::kMap: histoname += "Map"; break;
@@ -468,10 +473,11 @@ std::string CbmTrdUnpackMonitor::getHistoName(eDigiHistos kHisto)
     case eDigiHistos::kCharge: histoname += "Charge"; break;
     case eDigiHistos::kCharge_St: histoname += "Charge_St"; break;
     case eDigiHistos::kCharge_Nt: histoname += "Charge_Nt"; break;
+    case eDigiHistos::kChannel: histoname += "Channel"; break;
+    case eDigiHistos::kChannel_St: histoname += "Channel_St"; break;
+    case eDigiHistos::kChannel_Nt: histoname += "Channel_Nt"; break;
     case eDigiHistos::kTriggerType: histoname += "TriggerType"; break;
     case eDigiHistos::kDigiDeltaT: histoname += "DigiDeltaT"; break;
-
-    default: return ""; break;
   }
   return histoname;
 }
@@ -479,7 +485,7 @@ std::string CbmTrdUnpackMonitor::getHistoName(eDigiHistos kHisto)
 // ---- getHistoName ----
 std::string CbmTrdUnpackMonitor::getHistoName(eRawHistos kHisto)
 {
-  std::string histoname = "Raw_";
+  std::string histoname = "";
 
   switch (kHisto) {
     case eRawHistos::kSignalshape: histoname += "Signalshape"; break;
@@ -492,7 +498,6 @@ std::string CbmTrdUnpackMonitor::getHistoName(eRawHistos kHisto)
     case eRawHistos::kSampleDistStdDev: histoname += "SampleDistStdDev"; break;
     case eRawHistos::kSample0perChannel: histoname += "Sample0perChannel"; break;
     case eRawHistos::kHitType: histoname += "HitType"; break;
-    default: return ""; break;
   }
   return histoname;
 }
@@ -500,12 +505,11 @@ std::string CbmTrdUnpackMonitor::getHistoName(eRawHistos kHisto)
 // ---- getHistoName ----
 std::string CbmTrdUnpackMonitor::getHistoName(eOtherHistos kHisto)
 {
-  std::string histoname = "Other_";
+  std::string histoname = "";
 
   switch (kHisto) {
     case eOtherHistos::kSpadic_Info_Types: histoname += "Spadic_Info_Types"; break;
     case eOtherHistos::kMs_Flags: histoname += "Ms_Flags"; break;
-    default: return ""; break;
   }
   return histoname;
 }
diff --git a/reco/detectors/trd/unpack/CbmTrdUnpackMonitor.h b/reco/detectors/trd/unpack/CbmTrdUnpackMonitor.h
index ddca97906e..99767b260d 100644
--- a/reco/detectors/trd/unpack/CbmTrdUnpackMonitor.h
+++ b/reco/detectors/trd/unpack/CbmTrdUnpackMonitor.h
@@ -31,6 +31,7 @@
 
 #include <FairRunOnline.h>
 #include <FairTask.h>
+#include <Logger.h>
 
 #include <Rtypes.h>  // for types
 #include <RtypesCore.h>
@@ -40,6 +41,7 @@
 #include <cstdint>
 #include <map>
 #include <memory>
+#include <string>
 #include <vector>
 
 #include <cmath>
@@ -52,13 +54,14 @@ public:
     kMap = 0,
     kMap_St,
     kMap_Nt,
+    kChannel,
+    kChannel_St,
+    kChannel_Nt,
     kCharge,
     kCharge_St,
     kCharge_Nt,
     kTriggerType,
-    kDigiDeltaT,
-    // kDigiMeanHitFrequency,  // Heavy histogram add with care
-    // kDigiHitFrequency,
+    kDigiDeltaT
   };
 
   /** @brief Enum for the predefined raw histograms. */
@@ -145,11 +148,8 @@ protected:
                      std::map<histotype, std::map<std::uint32_t, std::shared_ptr<TH1>>>* histomap,
                      std::uint32_t moduleid, histotype kHisto)
   {
-    // Store the histo pointer in the global histos vec
-    {
-      auto histopair = std::make_pair(std::to_string(moduleid), histo);
-      fHistoVec.emplace_back(histopair);
-    }
+    // If the histogram already exists we stop here
+    if (checkIfHistoExists(kHisto, histomap, moduleid)) return;
 
     // Create a histo module pair
     auto histopair = std::make_pair(moduleid, histo);
@@ -177,6 +177,24 @@ protected:
     }
   }
 
+  template<typename THistotype>
+  bool checkIfHistoExists(THistotype etype,
+                          std::map<THistotype, std::map<std::uint32_t, std::shared_ptr<TH1>>>* histomap,
+                          std::uint32_t moduleid)
+  {
+    // First check if the map knows about the type, if not the histo does not exist yet.
+    auto histotypemapIt = histomap->find(etype);
+    if (histotypemapIt == histomap->end()) return false;
+
+    // Check if at the moduleId position we find something
+    auto histopair = histotypemapIt->second.find(moduleid);
+    if (histopair == histotypemapIt->second.end()) return false;
+
+    // Check if there is a pointer to the histo which not null
+    if (histopair->second != nullptr) return true;
+
+    return false;
+  }
 
   /** @brief Create the actual TH1 shared_ptrs */
   void createHistos();
@@ -229,6 +247,27 @@ protected:
   /** @brief Get the Histo Name for the given histo @param kHisto eOtherHistos @return std::string */
   std::string getHistoName(eOtherHistos kHisto);
 
+  /** @brief Get the Type Name for the given histo @param kHisto eDigiHistos @return std::string */
+  static std::string getTypeName(eDigiHistos kHisto)
+  {
+    (void) kHisto;
+    return "Digi";
+  };
+
+  /** @brief Get the Type Name for the given histo @param kHisto eRawHistos @return std::string */
+  static std::string getTypeName(eRawHistos kHisto)
+  {
+    (void) kHisto;
+    return "Raw";
+  };
+
+  /** @brief Get the Type Name for the given histo @param kHisto eOtherHistos @return std::string */
+  static std::string getTypeName(eOtherHistos kHisto)
+  {
+    (void) kHisto;
+    return "Other";
+  };
+
   /** @brief Get the Histo Type, i.e. "Digi/Raw/Other", deduced from the histo name. @param histo @return std::string */
   std::string getHistoType(std::shared_ptr<TH1> histo);
 
@@ -244,9 +283,41 @@ protected:
   /** @brief Extract the std deviation of all samples in the message */
   std::float_t getSamplesStdDev(CbmTrdRawMessageSpadic* raw);
 
-  /** @brief histogram pointers (all) stored in a vector to be accessible for THttpServer */
-  std::vector<std::pair<std::string, std::shared_ptr<TH1>>> fHistoVec = {};
+  template<class histotype>
+  size_t writeHistosToFile(std::map<histotype, std::map<std::uint32_t, std::shared_ptr<TH1>>>* histomap, TFile* file)
+  {
+    // Counter of written histos
+    size_t nhistos = 0;
+
+    // Make sure we are in the file to which we want to write our histos
+    file->cd();
+    for (auto typemappair : *histomap) {
+      for (auto histopair : typemappair.second) {
+
+        // Make sure we end up in chosen folder
+        std::string moduleidname = std::to_string(histopair.first);
+        if (nullptr == gDirectory->Get(moduleidname.data())) gDirectory->mkdir(moduleidname.data());
+        gDirectory->cd(moduleidname.data());
+
+        // Now move(create) to the histotype folder (digi, raw or other histo)
+        std::string histotypename = getTypeName(typemappair.first);
+        // (Create and) Move to the directory of the type
+        if (nullptr == gDirectory->Get(histotypename.data())) gDirectory->mkdir(histotypename.data());
+        gDirectory->cd(histotypename.data());
+
+        // Write histogram
+        LOG(debug) << Class_Name() << "::Finish() Write histo " << histopair.second->GetName() << " to file "
+                   << file->GetName() << " in folder " << moduleidname.data() << "/" << histotypename.data();
+        histopair.second->Write();
+        nhistos++;
+        // Move back to root directory of the output file
+        file->cd();
+      }
+    }
+    return nhistos;
+  }
 
+  // Member variables
   /** @brief Digi histogram pointers stored in a map together with the module id */
   std::map<eDigiHistos, std::map<std::uint32_t, std::shared_ptr<TH1>>> fDigiHistoMap = {};
 
diff --git a/reco/steer/CMakeLists.txt b/reco/steer/CMakeLists.txt
index 30157e8112..3006f4e33e 100644
--- a/reco/steer/CMakeLists.txt
+++ b/reco/steer/CMakeLists.txt
@@ -20,6 +20,11 @@ CbmSourceTsArchive.cxx
 set(INCLUDE_DIRECTORIES
 ${CBMROOT_SOURCE_DIR}/reco/steer
 ${CBMROOT_SOURCE_DIR}/reco/base
+
+${CBMROOT_SOURCE_DIR}/reco/detectors/psd/unpack
+${CBMROOT_SOURCE_DIR}/reco/detectors/rich/unpack
+${CBMROOT_SOURCE_DIR}/reco/detectors/sts/unpack
+${CBMROOT_SOURCE_DIR}/reco/detectors/tof/unpack
 ${CBMROOT_SOURCE_DIR}/reco/detectors/trd
 ${CBMROOT_SOURCE_DIR}/reco/detectors/trd/rawToDigiMethods
 ${CBMROOT_SOURCE_DIR}/reco/detectors/trd/unpack
@@ -27,9 +32,13 @@ ${CBMROOT_SOURCE_DIR}/reco/detectors/trd/unpack
 ${CBMROOT_SOURCE_DIR}/core/base
 ${CBMROOT_SOURCE_DIR}/core/data
 ${CBMROOT_SOURCE_DIR}/core/data/base
+${CBMROOT_SOURCE_DIR}/core/data/psd
+${CBMROOT_SOURCE_DIR}/core/detectors/psd
+${CBMROOT_SOURCE_DIR}/core/data/rich
+${CBMROOT_SOURCE_DIR}/core/data/sts
+# ${CBMROOT_SOURCE_DIR}/core/data/tof
 ${CBMROOT_SOURCE_DIR}/core/data/trd
 ${CBMROOT_SOURCE_DIR}/core/detectors/trd
-${CBMROOT_SOURCE_DIR}/core/data/sts
 
 
 )
@@ -47,6 +56,7 @@ set(LINK_DIRECTORIES
 ${ROOT_LIBRARY_DIR}
 ${FAIRROOT_LIBRARY_DIR}
 ${Boost_LIBRARY_DIRS}
+${KFParticle_LIB_DIR} # for rich reco
 )
 # ---------------------------------------------------------
 
@@ -57,8 +67,14 @@ Set(DEPENDENCIES
 fles_ipc
 Base
 CbmBase
+CbmPsdBase
+CbmPsdReco
+CbmRichReco
+CbmRecoSts
+# CbmTofReco
 CbmTrdReco
 CbmData
+KF
 )
 # ---------------------------------------------------------
 
diff --git a/reco/steer/CbmRecoUnpack.cxx b/reco/steer/CbmRecoUnpack.cxx
index f47821f370..b9483deef8 100644
--- a/reco/steer/CbmRecoUnpack.cxx
+++ b/reco/steer/CbmRecoUnpack.cxx
@@ -7,11 +7,17 @@
 
 #include "CbmRecoUnpack.h"
 
+#include "CbmTsEventHeader.h"
+
 #include "MicrosliceDescriptor.hpp"
 
 #include <FairRootManager.h>
 #include <Logger.h>
 
+#include <RtypesCore.h>
+
+#include <cstddef>
+#include <memory>
 #include <utility>
 #include <vector>
 
@@ -31,7 +37,13 @@ CbmRecoUnpack::CbmRecoUnpack() {}
 void CbmRecoUnpack::Finish()
 {
   LOG(info) << "CbmRecoUnpack::Finish() I do let the unpackers talk first : ";
-  fTrdConfig->GetUnpacker()->Finish();
+
+  if (fPsdConfig) fPsdConfig->GetUnpacker()->Finish();
+  if (fRichConfig) fRichConfig->GetUnpacker()->Finish();
+  if (fStsConfig) fStsConfig->GetUnpacker()->Finish();
+  // if (fTofConfig) fTofConfig->GetUnpacker()->Finish();
+  if (fTrdConfig) fTrdConfig->GetUnpacker()->Finish();
+  if (fTrdConfig2D) fTrdConfig2D->GetUnpacker()->Finish();
 }
 
 // ----------------------------------------------------------------------------
@@ -43,9 +55,32 @@ Bool_t CbmRecoUnpack::Init()
   FairRootManager* ioman = FairRootManager::Instance();
   assert(ioman);
 
-  // --- TRD
-  fTrdConfig->Init(ioman);
-  fTrdConfig->InitUnpacker();
+  // --- Register the branch for the Timeslice start time
+  fCbmTsEventHeader = new CbmTsEventHeader();
+  ioman->RegisterAny("TsEventHeader", fCbmTsEventHeader, kTRUE);
+  LOG(info) << "CbmRecoUnpack::Init() registered CbmTsEventHeader to output tree!";
+
+
+  // --- Psd
+  if (fPsdConfig) fPsdConfig->Init(ioman);
+  if (fPsdConfig) fPsdConfig->InitUnpacker();
+  // --- Rich
+  if (fRichConfig) fRichConfig->Init(ioman);
+  if (fRichConfig) fRichConfig->InitUnpacker();
+  // --- Sts
+  if (fStsConfig) fStsConfig->Init(ioman);
+  if (fStsConfig) fStsConfig->InitUnpacker();
+  // --- Tof
+  // if (fTofConfig) fTofConfig->Init(ioman);
+  // if (fTofConfig) fTofConfig->InitUnpacker();
+
+  // --- Trd
+  if (fTrdConfig) fTrdConfig->Init(ioman);
+  if (fTrdConfig) fTrdConfig->InitUnpacker();
+
+  // --- TRD2D
+  if (fTrdConfig2D) fTrdConfig2D->Init(ioman);
+  if (fTrdConfig2D) fTrdConfig2D->InitUnpacker();
 
   return kTRUE;
 }
@@ -57,8 +92,18 @@ void CbmRecoUnpack::Reset()
 {
   // Reset the unpackers for a new timeslice, e.g. clear the output vectors
 
+  // ---- Psd ----
+  if (fPsdConfig) fPsdConfig->GetUnpacker()->Finish();
+  // ---- Rich ----
+  if (fRichConfig) fRichConfig->GetUnpacker()->Finish();
+  // ---- Sts ----
+  if (fStsConfig) fStsConfig->GetUnpacker()->Finish();
+  // ---- Tof ----
+  // if (fTofConfig) fTofConfig->GetUnpacker()->Finish();
   // ---- Trd ----
-  fTrdConfig->Reset();
+  if (fTrdConfig) fTrdConfig->GetUnpacker()->Finish();
+  // ---- Trd2D ----
+  if (fTrdConfig2D) fTrdConfig2D->GetUnpacker()->Finish();
 }
 
 // ----------------------------------------------------------------------------
@@ -69,19 +114,34 @@ void CbmRecoUnpack::Unpack(unique_ptr<Timeslice> ts)
   // Prepare timeslice
   const fles::Timeslice& timeslice = *ts;
 
+  fCbmTsEventHeader->SetTsStartTime(ts->start_time());
+
   uint64_t nComponents = ts->num_components();
-  LOG(info) << "Unpack: TS index " << ts->index() << " components " << nComponents;
+  if (fDoDebugPrints) LOG(info) << "Unpack: TS index " << ts->index() << " components " << nComponents;
 
   for (uint64_t component = 0; component < nComponents; component++) {
 
     auto systemId = static_cast<std::uint16_t>(ts->descriptor(component, 0).sys_id);
 
     switch (systemId) {
+      case fkFlesPsd: {
+        if (fPsdConfig)
+          fCbmTsEventHeader->SetNDigisTrd(
+            unpack(&timeslice, component, fPsdConfig, fPsdConfig->GetOptOutAVec(), fPsdConfig->GetOptOutBVec()));
+        break;
+      }
       case fkFlesTrd: {
         if (fTrdConfig)
-          unpack(&timeslice, component, fTrdConfig, fTrdConfig->GetOptOutAVec(), fTrdConfig->GetOptOutBVec());
+          fCbmTsEventHeader->SetNDigisTrd(
+            unpack(&timeslice, component, fTrdConfig, fTrdConfig->GetOptOutAVec(), fTrdConfig->GetOptOutBVec()));
+        break;
+      }
+      case fkFlesTrd2D: {
+        if (fTrdConfig2D)
+          fCbmTsEventHeader->SetNDigisTrd2D(
+            unpack(&timeslice, component, fTrdConfig2D, fTrdConfig2D->GetOptOutAVec(), fTrdConfig2D->GetOptOutBVec()));
         break;
-      };
+      }
 
       default: {
         if (fDoDebugPrints) LOG(error) << "Unpack: Unknown system ID " << systemId << " for component " << component;
diff --git a/reco/steer/CbmRecoUnpack.h b/reco/steer/CbmRecoUnpack.h
index bbb1877930..e3fe856897 100644
--- a/reco/steer/CbmRecoUnpack.h
+++ b/reco/steer/CbmRecoUnpack.h
@@ -8,11 +8,17 @@
 #ifndef CBMRECOUNPACK_H
 #define CBMRECOUNPACK_H 1
 
+#include "CbmPsdUnpackConfig.h"
+#include "CbmRichUnpackConfig.h"
+#include "CbmStsUnpackConfig.h"
 #include "CbmTrdUnpackConfig.h"
+#include "CbmTrdUnpackConfig2D.h"
+#include "CbmTsEventHeader.h"
 
 #include <MicrosliceDescriptor.hpp>
 #include <Timeslice.hpp>
 
+#include <RtypesCore.h>
 #include <TObject.h>
 #include <type_traits>  // this is std::lib used for template is_member_function_pointer
 
@@ -64,46 +70,68 @@ public:
   */
   void SetDebugPrintout(bool value = true) { fDoDebugPrints = value; }
 
-  /**
-   * @brief Set the Trd Unpack Config
-   * 
-   * @param config 
-  */
+  /** @brief Set the Psd Unpack Config @param config */
+  void SetUnpackConfig(std::shared_ptr<CbmPsdUnpackConfig> config) { fPsdConfig = config; }
+
+  /** @brief Set the Rich Unpack Config @param config */
+  void SetUnpackConfig(std::shared_ptr<CbmRichUnpackConfig> config) { fRichConfig = config; }
+
+  /** @brief Set the Sts Unpack Config @param config */
+  void SetUnpackConfig(std::shared_ptr<CbmStsUnpackConfig> config) { fStsConfig = config; }
+
+  // /** @brief Set the Tof Unpack Config @param config */
+  // void SetUnpackConfig(std::shared_ptr<CbmTofUnpackConfig> config) { fTofConfig = config; }
+
+  /** @brief Set the Trd Unpack Config @param config */
   void SetUnpackConfig(std::shared_ptr<CbmTrdUnpackConfig> config) { fTrdConfig = config; }
 
+  /** @brief Set the Trd2D Unpack Config @param config */
+  void SetUnpackConfig(std::shared_ptr<CbmTrdUnpackConfig2D> config) { fTrdConfig2D = config; }
+
   /** @brief Trigger the unpacking procedure **/
   void Unpack(std::unique_ptr<fles::Timeslice> ts);
 
 private:
-  static constexpr std::uint16_t fkFlesMvd  = static_cast<std::uint16_t>(fles::SubsystemIdentifier::MVD);
-  static constexpr std::uint16_t fkFlesSts  = static_cast<std::uint16_t>(fles::SubsystemIdentifier::STS);
-  static constexpr std::uint16_t fkFlesRich = static_cast<std::uint16_t>(fles::SubsystemIdentifier::RICH);
-  static constexpr std::uint16_t fkFlesMuch = static_cast<std::uint16_t>(fles::SubsystemIdentifier::MUCH);
-  static constexpr std::uint16_t fkFlesTrd  = static_cast<std::uint16_t>(fles::SubsystemIdentifier::TRD);
-  static constexpr std::uint16_t fkFlesTof  = static_cast<std::uint16_t>(fles::SubsystemIdentifier::RPC);
-  static constexpr std::uint16_t fkFlesPsd  = static_cast<std::uint16_t>(fles::SubsystemIdentifier::PSD);
+  static constexpr std::uint16_t fkFlesMvd   = static_cast<std::uint16_t>(fles::SubsystemIdentifier::MVD);
+  static constexpr std::uint16_t fkFlesSts   = static_cast<std::uint16_t>(fles::SubsystemIdentifier::STS);
+  static constexpr std::uint16_t fkFlesRich  = static_cast<std::uint16_t>(fles::SubsystemIdentifier::RICH);
+  static constexpr std::uint16_t fkFlesMuch  = static_cast<std::uint16_t>(fles::SubsystemIdentifier::MUCH);
+  static constexpr std::uint16_t fkFlesTrd   = static_cast<std::uint16_t>(fles::SubsystemIdentifier::TRD);
+  static constexpr std::uint16_t fkFlesTrd2D = static_cast<std::uint16_t>(fles::SubsystemIdentifier::TRD2D);
+  static constexpr std::uint16_t fkFlesTof   = static_cast<std::uint16_t>(fles::SubsystemIdentifier::RPC);
+  static constexpr std::uint16_t fkFlesPsd   = static_cast<std::uint16_t>(fles::SubsystemIdentifier::PSD);
 
   /** @brief Flag if extended debug output is to be printed or not*/
   bool fDoDebugPrints = false;
 
   /** @brief Sort a vector timewise vector type has to provide GetTime() */
   template<typename TVecobj>
-  typename std::enable_if<std::is_member_function_pointer<decltype(&TVecobj::GetTime)>::value, void>::type
-  timesort(std::vector<TVecobj>* vec = nullptr)
+  typename std::enable_if<std::is_same<TVecobj, std::nullptr_t>::value == true, void>::type
+  timesort(std::vector<TVecobj>* /*vec = nullptr*/)
   {
-    if (vec == nullptr) return;
-    std::sort(vec->begin(), vec->end(),
-              [](const TVecobj& a, const TVecobj& b) -> bool { return a.GetTime() < b.GetTime(); });
+    LOG(debug) << "CbmRecoUnpack::timesort() got an object that has no member function GetTime(). Hence, we can and "
+                  "will not timesort it!";
   }
 
   template<typename TVecobj>
   typename std::enable_if<!std::is_member_function_pointer<decltype(&TVecobj::GetTime)>::value, void>::type
   timesort(std::vector<TVecobj>* /*vec = nullptr*/)
   {
-    LOG(debug) << "CbmRecoUnpack::timesort() object " << TVecobj::Class_Name()
-               << " has no member function GetTime(). Hence, we can and will not timesort it!";
+    LOG(debug) << "CbmRecoUnpack::timesort() " << TVecobj::Class_Name()
+               << "is  an object that has no member function GetTime(). Hence, we can and "
+                  "will not timesort it!";
   }
 
+  template<typename TVecobj>
+  typename std::enable_if<std::is_member_function_pointer<decltype(&TVecobj::GetTime)>::value, void>::type
+  timesort(std::vector<TVecobj>* vec = nullptr)
+  {
+    if (vec == nullptr) return;
+    std::sort(vec->begin(), vec->end(),
+              [](const TVecobj& a, const TVecobj& b) -> bool { return a.GetTime() < b.GetTime(); });
+  }
+
+
   /**
    * @brief Template for the unpacking call of a given algorithm.
    * 
@@ -117,8 +145,8 @@ private:
    * @param optoutputvecs Target vectors for optional outputs
   */
   template<class TConfig, class TOptOutA = std::nullptr_t, class TOptOutB = std::nullptr_t>
-  void unpack(const fles::Timeslice* ts, std::uint16_t icomp, TConfig config,
-              std::vector<TOptOutA>* optouttargetvecA = nullptr, std::vector<TOptOutB>* optouttargetvecB = nullptr)
+  size_t unpack(const fles::Timeslice* ts, std::uint16_t icomp, TConfig config,
+                std::vector<TOptOutA>* optouttargetvecA = nullptr, std::vector<TOptOutB>* optouttargetvecB = nullptr)
   {
     auto algo                        = config->GetUnpacker();
     std::vector<TOptOutA> optoutAvec = {};
@@ -126,6 +154,9 @@ private:
     if (optouttargetvecA) { algo->SetOptOutAVec(&optoutAvec); }
     if (optouttargetvecB) { algo->SetOptOutBVec(&optoutBvec); }
 
+    // Set the start time of the current TS for this algorithm
+    algo->SetTsStartTime(ts->start_time());
+
     // Run the actual unpacking
     auto digivec = algo->Unpack(ts, icomp);
 
@@ -148,6 +179,8 @@ private:
     }
     if (optouttargetvecB) {
       // Second opt output is not time sorted to allow non GetTime data container.
+      // Lets do some timesorting
+      timesort(&optoutAvec);
       // Transfer the data from the timeslice vector to the target branch vector
       for (auto optoutB : optoutBvec)
         optouttargetvecB->emplace_back(optoutB);
@@ -156,16 +189,33 @@ private:
 
     // Check some numbers from this timeslice
     size_t nDigis = digivec.size();
-
-    LOG(info) << "Component " << icomp << " connected to config " << config->GetName() << "   n-Digis " << nDigis
-              << " processed in this timeslice.";
+    return nDigis;
+    LOG(debug) << "Component " << icomp << " connected to config " << config->GetName() << "   n-Digis " << nDigis
+               << " processed in this timeslice.";
   }
   // ----------------------------------------------------------------------------
 
+  /** @brief Configuration of the Trd unpacker. Provides the configured algorithm */
+  std::shared_ptr<CbmPsdUnpackConfig> fPsdConfig = nullptr;  //!
+
+  /** @brief Configuration of the Trd unpacker. Provides the configured algorithm */
+  std::shared_ptr<CbmRichUnpackConfig> fRichConfig = nullptr;  //!
+
+  /** @brief Configuration of the Trd unpacker. Provides the configured algorithm */
+  std::shared_ptr<CbmStsUnpackConfig> fStsConfig = nullptr;  //!
+
+  // /** @brief Configuration of the Trd unpacker. Provides the configured algorithm */
+  // std::shared_ptr<CbmTofUnpackConfig> fTofConfig = nullptr;  //!
 
   /** @brief Configuration of the Trd unpacker. Provides the configured algorithm */
   std::shared_ptr<CbmTrdUnpackConfig> fTrdConfig = nullptr;  //!
 
+  /** @brief Configuration of the Trd unpacker. Provides the configured algorithm */
+  std::shared_ptr<CbmTrdUnpackConfig2D> fTrdConfig2D = nullptr;  //!
+
+  /** @brief Pointer to the Timeslice start time used to write it to the output tree @remark since we hand this to the FairRootManager it also wants to delete it and we do not have to take care of deletion */
+  CbmTsEventHeader* fCbmTsEventHeader = nullptr;
+
 
   ClassDef(CbmRecoUnpack, 1);
 };
-- 
GitLab