From ff3ccc6445ce858dc7e7627c70d69e5f48ecb35e Mon Sep 17 00:00:00 2001
From: Pascal Raisig <praisig@ikf.uni-frankfurt.de>
Date: Fri, 16 Jul 2021 19:00:39 +0200
Subject: [PATCH] Add a first version of the Sts algorithm

Unfortunately this does not yet produce digis, but I am note sure, if it is an parameter based issue or an issue with the implementation. I think it needs some experts from the Sts to have a look. Which is why I am pushing it.
---
 macro/run/run_unpack_tsa.C                    |  16 +-
 reco/base/CbmRecoUnpackAlgo.tmpl              |  12 +-
 reco/detectors/psd/CMakeLists.txt             |   5 +-
 .../detectors/psd/unpack/CbmPsdUnpackAlgo.cxx |   8 +-
 reco/detectors/sts/CMakeLists.txt             |   8 +-
 reco/detectors/sts/CbmRecoStsLinkDef.h        |   1 +
 .../detectors/sts/unpack/CbmStsUnpackAlgo.cxx | 735 +++++++++++++++++-
 reco/detectors/sts/unpack/CbmStsUnpackAlgo.h  | 212 ++++-
 .../sts/unpack/CbmStsUnpackConfig.cxx         |  16 +
 .../detectors/sts/unpack/CbmStsUnpackConfig.h |  50 +-
 .../sts/unpack/CbmStsUnpackMonitor.cxx        | 690 ++++++++++++++++
 .../sts/unpack/CbmStsUnpackMonitor.h          | 304 ++++++++
 .../trd/unpack/CbmTrdUnpackAlgoBaseR.cxx      |   4 +-
 .../trd/unpack/CbmTrdUnpackAlgoBaseR.h        |   9 +-
 reco/steer/CMakeLists.txt                     |   2 +
 reco/steer/CbmRecoUnpack.cxx                  |   6 +
 16 files changed, 2033 insertions(+), 45 deletions(-)
 create mode 100644 reco/detectors/sts/unpack/CbmStsUnpackMonitor.cxx
 create mode 100644 reco/detectors/sts/unpack/CbmStsUnpackMonitor.h

diff --git a/macro/run/run_unpack_tsa.C b/macro/run/run_unpack_tsa.C
index ba18d4be14..21f94d7444 100644
--- a/macro/run/run_unpack_tsa.C
+++ b/macro/run/run_unpack_tsa.C
@@ -89,6 +89,14 @@ void run_unpack_tsa(std::string infile = "test.tsa", UInt_t runid = 0, const cha
   richconfig->SetParFilesBasePath(parfilesbasepathRich);
   // -------------
 
+  // ---- STS ----
+  auto stsconfig = std::make_shared<CbmStsUnpackConfig>("", runid);
+  // stsconfig->SetDebugState();
+  stsconfig->SetDoWriteOutput();
+  std::string parfilesbasepathSts = Form("%s/macro/beamtime/mcbm2021/", srcDir.Data());
+  stsconfig->SetParFilesBasePath(parfilesbasepathRich);
+  // -------------
+
   // ---- TRD ----
   TString trdsetuptag = "";
   cbmsetup->GetGeoTag(ECbmModuleId::kTrd, trdsetuptag);
@@ -96,6 +104,8 @@ void run_unpack_tsa(std::string infile = "test.tsa", UInt_t runid = 0, const cha
   auto trdconfig = std::make_shared<CbmTrdUnpackConfig>(trdsetuptag.Data(), 3);
   // trdconfig->SetDebugState();
   trdconfig->SetDoWriteOutput();
+  // Activate the line below to write Trd1D digis to a separate "TrdSpadicDigi" branch. Can be used to separate between Fasp and Spadic digis
+  // trdconfig->SetOutputBranchName("TrdSpadicDigi");
   // trdconfig->SetDoWriteOptOutA(CbmTrdRawMessageSpadic::GetBranchName());
   // trdconfig->SetDoWriteOptOutB("SpadicInfoMessages"); // SpadicInfoMessages
 
@@ -110,7 +120,8 @@ void run_unpack_tsa(std::string infile = "test.tsa", UInt_t runid = 0, const cha
   auto trdfasp2dconfig = std::make_shared<CbmTrdUnpackConfigFasp2D>("", runid);
   // trdfasp2dconfig->SetDebugState();
   trdfasp2dconfig->SetDoWriteOutput();
-  trdfasp2dconfig->SetOutputBranchName("FaspTrdDigi");
+  // Activate the line below to write Trd1D digis to a separate "TrdFaspDigi" branch. Can be used to separate between Fasp and Spadic digis
+  // trdfasp2dconfig->SetOutputBranchName("TrdFaspDigi");
   std::string parfilesbasepathTrdfasp2d = Form("%s/parameters/trd", srcDir.Data());
   trdfasp2dconfig->SetParFilesBasePath(parfilesbasepathTrdfasp2d);
   // -------------
@@ -131,7 +142,8 @@ 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(psdconfig);
-  unpack->SetUnpackConfig(richconfig);
+  // unpack->SetUnpackConfig(richconfig); // Problematic in 1588
+  unpack->SetUnpackConfig(stsconfig);
   unpack->SetUnpackConfig(trdconfig);
   unpack->SetUnpackConfig(trdfasp2dconfig);
   // ------------------------------------------------------------------------
diff --git a/reco/base/CbmRecoUnpackAlgo.tmpl b/reco/base/CbmRecoUnpackAlgo.tmpl
index c655c0d661..9e3de1f7cf 100644
--- a/reco/base/CbmRecoUnpackAlgo.tmpl
+++ b/reco/base/CbmRecoUnpackAlgo.tmpl
@@ -135,6 +135,9 @@ protected:
   /** @brief Start time of the current TS [ns] */
   size_t fTsStartTime = 0;
 
+  /** @brief Index of the current TS */
+  size_t fTsIndex = 0;
+
   /** @brief Additional explicit finish function of the derived algo implementations. */
   virtual void finish() = 0;
 
@@ -259,7 +262,7 @@ public:
 
     LOG(info) << fName << "::Finish. \n Number of CrcValidFlags " << fNrCrcValidFlags
               << " \n Number of OverflowFlimFlags " << fNrOverflowFlimFlags << " \n Number of OverflowUserFlags "
-              << fNrOverflowUserFlags << " \n Number of DataErrorFlags " << fNrDataErrorFlags;
+              << fNrOverflowUserFlags << " \n Number of DataErrorFlags " << fNrDataErrorFlags << "\n";
 
     // Additional explicit finish of the derived class used during runtime.
     finish();
@@ -367,9 +370,10 @@ public:
     if (fIsFirstTs) getTimesliceParams(ts);
 
     // Get the index of the current timeslice
-    auto currTsIdx = ts->index();
+    fTsIndex = ts->index();
+
     // Get the number of the current timeslice (the index increases currently via nthTimeslice* fNrCoreMsPerTs)
-    size_t itimeslice = currTsIdx / fNrCoreMsPerTs;
+    size_t itimeslice = fTsIndex / fNrCoreMsPerTs;
 
     // Set further parameters required by the explicit algorithm
     setDerivedTsParameters(itimeslice);
@@ -385,8 +389,8 @@ public:
       }
     }
 
+    fNrCreatedDigis += fOutputVec.size();
 
-    // Sort the optional output vectors according to the time. (Digi vector is handled by CbmRecoUnpack)
     ++fNrProcessedTs;
     return fOutputVec;
   }
diff --git a/reco/detectors/psd/CMakeLists.txt b/reco/detectors/psd/CMakeLists.txt
index 2ab93daa82..8472941654 100644
--- a/reco/detectors/psd/CMakeLists.txt
+++ b/reco/detectors/psd/CMakeLists.txt
@@ -4,12 +4,13 @@ set(INCLUDE_DIRECTORIES
   
   ${CMAKE_SOURCE_DIR}/core/detectors/psd
 
+  ${CBMBASE_DIR}
+  
   ${CBMDATA_DIR}
-  ${CBMDATA_DIR}/global
   ${CBMDATA_DIR}/base
+  ${CBMDATA_DIR}/global
   ${CBMDATA_DIR}/psd
   ${CBMDATA_DIR}/raw
-  ${CBMBASE_DIR}
   
 
   ${CBMROOT_SOURCE_DIR}/reco/base
diff --git a/reco/detectors/psd/unpack/CbmPsdUnpackAlgo.cxx b/reco/detectors/psd/unpack/CbmPsdUnpackAlgo.cxx
index 36a2a1d93d..9ea813cbea 100644
--- a/reco/detectors/psd/unpack/CbmPsdUnpackAlgo.cxx
+++ b/reco/detectors/psd/unpack/CbmPsdUnpackAlgo.cxx
@@ -67,7 +67,7 @@ Bool_t CbmPsdUnpackAlgo::initParSet(FairParGenericSet* parset)
   return kFALSE;
 }
 
-// ---- initParSet(CbmTrdParSetAsic* parset) ----
+// ---- initParSet(CbmMcbm2018PsdPar* parset) ----
 Bool_t CbmPsdUnpackAlgo::initParSet(CbmMcbm2018PsdPar* parset)
 {
   LOG(debug) << fName << "::initParSetAsic - ";
@@ -114,12 +114,6 @@ Bool_t CbmPsdUnpackAlgo::initParSet(CbmMcbm2018PsdPar* parset)
   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;
 }
 
diff --git a/reco/detectors/sts/CMakeLists.txt b/reco/detectors/sts/CMakeLists.txt
index 1321d4bbcf..b93c508090 100644
--- a/reco/detectors/sts/CMakeLists.txt
+++ b/reco/detectors/sts/CMakeLists.txt
@@ -22,6 +22,7 @@ CbmStsTrackFinderIdeal.cxx
 
 unpack/CbmStsUnpackAlgo.cxx
 unpack/CbmStsUnpackConfig.cxx
+unpack/CbmStsUnpackMonitor.cxx
 )
 # -----  End of sources   ---------------------------------
 
@@ -37,6 +38,10 @@ ${CBMROOT_SOURCE_DIR}/reco/detectors/sts/unpack
 # Reco
 ${CBMROOT_SOURCE_DIR}/reco/base
 
+#QA
+${CBMROOT_SOURCE_DIR}/qa
+${CBMROOT_SOURCE_DIR}/core/qa
+
 # Base
 ${CBMBASE_DIR}
 
@@ -45,6 +50,7 @@ ${CBMDATA_DIR}
 ${CBMDATA_DIR}/base
 ${CBMDATA_DIR}/global
 ${CBMDATA_DIR}/sts
+${CBMDATA_DIR}/raw
 ${CBMDATA_DIR}/mvd
 
 # MVD
@@ -72,7 +78,7 @@ ${Boost_LIBRARY_DIRS}
 
 # -----   Specify library dependences   -------------------
 Set(DEPENDENCIES
-    CbmBase CbmData CbmRecoBase CbmStsBase
+    CbmBase CbmData CbmQaBase CbmRecoBase CbmStsBase 
 )
 # ---------------------------------------------------------
 
diff --git a/reco/detectors/sts/CbmRecoStsLinkDef.h b/reco/detectors/sts/CbmRecoStsLinkDef.h
index a1a0faab5b..1b139a204d 100644
--- a/reco/detectors/sts/CbmRecoStsLinkDef.h
+++ b/reco/detectors/sts/CbmRecoStsLinkDef.h
@@ -21,5 +21,6 @@
 
 #pragma link C++ class CbmStsUnpackAlgo + ;
 #pragma link C++ class CbmStsUnpackConfig + ;
+#pragma link C++ class CbmStsUnpackMonitor + ;
 
 #endif /* __CINT__ */
diff --git a/reco/detectors/sts/unpack/CbmStsUnpackAlgo.cxx b/reco/detectors/sts/unpack/CbmStsUnpackAlgo.cxx
index 0c76f5a8e0..84b9c426ea 100644
--- a/reco/detectors/sts/unpack/CbmStsUnpackAlgo.cxx
+++ b/reco/detectors/sts/unpack/CbmStsUnpackAlgo.cxx
@@ -1,9 +1,8 @@
-/* Copyright (C) 2010 - 2021 Goethe-University Frankfurt
-   SPDX-License-Identifier: GPL-3.0-only
-   Authors: Pascal Raisig */
 
 #include "CbmStsUnpackAlgo.h"
 
+#include "CbmStsDigi.h"
+
 #include <FairParGenericSet.h>
 #include <FairTask.h>
 #include <Logger.h>
@@ -11,10 +10,740 @@
 #include <Rtypes.h>
 #include <RtypesCore.h>
 
+#include <cstdint>
+
+// FIXME
+// #include "StsXyterFinalHit.h"
+#include "raw/StsXyterFinalHit.h"
+
 
 CbmStsUnpackAlgo::CbmStsUnpackAlgo() : CbmRecoUnpackAlgo("CbmStsUnpackAlgo") {}
 
 CbmStsUnpackAlgo::~CbmStsUnpackAlgo() {}
 
+// ---- getAsicIndex ----
+uint32_t CbmStsUnpackAlgo::getAsicIndex(uint32_t dpbidx, uint32_t crobidx, uint16_t elinkidx)
+{
+
+  uint32_t asicidx      = 0;
+  const int32_t uFebIdx = fElinkIdxToFebIdxVec.at(elinkidx);
+  uint32_t febtype      = fviFebType[fuCurrDpbIdx][crobidx][uFebIdx];
+  // Feb type a
+  if (febtype == 0) asicidx = fElinkIdxToAsicIdxVec.at(elinkidx).first;
+  //   Feb type b
+  if (febtype == 1) asicidx = fElinkIdxToAsicIdxVec.at(elinkidx).second;
+  // else would be inactive feb, this was not handled in the previous implementation, this I expect it should not happen
+
+
+  uint32_t uAsicIdx = (fuCurrDpbIdx * fNrCrobPerDpb + crobidx) * fNrAsicsPerCrob + asicidx;
+  return uAsicIdx;
+}
+
+// ---- getFullTimeStamp ----
+uint64_t CbmStsUnpackAlgo::getFullTimeStamp(const uint16_t usRawTs)
+{
+  // Use TS w/o overlap bits as they will anyway come from the TS_MSB
+  const uint64_t ulTime =
+    usRawTs
+    + static_cast<uint64_t>(stsxyter::kuHitNbTsBinsBinning) * static_cast<uint64_t>(fvulCurrentTsMsb[fuCurrDpbIdx])
+    + static_cast<uint64_t>(stsxyter::kulTsCycleNbBinsBinning)
+        * static_cast<uint64_t>(fvuCurrentTsMsbCycle[fuCurrDpbIdx]);
+
+  return ulTime;
+}
+
+
+// ---- GetParContainerRequest ----
+std::vector<std::pair<std::string, std::shared_ptr<FairParGenericSet>>>*
+  CbmStsUnpackAlgo::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 + "mStsPar.par";
+  fParContVec.emplace_back(std::make_pair(temppath, std::make_shared<CbmMcbm2018StsPar>()));
+
+  return &fParContVec;
+}
+
+// ---- init
+Bool_t CbmStsUnpackAlgo::init() { return kTRUE; }
+
+// ---- initDpbIdIndexMap ----
+void CbmStsUnpackAlgo::initDpbIdIndexMap(CbmMcbm2018StsPar* parset)
+{
+  fDpbIdIndexMap.clear();
+  for (uint32_t uDpb = 0; uDpb < parset->GetNrOfDpbs(); ++uDpb) {
+    fDpbIdIndexMap[parset->GetDpbId(uDpb)] = uDpb;
+    LOG(info) << "Eq. ID for DPB #" << std::setw(2) << uDpb << " = 0x" << std::setw(4) << std::hex
+              << parset->GetDpbId(uDpb) << std::dec << " => " << fDpbIdIndexMap[parset->GetDpbId(uDpb)];
+  }
+}
+
+
+// ---- initParSet(FairParGenericSet* parset) ----
+Bool_t CbmStsUnpackAlgo::initParSet(FairParGenericSet* parset)
+{
+  LOG(info) << fName << "::initParSet - for container " << parset->ClassName();
+  if (parset->IsA() == CbmMcbm2018StsPar::Class()) return initParSet(static_cast<CbmMcbm2018StsPar*>(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(CbmMcbm2018StsPar* parset) ----
+Bool_t CbmStsUnpackAlgo::initParSet(CbmMcbm2018StsPar* parset)
+{
+  LOG(debug) << fName << "::initParSetAsic - ";
+
+  //Type of each module: 0 for connectors on the right, 1 for connectors on the left
+  std::vector<int32_t> viModuleType;
+
+  // STS address for the first strip of each module
+  std::vector<int32_t> viModAddress;
+
+  // Idx of the STS module for each FEB, [ NbDpb ][ NbCrobPerDpb ][ NbFebsPerCrob ], -1 if inactive
+  std::vector<std::vector<std::vector<int32_t>>> viFebModuleIdx;
+
+  // Array to hold the active flag for all CROBs, [ NbDpb ][ NbCrobPerDpb ]
+  std::vector<std::vector<bool>> vbCrobActiveFlag;
+
+  //STS module side for each FEB, [ NbDpb ][ NbCrobPerDpb ][ NbFebsPerCrob ], 0 = P, 1 = N, -1 if inactive
+  std::vector<std::vector<std::vector<int32_t>>> viFebModuleSide;
+
+  // Total number of STS modules in the setup
+  const uint32_t uNbModules = parset->GetNbOfModules();
+  LOG(debug) << "Nr. of STS Modules:    " << uNbModules;
+
+  // Total number of STS DPBs in system
+  const uint32_t uNbOfDpbs = parset->GetNrOfDpbs();
+  LOG(info) << "Nr. of STS DPBs:       " << uNbOfDpbs;
+
+  // Get Nr of Febs
+  fuNbFebs = parset->GetNrOfFebs();
+  LOG(info) << "Nr. of FEBs:           " << fuNbFebs;
+
+  // Get Nr of eLinks per CROB
+  fNrElinksPerCrob = parset->GetNbElinkPerCrob();
+
+  // Get Nr of ASICs per CROB
+  fNrAsicsPerCrob = parset->GetNbAsicsPerCrob();
+
+  // Get Nr of CROBs per DPB
+  fNrCrobPerDpb = parset->GetNbCrobsPerDpb();
+
+  // Get Number of ASICs per FEB
+  fNrAsicsPerFeb = parset->GetNbAsicsPerFeb();
+
+  // Get Number of Channels per Asic
+  fNrChsPerAsic = parset->GetNbChanPerAsic();
+
+  // Get Number of Channels per FEB
+  fNrChsPerFeb = parset->GetNbChanPerFeb();
+
+  for (size_t ielink = 0; ielink < fNrElinksPerCrob; ++ielink) {
+    fElinkIdxToFebIdxVec.emplace_back(parset->ElinkIdxToFebIdx(ielink));
+    fElinkIdxToAsicIdxVec.emplace_back(
+      std::make_pair(parset->ElinkIdxToAsicIdxFebA(ielink), parset->ElinkIdxToAsicIdxFebB(ielink)));
+  }
+
+  // Get Nr of Asics
+  const uint32_t uNbStsXyters = parset->GetNrOfAsics();
+  LOG(info) << "Nr. of StsXyter ASICs: " << uNbStsXyters;
+
+  //Initialize temporary "per Feb" fields
+  initTempVectors(parset, &viModuleType, &viModAddress, &viFebModuleIdx, &vbCrobActiveFlag, &viFebModuleSide);
+
+  // Read dpb index map from parameter container
+  initDpbIdIndexMap(parset);
+
+  if (fvdTimeOffsetNsAsics.size() < uNbStsXyters) { fvdTimeOffsetNsAsics.resize(uNbStsXyters, 0.0); }
+
+  //Initialize class-wide "per Feb" fields
+  fviFebType.resize(uNbOfDpbs);
+
+  for (uint32_t uDpb = 0; uDpb < uNbOfDpbs; ++uDpb) {
+    fviFebType[uDpb].resize(fNrCrobPerDpb);
+    for (uint32_t uCrobIdx = 0; uCrobIdx < fNrCrobPerDpb; ++uCrobIdx) {
+      fviFebType[uDpb][uCrobIdx].resize(parset->GetNbFebsPerCrob(), -1);
+      for (uint32_t uFebIdx = 0; uFebIdx < parset->GetNbFebsPerCrob(); ++uFebIdx) {
+        fvdFebAdcGain.push_back(parset->GetFebAdcGain(uDpb, uCrobIdx, uFebIdx));
+        fvdFebAdcOffs.push_back(parset->GetFebAdcOffset(uDpb, uCrobIdx, uFebIdx));
+
+        if (0 <= viFebModuleIdx[uDpb][uCrobIdx][uFebIdx]
+            && static_cast<uint32_t>(viFebModuleIdx[uDpb][uCrobIdx][uFebIdx]) < uNbModules
+            && 0 <= viFebModuleSide[uDpb][uCrobIdx][uFebIdx] && viFebModuleSide[uDpb][uCrobIdx][uFebIdx] < 2) {
+          switch (viModuleType[viFebModuleIdx[uDpb][uCrobIdx][uFebIdx]]) {
+            case 0:  // FEB-8-1 with ZIF connector on the right
+            {
+              // P side (0) has type A (0)
+              // N side (1) has type B (1)
+              fviFebType[uDpb][uCrobIdx][uFebIdx] = viFebModuleSide[uDpb][uCrobIdx][uFebIdx];
+
+              ///! FIXME: 1) Geometry is using front/back while we are using P/N !!!!
+              ///!            => Assuming that front facing modules have connectors on right side
+              ///!            +> Volker warns that the front side should be electrons one so N
+              ///!        2) No accessor/setter to change only the side field of an STS address
+              ///!            => hardcode the shift
+              ///!            +> The bit is unused in the current scheme: the side is encoded in the Digi channel
+              fviFebAddress.push_back(viModAddress[viFebModuleIdx[uDpb][uCrobIdx][uFebIdx]]
+                                      + (viFebModuleSide[uDpb][uCrobIdx][uFebIdx] << 25));
+              fviFebSide.push_back(viFebModuleSide[uDpb][uCrobIdx][uFebIdx]);
+              break;
+            }        // case 0: // FEB-8-1 with ZIF connector on the right
+            case 1:  // FEB-8-1 with ZIF connector on the left
+            {
+              // P side (0) has type B (1)
+              // N side (1) has type A (0)
+              fviFebType[uDpb][uCrobIdx][uFebIdx] = !(viFebModuleSide[uDpb][uCrobIdx][uFebIdx]);
+
+              ///! FIXME: 1) Geometry is using front/back while we are using P/N !!!!
+              ///!            => Assuming that front facing modules have connectors on right side
+              ///!            +> Volker warns that the front side should be electrons one so N
+              ///!        2) No accessor/setter to change only the side field of an STS address
+              ///!            => hardcode the shift
+              ///!            +> The bit is unused in the current scheme: the side is encoded in the Digi channel
+              fviFebAddress.push_back(viModAddress[viFebModuleIdx[uDpb][uCrobIdx][uFebIdx]]
+                                      + ((!viFebModuleSide[uDpb][uCrobIdx][uFebIdx]) << 25));
+              fviFebSide.push_back(viFebModuleSide[uDpb][uCrobIdx][uFebIdx]);
+              break;
+            }  // case 1: // FEB-8-1 with ZIF connector on the left
+            default:
+              LOG(fatal) << Form("Bad module type for DPB #%02u CROB #%u FEB %02u: %d", uDpb, uCrobIdx, uFebIdx,
+                                 viModuleType[viFebModuleIdx[uDpb][uCrobIdx][uFebIdx]]);
+              break;
+          }
+        }  // FEB active and module index OK
+        else if (-1 == viFebModuleIdx[uDpb][uCrobIdx][uFebIdx] || -1 == viFebModuleSide[uDpb][uCrobIdx][uFebIdx]) {
+          fviFebAddress.push_back(0);
+          fviFebSide.push_back(-1);
+        }  // Module index or type is set to inactive
+        else {
+          LOG(fatal) << Form("Bad module Index and/or Side for DPB #%02u CROB "
+                             "#%u FEB %02u: %d %d",
+                             uDpb, uCrobIdx, uFebIdx, viFebModuleIdx[uDpb][uCrobIdx][uFebIdx],
+                             viFebModuleSide[uDpb][uCrobIdx][uFebIdx]);
+        }  // Bad module index or type for this FEB
+      }
+    }
+  }
+
+  printActiveCrobs(parset, vbCrobActiveFlag);
+  printAddressMaps(parset, viFebModuleIdx, viFebModuleSide);
+
+  LOG(debug) << "Unpacking data in bin sorter FW mode";
+  initInternalStatus(parset);
+
+
+  return kTRUE;
+}
+
+// ---- initTempVectors ----
+void CbmStsUnpackAlgo::initTempVectors(CbmMcbm2018StsPar* parset, std::vector<int32_t>* viModuleType,
+                                       std::vector<int32_t>* viModAddress,
+                                       std::vector<std::vector<std::vector<int32_t>>>* viFebModuleIdx,
+                                       std::vector<std::vector<bool>>* vbCrobActiveFlag,
+                                       std::vector<std::vector<std::vector<int32_t>>>* viFebModuleSide)
+{
+  const uint32_t uNbModules = parset->GetNbOfModules();
+  const uint32_t uNbOfDpbs  = parset->GetNrOfDpbs();
+
+  viModuleType->resize(uNbModules);
+  viModAddress->resize(uNbModules);
+  for (uint32_t uModIdx = 0; uModIdx < uNbModules; ++uModIdx) {
+    (*viModuleType)[uModIdx] = parset->GetModuleType(uModIdx);
+    (*viModAddress)[uModIdx] = parset->GetModuleAddress(uModIdx);
+    LOG(info) << "Module #" << std::setw(2) << uModIdx << " Type " << std::setw(4) << (*viModuleType)[uModIdx]
+              << " Address 0x" << std::setw(8) << std::hex << (*viModAddress)[uModIdx] << std::dec;
+  }
+  vbCrobActiveFlag->resize(uNbOfDpbs);
+  viFebModuleIdx->resize(uNbOfDpbs);
+  viFebModuleSide->resize(uNbOfDpbs);
+
+  for (uint32_t uDpb = 0; uDpb < uNbOfDpbs; ++uDpb) {
+    (*vbCrobActiveFlag)[uDpb].resize(fNrCrobPerDpb);
+    (*viFebModuleIdx)[uDpb].resize(fNrCrobPerDpb);
+    (*viFebModuleSide)[uDpb].resize(fNrCrobPerDpb);
+    for (uint32_t uCrobIdx = 0; uCrobIdx < fNrCrobPerDpb; ++uCrobIdx) {
+      (*vbCrobActiveFlag)[uDpb][uCrobIdx] = parset->IsCrobActive(uDpb, uCrobIdx);
+      (*viFebModuleIdx)[uDpb][uCrobIdx].resize(parset->GetNbFebsPerCrob());
+      (*viFebModuleSide)[uDpb][uCrobIdx].resize(parset->GetNbFebsPerCrob());
+      for (uint32_t uFebIdx = 0; uFebIdx < parset->GetNbFebsPerCrob(); ++uFebIdx) {
+        (*viFebModuleIdx)[uDpb][uCrobIdx][uFebIdx]  = parset->GetFebModuleIdx(uDpb, uCrobIdx, uFebIdx);
+        (*viFebModuleSide)[uDpb][uCrobIdx][uFebIdx] = parset->GetFebModuleSide(uDpb, uCrobIdx, uFebIdx);
+      }
+    }
+  }
+}
+
+// ---- initInternalStatus ----
+void CbmStsUnpackAlgo::initInternalStatus(CbmMcbm2018StsPar* parset)
+{
+  const uint32_t uNbOfDpbs    = parset->GetNrOfDpbs();
+  const uint32_t uNbStsXyters = parset->GetNrOfAsics();
+
+  fvulCurrentTsMsb.resize(uNbOfDpbs);
+  fvuCurrentTsMsbCycle.resize(uNbOfDpbs);
+  for (uint32_t uDpb = 0; uDpb < uNbOfDpbs; ++uDpb) {
+    fvulCurrentTsMsb[uDpb]     = 0;
+    fvuCurrentTsMsbCycle[uDpb] = 0;
+  }
+
+  fvvusLastTsChan.resize(uNbStsXyters);
+  fvvusLastAdcChan.resize(uNbStsXyters);
+  fvvusLastTsMsbChan.resize(uNbStsXyters);
+  fvvusLastTsMsbCycleChan.resize(uNbStsXyters);
+  for (uint32_t uAsicIdx = 0; uAsicIdx < uNbStsXyters; ++uAsicIdx) {
+    fvvusLastTsChan[uAsicIdx].resize(fNrChsPerAsic, 0);
+    fvvusLastAdcChan[uAsicIdx].resize(fNrChsPerAsic, 0);
+    fvvusLastTsMsbChan[uAsicIdx].resize(fNrChsPerAsic, 0);
+    fvvusLastTsMsbCycleChan[uAsicIdx].resize(fNrChsPerAsic, 0);
+  }
+}
+
+// ---- loopMsMessages ----
+void CbmStsUnpackAlgo::loopMsMessages(const uint8_t* msContent, const uint32_t uSize, const size_t uMsIdx, bool& bError,
+                                      std::string& sMessPatt, std::vector<uint32_t>& vNbMessType)
+{
+  // If not integer number of message in input buffer, print warning/error
+  if (0 != (uSize % sizeof(stsxyter::Message))) {
+    LOG(error) << "The input microslice buffer does NOT "
+               << "contain only complete sDPB messages!";
+  }
+  // Compute the number of complete messages in the input microslice buffer
+  const uint32_t uNbMessages = (uSize - (uSize % sizeof(stsxyter::Message))) / sizeof(stsxyter::Message);
+
+  // Prepare variables for the loop on contents
+  const stsxyter::Message* pMess = reinterpret_cast<const stsxyter::Message*>(msContent);
+
+  for (uint32_t uIdx = 0; uIdx < uNbMessages; uIdx++) {
+    /// Get message type
+    const stsxyter::MessType typeMess = pMess[uIdx].GetMessType();
+    if (fMonitor)
+      if (fMonitor->GetDebugMode()) {
+        bError = fMonitor->ProcessDebugInfo(pMess[uIdx], sMessPatt, vNbMessType, fuCurrDpbIdx);
+      }
+    switch (typeMess) {
+      case stsxyter::MessType::Hit: {
+        processHitInfo(pMess[uIdx]);
+        break;
+      }
+      case stsxyter::MessType::TsMsb: {
+        processTsMsbInfo(pMess[uIdx], uIdx, uMsIdx);
+        break;
+      }
+      case stsxyter::MessType::Epoch: {
+        processEpochInfo(pMess[uIdx]);
+        if (0 < uIdx) {
+          LOG(info) << "CbmAlgoUnpackSts::DoUnpack => "
+                    << "EPOCH message at unexpected position in MS: message " << uIdx << " VS message 0 expected!";
+        }
+        break;
+      }
+      case stsxyter::MessType::Status: {
+        processStatusInfo(pMess[uIdx], uIdx);
+        break;
+      }
+      case stsxyter::MessType::Empty: {
+        break;
+      }
+      case stsxyter::MessType::EndOfMs: {
+        processErrorInfo(pMess[uIdx]);
+        break;
+      }
+      case stsxyter::MessType::Dummy: {
+        break;
+      }
+      default: {
+        LOG(fatal) << "CbmAlgoUnpackSts::DoUnpack => "
+                   << "Unknown message type, should never happen, stopping "
+                      "here! Type found was: "
+                   << static_cast<int>(typeMess);
+      }
+    }
+  }
+}
+
+// ---- MaskNoisyChannel ----
+void CbmStsUnpackAlgo::MaskNoisyChannel(const uint32_t uFeb, const uint32_t uChan, const bool bMasked)
+{
+  if (false == fbUseChannelMask) {
+    fbUseChannelMask = true;
+    fvvbMaskedChannels.resize(fuNbFebs);
+    for (uint32_t uFebIdx = 0; uFebIdx < fuNbFebs; ++uFebIdx) {
+      fvvbMaskedChannels[uFebIdx].resize(fNrChsPerFeb, false);
+    }
+  }
+  if (uFeb < fuNbFebs && uChan < fNrChsPerFeb) fvvbMaskedChannels[uFeb][uChan] = bMasked;
+  else
+    LOG(fatal) << "CbmAlgoUnpackSts::MaskNoisyChannel => Invalid FEB "
+                  "and/or CHAN index:"
+               << Form(" %u vs %u and %u vs %u", uFeb, fuNbFebs, uChan, fNrChsPerFeb);
+}
+
+
+// ---- printActiveCrobs ----
+void CbmStsUnpackAlgo::printActiveCrobs(CbmMcbm2018StsPar* parset,
+                                        const std::vector<std::vector<bool>>& vbCrobActiveFlag)
+{
+  for (uint32_t uDpb = 0; uDpb < parset->GetNrOfDpbs(); ++uDpb) {
+    TString sPrintoutLine = Form("DPB #%02u CROB Active ?:       ", uDpb);
+    for (uint32_t uCrobIdx = 0; uCrobIdx < fNrCrobPerDpb; ++uCrobIdx) {
+      sPrintoutLine += Form("%1u", (vbCrobActiveFlag[uDpb][uCrobIdx] == true));
+    }
+    LOG(debug) << sPrintoutLine;
+  }
+}
+
+// ---- printAddressMaps ----
+void CbmStsUnpackAlgo::printAddressMaps(CbmMcbm2018StsPar* parset,
+                                        const std::vector<std::vector<std::vector<int32_t>>>& viFebModuleIdx,
+                                        const std::vector<std::vector<std::vector<int32_t>>>& viFebModuleSide)
+{
+  uint32_t uGlobalFebIdx = 0;
+  for (uint32_t uDpb = 0; uDpb < parset->GetNrOfDpbs(); ++uDpb) {
+    for (uint32_t uCrobIdx = 0; uCrobIdx < fNrCrobPerDpb; ++uCrobIdx) {
+      LOG(info) << Form("DPB #%02u CROB #%u:       ", uDpb, uCrobIdx);
+      for (uint32_t uFebIdx = 0; uFebIdx < parset->GetNbFebsPerCrob(); ++uFebIdx) {
+        if (0 <= viFebModuleIdx[uDpb][uCrobIdx][uFebIdx])
+          LOG(info) << Form("      FEB #%02u (%02u): Mod. Idx = %03d Side %c (%2d) Type %c "
+                            "(%2d) (Addr. 0x%08x) ADC gain %4.0f e- ADC Offs %5.0f e-",
+                            uFebIdx, uGlobalFebIdx, viFebModuleIdx[uDpb][uCrobIdx][uFebIdx],
+                            1 == viFebModuleSide[uDpb][uCrobIdx][uFebIdx] ? 'N' : 'P',
+                            viFebModuleSide[uDpb][uCrobIdx][uFebIdx],
+                            1 == fviFebType[uDpb][uCrobIdx][uFebIdx] ? 'B' : 'A', fviFebType[uDpb][uCrobIdx][uFebIdx],
+                            fviFebAddress[uGlobalFebIdx], fvdFebAdcGain[uGlobalFebIdx], fvdFebAdcOffs[uGlobalFebIdx]);
+        else
+          LOG(debug) << Form("Disabled FEB #%02u (%02u): Mod. Idx = %03d Side %c (%2d) Type %c "
+                             "(%2d) (Addr. 0x%08x) ADC gain %4.0f e- ADC Offs %5.0f e-",
+                             uFebIdx, uGlobalFebIdx, viFebModuleIdx[uDpb][uCrobIdx][uFebIdx],
+                             1 == viFebModuleSide[uDpb][uCrobIdx][uFebIdx] ? 'N' : 'P',
+                             viFebModuleSide[uDpb][uCrobIdx][uFebIdx],
+                             1 == fviFebType[uDpb][uCrobIdx][uFebIdx] ? 'B' : 'A', fviFebType[uDpb][uCrobIdx][uFebIdx],
+                             fviFebAddress[uGlobalFebIdx], fvdFebAdcGain[uGlobalFebIdx], fvdFebAdcOffs[uGlobalFebIdx]);
+        uGlobalFebIdx++;
+      }
+    }
+  }
+}
+
+// -------------------------------------------------------------------------
+void CbmStsUnpackAlgo::processErrorInfo(const stsxyter::Message& mess)
+{
+  if (mess.IsMsErrorFlagOn()) {
+    //   I do pass here the Ts start time instead of the ms time, since, we removed the ms time as member for the time being
+    if (fMonitor) { fMonitor->FillMsErrorsEvo(fMsStartTime, mess.GetMsErrorType()); }
+    if (fOptOutAVec)
+      fOptOutAVec->emplace_back(
+        CbmErrorMessage(ECbmModuleId::kSts, fMsStartTime, fuCurrDpbIdx, 0x20, mess.GetMsErrorType()));
+  }
+}
+
+// ---- processHitInfo ----
+void CbmStsUnpackAlgo::processHitInfo(const stsxyter::Message& mess)
+{
+  const uint16_t usElinkIdx = mess.GetLinkIndexHitBinning();
+  const uint32_t uCrobIdx   = usElinkIdx / fNrElinksPerCrob;
+  const int32_t uFebIdx     = fElinkIdxToFebIdxVec.at(usElinkIdx);
+  if (-1 == uFebIdx) {
+    LOG(warning) << "CbmAlgoUnpackSts::DoUnpack => "
+                 << "Wrong elink Idx! Elink raw " << Form("%d remap %d", usElinkIdx, uFebIdx);
+    return;
+  }
+  // Get the asix index
+  uint32_t uAsicIdx = getAsicIndex(fuCurrDpbIdx, uCrobIdx, usElinkIdx);
+
+
+  const uint16_t usChan     = mess.GetHitChannel();
+  const uint16_t usRawAdc   = mess.GetHitAdc();
+  const uint16_t usRawTs    = mess.GetHitTimeBinning();
+  const uint32_t uChanInFeb = usChan + fNrChsPerAsic * (uAsicIdx % fNrAsicsPerFeb);
+
+  /// Duplicate hits rejection
+  if (usRawTs == fvvusLastTsChan[uAsicIdx][usChan] &&
+      //       usRawAdc                           == fvvusLastAdcChan[ uAsicIdx ][ usChan ] &&
+      fvulCurrentTsMsb[fuCurrDpbIdx] - fvvusLastTsMsbChan[uAsicIdx][usChan] < kuMaxTsMsbDiffDuplicates
+      && fvuCurrentTsMsbCycle[fuCurrDpbIdx] == fvvusLastTsMsbCycleChan[uAsicIdx][usChan]) {
+    /// FIXME: add plots to check what is done in this rejection
+    return;
+  }  // if SMX 2.0 DPB and same TS, ADC, TS MSB, TS MSB cycle!
+  fvvusLastTsChan[uAsicIdx][usChan]         = usRawTs;
+  fvvusLastAdcChan[uAsicIdx][usChan]        = usRawAdc;
+  fvvusLastTsMsbChan[uAsicIdx][usChan]      = fvulCurrentTsMsb[fuCurrDpbIdx];
+  fvvusLastTsMsbCycleChan[uAsicIdx][usChan] = fvuCurrentTsMsbCycle[fuCurrDpbIdx];
+
+  // Compute the Full time stamp
+  const int64_t ulHitTime = getFullTimeStamp(usRawTs);
+
+  /// Store hit for output only if it is mapped to a module!!!
+  if (0 != fviFebAddress[uFebIdx] && fdAdcCut < usRawAdc) {
+    /// Store only if masking is disabled or if channeld is not masked
+    /// 2D vector is safe as always right size if masking enabled
+    if (false == fbUseChannelMask || false == fvvbMaskedChannels[uFebIdx][uChanInFeb]) {
+      // If you want to store this as well, add it to the template as TOptOut, otherwise I do not see a reason to create it at all
+      // auto finalhit       = stsxyter::FinalHit(ulHitTime, usRawAdc, uAsicIdx, usChan, fuCurrDpbIdx, uCrobIdx);
+      uint32_t uChanInMod = usChan + fNrChsPerAsic * (uAsicIdx % fNrAsicsPerFeb);
+
+      /// FIXME: see issue #1549
+      /// N side: 0-1023 =>    0-1023
+      /// P side: 0-1023 => 2047-1024
+
+      if (0 == fviFebSide[uFebIdx])
+        uChanInMod = fNrChsPerFeb - uChanInMod - 1  // Invert channel order
+                     + fNrChsPerFeb;                // Offset for P (back) side
+
+      // Get the time relative to the Timeslice time, I hope that the cast here works as expected. Otherwise Sts will also get into trouble with the size of UTC here
+      auto tsreltime   = static_cast<uint64_t>(ulHitTime * stsxyter::kdClockCycleNs) - fTsStartTime;
+      double dTimeInNs = tsreltime - fSystemTimeOffset;
+      if (uAsicIdx < fvdTimeOffsetNsAsics.size()) dTimeInNs -= fvdTimeOffsetNsAsics[uAsicIdx];
+
+      const uint64_t ulTimeInNs = static_cast<uint64_t>(dTimeInNs);
+      const double dCalAdc      = fvdFebAdcOffs[uFebIdx] + (usRawAdc - 1) * fvdFebAdcGain[uFebIdx];
+
+      if (0 == fviFebAddress[uFebIdx] || -1 == fviFebSide[uFebIdx]) {
+        LOG(error) << Form("Digi on disabled FEB %02u has address 0x%08x and side %d", uFebIdx, fviFebAddress[uFebIdx],
+                           fviFebSide[uFebIdx]);
+      }
+
+
+      CbmStsDigi(fviFebAddress[uFebIdx], uChanInMod, ulTimeInNs, dCalAdc);
+      // REMARK This seems to be double looping, I do not see any reason here, to first store the stsxyter hits in a vector over which we loop afterwards again. May be I miss something @sts-experts?
+      //   fvmHitsInMs.push_back(stsxyter::FinalHit(ulHitTime, usRawAdc, uAsicIdx, usChan, fuCurrDpbIdx, uCrobIdx));
+    }
+  }
+
+  // Convert the Hit time in bins to Hit time in ns
+  const double dHitTimeNs = ulHitTime * stsxyter::kdClockCycleNs;
+
+  /// If EM flag ON, store a corresponding error message with the next flag after all other possible status flags set
+  if (mess.IsHitMissedEvts())
+    if (fOptOutAVec)
+      fOptOutAVec->emplace_back(
+        CbmErrorMessage(ECbmModuleId::kSts, dHitTimeNs, uAsicIdx, 1 << stsxyter::kusLenStatStatus, usChan));
+
+  if (fMonitor) {
+    // Check Starting point of histos with time as X axis
+    if (-1 == fdStartTime) { fdStartTime = dHitTimeNs; }
+    if (fMonitor->GetDebugMode()) {
+      fMonitor->FillHitDebugMonitoringHistos(uAsicIdx, usChan, usRawAdc, usRawTs, mess.IsHitMissedEvts());
+    }
+    const uint32_t uAsicInFeb       = uAsicIdx % fNrAsicsPerFeb;
+    const double dTimeSinceStartSec = (dHitTimeNs - fdStartTime) * 1e-9;
+    const double dCalAdc            = fvdFebAdcOffs[uFebIdx] + (usRawAdc - 1) * fvdFebAdcGain[uFebIdx];
+    fMonitor->FillHitMonitoringHistos(uFebIdx, usChan, uChanInFeb, usRawAdc, dCalAdc, usRawTs, mess.IsHitMissedEvts());
+    fMonitor->FillHitEvoMonitoringHistos(uFebIdx, uAsicIdx, uAsicInFeb, uChanInFeb, dTimeSinceStartSec,
+                                         mess.IsHitMissedEvts());
+  }
+}
+
+// ---- processStatusInfo ----
+void CbmStsUnpackAlgo::processStatusInfo(const stsxyter::Message& mess, uint32_t uIdx)
+{
+  // again fMonitor settings used for debugging printouts, I would propose to separat this
+  if (fMonitor)
+    if (fMonitor->GetDebugMode()) {
+      std::cout << Form("DPB %2u TS %12lu mess %5u ", fuCurrDpbIdx, fTsIndex, uIdx);
+      mess.PrintMess(std::cout, stsxyter::MessagePrintMask::msg_print_Human);
+    }
+
+  const uint16_t usElinkIdx = mess.GetStatusLink();
+  const uint32_t uCrobIdx   = usElinkIdx / fNrElinksPerCrob;
+  const int32_t uFebIdx     = fElinkIdxToFebIdxVec.at(usElinkIdx);
+  if (-1 == uFebIdx) {
+    LOG(warning) << "CbmAlgoUnpackSts::DoUnpack => "
+                 << "Wrong elink Idx! Elink raw " << Form("%d remap %d", usElinkIdx, uFebIdx);
+    return;
+  }
+  const uint32_t uAsicIdx = getAsicIndex(fuCurrDpbIdx, uCrobIdx, usElinkIdx);
+  if (fMonitor) {
+    const uint16_t usStatusField = mess.GetStatusStatus();
+    fMonitor->FillStsStatusMessType(uAsicIdx, usStatusField);
+  }
+  /// Compute the Full time stamp
+  const int64_t ulTime = getFullTimeStamp(0);
+
+  /// Convert the time in bins to Hit time in ns
+  const double dTimeNs = ulTime * stsxyter::kdClockCycleNs;
+  if (fOptOutAVec)
+    fOptOutAVec->emplace_back(
+      CbmErrorMessage(ECbmModuleId::kSts, dTimeNs, uAsicIdx, mess.GetStatusStatus(), mess.GetData()));
+}
+
+
+// ---- processTsMsbInfo ----
+void CbmStsUnpackAlgo::processTsMsbInfo(const stsxyter::Message& mess, uint32_t uMessIdx, uint32_t uMsIdx)
+{
+  const uint32_t uVal = mess.GetTsMsbValBinning();
+
+  // Update Status counters
+  if (uVal < fvulCurrentTsMsb[fuCurrDpbIdx]) {
+
+    LOG(info) << " TS " << std::setw(12) << fTsIndex << uMsIdx << " MS Idx " << std::setw(4) << uMsIdx << " Msg Idx "
+              << std::setw(5) << uMessIdx << " DPB " << std::setw(2) << fuCurrDpbIdx << " Old TsMsb " << std::setw(5)
+              << fvulCurrentTsMsb[fuCurrDpbIdx] << " Old MsbCy " << std::setw(5) << fvuCurrentTsMsbCycle[fuCurrDpbIdx]
+              << " new TsMsb " << std::setw(5) << uVal;
+
+    fvuCurrentTsMsbCycle[fuCurrDpbIdx]++;
+  }
+  if (
+    uVal != fvulCurrentTsMsb[fuCurrDpbIdx] + 1 && !(0 == uVal && 4194303 == fvulCurrentTsMsb[fuCurrDpbIdx])
+    &&                /// Case where we reach a normal cycle edge
+    1 != uMessIdx &&  /// First TS_MSB in MS may jump if TS dropped by DAQ
+    !(0 == uVal && 0 == fvulCurrentTsMsb[fuCurrDpbIdx] && 2 == uMessIdx) &&  /// case with cycle et edge of 2 MS
+    !(uVal == fvulCurrentTsMsb[fuCurrDpbIdx] && 2 == uMessIdx)
+    &&  /// Msg 1 and 2 will be same TS_MSB if data in 1st bin
+    uVal < fvulCurrentTsMsb
+        [fuCurrDpbIdx]  /// New FW introduced TS_MSB suppression + large TS_MSB => warning only if value not increasing
+  ) {
+    LOG(info) << "TS MSb Jump in "
+              << " TS " << std::setw(12) << fTsIndex << " MS Idx " << std::setw(4) << uMsIdx << " Msg Idx "
+              << std::setw(5) << uMessIdx << " DPB " << std::setw(2) << fuCurrDpbIdx << " => Old TsMsb " << std::setw(5)
+              << fvulCurrentTsMsb[fuCurrDpbIdx] << " new TsMsb " << std::setw(5) << uVal;
+  }
+
+  /// Catch case where previous MS ended up on a TS MSB cycle as it is then
+  /// already updated from the MS index
+  if (4194303 == uVal && 1 == uMessIdx) fvulCurrentTsMsb[fuCurrDpbIdx] = 0;
+  else
+    fvulCurrentTsMsb[fuCurrDpbIdx] = uVal;
+
+  if (fMonitor)
+    if (fMonitor->GetDebugMode()) {  //also if( 1 < uMessIdx )?
+      fMonitor->FillStsDpbRawTsMsb(fuCurrDpbIdx, fvulCurrentTsMsb[fuCurrDpbIdx]);
+      fMonitor->FillStsDpbRawTsMsbSx(fuCurrDpbIdx, fvulCurrentTsMsb[fuCurrDpbIdx]);
+      fMonitor->FillStsDpbRawTsMsbDpb(fuCurrDpbIdx, fvulCurrentTsMsb[fuCurrDpbIdx]);
+      const uint16_t usElinkIdx = mess.GetStatusLink();
+      const uint32_t uCrobIdx   = usElinkIdx / fNrElinksPerCrob;
+      const uint32_t uAsicIdx   = getAsicIndex(fuCurrDpbIdx, uCrobIdx, usElinkIdx);
+
+      fMonitor->FillStsAsicTsMsb(fvulCurrentTsMsb[fuCurrDpbIdx], uAsicIdx);
+    }
+}
+
+// ---- refreshTsMsbFields ----
+void CbmStsUnpackAlgo::refreshTsMsbFields(const uint32_t imslice, const size_t mstime)
+{
+  const uint32_t uTsMsbCycleHeader =
+    std::floor(mstime / (stsxyter::kulTsCycleNbBinsBinning * stsxyter::kdClockCycleNs));
+
+  if (0 == imslice) {
+    if (uTsMsbCycleHeader != fvuCurrentTsMsbCycle[fuCurrDpbIdx])
+      LOG(info) << " TS " << std::setw(12) << fTsIndex << " MS " << std::setw(12) << mstime << " MS Idx "
+                << std::setw(4) << imslice << " Msg Idx " << std::setw(5) << 0 << " DPB " << std::setw(2)
+                << fuCurrDpbIdx << " Old TsMsb " << std::setw(5) << fvulCurrentTsMsb[fuCurrDpbIdx] << " Old MsbCy "
+                << std::setw(5) << fvuCurrentTsMsbCycle[fuCurrDpbIdx] << " New MsbCy " << uTsMsbCycleHeader;
+    fvuCurrentTsMsbCycle[fuCurrDpbIdx] = uTsMsbCycleHeader;
+    fvulCurrentTsMsb[fuCurrDpbIdx]     = 0;
+  }
+  else if (uTsMsbCycleHeader != fvuCurrentTsMsbCycle[fuCurrDpbIdx]) {
+    if (4194303 == fvulCurrentTsMsb[fuCurrDpbIdx]) {
+      LOG(info) << " TS " << std::setw(12) << fTsIndex << " MS " << std::setw(12) << mstime << " MS Idx "
+                << std::setw(4) << imslice << " Msg Idx " << std::setw(5) << 0 << " DPB " << std::setw(2)
+                << fuCurrDpbIdx << " Old TsMsb " << std::setw(5) << fvulCurrentTsMsb[fuCurrDpbIdx] << " Old MsbCy "
+                << std::setw(5) << fvuCurrentTsMsbCycle[fuCurrDpbIdx] << " New MsbCy " << uTsMsbCycleHeader;
+    }
+    else {
+      LOG(warning) << "TS MSB cycle from MS header does not match current cycle from data "
+                   << "for TS " << std::setw(12) << fTsIndex << " MS " << std::setw(12) << mstime << " MsInTs "
+                   << std::setw(3) << imslice << " ====> " << fvuCurrentTsMsbCycle[fuCurrDpbIdx] << " (cnt) VS "
+                   << uTsMsbCycleHeader << " (header)";
+    }
+    fvuCurrentTsMsbCycle[fuCurrDpbIdx] = uTsMsbCycleHeader;
+  }
+}
+
+// ---- unpack ----
+bool CbmStsUnpackAlgo::unpack(const fles::Timeslice* ts, std::uint16_t icomp, UInt_t imslice)
+{
+  auto msDescriptor = ts->descriptor(icomp, imslice);
+
+  //Current equipment ID, tells from which DPB the current MS is originating
+  const uint32_t uCurrentEquipmentId = msDescriptor.eq_id;
+  const uint8_t* msContent           = reinterpret_cast<const uint8_t*>(ts->content(icomp, imslice));
+  const uint32_t uSize               = msDescriptor.size;
+
+  fMsStartTime = msDescriptor.idx;
+  LOG(debug) << "Microslice: " << fMsStartTime << " from EqId " << std::hex << uCurrentEquipmentId << std::dec
+             << " has size: " << uSize;
+
+  if (0 == fvbMaskedComponents.size()) fvbMaskedComponents.resize(ts->num_components(), false);
+
+  //Temp holder until current equipment ID is properly filled in MS
+  const uint32_t uCurrDpbId = static_cast<uint32_t>(uCurrentEquipmentId & 0xFFFF);
+
+  if (fMonitor)
+    if (fMonitor->GetDebugMode()) {
+      const double dMsTime = (1e-9) * static_cast<double>(fMsStartTime);
+      if (icomp < fMonitor->GetMaxNbFlibLinks()) {
+        if (fdStartTimeMsSz < 0) fdStartTimeMsSz = dMsTime;
+        fMonitor->FillMsSize(icomp, uSize);
+        fMonitor->FillMsSizeTime(icomp, dMsTime - fdStartTimeMsSz, uSize);
+      }
+    }
+
+  /// Check if this sDPB ID was declared in parameter file and stop there if not
+  auto it = fDpbIdIndexMap.find(uCurrDpbId);
+  if (it == fDpbIdIndexMap.end()) {
+    if (false == fvbMaskedComponents[icomp]) {
+      LOG(info) << "---------------------------------------------------------------";
+      // Had to remove this line otherwise we would get circle dependencies in the current stage of cbmroot, since we still have Unpackers in the fles folders, which require the reco folders
+      // LOG(info) << FormatMsHeaderPrintout(msDescriptor);
+      LOG(warning) << fName << "unpack(...)::Could not find the sDPB index for AFCK id 0x" << std::hex << uCurrDpbId
+                   << std::dec << " in timeslice " << fNrProcessedTs << " in microslice " << imslice << " component "
+                   << icomp << "\n"
+                   << "If valid this index has to be added in the STS "
+                      "parameter file in the DbpIdArray field";
+      fvbMaskedComponents[icomp] = true;
+
+      /// If first TS being analyzed, we are probably detecting STS/MUCH boards with same sysid
+      /// => Do not report the MS as bad, just ignore it
+      if (1 == fNrProcessedTs) return true;
+    }
+    else
+      return true;
+
+    // REMARK please check experts, but I think this point can never be reached PR 072021
+    return false;
+  }
+  else
+    fuCurrDpbIdx = fDpbIdIndexMap[uCurrDpbId];
+
+  if (fMonitor) fMonitor->FillMsCntEvo(fMsStartTime);
+
+  // Check the current TS_MSb cycle and correct it if wrong
+  refreshTsMsbFields(imslice, fMsStartTime);
+
+  //Variables for debugging info
+  std::vector<uint32_t> vNbMessType(7, 0);
+  std::string sMessPatt = "";
+  bool bError           = false;
+
+  //Main processing of MS content
+  loopMsMessages(msContent, uSize, imslice, bError, sMessPatt, vNbMessType);
+
+  //Output debugging info
+  /** @todo @experts this is printout debugging which depends on monitor settings. Not sure whether this is a good way. Please check and decide. (It was this way already before to be clear) */
+  if (fMonitor)
+    if (fMonitor->GetDebugMode()) {
+      if (18967040000 == fMsStartTime || 18968320000 == fMsStartTime) { LOG(info) << sMessPatt; }
+      if (static_cast<uint16_t>(fles::MicrosliceFlags::CrcValid) != msDescriptor.flags) {
+        LOG(info) << "STS unp "
+                  << " TS " << std::setw(12) << fNrProcessedTs << " MS " << std::setw(12) << fMsStartTime
+                  << " MS flags 0x" << std::setw(4) << std::hex << msDescriptor.flags << std::dec << " Size "
+                  << std::setw(8) << uSize << " bytes "
+                  << " H " << std::setw(5) << vNbMessType[0] << " T " << std::setw(5) << vNbMessType[1] << " E "
+                  << std::setw(5) << vNbMessType[2] << " S " << std::setw(5) << vNbMessType[3] << " Em " << std::setw(5)
+                  << vNbMessType[4] << " En " << std::setw(5) << vNbMessType[5] << " D " << std::setw(5)
+                  << vNbMessType[6] << " Err " << bError << " Undet. bad " << (!bError && 400 != vNbMessType[1]);
+      }
+    }
+  return true;
+}
+
 
 ClassImp(CbmStsUnpackAlgo)
diff --git a/reco/detectors/sts/unpack/CbmStsUnpackAlgo.h b/reco/detectors/sts/unpack/CbmStsUnpackAlgo.h
index 576dbde875..ea5ea0ec59 100644
--- a/reco/detectors/sts/unpack/CbmStsUnpackAlgo.h
+++ b/reco/detectors/sts/unpack/CbmStsUnpackAlgo.h
@@ -21,8 +21,11 @@
 #ifndef CbmStsUnpackAlgo_H
 #define CbmStsUnpackAlgo_H
 
+#include "CbmErrorMessage.h"  // REMARK see remark in CbmStsUnpackConfig
+#include "CbmMcbm2018StsPar.h"
 #include "CbmRecoUnpackAlgo.tmpl"
 #include "CbmStsDigi.h"
+#include "CbmStsUnpackMonitor.h"
 
 #include "Timeslice.hpp"  // timeslice
 
@@ -34,7 +37,7 @@
 #include <memory>
 #include <utility>
 
-class CbmStsUnpackAlgo : public CbmRecoUnpackAlgo<CbmStsDigi> {
+class CbmStsUnpackAlgo : public CbmRecoUnpackAlgo<CbmStsDigi, CbmErrorMessage> {
 public:
   /** @brief Create the Cbm Trd Unpack AlgoBase object */
   CbmStsUnpackAlgo();
@@ -58,29 +61,67 @@ public:
    * @return fParContVec
   */
   virtual std::vector<std::pair<std::string, std::shared_ptr<FairParGenericSet>>>*
-  GetParContainerRequest(std::string geoTag, std::uint32_t runId)
-  {
-    return {};
-  };
+  GetParContainerRequest(std::string geoTag, std::uint32_t runId);
+
+  /**
+   * @brief Mask a Noisy Channel
+   * 
+   * @param uFeb 
+   * @param uChan 
+   * @param bMasked 
+  */
+  void MaskNoisyChannel(const uint32_t uFeb, const uint32_t uChan, const bool bMasked = true);
+
+  /** @brief Set the minimum adc cut value @param[in] value */
+  void SetMinAdcCut(uint32_t value) { fdAdcCut = value; }
+
+  /** @brief Set the minimum adc cut value @param[in] value */
+  void SetAsicTimeOffsetVec(std::vector<double> value) { fvdTimeOffsetNsAsics.swap(value); }
+
+  /** @brief Set a predefined monitor @param monitor predefined unpacking monitor */
+  void SetMonitor(std::shared_ptr<CbmStsUnpackMonitor> monitor) { fMonitor = monitor; }
 
 protected:
+  // /** @brief Add the sts-xyter-hits to the output buffer as Digis */
+  // void AddHitsToDigiVect(std::vector<stsxyter::FinalHit>* vmHitsIn, std::vector<CbmStsDigi>* vDigiVectOut);
+
+
   /** @brief Finish function for this algorithm base clase */
   void finish()
   {
-    finishDerived();
-    // Finish the monitor if we have one
     // if (fMonitor) fMonitor->Finish();
+    return;
   }
 
-  /** @brief Function that allows special calls during Finish in the derived algos */
-  virtual void finishDerived() { return; }
+  /**
+   * @brief Get the Asic Index
+   * 
+   * @param dpbidx 
+   * @param crobidx 
+   * @param elinkidx 
+  */
+  uint32_t getAsicIndex(uint32_t dpbidx, uint32_t crobidx, uint16_t elinkidx);
+
+  /**
+   * @brief Get the Full Time Stamp from raw time stamp 
+   * 
+   * @param usRawTs 
+   * @return uint64_t 
+  */
+  uint64_t getFullTimeStamp(const uint16_t usRawTs);
 
   /**
    * @brief Intialisation at begin of run. Special inits of the derived algos.
    * 
    * @retval Bool_t initOk
   */
-  Bool_t init() { return kTRUE; }
+  Bool_t init();
+
+  /** @brief Initialize the DpbIdIndexMap with the information from the parset @param[in] parset parameter set for the Sts unpacker */
+  void initDpbIdIndexMap(CbmMcbm2018StsPar* parset);
+
+  /** @brief experts please add description here */
+  void initInternalStatus(CbmMcbm2018StsPar* parset);
 
   /**
    * @brief Handles the distribution of the hidden derived classes to their explicit functions.
@@ -88,7 +129,60 @@ protected:
    * @param parset 
    * @return Bool_t initOk 
   */
-  Bool_t initParSet(FairParGenericSet* parset) { return kTRUE; }
+  Bool_t initParSet(FairParGenericSet* parset);
+
+  /**
+   * @brief Initialize the parameters from CbmMcbm2018StsPar.
+   * 
+   * @param parset 
+   * @return Bool_t initOk 
+  */
+  Bool_t initParSet(CbmMcbm2018StsPar* parset);
+
+  /** @brief Initialize and transfer the informations to the parameters storage vectors */
+  void initTempVectors(CbmMcbm2018StsPar* parset, std::vector<int32_t>* viModuleType,
+                       std::vector<int32_t>* viModAddress,
+                       std::vector<std::vector<std::vector<int32_t>>>* viFebModuleIdx,
+                       std::vector<std::vector<bool>>* vbCrobActiveFlag,
+                       std::vector<std::vector<std::vector<int32_t>>>* viFebModuleSide);
+  /**
+   * @brief Main loop over the sts xyter messages in the µSlices
+   * 
+   * @param msContent 
+   * @param uSize 
+   * @param uMsIdx 
+   * @param bError 
+   * @param sMessPatt 
+   * @param vNbMessType 
+  */
+  void loopMsMessages(const uint8_t* msContent, const uint32_t uSize, const size_t uMsIdx, bool& bError,
+                      std::string& sMessPatt, std::vector<uint32_t>& vNbMessType);
+
+
+  /** @brief experts please add description */
+  void printActiveCrobs(CbmMcbm2018StsPar* parset, const std::vector<std::vector<bool>>& vbCrobActiveFlag);
+
+  /** @brief experts please add description */
+  void printAddressMaps(CbmMcbm2018StsPar* parset, const std::vector<std::vector<std::vector<int32_t>>>& viFebModuleIdx,
+                        const std::vector<std::vector<std::vector<int32_t>>>& viFebModuleSide);
+
+  /** @brief experts please add description marked as not used currently*/
+  void processEpochInfo(const stsxyter::Message& /*mess*/) { return; };
+
+  /** @brief experts please add description */
+  void processErrorInfo(const stsxyter::Message& mess);
+
+  /** @brief Process the information of the hit message and create a StsDigi from it */
+  void processHitInfo(const stsxyter::Message& mess);
+
+  /** @brief experts please add description */
+  void processStatusInfo(const stsxyter::Message& mess, uint32_t uIdx);
+
+  /** @brief experts please add description */
+  void processTsMsbInfo(const stsxyter::Message& mess, uint32_t uMessIdx, uint32_t uMsIdx);
+
+  /** @brief experts please add description here */
+  void refreshTsMsbFields(const uint32_t imslice, const size_t mstime);
 
   /**
    * @brief Set the Derived Ts Parameters
@@ -99,7 +193,7 @@ protected:
    * @return true 
    * @return false 
   */
-  bool setDerivedTsParameters(size_t itimeslice) { return true; }
+  bool setDerivedTsParameters(size_t /*itimeslice*/) { return true; }
 
   /**
    * @brief Unpack a given microslice. To be implemented in the derived unpacker algos.
@@ -112,7 +206,99 @@ protected:
    * 
    * @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; }
+  bool unpack(const fles::Timeslice* ts, std::uint16_t icomp, UInt_t imslice);
+
+  // Monitoring
+  /** @brief Potential (online) monitor for the unpacking process */
+  std::shared_ptr<CbmStsUnpackMonitor> fMonitor = nullptr;
+
+  /** @brief Current µSlice time */
+  uint64_t fMsStartTime = 0;
+
+  // Parameter members
+  /** @brief Map of DPB Identifier to DPB index */
+  std::map<uint32_t, uint32_t> fDpbIdIndexMap = {};  //!
+
+  /** @brief Current dpb id */
+  uint32_t fuCurrDpbIdx = 0;
+
+  /** @brief Masked components to print out missing component only once */
+  std::vector<bool> fvbMaskedComponents;
+
+  /** @brief Number of FEBs with StsXyter ASICs */
+  uint32_t fuNbFebs = 0;  //!
+
+  /** @brief Number of eLinks per CROB */
+  uint32_t fNrElinksPerCrob = 0;  //!
+
+  /** @brief Number of ASICs per CROB */
+  uint32_t fNrAsicsPerCrob = 0;  //!
+
+  /** @brief Number of ASICs per FEB */
+  uint32_t fNrAsicsPerFeb = 0;  //!
+
+  /** @brief Number of Channels per Asic */
+  uint32_t fNrChsPerAsic = 0;  //!
+
+  /** @brief Number of Channels per FEB */
+  uint32_t fNrChsPerFeb = 0;  //!
+
+  /** @brief Number of CROBs per DPB */
+  uint32_t fNrCrobPerDpb = 0;  //!
+
+  /** @brief Vector used for the translation between eLink index and FEB index*/
+  std::vector<int> fElinkIdxToFebIdxVec = {};
+
+  /** @brief Vector used for the translation between eLink index and Asic index first is feb type A second is feb type b*/
+  std::vector<std::pair<uint32_t, uint32_t>> fElinkIdxToAsicIdxVec = {};
+
+  /** @brief Minimum adc cut to store a hit */
+  uint32_t fdAdcCut = 0;
+
+  /** @brief Time offsets per Asic??? @todo expert confirmation required */
+  std::vector<double> fvdTimeOffsetNsAsics = {};
+
+  /** @brief flag if channel mask is to be used or not. Set automatically via MaskNoisyChannels */
+  bool fbUseChannelMask = false;
+
+  /** @brief Vector of channel masks, [ NbFeb ][ NbCHanInFeb ], used only if fbUseChannelMask is true */
+  std::vector<std::vector<bool>> fvvbMaskedChannels = {};  //!
+
+
+  /** @brief  FEB type, [ NbDpb ][ NbCrobPerDpb ][ NbFebsPerCrob ], 0 = A, 1 = B, -1 if inactive */
+  std::vector<std::vector<std::vector<int32_t>>> fviFebType = {};  //!
+
+  /** @brief STS address for each FEB, [ NbDpb * NbCrobPerDpb * NbFebsPerCrob ] */
+  std::vector<int32_t> fviFebAddress;  //!
+  /** @brief Module side for each FEB, [ NbDpb * NbCrobPerDpb * NbFebsPerCrob ] */
+  std::vector<int32_t> fviFebSide;  //!
+  /** @brief ADC gain in e-/b, [ NbDpb * NbCrobPerDpb * NbFebsPerCrob ] */
+  std::vector<double> fvdFebAdcGain;  //!
+  /** @brief ADC offset in e-, [ NbDpb * NbCrobPerDpb * NbFebsPerCrob ] */
+  std::vector<double> fvdFebAdcOffs;  //!
+
+  /** @brief Current TS MSB for each DPB */
+  std::vector<uint64_t> fvulCurrentTsMsb = {};  //!
+  /** @brief Current TS MSB cycle for DPB */
+  std::vector<uint32_t> fvuCurrentTsMsbCycle = {};  //!
+
+
+  /// Duplicate hits suppression
+  static const uint32_t kuMaxTsMsbDiffDuplicates = 8;
+  /** @brief TS of last hit message for each channel, [ AsicIdx ][ Chan ] */
+  std::vector<std::vector<uint16_t>> fvvusLastTsChan = {};  //!
+  /** @brief ADC of last hit message for each channel, [ AsicIdx ][ Chan ] */
+  std::vector<std::vector<uint16_t>> fvvusLastAdcChan = {};  //!
+  /** @brief TS MSB of last hit message for each channel, [ AsicIdx ][ Chan ] */
+  std::vector<std::vector<uint16_t>> fvvusLastTsMsbChan = {};  //!
+  /** @brief TS MSB cycle of last hit message for each channel, [ AsicIdx ][ Chan ] */
+  std::vector<std::vector<uint16_t>> fvvusLastTsMsbCycleChan = {};  //!
+
+  /** @brief Time of first valid hit (TS_MSB available), used as reference for evolution plots @todo move this to the monitor the algo does not need it!*/
+  double fdStartTime = 0;
+
+  /** @brief Time of first microslice, used as reference for evolution plots @todo move this to the monitor the algo does not need it!*/
+  double fdStartTimeMsSz = 0;
 
 private:
   ClassDef(CbmStsUnpackAlgo, 2)
diff --git a/reco/detectors/sts/unpack/CbmStsUnpackConfig.cxx b/reco/detectors/sts/unpack/CbmStsUnpackConfig.cxx
index 256521dc56..48fdc0927f 100644
--- a/reco/detectors/sts/unpack/CbmStsUnpackConfig.cxx
+++ b/reco/detectors/sts/unpack/CbmStsUnpackConfig.cxx
@@ -33,13 +33,29 @@ void CbmStsUnpackConfig::InitUnpacker()
   // First choose the derived unpacking algorithm to be used and pass the raw to digi method
   auto algo = chooseAlgo();
 
+  if (fDoLog) LOG(info) << fName << "::Init - SetParFilesBasePath";
+  algo->SetParFilesBasePath(fParFilesBasePath);
+
   // Initialise the parameter containers required by the unpacker algo. Includes loading the corresponding ascii files
   auto reqparvec = algo->GetParContainerRequest(fGeoSetupTag, fRunId);
   initOk &= initParContainers(reqparvec);
 
+  // Set the minimum adc cut
+  algo->SetMinAdcCut(fdAdcCut);
+
+  // Set the single asics time offsets
+  algo->SetAsicTimeOffsetVec(fvdTimeOffsetNsAsics);
+
+  // Mask the noisy channels set by the user
+  for (auto chmask : fvChanMasks)
+    algo->MaskNoisyChannel(chmask.uFeb, chmask.uChan, chmask.bMasked);
+
+
   // Now we have all information required to initialise the algorithm
   algo->Init();
 
+  if (fMonitor) algo->SetMonitor(fMonitor);
+
   // Pass the algo to its member in the base class
   fAlgo = algo;
 }
diff --git a/reco/detectors/sts/unpack/CbmStsUnpackConfig.h b/reco/detectors/sts/unpack/CbmStsUnpackConfig.h
index d6a271c708..a930aa0aa6 100644
--- a/reco/detectors/sts/unpack/CbmStsUnpackConfig.h
+++ b/reco/detectors/sts/unpack/CbmStsUnpackConfig.h
@@ -18,9 +18,11 @@
 #ifndef CbmStsUnpackConfig_H
 #define CbmStsUnpackConfig_H
 
+#include "CbmErrorMessage.h"  // REMARK this should become a Sts specific container I Would propose PR
 #include "CbmRecoUnpackConfig.tmpl"
 #include "CbmStsDigi.h"
 #include "CbmStsUnpackAlgo.h"
+#include "CbmStsUnpackMonitor.h"
 
 #include <FairLogger.h>
 #include <Logger.h>
@@ -33,7 +35,7 @@
 #include <memory>
 #include <vector>
 
-class CbmStsUnpackConfig : public CbmRecoUnpackConfig<CbmStsUnpackAlgo, CbmStsDigi> {
+class CbmStsUnpackConfig : public CbmRecoUnpackConfig<CbmStsUnpackAlgo, CbmStsDigi, CbmErrorMessage> {
 
 public:
   /**
@@ -57,8 +59,15 @@ public:
   /** @brief Assignment operator - not implemented **/
   CbmStsUnpackConfig& operator=(const CbmStsUnpackConfig&) = delete;
 
-  // Getters
+  struct FebChanMaskReco {
+    UInt_t uFeb;
+    UInt_t uChan;
+    Bool_t bMasked;
+  };
 
+  // Getters
+  /** @brief Get the potentially added monitor. */
+  std::shared_ptr<CbmStsUnpackMonitor> GetMonitor() { return fMonitor; }
 
   /**
    * @brief Prepare the unpacker to be ready to run.
@@ -66,7 +75,31 @@ public:
   */
   void InitUnpacker();
 
+  void MaskNoisyChannel(UInt_t uFeb, UInt_t uChan, Bool_t bMasked = kTRUE)
+  {
+    fvChanMasks.emplace_back(FebChanMaskReco {uFeb, uChan, bMasked});
+  }
+
+
   // Setters
+  /**
+   * @brief Set the Asic Time Offset
+   * 
+   * @param asicid Idx of the ASIC with the given time offset
+   * @param value time offset
+  */
+  void SetAsicTimeOffset(size_t asicid, double value)
+  {
+    if (fvdTimeOffsetNsAsics.size() < (asicid + 1)) fvdTimeOffsetNsAsics.resize(asicid + 1);
+    fvdTimeOffsetNsAsics.at(asicid) = value;
+  }
+
+  /** @brief Set the minimum adc cut value @param[in] value */
+  void SetMinAdcCut(uint32_t value) { fdAdcCut = value; }
+
+  /** @brief Add a monitor to the unpacker. @param value CbmStsUnpackMonitor */
+  void SetMonitor(std::shared_ptr<CbmStsUnpackMonitor> value) { fMonitor = value; }
+
 
 protected:
   /**
@@ -76,6 +109,19 @@ protected:
   */
   virtual std::shared_ptr<CbmStsUnpackAlgo> chooseAlgo();
 
+  /** @brief pointer to the monitor object */
+  std::shared_ptr<CbmStsUnpackMonitor> fMonitor = nullptr;
+
+  /** @brief Minimum adc cut to store a hit */
+  uint32_t fdAdcCut = 0;
+
+  /** @brief Vector with the Asic time offsets */
+  std::vector<double> fvdTimeOffsetNsAsics = {};
+
+  /// Temporary storage of user parameters
+  std::vector<FebChanMaskReco> fvChanMasks = {};
+
+
   /** @brief Geometry setup tag for the given detector as used by CbmSetup objects */
   std::string fGeoSetupTag = "";
 
diff --git a/reco/detectors/sts/unpack/CbmStsUnpackMonitor.cxx b/reco/detectors/sts/unpack/CbmStsUnpackMonitor.cxx
new file mode 100644
index 0000000000..a42c5a2c58
--- /dev/null
+++ b/reco/detectors/sts/unpack/CbmStsUnpackMonitor.cxx
@@ -0,0 +1,690 @@
+#include "CbmStsUnpackMonitor.h"
+
+// FIXME
+// #include "CbmQaCanvas.h"
+#include "../qa/CbmQaCanvas.h"
+
+#include <FairRun.h>
+#include <FairRunOnline.h>
+#include <Logger.h>
+
+#include <RtypesCore.h>
+#include <TFile.h>
+#include <TH1.h>
+#include <TH2.h>
+#include <THttpServer.h>
+#include <TProfile.h>
+
+#include <cstdint>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <cmath>
+
+CbmStsUnpackMonitor::CbmStsUnpackMonitor(/* args */) : fvpAllHistoPointers()
+{
+  // Miscroslice component properties histos
+  for (UInt_t component = 0; component < kiMaxNbFlibLinks; component++) {
+    fvhMsSize[component]     = nullptr;
+    fvhMsSizeTime[component] = nullptr;
+  }
+}
+
+CbmStsUnpackMonitor::~CbmStsUnpackMonitor() {}
+
+Bool_t CbmStsUnpackMonitor::CreateHistograms(CbmMcbm2018StsPar* pUnpackPar)
+{
+  TString sHistName {""};
+  TString title {""};
+  const UInt_t uNbAsics       = pUnpackPar->GetNrOfAsics();
+  const UInt_t uNbFebs        = pUnpackPar->GetNrOfFebs();
+  const UInt_t uNbAsicsPerFeb = pUnpackPar->GetNbAsicsPerFeb();
+  const UInt_t uNbChanPerFeb  = pUnpackPar->GetNbChanPerFeb();
+
+  /// Create general unpacking histograms
+  fhDigisTimeInRun =
+    new TH1I("hStsDigisTimeInRun", "Digis Nb vs Time in Run; Time in run [s]; Digis Nb []", 36000, 0, 3600);
+  AddHistoToVector(fhDigisTimeInRun, "");
+
+  fhVectorSize = new TH1I("fhVectorSize", "Size of the vector VS TS index; TS index; Size [bytes]", 10000, 0., 10000.);
+  AddHistoToVector(fhVectorSize, "");
+
+  fhVectorCapacity =
+    new TH1I("fhVectorCapacity", "Size of the vector VS TS index; TS index; Size [bytes]", 10000, 0., 10000.);
+  AddHistoToVector(fhVectorCapacity, "");
+
+  fhMsCntEvo = new TH1I("fhMsCntEvo", "; MS index [s]; Counts []", 600, 0.0, 600.0);
+  AddHistoToVector(fhMsCntEvo, "");
+
+  fhMsErrorsEvo = new TH2I("fhMsErrorsEvo", "; MS index [s]; Error type []; Counts []", 600, 0.0, 600.0, 4, -0.5, 3.5);
+  AddHistoToVector(fhMsErrorsEvo, "");
+
+  /// Hit rates evo per FEB in system
+  sHistName              = "hStsAllFebsHitRateEvo";
+  title                  = "Hits per second & FEB; Time [s]; FEB []; Hits []";
+  fhStsAllFebsHitRateEvo = new TH2I(sHistName, title, 1800, 0, 1800, uNbFebs, -0.5, uNbFebs - 0.5);
+  AddHistoToVector(fhStsAllFebsHitRateEvo, "");
+
+  /// Hit rates evo per ASIC in system
+  sHistName               = "hStsAllAsicsHitRateEvo";
+  title                   = "Hits per second & ASIC; Time [s]; ASIC []; Hits []";
+  fhStsAllAsicsHitRateEvo = new TH2I(sHistName, title, 1800, 0, 1800, uNbAsics, -0.5, uNbAsics - 0.5);
+  AddHistoToVector(fhStsAllAsicsHitRateEvo, "");
+
+  /// Hit counts map in system
+  sHistName = "hStsFebAsicHitCounts";
+  title     = "Hits per FEB & ASIC; FEB []; ASIC in FEB[]; Hits []";
+  fhStsFebAsicHitCounts =
+    new TH2I(sHistName, title, uNbFebs, -0.5, uNbFebs - 0.5, uNbAsicsPerFeb, -0.5, uNbAsicsPerFeb - 0.5);
+  AddHistoToVector(fhStsFebAsicHitCounts, "");
+
+  sHistName           = "hStsStatusMessType";
+  title               = "Nb of status message of each type for each DPB; ASIC; Status Type";
+  fhStsStatusMessType = new TH2I(sHistName, title, uNbAsics, 0, uNbAsics, 16, 0., 16.);
+  AddHistoToVector(fhStsStatusMessType, "");
+
+  ///++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++///
+  UInt_t uAlignedLimit = fuLongHistoNbSeconds - (fuLongHistoNbSeconds % fuLongHistoBinSizeSec);
+  fuLongHistoBinNb     = uAlignedLimit / fuLongHistoBinSizeSec;
+
+  ///++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++///
+  /// FEB-8 plots
+  /// All histos per FEB: with channels or ASIC as axis!!
+  for (UInt_t uFebIdx = 0; uFebIdx < uNbFebs; ++uFebIdx) {
+
+    /// Channel counts
+    sHistName = Form("hStsFebChanCntRaw_%03u", uFebIdx);
+    title     = Form("Hits Count per channel, FEB #%03u; Channel; Hits []", uFebIdx);
+    fvhStsFebChanCntRaw.push_back(new TH1I(sHistName, title, uNbChanPerFeb, -0.5, uNbChanPerFeb - 0.5));
+
+    /// Raw Adc Distribution
+    sHistName = Form("hStsFebChanAdcRaw_%03u", uFebIdx);
+    title     = Form("Raw Adc distribution per channel, FEB #%03u; Channel []; Adc "
+                 "[]; Hits []",
+                 uFebIdx);
+    fvhStsFebChanAdcRaw.push_back(new TH2I(sHistName, title, uNbChanPerFeb, -0.5, uNbChanPerFeb - 0.5,
+                                           stsxyter::kuHitNbAdcBins, -0.5, stsxyter::kuHitNbAdcBins - 0.5));
+
+    /// Raw Adc Distribution profile
+    sHistName = Form("hStsFebChanAdcRawProfc_%03u", uFebIdx);
+    title     = Form("Raw Adc prodile per channel, FEB #%03u; Channel []; Adc []", uFebIdx);
+    fvhStsFebChanAdcRawProf.push_back(new TProfile(sHistName, title, uNbChanPerFeb, -0.5, uNbChanPerFeb - 0.5));
+
+    /// Cal Adc Distribution
+    sHistName = Form("hStsFebChanAdcCal_%03u", uFebIdx);
+    title     = Form("Cal. Adc distribution per channel, FEB #%03u; Channel []; Adc [e-]; Hits []", uFebIdx);
+    fvhStsFebChanAdcCal.push_back(
+      new TH2I(sHistName, title, uNbChanPerFeb, -0.5, uNbChanPerFeb - 0.5, 50, 0., 100000.));
+
+    /// Cal Adc Distribution profile
+    sHistName = Form("hStsFebChanAdcCalProfc_%03u", uFebIdx);
+    title     = Form("Cal. Adc prodile per channel, FEB #%03u; Channel []; Adc [e-]", uFebIdx);
+    fvhStsFebChanAdcCalProf.push_back(new TProfile(sHistName, title, uNbChanPerFeb, -0.5, uNbChanPerFeb - 0.5));
+
+    /// Raw Ts Distribution
+    sHistName = Form("hStsFebChanRawTs_%03u", uFebIdx);
+    title     = Form("Raw Timestamp distribution per channel, FEB #%03u; Channel "
+                 "[]; Ts []; Hits []",
+                 uFebIdx);
+    fvhStsFebChanRawTs.push_back(new TH2I(sHistName, title, uNbChanPerFeb, -0.5, uNbChanPerFeb - 0.5,
+                                          stsxyter::kuHitNbTsBins, -0.5, stsxyter::kuHitNbTsBins - 0.5));
+
+    /// Missed event flag
+    sHistName = Form("hStsFebChanMissEvt_%03u", uFebIdx);
+    title     = Form("Missed Event flags per channel, FEB #%03u; Channel []; Miss "
+                 "Evt []; Hits []",
+                 uFebIdx);
+    fvhStsFebChanMissEvt.push_back(new TH2I(sHistName, title, uNbChanPerFeb, -0.5, uNbChanPerFeb - 0.5, 2, -0.5, 1.5));
+
+    /// Missed event flag counts evolution
+    sHistName = Form("hStsFebChanMissEvtEvo_%03u", uFebIdx);
+    title     = Form("Missed Evt flags per second & channel in FEB #%03u; Time "
+                 "[s]; Channel []; Missed Evt flags []",
+                 uFebIdx);
+    fvhStsFebChanMissEvtEvo.push_back(
+      new TH2I(sHistName, title, 1800, 0, 1800, uNbChanPerFeb, -0.5, uNbChanPerFeb - 0.5));
+
+    /// Missed event flag counts evolution
+    sHistName = Form("hStsFebAsicMissEvtEvo_%03u", uFebIdx);
+    title     = Form("Missed Evt flags per second & StsXyter in FEB #%03u; Time "
+                 "[s]; Asic []; Missed Evt flags []",
+                 uFebIdx);
+    fvhStsFebAsicMissEvtEvo.push_back(
+      new TH2I(sHistName, title, 1800, 0, 1800, uNbAsicsPerFeb, -0.5, uNbAsicsPerFeb - 0.5));
+
+    /// Missed event flag counts evolution
+    sHistName = Form("hStsFebMissEvtEvo_%03u", uFebIdx);
+    title     = Form("Missed Evt flags per second & channel in FEB #%03u; Time "
+                 "[s]; Missed Evt flags []",
+                 uFebIdx);
+    fvhStsFebMissEvtEvo.push_back(new TH1I(sHistName, title, 1800, 0, 1800));
+
+    /// Hit rates evo per channel
+    sHistName = Form("hStsFebChanRateEvo_%03u", uFebIdx);
+    title     = Form("Hits per second & channel in FEB #%03u; Time [s]; Channel []; Hits []", uFebIdx);
+    fvhStsFebChanHitRateEvo.push_back(
+      new TH2I(sHistName, title, 1800, 0, 1800, uNbChanPerFeb, -0.5, uNbChanPerFeb - 0.5));
+
+    /// Hit rates evo per StsXyter
+    sHistName = Form("hStsFebAsicRateEvo_%03u", uFebIdx);
+    title     = Form("Hits per second & StsXyter in FEB #%03u; Time [s]; Asic []; Hits []", uFebIdx);
+    fvhStsFebAsicHitRateEvo.push_back(
+      new TH2I(sHistName, title, 1800, 0, 1800, uNbAsicsPerFeb, -0.5, uNbAsicsPerFeb - 0.5));
+
+    /// Hit rates evo per FEB
+    sHistName = Form("hStsFebRateEvo_%03u", uFebIdx);
+    title     = Form("Hits per second in FEB #%03u; Time [s]; Hits []", uFebIdx);
+    fvhStsFebHitRateEvo.push_back(new TH1I(sHistName, title, 1800, 0, 1800));
+
+    /// Hit rates evo per channel, 1 minute bins, 24h
+    sHistName = Form("hStsFebChanRateEvoLong_%03u", uFebIdx);
+    title     = Form("Hits per second & channel in FEB #%03u; Time [min]; Channel []; Hits []", uFebIdx);
+    fvhStsFebChanHitRateEvoLong.push_back(new TH2D(sHistName, title, fuLongHistoBinNb, -0.5, uAlignedLimit - 0.5,
+                                                   uNbChanPerFeb, -0.5, uNbChanPerFeb - 0.5));
+
+    /// Hit rates evo per channel, 1 minute bins, 24h
+    sHistName = Form("hStsFebAsicRateEvoLong_%03u", uFebIdx);
+    title     = Form("Hits per second & StsXyter in FEB #%03u; Time [min]; Asic []; Hits []", uFebIdx);
+    fvhStsFebAsicHitRateEvoLong.push_back(new TH2D(sHistName, title, fuLongHistoBinNb, -0.5, uAlignedLimit - 0.5,
+                                                   uNbAsicsPerFeb, -0.5, uNbAsicsPerFeb - 0.5));
+
+    /// Hit rates evo per FEB, 1 minute bins, 24h
+    sHistName = Form("hStsFebRateEvoLong_%03u", uFebIdx);
+    title     = Form("Hits per second in FEB #%03u; Time [min]; Hits []", uFebIdx);
+    fvhStsFebHitRateEvoLong.push_back(new TH1D(sHistName, title, fuLongHistoBinNb, -0.5, uAlignedLimit - 0.5));
+
+    AddHistoToVector(fvhStsFebChanCntRaw[uFebIdx], "perFeb");
+    AddHistoToVector(fvhStsFebChanAdcRaw[uFebIdx], "perFeb");
+    AddHistoToVector(fvhStsFebChanAdcRawProf[uFebIdx], "perFeb");
+    AddHistoToVector(fvhStsFebChanAdcCal[uFebIdx], "perFeb");
+    AddHistoToVector(fvhStsFebChanAdcCalProf[uFebIdx], "perFeb");
+    AddHistoToVector(fvhStsFebChanRawTs[uFebIdx], "perFeb");
+    AddHistoToVector(fvhStsFebChanMissEvt[uFebIdx], "perFeb");
+    AddHistoToVector(fvhStsFebChanMissEvtEvo[uFebIdx], "perFeb");
+    AddHistoToVector(fvhStsFebAsicMissEvtEvo[uFebIdx], "perFeb");
+    AddHistoToVector(fvhStsFebMissEvtEvo[uFebIdx], "perFeb");
+    AddHistoToVector(fvhStsFebChanHitRateEvo[uFebIdx], "perFeb");
+    AddHistoToVector(fvhStsFebAsicHitRateEvo[uFebIdx], "perFeb");
+    AddHistoToVector(fvhStsFebHitRateEvo[uFebIdx], "perFeb");
+    AddHistoToVector(fvhStsFebChanHitRateEvoLong[uFebIdx], "perFeb");
+    AddHistoToVector(fvhStsFebAsicHitRateEvoLong[uFebIdx], "perFeb");
+    AddHistoToVector(fvhStsFebHitRateEvoLong[uFebIdx], "perFeb");
+  }
+
+  /** Summary canvases **/
+  const Double_t w = 2 * 400;
+  const Double_t h = 3 * 200;
+
+  // Create summary canvases per FEB
+  fvcStsSumm.resize(uNbFebs);
+  fvcStsSmxErr.resize(uNbFebs);
+  for (UInt_t uFebIdx = 0; uFebIdx < uNbFebs; ++uFebIdx) {
+    // if (kTRUE == fUnpackParSts->IsFebActive(uFebIdx)) {
+    fvcStsSumm[uFebIdx] =
+      new CbmQaCanvas(Form("cStsSum_%03u", uFebIdx), Form("Summary plots for FEB %03u", uFebIdx), w, h);
+    AddCanvasToVector(fvcStsSumm[uFebIdx], "perFebCanvases");
+
+    fvcStsSumm[uFebIdx]->Divide(2, 3);
+  }
+  return kTRUE;
+}
+
+void CbmStsUnpackMonitor::DrawCanvases()
+{
+  for (UInt_t uFebIdx = 0; uFebIdx < fvcStsSumm.size(); ++uFebIdx) {
+    // if (kTRUE == fUnpackParSts->IsFebActive(uFebIdx)) {
+    fvcStsSumm[uFebIdx]->cd(1);
+    gPad->SetGridx();
+    gPad->SetGridy();
+    gPad->SetLogy();
+    fvhStsFebChanCntRaw[uFebIdx]->DrawCopy();
+
+    //fvcStsSumm[uFebIdx]->cd(2);
+    //gPad->SetGridx();
+    //gPad->SetGridy();
+    //gPad->SetLogy();
+    //fvhStsFebChanHitRateProf[uFebIdx]->DrawCopy("e0");
+
+    fvcStsSumm[uFebIdx]->cd(3);
+    gPad->SetGridx();
+    gPad->SetGridy();
+    gPad->SetLogz();
+    fvhStsFebChanAdcRaw[uFebIdx]->DrawCopy("colz");
+
+    fvcStsSumm[uFebIdx]->cd(4);
+    gPad->SetGridx();
+    gPad->SetGridy();
+    //gPad->SetLogy();
+    fvhStsFebChanAdcRawProf[uFebIdx]->DrawCopy();
+
+    fvcStsSumm[uFebIdx]->cd(5);
+    gPad->SetGridx();
+    gPad->SetGridy();
+    gPad->SetLogz();
+    fvhStsFebChanHitRateEvo[uFebIdx]->DrawCopy("colz");
+
+    fvcStsSumm[uFebIdx]->cd(6);
+    gPad->SetGridx();
+    gPad->SetGridy();
+    //gPad->SetLogy();
+    fvhStsFebChanMissEvt[uFebIdx]->DrawCopy("colz");
+
+    // two following two are inactive as currently adc raw and cal are the same
+
+    //fvcStsSumm[ uFebIdx ]->cd(5);
+    //gPad->SetGridx();
+    //gPad->SetGridy();
+    //gPad->SetLogz();
+    //fvhStsFebChanAdcCal[ uFebIdx ]->DrawCopy( "colz" );
+
+    //fvcStsSumm[ uFebIdx ]->cd(6);
+    //gPad->SetGridx();
+    //gPad->SetGridy();
+    //gPad->SetLogy();
+    //fvhStsFebChanAdcCalProf[ uFebIdx ]->DrawCopy();
+  }
+}
+
+Bool_t CbmStsUnpackMonitor::CreateMsComponentSizeHistos(UInt_t component)
+{
+  if (nullptr == fvhMsSize[component]) {
+    TString sMsSizeName  = Form("MsSize_link_%02lu", component);
+    TString sMsSizeTitle = Form("Size of MS for nDPB of link %02lu; Ms Size [bytes]", component);
+    fvhMsSize[component] = new TH1F(sMsSizeName.Data(), sMsSizeTitle.Data(), 30000, 0., 30000.);
+    fvhMsSize[component]->SetCanExtend(TH2::kAllAxes);
+
+    sMsSizeName              = Form("MsSizeTime_link_%02lu", component);
+    sMsSizeTitle             = Form("Size of MS vs time for gDPB of link %02lu; Time[s] ; Ms Size [bytes]", component);
+    fvhMsSizeTime[component] = new TProfile(sMsSizeName.Data(), sMsSizeTitle.Data(), 15000, 0., 300.);
+    fvhMsSizeTime[component]->SetCanExtend(TH2::kAllAxes);
+
+    AddHistoToVector(fvhMsSize[component], "perComponent");
+    AddHistoToVector(fvhMsSizeTime[component], "perComponent");
+  }
+  return kTRUE;
+}
+
+Bool_t CbmStsUnpackMonitor::ResetMsComponentSizeHistos(UInt_t component)
+{
+  if (nullptr == fvhMsSize[component]) {
+    fvhMsSize[component]->Reset();
+    fvhMsSizeTime[component]->Reset();
+  }
+}
+
+Bool_t CbmStsUnpackMonitor::ResetHistograms()
+{
+  fhDigisTimeInRun->Reset();
+  fhVectorSize->Reset();
+  fhVectorCapacity->Reset();
+  fhMsCntEvo->Reset();
+  fhMsErrorsEvo->Reset();
+  fhStsAllFebsHitRateEvo->Reset();
+  fhStsAllAsicsHitRateEvo->Reset();
+  fhStsFebAsicHitCounts->Reset();
+  fhStsStatusMessType->Reset();
+
+  for (UInt_t uFebIdx = 0; uFebIdx < fvhStsFebChanCntRaw.size(); ++uFebIdx) {
+    fvhStsFebChanCntRaw[uFebIdx]->Reset();
+  }
+  for (UInt_t uFebIdx = 0; uFebIdx < fvhStsFebChanAdcRaw.size(); ++uFebIdx) {
+    fvhStsFebChanAdcRaw[uFebIdx]->Reset();
+  }
+  for (UInt_t uFebIdx = 0; uFebIdx < fvhStsFebChanAdcRawProf.size(); ++uFebIdx) {
+    fvhStsFebChanAdcRawProf[uFebIdx]->Reset();
+  }
+  for (UInt_t uFebIdx = 0; uFebIdx < fvhStsFebChanAdcCal.size(); ++uFebIdx) {
+    fvhStsFebChanAdcCal[uFebIdx]->Reset();
+  }
+  for (UInt_t uFebIdx = 0; uFebIdx < fvhStsFebChanAdcCalProf.size(); ++uFebIdx) {
+    fvhStsFebChanAdcCalProf[uFebIdx]->Reset();
+  }
+  for (UInt_t uFebIdx = 0; uFebIdx < fvhStsFebChanRawTs.size(); ++uFebIdx) {
+    fvhStsFebChanRawTs[uFebIdx]->Reset();
+  }
+  for (UInt_t uFebIdx = 0; uFebIdx < fvhStsFebChanMissEvt.size(); ++uFebIdx) {
+    fvhStsFebChanMissEvt[uFebIdx]->Reset();
+  }
+  for (UInt_t uFebIdx = 0; uFebIdx < fvhStsFebChanMissEvtEvo.size(); ++uFebIdx) {
+    fvhStsFebChanMissEvtEvo[uFebIdx]->Reset();
+  }
+  for (UInt_t uFebIdx = 0; uFebIdx < fvhStsFebAsicMissEvtEvo.size(); ++uFebIdx) {
+    fvhStsFebAsicMissEvtEvo[uFebIdx]->Reset();
+  }
+  for (UInt_t uFebIdx = 0; uFebIdx < fvhStsFebMissEvtEvo.size(); ++uFebIdx) {
+    fvhStsFebMissEvtEvo[uFebIdx]->Reset();
+  }
+  for (UInt_t uFebIdx = 0; uFebIdx < fvhStsFebChanHitRateEvo.size(); ++uFebIdx) {
+    fvhStsFebChanHitRateEvo[uFebIdx]->Reset();
+  }
+  for (UInt_t uFebIdx = 0; uFebIdx < fvhStsFebAsicHitRateEvo.size(); ++uFebIdx) {
+    fvhStsFebAsicHitRateEvo[uFebIdx]->Reset();
+  }
+  for (UInt_t uFebIdx = 0; uFebIdx < fvhStsFebHitRateEvo.size(); ++uFebIdx) {
+    fvhStsFebHitRateEvo[uFebIdx]->Reset();
+  }
+  for (UInt_t uFebIdx = 0; uFebIdx < fvhStsFebChanHitRateEvoLong.size(); ++uFebIdx) {
+    fvhStsFebChanHitRateEvoLong[uFebIdx]->Reset();
+  }
+  for (UInt_t uFebIdx = 0; uFebIdx < fvhStsFebAsicHitRateEvoLong.size(); ++uFebIdx) {
+    fvhStsFebAsicHitRateEvoLong[uFebIdx]->Reset();
+  }
+  for (UInt_t uFebIdx = 0; uFebIdx < fvhStsFebHitRateEvoLong.size(); ++uFebIdx) {
+    fvhStsFebHitRateEvoLong[uFebIdx]->Reset();
+  }
+  return kTRUE;
+}
+
+Bool_t CbmStsUnpackMonitor::CreateDebugHistograms(CbmMcbm2018StsPar* pUnpackPar)
+{
+  const UInt_t uNbAsics       = pUnpackPar->GetNrOfAsics();
+  const UInt_t uNrOfDpbs      = pUnpackPar->GetNrOfDpbs();
+  const UInt_t uNbChanPerAsic = pUnpackPar->GetNbChanPerAsic();
+  TString sHistName {""};
+  TString title {""};
+
+  sHistName     = "hPulserMessageType";
+  title         = "Nb of message for each type; Type";
+  fhStsMessType = new TH1I(sHistName, title, 6, 0., 6.);
+  fhStsMessType->GetXaxis()->SetBinLabel(1, "Dummy");
+  fhStsMessType->GetXaxis()->SetBinLabel(2, "Hit");
+  fhStsMessType->GetXaxis()->SetBinLabel(3, "TsMsb");
+  fhStsMessType->GetXaxis()->SetBinLabel(4, "Epoch");
+  fhStsMessType->GetXaxis()->SetBinLabel(5, "Status");
+  fhStsMessType->GetXaxis()->SetBinLabel(6, "Empty");
+  AddHistoToVector(fhStsMessType, "");
+
+  sHistName           = "hPulserMessageTypePerDpb";
+  title               = "Nb of message of each type for each DPB; DPB; Type";
+  fhStsMessTypePerDpb = new TH2I(sHistName, title, uNrOfDpbs, 0, uNrOfDpbs, 6, 0., 6.);
+  fhStsMessTypePerDpb->GetYaxis()->SetBinLabel(1, "Dummy");
+  fhStsMessTypePerDpb->GetYaxis()->SetBinLabel(2, "Hit");
+  fhStsMessTypePerDpb->GetYaxis()->SetBinLabel(3, "TsMsb");
+  fhStsMessTypePerDpb->GetYaxis()->SetBinLabel(4, "Epoch");
+  fhStsMessTypePerDpb->GetYaxis()->SetBinLabel(5, "Status");
+  fhStsMessTypePerDpb->GetYaxis()->SetBinLabel(6, "Empty");
+  AddHistoToVector(fhStsMessTypePerDpb, "");
+
+  sHistName = "hStsMessTypePerElink";
+  title     = "Nb of message of each type for each DPB; DPB; Type";
+  fhStsMessTypePerElink =
+    new TH2I(sHistName, title, uNrOfDpbs * fNrElinksPerDpb, 0, uNrOfDpbs * fNrElinksPerDpb, 6, 0., 6.);
+  fhStsMessTypePerElink->GetYaxis()->SetBinLabel(1, "Dummy");
+  fhStsMessTypePerElink->GetYaxis()->SetBinLabel(2, "Hit");
+  fhStsMessTypePerElink->GetYaxis()->SetBinLabel(3, "TsMsb");
+  fhStsMessTypePerElink->GetYaxis()->SetBinLabel(4, "Epoch");
+  fhStsMessTypePerElink->GetYaxis()->SetBinLabel(5, "Status");
+  fhStsMessTypePerElink->GetYaxis()->SetBinLabel(6, "Empty");
+  AddHistoToVector(fhStsMessTypePerElink, "");
+
+  sHistName            = "hStsHitsElinkPerDpb";
+  title                = "Nb of hit messages per eLink for each DPB; DPB; eLink; Hits nb []";
+  fhStsHitsElinkPerDpb = new TH2I(sHistName, title, uNrOfDpbs, 0, uNrOfDpbs, 42, 0., 42.);
+  AddHistoToVector(fhStsHitsElinkPerDpb, "");
+
+  sHistName        = "hStsDpbRawTsMsb";
+  title            = "MSB messages for each DPB; DPB; TsMsb; Count []";
+  fhStsDpbRawTsMsb = new TH2I(sHistName, title, uNrOfDpbs, 0, uNrOfDpbs, 10, 0, 10);
+  fhStsDpbRawTsMsb->SetCanExtend(TH2::kAllAxes);
+  AddHistoToVector(fhStsDpbRawTsMsb, "");
+
+  sHistName          = "hStsDpbRawTsMsbSx";
+  title              = "MSB SX messages for each DPB; DPB; TsMsb & 0x1F; Count []";
+  fhStsDpbRawTsMsbSx = new TH2I(sHistName, title, uNrOfDpbs, 0, uNrOfDpbs, 10, 0, 10);
+  fhStsDpbRawTsMsbSx->SetCanExtend(TH2::kAllAxes);
+  AddHistoToVector(fhStsDpbRawTsMsbSx, "");
+
+  sHistName           = "hStsDpbRawTsMsbDpb";
+  title               = "MSB DPB messages for each DPB; DPB; TsMsb >> 5; Count []";
+  fhStsDpbRawTsMsbDpb = new TH2I(sHistName, title, uNrOfDpbs, 0, uNrOfDpbs, 10, 0, 10);
+  fhStsDpbRawTsMsbDpb->SetCanExtend(TH2::kAllAxes);
+  AddHistoToVector(fhStsDpbRawTsMsbDpb, "");
+
+  sHistName      = "fhStsAsicTsMsb";
+  title          = "MSB messages for each Asic; TsMsb; Asic; Count []";
+  fhStsAsicTsMsb = new TH2I(sHistName, title, 10, 0, 10, uNbAsics, 0, uNbAsics);
+  fhStsAsicTsMsb->SetCanExtend(TH2::kAllAxes);
+  AddHistoToVector(fhStsAsicTsMsb, "");
+
+  /// Asic plots
+  /// All histos per Asic: with channels or ASIC as axis!!
+  for (UInt_t uAsicIdx = 0; uAsicIdx < uNbAsics; ++uAsicIdx) {
+
+    /// Channel counts
+    sHistName = Form("hStsChanCntRaw_%03u", uAsicIdx);
+    title     = Form("Hits Count per channel, Asic #%03u; Channel; Hits []", uAsicIdx);
+    fvhStsChanCntRaw.push_back(new TH1I(sHistName, title, uNbChanPerAsic, -0.5, uNbChanPerAsic - 0.5));
+
+    /// Raw Adc Distribution
+    sHistName = Form("hStsChanAdcRaw_%03u", uAsicIdx);
+    title     = Form("Raw Adc distribution per channel, Asic #%03u; Channel []; Adc "
+                 "[]; Hits []",
+                 uAsicIdx);
+    fvhStsChanAdcRaw.push_back(new TH2I(sHistName, title, uNbChanPerAsic, -0.5, uNbChanPerAsic - 0.5,
+                                        stsxyter::kuHitNbAdcBins, -0.5, stsxyter::kuHitNbAdcBins - 0.5));
+
+    /// Raw Adc Distribution profile
+    sHistName = Form("hStsChanAdcRawProfc_%03u", uAsicIdx);
+    title     = Form("Raw Adc prodile per channel, Asic #%03u; Channel []; Adc []", uAsicIdx);
+    fvhStsChanAdcRawProf.push_back(new TProfile(sHistName, title, uNbChanPerAsic, -0.5, uNbChanPerAsic - 0.5));
+
+    /// Raw Ts Distribution
+    sHistName = Form("hStsChanRawTs_%03u", uAsicIdx);
+    title     = Form("Raw Timestamp distribution per channel, Asic #%03u; Channel "
+                 "[]; Ts []; Hits []",
+                 uAsicIdx);
+    fvhStsChanRawTs.push_back(new TH2I(sHistName, title, uNbChanPerAsic, -0.5, uNbChanPerAsic - 0.5,
+                                       stsxyter::kuHitNbTsBins, -0.5, stsxyter::kuHitNbTsBins - 0.5));
+
+    /// Missed event flag
+    sHistName = Form("hStsChanMissEvt_%03u", uAsicIdx);
+    title     = Form("Missed Event flags per channel, Asic #%03u; Channel []; Miss "
+                 "Evt []; Hits []",
+                 uAsicIdx);
+    fvhStsChanMissEvt.push_back(new TH2I(sHistName, title, uNbChanPerAsic, -0.5, uNbChanPerAsic - 0.5, 2, -0.5, 1.5));
+
+    AddHistoToVector(fvhStsChanCntRaw[uAsicIdx], "perAsic");
+    AddHistoToVector(fvhStsChanAdcRaw[uAsicIdx], "perAsic");
+    AddHistoToVector(fvhStsChanAdcRawProf[uAsicIdx], "perAsic");
+    AddHistoToVector(fvhStsChanRawTs[uAsicIdx], "perAsic");
+    AddHistoToVector(fvhStsChanMissEvt[uAsicIdx], "perAsic");
+  }
+  return kTRUE;
+}
+
+Bool_t CbmStsUnpackMonitor::ResetDebugHistograms()
+{
+  fhStsMessType->Reset();
+  fhStsMessTypePerDpb->Reset();
+  fhStsMessTypePerElink->Reset();
+  fhStsHitsElinkPerDpb->Reset();
+  fhStsDpbRawTsMsb->Reset();
+  fhStsDpbRawTsMsbSx->Reset();
+  fhStsDpbRawTsMsbDpb->Reset();
+  fhStsAsicTsMsb->Reset();
+
+  for (UInt_t uAsicIdx = 0; uAsicIdx < fvhStsChanCntRaw.size(); ++uAsicIdx) {
+    fvhStsChanCntRaw[uAsicIdx]->Reset();
+  }
+  for (UInt_t uAsicIdx = 0; uAsicIdx < fvhStsChanAdcRaw.size(); ++uAsicIdx) {
+    fvhStsChanAdcRaw[uAsicIdx]->Reset();
+  }
+  for (UInt_t uAsicIdx = 0; uAsicIdx < fvhStsChanAdcRawProf.size(); ++uAsicIdx) {
+    fvhStsChanAdcRawProf[uAsicIdx]->Reset();
+  }
+  for (UInt_t uAsicIdx = 0; uAsicIdx < fvhStsChanRawTs.size(); ++uAsicIdx) {
+    fvhStsChanRawTs[uAsicIdx]->Reset();
+  }
+  for (UInt_t uAsicIdx = 0; uAsicIdx < fvhStsChanMissEvt.size(); ++uAsicIdx) {
+    fvhStsChanMissEvt[uAsicIdx]->Reset();
+  }
+  return kTRUE;
+}
+// -------------------------------------------------------------------------
+
+
+// -------------------------------------------------------------------------
+void CbmStsUnpackMonitor::FillHitMonitoringHistos(const UInt_t& uFebIdx, const UShort_t& usChan,
+                                                  const UInt_t& uChanInFeb, const UShort_t& usRawAdc,
+                                                  const Double_t& dCalAdc, const UShort_t& usRawTs,
+                                                  const bool& isHitMissedEvts)
+{
+  FillStsFebChanAdcCal(uFebIdx, uChanInFeb, dCalAdc);
+  FillStsFebChanAdcCalProf(uFebIdx, uChanInFeb, dCalAdc);
+  FillStsFebChanCntRaw(uFebIdx, uChanInFeb);
+  FillStsFebChanAdcRaw(uFebIdx, uChanInFeb, usRawAdc);
+  FillStsFebChanRawTs(uFebIdx, usChan, usRawTs);
+  FillStsFebChanMissEvt(uFebIdx, usChan, isHitMissedEvts);
+  FillStsFebChanAdcRawProf(uFebIdx, uChanInFeb, usRawAdc);
+}
+
+
+// -------------------------------------------------------------------------
+void CbmStsUnpackMonitor::FillHitDebugMonitoringHistos(const UInt_t& uAsicIdx, const UShort_t& usChan,
+                                                       const UShort_t& usRawAdc, const UShort_t& usRawTs,
+                                                       const bool& isHitMissedEvts)
+{
+  FillStsChanCntRaw(uAsicIdx, usChan);
+  FillStsChanAdcRaw(uAsicIdx, usChan, usRawAdc);
+  FillStsChanAdcRawProf(uAsicIdx, usChan, usRawAdc);
+  FillStsChanRawTs(uAsicIdx, usChan, usRawTs);
+  FillStsChanMissEvt(uAsicIdx, usChan, isHitMissedEvts);
+}
+
+
+// -------------------------------------------------------------------------
+void CbmStsUnpackMonitor::FillHitEvoMonitoringHistos(const UInt_t& uFebIdx, const UInt_t& uAsicIdx,
+                                                     const UInt_t& uAsicInFeb, const UInt_t& uChanInFeb,
+                                                     const Double_t& dTimeSinceStartSec, const bool& isHitMissedEvts)
+{
+  // Fill histos with time as X axis
+  FillStsFebAsicHitCounts(uFebIdx, uAsicInFeb);
+  FillStsFebChanHitRateEvo(uFebIdx, dTimeSinceStartSec, uChanInFeb);
+  FillStsFebAsicHitRateEvo(uFebIdx, dTimeSinceStartSec, uAsicInFeb);
+  FillStsFebHitRateEvo(uFebIdx, dTimeSinceStartSec);
+  FillStsAllFebsHitRateEvo(dTimeSinceStartSec, uFebIdx);
+  FillStsAllAsicsHitRateEvo(dTimeSinceStartSec, uAsicIdx);
+  if (isHitMissedEvts) {
+    FillStsFebChanMissEvtEvo(uFebIdx, dTimeSinceStartSec, uChanInFeb);
+    FillStsFebAsicMissEvtEvo(uFebIdx, dTimeSinceStartSec, uAsicInFeb);
+    FillStsFebMissEvtEvo(uFebIdx, dTimeSinceStartSec);
+  }
+  const Double_t dTimeSinceStartMin = dTimeSinceStartSec / 60.0;
+  FillStsFebHitRateEvoLong(uFebIdx, dTimeSinceStartMin);
+  FillStsFebChanHitRateEvoLong(uFebIdx, dTimeSinceStartMin, uChanInFeb);
+  FillStsFebAsicHitRateEvoLong(uFebIdx, dTimeSinceStartMin, uAsicInFeb);
+
+  // fviFebCountsSinceLastRateUpdate[uFebIdx]++;
+  // fvdFebChanCountsSinceLastRateUpdate[uFebIdx][uChanInFeb] += 1;
+  /*
+   if( kTRUE == fbLongHistoEnable )
+   {
+      std::chrono::steady_clock::time_point tNow = std::chrono::steady_clock::now();
+      Double_t dUnixTimeInRun = std::chrono::duration_cast< std::chrono::seconds >(tNow - ftStartTimeUnix).count();
+      fhFebRateEvoLong[ uAsicIdx ]->Fill( dUnixTimeInRun , 1.0 / fuLongHistoBinSizeSec );
+      fhFebChRateEvoLong[ uAsicIdx ]->Fill( dUnixTimeInRun , usChan, 1.0 / fuLongHistoBinSizeSec );
+   }
+*/
+}
+
+
+// -------------------------------------------------------------------------
+bool CbmStsUnpackMonitor::ProcessDebugInfo(const stsxyter::Message& Mess, std::string& sMessPatt,
+                                           std::vector<uint32_t>& vNbMessType, const UInt_t& uCurrDpbIdx)
+{
+  bool bError                       = false;
+  const stsxyter::MessType typeMess = Mess.GetMessType();
+
+  FillStsMessType(static_cast<uint16_t>(typeMess));
+  FillStsMessTypePerDpb(uCurrDpbIdx, static_cast<uint16_t>(typeMess));
+  switch (typeMess) {
+    case stsxyter::MessType::Hit: {
+      ++vNbMessType[0];
+      sMessPatt += " H ";
+      sMessPatt += ".";
+
+      const UShort_t usElinkIdx = Mess.GetLinkIndexHitBinning();
+      FillStsMessTypePerElink(usElinkIdx, static_cast<uint16_t>(typeMess));
+      FillStsHitsElinkPerDpb(uCurrDpbIdx, usElinkIdx);
+      break;
+    }
+    case stsxyter::MessType::TsMsb: {
+      ++vNbMessType[1];
+      sMessPatt += " T ";
+      FillStsMessTypePerElink(uCurrDpbIdx * fNrElinksPerDpb, static_cast<uint16_t>(typeMess));
+      break;
+    }
+    case stsxyter::MessType::Epoch: {
+      ++vNbMessType[2];
+      sMessPatt += " E ";
+      FillStsMessTypePerElink(uCurrDpbIdx * fNrElinksPerDpb, static_cast<uint16_t>(typeMess));
+      break;
+    }
+    case stsxyter::MessType::Status: {
+      ++vNbMessType[3];
+      sMessPatt += " S ";
+      FillStsMessTypePerElink(Mess.GetLinkIndex(), static_cast<uint16_t>(typeMess));
+      break;
+    }
+    case stsxyter::MessType::Empty: {
+      ++vNbMessType[4];
+      sMessPatt += " Em";
+      FillStsMessTypePerElink(uCurrDpbIdx * fNrElinksPerDpb, static_cast<uint16_t>(typeMess));
+      break;
+    }
+    case stsxyter::MessType::EndOfMs: {
+      ++vNbMessType[5];
+      sMessPatt += " En";
+      bError = Mess.IsMsErrorFlagOn();
+      FillStsMessTypePerElink(uCurrDpbIdx * fNrElinksPerDpb, static_cast<uint16_t>(typeMess));
+      break;
+    }
+    case stsxyter::MessType::Dummy: {
+      ++vNbMessType[6];
+      sMessPatt += " D ";
+      FillStsMessTypePerElink(uCurrDpbIdx * fNrElinksPerDpb, static_cast<uint16_t>(typeMess));
+      break;
+    }
+    default: {
+    }
+  }
+  return bError;
+}
+
+
+// ---- Init ----
+Bool_t CbmStsUnpackMonitor::Init(CbmMcbm2018StsPar* parset)
+{
+  // Get Infos from the parset
+  fNrElinksPerDpb = parset->GetNbElinkPerDpb();
+
+  /// Trigger histo creation on all associated monitors
+  CreateHistograms(parset);
+  if (fDebugMode) CreateDebugHistograms(parset);
+
+  /// Obtain vector of pointers on each histo from the algo (+ optionally desired folder)
+  std::vector<std::pair<TNamed*, std::string>> vHistos = GetHistoVector();
+
+  /// Obtain vector of pointers on each canvas from the algo (+ optionally desired folder)
+  std::vector<std::pair<CbmQaCanvas*, std::string>> vCanvases = GetCanvasVector();
+
+  /// Register the histos and canvases in the HTTP server
+  THttpServer* server = FairRunOnline::Instance()->GetHttpServer();
+  if (nullptr != server) {
+    for (UInt_t uCanvas = 0; uCanvas < vCanvases.size(); ++uCanvas) {
+      server->Register(Form("/%s", vCanvases[uCanvas].second.data()), vCanvases[uCanvas].first);
+    }
+    for (UInt_t uHisto = 0; uHisto < vHistos.size(); ++uHisto) {
+      server->Register(Form("/%s", vHistos[uHisto].second.data()), vHistos[uHisto].first);
+    }
+    server->RegisterCommand("/Reset_UnpSts_Hist", "bMcbm2018UnpackerTaskStsResetHistos=kTRUE");
+    server->Restrict("/Reset_UnpSts_Hist", "allow=admin");
+  }
+
+  return kTRUE;
+}
+
+
+ClassImp(CbmStsUnpackMonitor)
diff --git a/reco/detectors/sts/unpack/CbmStsUnpackMonitor.h b/reco/detectors/sts/unpack/CbmStsUnpackMonitor.h
new file mode 100644
index 0000000000..41e99d6f0a
--- /dev/null
+++ b/reco/detectors/sts/unpack/CbmStsUnpackMonitor.h
@@ -0,0 +1,304 @@
+// -----------------------------------------------------------------------------
+// -----                                                                   -----
+// -----                  CbmStsUnpackMonitor                       -----
+// -----               Created 26.01.2019 by P.-A. Loizeau                 -----
+// -----                                                                   -----
+// -----------------------------------------------------------------------------
+
+#ifndef CbmStsUnpackMonitor_H
+#define CbmStsUnpackMonitor_H
+
+#include "CbmMcbm2018StsPar.h"
+
+#include "Rtypes.h"
+#include "TH1.h"
+#include "TH2.h"
+#include "TProfile.h"
+
+#include <cstdint>
+
+// FIXME
+// #include "StsXyterMessage.h"
+#include "raw/StsXyterMessage.h"
+
+/** @remark On pointers to histograms: Why not use shared pointers here, than you do not need to take care of deleting them. Right now I think you have a massive memory leak. PR */
+
+class CbmQaCanvas;
+
+class CbmStsUnpackMonitor {
+public:
+  CbmStsUnpackMonitor();
+
+  virtual ~CbmStsUnpackMonitor();
+
+  /** @brief Init all required parameter informations and histograms */
+  Bool_t Init(CbmMcbm2018StsPar* digiParSet);
+
+  Bool_t CreateHistograms(CbmMcbm2018StsPar* pUnpackPar);
+  Bool_t ResetHistograms();
+
+  Bool_t CreateDebugHistograms(CbmMcbm2018StsPar* pUnpackPar);
+  Bool_t ResetDebugHistograms();
+
+  Bool_t CreateMsComponentSizeHistos(UInt_t component);
+  Bool_t ResetMsComponentSizeHistos(UInt_t component);
+
+  void DrawCanvases();
+
+  void AddHistoToVector(TNamed* pointer, std::string sFolder = "")
+  {
+    fvpAllHistoPointers.push_back(std::pair<TNamed*, std::string>(pointer, sFolder));
+  }
+  std::vector<std::pair<TNamed*, std::string>> GetHistoVector() { return fvpAllHistoPointers; }
+
+  void AddCanvasToVector(CbmQaCanvas* pointer, std::string sFolder = "")
+  {
+    fvpAllCanvasPointers.push_back(std::pair<CbmQaCanvas*, std::string>(pointer, sFolder));
+  }
+  std::vector<std::pair<CbmQaCanvas*, std::string>> GetCanvasVector() { return fvpAllCanvasPointers; }
+
+  void SetLongDurationLimits(UInt_t uDurationSeconds, UInt_t uBinSize)
+  {
+    //fbLongHistoEnable     = kTRUE;
+    fuLongHistoNbSeconds  = uDurationSeconds;
+    fuLongHistoBinSizeSec = uBinSize;
+  }
+
+  UInt_t GetMaxNbFlibLinks() { return kiMaxNbFlibLinks; }
+
+  ///Fill general histograms
+  void FillVectorSize(ULong64_t TsIdx, UInt_t Size) { fhVectorSize->Fill(TsIdx, Size); }
+  void FillVectorCapacity(ULong64_t TsIdx, UInt_t Capacity) { fhVectorCapacity->Fill(TsIdx, Capacity); }
+  void FillMsCntEvo(ULong64_t MsIdx) { fhMsCntEvo->Fill(MsIdx * 1e-9); }
+  void FillMsErrorsEvo(ULong64_t MsIdx, uint16_t MsErrorType) { fhMsErrorsEvo->Fill(1e-9 * MsIdx, MsErrorType); }
+  void FillDigisTimeInRun(Double_t Time) { fhDigisTimeInRun->Fill(Time * 1e-9); }
+  void FillStsAllFebsHitRateEvo(Double_t dTimeSinceStartSec, UInt_t uFebIdx)
+  {
+    fhStsAllFebsHitRateEvo->Fill(dTimeSinceStartSec, uFebIdx);
+  }
+  void FillStsAllAsicsHitRateEvo(Double_t dTimeSinceStartSec, UInt_t uAsicIdx)
+  {
+    fhStsAllAsicsHitRateEvo->Fill(dTimeSinceStartSec, uAsicIdx);
+  }
+  void FillStsFebAsicHitCounts(UInt_t uFebIdx, UInt_t uAsicInFeb) { fhStsFebAsicHitCounts->Fill(uFebIdx, uAsicInFeb); }
+  void FillStsStatusMessType(UInt_t uAsicIdx, UShort_t usStatusField)
+  {
+    fhStsStatusMessType->Fill(uAsicIdx, usStatusField);
+  }
+
+  ///Fill general "per Feb" histogram vectors
+  void FillStsFebChanCntRaw(UInt_t uFebIdx, UInt_t uChanInFeb) { fvhStsFebChanCntRaw[uFebIdx]->Fill(uChanInFeb); }
+  void FillStsFebChanAdcRaw(UInt_t uFebIdx, UInt_t uChanInFeb, UShort_t usRawAdc)
+  {
+    fvhStsFebChanAdcRaw[uFebIdx]->Fill(uChanInFeb, usRawAdc);
+  }
+  void FillStsFebChanAdcRawProf(UInt_t uFebIdx, UInt_t uChanInFeb, UShort_t usRawAdc)
+  {
+    fvhStsFebChanAdcRawProf[uFebIdx]->Fill(uChanInFeb, usRawAdc);
+  }
+  void FillStsFebChanAdcCal(UInt_t uFebIdx, UInt_t uChanInFeb, Double_t dCalAdc)
+  {
+    fvhStsFebChanAdcCal[uFebIdx]->Fill(uChanInFeb, dCalAdc);
+  }
+  void FillStsFebChanAdcCalProf(UInt_t uFebIdx, UInt_t uChanInFeb, Double_t dCalAdc)
+  {
+    fvhStsFebChanAdcCalProf[uFebIdx]->Fill(uChanInFeb, dCalAdc);
+  }
+  void FillStsFebChanRawTs(UInt_t uFebIdx, UInt_t uChan, UShort_t usRawTs)
+  {
+    fvhStsFebChanRawTs[uFebIdx]->Fill(uChan, usRawTs);
+  }
+  void FillStsFebChanMissEvt(UInt_t uFebIdx, UInt_t uChan, bool missEvtFlag)
+  {
+    fvhStsFebChanMissEvt[uFebIdx]->Fill(uChan, missEvtFlag);
+  }
+  void FillStsFebChanMissEvtEvo(UInt_t uFebIdx, Double_t dTimeSinceStartSec, UInt_t uChanInFeb)
+  {
+    fvhStsFebChanMissEvtEvo[uFebIdx]->Fill(dTimeSinceStartSec, uChanInFeb);
+  }
+  void FillStsFebAsicMissEvtEvo(UInt_t uFebIdx, Double_t dTimeSinceStartSec, UInt_t uAsicInFeb)
+  {
+    fvhStsFebAsicMissEvtEvo[uFebIdx]->Fill(dTimeSinceStartSec, uAsicInFeb);
+  }
+  void FillStsFebMissEvtEvo(UInt_t uFebIdx, Double_t dTimeSinceStartSec)
+  {
+    fvhStsFebMissEvtEvo[uFebIdx]->Fill(dTimeSinceStartSec);
+  }
+  void FillStsFebChanHitRateEvo(UInt_t uFebIdx, Double_t dTimeSinceStartSec, UInt_t uChanInFeb)
+  {
+    fvhStsFebChanHitRateEvo[uFebIdx]->Fill(dTimeSinceStartSec, uChanInFeb);
+  }
+  void FillStsFebAsicHitRateEvo(UInt_t uFebIdx, Double_t dTimeSinceStartSec, UInt_t uAsicInFeb)
+  {
+    fvhStsFebAsicHitRateEvo[uFebIdx]->Fill(dTimeSinceStartSec, uAsicInFeb);
+  }
+  void FillStsFebHitRateEvo(UInt_t uFebIdx, Double_t dTimeSinceStartSec)
+  {
+    fvhStsFebHitRateEvo[uFebIdx]->Fill(dTimeSinceStartSec);
+  }
+  void FillStsFebChanHitRateEvoLong(UInt_t uFebIdx, Double_t dTimeSinceStartMin, UInt_t uChanInFeb)
+  {
+    fvhStsFebChanHitRateEvoLong[uFebIdx]->Fill(dTimeSinceStartMin, uChanInFeb, 1.0 / 60.0);
+  }
+  void FillStsFebAsicHitRateEvoLong(UInt_t uFebIdx, Double_t dTimeSinceStartMin, UInt_t uAsicInFeb)
+  {
+    fvhStsFebAsicHitRateEvoLong[uFebIdx]->Fill(dTimeSinceStartMin, uAsicInFeb, 1.0 / 60.0);
+  }
+  void FillStsFebHitRateEvoLong(UInt_t uFebIdx, Double_t dTimeSinceStartMin)
+  {
+    fvhStsFebHitRateEvoLong[uFebIdx]->Fill(dTimeSinceStartMin, 1.0 / 60.0);
+  }
+
+  ///Fill debugging histograms
+  void FillStsMessType(uint16_t typeMess) { fhStsMessType->Fill(typeMess); }
+  void FillStsMessTypePerDpb(UInt_t DpbIdx, uint16_t typeMess) { fhStsMessTypePerDpb->Fill(DpbIdx, typeMess); }
+  void FillStsMessTypePerElink(UInt_t ElinkIdx, uint16_t typeMess) { fhStsMessTypePerElink->Fill(ElinkIdx, typeMess); }
+  void FillStsHitsElinkPerDpb(UInt_t DpbIdx, UInt_t ElinkIdx) { fhStsHitsElinkPerDpb->Fill(DpbIdx, ElinkIdx); }
+  void FillStsDpbRawTsMsb(UInt_t uCurrDpbIdx, ULong_t ulCurrentTsMsb)
+  {
+    fhStsDpbRawTsMsb->Fill(uCurrDpbIdx, ulCurrentTsMsb);
+  }
+  void FillStsDpbRawTsMsbSx(UInt_t uCurrDpbIdx, ULong_t ulCurrentTsMsb)
+  {
+    fhStsDpbRawTsMsbSx->Fill(uCurrDpbIdx, (ulCurrentTsMsb & 0x1F));
+  }
+  void FillStsDpbRawTsMsbDpb(UInt_t uCurrDpbIdx, ULong_t ulCurrentTsMsb)
+  {
+    fhStsDpbRawTsMsbDpb->Fill(uCurrDpbIdx, (ulCurrentTsMsb >> 5));
+  }
+  void FillStsAsicTsMsb(ULong_t ulCurrentTsMsb, UInt_t uAsicIdx) { fhStsAsicTsMsb->Fill(ulCurrentTsMsb, uAsicIdx); }
+
+  ///Fill debugging "per Asic" histogram vectors
+  void FillStsChanCntRaw(UInt_t uAsicIdx, UShort_t usChan) { fvhStsChanCntRaw[uAsicIdx]->Fill(usChan); }
+  void FillStsChanAdcRaw(UInt_t uAsicIdx, UShort_t usChan, UShort_t usRawAdc)
+  {
+    fvhStsChanAdcRaw[uAsicIdx]->Fill(usChan, usRawAdc);
+  }
+  void FillStsChanAdcRawProf(UInt_t uAsicIdx, UShort_t usChan, UShort_t usRawAdc)
+  {
+    fvhStsChanAdcRawProf[uAsicIdx]->Fill(usChan, usRawAdc);
+  }
+  void FillStsChanRawTs(UInt_t uAsicIdx, UShort_t usChan, UShort_t usRawTs)
+  {
+    fvhStsChanRawTs[uAsicIdx]->Fill(usChan, usRawTs);
+  }
+  void FillStsChanMissEvt(UInt_t uAsicIdx, UShort_t usChan, bool missedEvtFlag)
+  {
+    fvhStsChanMissEvt[uAsicIdx]->Fill(usChan, missedEvtFlag);
+  }
+
+  //Fill Ms Component Size Histos
+  void FillMsSize(UInt_t uMsComp, UInt_t uSize) { fvhMsSize[uMsComp]->Fill(uSize); }
+  void FillMsSizeTime(UInt_t uMsComp, Double_t dTime, UInt_t uSize) { fvhMsSizeTime[uMsComp]->Fill(dTime, uSize); }
+
+  void FillHitMonitoringHistos(const UInt_t& uFebIdx, const UShort_t& usChan, const UInt_t& uChanInFeb,
+                               const UShort_t& usRawAdc, const Double_t& dCalAdc, const UShort_t& usRawTs,
+                               const bool& isHitMissedEvts);
+
+  void FillHitDebugMonitoringHistos(const UInt_t& uAsicIdx, const UShort_t& usChan, const UShort_t& usRawAdc,
+                                    const UShort_t& usRawTs, const bool& isHitMissedEvts);
+
+  void FillHitEvoMonitoringHistos(const UInt_t& uFebIdx, const UInt_t& uAsicIdx, const UInt_t& uAsicInFeb,
+                                  const UInt_t& uChanInFeb, const Double_t& dTimeSinceStartSec,
+                                  const bool& isHitMissedEvts);
+
+  /** @brief Activate the debug mode */
+  bool GetDebugMode() { return fDebugMode; }
+
+  bool ProcessDebugInfo(const stsxyter::Message& Mess, std::string& sMessPatt, std::vector<uint32_t>& vNbMessType,
+                        const UInt_t& uCurrDpbIdx);
+
+  /** @brief Activate the debug mode */
+  void SetDebugMode(bool value) { fDebugMode = value; }
+
+private:
+  /// Rate evolution histos
+  //Bool_t fbLongHistoEnable;
+  UInt_t fuLongHistoNbSeconds  = 3600;
+  UInt_t fuLongHistoBinSizeSec = 10;
+  UInt_t fuLongHistoBinNb;
+
+  /// Canvases
+  std::vector<CbmQaCanvas*> fvcStsSumm;
+  std::vector<CbmQaCanvas*> fvcStsSmxErr;
+
+  ///General histograms
+  TH1* fhDigisTimeInRun        = nullptr;
+  TH1* fhVectorSize            = nullptr;
+  TH1* fhVectorCapacity        = nullptr;
+  TH1* fhMsCntEvo              = nullptr;
+  TH2* fhMsErrorsEvo           = nullptr;
+  TH2* fhStsAllFebsHitRateEvo  = nullptr;
+  TH2* fhStsAllAsicsHitRateEvo = nullptr;
+  TH2* fhStsFebAsicHitCounts   = nullptr;
+  TH2* fhStsStatusMessType     = nullptr;
+
+  ///General "per Feb" histogram vectors
+  std::vector<TH1*> fvhStsFebChanCntRaw;
+  std::vector<TH2*> fvhStsFebChanAdcRaw;
+  std::vector<TProfile*> fvhStsFebChanAdcRawProf;
+  std::vector<TH2*> fvhStsFebChanAdcCal;
+  std::vector<TProfile*> fvhStsFebChanAdcCalProf;
+  std::vector<TH2*> fvhStsFebChanRawTs;
+  std::vector<TH2*> fvhStsFebChanMissEvt;
+  std::vector<TH2*> fvhStsFebChanMissEvtEvo;
+  std::vector<TH2*> fvhStsFebAsicMissEvtEvo;
+  std::vector<TH1*> fvhStsFebMissEvtEvo;
+  std::vector<TH2*> fvhStsFebChanHitRateEvo;
+  std::vector<TH2*> fvhStsFebAsicHitRateEvo;
+  std::vector<TH1*> fvhStsFebHitRateEvo;
+  std::vector<TH2*> fvhStsFebChanHitRateEvoLong;
+  std::vector<TH2*> fvhStsFebAsicHitRateEvoLong;
+  std::vector<TH1*> fvhStsFebHitRateEvoLong;
+
+  /** @brief Flag if debug mode is active or not */
+  bool fDebugMode = false;
+
+  ///Debugging histograms
+  TH1* fhStsMessType         = nullptr;
+  TH2* fhStsMessTypePerDpb   = nullptr;
+  TH2* fhStsMessTypePerElink = nullptr;
+  TH2* fhStsHitsElinkPerDpb  = nullptr;
+  TH2* fhStsDpbRawTsMsb      = nullptr;
+  TH2* fhStsDpbRawTsMsbSx    = nullptr;
+  TH2* fhStsDpbRawTsMsbDpb   = nullptr;
+  TH2* fhStsAsicTsMsb        = nullptr;
+
+  static const UInt_t kiMaxNbFlibLinks = 32;
+  TH1* fvhMsSize[kiMaxNbFlibLinks];
+  TProfile* fvhMsSizeTime[kiMaxNbFlibLinks];
+
+  ///Debugging "per Asic" histogram vectors
+  std::vector<TH1*> fvhStsChanCntRaw;
+  std::vector<TH2*> fvhStsChanAdcRaw;
+  std::vector<TProfile*> fvhStsChanAdcRawProf;
+  std::vector<TH2*> fvhStsChanRawTs;
+  std::vector<TH2*> fvhStsChanMissEvt;
+
+  /** @brief Number of elinks per dpb, extracted from the parset */
+  uint32_t fNrElinksPerDpb = 0;
+
+
+  /// For monitoring of internal processes.
+  /// => Pointers should be filled with TH1*, TH2*, TProfile*, ...
+  /// ==> To check if object N is of type T, use "T ObjectPointer = dynamic_cast<T>( fvpAllHistoPointers[N].first );" and check for nullptr
+  /// ==> To get back the original class name use "fvpAllHistoPointers[N].first->ClassName()" which returns a const char * (e.g. "TH1I")
+  /// ===> Usage example with feeding a THttpServer:
+  /// ===> #include "TH2.h"
+  /// ===> std::string sClassName = vHistos[ uHisto ].first.ClassName();
+  /// ===> if( !strncmp( sClassName, "TH1", 3 ) )
+  /// ===>    server->Register( vHistos[ uHisto ].second.data(), dynamic_cast< TH1 * >(vHistos[ uHisto ].first) );
+  /// ===> else if( !strncmp( sClassName, "TH2", 3 ) )
+  /// ===>    server->Register( vHistos[ uHisto ].second.data(), dynamic_cast< TH2 * >(vHistos[ uHisto ].first) );
+  std::vector<std::pair<TNamed*, std::string>>
+    fvpAllHistoPointers;  //! Vector of pointers to histograms + optional folder name
+  std::vector<std::pair<CbmQaCanvas*, std::string>>
+    fvpAllCanvasPointers;  //! Vector of pointers to canvases + optional folder name
+
+  CbmStsUnpackMonitor(const CbmStsUnpackMonitor&);
+  CbmStsUnpackMonitor operator=(const CbmStsUnpackMonitor&);
+
+  ClassDef(CbmStsUnpackMonitor, 1)
+};
+
+#endif
diff --git a/reco/detectors/trd/unpack/CbmTrdUnpackAlgoBaseR.cxx b/reco/detectors/trd/unpack/CbmTrdUnpackAlgoBaseR.cxx
index 757262548d..26a4e8f4bf 100644
--- a/reco/detectors/trd/unpack/CbmTrdUnpackAlgoBaseR.cxx
+++ b/reco/detectors/trd/unpack/CbmTrdUnpackAlgoBaseR.cxx
@@ -24,8 +24,6 @@ CbmTrdUnpackAlgoBaseR::~CbmTrdUnpackAlgoBaseR() {}
 // ---- digestOutput ----
 void CbmTrdUnpackAlgoBaseR::digestOutput(std::unique_ptr<CbmTrdDigi> digi, CbmTrdRawMessageSpadic raw)
 {
-  ++fNrCreatedDigis;
-
   // If requested lets monitor something
   if (fMonitor) { fMonitor->FillHistos(digi.get(), &raw); }
 
@@ -134,7 +132,7 @@ Bool_t CbmTrdUnpackAlgoBaseR::initParSet(CbmMcbm2020TrdTshiftPar* parset)
 }
 
 // ---- initR ----
-Bool_t CbmTrdUnpackAlgoBaseR::initR()
+Bool_t CbmTrdUnpackAlgoBaseR::init()
 {
   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.
diff --git a/reco/detectors/trd/unpack/CbmTrdUnpackAlgoBaseR.h b/reco/detectors/trd/unpack/CbmTrdUnpackAlgoBaseR.h
index 363bd87079..152948e9ae 100644
--- a/reco/detectors/trd/unpack/CbmTrdUnpackAlgoBaseR.h
+++ b/reco/detectors/trd/unpack/CbmTrdUnpackAlgoBaseR.h
@@ -119,14 +119,7 @@ protected:
    * 
    * @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();
+  virtual Bool_t init();
 
   // Initialise par set, the base function handles the casting to distribute the pointers to their explicit functions
 
diff --git a/reco/steer/CMakeLists.txt b/reco/steer/CMakeLists.txt
index 7b73424842..236f0c4d2e 100644
--- a/reco/steer/CMakeLists.txt
+++ b/reco/steer/CMakeLists.txt
@@ -24,6 +24,7 @@ ${CBMROOT_SOURCE_DIR}/reco/base
 ${CBMROOT_SOURCE_DIR}/reco/detectors/psd
 ${CBMROOT_SOURCE_DIR}/reco/detectors/psd/unpack
 ${CBMROOT_SOURCE_DIR}/reco/detectors/rich/unpack
+${CBMROOT_SOURCE_DIR}/reco/detectors/sts/
 ${CBMROOT_SOURCE_DIR}/reco/detectors/sts/unpack
 ${CBMROOT_SOURCE_DIR}/reco/detectors/tof/unpack
 ${CBMROOT_SOURCE_DIR}/reco/detectors/trd
@@ -32,6 +33,7 @@ ${CBMROOT_SOURCE_DIR}/reco/detectors/trd/unpack
 
 ${CBMROOT_SOURCE_DIR}/core/base
 ${CBMROOT_SOURCE_DIR}/core/data
+${CBMROOT_SOURCE_DIR}/core/qa
 ${CBMROOT_SOURCE_DIR}/core/data/raw
 ${CBMROOT_SOURCE_DIR}/core/data/base
 ${CBMROOT_SOURCE_DIR}/core/data/psd
diff --git a/reco/steer/CbmRecoUnpack.cxx b/reco/steer/CbmRecoUnpack.cxx
index 6e3aacf795..162ee19954 100644
--- a/reco/steer/CbmRecoUnpack.cxx
+++ b/reco/steer/CbmRecoUnpack.cxx
@@ -135,6 +135,12 @@ void CbmRecoUnpack::Unpack(unique_ptr<Timeslice> ts)
             unpack(&timeslice, component, fRichConfig, fRichConfig->GetOptOutAVec(), fRichConfig->GetOptOutBVec()));
         break;
       }
+      case fkFlesSts: {
+        if (fStsConfig)
+          fCbmTsEventHeader->SetNDigisSts(
+            unpack(&timeslice, component, fStsConfig, fStsConfig->GetOptOutAVec(), fStsConfig->GetOptOutBVec()));
+        break;
+      }
       case fkFlesTrd: {
         if (fTrdConfig)
           fCbmTsEventHeader->SetNDigisTrd(
-- 
GitLab