From 68af28cffdbcb34fb1961ff235af6799acff5475 Mon Sep 17 00:00:00 2001
From: drslebedev <dr.s.lebedev@gmail.com>
Date: Wed, 30 Jun 2021 12:17:11 +0200
Subject: [PATCH] New RICH unpacker for the new data format for 2021 beamtime. 
 * The old unpacker is still in the repository CbmMcbm2018UnpackerAlgoRich2020
  * The old unpacker are still needed to analyze old data, later it will be
 removed  * Clang-format  * Update macro

---
 fles/mcbm2018/CMakeLists.txt                  |    4 +-
 fles/mcbm2018/CbmFlibMcbm2018LinkDef.h        |    2 +
 .../unpacker/CbmMcbm2018UnpackerAlgoRich.cxx  | 1849 +++-------------
 .../unpacker/CbmMcbm2018UnpackerAlgoRich.h    |  404 ++--
 .../CbmMcbm2018UnpackerAlgoRich2020.cxx       | 1882 +++++++++++++++++
 .../CbmMcbm2018UnpackerAlgoRich2020.h         |  384 ++++
 .../CbmMcbm2018UnpackerTaskRich2020.cxx       |  258 +++
 .../CbmMcbm2018UnpackerTaskRich2020.h         |   75 +
 .../unpacker/CbmMcbm2018UnpackerUtilRich.hpp  |  125 --
 ...xx => CbmMcbm2018UnpackerUtilRich2020.cxx} |    2 +-
 ...ch.h => CbmMcbm2018UnpackerUtilRich2020.h} |    6 +-
 macro/beamtime/mcbm2018/MonitorRich.C         |   12 +-
 macro/beamtime/mcbm2019/MonitorRich.C         |    2 +-
 macro/beamtime/mcbm2019/unpack_tsa_mcbm.C     |   10 +-
 macro/beamtime/mcbm2020/MonitorRich.C         |   10 +-
 macro/beamtime/mcbm2020/unpack_tsa_mcbm.C     |   12 +-
 macro/beamtime/mcbm2021/MonitorRich.C         |  105 +
 17 files changed, 3264 insertions(+), 1878 deletions(-)
 create mode 100644 fles/mcbm2018/unpacker/CbmMcbm2018UnpackerAlgoRich2020.cxx
 create mode 100644 fles/mcbm2018/unpacker/CbmMcbm2018UnpackerAlgoRich2020.h
 create mode 100644 fles/mcbm2018/unpacker/CbmMcbm2018UnpackerTaskRich2020.cxx
 create mode 100644 fles/mcbm2018/unpacker/CbmMcbm2018UnpackerTaskRich2020.h
 delete mode 100644 fles/mcbm2018/unpacker/CbmMcbm2018UnpackerUtilRich.hpp
 rename fles/mcbm2018/unpacker/{CbmMcbm2018UnpackerUtilRich.cxx => CbmMcbm2018UnpackerUtilRich2020.cxx} (98%)
 rename fles/mcbm2018/unpacker/{CbmMcbm2018UnpackerUtilRich.h => CbmMcbm2018UnpackerUtilRich2020.h} (87%)
 create mode 100644 macro/beamtime/mcbm2021/MonitorRich.C

diff --git a/fles/mcbm2018/CMakeLists.txt b/fles/mcbm2018/CMakeLists.txt
index ae02f061b7..0a1d9a4273 100644
--- a/fles/mcbm2018/CMakeLists.txt
+++ b/fles/mcbm2018/CMakeLists.txt
@@ -73,7 +73,9 @@ Set(SRCS
    unpacker/CbmMcbm2018UnpackerTaskTof.cxx
    unpacker/CbmMcbm2018UnpackerAlgoRich.cxx
    unpacker/CbmMcbm2018UnpackerTaskRich.cxx
-   unpacker/CbmMcbm2018UnpackerUtilRich.cxx
+   unpacker/CbmMcbm2018UnpackerTaskRich2020.cxx
+   unpacker/CbmMcbm2018UnpackerAlgoRich2020.cxx
+   unpacker/CbmMcbm2018UnpackerUtilRich2020.cxx
    unpacker/CbmMcbm2018UnpackerAlgoPsd.cxx
    unpacker/CbmMcbm2018UnpackerTaskPsd.cxx
    unpacker/CbmMcbm2018UnpackerAlgoTrdR.cxx
diff --git a/fles/mcbm2018/CbmFlibMcbm2018LinkDef.h b/fles/mcbm2018/CbmFlibMcbm2018LinkDef.h
index ea95ccbbca..674783848c 100644
--- a/fles/mcbm2018/CbmFlibMcbm2018LinkDef.h
+++ b/fles/mcbm2018/CbmFlibMcbm2018LinkDef.h
@@ -22,7 +22,9 @@
 #pragma link C++ class CbmMcbm2018UnpackerAlgoTof;
 #pragma link C++ class CbmMcbm2018UnpackerTaskTof + ;
 #pragma link C++ class CbmMcbm2018UnpackerAlgoRich;
+#pragma link C++ class CbmMcbm2018UnpackerAlgoRich2020;
 #pragma link C++ class CbmMcbm2018UnpackerTaskRich + ;
+#pragma link C++ class CbmMcbm2018UnpackerTaskRich2020 + ;
 #pragma link C++ class CbmMcbm2018UnpackerAlgoPsd;
 #pragma link C++ class CbmMcbm2018UnpackerTaskPsd + ;
 #pragma link C++ class CbmMcbm2018UnpackerAlgoTrdR + ;
diff --git a/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerAlgoRich.cxx b/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerAlgoRich.cxx
index 03c83ca0e5..a6daab289c 100644
--- a/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerAlgoRich.cxx
+++ b/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerAlgoRich.cxx
@@ -5,62 +5,10 @@
 /**
  * CbmMcbm2018UnpackerAlgoRich
  * E. Ovcharenko, Mar 2019
+ * S. Lebedev, June 2021
  * based on other detectors' classes by P.-A. Loizeau
  */
 
-/**
-
-Consider two consequent microslices A and B.
-Each microslice contains one CTS subevent which contains at least 3 timestamp messages.
-A microslice may also contain TRB subevents, each containing also one timestamp message from ch0.
-
-Microslice A
-===================
-CTS   | ch 0 re      = AC0R (stands for ms A, Cts, ch 0, Rising edge)
-CTS   | ch 2 fe
-CTS   | ch 2 re      = AC2R (stands for ms A, Cts, ch 2, Rising edge)
--------------------
-TDC K | ch 0 re
--------------------
-TDC Z | ch 0 re      = AZ0R (stands for ms A, tdc Z, ch 0, Rising edge))
-      | ch X re
-      | ch X fe
-      | ...
--------------------
-...
-===================
-
-Microslice B (next after A)
-===================
-CTS   | ch 0 re
-CTS   | ch 2 fe
-CTS   | ch 2 re
--------------------
-TDC L | ch 0 re
--------------------
-TDC Z | ch 0 re
-      | ch Y re      = (T - AZ0R) + corr
-      | ch Y fe
-      | ...
--------------------
-...
-===================
-
-corr = -(AC2R-AC0R)
-
-Uncorrected full time in ns of the TIMESTAMP message calculated as
-T = epoch*2048.*5. + (coarse)*5. - fine*0.005
-
-Full corrected global time is then computed by adding the microslice
-index from the descriptor to the corrected time:
-
-fullTimeCorr = (T - AZ0R) - (AC2R-AC0R) + MSidx
-
-*/
-
-//TODO: Check that to 'real' actions are performed in the lines which are intended for debug output only.
-//      i.e. LOG(XXXX) << ...
-
 #include "CbmMcbm2018UnpackerAlgoRich.h"
 
 // ROOT
@@ -74,42 +22,7 @@ fullTimeCorr = (T - AZ0R) - (AC2R-AC0R) + MSidx
 
 #include <iostream>
 
-CbmMcbm2018UnpackerAlgoRich::CbmMcbm2018UnpackerAlgoRich()
-  : CbmStar2019Algo()
-  , fbMonitorMode(kFALSE)
-  , fbDebugMonitorMode(kFALSE)
-  , fRawDataMode(kFALSE)
-  , fError(kFALSE)
-  , fTrbState(TrbNetState::IDLE)
-  , fErrorCorr(0)
-  , fbDoToTCorr(kTRUE)
-  , fSkipMs(kFALSE)
-  , fdTimeOffsetNs(0.0)
-  , fRICHcompIdx(6)
-  ,  //TODO experimentally obtained value
-  fUnpackPar(nullptr)
-  , fTScounter(0)
-  , fCurMSid(0)
-  , fGwordCnt(0)
-  , fInSubSubEvent(kFALSE)
-  , fCurEpochCounter(0)
-  , fSubSubEvId(0)
-  , fLastCTSch0_re_time(0.)
-  , fLastCTSch2_re_time(0.)
-  , fLastCTSch2_fe_time(0.)
-  , fPrevLastCTSch0_re_time(0.)
-  , fPrevLastCTSch2_re_time(0.)
-  , fPrevLastCTSch2_fe_time(0.)
-  , /*,
-	fhTDCch0re_minusCTSch0re(nullptr),
-	fhTDCch0re_minusCTSch2re(nullptr),
-	fhTDCch0re_minusCTSch2fe(nullptr),
-	fhTDCch0re_minusPrevCTSch0re(nullptr),
-	fhTDCch0re_minusPrevCTSch2re(nullptr),
-	fhTDCch0re_minusPrevCTSch2fe(nullptr)*/
-  fMapFEE()
-  , fhTotMap()
-  , fhTot2dMap()
+CbmMcbm2018UnpackerAlgoRich::CbmMcbm2018UnpackerAlgoRich() : CbmStar2019Algo()
 {
   this->Init();  //TODO why this is not called by the framework?
 }
@@ -120,13 +33,7 @@ CbmMcbm2018UnpackerAlgoRich::~CbmMcbm2018UnpackerAlgoRich()
   if (nullptr != fUnpackPar) delete fUnpackPar;
 }
 
-Bool_t CbmMcbm2018UnpackerAlgoRich::Init()
-{
-  LOG(info) << "Initializing mCBM RICH 2019 unpacker algo";
-  //fhDigisInChnl   = new TH2D("fhDigisInChnl","fhDigisInChnl;channel;#Digis;" ,2304 , -0.5, 2303.5,  50, -0.5, 49.5);
-  //fhDigisInDiRICH = new TH2D("fhDigisInDiRICH","fhDigisInDiRICH;DiRICH;#Digis;",72 , -0.5, 71.5,  300, -0.5, 299.5);
-  return kTRUE;
-}
+Bool_t CbmMcbm2018UnpackerAlgoRich::Init() { return kTRUE; }
 
 void CbmMcbm2018UnpackerAlgoRich::Reset() {}
 
@@ -167,16 +74,8 @@ Bool_t CbmMcbm2018UnpackerAlgoRich::InitParameters()
   return kTRUE;
 }
 
-void CbmMcbm2018UnpackerAlgoRich::InitStorage()
-{
-  fLastCh0_re_time.Set(fUnpackPar->GetNaddresses());      // Set the size of the array
-  fPrevLastCh0_re_time.Set(fUnpackPar->GetNaddresses());  // Set the size of the array
-}
+void CbmMcbm2018UnpackerAlgoRich::InitStorage() {}
 
-/**
-  Copied from other detectors without any brain effort...
-  A little bug-fix added
-**/
 void CbmMcbm2018UnpackerAlgoRich::AddMsComponentToList(size_t component, UShort_t usDetectorId)
 {
   /// Check for duplicates and ignore if it is the case
@@ -189,8 +88,7 @@ void CbmMcbm2018UnpackerAlgoRich::AddMsComponentToList(size_t component, UShort_
 
   if (fvMsComponentsList.size() == 1) { fRICHcompIdx = component; }
   else {
-    LOG(WARN) << "fvMsComponentsList.size() > 1 for RICH. Unpacking may not "
-                 "work due to implementation limitations.";
+    LOG(WARN) << "fvMsComponentsList.size() > 1 for RICH. Unpacking may not work due to implementation limitations.";
   }
 
   LOG(info) << "CbmMcbm2018UnpackerAlgoRich::AddMsComponentToList => Component " << component << " with detector ID 0x"
@@ -199,41 +97,30 @@ void CbmMcbm2018UnpackerAlgoRich::AddMsComponentToList(size_t component, UShort_
 
 Bool_t CbmMcbm2018UnpackerAlgoRich::ProcessTs(const fles::Timeslice& /*ts*/)
 {
-  LOG(debug2) << "CbmMcbm2018UnpackerAlgoRich::ProcessTs(ts)";
-  /*
-	//TODO: shortcut. We love shortcuts, right?
-	if (fvMsComponentsList.size() == 1) {
-		this->ProcessTs(ts, fvMsComponentsList.at(0));
-	}
-
-	//TODO: implement case when multiple components have to be processed
-*/
+  LOG(debug2) << "CbmMcbm2018UnpackerAlgoRich::ProcessTs(ts): this method do not have implementation.";
   return kTRUE;
 }
 
 Bool_t CbmMcbm2018UnpackerAlgoRich::ProcessTs(const fles::Timeslice& ts, size_t component)
 {
   /// Ignore First TS as first MS is typically corrupt
-  if (0 == ts.index()) { return kTRUE; }  // if( 0 == ts.index() )
+  if (0 == ts.index()) { return kTRUE; }
 
   LOG(debug2) << "CbmMcbm2018UnpackerAlgoRich::ProcessTs(ts, " << component << ")";
 
-  //TODO: skip if this method was called for a wrong component
-  //if (component != fRICHcompIdx) return kTRUE;
-  //FIXME: this is really nasty...
-  //	component = fRICHcompIdx;
   if (1 != fvMsComponentsList.size()) {
     /// If no RICH component, do nothing!
     if (0 == fvMsComponentsList.size()) return kTRUE;
 
     /// If multiple RICH components, fail the run
     TString sCompList = "";
-    for (UInt_t uMsCompIdx = 0; uMsCompIdx < fvMsComponentsList.size(); ++uMsCompIdx)
+    for (UInt_t uMsCompIdx = 0; uMsCompIdx < fvMsComponentsList.size(); ++uMsCompIdx) {
       sCompList += Form(" %2lu ", fvMsComponentsList[uMsCompIdx]);
-    LOG(fatal) << "CbmMcbm2018UnpackerAlgoRich::ProcessTs => More than 1 "
-                  "component in list, unpacking impossible! List is "
-               << sCompList;
-  }  // if( 1 != fvMsComponentsList.size() )
+    }
+    LOG(fatal)
+      << "CbmMcbm2018UnpackerAlgoRich::ProcessTs => More than 1 component in list, unpacking impossible! List is "
+      << sCompList;
+  }
   component = fvMsComponentsList[0];
 
   LOG(debug) << "Components:  " << ts.num_components();
@@ -259,1133 +146,359 @@ Bool_t CbmMcbm2018UnpackerAlgoRich::ProcessTs(const fles::Timeslice& ts, size_t
   }  // if( -1.0 == fdTsCoreSizeInNs )
 
   for (size_t iMS = 0; iMS < fuNbMsLoop; ++iMS) {
-    fCurMSid = iMS;
-    LOG(debug) << "=======================================================";
+    fMsInd = iMS;
+    if (IsLog()) LOG(debug) << "=======================================================";
     const fles::MicrosliceView mv            = ts.get_microslice(component, iMS);
     const fles::MicrosliceDescriptor& msDesc = mv.desc();
-    ////const uint8_t* msContent = mv.content();
-    LOG(debug) << "msDesc.size=" << msDesc.size;
+
     fCurMSidx = msDesc.idx;
-    LOG(debug) << "msDesc.idx=" << msDesc.idx;
-    ////mRichSupport::PrintRaw(msDesc.size, msContent);//TODO delete
-    ////LOG(debug) << "=======================================================";
-    ////////////////////////////////
-    //ProcessMs(ts, component, iMS);//
-    ////////////////////////////////
+    if (IsLog()) LOG(debug) << "msDesc.size=" << msDesc.size << " msDesc.idx=" << msDesc.idx;
 
     if (!fRawDataMode) ProcessMs(ts, component, iMS);
     if (fRawDataMode) DebugMs(ts, component, iMS);
-
-    LOG(debug) << "=======================================================";
   }
 
-  ///////////////
-  FinalizeTs();  //
-  ///////////////
+  FinalizeTs();
 
-  if (0 == fTScounter % 1000) { LOG(info) << "Processed " << fTScounter << " TS"; }
+  if (0 == fTsCounter % 1000) { LOG(info) << "Processed " << fTsCounter << " TS"; }
 
-  fTScounter++;
+  fTsCounter++;
 
   /// Sort the buffers of hits due to the time offsets applied
   std::sort(fDigiVect.begin(), fDigiVect.end(),
             [](const CbmRichDigi& a, const CbmRichDigi& b) -> bool { return a.GetTime() < b.GetTime(); });
 
-  if (fbMonitorMode || fbDebugMonitorMode) {
-    fhVectorSize->Fill(ts.index(), fDigiVect.size());
-    fhVectorCapacity->Fill(ts.index(), fDigiVect.capacity());
-  }  // if( fbMonitorMode || fbDebugMonitorMode )
+  return kTRUE;
+}
 
-  if (fuTsMaxVectorSize < fDigiVect.size()) {
-    fuTsMaxVectorSize = fDigiVect.size() * fdCapacityIncFactor;
-    fDigiVect.shrink_to_fit();
-    fDigiVect.reserve(fuTsMaxVectorSize);
-  }  // if( fuTsMaxVectorSize < fDigiVect.size() )
 
-  return kTRUE;
+std::string CbmMcbm2018UnpackerAlgoRich::GetLogHeader(CbmMcbm2018RichMicrosliceReader& reader)
+{
+  std::stringstream stream;
+  stream << "[" << fTsCounter << "-" << fMsInd << "-" << reader.GetWordCounter() << "/" << reader.GetSize() / 4 << " "
+         << reader.GetWordAsHexString(reader.GetCurWord()) << "] ";
+  return stream.str();
+}
+
+bool CbmMcbm2018UnpackerAlgoRich::IsLog()
+{
+  //if (fTsCounter == 25215) return true;
+  return false;
 }
 
 Bool_t CbmMcbm2018UnpackerAlgoRich::ProcessMs(const fles::Timeslice& ts, size_t uMsCompIdx, size_t uMsIdx)
 {
   const fles::MicrosliceView mv            = ts.get_microslice(uMsCompIdx, uMsIdx);
   const fles::MicrosliceDescriptor& msDesc = mv.desc();
-  const uint8_t* ptr                       = mv.content();
-  const size_t size                        = msDesc.size;
-
-  if (size == 0) return kTRUE;
 
-  fGwordCnt = 0;  //TODO check that this global word counter works properly
-
-  Int_t offset;  // offset in bytes
-  Int_t* dataPtr;
-
-  offset = 0;
-  mRichSupport::SwapBytes(4, ptr + offset);
-  LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
-              << "ok"
-              << "\t"
-              << "Reserved 0000 0000";
-  fGwordCnt++;
-
-  offset = 4;
-  mRichSupport::SwapBytes(4, ptr + offset);
-  dataPtr               = (Int_t*) (ptr + offset);
-  Int_t mbsNumber       = (Int_t)(dataPtr[0] & 0xffffff);
-  uint8_t mts_error_msg = (uint8_t)((dataPtr[0] >> 24) & 0xff);
-  ErrorMsg(static_cast<uint16_t>(mts_error_msg), RichErrorType::mtsError);
-  LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
-              << "ok"
-              << "\t"
-              << "mbsNumber = " << mbsNumber;
-  fGwordCnt++;
-
-  // We suppose that the first word is
-  // "HadesTransportUnitQueue - Length"
-  offset = 0 + 8;
-  mRichSupport::SwapBytes(4, ptr + offset);
-  dataPtr             = (Int_t*) (ptr + offset);
-  Int_t TRBeventSize1 = (Int_t)(dataPtr[0]);
-  LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
-              << "ok"
-              << "\t"
-              << "HadesTransportUnitQueue - Length = " << TRBeventSize1;
-  fGwordCnt++;
-
-  if (*dataPtr > 0 && UInt_t(*dataPtr) == 0x80030000) {
-    LOG(info) << "dataPtr == 0x80030000";
-    exit(EXIT_FAILURE);
-  }
+  CbmMcbm2018RichMicrosliceReader reader;
+  reader.SetData(mv.content(), msDesc.size);
 
-  // We suppose that the second word is
-  // "HadesTransportUnitQueue - Decoder  (Seems to be allways the same)"
-  offset = 4 + 8;
-  mRichSupport::SwapBytes(4, ptr + offset);
-  dataPtr    = (Int_t*) (ptr + offset);
-  Int_t dcdr = (Int_t)(dataPtr[0]);
-  if (dcdr == 0x00030062) {
-    LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
-                << "ok"
-                << "\t"
-                << "HadesTransportUnitQueue - Decoder = " << dcdr;
-    fGwordCnt++;
-  }
-  else {
-    LOG(warning) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
-                 << "er"
-                 << "\t"
-                 << "HadesTransportUnitQueue - Decoder = " << dcdr << " is not 0x00030062 (196706) => 0x" << std::hex
-                 << dcdr << std::dec;
-    fGwordCnt++;
-
-    /// Probably corrupted MS, stop there and skip remaining data
-    fSkipMs = kTRUE;
-    return kFALSE;
-  }
+  // There are a lot of MS  with 8 bytes size
+  // Does one need these MS?
+  if (reader.GetSize() <= 8) return true;
 
-  // We suppose that the third word is
-  // TRB event length (in bytes)
-  // It should be 8 less than the size specified two words ago
-  offset = 8 + 8;
-  mRichSupport::SwapBytes(4, ptr + offset);
-  dataPtr             = (Int_t*) (ptr + offset);
-  Int_t TRBeventSize2 = (Int_t)(dataPtr[0]);
-  if (TRBeventSize2 == TRBeventSize1 - 8) {
-    LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
-                << "ok"
-                << "\t"
-                << "TRB event - Length = " << TRBeventSize2 << " == " << TRBeventSize1 << "-8";
-    fGwordCnt++;
-  }
-  else {
-    LOG(debug) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
-               << "er"
-               << "\t"
-               << "TRB event - Length = " << TRBeventSize2 << " != " << TRBeventSize1 << "-8=" << TRBeventSize1 - 8;
-    fGwordCnt++;
+  while (true) {
+    ProcessTrbPacket(reader);
+    // -4*2 for 2 last words which contain microslice index
+    if (reader.GetOffset() >= reader.GetSize() - 8) break;
+    // -4*3 for 0xffffffff padding and 2 last words which contain microslice index
+    if (reader.IsNextPadding() && reader.GetOffset() >= reader.GetSize() - 12) break;
   }
 
-  /////////////////////////////////////////////
-  ProcessTRBevent(TRBeventSize2, ptr + offset);  //
-  /////////////////////////////////////////////
-
-  // Bytes in a TrbEvent
-  if (fbDebugMonitorMode) fhEventSize->Fill(TRBeventSize2);
-
-
-  if (fSkipMs == kTRUE) {
-    // problem in data; delete vectors.
-    fDigiVect.clear();
-    fSkipMs = kFALSE;
-  }
+  uint32_t msIndexWord1 = reader.NextWord();
+  if (IsLog()) LOG(DEBUG4) << GetLogHeader(reader) << "Microslice Index 1:" << reader.GetWordAsHexString(msIndexWord1);
 
+  uint32_t msIndexWord2 = reader.NextWord();
+  if (IsLog()) LOG(DEBUG4) << GetLogHeader(reader) << "Microslice Index 2:" << reader.GetWordAsHexString(msIndexWord2);
 
   return kTRUE;
 }
 
-Int_t CbmMcbm2018UnpackerAlgoRich::ProcessTRBevent(size_t const size, uint8_t const* const ptr)
-{
-  Int_t offset;  // offset in bytes
-  Int_t* dataPtr;
-
-  // We assume that the TRB event header is 4 words and
-  // the first word is already processed outside of this method
-
-  //////////////////////////////////
-  ProcessTRBeventHeader(4 * 4, ptr);  //
-  //////////////////////////////////
-
-  offset = 16;  // start from after the TRB event header
-
-  // 1. Search for the CTS subevent and extract reference time
-
-  while (static_cast<size_t>(offset) < size) {
-    /// Escape bad MS before doing anything
-    if (fSkipMs == kTRUE) break;
-
-    // Duplicate the header word in order to avoid corrupting (by bytes swapping)
-    // the original data
-    dataPtr          = (Int_t*) (ptr + offset);
-    Int_t headerCopy = *dataPtr;
-    mRichSupport::SwapBytes(4, (uint8_t*) &headerCopy);
-    dataPtr = &headerCopy;
-
-    Int_t SubEvSize = (Int_t)((dataPtr[0] >> 16) & 0xffff);
-    Int_t HubId     = (Int_t)((dataPtr[0]) & 0xffff);
-
-    // Process only CTS subevent
-    //FIXME change from 0xc001 to 0xc000 at some point // ?
-    if ((HubId == 0xc001) || (HubId == 0xc000)) {
-
-      // Not a very nice shortcut
-      // The global counter of the words is incremented for the CTS subevent header here
-      // However for the TRB subevent headers it is incremented in the second run,
-      // where only TRB subevent headers are processed and the CTS subevents are skipped
-      LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr((uint8_t*) &headerCopy) << "\t"
-                  << "ok"
-                  << "\t"
-                  << "hub ID = 0x" << mRichSupport::GetHexRepresentation(2, (uint8_t*) &headerCopy) << "\t"
-                  << "subevent size = " << SubEvSize;
-      fGwordCnt++;
-
-      fSubSubEvId = HubId;
-      //////////////////////////////////////////////////////////////
-      offset += (4 + ProcessCTSsubevent(SubEvSize * 4, ptr + offset));  //
-      //////////////////////////////////////////////////////////////
-
-      //std::cout<<"Words in CTS 0x"<< std::hex << HubId << std::dec <<" : "<< SubEvSize <<std::endl;
-      // In principle, should be reset here for safety
-      fSubSubEvId = 0;
-    }
-    else {
-      // Skip all other subevents
-      offset += (4 + SubEvSize * 4);
-    }
-  }
-
-  offset = 16;  // start from after the TRB event header again
-
-  // 2. Process TRB subsubevents
-
-  //Int_t iIter = 0;
-  while (static_cast<size_t>(offset) < size) {
-    /// Escape bad MS before doing anything
-    if (fSkipMs == kTRUE) break;
-
-    //std::cout << "SE iteration " << iIter++ << "\toffset=" << offset << "\tsize=" << size << std::endl;
-
-    // We suppose that the fifth word is the header of the subevent
-    // <Length> <HubId>
-    mRichSupport::SwapBytes(4, ptr + offset);
-    dataPtr         = (Int_t*) (ptr + offset);
-    Int_t SubEvSize = (Int_t)((dataPtr[0] >> 16) & 0xffff);
-    Int_t HubId     = (Int_t)((dataPtr[0]) & 0xffff);
-
-    //FIXME change from 0xc001 to 0xc000 at some point // ?
-    if ((HubId == 0xc001) || (HubId == 0xc000)) {
-      ////fSubSubEvId = HubId;
-      //////////////////////////////////////////////////////////////////
-      ////offset += (4 + ProcessCTSsubevent(SubEvSize*4, ptr+offset));//
-      //////////////////////////////////////////////////////////////////
-      ////// In principle, should be reset here for safety
-      ////fSubSubEvId = 0;
-
-      // Skip CTS subevent as it has been already processed during the first run
-      offset += (4 + SubEvSize * 4);
-      fLastFeeOnHub = false;
-    }
-    else if (HubId == 0x5555) {
-      LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
-                  << "ok"
-                  << "\t"
-                  << "hub ID = 0x" << mRichSupport::GetHexRepresentation(2, ptr + offset) << "\t"
-                  << "subevent size = " << SubEvSize;
-      fGwordCnt++;
-      fLastFeeOnHub = false;
-      //TODO one could implement additional checks here about the
-      // words coming after the "event end" but we skip everything by now.
-      ///////////////////////////////////////////////////////////////
-      offset += (4 + ProcessSKIPsubevent(SubEvSize * 4, ptr + offset));  //
-      ///////////////////////////////////////////////////////////////
-    }
-    else if (((HubId >> 8) & 0xFF) == 0x82) {
-      LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
-                  << "ok"
-                  << "\t"
-                  << "hub ID = 0x" << mRichSupport::GetHexRepresentation(2, ptr + offset) << "\t"
-                  << "subevent size = " << SubEvSize;
-      fGwordCnt++;
-      fLastFeeOnHub = false;
-      //std::cout<<"Hub: "<<std::hex<<HubId <<std::dec<<"   Size:"<< SubEvSize<<std::endl;
-      //////////////////////////////////////////////////////////////
-      offset += (4 + ProcessTRBsubevent(SubEvSize * 4, ptr + offset));  //
-      //////////////////////////////////////////////////////////////
-
-      //std::cout<<"Words in Hub 0x"<< std::hex << HubId << std::dec <<" : "<< SubEvSize <<std::endl;
-      // Bytes in a Hub
-      if (fbDebugMonitorMode) {
-        uint16_t combiner_address = ((HubId >> 4) & 0xF) * 3 + (HubId & 0xF);
-        fhSubEventSize->Fill(combiner_address, (SubEvSize * 4));
-      }
-    }
-    else {
-      LOG(WARN) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
-                << "ok"
-                << "\t"
-                << "hub ID = 0x" << mRichSupport::GetHexRepresentation(2, ptr + offset) << "\t"
-                << "subevent size = " << SubEvSize << "\n"
-                << "This is not a valid Combiner Id!"
-                << "\n"
-                << "prev prev2:" << mRichSupport::GetWordHexRepr(ptr + offset - 12) << "\n"
-                << "prev prev: " << mRichSupport::GetWordHexRepr(ptr + offset - 8) << "\n"
-                << "prev:      " << mRichSupport::GetWordHexRepr(ptr + offset - 4) << "\n"
-                << "next:      " << mRichSupport::GetWordHexRepr(ptr + offset + 4) << "\n"
-                << "next next: " << mRichSupport::GetWordHexRepr(ptr + offset + 8) << "\n";
-      //////////////////////////////////////////////////////////////
-      offset += (4 + SubEvSize * 4);
-      //////////////////////////////////////////////////////////////
-    }
-  }
-
-  ////LOG(debug4) <<  "Done processing TRB event. offset=" << offset << "\tsize=" << size;
-  //TODO implement checks
-  if (size != static_cast<size_t>(offset)) {
-    LOG(WARN) << "CbmMcbm2018UnpackerAlgoRich::ProcessTRBevent() warning:"
-              << "Number of processed bytes is not equal to the expected size. "
-                 "This should not happen. ("
-              << size << " VS " << offset << ")";
-  }
-
-  return size;  //TODO check
-}
-
-// Process TRB event header.
-// Input arguments are the size of the TRB event header (16 bytes) and the pointer to the first word.
-// Note that the first word can already be analysed outside of this method.
-// Return number of bytes processed. For this particular method the value of the input 'size' argument
-// is returned as we expect that the TRB header is always 16 bytes.
-Int_t CbmMcbm2018UnpackerAlgoRich::ProcessTRBeventHeader(size_t const size, uint8_t const* const ptr)
+void CbmMcbm2018UnpackerAlgoRich::ProcessTrbPacket(CbmMcbm2018RichMicrosliceReader& reader)
 {
-  Int_t offset;  // offset in bytes
-  Int_t* dataPtr;
-
-  // Skip first word (already processed outside)
-  //offset = 0;
-  // do nothing
-
-  // We suppose that the second word consists of
-  // 0002 - number of following word till the Event Data Starts (should be the same)
-  // 00<TriggerType>1 - value in [7:4] defines TriggerType
-  offset = 4;
-  mRichSupport::SwapBytes(4, ptr + offset);
-  dataPtr           = (Int_t*) (ptr + offset);
-  Int_t checkSize   = (Int_t)((dataPtr[0] >> 16) & 0xffff);
-  Int_t triggerType = (Int_t)((dataPtr[0] >> 4) & 0xf);
-  if (checkSize == 2) {
-    LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
-                << "ok"
-                << "\t"
-                << "checkSize == 2"
-                << "\t"
-                << "trigger type = " << triggerType;
-    fGwordCnt++;
-  }
-  else {
-    LOG(warning) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
-                 << "er"
-                 << "\t"
-                 << "checkSize != 2 (" << checkSize << ")\t"
-                 << "trigger type = " << triggerType;
-    fGwordCnt++;
-
-    /// Probably corrupted MS, stop there and skip remaining data
-    fSkipMs = kTRUE;
-    return 0;
-  }
+  ProcessMbs(reader, false);  // Current MBS
+  ProcessMbs(reader, true);   // Previous MBS
 
-  /*for (size_t iWord=2; iWord<size; iWord++) {
-		offset = iWord*4;
-		LOG(debug4) << "\t" << GetWordHexRepr(ptr+offset);
-	}*/
-
-  // We suppose that the third word consists of
-  // 0000 <SubEventId>
-  offset = 8;
-  mRichSupport::SwapBytes(4, ptr + offset);
-  dataPtr          = (Int_t*) (ptr + offset);
-  Int_t checkBytes = (Int_t)((dataPtr[0] >> 16) & 0xffff);
-  //	Int_t SubEvId = (Int_t)((dataPtr[0]) & 0xffff);
-  if (checkBytes == 0) {
-    LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
-                << "ok"
-                << "\t"
-                << "checkBytes == 0"
-                << "\t"
-                << "subevent ID = 0x" << mRichSupport::GetHexRepresentation(2, ptr + offset);
-    fGwordCnt++;
-  }
-  else {
-    LOG(warning) << "[" << fGwordCnt++ << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
-                 << "er"
-                 << "\t"
-                 << "checkBytes != 0 (" << checkBytes << ")\t"
-                 << "subevent ID = 0x" << mRichSupport::GetHexRepresentation(2, ptr + offset);
-    fGwordCnt++;
-
-    /// Probably corrupted MS, stop there and skip remaining data
-    fSkipMs = kTRUE;
-    return 0;
-  }
+  uint32_t trbNum = reader.NextWord();  // TRB trigger number
+  if (IsLog()) LOG(debug4) << GetLogHeader(reader) << "TRB Num:" << reader.GetWordAsHexString(trbNum);
 
-  // We suppose that the fourth word is the trigger number
-  offset = 12;
-  mRichSupport::SwapBytes(4, ptr + offset);
-  dataPtr           = (Int_t*) (ptr + offset);
-  UInt_t TriggerNum = (UInt_t)(dataPtr[0]);
-  LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
-              << "ok"
-              << "\t"
-              << "trigger num = " << TriggerNum;
-  fGwordCnt++;
-
-  return size;
+  ProcessHubBlock(reader);
 }
 
-Int_t CbmMcbm2018UnpackerAlgoRich::ProcessSKIPsubevent(size_t const size, uint8_t const* const ptr)
+void CbmMcbm2018UnpackerAlgoRich::ProcessMbs(CbmMcbm2018RichMicrosliceReader& reader, bool isPrev)
 {
-  ////LOG(debug4) << "ProcessSKIPsubevent size=" << size << " bytes";
-
-  Int_t offset;    // offset in bytes
-  Int_t* dataPtr;  //(FU) not used
-  uint16_t SubEventError = 0;
+  uint32_t word     = reader.NextWord();
+  uint32_t mbsNum   = word & 0xffffff;      //24 bits
+  uint32_t nofCtsCh = (word >> 24) & 0xff;  // 8 bits
+  if (IsLog())
+    LOG(debug4) << GetLogHeader(reader) << "MBS mbsNum:0x" << std::hex << mbsNum << std::dec
+                << " nofCtsCh:" << nofCtsCh;
 
-  // Skip first word (already processed outside)
-  offset = 4;
+  for (uint32_t i = 0; i < nofCtsCh; i++) {
+    uint32_t wordEpoch = reader.NextWord();
+    uint32_t epoch     = CbmMcbm2018RichTdcWordReader::ProcessEpoch(wordEpoch);
+    if (IsLog()) LOG(debug4) << GetLogHeader(reader) << "MBS ch:" << i << " epoch:" << epoch;
 
-  //Start Error identification
-  mRichSupport::SwapBytes(4, ptr + offset);
-  dataPtr       = (Int_t*) (ptr + offset);
-  SubEventError = (uint16_t)((dataPtr[0] >> 16) & 0xffff);
-  ErrorMsg(static_cast<uint16_t>(SubEventError), RichErrorType::subEventError);
+    uint32_t wordTime = reader.NextWord();
+    CbmMcbm2018RichTdcTimeData td;
+    CbmMcbm2018RichTdcWordReader::ProcessTimeData(wordTime, td);
+    if (IsLog()) LOG(debug4) << GetLogHeader(reader) << "MBS ch:" << i << " " << td.ToString();
 
-  offset = 8;
-  //End Error identification
+    double fullTime = CalculateTime(epoch, td.fCoarse, td.fFine);
 
-  while (static_cast<size_t>(offset) < size + 4) {
-    mRichSupport::SwapBytes(4, ptr + offset);
-    //                dataPtr = (Int_t*)(ptr+offset); (FU) not used
-    LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset);
-    fGwordCnt++;
-
-    offset += 4;
-  }
-
-  ////LOG(debug4) << "Done processing SKIP subevent. offset=" << offset << "\tsize=" << size;
-  //TODO implement checks
-  if (size != static_cast<size_t>(offset - 4)) {
-    LOG(WARN) << "CbmMcbm2018UnpackerAlgoRich::ProcessSKIPsubevent() warning:"
-              << "Number of processed bytes is not equal to the expected size. "
-                 "This should not happen.";
-  }
-
-  return size;  //TODO check
-}
-
-Int_t CbmMcbm2018UnpackerAlgoRich::ProcessCTSsubevent(size_t const size, uint8_t const* const ptr)
-{
-  ////LOG(debug4) << "ProcessCTSsubevent size=" << size << " bytes";
-
-  Int_t offset;  // offset in bytes
-  Int_t* dataPtr;
-
-  // Skip first word (already processed outside)
-
-  // We suppose that the second word is the header of the subsubevent
-  offset = 4;
-  mRichSupport::SwapBytes(4, ptr + offset);
-  dataPtr = (Int_t*) (ptr + offset);
-  LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
-              << "ok"
-              << "\t"
-              << "CTS header";
-  fGwordCnt++;
-
-  /*      (FU) not used
-	Short_t trigState[16];
-	for (Int_t i=0; i<16; i++) {
-		trigState[i] = ((*dataPtr >> i) & 0x1); // 16 x 1 bit
-	}
-*/
-  Short_t nInp         = ((*dataPtr >> 16) & 0xf);   // 4 bits
-  Short_t nTrigCh      = ((*dataPtr >> 20) & 0x1f);  // 5 bits
-  Short_t inclLastIdle = ((*dataPtr >> 25) & 0x1);   // 1 bit
-  Short_t inclTrigInfo = ((*dataPtr >> 26) & 0x1);   // 1 bit
-  Short_t inclTS       = ((*dataPtr >> 27) & 0x1);   // 1 bit
-  Short_t ETM          = ((*dataPtr >> 28) & 0x3);   // 2 bits
-
-  // in words (not bytes)
-  Short_t CTSinfo_size = nInp * 2 + nTrigCh * 2 + inclLastIdle * 2 + inclTrigInfo * 3 + inclTS;
-  switch (ETM) {
-    case 0: break;
-    case 1: CTSinfo_size += 1; break;
-    case 2: CTSinfo_size += 4; break;
-    case 3:
-      LOG(debug) << "ETM == 3";
-      //TODO implement
-      break;
-  }
-
-  LOG(debug) << "CTS information size (extracted from the CTS header): " << CTSinfo_size;
-
-  offset = 8;
-
-  while (offset - 8 < CTSinfo_size * 4) {
-    mRichSupport::SwapBytes(4, ptr + offset);
-    dataPtr       = (Int_t*) (ptr + offset);
-    ULong_t MSidx = 102400UL * ((ULong_t)(*dataPtr) - 1);
-    LOG(debug) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
-               << "ok"
-               << "\t"
-               << "CTS information"
-               << " MSidx=" << MSidx;
-    fGwordCnt++;
-
-    offset += 4;
-  }
-
-  // size - full size including CTS header word, CTS informations words (CTSinfo_size) and TCD data
-  // Thus TDC data size = full size - 1 word (header) - CTSinfo_size words (CTS informations)
-  ///////////////////////////////////////////////////////////////////////////
-  fChnlMsgCnt.fill(0);
-  offset += (ProcessTRBsubsubevent((size - (1 + CTSinfo_size) * 4), ptr + offset, 0, 0));  //
-  ///////////////////////////////////////////////////////////////////////////
-
-  ////LOG(debug4) << "Done processing CTS subevent. offset-4=" << offset-4 << "\tsize=" << size;
-  //TODO implement checks
-  if (size != static_cast<size_t>(offset - 4)) {
-    LOG(WARN) << "CbmMcbm2018UnpackerAlgoRich::ProcessCTSsubevent() warning:"
-              << "Number of processed bytes is not equal to the expected size. "
-                 "This should not happen.";
+    if (isPrev && td.fChannel == 0) fMbsPrevTimeCh0 = fullTime;
+    if (isPrev && td.fChannel == 1) fMbsPrevTimeCh1 = fullTime;
   }
 
-  return size;  //TODO check
+  double mbsCorr = fMbsPrevTimeCh1 - fMbsPrevTimeCh0;
+  if (IsLog())
+    LOG(debug4) << GetLogHeader(reader) << "MBS Prev ch1:" << std::setprecision(15) << fMbsPrevTimeCh1
+                << " ch0:" << fMbsPrevTimeCh0 << " corr:" << mbsCorr;
 }
 
-Int_t CbmMcbm2018UnpackerAlgoRich::ProcessTRBsubevent(size_t const size, uint8_t const* const ptr)
+void CbmMcbm2018UnpackerAlgoRich::ProcessHubBlock(CbmMcbm2018RichMicrosliceReader& reader)
 {
-  ////LOG(debug4) << "ProcessTRBsubevent size=" << size << " bytes";
-
-  Int_t offset;  // offset in bytes
-  Int_t* dataPtr;
-
-  // Skip first word (already processed outside)
-  offset = 4;
-
-  fTdcWordCorrectionCnt = 0;
+  uint32_t word    = reader.NextWord();
+  uint32_t hubId   = word & 0xffff;          // 16 bits
+  uint32_t hubSize = (word >> 16) & 0xffff;  // 16 bits
+  if (IsLog())
+    LOG(debug4) << GetLogHeader(reader) << "hubId:0x" << std::hex << hubId << std::dec << " hubSize:" << hubSize;
 
-  findTDCAlignmentError(ptr, size);
+  //if ((HubId == 0xc001) || (HubId == 0xc000)) //CTS subevent?
+  //if (HubId == 0x5555)
+  //if (((HubId >> 8) & 0xff) == 0x82) // TRB subevent? // TODO: check if it starts from 0x82
 
-  //Int_t iIter = 0;
-  while (static_cast<size_t>(offset) < (size - 2)) {  // test for cases with odd number of corrections
-    if (fSkipMs == kTRUE) break;
-    //std::cout << "SSE iteration " << iIter++ << "\toffset=" << offset << "\tsize=" << size << std::endl;
+  // if true then it is CTS sub-sub-event
+  bool isLast      = false;
+  size_t counter   = 0;
+  size_t totalSize = 0;
+  while (!isLast) {
+    word                     = reader.NextWord();
+    uint32_t subSubEventId   = word & 0xffff;                              // 16 bits
+    uint32_t subSubEventSize = (word >> 16) & 0xffff;                      // 16 bits
+    isLast                   = reader.IsLastSubSubEvent(subSubEventSize);  // if true then it is CTS sub-sub-event
+    counter++;
+    totalSize += (1 + subSubEventSize);
 
-    //correct for misalignment
-    if (fTDCAlignmentErrorPositions.size() > static_cast<unsigned int>(fTdcWordCorrectionCnt)
-        && fTDCAlignmentErrorPositions[fTdcWordCorrectionCnt] == offset) {
-      //std::cout<<"Correction in DiRICH Header: "<< fTDCAlignmentErrorPositions[fTdcWordCorrectionCnt]<<std::endl;
-      offset += 2;
-      fTdcWordCorrectionCnt++;
-    }
-
-    // We suppose that the second word is the header of the subsubevent
-    // <Length> <SubSubEv.Id>
-    mRichSupport::SwapBytes(4, ptr + offset);
-    dataPtr            = (Int_t*) (ptr + offset);
-    Int_t SubSubEvSize = (Int_t)((dataPtr[0] >> 16) & 0xffff);
-    fSubSubEvId        = (Int_t)((dataPtr[0]) & 0xffff);
-
-    //check if it is the last DiRICH in the Hub Data stream
-    //TODO CHECK!
-    if ((static_cast<size_t>(offset) + SubSubEvSize * 4) >= size) {
-      LOG(debug) << "Last DiRICH on HUB";
-      fLastFeeOnHub = true;
-    }
+    if (IsLog())
+      LOG(debug4) << GetLogHeader(reader) << counter << ((isLast) ? " CTS" : " DiRICH") << " subSubEventId:0x"
+                  << std::hex << subSubEventId << std::dec << " subSubEventSize:" << subSubEventSize;
 
-    if (((fSubSubEvId >> 12) & 0xF) != 0x7) {
-      LOG(error) << mRichSupport::GetWordHexRepr(ptr + offset - 12) << "\t"
-                 << "er"
-                 << "\t"
-                 << "ILLEGAL SubSubEvent Id  prev";
-      LOG(error) << mRichSupport::GetWordHexRepr(ptr + offset - 8) << "\t"
-                 << "er"
-                 << "\t"
-                 << "ILLEGAL SubSubEvent Id  prev";
-      LOG(error) << mRichSupport::GetWordHexRepr(ptr + offset - 4) << "\t"
-                 << "er"
-                 << "\t"
-                 << "ILLEGAL SubSubEvent Id  prev";
-      LOG(error) << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
-                 << "er"
-                 << "\t"
-                 << "ILLEGAL SubSubEvent Id  "
-                 << "Offset:" << static_cast<size_t>(offset) << "  Size:" << size;
-      LOG(error) << mRichSupport::GetWordHexRepr(ptr + offset + 4) << "\t"
-                 << "er"
-                 << "\t"
-                 << "ILLEGAL SubSubEvent Id next";
-      LOG(error) << mRichSupport::GetWordHexRepr(ptr + offset + 8) << "\t"
-                 << "er"
-                 << "\t"
-                 << "ILLEGAL SubSubEvent Id next";
-      LOG(error) << mRichSupport::GetWordHexRepr(ptr + offset + 12) << "\t"
-                 << "er"
-                 << "\t"
-                 << "ILLEGAL SubSubEvent Id next";
+    if (!isLast) {  // DiRICH event
+      // check correctness of subsub event, for safety reasons
+      if (((subSubEventId >> 12) & 0xF) != 0x7) {
+        LOG(error) << GetLogHeader(reader) << "ERROR: subSubEventId has strange value:0x" << std::hex << subSubEventId
+                   << std::dec;
+      }
+      ProcessSubSubEvent(reader, subSubEventSize, subSubEventId);
     }
-
-    LOG(debug) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
-               << "ok"
-               << "\t"
-               << "subsubevent ID (FPGA ID) = 0x" << mRichSupport::GetHexRepresentation(2, ptr + offset) << "\t"
-               << "subsubevent size = " << SubSubEvSize << " | HUB Offset:" << static_cast<size_t>(offset)
-               << "  Size:" << size;
-    fGwordCnt++;
-
-    if (size + 4 < static_cast<size_t>(offset + 4 + SubSubEvSize * 4 - fTdcWordCorrectionCnt * 2)) {
-      LOG(WARN) << "CbmMcbm2018UnpackerAlgoRich::ProcessTRBsubevent() warning:"
-                << "SubEvent out of bounds. This should not happen. (" << size << " VS "
-                << (offset + 4 + SubSubEvSize * 4 - fTdcWordCorrectionCnt * 2) << ")";
-
-      /// Probably corrupted MS, stop there and skip remaining data
-      //fSkipMs = kTRUE;
+    else {  // CTS event
+      ProcessCtsSubSubEvent(reader, subSubEventSize, subSubEventId);
     }
 
-    fChnlMsgCnt.fill(0);
-
-    // Add 4 bytes which correspond to the header word
-    //////////////////////////////////////////////////////////////////////
-    offset += (4 + ProcessTRBsubsubevent(SubSubEvSize * 4, ptr + offset + 4, offset + 4, size));  //
-    //////////////////////////////////////////////////////////////////////
-
-    //std::cout<<"Words in DiRICH 0x"<< std::hex << fSubSubEvId << std::dec <<" : "<< SubSubEvSize <<std::endl;
-
     if (fbDebugMonitorMode) {
       //This address calculation is just for mCBM; will be a problem when using full CBM RICH acceptance
-      uint16_t DiRICH_address = ((fSubSubEvId >> 8) & 0xF) * 18 + ((fSubSubEvId >> 4) & 0xF) * 2 + (fSubSubEvId & 0xF);
-      fhSubSubEventSize->Fill(DiRICH_address,
-                              SubSubEvSize);  // Words in a DiRICH
+      uint16_t histAddr = ((subSubEventId >> 8) & 0xF) * 18 + ((subSubEventId >> 4) & 0xF) * 2 + (subSubEventId & 0xF);
+      fhSubSubEventSize->Fill(histAddr, subSubEventSize);  // Words in a DiRICH
+    }
 
-      //Words per channel
-      for (size_t i = 1; i < fChnlMsgCnt.size(); ++i) {
-        if (fChnlMsgCnt.at(i) > 0) fhChnlSize->Fill(static_cast<int>(i), fChnlMsgCnt.at(i));
-      }
+    if ((totalSize == hubSize && !isLast) || (totalSize != hubSize && isLast)) {
+      if (IsLog()) LOG(error) << "ERROR: totalSize OR isLast is wrong";
     }
 
-    // In principle, should be reset here for safety
-    fSubSubEvId = 0;
+    if (totalSize >= hubSize || isLast) break;
   }
 
-  if (static_cast<Int_t>(fTDCAlignmentErrorPositions.size()) != fTdcWordCorrectionCnt)
-    std::cout << "Missing Correction" << std::endl;
-
-  // 	if (fTDCAlignmentErrorPositions.size() > 0){
-  // 		std::cout<<"Offset : "<<offset-4<<"   Size:"<< size <<std::endl;
-  // 		std::cout<<"END of Hub    : "<<mRichSupport::GetWordHexRepr(ptr+offset-4)<<std::endl; // Last word, processed as TDCWord
-  // 		std::cout<<"END of Hub +1 : "<<mRichSupport::GetWordHexRepr(ptr+offset+0)<<std::endl;
-  // 		std::cout<<"END of Hub +2 : "<<mRichSupport::GetWordHexRepr(ptr+offset+4)<<std::endl;
-  // 		std::cout<<"END of Hub +3 : "<<mRichSupport::GetWordHexRepr(ptr+offset+8)<<std::endl;
-  // 	}
-
-  ////LOG(debug4) << "Done processing TRB subevent. offset-4=" << offset-4 << "\tsize=" << size;
-  if (size != static_cast<size_t>(offset - 4)) {
-    LOG(WARN) << "CbmMcbm2018UnpackerAlgoRich::ProcessTRBsubevent() warning:"
-              << "Number of processed bytes is not equal to the expected size. "
-                 "This should not happen. ("
-              << size << " VS " << (offset - 4) << ")"
-              << "  Correction: " << fTdcWordCorrectionCnt * 2 << "  fLastFeeOnHub:" << fLastFeeOnHub;
-
-    /// Probably corrupted MS, stop there and skip remaining data
-    //fSkipMs = kTRUE;
+  // read last words
+  int lastWordsCounter = 0;
+  while (true) {
+    lastWordsCounter++;
+    word = reader.NextWord();
+    if (IsLog()) LOG(debug4) << GetLogHeader(reader);
+    if (word == 0x600dda7a) break;
+    if (lastWordsCounter >= 7) {
+      LOG(error) << GetLogHeader(reader)
+                 << "CbmMcbm2018UnpackerAlgoRich::ProcessHubBlock() ERROR: No word == 0x600dda7a";
+    }
   }
-
-  fTdcWordCorrectionGlobalCnt += fTdcWordCorrectionCnt;
-
-  return size;  //TODO check
 }
 
-Int_t CbmMcbm2018UnpackerAlgoRich::ProcessTRBsubsubevent(size_t const size, uint8_t const* const ptr,
-                                                         Int_t const hubOffset, size_t const hubSize)
-{  //size: Size of Data from DiRICH in Bytes
-  ////LOG(debug4) << "ProcessTRBsubsubevent size=" << size  << " bytes";
-  Int_t offset                  = 0;       // offset in bytes
-  fCurEpochCounter              = 0;       //TODO check
-  fInSubSubEvent                = kFALSE;  //TODO check
-  fTrbState                     = TrbNetState::IDLE;
-  Int_t TdcWordCorrection_local = 0;
-  Int_t WordCnt                 = 0;
-  bool break_flag               = false;
-
-  for (size_t iWord = 0; iWord < size / 4; iWord++) {  // iWord is size in Lines
-    //correct for misalignment
-    //hubOffset is pointing to first word after DiRICH address
-    if (fTDCAlignmentErrorPositions.size() > static_cast<unsigned int>(fTdcWordCorrectionCnt)
-        && fTDCAlignmentErrorPositions[fTdcWordCorrectionCnt] == static_cast<Int_t>(hubOffset + offset + iWord * 4)) {
-      //BEGIN DEBUG
-      //                 std::cout<<"DEBUG -1: "<< mRichSupport::GetWordHexRepr(ptr-hubOffset+fTDCAlignmentErrorPositions[fTdcWordCorrectionCnt]-4)   << std::endl;
-      //                 std::cout<<"DEBUG  0: "<< mRichSupport::GetWordHexRepr(ptr-hubOffset+fTDCAlignmentErrorPositions[fTdcWordCorrectionCnt])   << std::endl;
-      //                 std::cout<<"DEBUG +1: "<< mRichSupport::GetWordHexRepr(ptr-hubOffset+fTDCAlignmentErrorPositions[fTdcWordCorrectionCnt]+4)   << std::endl;
-      //                 std::cout<<"DEBUG_  : "<< mRichSupport::GetWordHexRepr(ptr+iWord*4+offset)   << std::endl;
-      //
-      //                 std::cout<<"Correction in DiRICH Header: "<< fTDCAlignmentErrorPositions[fTdcWordCorrectionCnt]<<std::endl;
-
-      //END   DEBUG
-      offset += 2;
-      fTdcWordCorrectionCnt++;
-      TdcWordCorrection_local++;
-      // repeat word
-      iWord--;
-      continue;
-    }
-    if (fSkipMs == kTRUE) break;
-
-
-    //if (fTDCAlignmentErrorPositions.size() > 0 && fLastFeeOnHub) std::cout<<"Final Word: "<< mRichSupport::GetWordHexRepr(ptr+iWord*4+offset)<<std::endl;
-
-    //////////////////////////////
-    if ((hubSize > 0) && (hubOffset + offset + iWord * 4 > hubSize)) {
-      //std::cout<<"BREAKING   : "<<hubOffset+offset+iWord*4 <<" > "<<  hubSize <<" | "<< offset << " | "<< fTdcWordCorrectionCnt <<std::endl;
-      break_flag = true;
-      break;
-    }
-    //if (isCTSWord) std::cout<<"TDCWORD: "<<mRichSupport::GetWordHexRepr(ptr+iWord*4+offset)<<std::endl;
-
-    mRichSupport::SwapBytes(4, ptr + iWord * 4 + offset);
-    ProcessTDCword(ptr + iWord * 4 + offset, iWord, size);  //
-
-    WordCnt++;
-
-    //std::cout<<"   "<< iWord <<"  "<< WordCnt <<std::endl;
-  }  //END of for Loop
-
-  // 	if (fTdcWordCorrectionCnt > 0){
-  // 		std::cout<<"LAST Processed Word    : "<<mRichSupport::GetWordHexRepr(ptr+(WordCnt-1)*4+offset)<<std::endl;
-  // 	}
-  //if (TdcWordCorrection_local != 0) printf(" --- TDC WORD FIX APPLIED ! --- [DiRICH : 0x%4x]\n",fSubSubEvId);
-
-  if (fSkipMs == kTRUE) return 0;
-
-  //TODO Implement checks that the first word was the header and the last word was the trailer
-
-  //if (size != static_cast<size_t>((WordCnt)*4) && fTdcWordCorrectionCnt == 0) {
-  if (!((!break_flag && ((size) == static_cast<size_t>((WordCnt) *4)))
-        || (break_flag && ((size - (fTdcWordCorrectionCnt * 2)) == static_cast<size_t>((WordCnt) *4))))) {
-    LOG(WARN) << "CbmMcbm2018UnpackerAlgoRich::ProcessTRBsubsubevent() warning:"
-              << "Number of processed bytes is not equal to the expected size. "
-                 "This should not happen."
-              << static_cast<size_t>(WordCnt * 4) << "   " << size;
-    /// Probably corrupted MS, stop there and skip remaining data
-    //fSkipMs = kTRUE;
+void CbmMcbm2018UnpackerAlgoRich::ProcessCtsSubSubEvent(CbmMcbm2018RichMicrosliceReader& reader,
+                                                        uint32_t subSubEventSize, uint32_t subSubEventId)
+{
+  uint32_t word         = reader.NextWord();
+  uint32_t ctsState     = word & 0xffff;                                                                   // 16 bits
+  uint32_t nofInputs    = (word >> 16) & 0xf;                                                              // 4 bits
+  uint32_t nofTrigCh    = (word >> 20) & 0x1f;                                                             // 5 bits
+  uint32_t inclLastIdle = (word >> 25) & 0x1;                                                              // 1 bit
+  uint32_t inclTrigInfo = (word >> 26) & 0x1;                                                              // 1 bit
+  uint32_t inclTime     = (word >> 27) & 0x1;                                                              // 1 bit
+  uint32_t ETM          = (word >> 28) & 0x3;                                                              // 2 bits
+  uint32_t ctsInfoSize  = 2 * nofInputs + 2 * nofTrigCh + 2 * inclLastIdle + 3 * inclTrigInfo + inclTime;  // in words
+  switch (ETM) {
+    case 0: break;
+    case 1: ctsInfoSize += 1; break;
+    case 2: ctsInfoSize += 4; break;
+    case 3: break;
   }
-
-
-  return (WordCnt * 4 + offset);  //TODO check
+  if (IsLog()) LOG(debug4) << GetLogHeader(reader) << "CTS ctsState:" << ctsState << " ctsInfoSize:" << ctsInfoSize;
+  for (uint32_t i = 0; i < ctsInfoSize; i++) {
+    word = reader.NextWord();  // do nothing?
+    if (IsLog()) LOG(debug4) << GetLogHeader(reader) << "CTS info words";
+  }
+  int nofTimeWords = subSubEventSize - ctsInfoSize - 1;
+  ProcessSubSubEvent(reader, nofTimeWords, subSubEventId);
 }
 
-Int_t CbmMcbm2018UnpackerAlgoRich::ProcessTDCword(uint8_t const* const ptr, Int_t const word, size_t const size)
+void CbmMcbm2018UnpackerAlgoRich::ProcessSubSubEvent(CbmMcbm2018RichMicrosliceReader& reader, int nofTimeWords,
+                                                     uint32_t subSubEventId)
 {
-  Int_t* tdcDataPtr       = (Int_t*) ptr;
-  Int_t tdcData           = tdcDataPtr[0];
-  Int_t tdcTimeDataMarker = (tdcData >> 31) & 0x1;  // 1 bit
-
+  // Store if a certain TDC word type was analysed,
+  // later one can check if the order is correct
+  bool wasHeader   = false;
+  bool wasEpoch    = false;
+  bool wasTime     = false;
+  bool wasTrailer  = false;
+  uint32_t epoch   = 0;  // store last epoch obtained in sub-sub-event
   bool errorInData = false;
 
-  // A TDC Time i only valid after a EPOCH or another TDC value
-  if ((tdcTimeDataMarker == 0x1 && fTrbState == TrbNetState::TDC)
-      || (tdcTimeDataMarker == 0x1 && fTrbState == TrbNetState::EPOCH)) {
-    UInt_t tdcMarker = (tdcData >> 29) & 0x7;  // 3 bits
-    if (tdcMarker == 0x4 || tdcMarker == 0x5) {
-      fDebugPrint = 0;
-      ////////////////////////////////
-      ProcessTimestampWord(tdcData);  //
-      ////////////////////////////////
-      fTrbState = TrbNetState::TDC;
-    }
-    else {
-      std::cout << "wrong TDC Word!!" << std::endl;
-      errorInData = true;
-    }
-  }
-  else {
-    UInt_t tdcMarker = (tdcData >> 29) & 0x7;  // 3 bits
-
-    if (tdcMarker == 0x0) {  // TDC trailer
-      if (fInSubSubEvent) {
-        if (!(fTrbState == TrbNetState::HEADER || fTrbState == TrbNetState::EPOCH || fTrbState == TrbNetState::TDC)) {
-          LOG(error) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr) << "\t"
-                     << "er"
-                     << "\t"
-                     << "ILLEGAL TRAILER Position";
-          errorInData = true;
-        }
-        else if ((size / 4 - static_cast<size_t>(word)) > 1) {
-          //Trailer only at end of SubSubEvent!
-          LOG(error) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr) << "\t"
-                     << "er"
-                     << "\t"
-                     << "Trailer only at end of SubSubEvent!" << size / 4 << "  " << static_cast<size_t>(word);
-          errorInData = true;
-        }
-        else {
-          fTrbState = TrbNetState::TRAILER;
-
-          LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr) << "\t"
-                      << "ok"
-                      << "\t"
-                      << "TDC TRAILER";
-          //extract TDC Trailer Error
-          uint16_t errorBits = (tdcData) &0xffff;  //16 bits
-          ErrorMsg(errorBits, RichErrorType::tdcTrailer, fSubSubEvId);
-          fInSubSubEvent = kFALSE;  // go out of InSubSubEvent state
-          //fGwordCnt++;
-          fDebugPrint = 0;
-        }
-      }
-      else {
-        LOG(info) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr) << "\t"
-                  << "er"
-                  << "\t"
-                  << "UNKNOWN (TDC TRAILER not after header)";
-        //fSkipMs = kTRUE;
+  // Store last raising edge time for each channel or -1. if no time
+  // this array is used to match raising and falling edges
+  std::vector<double> raisingTime(33, -1.);
+
+  for (int i = 0; i < nofTimeWords; i++) {
+    uint32_t word                   = reader.NextWord();
+    CbmMcbm2018RichTdcWordType type = CbmMcbm2018RichTdcWordReader::GetTdcWordType(word);
+
+    if (type == CbmMcbm2018RichTdcWordType::TimeData) {
+      if (!wasHeader || !wasEpoch || wasTrailer) {
+        LOG(error) << GetLogHeader(reader) << "illegal position of TDC Time (before header/epoch or after trailer)";
         errorInData = true;
-        //exit(EXIT_FAILURE); //TODO probably one should get rid of explicit EXIT calls not to ruin unpacking of other detectors?
+        continue;
       }
+      wasTime = true;
+      ProcessTimeDataWord(reader, i, epoch, word, subSubEventId, raisingTime);
     }
-    else if (tdcMarker == 0x1) {  // TDC header
-      //	UInt_t randomCode = (tdcData >> 16) & 0xff; // 8 bits
-      //	UInt_t errorBits = (tdcData) & 0xffff; //16 bits
-      if (!fInSubSubEvent) {
-        fInSubSubEvent = kTRUE;  // go into InSubSubEvent state
-
-        if (!(fTrbState == TrbNetState::IDLE || fTrbState == TrbNetState::TRAILER)) {
-          LOG(error) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr) << "\t"
-                     << "er"
-                     << "\t"
-                     << "ILLEGAL HEADER Position";
-          errorInData = true;
-        }
-        else if (!((((tdcData >> 8) & 0xFFFFFF) == 0x200096) || (((tdcData >> 8) & 0xFFFFFF) == 0x200095))) {
-          LOG(error) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr) << "\t"
-                     << "er"
-                     << "\t"
-                     << "ILLEGAL HEADER Value";
-          errorInData = true;
-        }
-        else {
-          fTrbState = TrbNetState::HEADER;
-          //extract TDC Header Error
-          uint8_t errorBits = (tdcData) &0xff;  //8 bits
-          ErrorMsg(errorBits, RichErrorType::tdcHeader, fSubSubEvId);
-          LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr) << "\t"
-                      << "ok"
-                      << "\t"
-                      << "TDC HEADER";
-        }
-        //fGwordCnt++;
-      }
-      else {
-        LOG(info) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr) << "\t"
-                  << "er"
-                  << "\t"
-                  << "UNKNOWN (TDC HEADER not after trailer)";
+    else if (type == CbmMcbm2018RichTdcWordType::Epoch) {
+      if (!wasHeader || wasTrailer) {
+        LOG(error) << GetLogHeader(reader) << "illegal position of TDC Epoch (before header or after trailer)";
         errorInData = true;
-        //fGwordCnt++;
-        //fSkipMs = kTRUE;
-        //exit(EXIT_FAILURE); //TODO probably one should get rid of explicit EXIT calls not to ruin unpacking of other detectors?
+        continue;
       }
+      wasEpoch = true;
+      epoch    = CbmMcbm2018RichTdcWordReader::ProcessEpoch(word);
+      if (IsLog()) LOG(debug4) << GetLogHeader(reader) << "SubSubEv[" << i << "] epoch:" << epoch;
     }
-    else if (tdcMarker == 0x2) {  // DEBUG
-      //	UInt_t debugMode = (tdcData >> 24) & 0x1f; // 5 bits
-      //	UInt_t debugBits = (tdcData) & 0xffffff; // 24 bits
-      //fTrbState = TrbNetState::DEBUG;
-      LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr) << "\t"
-                  << "ok"
-                  << "\t"
-                  << "DEBUG";
-      LOG(info) << "DEBUG VALUE [" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr);
-      errorInData = true;
-      //fGwordCnt++;
-      // currently no actions if a DEBUG message is encountered.
-    }
-    else if (tdcMarker == 0x3) {  // EPOCH counter
-      if (!(fTrbState == TrbNetState::HEADER || fTrbState == TrbNetState::TDC || fTrbState == TrbNetState::EPOCH)) {
-        LOG(error) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr) << "\t"
-                   << "er"
-                   << "\t"
-                   << "ILLEGAL EPOCH Position!";
+    else if (type == CbmMcbm2018RichTdcWordType::Header) {
+      if (wasEpoch || wasTime || wasTrailer) {
+        LOG(error) << GetLogHeader(reader) << "illegal position of TDC Header (after time/epoch/trailer)";
         errorInData = true;
+        continue;
       }
-      else if (((tdcData >> 28) & 0xF) != 0x6) {  //EPOCH is always 0x6....
-        LOG(error) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr) << "\t"
-                   << "er"
-                   << "\t"
-                   << "ILLEGAL EPOCH value :";
+      wasHeader          = true;
+      uint16_t errorBits = CbmMcbm2018RichTdcWordReader::ProcessHeader(word);
+      ErrorMsg(errorBits, CbmMcbm2018RichErrorType::tdcHeader, subSubEventId);
+      if (IsLog()) LOG(debug4) << GetLogHeader(reader) << "SubSubEv[" << i << "] header";
+    }
+    else if (type == CbmMcbm2018RichTdcWordType::Trailer) {
+      if (!wasEpoch || !wasTime || !wasHeader) {
+        LOG(error) << GetLogHeader(reader) << "illegal position of TDC Trailer (before time/epoch/header)";
         errorInData = true;
+        continue;
       }
-      else {
-        fTrbState        = TrbNetState::EPOCH;
-        fDebugPrint      = 0;
-        fCurEpochCounter = (tdcData) &0xfffffff;  // 28 bits
-        LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr) << "\t"
-                    << "ok"
-                    << "\t"
-                    << "EPOCH\t" << fCurEpochCounter;
-        //fGwordCnt++;
-      }
+      wasTrailer         = true;
+      uint16_t errorBits = CbmMcbm2018RichTdcWordReader::ProcessTrailer(word);
+      ErrorMsg(errorBits, CbmMcbm2018RichErrorType::tdcTrailer, subSubEventId);
+      if (IsLog()) LOG(debug4) << GetLogHeader(reader) << "SubSubEv[" << i << "] trailer";
     }
-    else {
-      if (tdcTimeDataMarker != 0x1) {
-        LOG(error) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr) << "\t"
-                   << "er"
-                   << "\t"
-                   << "UNKNOWN";
-        errorInData = true;
-      }
+    else if (type == CbmMcbm2018RichTdcWordType::Debug) {
+      // for the moment do nothing
+    }
+    else if (type == CbmMcbm2018RichTdcWordType::Error) {
+      LOG(error) << GetLogHeader(reader) << "Wrong TDC word!!! marker:" << ((word >> 29) & 0x7);
+      errorInData = true;
     }
   }
 
   if (errorInData) {
-    //Handle error
-    fSkipMs = kTRUE;
-    fSkipCnt++;
-    LOG(error) << " >>> Skipping MicroTS due to error in data! <<<";
+    //TODO:
   }
+}
 
-
-  return 0;  //correction;
+double CbmMcbm2018UnpackerAlgoRich::CalculateTime(uint32_t epoch, uint32_t coarse, uint32_t fine)
+{
+  return ((double) epoch) * 2048. * 5. + ((double) coarse) * 5. - ((double) fine) * 0.005;
 }
 
-void CbmMcbm2018UnpackerAlgoRich::ProcessTimestampWord(Int_t tdcData)
+void CbmMcbm2018UnpackerAlgoRich::ProcessTimeDataWord(CbmMcbm2018RichMicrosliceReader& reader, int iTdc, uint32_t epoch,
+                                                      uint32_t tdcWord, uint32_t subSubEventId,
+                                                      std::vector<double>& raisingTime)
 {
-  Int_t channel = (tdcData >> 22) & 0x7f;   // 7 bits
-  Int_t fine    = (tdcData >> 12) & 0x3ff;  // 10 bits
-  Int_t edge    = (tdcData >> 11) & 0x1;    // 1 bit
-  Int_t coarse  = (tdcData) &0x7ff;         // 11 bits
-  Int_t epoch   = fCurEpochCounter;
-
-  //TODO move full time calculation outside
-  Double_t fullTime = (Double_t) epoch * 2048. * 5. + (Double_t)(coarse) *5. - (Double_t)(fine) *0.005;
-
-  LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr((uint8_t*) &tdcData) << "\t"
-              << "ok"
-              << "\t"
-              << "TIMESTAMP"
-              << "\t"
-              << "ch=" << channel << "\t"
-              << "edge=" << edge << "\t"
-              << "epoch=" << epoch << "\t"
-              << "coarse=" << coarse << "\t"
-              << "fine=" << fine << "\t"
-              << "full=" << fullTime;
-  //fGwordCnt++;
-
-  // Storing reference times
-  // =======================
-
-  ////LOG(debug4) << "fSubSubEvId=0x" << std::hex << fSubSubEvId << std::dec;
-  Int_t idx = fUnpackPar->GetAddressIdx(fSubSubEvId);
-  if (-1 == idx) {
-    /// Probably corrupted MS, stop there and skip remaining data
-    fSkipMs = kTRUE;
-    return;
-  }
-  ////LOG(debug4) << "fSubSubEvId=0x" << std::hex << fSubSubEvId << std::dec << " idx=" << idx;
-
-  if ((fSubSubEvId == 0xc000) || (fSubSubEvId == 0xc001)) {
-    // if CTS
-    if ((channel == 0) && (edge == RISINGEDGEID)) {
-      fPrevLastCTSch0_re_time = fLastCTSch0_re_time;
-      fLastCTSch0_re_time     = fullTime;
-      ////LOG(debug4) << "Storing full time for the CTS ch=0 edge=RISINGEDGEID";
-    }
-    else if ((channel == 2) && (edge == RISINGEDGEID)) {
-      fPrevLastCTSch2_re_time = fLastCTSch2_re_time;
-      fLastCTSch2_re_time     = fullTime;
-      ////LOG(debug4) << "Storing full time for the CTS ch=2 edge=RISINGEDGEID";
-    }
-    else if ((channel == 2) && (edge == FALLINGEDGEID)) {
-      fPrevLastCTSch2_fe_time = fLastCTSch2_fe_time;
-      fLastCTSch2_fe_time     = fullTime;
-      ////LOG(debug4) << "Storing full time for the CTS ch=2 edge=FALLINGEDGEID";
+  CbmMcbm2018RichTdcTimeData td;
+  CbmMcbm2018RichTdcWordReader::ProcessTimeData(tdcWord, td);
+  double fullTime = CalculateTime(epoch, td.fCoarse, td.fFine);
+  // TODO: I do not use unpacker parameters, I use std::map to store full time
+  // int idx = fUnpackPar->GetAddressIdx(subSubEventId);
+  // if (idx == -1) {
+  //   LOG(error) << "ERROR: No AddressIdx found for subSubEventId:0x" << std::hex << subSubEventId << std::dec;
+  //   return;
+  // }
+
+  if (td.fChannel == 0) {
+    if (td.IsRisingEdge()) {
+      fPrevLastCh0ReTime[subSubEventId] = fLastCh0ReTime[subSubEventId];
+      fLastCh0ReTime[subSubEventId]     = fullTime;
+      if (IsLog())
+        LOG(debug4) << GetLogHeader(reader) << "SubSubEv[" << iTdc << "] " << td.ToString()
+                    << " CH0 Last:" << std::setprecision(15) << fLastCh0ReTime[subSubEventId]
+                    << " PrevLast:" << fPrevLastCh0ReTime[subSubEventId]
+                    << " diff:" << fLastCh0ReTime[subSubEventId] - fPrevLastCh0ReTime[subSubEventId];
     }
   }
   else {
-    // if not CTS (which means TRB)
-    if ((channel == 0) && (edge == RISINGEDGEID)) {
-      fPrevLastCh0_re_time[idx] = fLastCh0_re_time[idx];
-      fLastCh0_re_time[idx]     = fullTime;
-      ////LOG(debug4) << "Storing full time for TDC 0x" << std::hex << fSubSubEvId << std::dec << " ch=0 edge=RISINGEDGEID";
-    }
-  }
+    double dT           = fullTime - fPrevLastCh0ReTime[subSubEventId];
+    double mbsCorr      = fMbsPrevTimeCh1 - fMbsPrevTimeCh0;
+    double fullTimeCorr = dT - mbsCorr;
+    if (IsLog())
+      LOG(debug4) << GetLogHeader(reader) << "SubSubEv[" << iTdc << "] " << td.ToString()
+                  << " time:" << std::setprecision(15) << fullTime << " fullTimeCorr:" << fullTimeCorr;
 
-  // Calculation of corrected time
-  // =============================
-  Double_t fullTimeCorr = 0.;
-  if (!((fSubSubEvId == 0xc000) || (fSubSubEvId == 0xc001))) {
-    if (channel != 0) {
-      Double_t dT   = fullTime - fPrevLastCh0_re_time[idx];
-      Double_t corr = fPrevLastCTSch2_re_time - fPrevLastCTSch0_re_time;
-      fullTimeCorr  = dT - corr;
+    if (td.fChannel < 1 || td.fChannel >= raisingTime.size()) {
+      LOG(error) << "ERROR: channel number is out of limit. Channel:" << td.fChannel;
     }
-  }
 
-  // Filling histograms
-  // ==================
-  if (fbMonitorMode == kTRUE) {
-    if (!((fSubSubEvId == 0xc000) || (fSubSubEvId == 0xc001))) {
-      // if not CTS (which means TRB)
-      if ((channel == 0) && (edge == RISINGEDGEID)) {
-        /*				Double_t dT1 = fullTime - fLastCTSch0_re_time;
-				Double_t dT2 = fullTime - fLastCTSch2_re_time;
-				Double_t dT3 = fullTime - fLastCTSch2_fe_time;
-				fhTDCch0re_minusCTSch0re->Fill(idx, dT1);
-				fhTDCch0re_minusCTSch2re->Fill(idx, dT2);
-				fhTDCch0re_minusCTSch2fe->Fill(idx, dT3);
-
-				Double_t dT4 = fullTime - fPrevLastCTSch0_re_time;
-				Double_t dT5 = fullTime - fPrevLastCTSch2_re_time;
-				Double_t dT6 = fullTime - fPrevLastCTSch2_fe_time;
-				fhTDCch0re_minusPrevCTSch0re->Fill(idx, dT4);
-				fhTDCch0re_minusPrevCTSch2re->Fill(idx, dT5);
-				fhTDCch0re_minusPrevCTSch2fe->Fill(idx, dT6);
-
-				LOG(debug4) << "dT1=" << dT1 << "\tdT2=" << dT2 << "\tdT3=" << dT3
-				            << "\tdT4=" << dT4 << "\tdT5=" << dT5 << "\tdT6=" << dT6;
-*/
-      }
-
-      if ((channel != 0) && (edge == RISINGEDGEID)) {
-        /*Double_t dT7 = fullTime - fLastCh0_re_time[idx];
-				TH2D* h1 = fhTDCre_minusTDCch0re.at(idx);
-				h1->Fill(channel, dT7);*/
-
-        Double_t dT8 = fullTime - fPrevLastCh0_re_time[idx];
-        /*TH2D* h2 = fhTDCre_minusPrevTDCch0re.at(idx);
-				h2->Fill(channel, dT8);*/
-
-        Double_t corr1       = fPrevLastCTSch2_re_time - fPrevLastCTSch0_re_time;
-        Double_t correctedT1 = dT8 + corr1;
-        Double_t correctedT2 = dT8 - corr1;
-        /*
-//				TH2D* h3 = fhTDCre_corrected1.at(idx);
-//				h3->Fill(channel, correctedT1);
-//				TH2D* h4 = fhTDCre_corrected2.at(idx);
-//				h4->Fill(channel, correctedT2);
-*/
-        LOG(debug4)
-          /*<< "dT7=" << dT7*/ << "\tdT8=" << dT8 << "\tcorr1=" << corr1 << "\tcorrectedT1=" << correctedT1
-                               << "\tcorrectedT2=" << correctedT2;
-      }
+    if (td.IsRisingEdge()) {
+      // always store the latest raising edge. It means that in case RRFF situation only middle RF will be matched.
+      raisingTime[td.fChannel] = fullTimeCorr;
     }
-  }
-
-  if (edge == RISINGEDGEID) { this->ProcessRisingEdge(fSubSubEvId, channel, fullTimeCorr); }
-  else {
-    this->ProcessFallingEdge(fSubSubEvId, channel, fullTimeCorr);
-  }
-
-  fChnlMsgCnt.at(channel)++;
-  if (fTrbState == TrbNetState::EPOCH) fChnlMsgCnt.at(channel)++;  // If there was a correp. EPOCH before
-}
-
-void CbmMcbm2018UnpackerAlgoRich::ProcessRisingEdge(Int_t subSubEvId, Int_t channel, Double_t time)
-{
-  ////LOG(debug4) << "CbmMcbm2018UnpackerAlgoRich::ProcessRisingEdge()";
-
-  //TODO: not a very nice hack.
-  // All messages from ch0 are skipped. Though, probably, that is corect.
-  if (channel == 0) return;
-
-  // Also skip everything from CST
-  if ((subSubEvId == 0xc000) || (subSubEvId == 0xc001)) return;
-
-  fRisingEdgesBuf.push_back(CbmMcbmRichEdge(subSubEvId, channel, time));
-}
-
-void CbmMcbm2018UnpackerAlgoRich::ProcessFallingEdge(Int_t subSubEvId, Int_t channel, Double_t time)
-{
-  ////LOG(debug4) << "CbmMcbm2018UnpackerAlgoRich::ProcessFallingEdge()";
-
-  // Skip everything from CST
-  if ((subSubEvId == 0xc000) || (subSubEvId == 0xc001)) return;
-
-  Bool_t reFound = kFALSE;
-
-  std::vector<CbmMcbmRichEdge>::iterator reIter = fRisingEdgesBuf.begin();
-
-  while (reIter != fRisingEdgesBuf.end()) {
-    if (((*reIter).fSubSubEventID == subSubEvId) && ((*reIter).fChannel == channel)) {
-      Double_t reTime = (*reIter).fTime;
-      // Found corresponding rising edge
-      Double_t ToT = time - reTime;
-
-      if ((ToT >= TOTMIN) && (ToT <= TOTMAX)) {
-        // Time-over-threshold is within allowed range
-
-        reFound = kTRUE;
-
-        LOG(debug4) << "Found pair for FPGA ID 0x" << std::hex << subSubEvId << std::dec << "\tch=" << channel
-                    << "\tToT=" << ToT;
-
-        //TODO implement
-        // Writing output digi
-        //////////////////////////////////////////////////
-        if (fbMonitorMode) {
-          TH1D* h = GetTotH1(subSubEvId, channel);
-          if (h != nullptr) h->Fill(ToT);
-
-          TH2D* h2 = GetTotH2(subSubEvId);
-          if (h2 != nullptr) h2->Fill(channel, ToT);
-        }
-        WriteOutputDigi(subSubEvId, channel, reTime, ToT, fCurMSidx);  //
-        //////////////////////////////////////////////////
-
-        reIter = fRisingEdgesBuf.erase(reIter);
-        continue;  // Take care. This has to be the last operation in this block
+    else {
+      if (raisingTime[td.fChannel] == -1.) {
+        //No raising channel was found before. Skip this falling edge time.
+        if (IsLog())
+          LOG(debug4) << GetLogHeader(reader) << "SubSubEv[" << iTdc << "] "
+                      << "No raising channel was found before. Skip this falling edge time.";
       }
       else {
-        //TODO: exception. By now we can just do nothing
+        // Matching was found, calculate ToT, if tot is in a good range -> create digi
+        double ToT = fullTimeCorr - raisingTime[td.fChannel];
+        if (IsLog())
+          LOG(debug4) << GetLogHeader(reader) << "SubSubEv[" << iTdc << "] "
+                      << "ToT:" << ToT;
+        if (ToT >= fToTMin && ToT <= fToTMax) {
+          if (fbMonitorMode) {
+            TH1D* h = GetTotH1(subSubEventId, td.fChannel);
+            if (h != nullptr) h->Fill(ToT);
+
+            TH2D* h2 = GetTotH2(subSubEventId);
+            if (h2 != nullptr) h2->Fill(td.fChannel, ToT);
+          }
+          WriteOutputDigi(subSubEventId, td.fChannel, raisingTime[td.fChannel], ToT, fCurMSidx);
+        }
+        // pair was created, set raising edge to -1.
+        raisingTime[td.fChannel] = -1.;
       }
-    }  // end of if condition
-
-    // This construction is a little bit tricky.
-    // The iterator is either incremented here or (if a pair was found)
-    // incremented using erase call, followed by the continue.
-    ++reIter;
-  }  // end of for loop
-
-  if (reFound == kFALSE) {
-    // Corresponding rising edge not found - store the falling edge in the bufer
-    fFallingEdgesBuf.push_back(CbmMcbmRichEdge(subSubEvId, channel, time));
+    }
   }
 }
 
@@ -1406,9 +519,7 @@ void CbmMcbm2018UnpackerAlgoRich::WriteOutputDigi(Int_t fpgaID, Int_t channel, D
       for (int i = fDigiVect.size() - 1; i >= 0; i--) {
         lastTime = fDigiVect[i].GetTime();
         if (lastTime <= finalTime) {
-          // LOG(info) << " before:"<< fDigiVect.size();
           fDigiVect.emplace(fDigiVect.begin() + i + 1, pixelUID, finalTime, tot - ToTcorr);
-          // LOG(info) << fDigiVect.size();
           break;
         }
       }
@@ -1417,184 +528,52 @@ void CbmMcbm2018UnpackerAlgoRich::WriteOutputDigi(Int_t fpgaID, Int_t channel, D
       fDigiVect.emplace_back(pixelUID, finalTime, tot - ToTcorr);
     }
   }
-  LOG(debug4) << "CbmMcbm2018UnpackerAlgoRich::WriteOutputDigi fDigiVect.size=" << fDigiVect.size();
+  // LOG(debug4) << "CbmMcbm2018UnpackerAlgoRich::WriteOutputDigi fDigiVect.size=" << fDigiVect.size();
 }
 
-void CbmMcbm2018UnpackerAlgoRich::FinalizeTs()
-{
-  //          for (int i = 0; i < fDigiVect.size();++i) {
-  //                 LOG(info) << "CbmMcbm2018UnpackerAlgoRich::Final Vector: "
-  //                     << i+1 <<"/"<<fDigiVect.size()
-  // 	            << "\t" << std::setprecision(15)<< fDigiVect[i].GetTime();
-  //
-  //
-  //         }
-  LOG(debug4) << "CbmMcbm2018UnpackerAlgoRich::FinalizeTs: " << fRisingEdgesBuf.size() << " entries in fRisingEdgesBuf"
-              << "\t" << fFallingEdgesBuf.size() << " entries in fFallingEdgesBuf";
-
-  // Clear rising edges buffer
-  LOG(debug4) << "Rising edges: "
-                 "----------------------------------------------------------";
-  std::vector<CbmMcbmRichEdge>::iterator reIter = fRisingEdgesBuf.begin();
-  while (reIter != fRisingEdgesBuf.end()) {
-    LOG(debug4) << "FPGA=0x" << std::hex << (*reIter).fSubSubEventID << std::dec << "\tch=" << (*reIter).fChannel;
-    ++reIter;
-  }
-  fRisingEdgesBuf.clear();
-
-  // Clear falling edges buffer
-  LOG(debug4) << "Falling edges: "
-                 "---------------------------------------------------------";
-  std::vector<CbmMcbmRichEdge>::iterator feIter = fFallingEdgesBuf.begin();
-  while (feIter != fFallingEdgesBuf.end()) {
-    LOG(debug4) << "FPGA=0x" << std::hex << (*feIter).fSubSubEventID << std::dec << "\tch=" << (*feIter).fChannel;
-    ++feIter;
-  }
-  fFallingEdgesBuf.clear();
-
-  LOG(debug4) << "---------------------------------------------------------";
-}
+void CbmMcbm2018UnpackerAlgoRich::FinalizeTs() {}
 
 Bool_t CbmMcbm2018UnpackerAlgoRich::CreateHistograms()
 {
-  Int_t nTDCs = fUnpackPar->GetNaddresses();
-  //	std::vector<TCanvas*> fcToT2d;
-  /*
-	fhTDCch0re_minusCTSch0re = new TH2D("fhTDCch0re_minusCTSch0re", "TDC ch0 re - CTS ch0 re;TDC index;ns", nTDCs, 0, nTDCs, 1000, -500., 500.);
-	fhTDCch0re_minusCTSch2re = new TH2D("fhTDCch0re_minusCTSch2re", "TDC ch0 re - CTS ch2 re;TDC index;ns", nTDCs, 0, nTDCs, 1000, -500., 500.);
-	fhTDCch0re_minusCTSch2fe = new TH2D("fhTDCch0re_minusCTSch2fe", "TDC ch0 re - CTS ch2 fe;TDC index;ns", nTDCs, 0, nTDCs, 1000, -500., 500.);
-
-	AddHistoToVector(fhTDCch0re_minusCTSch0re, "");
-	AddHistoToVector(fhTDCch0re_minusCTSch2re, "");
-	AddHistoToVector(fhTDCch0re_minusCTSch2fe, "");
-
-	fhTDCch0re_minusPrevCTSch0re = new TH2D("fhTDCch0re_minusPrevCTSch0re", "TDC ch0 re - prev CTS ch0 re;TDC index;ns", nTDCs, 0, nTDCs, 1000, -500., 500.);
-	fhTDCch0re_minusPrevCTSch2re = new TH2D("fhTDCch0re_minusPrevCTSch2re", "TDC ch0 re - prev CTS ch2 re;TDC index;ns", nTDCs, 0, nTDCs, 1000, -500., 500.);
-	fhTDCch0re_minusPrevCTSch2fe = new TH2D("fhTDCch0re_minusPrevCTSch2fe", "TDC ch0 re - prev CTS ch2 fe;TDC index;ns", nTDCs, 0, nTDCs, 1000, -500., 500.);
-
-	AddHistoToVector(fhTDCch0re_minusPrevCTSch0re, "");
-	AddHistoToVector(fhTDCch0re_minusPrevCTSch2re, "");
-	AddHistoToVector(fhTDCch0re_minusPrevCTSch2fe, "");
-*/
-
-  fhTdcErrors = new TH2D("fhTdcErrors", "Errors in TDC msgs;;", nTDCs, -0.5, nTDCs - 0.5, 9, -0.5, 8.5);
-  fhTdcErrors->GetYaxis()->SetBinLabel(1, "RingBuffOverw.");
-  fhTdcErrors->GetYaxis()->SetBinLabel(2, "noRefTime");
-  fhTdcErrors->GetYaxis()->SetBinLabel(3, "refTimePrecedes");
-  fhTdcErrors->GetYaxis()->SetBinLabel(4, "trigW/oRefTime");
-  fhTdcErrors->GetYaxis()->SetBinLabel(5, "markMisRefTime");
-  fhTdcErrors->GetYaxis()->SetBinLabel(6, "multiRefTime");
-  fhTdcErrors->GetYaxis()->SetBinLabel(7, "refTime<40ns");
-  fhTdcErrors->GetYaxis()->SetBinLabel(8, "noValidation");
-  fhTdcErrors->GetYaxis()->SetBinLabel(9, "trigger!=0x1");
+  int nofTdc = fUnpackPar->GetNaddresses();
+
+  std::vector<std::string> tdcErrorLabels = {"RingBuffOverw.", "noRefTime",      "refTimePrecedes",
+                                             "trigW/oRefTime", "markMisRefTime", "multiRefTime",
+                                             "refTime<40ns",   "noValidation",   "trigger!=0x1"};
+  fhTdcErrors = new TH2D("fhTdcErrors", "Errors in TDC msgs;;", nofTdc, -0.5, nofTdc - 0.5, tdcErrorLabels.size(), -0.5,
+                         (double) tdcErrorLabels.size() - 0.5);
+  for (size_t i = 0; i < tdcErrorLabels.size(); i++) {
+    fhTdcErrors->GetYaxis()->SetBinLabel(i + 1, tdcErrorLabels[i].c_str());
+  }
   fhTdcErrors->GetXaxis()->LabelsOption("v");
   fhTdcErrors->GetYaxis()->SetTickSize(0.0);
   fhTdcErrors->GetXaxis()->SetTickSize(0.0);
-  //fhTdcErrors->SetGrid();
-
-  fhEventErrors = new TH2D("fhEventErrors", "Errors in Event/mts msgs;;", 1, -0.5, 0.5, 13, -0.5, 12.5);
-  fhEventErrors->GetYaxis()->SetBinLabel(1, "UDPProblem");
-  fhEventErrors->GetYaxis()->SetBinLabel(2, "evNumMism");
-  fhEventErrors->GetYaxis()->SetBinLabel(3, "trigMism");
-  fhEventErrors->GetYaxis()->SetBinLabel(4, "wrongLength");
-  fhEventErrors->GetYaxis()->SetBinLabel(5, "answMissing");
-  fhEventErrors->GetYaxis()->SetBinLabel(6, "evRequFail");
-  fhEventErrors->GetYaxis()->SetBinLabel(7, "evPartFound");
-  fhEventErrors->GetYaxis()->SetBinLabel(8, "sevBuffProb");
-  fhEventErrors->GetYaxis()->SetBinLabel(9, "brokenEv");
-  fhEventErrors->GetYaxis()->SetBinLabel(10, "ethLinkDwn");
-  fhEventErrors->GetYaxis()->SetBinLabel(11, "subEvBuffAlmFull");
-  fhEventErrors->GetYaxis()->SetBinLabel(12, "eth/BufProb");
-  fhEventErrors->GetYaxis()->SetBinLabel(13, "timingTrigErr");
-  fhEventErrors->GetXaxis()->LabelsOption("v");
-  fhEventErrors->GetXaxis()->SetTickSize(0.0);
-  fhEventErrors->GetYaxis()->SetTickSize(0.0);
-
-  for (Int_t iTDC = 0; iTDC < nTDCs; iTDC++) {
-    TString histoName;
-    TString histoTitle;
-    TString subFolder;
-
-    Int_t Addr    = fUnpackPar->GetAddress(iTDC);
-    fMapFEE[Addr] = iTDC;
-    fhTdcErrors->GetXaxis()->SetBinLabel(iTDC + 1, Form("0x%4x", Addr));
-    /*
-		histoName.Form("fhTDC%dre_minusTDC%dch0re", iTDC, iTDC);
-		histoTitle.Form("TDC %d re - TDC %d ch0 re;channel;ns", iTDC, iTDC);
-		TH2D* h1 = new TH2D(histoName, histoTitle, 32, 0., 32., 1200, 0., 600.);
-		fhTDCre_minusTDCch0re.push_back(h1);
-		AddHistoToVector(h1);
-
-		histoName.Form("fhTDC%dre_minusPrevTDC%dch0re", iTDC, iTDC);
-		histoTitle.Form("TDC %d re - prev. TDC %d ch0 re;channel;ns", iTDC, iTDC);
-		TH2D* h2 = new TH2D(histoName, histoTitle, 32, 0., 32., 1200, 0., 600.);
-		fhTDCre_minusPrevTDCch0re.push_back(h2);
-		AddHistoToVector(h2);
-*/
-    /*		histoName.Form("fhTDC%dre_corrected1", iTDC);
-		histoTitle.Form("TDC %d re corrected1;channel;ns", iTDC);
-		TH2D* h3 = new TH2D(histoName, histoTitle, 32, 0., 32., 1200, 0., 600.);
-		fhTDCre_corrected1.push_back(h3);
-		AddHistoToVector(h3);
-
-		histoName.Form("fhTDC%dre_corrected2", iTDC);
-		histoTitle.Form("TDC %d re corrected2;channel;ns", iTDC);
-		TH2D* h4 = new TH2D(histoName, histoTitle, 32, 0., 32., 1200, 0., 600.);
-		fhTDCre_corrected2.push_back(h4);
-		AddHistoToVector(h4);
-*/
-    // TODO
-    //workaround we need to init all histograms for ToT here. Otherwise they will not be added to monitoring.
+
+  for (Int_t iTdc = 0; iTdc < nofTdc; iTdc++) {
+    Int_t tdcId    = fUnpackPar->GetAddress(iTdc);
+    fMapFEE[tdcId] = iTdc;
+    fhTdcErrors->GetXaxis()->SetBinLabel(iTdc + 1, Form("0x%4x", tdcId));
+
+    // init ToT histogramms
     for (Int_t iCh = 0; iCh <= 32; iCh++) {
-      Int_t tdc = fUnpackPar->GetAddress(iTDC);
-      GetTotH1(tdc, iCh);
-    }
-    {
-      Int_t tdc = fUnpackPar->GetAddress(iTDC);
-      GetTotH2(tdc);
+      GetTotH1(tdcId, iCh);
     }
+    GetTotH2(tdcId);
 
-    /*******************************************************************/
-
-    /// Map of hits over T0 detector and same vs time in run
-    {  //if (iTDC == 0){
-      Double_t w = 10;
-      Double_t h = 10;
-
-      TCanvas* c;
-      TString canvasName;
-      TString canvasTitle;
-      Int_t tdc = fUnpackPar->GetAddress(iTDC);
-      canvasName.Form("cToT2d_TDC_0x%4x", tdc);
-      canvasTitle.Form("ToTs of TDC 0x%4x", tdc);
-      c = new TCanvas(canvasName, canvasTitle, w, h);
-      //		   fcHitMaps->Divide( 2 );
-      //		   fcHitMaps->cd( 1 );
-      //		   gPad->SetGridx();
-      //		   gPad->SetGridy();
-      //		   gPad->SetLogy();
-      //		   fhChannelMap->Draw();
-      //		   fcHitMaps->cd( 2 );
-      //		   gPad->SetGridx();
-      //		   gPad->SetGridy();
-      //		   gPad->SetLogz();
-      TH2D* h2 = GetTotH2(tdc);
+    {
+      std::stringstream cName, cTitle;
+      int tdc = fUnpackPar->GetAddress(iTdc);
+      cName << "cToT2d_TDC_0x" << std::hex << tdc;
+      cTitle << "ToTs of TDC 0x" << std::hex << tdc;
+      TCanvas* c = new TCanvas(cName.str().c_str(), cTitle.str().c_str(), 10, 10);
+      TH2D* h2   = GetTotH2(tdc);
       h2->Draw("colz");
       fcTot2d.push_back(c);
       AddCanvasToVector(c, "ToT_Canvases");
     }
-    /*******************************************************************/
   }
 
-
   AddHistoToVector(fhTdcErrors, "");
-  AddHistoToVector(fhEventErrors, "");
-
-  fhVectorSize = new TH1I("fhVectorSize", "Size of the vector VS TS index; TS index; Size [bytes]", 10000, 0., 10000.);
-  fhVectorCapacity =
-    new TH1I("fhVectorCapacity", "Size of the vector VS TS index; TS index; Size [bytes]", 10000, 0., 10000.);
-  AddHistoToVector(fhVectorSize, "");
-  AddHistoToVector(fhVectorCapacity, "");
 
   if (fbDebugMonitorMode) {
     fhEventSize = new TH1I("fhEventSize", "Size of the Event from TrbNet; Size [bytes]", 350, 0., 70000.);
@@ -1615,31 +594,31 @@ Bool_t CbmMcbm2018UnpackerAlgoRich::CreateHistograms()
   return kTRUE;
 }
 
-TH1D* CbmMcbm2018UnpackerAlgoRich::GetTotH1(Int_t tdc, Int_t channel)
+TH1D* CbmMcbm2018UnpackerAlgoRich::GetTotH1(uint32_t tdc, uint32_t channel)
 {
   TH1D* h = fhTotMap[tdc][channel];
   if (h == nullptr) {
-    TString name, title, subFolder;
-    name.Form("ToT_tdc0x%x_ch%u", tdc, channel);
-    title.Form("%s;ToT [ns];Entries", name.Data());
-    subFolder.Form("ToT/tdc0x%x", tdc);
-    h = new TH1D(name, title, 100, -1., 49.);
-    AddHistoToVector(h, std::string(subFolder.Data()));
+    std::stringstream name, subFolder;
+    name << "ToT_tdc0x" << std::hex << tdc << std::dec << "_ch" << channel;
+    std::string title = name.str() + ";ToT [ns];Entries";
+    subFolder << "ToT/tdc0x" << std::hex << tdc;
+    h = new TH1D(name.str().c_str(), title.c_str(), 100, -1., 49.);
+    AddHistoToVector(h, subFolder.str());
     fhTotMap[tdc][channel] = h;
   }
   return h;
 }
 
-TH2D* CbmMcbm2018UnpackerAlgoRich::GetTotH2(Int_t tdc)
+TH2D* CbmMcbm2018UnpackerAlgoRich::GetTotH2(uint32_t tdc)
 {
   TH2D* h = fhTot2dMap[tdc];
   if (h == nullptr) {
-    TString name, title, subFolder;
-    name.Form("ToT_2d_tdc0x%x", tdc);
-    title.Form("%s;channels;ToT [ns]", name.Data());
-    subFolder.Form("ToT2d");
-    h = new TH2D(name, title, 33, 0, 32, 200, -1., 49.);
-    AddHistoToVector(h, std::string(subFolder.Data()));
+    std::stringstream name;
+    name << "ToT_2d_tdc0x" << std::hex << tdc;
+    std::string title     = name.str() + ";channels;ToT [ns]";
+    std::string subFolder = "ToT2d";
+    h                     = new TH2D(name.str().c_str(), title.c_str(), 33, 0, 32, 200, -1., 49.);
+    AddHistoToVector(h, subFolder);
     fhTot2dMap[tdc] = h;
   }
   return h;
@@ -1658,194 +637,48 @@ Bool_t CbmMcbm2018UnpackerAlgoRich::DebugMs(const fles::Timeslice& ts, size_t uM
   return kTRUE;
 }
 
-Int_t CbmMcbm2018UnpackerAlgoRich::Debug(const uint8_t* ptr, const size_t size)
-{
-
-  if (size == 0) return size;
-
-  //LOG(info)<<"DEBUG MODE IS ACTIVE; Printing raw data:";
-
-  uint8_t nblCnt = 0;
-  uint8_t wrdCnt = 0;
-  std::cout << std::endl << "SIZE: " << std::dec << size << "Byte" << std::endl;
-  for (size_t i = 0; i < size; ++i) {
-
-    //if (wrdCnt == 0) std::cout<<"HEX: ";
-    uint8_t* tdcDataPtr = (uint8_t*) (ptr + i);
-
-    if (wrdCnt == 0 && nblCnt == 0) { printf("%08d : ", static_cast<int>(i)); }
-
-    printf("%02x", unsigned(*tdcDataPtr));
-    nblCnt++;
-    if (nblCnt % 2 == 0) { printf(" "); }
-    if (nblCnt % 4 == 0) {
-      printf("  ");
-      wrdCnt++;
-      nblCnt = 0;
-    }
-
-    if (wrdCnt == 10) {
-      printf("\n");
-      wrdCnt = 0;
-    }
-  }
-  printf("\n");
-  return size;
-}
+Int_t CbmMcbm2018UnpackerAlgoRich::Debug(const uint8_t* ptr, const size_t size) { return 0; }
 
 
-void CbmMcbm2018UnpackerAlgoRich::ErrorMsg(uint16_t errbits, RichErrorType type, uint16_t tdcAddr)
+void CbmMcbm2018UnpackerAlgoRich::ErrorMsg(uint16_t errbits, CbmMcbm2018RichErrorType type, uint16_t tdcId)
 {
-  if (fbMonitorMode) {
-    switch (type) {
-      case RichErrorType::mtsError:
-        //UDP problem
-        if ((errbits & 0x1) == 1) fhEventErrors->Fill(0.0, 0.0);
-
-        break;
-
-      case RichErrorType::tdcHeader:
-        // min. 1 rinǵ buffer overwritten
-        if ((errbits & 0x1) == 1) fhTdcErrors->Fill(fMapFEE[tdcAddr], 0.0);
-
-        break;
-
-      case RichErrorType::tdcTrailer:
-        // no reference time in trigger handler in TDC
-        if (((errbits >> 0) & 0x1) == 1) fhTdcErrors->Fill(fMapFEE[tdcAddr], 1.0);
-
-        // reference time precedes a non-timing trigger
-        if (((errbits >> 1) & 0x1) == 1) fhTdcErrors->Fill(fMapFEE[tdcAddr], 2.0);
-
-        // timing trigger is delivered without a reference time
-        if (((errbits >> 2) & 0x1) == 1) fhTdcErrors->Fill(fMapFEE[tdcAddr], 3.0);
-
-        // Set with the bit 2 to mark the missing reference time
-        if (((errbits >> 3) & 0x1) == 1) fhTdcErrors->Fill(fMapFEE[tdcAddr], 4.0);
-
-        // there are more than one detected reference time
-        if (((errbits >> 4) & 0x1) == 1) fhTdcErrors->Fill(fMapFEE[tdcAddr], 5.0);
-
-        // reference time was too short (<40 ns)
-        if (((errbits >> 5) & 0x1) == 1) fhTdcErrors->Fill(fMapFEE[tdcAddr], 6.0);
-
-        // no trigger validation arrives from the endpoint after a valid  reference time
-        if (((errbits >> 6) & 0x1) == 1) fhTdcErrors->Fill(fMapFEE[tdcAddr], 7.0);
-
-        // any timing trigger type except 0x1 is send
-        if (((errbits >> 7) & 0x1) == 1) fhTdcErrors->Fill(fMapFEE[tdcAddr], 8.0);
-
-        break;
-
-      case RichErrorType::ctsHeader:
-        // To be implemented
-        break;
+  if (!fbMonitorMode) return;
 
-      case RichErrorType::ctsTrailer:
-        // To be implemented
-        break;
-
-      case RichErrorType::subEventError:
-        // event number mismatch
-        if (((errbits >> 0) & 0x1) == 1) fhEventErrors->Fill(0.0, 1.0);
-
-        // trigger code mismatch
-        if (((errbits >> 1) & 0x1) == 1) fhEventErrors->Fill(0.0, 2.0);
-
-        // wrong length
-        if (((errbits >> 2) & 0x1) == 1) fhEventErrors->Fill(0.0, 3.0);
-
-        // answer missing
-        if (((errbits >> 3) & 0x1) == 1) fhEventErrors->Fill(0.0, 4.0);
-
-        // event number request by CTS was not available (Not found)
-        if (((errbits >> 4) & 0x1) == 1) fhEventErrors->Fill(0.0, 5.0);
-
-        // event partially found in data buffer
-        if (((errbits >> 5) & 0x1) == 1) fhEventErrors->Fill(0.0, 6.0);
-
-        // Severe Problem with data buffer and/or read-out
-        if (((errbits >> 6) & 0x1) == 1) fhEventErrors->Fill(0.0, 7.0);
-
-        // Single broken event
-        if (((errbits >> 7) & 0x1) == 1) fhEventErrors->Fill(0.0, 8.0);
-
-        // Ethernet Link down
-        if (((errbits >> 8) & 0x1) == 1) fhEventErrors->Fill(0.0, 9.0);
-
-        // SubEvent buffer almost full
-        if (((errbits >> 9) & 0x1) == 1) fhEventErrors->Fill(0.0, 10.0);
-
-        // Ethernet/SubEventBuilder error
-        if (((errbits >> 10) & 0x1) == 1) fhEventErrors->Fill(0.0, 11.0);
-
-        // Timing trigger error
-        if (((errbits >> 11) & 0x1) == 1) fhEventErrors->Fill(0.0, 12.0);
-
-        break;
-
-      default: break;
+  if (type == CbmMcbm2018RichErrorType::tdcHeader) {
+    // Errors description in TRB manual section 11.3.2. TDC HEADER
+    // Bit 0: min. 1 rinǵ buffer overwritten
+    int nofHeaderErrorBits       = 1;
+    int histBinOffsetHeaderError = 0;
+    for (int i = 0; i < nofHeaderErrorBits; i++) {
+      if (((errbits >> i) & 0x1) == 1) fhTdcErrors->Fill(fMapFEE[tdcId], i + histBinOffsetHeaderError);
     }
   }
-}
-
-/*
-Bool_t CbmMcbm2018UnpackerAlgoRich::FillHistograms()
-{
-	return kTRUE;
-}
-*/
-Bool_t CbmMcbm2018UnpackerAlgoRich::ResetHistograms()
-{
-  //TODO: do something?
-  return kTRUE;
-}
-
-void CbmMcbm2018UnpackerAlgoRich::findTDCAlignmentError(uint8_t const* const ptr, size_t const size)
-{
-
-  fTDCAlignmentErrorPositions.clear();
-
-  //     mRichSupport::SwapBytes(4, ptr+size);
-  //     if((((((Int_t*)(ptr+size))[0]) >> 28) & 0xF) != 0x0) {
-  //         LOG(WARN) << "CbmMcbm2018UnpackerAlgoRich::ProcessTRBsubevent() warning:"
-  //                   << "End on Hub is not where expected. Is it a Buffer overflow?  LastWord: "<<mRichSupport::GetWordHexRepr(ptr+size);
-  //     }
-  //     mRichSupport::SwapBytes(4, ptr+size);
-
-  /***
-	 * Signature of Error:
-	 *  82b7   8ca6
-	 *  8297  *34ad*
-	 * *34ad*  66af    // data Ptr
-	 * *cf8b* *cf8b*
-	 *  82c8   cca9
-	 */
-
-  //start at 8 to skip header of Hub and first row as this has to be checked
-  //stop at size -4 to avoid comparing with following hub
-
-  for (Int_t i = 8; i < static_cast<Int_t>(size - 4); i += 4) {  // i represents bytes (4 per line)
-    //TODO: Optimize the swaping
-    mRichSupport::SwapBytes(4, ptr + i - 4);
-    mRichSupport::SwapBytes(4, ptr + i);
-    mRichSupport::SwapBytes(4, ptr + i + 4);
-    bool problem = false;
-    if ((((Int_t*) (ptr + i - 4))[0] & 0xFFFF) == ((((Int_t*) (ptr + i))[0] >> 16) & 0xFFFF)) {
-      if ((((Int_t*) (ptr + i + 4))[0] & 0xFFFF) == ((((Int_t*) (ptr + i + 4))[0] >> 16) & 0xFFFF)) {
-        //Signature of problem!
-        problem = true;
-        fTDCAlignmentErrorPositions.push_back(i);
-        fTDCAlignmentErrorPositions.push_back(i + 6);
-      }
+  else if (type == CbmMcbm2018RichErrorType::tdcTrailer) {
+    // Errors description in TRB manual section 11.3.5. TDC TRAILER
+    // Bit 0: no reference time in trigger handler in TDC
+    // Bit 1: reference time precedes a non-timing trigger
+    // Bit 2: timing trigger is delivered without a reference time
+    // Bit 3: Set with the bit 2 to mark the missing reference time
+    // Bit 4: there are more than one detected reference time
+    // Bit 5: reference time was too short (<40 ns)
+    // Bit 6: no trigger validation arrives from the endpoint after a valid  reference time
+    // Bit 7: any timing trigger type except 0x1 is send
+    int nofTrailerErrorBits       = 8;
+    int histBinOffsetTrailerError = 1;
+    for (int i = 0; i < nofTrailerErrorBits; i++) {
+      if (((errbits >> i) & 0x1) == 1) fhTdcErrors->Fill(fMapFEE[tdcId], i + histBinOffsetTrailerError);
     }
-
-    mRichSupport::SwapBytes(4, ptr + i - 4);
-    mRichSupport::SwapBytes(4, ptr + i);
-    mRichSupport::SwapBytes(4, ptr + i + 4);
-
-    if (problem) i += 8;  //jump after the problem
+  }
+  else if (type == CbmMcbm2018RichErrorType::ctsHeader) {
+    // To be implemented
+  }
+  else if (type == CbmMcbm2018RichErrorType::ctsTrailer) {
+    // To be implemented
+  }
+  else {
   }
 }
 
+Bool_t CbmMcbm2018UnpackerAlgoRich::ResetHistograms() { return kTRUE; }
+
 ClassImp(CbmMcbm2018UnpackerAlgoRich)
diff --git a/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerAlgoRich.h b/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerAlgoRich.h
index f5459192e7..b449b1b391 100644
--- a/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerAlgoRich.h
+++ b/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerAlgoRich.h
@@ -5,6 +5,7 @@
 /**
  * CbmMcbm2018UnpackerAlgoRich
  * E. Ovcharenko, Mar 2019
+ * S. Lebedev, June 2021
  * based on other detectors' classes by P.-A. Loizeau
  */
 
@@ -14,38 +15,25 @@
 #include "CbmStar2019Algo.h"  // mother class
 
 // STD
+#include <bitset>
+#include <iomanip>
 #include <map>
 #include <vector>
 
 // ROOT
+#include <Logger.h>
+
 #include <TArrayD.h>
 #include <TH2D.h>
 
 // CbmRoot
-#include "CbmMcbm2018UnpackerUtilRich.h"
 #include "CbmRichDigi.h"
 
 ////class TList; // not really needed, already declared in the mother class
 class CbmMcbm2018RichPar;
 
-#define RISINGEDGEID 1
-#define FALLINGEDGEID 0
-
-#define TOTMIN -20.
-#define TOTMAX 100.
-
-enum class TrbNetState
-{
-  IDLE,
-  HEADER,
-  EPOCH,
-  TDC,
-  TRAILER,
-  CTS,
-  DEBUG
-};
 
-enum class RichErrorType
+enum class CbmMcbm2018RichErrorType
 {
   mtsError,
   tdcHeader,
@@ -55,6 +43,153 @@ enum class RichErrorType
   subEventError
 };
 
+enum class CbmMcbm2018RichTdcWordType
+{
+  TimeData,
+  Header,
+  Epoch,
+  Trailer,
+  Debug,
+  Error
+};
+
+class CbmMcbm2018RichTdcTimeData {
+public:
+  uint32_t fCoarse  = 0;  // 11 bits
+  uint32_t fEdge    = 0;  // 1 bit
+  uint32_t fFine    = 0;  // 10 bits
+  uint32_t fChannel = 0;  // 7 bits
+
+  std::string ToString()
+  {
+    std::stringstream stream;
+    stream << "channel:" << fChannel << " coarse:" << fCoarse << " fine:" << fFine
+           << " edge:" << ((fEdge == 1) ? "R" : "F");
+    return stream.str();
+  }
+
+  bool IsRisingEdge() { return (fEdge == 1); }
+};
+
+class CbmMcbm2018RichTdcWordReader {
+public:
+  static CbmMcbm2018RichTdcWordType GetTdcWordType(uint32_t tdcWord)
+  {
+    uint32_t tdcTimeDataMarker = (tdcWord >> 31) & 0x1;  // 1 bit
+    uint32_t tdcMarker         = (tdcWord >> 29) & 0x7;  // 3 bits
+
+    if (tdcTimeDataMarker == 0x1) {
+      // TODO: I also include tdcMarker == 0x5, some tdc time data words have this marker. Is it correct?
+      if (tdcMarker == 0x4 || tdcMarker == 0x5) { return CbmMcbm2018RichTdcWordType::TimeData; }
+      else {
+        return CbmMcbm2018RichTdcWordType::Error;
+      }
+    }
+
+    if (tdcMarker == 0x0) return CbmMcbm2018RichTdcWordType::Trailer;
+    if (tdcMarker == 0x1) return CbmMcbm2018RichTdcWordType::Header;
+    if (tdcMarker == 0x2) return CbmMcbm2018RichTdcWordType::Debug;
+    if (tdcMarker == 0x3) return CbmMcbm2018RichTdcWordType::Epoch;
+
+    return CbmMcbm2018RichTdcWordType::Error;
+  }
+
+  static void ProcessTimeData(uint32_t tdcWord, CbmMcbm2018RichTdcTimeData& outData)
+  {
+    outData.fCoarse  = static_cast<uint32_t>(tdcWord & 0x7ff);          // 11 bits
+    outData.fEdge    = static_cast<uint32_t>((tdcWord >> 11) & 0x1);    // 1 bit
+    outData.fFine    = static_cast<uint32_t>((tdcWord >> 12) & 0x3ff);  // 10 bits
+    outData.fChannel = static_cast<uint32_t>((tdcWord >> 22) & 0x7f);   // 7 bits
+  }
+
+  static uint32_t ProcessEpoch(uint32_t tdcWord) { return static_cast<uint32_t>(tdcWord & 0xfffffff); }
+
+  static uint16_t ProcessHeader(uint32_t tdcWord)
+  {
+    // for the moment just extract error bits
+    return static_cast<uint16_t>(tdcWord & 0xff);  //8 bits
+  }
+
+  static uint16_t ProcessTrailer(uint32_t tdcWord)
+  {
+    // for the moment just extract error bits
+    return static_cast<uint16_t>(tdcWord & 0xffff);
+  }
+
+  static void ProcessDebug(uint32_t tdcWord)
+  {
+    LOG(debug4) << "ProcessDebug is not implemented. tdcWord:0x" << std::hex << tdcWord << std::dec;
+    // for the moment do nothing
+  }
+};
+
+class CbmMcbm2018RichMicrosliceReader {
+private:
+  const uint8_t* fData = nullptr;
+  size_t fSize         = 0;
+  size_t fOffset       = 0;  // offset in bytes
+  size_t fWordCounter  = 0;
+  uint32_t fCurWord;
+
+public:
+  void SetData(const uint8_t* data, size_t size)
+  {
+    fData        = data;
+    fSize        = size;
+    fOffset      = 0;
+    fWordCounter = 0;
+    fCurWord     = 0;
+  }
+
+  const uint8_t* GetData() { return fData; }
+
+  size_t GetSize() { return fSize; }
+
+  size_t GetOffset() { return fOffset; }
+
+  size_t GetWordCounter() { return fWordCounter; }
+
+  uint32_t GetCurWord() { return fCurWord; }
+
+  std::string GetWordAsHexString(uint32_t word)
+  {
+    std::stringstream stream;
+    stream << "0x" << std::setfill('0') << std::setw(sizeof(uint32_t) * 2) << std::hex << word;
+    return stream.str();
+  }
+
+  uint32_t NextWord()
+  {
+    //mRichSupport::SwapBytes(4, fData + fOffset);
+    //Int_t* dataPtr = (Int_t*) (fData + fOffset);
+    uint32_t i = ((uint32_t*) (fData + fOffset))[0];
+    //swap bytes
+    i = (i >> 24) | ((i << 8) & 0x00FF0000) | ((i >> 8) & 0x0000FF00) | (i << 24);
+    //i = ((i&0xFF)<<24) | (((i>>8)&0xFF)<<16) |   (((i>>16)&0xFF)<<8) | (((i>>24)&0xFF)<<0);
+
+    fOffset += 4;
+    fWordCounter++;
+    fCurWord = i;
+    //return (Int_t)(dataPtr[0]);
+    return i;
+  }
+
+  bool IsNextPadding()
+  {
+    uint32_t nextWord = ((uint32_t*) (fData + fOffset))[0];
+    if (nextWord == 0xffffffff) return true;
+    return false;
+  }
+
+  bool IsLastSubSubEvent(uint32_t subSubEventSize)
+  {
+    uint32_t i = ((uint32_t*) (fData + fOffset + 4 * subSubEventSize))[0];
+    i          = (i >> 24) | ((i << 8) & 0x00ff0000) | ((i >> 8) & 0x0000ff00) | (i << 24);
+    if (i == 0x00015555) return true;
+    return false;
+  }
+};
+
 class CbmMcbm2018UnpackerAlgoRich : public CbmStar2019Algo<CbmRichDigi> {
 public:
   CbmMcbm2018UnpackerAlgoRich();
@@ -106,43 +241,26 @@ private:
 	 */
   void InitStorage();
 
+  std::string GetLogHeader(CbmMcbm2018RichMicrosliceReader& reader);
+
   void ProcessMicroslice(size_t const size, uint8_t const* const ptr);
 
-  /**
-	 * Including header
-	 */
-  Int_t ProcessTRBevent(size_t const size, uint8_t const* const ptr);
+  void ProcessTrbPacket(CbmMcbm2018RichMicrosliceReader& reader);
 
-  /**
-	 *
-	 */
-  Int_t ProcessTRBeventHeader(size_t const size, uint8_t const* const ptr);
+  void ProcessMbs(CbmMcbm2018RichMicrosliceReader& reader, bool isPrev);
 
-  /**
-	 * Including header - ?
-	 * Return number of processed bytes
-	 */
-  Int_t ProcessSKIPsubevent(size_t const size, uint8_t const* const ptr);
+  void ProcessHubBlock(CbmMcbm2018RichMicrosliceReader& reader);
 
-  Int_t ProcessCTSsubevent(size_t const size, uint8_t const* const ptr);
+  void ProcessCtsSubSubEvent(CbmMcbm2018RichMicrosliceReader& reader, uint32_t subSubEventSize, uint32_t subSubEventId);
 
-  Int_t ProcessTRBsubevent(size_t const size, uint8_t const* const ptr);
+  void ProcessSubSubEvent(CbmMcbm2018RichMicrosliceReader& reader, int nofTimeWords, uint32_t subSubEventId);
 
-  /**
-	 * Including TDC header, but not including TRB subsubevent header
-	 * Return number of processed bytes
-	 */
-  Int_t ProcessTRBsubsubevent(size_t const size, uint8_t const* const ptr, Int_t const hubOffset, size_t const hubSize);
+  double CalculateTime(uint32_t epoch, uint32_t coarse, uint32_t fine);
 
-  /**
-	 * Process a word written out by the TDC - TIMESTEMP, HEADER, TRAILER, DEBUG, EPOCH, ...
-	 */
-  Int_t ProcessTDCword(uint8_t const* const ptr, Int_t const word, size_t const size);
+  bool IsLog();
 
-  /**
-	 * Process specifically a TIMESTAMP type of word
-	 */
-  void ProcessTimestampWord(Int_t tdcData);
+  void ProcessTimeDataWord(CbmMcbm2018RichMicrosliceReader& reader, int iTdc, uint32_t epoch, uint32_t tdcWord,
+                           uint32_t subSubEventId, std::vector<double>& raisingTime);
 
   /**
 	 * Write a gidi object into the output collection
@@ -157,7 +275,7 @@ private:
   /**
      * Write Errors to Histograms
      */
-  void ErrorMsg(uint16_t errbits, RichErrorType type, uint16_t tdcAddr = 0);
+  void ErrorMsg(uint16_t errbits, CbmMcbm2018RichErrorType type, uint16_t tdcId = 0);
 
   Int_t GetPixelUID(Int_t fpgaID, Int_t ch) const
   {
@@ -166,52 +284,35 @@ private:
     return ((fpgaID << 16) | (ch & 0x00FF));
   }
 
-  void findTDCAlignmentError(uint8_t const* const ptr, size_t const size);
+private:
+  double fToTMin = -20.;
+  double fToTMax = 100.;
 
-private:  // data members
   /// Control flags
-  Bool_t fbMonitorMode;  //! Switch ON the filling of a minimal set of histograms
-
-  Bool_t fbDebugMonitorMode;  //! Switch ON the filling of a additional set of histograms
-
-  Bool_t fRawDataMode;
-
-  Bool_t fError;  //! flag for an error in the datastream
+  Bool_t fbMonitorMode      = false;  // Switch ON the filling of a minimal set of histograms
+  Bool_t fbDebugMonitorMode = false;  // Switch ON the filling of a additional set of histograms
+  Bool_t fRawDataMode       = false;
 
-  TrbNetState fTrbState;  // State of TrbNet (HEADER,TRAILER,...)
+  uint64_t fTsCounter = 0;  //Counter of processed timeslices
+  uint32_t fMsInd     = 0;  // Current microslice index
 
-  uint32_t fErrorCorr;  // Offset to correct a error in datastream
+  double fMbsPrevTimeCh0 = 0.;
+  double fMbsPrevTimeCh1 = 0.;
 
-  /// User setting: kTRUE activates ToT correction from Parameterfile
-  Bool_t fbDoToTCorr;
+  std::map<uint32_t, double> fLastCh0ReTime;      //key:TDC ID, value:Full time of last rising edge from ch 0
+  std::map<uint32_t, double> fPrevLastCh0ReTime;  // key:TDC ID, value:Full time of previous last rising edge from ch 0
 
-  Bool_t fSkipMs;
-  /// User settings: Data correction parameters
-  Double_t fdTimeOffsetNs;
-
-  size_t fDataSize = 0;
+  Bool_t fbDoToTCorr = true;  // kTRUE activates ToT correction from Parameterfile
 
+  Double_t fdTimeOffsetNs = 0.;  // User settings: Data correction parameters
 
   /**
 	 * Bug fix / shortcut, which helps to run correct ProcessTs method.
 	 * Works only if there is one componentID assigned to the detector.
 	 */
-  Int_t fRICHcompIdx;
-
-  /**
-	 * Unpacker parameters object
-	 */
-  CbmMcbm2018RichPar* fUnpackPar;  //!
-
-  /**
-	 * Counter of processed timeslices
-	 */
-  uint64_t fTScounter;
+  Int_t fRICHcompIdx = 6;
 
-  /**
-	 * Current microslice ID
-	 */
-  Int_t fCurMSid;  //!
+  CbmMcbm2018RichPar* fUnpackPar = nullptr;  //!
 
   /**
 	 * Current microslice index from the microslice descriptor
@@ -219,164 +320,33 @@ private:  // data members
 	 */
   uint64_t fCurMSidx;  //!
 
-  /**
-	 * Global word counter within current microslice
-	 */
-  Int_t fGwordCnt;  //!
-
-  /**
-	 * Flag indicating that we are in the subsubevent
-	 */
-  Bool_t fInSubSubEvent;  //!
-
-  /**
-	 * Current epoch value
-	 */
-  UInt_t fCurEpochCounter;  //!
-
-  /**
-	 * Current subsubevent ID
-	 */
-  Int_t fSubSubEvId;  //!
-
-  /**
-	 * Flag to mark the last DiRICH on a Hub
-	 */
-  Bool_t fLastFeeOnHub = false;
-
-  std::vector<Int_t> fTDCAlignmentErrorPositions;
-
-  Int_t fTdcWordCorrectionCnt = 0;
-
-  Int_t fTdcWordCorrectionGlobalCnt = 0;
-
-  Int_t fSkipCnt = 0;
-
-private:  // Stored timestamps
-  /**
-	 * Full time of the last rising edge from ch 0 of CTS
-	 */
-  Double_t fLastCTSch0_re_time;  //!
-
-  /**
-	 * Full time of the last rising edge from ch 2 of CTS
-	 */
-  Double_t fLastCTSch2_re_time;  //!
-
-  /**
-	 * Full time of the last falling edge from ch 2 of CTS
-	 */
-  Double_t fLastCTSch2_fe_time;  //!
-
-  /**
-	 * Full time of the last rising edge from ch 0 of CTS from the previous microslice
-	 */
-  Double_t fPrevLastCTSch0_re_time;  //!
-
-  /**
-	 * Full time of the last rising edge from ch 2 of CTS from the previous microslice
-	 */
-  Double_t fPrevLastCTSch2_re_time;  //!
-
-  /**
-	 * Full time of the last falling edge from ch 2 of CTS from the previous microslice
-	 */
-  Double_t fPrevLastCTSch2_fe_time;  //!
-
-  /**
-	 * Full time of the last rising edge from ch 0 of each TDC
-	 */
-  TArrayD fLastCh0_re_time;  //!
-
-  /**
-	 * Full time of the previous last rising edge from ch 0 of each TDC (from the previous microslice)
-	 */
-  TArrayD fPrevLastCh0_re_time;  //!
-
-private:  // digi building
-  void ProcessRisingEdge(Int_t subSubEvId, Int_t channel, Double_t time);
-
-  void ProcessFallingEdge(Int_t subSubEvId, Int_t channel, Double_t time);
-
-  /**
-	 * Buffer for rising edges. It is filled during unpacking whenever
-	 * the rising edge is encountered. Afterwards, when a falling edge
-	 * is encountered, corresponding rising edge is searched here and
-	 * removed if found.
-	 */
-  std::vector<CbmMcbmRichEdge> fRisingEdgesBuf;  //! Exclude from ROOT dictionnary due to missing empty constructor!!
-
-  /**
-	 * Buffer of falling edges for which corresponding rising edges were not found
-	 */
-  std::vector<CbmMcbmRichEdge> fFallingEdgesBuf;  //! Exclude from ROOT dictionnary due to missing empty constructor!!
-
 public:  // histograms
   /**
 	 *
 	 */
   Bool_t CreateHistograms();
 
-  /**
-	 *
-	 */
-  //	Bool_t FillHistograms();
-
   /**
 	 *
 	 */
   Bool_t ResetHistograms();
 
-  TH1D* GetTotH1(Int_t fpgaID, Int_t channel);
-  TH2D* GetTotH2(Int_t fpgaID);
-  /*
-	TH2D* fhTDCch0re_minusCTSch0re; //!
-	TH2D* fhTDCch0re_minusCTSch2re; //!
-	TH2D* fhTDCch0re_minusCTSch2fe; //!
-	TH2D* fhTDCch0re_minusPrevCTSch0re; //!
-	TH2D* fhTDCch0re_minusPrevCTSch2re; //!
-	TH2D* fhTDCch0re_minusPrevCTSch2fe; //!
-
-	std::vector<TH2D*> fhTDCre_minusTDCch0re; //!
+  TH1D* GetTotH1(uint32_t fpgaID, uint32_t channel);
+  TH2D* GetTotH2(uint32_t fpgaID);
 
-	std::vector<TH2D*> fhTDCre_minusPrevTDCch0re; //!
-*/
-  std::vector<TH2D*> fhTDCre_corrected1;  //!
+  std::vector<TCanvas*> fcTot2d;
 
-  std::vector<TH2D*> fhTDCre_corrected2;  //!
-
-  std::vector<TCanvas*> fcTot2d;  //!
-
-  TH1* fhVectorSize     = nullptr;
-  TH1* fhVectorCapacity = nullptr;
-
-  //TH2* fhDigisInChnl       = nullptr;
-  //TH2* fhDigisInDiRICH     = nullptr;
-
-  TH2D* fhTdcErrors   = nullptr;
-  TH2D* fhEventErrors = nullptr;
-
-  TH2D* fhDiRICHWords = nullptr;
-  TH2D* fhChnlWords   = nullptr;
+  TH2D* fhTdcErrors = nullptr;
 
   TH1* fhEventSize       = nullptr;
   TH2* fhSubEventSize    = nullptr;
   TH2* fhSubSubEventSize = nullptr;
   TH2* fhChnlSize        = nullptr;
 
-  std::array<unsigned int, 33> fChnlMsgCnt;
-
-  bool fDebugPrint = 0;
   std::map<uint16_t, uint16_t> fMapFEE;
 
-  std::map<Int_t, std::map<Int_t, TH1D*>> fhTotMap;
-
-  std::map<Int_t, TH2D*> fhTot2dMap;
-
-  size_t fuTsMaxVectorSize     = 0;
-  Double_t fdCapacityIncFactor = 1.1;
-
-  inline void SetVectCapInc(Double_t dIncFact) { fdCapacityIncFactor = dIncFact; }
+  std::map<uint32_t, std::map<Int_t, TH1D*>> fhTotMap;
+  std::map<uint32_t, TH2D*> fhTot2dMap;
 
   ClassDef(CbmMcbm2018UnpackerAlgoRich, 1);
 };
diff --git a/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerAlgoRich2020.cxx b/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerAlgoRich2020.cxx
new file mode 100644
index 0000000000..7c276ce115
--- /dev/null
+++ b/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerAlgoRich2020.cxx
@@ -0,0 +1,1882 @@
+/* Copyright (C) 2019-2020 Justus-Liebig-Universitaet Giessen, Giessen
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Egor Ovcharenko [committer], Pierre-Alain Loizeau */
+
+/**
+ * CbmMcbm2018UnpackerAlgoRich20202020
+ * E. Ovcharenko, Mar 2019
+ * based on other detectors' classes by P.-A. Loizeau
+ */
+
+/**
+
+Consider two consequent microslices A and B.
+Each microslice contains one CTS subevent which contains at least 3 timestamp messages.
+A microslice may also contain TRB subevents, each containing also one timestamp message from ch0.
+
+Microslice A
+===================
+CTS   | ch 0 re      = AC0R (stands for ms A, Cts, ch 0, Rising edge)
+CTS   | ch 2 fe
+CTS   | ch 2 re      = AC2R (stands for ms A, Cts, ch 2, Rising edge)
+-------------------
+TDC K | ch 0 re
+-------------------
+TDC Z | ch 0 re      = AZ0R (stands for ms A, tdc Z, ch 0, Rising edge))
+      | ch X re
+      | ch X fe
+      | ...
+-------------------
+...
+===================
+
+Microslice B (next after A)
+===================
+CTS   | ch 0 re
+CTS   | ch 2 fe
+CTS   | ch 2 re
+-------------------
+TDC L | ch 0 re
+-------------------
+TDC Z | ch 0 re
+      | ch Y re      = (T - AZ0R) + corr
+      | ch Y fe
+      | ...
+-------------------
+...
+===================
+
+corr = -(AC2R-AC0R)
+
+Uncorrected full time in ns of the TIMESTAMP message calculated as
+T = epoch*2048.*5. + (coarse)*5. - fine*0.005
+
+Full corrected global time is then computed by adding the microslice
+index from the descriptor to the corrected time:
+
+fullTimeCorr = (T - AZ0R) - (AC2R-AC0R) + MSidx
+
+*/
+
+//TODO: Check that to 'real' actions are performed in the lines which are intended for debug output only.
+//      i.e. LOG(XXXX) << ...
+
+#include "CbmMcbm2018UnpackerAlgoRich2020.h"
+
+// ROOT
+#include <Logger.h>
+
+#include <TCanvas.h>
+#include <TList.h>
+
+// CbmRoot
+#include "CbmMcbm2018RichPar.h"
+
+#include <iostream>
+
+CbmMcbm2018UnpackerAlgoRich2020::CbmMcbm2018UnpackerAlgoRich2020()
+  : CbmStar2019Algo()
+  , fbMonitorMode(kFALSE)
+  , fbDebugMonitorMode(kFALSE)
+  , fRawDataMode(kFALSE)
+  , fError(kFALSE)
+  , fTrbState(TrbNetState::IDLE)
+  , fErrorCorr(0)
+  , fbDoToTCorr(kTRUE)
+  , fSkipMs(kFALSE)
+  , fdTimeOffsetNs(0.0)
+  , fRICHcompIdx(6)
+  ,  //TODO experimentally obtained value
+  fUnpackPar(nullptr)
+  , fTScounter(0)
+  , fCurMSid(0)
+  , fGwordCnt(0)
+  , fInSubSubEvent(kFALSE)
+  , fCurEpochCounter(0)
+  , fSubSubEvId(0)
+  , fLastCTSch0_re_time(0.)
+  , fLastCTSch2_re_time(0.)
+  , fLastCTSch2_fe_time(0.)
+  , fPrevLastCTSch0_re_time(0.)
+  , fPrevLastCTSch2_re_time(0.)
+  , fPrevLastCTSch2_fe_time(0.)
+  , /*,
+	fhTDCch0re_minusCTSch0re(nullptr),
+	fhTDCch0re_minusCTSch2re(nullptr),
+	fhTDCch0re_minusCTSch2fe(nullptr),
+	fhTDCch0re_minusPrevCTSch0re(nullptr),
+	fhTDCch0re_minusPrevCTSch2re(nullptr),
+	fhTDCch0re_minusPrevCTSch2fe(nullptr)*/
+  fMapFEE()
+  , fhTotMap()
+  , fhTot2dMap()
+{
+  this->Init();  //TODO why this is not called by the framework?
+}
+
+CbmMcbm2018UnpackerAlgoRich2020::~CbmMcbm2018UnpackerAlgoRich2020()
+{
+  if (nullptr != fParCList) delete fParCList;
+  if (nullptr != fUnpackPar) delete fUnpackPar;
+}
+
+Bool_t CbmMcbm2018UnpackerAlgoRich2020::Init()
+{
+  LOG(info) << "Initializing mCBM RICH 2019 unpacker algo";
+  //fhDigisInChnl   = new TH2D("fhDigisInChnl","fhDigisInChnl;channel;#Digis;" ,2304 , -0.5, 2303.5,  50, -0.5, 49.5);
+  //fhDigisInDiRICH = new TH2D("fhDigisInDiRICH","fhDigisInDiRICH;DiRICH;#Digis;",72 , -0.5, 71.5,  300, -0.5, 299.5);
+  return kTRUE;
+}
+
+void CbmMcbm2018UnpackerAlgoRich2020::Reset() {}
+
+void CbmMcbm2018UnpackerAlgoRich2020::Finish() {}
+
+Bool_t CbmMcbm2018UnpackerAlgoRich2020::InitContainers()
+{
+  LOG(info) << "Init parameter containers for CbmMcbm2018UnpackerAlgoRich2020";
+  Bool_t initOK = ReInitContainers();
+
+  return initOK;
+}
+
+Bool_t CbmMcbm2018UnpackerAlgoRich2020::ReInitContainers()
+{
+  LOG(info) << "ReInit parameter containers for CbmMcbm2018UnpackerAlgoRich2020";
+
+  fUnpackPar = (CbmMcbm2018RichPar*) fParCList->FindObject("CbmMcbm2018RichPar");
+  if (fUnpackPar == nullptr) { return kFALSE; }
+
+  Bool_t initOK = InitParameters();
+
+  return initOK;
+}
+
+TList* CbmMcbm2018UnpackerAlgoRich2020::GetParList()
+{
+  if (fParCList == nullptr) { fParCList = new TList(); }
+  fUnpackPar = new CbmMcbm2018RichPar("CbmMcbm2018RichPar");
+  fParCList->Add(fUnpackPar);
+
+  return fParCList;
+}
+
+Bool_t CbmMcbm2018UnpackerAlgoRich2020::InitParameters()
+{
+  InitStorage();
+  return kTRUE;
+}
+
+void CbmMcbm2018UnpackerAlgoRich2020::InitStorage()
+{
+  fLastCh0_re_time.Set(fUnpackPar->GetNaddresses());      // Set the size of the array
+  fPrevLastCh0_re_time.Set(fUnpackPar->GetNaddresses());  // Set the size of the array
+}
+
+/**
+  Copied from other detectors without any brain effort...
+  A little bug-fix added
+**/
+void CbmMcbm2018UnpackerAlgoRich2020::AddMsComponentToList(size_t component, UShort_t usDetectorId)
+{
+  /// Check for duplicates and ignore if it is the case
+  for (UInt_t uCompIdx = 0; uCompIdx < fvMsComponentsList.size(); ++uCompIdx) {
+    if (component == fvMsComponentsList[uCompIdx]) { return; }
+  }
+
+  /// Add to list
+  fvMsComponentsList.push_back(component);
+
+  if (fvMsComponentsList.size() == 1) { fRICHcompIdx = component; }
+  else {
+    LOG(WARN) << "fvMsComponentsList.size() > 1 for RICH. Unpacking may not "
+                 "work due to implementation limitations.";
+  }
+
+  LOG(info) << "CbmMcbm2018UnpackerAlgoRich2020::AddMsComponentToList => Component " << component
+            << " with detector ID 0x" << std::hex << usDetectorId << std::dec << " added to list";
+}
+
+Bool_t CbmMcbm2018UnpackerAlgoRich2020::ProcessTs(const fles::Timeslice& /*ts*/)
+{
+  LOG(debug2) << "CbmMcbm2018UnpackerAlgoRich2020::ProcessTs(ts)";
+  /*
+	//TODO: shortcut. We love shortcuts, right?
+	if (fvMsComponentsList.size() == 1) {
+		this->ProcessTs(ts, fvMsComponentsList.at(0));
+	}
+
+	//TODO: implement case when multiple components have to be processed
+*/
+  return kTRUE;
+}
+
+Bool_t CbmMcbm2018UnpackerAlgoRich2020::ProcessTs(const fles::Timeslice& ts, size_t component)
+{
+  /// Ignore First TS as first MS is typically corrupt
+  if (0 == ts.index()) { return kTRUE; }  // if( 0 == ts.index() )
+
+  LOG(debug2) << "CbmMcbm2018UnpackerAlgoRich2020::ProcessTs(ts, " << component << ")";
+
+  //TODO: skip if this method was called for a wrong component
+  //if (component != fRICHcompIdx) return kTRUE;
+  //FIXME: this is really nasty...
+  //	component = fRICHcompIdx;
+  if (1 != fvMsComponentsList.size()) {
+    /// If no RICH component, do nothing!
+    if (0 == fvMsComponentsList.size()) return kTRUE;
+
+    /// If multiple RICH components, fail the run
+    TString sCompList = "";
+    for (UInt_t uMsCompIdx = 0; uMsCompIdx < fvMsComponentsList.size(); ++uMsCompIdx)
+      sCompList += Form(" %2lu ", fvMsComponentsList[uMsCompIdx]);
+    LOG(fatal) << "CbmMcbm2018UnpackerAlgoRich2020::ProcessTs => More than 1 "
+                  "component in list, unpacking impossible! List is "
+               << sCompList;
+  }  // if( 1 != fvMsComponentsList.size() )
+  component = fvMsComponentsList[0];
+
+  LOG(debug) << "Components:  " << ts.num_components();
+  LOG(debug) << "Microslices: " << ts.num_microslices(component);
+
+  const uint64_t compSize = ts.size_component(component);
+  LOG(debug) << "Component " << component << " has size " << compSize;
+
+  /// On first TS, extract the TS parameters from header (by definition stable over time)
+  if (-1.0 == fdTsCoreSizeInNs) {
+    fuNbCoreMsPerTs  = ts.num_core_microslices();
+    fuNbOverMsPerTs  = ts.num_microslices(0) - ts.num_core_microslices();
+    fdTsCoreSizeInNs = fdMsSizeInNs * (fuNbCoreMsPerTs);
+    fdTsFullSizeInNs = fdMsSizeInNs * (fuNbCoreMsPerTs + fuNbOverMsPerTs);
+    LOG(info) << "Timeslice parameters: each TS has " << fuNbCoreMsPerTs << " Core MS and " << fuNbOverMsPerTs
+              << " Overlap MS, for a core duration of " << fdTsCoreSizeInNs << " ns and a full duration of "
+              << fdTsFullSizeInNs << " ns";
+
+    /// Ignore overlap ms if flag set by user
+    fuNbMsLoop = fuNbCoreMsPerTs;
+    if (kFALSE == fbIgnoreOverlapMs) fuNbMsLoop += fuNbOverMsPerTs;
+    LOG(info) << "In each TS " << fuNbMsLoop << " MS will be looped over";
+  }  // if( -1.0 == fdTsCoreSizeInNs )
+
+  for (size_t iMS = 0; iMS < fuNbMsLoop; ++iMS) {
+    fCurMSid = iMS;
+    LOG(debug) << "=======================================================";
+    const fles::MicrosliceView mv            = ts.get_microslice(component, iMS);
+    const fles::MicrosliceDescriptor& msDesc = mv.desc();
+    ////const uint8_t* msContent = mv.content();
+    LOG(debug) << "msDesc.size=" << msDesc.size;
+    fCurMSidx = msDesc.idx;
+    LOG(debug) << "msDesc.idx=" << msDesc.idx;
+    ////mRichSupport::PrintRaw(msDesc.size, msContent);//TODO delete
+    ////LOG(debug) << "=======================================================";
+    ////////////////////////////////
+    //ProcessMs(ts, component, iMS);//
+    ////////////////////////////////
+
+    if (!fRawDataMode) ProcessMs(ts, component, iMS);
+    if (fRawDataMode) DebugMs(ts, component, iMS);
+
+    LOG(debug) << "=======================================================";
+  }
+
+  ///////////////
+  FinalizeTs();  //
+  ///////////////
+
+  if (0 == fTScounter % 1000) { LOG(info) << "Processed " << fTScounter << " TS"; }
+
+  fTScounter++;
+
+  /// Sort the buffers of hits due to the time offsets applied
+  std::sort(fDigiVect.begin(), fDigiVect.end(),
+            [](const CbmRichDigi& a, const CbmRichDigi& b) -> bool { return a.GetTime() < b.GetTime(); });
+
+  if (fbMonitorMode || fbDebugMonitorMode) {
+    fhVectorSize->Fill(ts.index(), fDigiVect.size());
+    fhVectorCapacity->Fill(ts.index(), fDigiVect.capacity());
+  }  // if( fbMonitorMode || fbDebugMonitorMode )
+
+  if (fuTsMaxVectorSize < fDigiVect.size()) {
+    fuTsMaxVectorSize = fDigiVect.size() * fdCapacityIncFactor;
+    fDigiVect.shrink_to_fit();
+    fDigiVect.reserve(fuTsMaxVectorSize);
+  }  // if( fuTsMaxVectorSize < fDigiVect.size() )
+
+  return kTRUE;
+}
+
+Bool_t CbmMcbm2018UnpackerAlgoRich2020::ProcessMs(const fles::Timeslice& ts, size_t uMsCompIdx, size_t uMsIdx)
+{
+  const fles::MicrosliceView mv            = ts.get_microslice(uMsCompIdx, uMsIdx);
+  const fles::MicrosliceDescriptor& msDesc = mv.desc();
+  const uint8_t* ptr                       = mv.content();
+  const size_t size                        = msDesc.size;
+
+  if (size == 0) return kTRUE;
+
+  fGwordCnt = 0;  //TODO check that this global word counter works properly
+
+  Int_t offset;  // offset in bytes
+  Int_t* dataPtr;
+
+  offset = 0;
+  mRichSupport::SwapBytes(4, ptr + offset);
+  LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
+              << "ok"
+              << "\t"
+              << "Reserved 0000 0000";
+  fGwordCnt++;
+
+  offset = 4;
+  mRichSupport::SwapBytes(4, ptr + offset);
+  // clang-format off
+  dataPtr               = (Int_t*) (ptr + offset);
+  Int_t mbsNumber       = (Int_t)(dataPtr[0] & 0xffffff);
+  uint8_t mts_error_msg = (uint8_t)((dataPtr[0] >> 24) & 0xff);
+  // clang-format on
+  ErrorMsg(static_cast<uint16_t>(mts_error_msg), RichErrorType::mtsError);
+  LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
+              << "ok"
+              << "\t"
+              << "mbsNumber = " << mbsNumber;
+  fGwordCnt++;
+
+  // We suppose that the first word is
+  // "HadesTransportUnitQueue - Length"
+  offset = 0 + 8;
+  mRichSupport::SwapBytes(4, ptr + offset);
+  // clang-format off
+  dataPtr             = (Int_t*) (ptr + offset);
+  Int_t TRBeventSize1 = (Int_t)(dataPtr[0]);
+  // clang-format on
+  LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
+              << "ok"
+              << "\t"
+              << "HadesTransportUnitQueue - Length = " << TRBeventSize1;
+  fGwordCnt++;
+
+  if (*dataPtr > 0 && UInt_t(*dataPtr) == 0x80030000) {
+    LOG(info) << "dataPtr == 0x80030000";
+    exit(EXIT_FAILURE);
+  }
+
+  // We suppose that the second word is
+  // "HadesTransportUnitQueue - Decoder  (Seems to be allways the same)"
+  offset = 4 + 8;
+  mRichSupport::SwapBytes(4, ptr + offset);
+  // clang-format off
+  dataPtr    = (Int_t*) (ptr + offset);
+  Int_t dcdr = (Int_t)(dataPtr[0]);
+  // clang-format on
+  if (dcdr == 0x00030062) {
+    LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
+                << "ok"
+                << "\t"
+                << "HadesTransportUnitQueue - Decoder = " << dcdr;
+    fGwordCnt++;
+  }
+  else {
+    LOG(warning) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
+                 << "er"
+                 << "\t"
+                 << "HadesTransportUnitQueue - Decoder = " << dcdr << " is not 0x00030062 (196706) => 0x" << std::hex
+                 << dcdr << std::dec;
+    fGwordCnt++;
+
+    /// Probably corrupted MS, stop there and skip remaining data
+    fSkipMs = kTRUE;
+    return kFALSE;
+  }
+
+  // We suppose that the third word is
+  // TRB event length (in bytes)
+  // It should be 8 less than the size specified two words ago
+  offset = 8 + 8;
+  mRichSupport::SwapBytes(4, ptr + offset);
+  // clang-format off
+  dataPtr             = (Int_t*) (ptr + offset);
+  Int_t TRBeventSize2 = (Int_t)(dataPtr[0]);
+  // clang-format on
+  if (TRBeventSize2 == TRBeventSize1 - 8) {
+    LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
+                << "ok"
+                << "\t"
+                << "TRB event - Length = " << TRBeventSize2 << " == " << TRBeventSize1 << "-8";
+    fGwordCnt++;
+  }
+  else {
+    LOG(debug) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
+               << "er"
+               << "\t"
+               << "TRB event - Length = " << TRBeventSize2 << " != " << TRBeventSize1 << "-8=" << TRBeventSize1 - 8;
+    fGwordCnt++;
+  }
+
+  /////////////////////////////////////////////
+  ProcessTRBevent(TRBeventSize2, ptr + offset);  //
+  /////////////////////////////////////////////
+
+  // Bytes in a TrbEvent
+  if (fbDebugMonitorMode) fhEventSize->Fill(TRBeventSize2);
+
+
+  if (fSkipMs == kTRUE) {
+    // problem in data; delete vectors.
+    fDigiVect.clear();
+    fSkipMs = kFALSE;
+  }
+
+
+  return kTRUE;
+}
+
+Int_t CbmMcbm2018UnpackerAlgoRich2020::ProcessTRBevent(size_t const size, uint8_t const* const ptr)
+{
+  Int_t offset;  // offset in bytes
+  Int_t* dataPtr;
+
+  // We assume that the TRB event header is 4 words and
+  // the first word is already processed outside of this method
+
+  //////////////////////////////////
+  ProcessTRBeventHeader(4 * 4, ptr);  //
+  //////////////////////////////////
+
+  offset = 16;  // start from after the TRB event header
+
+  // 1. Search for the CTS subevent and extract reference time
+
+  while (static_cast<size_t>(offset) < size) {
+    /// Escape bad MS before doing anything
+    if (fSkipMs == kTRUE) break;
+
+    // Duplicate the header word in order to avoid corrupting (by bytes swapping)
+    // the original data
+    // clang-format off
+    dataPtr          = (Int_t*) (ptr + offset);
+    
+    Int_t headerCopy = *dataPtr;
+    mRichSupport::SwapBytes(4, (uint8_t*) &headerCopy);
+    dataPtr = &headerCopy;
+
+    Int_t SubEvSize = (Int_t) ((dataPtr[0] >> 16) & 0xffff);
+    Int_t HubId     = (Int_t) ((dataPtr[0]) & 0xffff);
+    // clang-format on
+    // Process only CTS subevent
+    //FIXME change from 0xc001 to 0xc000 at some point // ?
+    if ((HubId == 0xc001) || (HubId == 0xc000)) {
+
+      // Not a very nice shortcut
+      // The global counter of the words is incremented for the CTS subevent header here
+      // However for the TRB subevent headers it is incremented in the second run,
+      // where only TRB subevent headers are processed and the CTS subevents are skipped
+      LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr((uint8_t*) &headerCopy) << "\t"
+                  << "ok"
+                  << "\t"
+                  << "hub ID = 0x" << mRichSupport::GetHexRepresentation(2, (uint8_t*) &headerCopy) << "\t"
+                  << "subevent size = " << SubEvSize;
+      fGwordCnt++;
+
+      fSubSubEvId = HubId;
+      //////////////////////////////////////////////////////////////
+      offset += (4 + ProcessCTSsubevent(SubEvSize * 4, ptr + offset));  //
+      //////////////////////////////////////////////////////////////
+
+      //std::cout<<"Words in CTS 0x"<< std::hex << HubId << std::dec <<" : "<< SubEvSize <<std::endl;
+      // In principle, should be reset here for safety
+      fSubSubEvId = 0;
+    }
+    else {
+      // Skip all other subevents
+      offset += (4 + SubEvSize * 4);
+    }
+  }
+
+  offset = 16;  // start from after the TRB event header again
+
+  // 2. Process TRB subsubevents
+
+  //Int_t iIter = 0;
+  while (static_cast<size_t>(offset) < size) {
+    /// Escape bad MS before doing anything
+    if (fSkipMs == kTRUE) break;
+
+    //std::cout << "SE iteration " << iIter++ << "\toffset=" << offset << "\tsize=" << size << std::endl;
+
+    // We suppose that the fifth word is the header of the subevent
+    // <Length> <HubId>
+    mRichSupport::SwapBytes(4, ptr + offset);
+    // clang-format off
+    dataPtr         = (Int_t*) (ptr + offset);
+    Int_t SubEvSize = (Int_t)((dataPtr[0] >> 16) & 0xffff);
+    Int_t HubId     = (Int_t)((dataPtr[0]) & 0xffff);
+    // clang-format on
+    //FIXME change from 0xc001 to 0xc000 at some point // ?
+    if ((HubId == 0xc001) || (HubId == 0xc000)) {
+      ////fSubSubEvId = HubId;
+      //////////////////////////////////////////////////////////////////
+      ////offset += (4 + ProcessCTSsubevent(SubEvSize*4, ptr+offset));//
+      //////////////////////////////////////////////////////////////////
+      ////// In principle, should be reset here for safety
+      ////fSubSubEvId = 0;
+
+      // Skip CTS subevent as it has been already processed during the first run
+      offset += (4 + SubEvSize * 4);
+      fLastFeeOnHub = false;
+    }
+    else if (HubId == 0x5555) {
+      LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
+                  << "ok"
+                  << "\t"
+                  << "hub ID = 0x" << mRichSupport::GetHexRepresentation(2, ptr + offset) << "\t"
+                  << "subevent size = " << SubEvSize;
+      fGwordCnt++;
+      fLastFeeOnHub = false;
+      //TODO one could implement additional checks here about the
+      // words coming after the "event end" but we skip everything by now.
+      ///////////////////////////////////////////////////////////////
+      offset += (4 + ProcessSKIPsubevent(SubEvSize * 4, ptr + offset));  //
+      ///////////////////////////////////////////////////////////////
+    }
+    else if (((HubId >> 8) & 0xFF) == 0x82) {
+      LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
+                  << "ok"
+                  << "\t"
+                  << "hub ID = 0x" << mRichSupport::GetHexRepresentation(2, ptr + offset) << "\t"
+                  << "subevent size = " << SubEvSize;
+      fGwordCnt++;
+      fLastFeeOnHub = false;
+      //std::cout<<"Hub: "<<std::hex<<HubId <<std::dec<<"   Size:"<< SubEvSize<<std::endl;
+      //////////////////////////////////////////////////////////////
+      offset += (4 + ProcessTRBsubevent(SubEvSize * 4, ptr + offset));  //
+      //////////////////////////////////////////////////////////////
+
+      //std::cout<<"Words in Hub 0x"<< std::hex << HubId << std::dec <<" : "<< SubEvSize <<std::endl;
+      // Bytes in a Hub
+      if (fbDebugMonitorMode) {
+        uint16_t combiner_address = ((HubId >> 4) & 0xF) * 3 + (HubId & 0xF);
+        fhSubEventSize->Fill(combiner_address, (SubEvSize * 4));
+      }
+    }
+    else {
+      LOG(WARN) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
+                << "ok"
+                << "\t"
+                << "hub ID = 0x" << mRichSupport::GetHexRepresentation(2, ptr + offset) << "\t"
+                << "subevent size = " << SubEvSize << "\n"
+                << "This is not a valid Combiner Id!"
+                << "\n"
+                << "prev prev2:" << mRichSupport::GetWordHexRepr(ptr + offset - 12) << "\n"
+                << "prev prev: " << mRichSupport::GetWordHexRepr(ptr + offset - 8) << "\n"
+                << "prev:      " << mRichSupport::GetWordHexRepr(ptr + offset - 4) << "\n"
+                << "next:      " << mRichSupport::GetWordHexRepr(ptr + offset + 4) << "\n"
+                << "next next: " << mRichSupport::GetWordHexRepr(ptr + offset + 8) << "\n";
+      //////////////////////////////////////////////////////////////
+      offset += (4 + SubEvSize * 4);
+      //////////////////////////////////////////////////////////////
+    }
+  }
+
+  ////LOG(debug4) <<  "Done processing TRB event. offset=" << offset << "\tsize=" << size;
+  //TODO implement checks
+  if (size != static_cast<size_t>(offset)) {
+    LOG(WARN) << "CbmMcbm2018UnpackerAlgoRich2020::ProcessTRBevent() warning:"
+              << "Number of processed bytes is not equal to the expected size. "
+                 "This should not happen. ("
+              << size << " VS " << offset << ")";
+  }
+
+  return size;  //TODO check
+}
+
+// Process TRB event header.
+// Input arguments are the size of the TRB event header (16 bytes) and the pointer to the first word.
+// Note that the first word can already be analysed outside of this method.
+// Return number of bytes processed. For this particular method the value of the input 'size' argument
+// is returned as we expect that the TRB header is always 16 bytes.
+Int_t CbmMcbm2018UnpackerAlgoRich2020::ProcessTRBeventHeader(size_t const size, uint8_t const* const ptr)
+{
+  Int_t offset;  // offset in bytes
+  Int_t* dataPtr;
+
+  // Skip first word (already processed outside)
+  //offset = 0;
+  // do nothing
+
+  // We suppose that the second word consists of
+  // 0002 - number of following word till the Event Data Starts (should be the same)
+  // 00<TriggerType>1 - value in [7:4] defines TriggerType
+  offset = 4;
+  mRichSupport::SwapBytes(4, ptr + offset);
+  // clang-format off
+  dataPtr           = (Int_t*) (ptr + offset);
+  Int_t checkSize   = (Int_t)((dataPtr[0] >> 16) & 0xffff);
+  Int_t triggerType = (Int_t)((dataPtr[0] >> 4) & 0xf);
+  // clang-format on
+  if (checkSize == 2) {
+    LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
+                << "ok"
+                << "\t"
+                << "checkSize == 2"
+                << "\t"
+                << "trigger type = " << triggerType;
+    fGwordCnt++;
+  }
+  else {
+    LOG(warning) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
+                 << "er"
+                 << "\t"
+                 << "checkSize != 2 (" << checkSize << ")\t"
+                 << "trigger type = " << triggerType;
+    fGwordCnt++;
+
+    /// Probably corrupted MS, stop there and skip remaining data
+    fSkipMs = kTRUE;
+    return 0;
+  }
+
+  /*for (size_t iWord=2; iWord<size; iWord++) {
+		offset = iWord*4;
+		LOG(debug4) << "\t" << GetWordHexRepr(ptr+offset);
+	}*/
+
+  // We suppose that the third word consists of
+  // 0000 <SubEventId>
+  offset = 8;
+  mRichSupport::SwapBytes(4, ptr + offset);
+  // clang-format off
+  dataPtr          = (Int_t*) (ptr + offset);
+  Int_t checkBytes = (Int_t)((dataPtr[0] >> 16) & 0xffff);
+  // clang-format on
+  //	Int_t SubEvId = (Int_t)((dataPtr[0]) & 0xffff);
+  if (checkBytes == 0) {
+    LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
+                << "ok"
+                << "\t"
+                << "checkBytes == 0"
+                << "\t"
+                << "subevent ID = 0x" << mRichSupport::GetHexRepresentation(2, ptr + offset);
+    fGwordCnt++;
+  }
+  else {
+    LOG(warning) << "[" << fGwordCnt++ << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
+                 << "er"
+                 << "\t"
+                 << "checkBytes != 0 (" << checkBytes << ")\t"
+                 << "subevent ID = 0x" << mRichSupport::GetHexRepresentation(2, ptr + offset);
+    fGwordCnt++;
+
+    /// Probably corrupted MS, stop there and skip remaining data
+    fSkipMs = kTRUE;
+    return 0;
+  }
+
+  // We suppose that the fourth word is the trigger number
+  offset = 12;
+  mRichSupport::SwapBytes(4, ptr + offset);
+  // clang-format off
+  dataPtr           = (Int_t*) (ptr + offset);
+  UInt_t TriggerNum = (UInt_t)(dataPtr[0]);
+  // clang-format on
+  LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
+              << "ok"
+              << "\t"
+              << "trigger num = " << TriggerNum;
+  fGwordCnt++;
+
+  return size;
+}
+
+Int_t CbmMcbm2018UnpackerAlgoRich2020::ProcessSKIPsubevent(size_t const size, uint8_t const* const ptr)
+{
+  ////LOG(debug4) << "ProcessSKIPsubevent size=" << size << " bytes";
+
+  Int_t offset;    // offset in bytes
+  Int_t* dataPtr;  //(FU) not used
+  uint16_t SubEventError = 0;
+
+  // Skip first word (already processed outside)
+  offset = 4;
+
+  //Start Error identification
+  mRichSupport::SwapBytes(4, ptr + offset);
+  // clang-format off
+  dataPtr       = (Int_t*) (ptr + offset);
+  SubEventError = (uint16_t)((dataPtr[0] >> 16) & 0xffff);
+  // clang-format on
+  ErrorMsg(static_cast<uint16_t>(SubEventError), RichErrorType::subEventError);
+
+  offset = 8;
+  //End Error identification
+
+  while (static_cast<size_t>(offset) < size + 4) {
+    mRichSupport::SwapBytes(4, ptr + offset);
+    //                dataPtr = (Int_t*)(ptr+offset); (FU) not used
+    LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset);
+    fGwordCnt++;
+
+    offset += 4;
+  }
+
+  ////LOG(debug4) << "Done processing SKIP subevent. offset=" << offset << "\tsize=" << size;
+  //TODO implement checks
+  if (size != static_cast<size_t>(offset - 4)) {
+    LOG(WARN) << "CbmMcbm2018UnpackerAlgoRich2020::ProcessSKIPsubevent() warning:"
+              << "Number of processed bytes is not equal to the expected size. "
+                 "This should not happen.";
+  }
+
+  return size;  //TODO check
+}
+
+Int_t CbmMcbm2018UnpackerAlgoRich2020::ProcessCTSsubevent(size_t const size, uint8_t const* const ptr)
+{
+  ////LOG(debug4) << "ProcessCTSsubevent size=" << size << " bytes";
+
+  Int_t offset;  // offset in bytes
+  Int_t* dataPtr;
+
+  // Skip first word (already processed outside)
+
+  // We suppose that the second word is the header of the subsubevent
+  offset = 4;
+  mRichSupport::SwapBytes(4, ptr + offset);
+  // clang-format off
+  dataPtr = (Int_t*) (ptr + offset);
+  // clang-format on
+  LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
+              << "ok"
+              << "\t"
+              << "CTS header";
+  fGwordCnt++;
+
+  /*      (FU) not used
+	Short_t trigState[16];
+	for (Int_t i=0; i<16; i++) {
+		trigState[i] = ((*dataPtr >> i) & 0x1); // 16 x 1 bit
+	}
+*/
+  Short_t nInp         = ((*dataPtr >> 16) & 0xf);   // 4 bits
+  Short_t nTrigCh      = ((*dataPtr >> 20) & 0x1f);  // 5 bits
+  Short_t inclLastIdle = ((*dataPtr >> 25) & 0x1);   // 1 bit
+  Short_t inclTrigInfo = ((*dataPtr >> 26) & 0x1);   // 1 bit
+  Short_t inclTS       = ((*dataPtr >> 27) & 0x1);   // 1 bit
+  Short_t ETM          = ((*dataPtr >> 28) & 0x3);   // 2 bits
+
+  // in words (not bytes)
+  Short_t CTSinfo_size = nInp * 2 + nTrigCh * 2 + inclLastIdle * 2 + inclTrigInfo * 3 + inclTS;
+  switch (ETM) {
+    case 0: break;
+    case 1: CTSinfo_size += 1; break;
+    case 2: CTSinfo_size += 4; break;
+    case 3:
+      LOG(debug) << "ETM == 3";
+      //TODO implement
+      break;
+  }
+
+  LOG(debug) << "CTS information size (extracted from the CTS header): " << CTSinfo_size;
+
+  offset = 8;
+
+  while (offset - 8 < CTSinfo_size * 4) {
+    mRichSupport::SwapBytes(4, ptr + offset);
+    // clang-format off
+    dataPtr       = (Int_t*) (ptr + offset);
+    ULong_t MSidx = 102400UL * ((ULong_t)(*dataPtr) - 1);
+    // clang-format on
+    LOG(debug) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
+               << "ok"
+               << "\t"
+               << "CTS information"
+               << " MSidx=" << MSidx;
+    fGwordCnt++;
+
+    offset += 4;
+  }
+
+  // size - full size including CTS header word, CTS informations words (CTSinfo_size) and TCD data
+  // Thus TDC data size = full size - 1 word (header) - CTSinfo_size words (CTS informations)
+  ///////////////////////////////////////////////////////////////////////////
+  fChnlMsgCnt.fill(0);
+  offset += (ProcessTRBsubsubevent((size - (1 + CTSinfo_size) * 4), ptr + offset, 0, 0));  //
+  ///////////////////////////////////////////////////////////////////////////
+
+  ////LOG(debug4) << "Done processing CTS subevent. offset-4=" << offset-4 << "\tsize=" << size;
+  //TODO implement checks
+  if (size != static_cast<size_t>(offset - 4)) {
+    LOG(WARN) << "CbmMcbm2018UnpackerAlgoRich2020::ProcessCTSsubevent() warning:"
+              << "Number of processed bytes is not equal to the expected size. "
+                 "This should not happen.";
+  }
+
+  return size;  //TODO check
+}
+
+Int_t CbmMcbm2018UnpackerAlgoRich2020::ProcessTRBsubevent(size_t const size, uint8_t const* const ptr)
+{
+  ////LOG(debug4) << "ProcessTRBsubevent size=" << size << " bytes";
+
+  Int_t offset;  // offset in bytes
+  Int_t* dataPtr;
+
+  // Skip first word (already processed outside)
+  offset = 4;
+
+  fTdcWordCorrectionCnt = 0;
+
+  findTDCAlignmentError(ptr, size);
+
+  //Int_t iIter = 0;
+  while (static_cast<size_t>(offset) < (size - 2)) {  // test for cases with odd number of corrections
+    if (fSkipMs == kTRUE) break;
+    //std::cout << "SSE iteration " << iIter++ << "\toffset=" << offset << "\tsize=" << size << std::endl;
+
+    //correct for misalignment
+    if (fTDCAlignmentErrorPositions.size() > static_cast<unsigned int>(fTdcWordCorrectionCnt)
+        && fTDCAlignmentErrorPositions[fTdcWordCorrectionCnt] == offset) {
+      //std::cout<<"Correction in DiRICH Header: "<< fTDCAlignmentErrorPositions[fTdcWordCorrectionCnt]<<std::endl;
+      offset += 2;
+      fTdcWordCorrectionCnt++;
+    }
+
+    // We suppose that the second word is the header of the subsubevent
+    // <Length> <SubSubEv.Id>
+    mRichSupport::SwapBytes(4, ptr + offset);
+    // clang-format off
+    dataPtr            = (Int_t*) (ptr + offset);
+    Int_t SubSubEvSize = (Int_t)((dataPtr[0] >> 16) & 0xffff);
+    fSubSubEvId        = (Int_t)((dataPtr[0]) & 0xffff);
+    // clang-format on
+    //check if it is the last DiRICH in the Hub Data stream
+    //TODO CHECK!
+    if ((static_cast<size_t>(offset) + SubSubEvSize * 4) >= size) {
+      LOG(debug) << "Last DiRICH on HUB";
+      fLastFeeOnHub = true;
+    }
+
+    if (((fSubSubEvId >> 12) & 0xF) != 0x7) {
+      LOG(error) << mRichSupport::GetWordHexRepr(ptr + offset - 12) << "\t"
+                 << "er"
+                 << "\t"
+                 << "ILLEGAL SubSubEvent Id  prev";
+      LOG(error) << mRichSupport::GetWordHexRepr(ptr + offset - 8) << "\t"
+                 << "er"
+                 << "\t"
+                 << "ILLEGAL SubSubEvent Id  prev";
+      LOG(error) << mRichSupport::GetWordHexRepr(ptr + offset - 4) << "\t"
+                 << "er"
+                 << "\t"
+                 << "ILLEGAL SubSubEvent Id  prev";
+      LOG(error) << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
+                 << "er"
+                 << "\t"
+                 << "ILLEGAL SubSubEvent Id  "
+                 << "Offset:" << static_cast<size_t>(offset) << "  Size:" << size;
+      LOG(error) << mRichSupport::GetWordHexRepr(ptr + offset + 4) << "\t"
+                 << "er"
+                 << "\t"
+                 << "ILLEGAL SubSubEvent Id next";
+      LOG(error) << mRichSupport::GetWordHexRepr(ptr + offset + 8) << "\t"
+                 << "er"
+                 << "\t"
+                 << "ILLEGAL SubSubEvent Id next";
+      LOG(error) << mRichSupport::GetWordHexRepr(ptr + offset + 12) << "\t"
+                 << "er"
+                 << "\t"
+                 << "ILLEGAL SubSubEvent Id next";
+    }
+
+    LOG(debug) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr + offset) << "\t"
+               << "ok"
+               << "\t"
+               << "subsubevent ID (FPGA ID) = 0x" << mRichSupport::GetHexRepresentation(2, ptr + offset) << "\t"
+               << "subsubevent size = " << SubSubEvSize << " | HUB Offset:" << static_cast<size_t>(offset)
+               << "  Size:" << size;
+    fGwordCnt++;
+
+    if (size + 4 < static_cast<size_t>(offset + 4 + SubSubEvSize * 4 - fTdcWordCorrectionCnt * 2)) {
+      LOG(WARN) << "CbmMcbm2018UnpackerAlgoRich2020::ProcessTRBsubevent() warning:"
+                << "SubEvent out of bounds. This should not happen. (" << size << " VS "
+                << (offset + 4 + SubSubEvSize * 4 - fTdcWordCorrectionCnt * 2) << ")";
+
+      /// Probably corrupted MS, stop there and skip remaining data
+      //fSkipMs = kTRUE;
+    }
+
+    fChnlMsgCnt.fill(0);
+
+    // Add 4 bytes which correspond to the header word
+    //////////////////////////////////////////////////////////////////////
+    offset += (4 + ProcessTRBsubsubevent(SubSubEvSize * 4, ptr + offset + 4, offset + 4, size));  //
+    //////////////////////////////////////////////////////////////////////
+
+    //std::cout<<"Words in DiRICH 0x"<< std::hex << fSubSubEvId << std::dec <<" : "<< SubSubEvSize <<std::endl;
+
+    if (fbDebugMonitorMode) {
+      //This address calculation is just for mCBM; will be a problem when using full CBM RICH acceptance
+      uint16_t DiRICH_address = ((fSubSubEvId >> 8) & 0xF) * 18 + ((fSubSubEvId >> 4) & 0xF) * 2 + (fSubSubEvId & 0xF);
+      fhSubSubEventSize->Fill(DiRICH_address,
+                              SubSubEvSize);  // Words in a DiRICH
+
+      //Words per channel
+      for (size_t i = 1; i < fChnlMsgCnt.size(); ++i) {
+        if (fChnlMsgCnt.at(i) > 0) fhChnlSize->Fill(static_cast<int>(i), fChnlMsgCnt.at(i));
+      }
+    }
+
+    // In principle, should be reset here for safety
+    fSubSubEvId = 0;
+  }
+
+  if (static_cast<Int_t>(fTDCAlignmentErrorPositions.size()) != fTdcWordCorrectionCnt)
+    std::cout << "Missing Correction" << std::endl;
+
+  // 	if (fTDCAlignmentErrorPositions.size() > 0){
+  // 		std::cout<<"Offset : "<<offset-4<<"   Size:"<< size <<std::endl;
+  // 		std::cout<<"END of Hub    : "<<mRichSupport::GetWordHexRepr(ptr+offset-4)<<std::endl; // Last word, processed as TDCWord
+  // 		std::cout<<"END of Hub +1 : "<<mRichSupport::GetWordHexRepr(ptr+offset+0)<<std::endl;
+  // 		std::cout<<"END of Hub +2 : "<<mRichSupport::GetWordHexRepr(ptr+offset+4)<<std::endl;
+  // 		std::cout<<"END of Hub +3 : "<<mRichSupport::GetWordHexRepr(ptr+offset+8)<<std::endl;
+  // 	}
+
+  ////LOG(debug4) << "Done processing TRB subevent. offset-4=" << offset-4 << "\tsize=" << size;
+  if (size != static_cast<size_t>(offset - 4)) {
+    LOG(WARN) << "CbmMcbm2018UnpackerAlgoRich2020::ProcessTRBsubevent() warning:"
+              << "Number of processed bytes is not equal to the expected size. "
+                 "This should not happen. ("
+              << size << " VS " << (offset - 4) << ")"
+              << "  Correction: " << fTdcWordCorrectionCnt * 2 << "  fLastFeeOnHub:" << fLastFeeOnHub;
+
+    /// Probably corrupted MS, stop there and skip remaining data
+    //fSkipMs = kTRUE;
+  }
+
+  fTdcWordCorrectionGlobalCnt += fTdcWordCorrectionCnt;
+
+  return size;  //TODO check
+}
+
+Int_t CbmMcbm2018UnpackerAlgoRich2020::ProcessTRBsubsubevent(size_t const size, uint8_t const* const ptr,
+                                                             Int_t const hubOffset, size_t const hubSize)
+{  //size: Size of Data from DiRICH in Bytes
+  ////LOG(debug4) << "ProcessTRBsubsubevent size=" << size  << " bytes";
+  Int_t offset                  = 0;       // offset in bytes
+  fCurEpochCounter              = 0;       //TODO check
+  fInSubSubEvent                = kFALSE;  //TODO check
+  fTrbState                     = TrbNetState::IDLE;
+  Int_t TdcWordCorrection_local = 0;
+  Int_t WordCnt                 = 0;
+  bool break_flag               = false;
+
+  for (size_t iWord = 0; iWord < size / 4; iWord++) {  // iWord is size in Lines
+    //correct for misalignment
+    //hubOffset is pointing to first word after DiRICH address
+    if (fTDCAlignmentErrorPositions.size() > static_cast<unsigned int>(fTdcWordCorrectionCnt)
+        && fTDCAlignmentErrorPositions[fTdcWordCorrectionCnt] == static_cast<Int_t>(hubOffset + offset + iWord * 4)) {
+      //BEGIN DEBUG
+      //                 std::cout<<"DEBUG -1: "<< mRichSupport::GetWordHexRepr(ptr-hubOffset+fTDCAlignmentErrorPositions[fTdcWordCorrectionCnt]-4)   << std::endl;
+      //                 std::cout<<"DEBUG  0: "<< mRichSupport::GetWordHexRepr(ptr-hubOffset+fTDCAlignmentErrorPositions[fTdcWordCorrectionCnt])   << std::endl;
+      //                 std::cout<<"DEBUG +1: "<< mRichSupport::GetWordHexRepr(ptr-hubOffset+fTDCAlignmentErrorPositions[fTdcWordCorrectionCnt]+4)   << std::endl;
+      //                 std::cout<<"DEBUG_  : "<< mRichSupport::GetWordHexRepr(ptr+iWord*4+offset)   << std::endl;
+      //
+      //                 std::cout<<"Correction in DiRICH Header: "<< fTDCAlignmentErrorPositions[fTdcWordCorrectionCnt]<<std::endl;
+
+      //END   DEBUG
+      offset += 2;
+      fTdcWordCorrectionCnt++;
+      TdcWordCorrection_local++;
+      // repeat word
+      iWord--;
+      continue;
+    }
+    if (fSkipMs == kTRUE) break;
+
+
+    //if (fTDCAlignmentErrorPositions.size() > 0 && fLastFeeOnHub) std::cout<<"Final Word: "<< mRichSupport::GetWordHexRepr(ptr+iWord*4+offset)<<std::endl;
+
+    //////////////////////////////
+    if ((hubSize > 0) && (hubOffset + offset + iWord * 4 > hubSize)) {
+      //std::cout<<"BREAKING   : "<<hubOffset+offset+iWord*4 <<" > "<<  hubSize <<" | "<< offset << " | "<< fTdcWordCorrectionCnt <<std::endl;
+      break_flag = true;
+      break;
+    }
+    //if (isCTSWord) std::cout<<"TDCWORD: "<<mRichSupport::GetWordHexRepr(ptr+iWord*4+offset)<<std::endl;
+
+    mRichSupport::SwapBytes(4, ptr + iWord * 4 + offset);
+    ProcessTDCword(ptr + iWord * 4 + offset, iWord, size);  //
+
+    WordCnt++;
+
+    //std::cout<<"   "<< iWord <<"  "<< WordCnt <<std::endl;
+  }  //END of for Loop
+
+  // 	if (fTdcWordCorrectionCnt > 0){
+  // 		std::cout<<"LAST Processed Word    : "<<mRichSupport::GetWordHexRepr(ptr+(WordCnt-1)*4+offset)<<std::endl;
+  // 	}
+  //if (TdcWordCorrection_local != 0) printf(" --- TDC WORD FIX APPLIED ! --- [DiRICH : 0x%4x]\n",fSubSubEvId);
+
+  if (fSkipMs == kTRUE) return 0;
+
+  //TODO Implement checks that the first word was the header and the last word was the trailer
+
+  //if (size != static_cast<size_t>((WordCnt)*4) && fTdcWordCorrectionCnt == 0) {
+  if (!((!break_flag && ((size) == static_cast<size_t>((WordCnt) *4)))
+        || (break_flag && ((size - (fTdcWordCorrectionCnt * 2)) == static_cast<size_t>((WordCnt) *4))))) {
+    LOG(WARN) << "CbmMcbm2018UnpackerAlgoRich2020::ProcessTRBsubsubevent() warning:"
+              << "Number of processed bytes is not equal to the expected size. "
+                 "This should not happen."
+              << static_cast<size_t>(WordCnt * 4) << "   " << size;
+    /// Probably corrupted MS, stop there and skip remaining data
+    //fSkipMs = kTRUE;
+  }
+
+
+  return (WordCnt * 4 + offset);  //TODO check
+}
+
+Int_t CbmMcbm2018UnpackerAlgoRich2020::ProcessTDCword(uint8_t const* const ptr, Int_t const word, size_t const size)
+{
+  // clang-format off
+  Int_t* tdcDataPtr       = (Int_t*) ptr;
+  Int_t tdcData           = tdcDataPtr[0];
+  Int_t tdcTimeDataMarker = (tdcData >> 31) & 0x1;  // 1 bit
+  // clang-format on
+
+  bool errorInData = false;
+
+  // A TDC Time i only valid after a EPOCH or another TDC value
+  if ((tdcTimeDataMarker == 0x1 && fTrbState == TrbNetState::TDC)
+      || (tdcTimeDataMarker == 0x1 && fTrbState == TrbNetState::EPOCH)) {
+    UInt_t tdcMarker = (tdcData >> 29) & 0x7;  // 3 bits
+    if (tdcMarker == 0x4 || tdcMarker == 0x5) {
+      fDebugPrint = 0;
+      ////////////////////////////////
+      ProcessTimestampWord(tdcData);  //
+      ////////////////////////////////
+      fTrbState = TrbNetState::TDC;
+    }
+    else {
+      std::cout << "wrong TDC Word!!" << std::endl;
+      errorInData = true;
+    }
+  }
+  else {
+    UInt_t tdcMarker = (tdcData >> 29) & 0x7;  // 3 bits
+
+    if (tdcMarker == 0x0) {  // TDC trailer
+      if (fInSubSubEvent) {
+        if (!(fTrbState == TrbNetState::HEADER || fTrbState == TrbNetState::EPOCH || fTrbState == TrbNetState::TDC)) {
+          LOG(error) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr) << "\t"
+                     << "er"
+                     << "\t"
+                     << "ILLEGAL TRAILER Position";
+          errorInData = true;
+        }
+        else if ((size / 4 - static_cast<size_t>(word)) > 1) {
+          //Trailer only at end of SubSubEvent!
+          LOG(error) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr) << "\t"
+                     << "er"
+                     << "\t"
+                     << "Trailer only at end of SubSubEvent!" << size / 4 << "  " << static_cast<size_t>(word);
+          errorInData = true;
+        }
+        else {
+          fTrbState = TrbNetState::TRAILER;
+
+          LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr) << "\t"
+                      << "ok"
+                      << "\t"
+                      << "TDC TRAILER";
+          //extract TDC Trailer Error
+          uint16_t errorBits = (tdcData) &0xffff;  //16 bits
+          ErrorMsg(errorBits, RichErrorType::tdcTrailer, fSubSubEvId);
+          fInSubSubEvent = kFALSE;  // go out of InSubSubEvent state
+          //fGwordCnt++;
+          fDebugPrint = 0;
+        }
+      }
+      else {
+        LOG(info) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr) << "\t"
+                  << "er"
+                  << "\t"
+                  << "UNKNOWN (TDC TRAILER not after header)";
+        //fSkipMs = kTRUE;
+        errorInData = true;
+        //exit(EXIT_FAILURE); //TODO probably one should get rid of explicit EXIT calls not to ruin unpacking of other detectors?
+      }
+    }
+    else if (tdcMarker == 0x1) {  // TDC header
+      //	UInt_t randomCode = (tdcData >> 16) & 0xff; // 8 bits
+      //	UInt_t errorBits = (tdcData) & 0xffff; //16 bits
+      if (!fInSubSubEvent) {
+        fInSubSubEvent = kTRUE;  // go into InSubSubEvent state
+
+        if (!(fTrbState == TrbNetState::IDLE || fTrbState == TrbNetState::TRAILER)) {
+          LOG(error) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr) << "\t"
+                     << "er"
+                     << "\t"
+                     << "ILLEGAL HEADER Position";
+          errorInData = true;
+        }
+        else if (!((((tdcData >> 8) & 0xFFFFFF) == 0x200096) || (((tdcData >> 8) & 0xFFFFFF) == 0x200095))) {
+          LOG(error) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr) << "\t"
+                     << "er"
+                     << "\t"
+                     << "ILLEGAL HEADER Value";
+          errorInData = true;
+        }
+        else {
+          fTrbState = TrbNetState::HEADER;
+          //extract TDC Header Error
+          uint8_t errorBits = (tdcData) &0xff;  //8 bits
+          ErrorMsg(errorBits, RichErrorType::tdcHeader, fSubSubEvId);
+          LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr) << "\t"
+                      << "ok"
+                      << "\t"
+                      << "TDC HEADER";
+        }
+        //fGwordCnt++;
+      }
+      else {
+        LOG(info) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr) << "\t"
+                  << "er"
+                  << "\t"
+                  << "UNKNOWN (TDC HEADER not after trailer)";
+        errorInData = true;
+        //fGwordCnt++;
+        //fSkipMs = kTRUE;
+        //exit(EXIT_FAILURE); //TODO probably one should get rid of explicit EXIT calls not to ruin unpacking of other detectors?
+      }
+    }
+    else if (tdcMarker == 0x2) {  // DEBUG
+      //	UInt_t debugMode = (tdcData >> 24) & 0x1f; // 5 bits
+      //	UInt_t debugBits = (tdcData) & 0xffffff; // 24 bits
+      //fTrbState = TrbNetState::DEBUG;
+      LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr) << "\t"
+                  << "ok"
+                  << "\t"
+                  << "DEBUG";
+      LOG(info) << "DEBUG VALUE [" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr);
+      errorInData = true;
+      //fGwordCnt++;
+      // currently no actions if a DEBUG message is encountered.
+    }
+    else if (tdcMarker == 0x3) {  // EPOCH counter
+      if (!(fTrbState == TrbNetState::HEADER || fTrbState == TrbNetState::TDC || fTrbState == TrbNetState::EPOCH)) {
+        LOG(error) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr) << "\t"
+                   << "er"
+                   << "\t"
+                   << "ILLEGAL EPOCH Position!";
+        errorInData = true;
+      }
+      else if (((tdcData >> 28) & 0xF) != 0x6) {  //EPOCH is always 0x6....
+        LOG(error) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr) << "\t"
+                   << "er"
+                   << "\t"
+                   << "ILLEGAL EPOCH value :";
+        errorInData = true;
+      }
+      else {
+        fTrbState        = TrbNetState::EPOCH;
+        fDebugPrint      = 0;
+        fCurEpochCounter = (tdcData) &0xfffffff;  // 28 bits
+        LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr) << "\t"
+                    << "ok"
+                    << "\t"
+                    << "EPOCH\t" << fCurEpochCounter;
+        //fGwordCnt++;
+      }
+    }
+    else {
+      if (tdcTimeDataMarker != 0x1) {
+        LOG(error) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr(ptr) << "\t"
+                   << "er"
+                   << "\t"
+                   << "UNKNOWN";
+        errorInData = true;
+      }
+    }
+  }
+
+  if (errorInData) {
+    //Handle error
+    fSkipMs = kTRUE;
+    fSkipCnt++;
+    LOG(error) << " >>> Skipping MicroTS due to error in data! <<<";
+  }
+
+
+  return 0;  //correction;
+}
+
+void CbmMcbm2018UnpackerAlgoRich2020::ProcessTimestampWord(Int_t tdcData)
+{
+  Int_t channel = (tdcData >> 22) & 0x7f;   // 7 bits
+  Int_t fine    = (tdcData >> 12) & 0x3ff;  // 10 bits
+  Int_t edge    = (tdcData >> 11) & 0x1;    // 1 bit
+  Int_t coarse  = (tdcData) &0x7ff;         // 11 bits
+  Int_t epoch   = fCurEpochCounter;
+
+  //TODO move full time calculation outside
+  // clang-format off
+  Double_t fullTime = (Double_t) epoch * 2048. * 5. + (Double_t) (coarse) *5. - (Double_t) (fine) *0.005;
+  // clang-format on
+
+  LOG(debug4) << "[" << fGwordCnt << "]\t" << mRichSupport::GetWordHexRepr((uint8_t*) &tdcData) << "\t"
+              << "ok"
+              << "\t"
+              << "TIMESTAMP"
+              << "\t"
+              << "ch=" << channel << "\t"
+              << "edge=" << edge << "\t"
+              << "epoch=" << epoch << "\t"
+              << "coarse=" << coarse << "\t"
+              << "fine=" << fine << "\t"
+              << "full=" << fullTime;
+  //fGwordCnt++;
+
+  // Storing reference times
+  // =======================
+
+  ////LOG(debug4) << "fSubSubEvId=0x" << std::hex << fSubSubEvId << std::dec;
+  Int_t idx = fUnpackPar->GetAddressIdx(fSubSubEvId);
+  if (-1 == idx) {
+    /// Probably corrupted MS, stop there and skip remaining data
+    fSkipMs = kTRUE;
+    return;
+  }
+  ////LOG(debug4) << "fSubSubEvId=0x" << std::hex << fSubSubEvId << std::dec << " idx=" << idx;
+
+  if ((fSubSubEvId == 0xc000) || (fSubSubEvId == 0xc001)) {
+    // if CTS
+    if ((channel == 0) && (edge == RISINGEDGEID)) {
+      fPrevLastCTSch0_re_time = fLastCTSch0_re_time;
+      fLastCTSch0_re_time     = fullTime;
+      ////LOG(debug4) << "Storing full time for the CTS ch=0 edge=RISINGEDGEID";
+    }
+    else if ((channel == 2) && (edge == RISINGEDGEID)) {
+      fPrevLastCTSch2_re_time = fLastCTSch2_re_time;
+      fLastCTSch2_re_time     = fullTime;
+      ////LOG(debug4) << "Storing full time for the CTS ch=2 edge=RISINGEDGEID";
+    }
+    else if ((channel == 2) && (edge == FALLINGEDGEID)) {
+      fPrevLastCTSch2_fe_time = fLastCTSch2_fe_time;
+      fLastCTSch2_fe_time     = fullTime;
+      ////LOG(debug4) << "Storing full time for the CTS ch=2 edge=FALLINGEDGEID";
+    }
+  }
+  else {
+    // if not CTS (which means TRB)
+    if ((channel == 0) && (edge == RISINGEDGEID)) {
+      fPrevLastCh0_re_time[idx] = fLastCh0_re_time[idx];
+      fLastCh0_re_time[idx]     = fullTime;
+      ////LOG(debug4) << "Storing full time for TDC 0x" << std::hex << fSubSubEvId << std::dec << " ch=0 edge=RISINGEDGEID";
+    }
+  }
+
+  // Calculation of corrected time
+  // =============================
+  Double_t fullTimeCorr = 0.;
+  if (!((fSubSubEvId == 0xc000) || (fSubSubEvId == 0xc001))) {
+    if (channel != 0) {
+      Double_t dT   = fullTime - fPrevLastCh0_re_time[idx];
+      Double_t corr = fPrevLastCTSch2_re_time - fPrevLastCTSch0_re_time;
+      fullTimeCorr  = dT - corr;
+    }
+  }
+
+  // Filling histograms
+  // ==================
+  if (fbMonitorMode == kTRUE) {
+    if (!((fSubSubEvId == 0xc000) || (fSubSubEvId == 0xc001))) {
+      // if not CTS (which means TRB)
+      if ((channel == 0) && (edge == RISINGEDGEID)) {
+        /*				Double_t dT1 = fullTime - fLastCTSch0_re_time;
+				Double_t dT2 = fullTime - fLastCTSch2_re_time;
+				Double_t dT3 = fullTime - fLastCTSch2_fe_time;
+				fhTDCch0re_minusCTSch0re->Fill(idx, dT1);
+				fhTDCch0re_minusCTSch2re->Fill(idx, dT2);
+				fhTDCch0re_minusCTSch2fe->Fill(idx, dT3);
+
+				Double_t dT4 = fullTime - fPrevLastCTSch0_re_time;
+				Double_t dT5 = fullTime - fPrevLastCTSch2_re_time;
+				Double_t dT6 = fullTime - fPrevLastCTSch2_fe_time;
+				fhTDCch0re_minusPrevCTSch0re->Fill(idx, dT4);
+				fhTDCch0re_minusPrevCTSch2re->Fill(idx, dT5);
+				fhTDCch0re_minusPrevCTSch2fe->Fill(idx, dT6);
+
+				LOG(debug4) << "dT1=" << dT1 << "\tdT2=" << dT2 << "\tdT3=" << dT3
+				            << "\tdT4=" << dT4 << "\tdT5=" << dT5 << "\tdT6=" << dT6;
+*/
+      }
+
+      if ((channel != 0) && (edge == RISINGEDGEID)) {
+        /*Double_t dT7 = fullTime - fLastCh0_re_time[idx];
+				TH2D* h1 = fhTDCre_minusTDCch0re.at(idx);
+				h1->Fill(channel, dT7);*/
+
+        Double_t dT8 = fullTime - fPrevLastCh0_re_time[idx];
+        /*TH2D* h2 = fhTDCre_minusPrevTDCch0re.at(idx);
+				h2->Fill(channel, dT8);*/
+
+        Double_t corr1       = fPrevLastCTSch2_re_time - fPrevLastCTSch0_re_time;
+        Double_t correctedT1 = dT8 + corr1;
+        Double_t correctedT2 = dT8 - corr1;
+        /*
+//				TH2D* h3 = fhTDCre_corrected1.at(idx);
+//				h3->Fill(channel, correctedT1);
+//				TH2D* h4 = fhTDCre_corrected2.at(idx);
+//				h4->Fill(channel, correctedT2);
+*/
+        LOG(debug4)
+          /*<< "dT7=" << dT7*/ << "\tdT8=" << dT8 << "\tcorr1=" << corr1 << "\tcorrectedT1=" << correctedT1
+                               << "\tcorrectedT2=" << correctedT2;
+      }
+    }
+  }
+
+  if (edge == RISINGEDGEID) { this->ProcessRisingEdge(fSubSubEvId, channel, fullTimeCorr); }
+  else {
+    this->ProcessFallingEdge(fSubSubEvId, channel, fullTimeCorr);
+  }
+
+  fChnlMsgCnt.at(channel)++;
+  if (fTrbState == TrbNetState::EPOCH) fChnlMsgCnt.at(channel)++;  // If there was a correp. EPOCH before
+}
+
+void CbmMcbm2018UnpackerAlgoRich2020::ProcessRisingEdge(Int_t subSubEvId, Int_t channel, Double_t time)
+{
+  ////LOG(debug4) << "CbmMcbm2018UnpackerAlgoRich2020::ProcessRisingEdge()";
+
+  //TODO: not a very nice hack.
+  // All messages from ch0 are skipped. Though, probably, that is corect.
+  if (channel == 0) return;
+
+  // Also skip everything from CST
+  if ((subSubEvId == 0xc000) || (subSubEvId == 0xc001)) return;
+
+  fRisingEdgesBuf.push_back(CbmMcbmRichEdge(subSubEvId, channel, time));
+}
+
+void CbmMcbm2018UnpackerAlgoRich2020::ProcessFallingEdge(Int_t subSubEvId, Int_t channel, Double_t time)
+{
+  ////LOG(debug4) << "CbmMcbm2018UnpackerAlgoRich2020::ProcessFallingEdge()";
+
+  // Skip everything from CST
+  if ((subSubEvId == 0xc000) || (subSubEvId == 0xc001)) return;
+
+  Bool_t reFound = kFALSE;
+
+  std::vector<CbmMcbmRichEdge>::iterator reIter = fRisingEdgesBuf.begin();
+
+  while (reIter != fRisingEdgesBuf.end()) {
+    if (((*reIter).fSubSubEventID == subSubEvId) && ((*reIter).fChannel == channel)) {
+      Double_t reTime = (*reIter).fTime;
+      // Found corresponding rising edge
+      Double_t ToT = time - reTime;
+
+      if ((ToT >= TOTMIN) && (ToT <= TOTMAX)) {
+        // Time-over-threshold is within allowed range
+
+        reFound = kTRUE;
+
+        LOG(debug4) << "Found pair for FPGA ID 0x" << std::hex << subSubEvId << std::dec << "\tch=" << channel
+                    << "\tToT=" << ToT;
+
+        //TODO implement
+        // Writing output digi
+        //////////////////////////////////////////////////
+        if (fbMonitorMode) {
+          TH1D* h = GetTotH1(subSubEvId, channel);
+          if (h != nullptr) h->Fill(ToT);
+
+          TH2D* h2 = GetTotH2(subSubEvId);
+          if (h2 != nullptr) h2->Fill(channel, ToT);
+        }
+        WriteOutputDigi(subSubEvId, channel, reTime, ToT, fCurMSidx);  //
+        //////////////////////////////////////////////////
+
+        reIter = fRisingEdgesBuf.erase(reIter);
+        continue;  // Take care. This has to be the last operation in this block
+      }
+      else {
+        //TODO: exception. By now we can just do nothing
+      }
+    }  // end of if condition
+
+    // This construction is a little bit tricky.
+    // The iterator is either incremented here or (if a pair was found)
+    // incremented using erase call, followed by the continue.
+    ++reIter;
+  }  // end of for loop
+
+  if (reFound == kFALSE) {
+    // Corresponding rising edge not found - store the falling edge in the bufer
+    fFallingEdgesBuf.push_back(CbmMcbmRichEdge(subSubEvId, channel, time));
+  }
+}
+
+void CbmMcbm2018UnpackerAlgoRich2020::WriteOutputDigi(Int_t fpgaID, Int_t channel, Double_t time, Double_t tot,
+                                                      uint64_t MSidx)
+{
+  Double_t ToTcorr = fbDoToTCorr ? fUnpackPar->GetToTshift(fpgaID, channel) : 0.;
+  Int_t pixelUID   = this->GetPixelUID(fpgaID, channel);
+  //check ordering
+  Double_t finalTime = time + (Double_t) MSidx - fdTimeOffsetNs;
+
+  Double_t lastTime = 0.;
+
+  if (fDigiVect.size() < 1) { fDigiVect.emplace_back(pixelUID, finalTime, tot - ToTcorr); }
+  else {
+    lastTime = fDigiVect[fDigiVect.size() - 1].GetTime();
+    if (lastTime > finalTime) {
+      for (int i = fDigiVect.size() - 1; i >= 0; i--) {
+        lastTime = fDigiVect[i].GetTime();
+        if (lastTime <= finalTime) {
+          // LOG(info) << " before:"<< fDigiVect.size();
+          fDigiVect.emplace(fDigiVect.begin() + i + 1, pixelUID, finalTime, tot - ToTcorr);
+          // LOG(info) << fDigiVect.size();
+          break;
+        }
+      }
+    }
+    else {
+      fDigiVect.emplace_back(pixelUID, finalTime, tot - ToTcorr);
+    }
+  }
+  LOG(debug4) << "CbmMcbm2018UnpackerAlgoRich2020::WriteOutputDigi fDigiVect.size=" << fDigiVect.size();
+}
+
+void CbmMcbm2018UnpackerAlgoRich2020::FinalizeTs()
+{
+  //          for (int i = 0; i < fDigiVect.size();++i) {
+  //                 LOG(info) << "CbmMcbm2018UnpackerAlgoRich2020::Final Vector: "
+  //                     << i+1 <<"/"<<fDigiVect.size()
+  // 	            << "\t" << std::setprecision(15)<< fDigiVect[i].GetTime();
+  //
+  //
+  //         }
+  LOG(debug4) << "CbmMcbm2018UnpackerAlgoRich2020::FinalizeTs: " << fRisingEdgesBuf.size()
+              << " entries in fRisingEdgesBuf"
+              << "\t" << fFallingEdgesBuf.size() << " entries in fFallingEdgesBuf";
+
+  // Clear rising edges buffer
+  LOG(debug4) << "Rising edges: "
+                 "----------------------------------------------------------";
+  std::vector<CbmMcbmRichEdge>::iterator reIter = fRisingEdgesBuf.begin();
+  while (reIter != fRisingEdgesBuf.end()) {
+    LOG(debug4) << "FPGA=0x" << std::hex << (*reIter).fSubSubEventID << std::dec << "\tch=" << (*reIter).fChannel;
+    ++reIter;
+  }
+  fRisingEdgesBuf.clear();
+
+  // Clear falling edges buffer
+  LOG(debug4) << "Falling edges: "
+                 "---------------------------------------------------------";
+  std::vector<CbmMcbmRichEdge>::iterator feIter = fFallingEdgesBuf.begin();
+  while (feIter != fFallingEdgesBuf.end()) {
+    LOG(debug4) << "FPGA=0x" << std::hex << (*feIter).fSubSubEventID << std::dec << "\tch=" << (*feIter).fChannel;
+    ++feIter;
+  }
+  fFallingEdgesBuf.clear();
+
+  LOG(debug4) << "---------------------------------------------------------";
+}
+
+Bool_t CbmMcbm2018UnpackerAlgoRich2020::CreateHistograms()
+{
+  Int_t nTDCs = fUnpackPar->GetNaddresses();
+  //	std::vector<TCanvas*> fcToT2d;
+  /*
+	fhTDCch0re_minusCTSch0re = new TH2D("fhTDCch0re_minusCTSch0re", "TDC ch0 re - CTS ch0 re;TDC index;ns", nTDCs, 0, nTDCs, 1000, -500., 500.);
+	fhTDCch0re_minusCTSch2re = new TH2D("fhTDCch0re_minusCTSch2re", "TDC ch0 re - CTS ch2 re;TDC index;ns", nTDCs, 0, nTDCs, 1000, -500., 500.);
+	fhTDCch0re_minusCTSch2fe = new TH2D("fhTDCch0re_minusCTSch2fe", "TDC ch0 re - CTS ch2 fe;TDC index;ns", nTDCs, 0, nTDCs, 1000, -500., 500.);
+
+	AddHistoToVector(fhTDCch0re_minusCTSch0re, "");
+	AddHistoToVector(fhTDCch0re_minusCTSch2re, "");
+	AddHistoToVector(fhTDCch0re_minusCTSch2fe, "");
+
+	fhTDCch0re_minusPrevCTSch0re = new TH2D("fhTDCch0re_minusPrevCTSch0re", "TDC ch0 re - prev CTS ch0 re;TDC index;ns", nTDCs, 0, nTDCs, 1000, -500., 500.);
+	fhTDCch0re_minusPrevCTSch2re = new TH2D("fhTDCch0re_minusPrevCTSch2re", "TDC ch0 re - prev CTS ch2 re;TDC index;ns", nTDCs, 0, nTDCs, 1000, -500., 500.);
+	fhTDCch0re_minusPrevCTSch2fe = new TH2D("fhTDCch0re_minusPrevCTSch2fe", "TDC ch0 re - prev CTS ch2 fe;TDC index;ns", nTDCs, 0, nTDCs, 1000, -500., 500.);
+
+	AddHistoToVector(fhTDCch0re_minusPrevCTSch0re, "");
+	AddHistoToVector(fhTDCch0re_minusPrevCTSch2re, "");
+	AddHistoToVector(fhTDCch0re_minusPrevCTSch2fe, "");
+*/
+
+  fhTdcErrors = new TH2D("fhTdcErrors", "Errors in TDC msgs;;", nTDCs, -0.5, nTDCs - 0.5, 9, -0.5, 8.5);
+  fhTdcErrors->GetYaxis()->SetBinLabel(1, "RingBuffOverw.");
+  fhTdcErrors->GetYaxis()->SetBinLabel(2, "noRefTime");
+  fhTdcErrors->GetYaxis()->SetBinLabel(3, "refTimePrecedes");
+  fhTdcErrors->GetYaxis()->SetBinLabel(4, "trigW/oRefTime");
+  fhTdcErrors->GetYaxis()->SetBinLabel(5, "markMisRefTime");
+  fhTdcErrors->GetYaxis()->SetBinLabel(6, "multiRefTime");
+  fhTdcErrors->GetYaxis()->SetBinLabel(7, "refTime<40ns");
+  fhTdcErrors->GetYaxis()->SetBinLabel(8, "noValidation");
+  fhTdcErrors->GetYaxis()->SetBinLabel(9, "trigger!=0x1");
+  fhTdcErrors->GetXaxis()->LabelsOption("v");
+  fhTdcErrors->GetYaxis()->SetTickSize(0.0);
+  fhTdcErrors->GetXaxis()->SetTickSize(0.0);
+  //fhTdcErrors->SetGrid();
+
+  fhEventErrors = new TH2D("fhEventErrors", "Errors in Event/mts msgs;;", 1, -0.5, 0.5, 13, -0.5, 12.5);
+  fhEventErrors->GetYaxis()->SetBinLabel(1, "UDPProblem");
+  fhEventErrors->GetYaxis()->SetBinLabel(2, "evNumMism");
+  fhEventErrors->GetYaxis()->SetBinLabel(3, "trigMism");
+  fhEventErrors->GetYaxis()->SetBinLabel(4, "wrongLength");
+  fhEventErrors->GetYaxis()->SetBinLabel(5, "answMissing");
+  fhEventErrors->GetYaxis()->SetBinLabel(6, "evRequFail");
+  fhEventErrors->GetYaxis()->SetBinLabel(7, "evPartFound");
+  fhEventErrors->GetYaxis()->SetBinLabel(8, "sevBuffProb");
+  fhEventErrors->GetYaxis()->SetBinLabel(9, "brokenEv");
+  fhEventErrors->GetYaxis()->SetBinLabel(10, "ethLinkDwn");
+  fhEventErrors->GetYaxis()->SetBinLabel(11, "subEvBuffAlmFull");
+  fhEventErrors->GetYaxis()->SetBinLabel(12, "eth/BufProb");
+  fhEventErrors->GetYaxis()->SetBinLabel(13, "timingTrigErr");
+  fhEventErrors->GetXaxis()->LabelsOption("v");
+  fhEventErrors->GetXaxis()->SetTickSize(0.0);
+  fhEventErrors->GetYaxis()->SetTickSize(0.0);
+
+  for (Int_t iTDC = 0; iTDC < nTDCs; iTDC++) {
+    TString histoName;
+    TString histoTitle;
+    TString subFolder;
+
+    Int_t Addr    = fUnpackPar->GetAddress(iTDC);
+    fMapFEE[Addr] = iTDC;
+    fhTdcErrors->GetXaxis()->SetBinLabel(iTDC + 1, Form("0x%4x", Addr));
+    /*
+		histoName.Form("fhTDC%dre_minusTDC%dch0re", iTDC, iTDC);
+		histoTitle.Form("TDC %d re - TDC %d ch0 re;channel;ns", iTDC, iTDC);
+		TH2D* h1 = new TH2D(histoName, histoTitle, 32, 0., 32., 1200, 0., 600.);
+		fhTDCre_minusTDCch0re.push_back(h1);
+		AddHistoToVector(h1);
+
+		histoName.Form("fhTDC%dre_minusPrevTDC%dch0re", iTDC, iTDC);
+		histoTitle.Form("TDC %d re - prev. TDC %d ch0 re;channel;ns", iTDC, iTDC);
+		TH2D* h2 = new TH2D(histoName, histoTitle, 32, 0., 32., 1200, 0., 600.);
+		fhTDCre_minusPrevTDCch0re.push_back(h2);
+		AddHistoToVector(h2);
+*/
+    /*		histoName.Form("fhTDC%dre_corrected1", iTDC);
+		histoTitle.Form("TDC %d re corrected1;channel;ns", iTDC);
+		TH2D* h3 = new TH2D(histoName, histoTitle, 32, 0., 32., 1200, 0., 600.);
+		fhTDCre_corrected1.push_back(h3);
+		AddHistoToVector(h3);
+
+		histoName.Form("fhTDC%dre_corrected2", iTDC);
+		histoTitle.Form("TDC %d re corrected2;channel;ns", iTDC);
+		TH2D* h4 = new TH2D(histoName, histoTitle, 32, 0., 32., 1200, 0., 600.);
+		fhTDCre_corrected2.push_back(h4);
+		AddHistoToVector(h4);
+*/
+    // TODO
+    //workaround we need to init all histograms for ToT here. Otherwise they will not be added to monitoring.
+    for (Int_t iCh = 0; iCh <= 32; iCh++) {
+      Int_t tdc = fUnpackPar->GetAddress(iTDC);
+      GetTotH1(tdc, iCh);
+    }
+    {
+      Int_t tdc = fUnpackPar->GetAddress(iTDC);
+      GetTotH2(tdc);
+    }
+
+    /*******************************************************************/
+
+    /// Map of hits over T0 detector and same vs time in run
+    {  //if (iTDC == 0){
+      Double_t w = 10;
+      Double_t h = 10;
+
+      TCanvas* c;
+      TString canvasName;
+      TString canvasTitle;
+      Int_t tdc = fUnpackPar->GetAddress(iTDC);
+      canvasName.Form("cToT2d_TDC_0x%4x", tdc);
+      canvasTitle.Form("ToTs of TDC 0x%4x", tdc);
+      c = new TCanvas(canvasName, canvasTitle, w, h);
+      //		   fcHitMaps->Divide( 2 );
+      //		   fcHitMaps->cd( 1 );
+      //		   gPad->SetGridx();
+      //		   gPad->SetGridy();
+      //		   gPad->SetLogy();
+      //		   fhChannelMap->Draw();
+      //		   fcHitMaps->cd( 2 );
+      //		   gPad->SetGridx();
+      //		   gPad->SetGridy();
+      //		   gPad->SetLogz();
+      TH2D* h2 = GetTotH2(tdc);
+      h2->Draw("colz");
+      fcTot2d.push_back(c);
+      AddCanvasToVector(c, "ToT_Canvases");
+    }
+    /*******************************************************************/
+  }
+
+
+  AddHistoToVector(fhTdcErrors, "");
+  AddHistoToVector(fhEventErrors, "");
+
+  fhVectorSize = new TH1I("fhVectorSize", "Size of the vector VS TS index; TS index; Size [bytes]", 10000, 0., 10000.);
+  fhVectorCapacity =
+    new TH1I("fhVectorCapacity", "Size of the vector VS TS index; TS index; Size [bytes]", 10000, 0., 10000.);
+  AddHistoToVector(fhVectorSize, "");
+  AddHistoToVector(fhVectorCapacity, "");
+
+  if (fbDebugMonitorMode) {
+    fhEventSize = new TH1I("fhEventSize", "Size of the Event from TrbNet; Size [bytes]", 350, 0., 70000.);
+    AddHistoToVector(fhEventSize, "");
+
+    fhSubEventSize =
+      new TH2I("fhSubEventSize", "fhSubEventSize; HubId ; Size [bytes]; Entries", 6, 0, 6, 10000, 0., 10000.);
+    AddHistoToVector(fhSubEventSize, "");
+
+    fhSubSubEventSize =
+      new TH2I("fhSubSubEventSize", "fhSubSubEventSize; DiRICH ; Size [words]; Entries", 72, 0, 72, 510, 0., 510.);
+    AddHistoToVector(fhSubSubEventSize, "");
+
+    fhChnlSize = new TH2I("fhChnlSize", "fhChnlSize; channel; Size [words]; Entries", 33, 0, 33, 25, 0, 25.);
+    AddHistoToVector(fhChnlSize, "");
+  }
+
+  return kTRUE;
+}
+
+TH1D* CbmMcbm2018UnpackerAlgoRich2020::GetTotH1(Int_t tdc, Int_t channel)
+{
+  TH1D* h = fhTotMap[tdc][channel];
+  if (h == nullptr) {
+    TString name, title, subFolder;
+    name.Form("ToT_tdc0x%x_ch%u", tdc, channel);
+    title.Form("%s;ToT [ns];Entries", name.Data());
+    subFolder.Form("ToT/tdc0x%x", tdc);
+    h = new TH1D(name, title, 100, -1., 49.);
+    AddHistoToVector(h, std::string(subFolder.Data()));
+    fhTotMap[tdc][channel] = h;
+  }
+  return h;
+}
+
+TH2D* CbmMcbm2018UnpackerAlgoRich2020::GetTotH2(Int_t tdc)
+{
+  TH2D* h = fhTot2dMap[tdc];
+  if (h == nullptr) {
+    TString name, title, subFolder;
+    name.Form("ToT_2d_tdc0x%x", tdc);
+    title.Form("%s;channels;ToT [ns]", name.Data());
+    subFolder.Form("ToT2d");
+    h = new TH2D(name, title, 33, 0, 32, 200, -1., 49.);
+    AddHistoToVector(h, std::string(subFolder.Data()));
+    fhTot2dMap[tdc] = h;
+  }
+  return h;
+}
+
+Bool_t CbmMcbm2018UnpackerAlgoRich2020::DebugMs(const fles::Timeslice& ts, size_t uMsCompIdx, size_t uMsIdx)
+{
+  const fles::MicrosliceView mv            = ts.get_microslice(uMsCompIdx, uMsIdx);
+  const fles::MicrosliceDescriptor& msDesc = mv.desc();
+  const uint8_t* ptr                       = mv.content();
+  const size_t size                        = msDesc.size;
+
+  if (size == 0) return kTRUE;
+  Debug(ptr, size);
+
+  return kTRUE;
+}
+
+Int_t CbmMcbm2018UnpackerAlgoRich2020::Debug(const uint8_t* ptr, const size_t size)
+{
+
+  if (size == 0) return size;
+
+  //LOG(info)<<"DEBUG MODE IS ACTIVE; Printing raw data:";
+
+  uint8_t nblCnt = 0;
+  uint8_t wrdCnt = 0;
+  std::cout << std::endl << "SIZE: " << std::dec << size << "Byte" << std::endl;
+  for (size_t i = 0; i < size; ++i) {
+
+    //if (wrdCnt == 0) std::cout<<"HEX: ";
+    uint8_t* tdcDataPtr = (uint8_t*) (ptr + i);
+
+    if (wrdCnt == 0 && nblCnt == 0) { printf("%08d : ", static_cast<int>(i)); }
+
+    printf("%02x", unsigned(*tdcDataPtr));
+    nblCnt++;
+    if (nblCnt % 2 == 0) { printf(" "); }
+    if (nblCnt % 4 == 0) {
+      printf("  ");
+      wrdCnt++;
+      nblCnt = 0;
+    }
+
+    if (wrdCnt == 10) {
+      printf("\n");
+      wrdCnt = 0;
+    }
+  }
+  printf("\n");
+  return size;
+}
+
+
+void CbmMcbm2018UnpackerAlgoRich2020::ErrorMsg(uint16_t errbits, RichErrorType type, uint16_t tdcAddr)
+{
+  if (fbMonitorMode) {
+    switch (type) {
+      case RichErrorType::mtsError:
+        //UDP problem
+        if ((errbits & 0x1) == 1) fhEventErrors->Fill(0.0, 0.0);
+
+        break;
+
+      case RichErrorType::tdcHeader:
+        // min. 1 rinǵ buffer overwritten
+        if ((errbits & 0x1) == 1) fhTdcErrors->Fill(fMapFEE[tdcAddr], 0.0);
+
+        break;
+
+      case RichErrorType::tdcTrailer:
+        // no reference time in trigger handler in TDC
+        if (((errbits >> 0) & 0x1) == 1) fhTdcErrors->Fill(fMapFEE[tdcAddr], 1.0);
+
+        // reference time precedes a non-timing trigger
+        if (((errbits >> 1) & 0x1) == 1) fhTdcErrors->Fill(fMapFEE[tdcAddr], 2.0);
+
+        // timing trigger is delivered without a reference time
+        if (((errbits >> 2) & 0x1) == 1) fhTdcErrors->Fill(fMapFEE[tdcAddr], 3.0);
+
+        // Set with the bit 2 to mark the missing reference time
+        if (((errbits >> 3) & 0x1) == 1) fhTdcErrors->Fill(fMapFEE[tdcAddr], 4.0);
+
+        // there are more than one detected reference time
+        if (((errbits >> 4) & 0x1) == 1) fhTdcErrors->Fill(fMapFEE[tdcAddr], 5.0);
+
+        // reference time was too short (<40 ns)
+        if (((errbits >> 5) & 0x1) == 1) fhTdcErrors->Fill(fMapFEE[tdcAddr], 6.0);
+
+        // no trigger validation arrives from the endpoint after a valid  reference time
+        if (((errbits >> 6) & 0x1) == 1) fhTdcErrors->Fill(fMapFEE[tdcAddr], 7.0);
+
+        // any timing trigger type except 0x1 is send
+        if (((errbits >> 7) & 0x1) == 1) fhTdcErrors->Fill(fMapFEE[tdcAddr], 8.0);
+
+        break;
+
+      case RichErrorType::ctsHeader:
+        // To be implemented
+        break;
+
+      case RichErrorType::ctsTrailer:
+        // To be implemented
+        break;
+
+      case RichErrorType::subEventError:
+        // event number mismatch
+        if (((errbits >> 0) & 0x1) == 1) fhEventErrors->Fill(0.0, 1.0);
+
+        // trigger code mismatch
+        if (((errbits >> 1) & 0x1) == 1) fhEventErrors->Fill(0.0, 2.0);
+
+        // wrong length
+        if (((errbits >> 2) & 0x1) == 1) fhEventErrors->Fill(0.0, 3.0);
+
+        // answer missing
+        if (((errbits >> 3) & 0x1) == 1) fhEventErrors->Fill(0.0, 4.0);
+
+        // event number request by CTS was not available (Not found)
+        if (((errbits >> 4) & 0x1) == 1) fhEventErrors->Fill(0.0, 5.0);
+
+        // event partially found in data buffer
+        if (((errbits >> 5) & 0x1) == 1) fhEventErrors->Fill(0.0, 6.0);
+
+        // Severe Problem with data buffer and/or read-out
+        if (((errbits >> 6) & 0x1) == 1) fhEventErrors->Fill(0.0, 7.0);
+
+        // Single broken event
+        if (((errbits >> 7) & 0x1) == 1) fhEventErrors->Fill(0.0, 8.0);
+
+        // Ethernet Link down
+        if (((errbits >> 8) & 0x1) == 1) fhEventErrors->Fill(0.0, 9.0);
+
+        // SubEvent buffer almost full
+        if (((errbits >> 9) & 0x1) == 1) fhEventErrors->Fill(0.0, 10.0);
+
+        // Ethernet/SubEventBuilder error
+        if (((errbits >> 10) & 0x1) == 1) fhEventErrors->Fill(0.0, 11.0);
+
+        // Timing trigger error
+        if (((errbits >> 11) & 0x1) == 1) fhEventErrors->Fill(0.0, 12.0);
+
+        break;
+
+      default: break;
+    }
+  }
+}
+
+/*
+Bool_t CbmMcbm2018UnpackerAlgoRich2020::FillHistograms()
+{
+	return kTRUE;
+}
+*/
+Bool_t CbmMcbm2018UnpackerAlgoRich2020::ResetHistograms()
+{
+  //TODO: do something?
+  return kTRUE;
+}
+
+void CbmMcbm2018UnpackerAlgoRich2020::findTDCAlignmentError(uint8_t const* const ptr, size_t const size)
+{
+
+  fTDCAlignmentErrorPositions.clear();
+
+  //     mRichSupport::SwapBytes(4, ptr+size);
+  //     if((((((Int_t*)(ptr+size))[0]) >> 28) & 0xF) != 0x0) {
+  //         LOG(WARN) << "CbmMcbm2018UnpackerAlgoRich2020::ProcessTRBsubevent() warning:"
+  //                   << "End on Hub is not where expected. Is it a Buffer overflow?  LastWord: "<<mRichSupport::GetWordHexRepr(ptr+size);
+  //     }
+  //     mRichSupport::SwapBytes(4, ptr+size);
+
+  /***
+	 * Signature of Error:
+	 *  82b7   8ca6
+	 *  8297  *34ad*
+	 * *34ad*  66af    // data Ptr
+	 * *cf8b* *cf8b*
+	 *  82c8   cca9
+	 */
+
+  //start at 8 to skip header of Hub and first row as this has to be checked
+  //stop at size -4 to avoid comparing with following hub
+
+  for (Int_t i = 8; i < static_cast<Int_t>(size - 4); i += 4) {  // i represents bytes (4 per line)
+    //TODO: Optimize the swaping
+    mRichSupport::SwapBytes(4, ptr + i - 4);
+    mRichSupport::SwapBytes(4, ptr + i);
+    mRichSupport::SwapBytes(4, ptr + i + 4);
+    bool problem = false;
+    // clang-format off
+    if ((((Int_t*) (ptr + i - 4))[0] & 0xFFFF) == ((((Int_t*) (ptr + i))[0] >> 16) & 0xFFFF)) {
+      if ((((Int_t*) (ptr + i + 4))[0] & 0xFFFF) == ((((Int_t*) (ptr + i + 4))[0] >> 16) & 0xFFFF)) {
+        //Signature of problem!
+        problem = true;
+        fTDCAlignmentErrorPositions.push_back(i);
+        fTDCAlignmentErrorPositions.push_back(i + 6);
+      }
+    }
+    // clang-format on
+
+    mRichSupport::SwapBytes(4, ptr + i - 4);
+    mRichSupport::SwapBytes(4, ptr + i);
+    mRichSupport::SwapBytes(4, ptr + i + 4);
+
+    if (problem) i += 8;  //jump after the problem
+  }
+}
+
+ClassImp(CbmMcbm2018UnpackerAlgoRich2020)
diff --git a/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerAlgoRich2020.h b/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerAlgoRich2020.h
new file mode 100644
index 0000000000..de1446482c
--- /dev/null
+++ b/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerAlgoRich2020.h
@@ -0,0 +1,384 @@
+/* Copyright (C) 2019-2020 Justus-Liebig-Universitaet Giessen, Giessen
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Egor Ovcharenko [committer] */
+
+/**
+ * CbmMcbm2018UnpackerAlgoRich2020
+ * E. Ovcharenko, Mar 2019
+ * based on other detectors' classes by P.-A. Loizeau
+ */
+
+#ifndef CbmMcbm2018UnpackerAlgoRich2020_H
+#define CbmMcbm2018UnpackerAlgoRich2020_H
+
+#include "CbmStar2019Algo.h"  // mother class
+
+// STD
+#include <map>
+#include <vector>
+
+// ROOT
+#include <TArrayD.h>
+#include <TH2D.h>
+
+// CbmRoot
+#include "CbmMcbm2018UnpackerUtilRich2020.h"
+#include "CbmRichDigi.h"
+
+////class TList; // not really needed, already declared in the mother class
+class CbmMcbm2018RichPar;
+
+#define RISINGEDGEID 1
+#define FALLINGEDGEID 0
+
+#define TOTMIN -20.
+#define TOTMAX 100.
+
+enum class TrbNetState
+{
+  IDLE,
+  HEADER,
+  EPOCH,
+  TDC,
+  TRAILER,
+  CTS,
+  DEBUG
+};
+
+enum class RichErrorType
+{
+  mtsError,
+  tdcHeader,
+  tdcTrailer,
+  ctsHeader,
+  ctsTrailer,
+  subEventError
+};
+
+class CbmMcbm2018UnpackerAlgoRich2020 : public CbmStar2019Algo<CbmRichDigi> {
+public:
+  CbmMcbm2018UnpackerAlgoRich2020();
+
+  virtual ~CbmMcbm2018UnpackerAlgoRich2020();
+
+  virtual Bool_t Init();
+
+  virtual void Reset();
+
+  virtual void Finish();
+
+  virtual Bool_t InitContainers();
+
+  virtual Bool_t ReInitContainers();
+
+  virtual TList* GetParList();
+
+  Bool_t InitParameters();
+
+  /**
+  	 * Copied from other detectors without any brain effort...
+	 */
+  void AddMsComponentToList(size_t component, UShort_t usDetectorId);
+
+  virtual Bool_t ProcessTs(const fles::Timeslice& ts);
+
+  virtual Bool_t ProcessTs(const fles::Timeslice& ts, size_t component);
+
+  virtual Bool_t ProcessMs(const fles::Timeslice& ts, size_t uMsCompIdx, size_t uMsIdx);
+
+  Bool_t DebugMs(const fles::Timeslice& ts, size_t uMsCompIdx, size_t uMsIdx);
+
+  void SetMonitorMode(Bool_t bFlagIn = kTRUE) { fbMonitorMode = bFlagIn; }
+
+  inline void SetTimeOffsetNs(Double_t dOffsetIn = 0.0) { fdTimeOffsetNs = dOffsetIn; }
+
+  inline void DoTotCorr(Bool_t bDoToTCorr = kTRUE) { fbDoToTCorr = bDoToTCorr; }
+
+  void SetRawDataMode(Bool_t bDebug = kFALSE) { fRawDataMode = bDebug; }
+
+  Int_t Debug(const uint8_t* ptr, const size_t size);
+
+private:
+  /**
+	 * Initialize space required for monitoring.
+	 * This depends on the parameters read from the par file.
+	 * This method should be called once after parameters import.
+	 */
+  void InitStorage();
+
+  void ProcessMicroslice(size_t const size, uint8_t const* const ptr);
+
+  /**
+	 * Including header
+	 */
+  Int_t ProcessTRBevent(size_t const size, uint8_t const* const ptr);
+
+  /**
+	 *
+	 */
+  Int_t ProcessTRBeventHeader(size_t const size, uint8_t const* const ptr);
+
+  /**
+	 * Including header - ?
+	 * Return number of processed bytes
+	 */
+  Int_t ProcessSKIPsubevent(size_t const size, uint8_t const* const ptr);
+
+  Int_t ProcessCTSsubevent(size_t const size, uint8_t const* const ptr);
+
+  Int_t ProcessTRBsubevent(size_t const size, uint8_t const* const ptr);
+
+  /**
+	 * Including TDC header, but not including TRB subsubevent header
+	 * Return number of processed bytes
+	 */
+  Int_t ProcessTRBsubsubevent(size_t const size, uint8_t const* const ptr, Int_t const hubOffset, size_t const hubSize);
+
+  /**
+	 * Process a word written out by the TDC - TIMESTEMP, HEADER, TRAILER, DEBUG, EPOCH, ...
+	 */
+  Int_t ProcessTDCword(uint8_t const* const ptr, Int_t const word, size_t const size);
+
+  /**
+	 * Process specifically a TIMESTAMP type of word
+	 */
+  void ProcessTimestampWord(Int_t tdcData);
+
+  /**
+	 * Write a gidi object into the output collection
+	 */
+  void WriteOutputDigi(Int_t fpgaID, Int_t channel, Double_t time, Double_t tot, uint64_t MSidx);
+
+  /**
+	 * Method which is called at the end of the timeslice processing
+	 */
+  void FinalizeTs();
+
+  /**
+     * Write Errors to Histograms
+     */
+  void ErrorMsg(uint16_t errbits, RichErrorType type, uint16_t tdcAddr = 0);
+
+  Int_t GetPixelUID(Int_t fpgaID, Int_t ch) const
+  {
+    // First 16 bits are used for the FPGA ID, then
+    // 8 bits unused and then 8 bits are used for the channel
+    return ((fpgaID << 16) | (ch & 0x00FF));
+  }
+
+  void findTDCAlignmentError(uint8_t const* const ptr, size_t const size);
+
+private:  // data members
+  /// Control flags
+  Bool_t fbMonitorMode;  //! Switch ON the filling of a minimal set of histograms
+
+  Bool_t fbDebugMonitorMode;  //! Switch ON the filling of a additional set of histograms
+
+  Bool_t fRawDataMode;
+
+  Bool_t fError;  //! flag for an error in the datastream
+
+  TrbNetState fTrbState;  // State of TrbNet (HEADER,TRAILER,...)
+
+  uint32_t fErrorCorr;  // Offset to correct a error in datastream
+
+  /// User setting: kTRUE activates ToT correction from Parameterfile
+  Bool_t fbDoToTCorr;
+
+  Bool_t fSkipMs;
+  /// User settings: Data correction parameters
+  Double_t fdTimeOffsetNs;
+
+  size_t fDataSize = 0;
+
+
+  /**
+	 * Bug fix / shortcut, which helps to run correct ProcessTs method.
+	 * Works only if there is one componentID assigned to the detector.
+	 */
+  Int_t fRICHcompIdx;
+
+  /**
+	 * Unpacker parameters object
+	 */
+  CbmMcbm2018RichPar* fUnpackPar;  //!
+
+  /**
+	 * Counter of processed timeslices
+	 */
+  uint64_t fTScounter;
+
+  /**
+	 * Current microslice ID
+	 */
+  Int_t fCurMSid;  //!
+
+  /**
+	 * Current microslice index from the microslice descriptor
+	 * CBM reference time //TODO clearify
+	 */
+  uint64_t fCurMSidx;  //!
+
+  /**
+	 * Global word counter within current microslice
+	 */
+  Int_t fGwordCnt;  //!
+
+  /**
+	 * Flag indicating that we are in the subsubevent
+	 */
+  Bool_t fInSubSubEvent;  //!
+
+  /**
+	 * Current epoch value
+	 */
+  UInt_t fCurEpochCounter;  //!
+
+  /**
+	 * Current subsubevent ID
+	 */
+  Int_t fSubSubEvId;  //!
+
+  /**
+	 * Flag to mark the last DiRICH on a Hub
+	 */
+  Bool_t fLastFeeOnHub = false;
+
+  std::vector<Int_t> fTDCAlignmentErrorPositions;
+
+  Int_t fTdcWordCorrectionCnt = 0;
+
+  Int_t fTdcWordCorrectionGlobalCnt = 0;
+
+  Int_t fSkipCnt = 0;
+
+private:  // Stored timestamps
+  /**
+	 * Full time of the last rising edge from ch 0 of CTS
+	 */
+  Double_t fLastCTSch0_re_time;  //!
+
+  /**
+	 * Full time of the last rising edge from ch 2 of CTS
+	 */
+  Double_t fLastCTSch2_re_time;  //!
+
+  /**
+	 * Full time of the last falling edge from ch 2 of CTS
+	 */
+  Double_t fLastCTSch2_fe_time;  //!
+
+  /**
+	 * Full time of the last rising edge from ch 0 of CTS from the previous microslice
+	 */
+  Double_t fPrevLastCTSch0_re_time;  //!
+
+  /**
+	 * Full time of the last rising edge from ch 2 of CTS from the previous microslice
+	 */
+  Double_t fPrevLastCTSch2_re_time;  //!
+
+  /**
+	 * Full time of the last falling edge from ch 2 of CTS from the previous microslice
+	 */
+  Double_t fPrevLastCTSch2_fe_time;  //!
+
+  /**
+	 * Full time of the last rising edge from ch 0 of each TDC
+	 */
+  TArrayD fLastCh0_re_time;  //!
+
+  /**
+	 * Full time of the previous last rising edge from ch 0 of each TDC (from the previous microslice)
+	 */
+  TArrayD fPrevLastCh0_re_time;  //!
+
+private:  // digi building
+  void ProcessRisingEdge(Int_t subSubEvId, Int_t channel, Double_t time);
+
+  void ProcessFallingEdge(Int_t subSubEvId, Int_t channel, Double_t time);
+
+  /**
+	 * Buffer for rising edges. It is filled during unpacking whenever
+	 * the rising edge is encountered. Afterwards, when a falling edge
+	 * is encountered, corresponding rising edge is searched here and
+	 * removed if found.
+	 */
+  std::vector<CbmMcbmRichEdge> fRisingEdgesBuf;  //! Exclude from ROOT dictionnary due to missing empty constructor!!
+
+  /**
+	 * Buffer of falling edges for which corresponding rising edges were not found
+	 */
+  std::vector<CbmMcbmRichEdge> fFallingEdgesBuf;  //! Exclude from ROOT dictionnary due to missing empty constructor!!
+
+public:  // histograms
+  /**
+	 *
+	 */
+  Bool_t CreateHistograms();
+
+  /**
+	 *
+	 */
+  //	Bool_t FillHistograms();
+
+  /**
+	 *
+	 */
+  Bool_t ResetHistograms();
+
+  TH1D* GetTotH1(Int_t fpgaID, Int_t channel);
+  TH2D* GetTotH2(Int_t fpgaID);
+  /*
+	TH2D* fhTDCch0re_minusCTSch0re; //!
+	TH2D* fhTDCch0re_minusCTSch2re; //!
+	TH2D* fhTDCch0re_minusCTSch2fe; //!
+	TH2D* fhTDCch0re_minusPrevCTSch0re; //!
+	TH2D* fhTDCch0re_minusPrevCTSch2re; //!
+	TH2D* fhTDCch0re_minusPrevCTSch2fe; //!
+
+	std::vector<TH2D*> fhTDCre_minusTDCch0re; //!
+
+	std::vector<TH2D*> fhTDCre_minusPrevTDCch0re; //!
+*/
+  std::vector<TH2D*> fhTDCre_corrected1;  //!
+
+  std::vector<TH2D*> fhTDCre_corrected2;  //!
+
+  std::vector<TCanvas*> fcTot2d;  //!
+
+  TH1* fhVectorSize     = nullptr;
+  TH1* fhVectorCapacity = nullptr;
+
+  //TH2* fhDigisInChnl       = nullptr;
+  //TH2* fhDigisInDiRICH     = nullptr;
+
+  TH2D* fhTdcErrors   = nullptr;
+  TH2D* fhEventErrors = nullptr;
+
+  TH2D* fhDiRICHWords = nullptr;
+  TH2D* fhChnlWords   = nullptr;
+
+  TH1* fhEventSize       = nullptr;
+  TH2* fhSubEventSize    = nullptr;
+  TH2* fhSubSubEventSize = nullptr;
+  TH2* fhChnlSize        = nullptr;
+
+  std::array<unsigned int, 33> fChnlMsgCnt;
+
+  bool fDebugPrint = 0;
+  std::map<uint16_t, uint16_t> fMapFEE;
+
+  std::map<Int_t, std::map<Int_t, TH1D*>> fhTotMap;
+
+  std::map<Int_t, TH2D*> fhTot2dMap;
+
+  size_t fuTsMaxVectorSize     = 0;
+  Double_t fdCapacityIncFactor = 1.1;
+
+  inline void SetVectCapInc(Double_t dIncFact) { fdCapacityIncFactor = dIncFact; }
+
+  ClassDef(CbmMcbm2018UnpackerAlgoRich2020, 1);
+};
+
+#endif  // CbmMcbm2018UnpackerAlgoRich2020_H
diff --git a/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerTaskRich2020.cxx b/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerTaskRich2020.cxx
new file mode 100644
index 0000000000..fab8a57010
--- /dev/null
+++ b/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerTaskRich2020.cxx
@@ -0,0 +1,258 @@
+/* Copyright (C) 2019-2021 Justus-Liebig-Universitaet Giessen, Giessen
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Egor Ovcharenko [committer], Pierre-Alain Loizeau */
+
+/**
+ * CbmMcbm2018UnpackerTaskRich2020
+ * E. Ovcharenko, Mar 2019
+ * based on other detectors' classes by P.-A. Loizeau
+ */
+
+//TODO: check that all data members are initialized in the constructor
+
+#include "CbmMcbm2018UnpackerTaskRich2020.h"
+
+// ROOT
+#include <TCanvas.h>
+#include <TClonesArray.h>
+#include <TFile.h>
+#include <THttpServer.h>
+#include <TROOT.h>
+
+// FairRoot
+#include <FairParGenericSet.h>
+#include <FairRootManager.h>
+#include <FairRun.h>
+#include <FairRunOnline.h>
+#include <FairRuntimeDb.h>
+#include <Logger.h>
+
+// CbmRoot
+#include "CbmMcbm2018RichPar.h"
+#include "CbmMcbm2018UnpackerAlgoRich2020.h"
+
+#include <thread>
+
+#include <chrono>
+
+//TODO global variable, really?
+Bool_t bMcbm2018UnpackerTaskRichResetHistos2020 = kFALSE;
+
+CbmMcbm2018UnpackerTaskRich2020::CbmMcbm2018UnpackerTaskRich2020()
+  : CbmMcbmUnpack()
+  , fbMonitorMode(kFALSE)
+  , fbDebugMonitorMode(kFALSE)
+  , fbWriteOutput(kTRUE)
+  , fUnpackerAlgo(new CbmMcbm2018UnpackerAlgoRich2020())
+{
+}
+
+CbmMcbm2018UnpackerTaskRich2020::~CbmMcbm2018UnpackerTaskRich2020()
+{
+  if (fUnpackerAlgo) {
+    delete fUnpackerAlgo;
+    fUnpackerAlgo = nullptr;
+  }
+}
+
+Bool_t CbmMcbm2018UnpackerTaskRich2020::Init()
+{
+  LOG(info) << "CbmMcbm2018UnpackerTaskRich2020::Init";
+  LOG(info) << "Initializing mCBM RICH 2018 Unpacker";
+
+  FairRootManager* ioman = FairRootManager::Instance();
+  if (ioman == NULL) { LOG(fatal) << "No FairRootManager instance"; }
+
+  /// Get address of vector from algo
+  fpvDigiRich = &(fUnpackerAlgo->GetVector());
+  ioman->RegisterAny("RichDigi", fpvDigiRich, fbWriteOutput);
+
+  return kTRUE;
+}
+
+Bool_t CbmMcbm2018UnpackerTaskRich2020::DoUnpack(const fles::Timeslice& ts, size_t component)
+{
+  //std::this_thread::sleep_for(std::chrono::milliseconds(10));
+  if (fbMonitorMode && bMcbm2018UnpackerTaskRichResetHistos2020) {
+    LOG(info) << "Reset RICH unpacker histos ";
+    fUnpackerAlgo->ResetHistograms();
+    bMcbm2018UnpackerTaskRichResetHistos2020 = kFALSE;
+  }  // if( fbMonitorMode && bMcbm2018UnpackerTaskRichResetHistos2020 )
+
+  if (kFALSE == fUnpackerAlgo->ProcessTs(ts, component)) {
+    LOG(error) << "Failed processing TS " << ts.index() << " in mRICH unpacker algorithm class";
+    return kTRUE;
+  }  // if( kFALSE == fUnpackerAlgo->ProcessTs( ts, component ) )
+
+  /*
+   /// Sort the buffers of hits due to the time offsets applied
+   //=> Done in the algo!!!
+   sort(fpvDigiRich->begin(), fpvDigiRich->end(),
+        [](const CbmRichDigi & a, const CbmRichDigi & b) -> bool
+        {
+          return a.GetTime() < b.GetTime();
+        });
+*/
+
+  if (0 == fulTsCounter % 10000) LOG(info) << "Processed " << fulTsCounter << "TS";
+  fulTsCounter++;
+
+  return kTRUE;
+}
+
+void CbmMcbm2018UnpackerTaskRich2020::Reset() { fUnpackerAlgo->ClearVector(); }
+
+/**
+  Copied from the CbmMcbm2018UnpackerTaskSts class without giving any thinking...
+*/
+void CbmMcbm2018UnpackerTaskRich2020::Finish()
+{
+  /// If monitor mode enabled, trigger histos creation, obtain pointer on them and add them to the HTTP server
+  if (kTRUE == fbMonitorMode) {
+    /// Obtain vector of pointers on each histo from the algo (+ optionally desired folder)
+    std::vector<std::pair<TNamed*, std::string>> vHistos = fUnpackerAlgo->GetHistoVector();
+
+    /// Save old global file and folder pointer to avoid messing with FairRoot
+    TFile* oldFile     = gFile;
+    TDirectory* oldDir = gDirectory;
+
+    TFile* histoFile = nullptr;
+
+    // open separate histo file in recreate mode
+    histoFile = new TFile("data/HistosUnpackerRich.root", "RECREATE");
+    histoFile->cd();
+
+    /// Register the histos in the HTTP server
+    for (UInt_t uHisto = 0; uHisto < vHistos.size(); ++uHisto) {
+      /// Make sure we end up in chosen folder
+      TString sFolder = vHistos[uHisto].second.data();
+      if (nullptr == gDirectory->Get(sFolder)) gDirectory->mkdir(sFolder);
+      gDirectory->cd(sFolder);
+
+      /// Write plot
+      vHistos[uHisto].first->Write();
+
+      histoFile->cd();
+    }  // for( UInt_t uHisto = 0; uHisto < vHistos.size(); ++uHisto )
+
+    /// Restore old global file and folder pointer to avoid messing with FairRoot
+    gFile      = oldFile;
+    gDirectory = oldDir;
+
+    histoFile->Close();
+  }  // if( kTRUE == fbMonitorMode )
+}
+
+/**
+  Copied from the CbmMcbm2018UnpackerTaskTof class without giving any thinking...
+*/
+void CbmMcbm2018UnpackerTaskRich2020::SetParContainers()
+{
+  LOG(info) << "Setting parameter containers for " << GetName();
+
+  TList* fParCList = fUnpackerAlgo->GetParList();
+
+  for (Int_t iparC = 0; iparC < fParCList->GetEntries(); ++iparC) {
+    FairParGenericSet* tempObj = (FairParGenericSet*) (fParCList->At(iparC));
+    fParCList->Remove(tempObj);
+
+    std::string sParamName {tempObj->GetName()};
+    FairParGenericSet* newObj =
+      dynamic_cast<FairParGenericSet*>(FairRun::Instance()->GetRuntimeDb()->getContainer(sParamName.data()));
+
+    if (nullptr == newObj) {
+      LOG(error) << "Failed to obtain parameter container " << sParamName << ", for parameter index " << iparC;
+      return;
+    }  // if( nullptr == newObj )
+
+    fParCList->AddAt(newObj, iparC);
+    //      delete tempObj;
+  }  // for( Int_t iparC = 0; iparC < fParCList->GetEntries(); ++iparC )
+}
+
+/**
+  Copied from the CbmMcbm2018UnpackerTaskTof class without giving any thinking...
+*/
+Bool_t CbmMcbm2018UnpackerTaskRich2020::InitContainers()
+{
+  LOG(info) << "Init parameter containers for " << GetName();
+
+  /// Control flags
+  CbmMcbm2018RichPar* pUnpackPar =
+    dynamic_cast<CbmMcbm2018RichPar*>(FairRun::Instance()->GetRuntimeDb()->getContainer("CbmMcbm2018RichPar"));
+  if (nullptr == pUnpackPar) {
+    LOG(error) << "Failed to obtain parameter container CbmMcbm2018RichPar";
+    return kFALSE;
+  }  // if( nullptr == pUnpackPar )
+     /*
+   fbMonitorMode = pUnpackPar->GetMonitorMode();
+   LOG(info) << "Monitor mode:       "
+             << ( fbMonitorMode ? "ON" : "OFF" );
+
+   fbDebugMonitorMode = pUnpackPar->GetDebugMonitorMode();
+   LOG(info) << "Debug Monitor mode: "
+             << ( fbDebugMonitorMode ? "ON" : "OFF" );
+*/
+  Bool_t initOK = fUnpackerAlgo->InitContainers();
+
+  /// If monitor mode enabled, trigger histos creation, obtain pointer on them and add them to the HTTP server
+  if (kTRUE == fbMonitorMode) {
+    /// Trigger histo creation on all associated algos
+    initOK &= fUnpackerAlgo->CreateHistograms();
+
+    /// Obtain vector of pointers on each histo from the algo (+ optionally desired folder)
+    std::vector<std::pair<TNamed*, std::string>> vHistos = fUnpackerAlgo->GetHistoVector();
+    /// Obtain vector of pointers on each canvas from the algo (+ optionally desired folder)
+    std::vector<std::pair<TCanvas*, std::string>> vCanvases = fUnpackerAlgo->GetCanvasVector();
+
+    /// Register the histos in the HTTP server
+    THttpServer* server = FairRunOnline::Instance()->GetHttpServer();
+    if (nullptr != server) {
+      for (UInt_t uHisto = 0; uHisto < vHistos.size(); ++uHisto) {
+        server->Register(Form("/rich/%s", vHistos[uHisto].second.data()), vHistos[uHisto].first);
+      }  // for( UInt_t uHisto = 0; uHisto < vHistos.size(); ++uHisto )
+      for (UInt_t uCanv = 0; uCanv < vCanvases.size(); ++uCanv) {
+        server->Register(Form("/rich/%s", vCanvases[uCanv].second.data()),
+                         gROOT->FindObject((vCanvases[uCanv].first)->GetName()));
+      }  //  for( UInt_t uCanv = 0; uCanv < vCanvases.size(); ++uCanv )
+
+
+      server->RegisterCommand("/Reset_UnpRich_Hist", "bMcbm2018UnpackerTaskRichResetHistos2020=kTRUE");
+      server->Restrict("/Reset_UnpRich_Hist", "allow=admin");
+    }  // if( nullptr != server )
+
+  }  // if( kTRUE == fbMonitorMode )
+
+  fUnpackerAlgo->SetMonitorMode(fbMonitorMode);
+
+  return initOK;
+}
+
+/**
+  Copied from the CbmMcbm2018UnpackerTaskTof class without giving any thinking...
+*/
+Bool_t CbmMcbm2018UnpackerTaskRich2020::ReInitContainers()
+{
+  LOG(info) << "ReInit parameter containers for " << GetName();
+  Bool_t initOK = fUnpackerAlgo->ReInitContainers();
+
+  return initOK;
+}
+
+/**
+  Copied from other detectors without any brain effort...
+**/
+void CbmMcbm2018UnpackerTaskRich2020::AddMsComponentToList(size_t component, UShort_t usDetectorId)
+{
+  fUnpackerAlgo->AddMsComponentToList(component, usDetectorId);
+}
+
+void CbmMcbm2018UnpackerTaskRich2020::SetNbMsInTs(size_t /*uCoreMsNb*/, size_t /*uOverlapMsNb*/) {}
+
+void CbmMcbm2018UnpackerTaskRich2020::SetIgnoreOverlapMs(Bool_t bFlagIn) { fUnpackerAlgo->SetIgnoreOverlapMs(bFlagIn); }
+
+void CbmMcbm2018UnpackerTaskRich2020::SetTimeOffsetNs(Double_t dOffsetIn) { fUnpackerAlgo->SetTimeOffsetNs(dOffsetIn); }
+
+void CbmMcbm2018UnpackerTaskRich2020::DoTotCorr(Bool_t bDoToTCorr) { fUnpackerAlgo->DoTotCorr(bDoToTCorr); }
+
+ClassImp(CbmMcbm2018UnpackerTaskRich2020)
diff --git a/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerTaskRich2020.h b/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerTaskRich2020.h
new file mode 100644
index 0000000000..b3dbfce1a8
--- /dev/null
+++ b/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerTaskRich2020.h
@@ -0,0 +1,75 @@
+/* Copyright (C) 2019-2020 Justus-Liebig-Universitaet Giessen, Giessen
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Egor Ovcharenko [committer], Pierre-Alain Loizeau */
+
+/**
+ * CbmMcbm2018UnpackerTaskRich
+ * E. Ovcharenko, Mar 2019
+ * based on other detectors' classes by P.-A. Loizeau
+ */
+
+#ifndef CbmMcbm2018UnpackerTaskRich2020_H
+#define CbmMcbm2018UnpackerTaskRich2020_H
+
+#include "CbmMcbmUnpack.h"  // mother class
+#include "CbmRichDigi.h"
+
+//class TList; // Needed?
+//class TClonesArray;
+class CbmMcbm2018UnpackerAlgoRich2020;
+
+class CbmMcbm2018UnpackerTaskRich2020 : public CbmMcbmUnpack {
+public:
+  CbmMcbm2018UnpackerTaskRich2020();
+
+  CbmMcbm2018UnpackerTaskRich2020(const CbmMcbm2018UnpackerTaskRich2020&) = delete;
+  CbmMcbm2018UnpackerTaskRich2020 operator=(const CbmMcbm2018UnpackerTaskRich2020&) = delete;
+
+  virtual ~CbmMcbm2018UnpackerTaskRich2020();
+
+  virtual Bool_t Init();
+
+  virtual Bool_t DoUnpack(const fles::Timeslice& ts, size_t component);
+
+  virtual void Reset();
+
+  virtual void Finish();
+
+  virtual void SetParContainers();
+
+  virtual Bool_t InitContainers();
+
+  virtual Bool_t ReInitContainers();
+
+  virtual void AddMsComponentToList(size_t component, UShort_t usDetectorId);
+
+  virtual void SetNbMsInTs(size_t uCoreMsNb, size_t uOverlapMsNb);
+
+  /// Algo settings setters
+  inline void SetMonitorMode(Bool_t bFlagIn = kTRUE) { fbMonitorMode = bFlagIn; }
+  void SetIgnoreOverlapMs(Bool_t bFlagIn = kTRUE);
+  void SetTimeOffsetNs(Double_t dOffsetIn = 0.0);
+  void DoTotCorr(Bool_t bDoToTCorr = kTRUE);
+
+  /// Task settings
+  void SetWriteOutputFlag(Bool_t bFlagIn) { fbWriteOutput = bFlagIn; }
+
+private:
+  /// Control flags
+  Bool_t fbMonitorMode;       //! Switch ON the filling of a minimal set of histograms
+  Bool_t fbDebugMonitorMode;  //! Switch ON the filling of a additional set of histograms
+  Bool_t fbWriteOutput;       //! If ON the output TClonesArray of digi is written to disk
+
+  /// Statistics & first TS rejection
+  uint64_t fulTsCounter;
+
+  /// Algo implementation of the unpacking
+  CbmMcbm2018UnpackerAlgoRich2020* fUnpackerAlgo;
+
+  /// Output vectors
+  std::vector<CbmRichDigi>* fpvDigiRich = nullptr;
+
+  ClassDef(CbmMcbm2018UnpackerTaskRich2020, 1);
+};
+
+#endif  // CbmMcbm2018UnpackerTaskRich2020_H
diff --git a/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerUtilRich.hpp b/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerUtilRich.hpp
deleted file mode 100644
index 9b9eff5d55..0000000000
--- a/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerUtilRich.hpp
+++ /dev/null
@@ -1,125 +0,0 @@
-#ifndef CbmMcbm2018UnpackerUtilRich_HPP
-#define CbmMcbm2018UnpackerUtilRich_HPP
-
-#include <iostream>
-#include <string>
-
-std::string GetBinaryRepresentation(size_t const size, uint8_t const* const ptr)
-{
-  std::string outString;
-
-  unsigned char* b = (unsigned char*) ptr;
-  unsigned char byte;
-
-  char cStr[2];
-  cStr[1] = '\0';
-
-  for (int i = size - 1; i >= 0; i--) {
-    for (int j = 7; j >= 0; j--) {
-      byte = (b[i] >> j) & 1;
-      sprintf(cStr, "%u", byte);
-      outString.append(cStr);
-    }
-  }
-
-  return outString;
-}
-
-/**
- * size in bytes
- */
-std::string GetHexRepresentation(size_t const size, uint8_t const* const ptr)
-{
-  std::string outString;
-
-  unsigned char* b = (unsigned char*) ptr;
-  unsigned char byte;
-
-  char cStr[3];
-  cStr[2] = '\0';
-
-  for (int i = size - 1; i >= 0; i--) {
-    byte = b[i] & 0xff;
-    sprintf(cStr, "%02x", byte);
-    outString.append(cStr);
-  }
-
-  return outString;
-}
-
-std::string GetWordHexRepr(uint8_t const* const ptr)
-{
-  std::string outString;
-
-  unsigned char* b = (unsigned char*) ptr;
-  unsigned char byte[4];
-  byte[0] = b[3] & 0xff;
-  byte[1] = b[2] & 0xff;
-  byte[2] = b[1] & 0xff;
-  byte[3] = b[0] & 0xff;
-
-  char cStr[10];
-  cStr[9] = '\0';
-
-  sprintf(cStr, "%02x%02x %02x%02x", byte[0], byte[1], byte[2], byte[3]);
-
-  outString.append(cStr);
-
-  return outString;
-}
-
-std::string GetWordHexReprInv(uint8_t const* const ptr)
-{
-  std::string outString;
-
-  unsigned char* b = (unsigned char*) ptr;
-  unsigned char byte[4];
-  byte[0] = b[0] & 0xff;
-  byte[1] = b[1] & 0xff;
-  byte[2] = b[2] & 0xff;
-  byte[3] = b[3] & 0xff;
-
-  char cStr[10];
-  cStr[9] = '\0';
-
-  sprintf(cStr, "%02x%02x %02x%02x", byte[0], byte[1], byte[2], byte[3]);
-
-  outString.append(cStr);
-
-  return outString;
-}
-
-void SwapBytes(size_t const size, uint8_t const* ptr)
-{
-  unsigned char* b = (unsigned char*) ptr;
-  unsigned char byte[4];
-  byte[0] = b[3] & 0xff;
-  byte[1] = b[2] & 0xff;
-  byte[2] = b[1] & 0xff;
-  byte[3] = b[0] & 0xff;
-
-  b[0] = byte[0];
-  b[1] = byte[1];
-  b[2] = byte[2];
-  b[3] = byte[3];
-}
-
-void PrintRaw(size_t const size, uint8_t const* const ptr)
-{
-  size_t nWords     = size / 4;
-  size_t nRestBytes = size % 4;
-
-  for (size_t iWord = 0; iWord < nWords; iWord++) {
-    //std::cout << GetHexRepresentation(4, ptr+iWord*4) << " ";
-
-    std::cout << GetWordHexReprInv(ptr + iWord * 4) << " ";
-  }
-  /*if (nRestBytes > 0) {
-		std::cout << GetHexRepresentation(nRestBytes, ptr+nWords*4) << " " << std::endl;
-	} else {
-		std::cout << std::endl;
-	}*/
-  std::cout << std::endl;
-}
-
-#endif  // CbmMcbm2018UnpackerUtilRich_HPP
diff --git a/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerUtilRich.cxx b/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerUtilRich2020.cxx
similarity index 98%
rename from fles/mcbm2018/unpacker/CbmMcbm2018UnpackerUtilRich.cxx
rename to fles/mcbm2018/unpacker/CbmMcbm2018UnpackerUtilRich2020.cxx
index 2050c5b1ba..bdb1b87062 100644
--- a/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerUtilRich.cxx
+++ b/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerUtilRich2020.cxx
@@ -2,7 +2,7 @@
    SPDX-License-Identifier: GPL-3.0-only
    Authors: Egor Ovcharenko [committer] */
 
-#include "CbmMcbm2018UnpackerUtilRich.h"
+#include "CbmMcbm2018UnpackerUtilRich2020.h"
 
 #include <iostream>
 
diff --git a/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerUtilRich.h b/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerUtilRich2020.h
similarity index 87%
rename from fles/mcbm2018/unpacker/CbmMcbm2018UnpackerUtilRich.h
rename to fles/mcbm2018/unpacker/CbmMcbm2018UnpackerUtilRich2020.h
index cb9c6955ff..d5f3e9082f 100644
--- a/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerUtilRich.h
+++ b/fles/mcbm2018/unpacker/CbmMcbm2018UnpackerUtilRich2020.h
@@ -2,8 +2,8 @@
    SPDX-License-Identifier: GPL-3.0-only
    Authors: Egor Ovcharenko [committer] */
 
-#ifndef CbmMcbm2018UnpackerUtilRich_H
-#define CbmMcbm2018UnpackerUtilRich_H
+#ifndef CbmMcbm2018UnpackerUtilRich2020_H
+#define CbmMcbm2018UnpackerUtilRich2020_H
 
 // STD
 #include <string>
@@ -40,4 +40,4 @@ namespace mRichSupport
 
 };  // namespace mRichSupport
 
-#endif  // CbmMcbm2018UnpackerUtilRich_H
+#endif  // CbmMcbm2018UnpackerUtilRich2020_H
diff --git a/macro/beamtime/mcbm2018/MonitorRich.C b/macro/beamtime/mcbm2018/MonitorRich.C
index ab53ebb387..a2b8026f5a 100644
--- a/macro/beamtime/mcbm2018/MonitorRich.C
+++ b/macro/beamtime/mcbm2018/MonitorRich.C
@@ -11,9 +11,9 @@
 // In order to call later Finish, we make this global
 FairRunOnline* run = NULL;
 
-void MonitorRich(TString inFile    = "/Users/slebedev/Development/cbm/data/mcbm18/159_pn02_0000.tsa",
+void MonitorRich(TString inFile    = "/Users/slebedev/Development/cbm/data/mcbm18/out100800.tsa",
                  TString sHostname = "localhost", Int_t iServerHttpPort = 8080, Int_t iServerRefreshRate = 100,
-                 UInt_t uRunId = 0, UInt_t nrEvents = 0)
+                 UInt_t uRunId = 0, UInt_t nrEvents = 10)
 {
   TString srcDir = gSystem->Getenv("VMCWORKDIR");
 
@@ -26,15 +26,15 @@ void MonitorRich(TString inFile    = "/Users/slebedev/Development/cbm/data/mcbm1
 
   // --- Set log output levels
   FairLogger::GetLogger();
-  gLogger->SetLogScreenLevel("INFO");
-  //gLogger->SetLogScreenLevel("DEBUG");
+  //gLogger->SetLogScreenLevel("INFO");
+  gLogger->SetLogScreenLevel("DEBUG4");
   gLogger->SetLogVerbosityLevel("MEDIUM");
 
   // --- Define parameter files
   TList* parFileList = new TList();
   TString paramDir   = srcDir + "/macro/beamtime/mcbm2018/";
 
-  TString paramFileRich       = paramDir + "mRichPar.par";
+  TString paramFileRich       = paramDir + "mRichPar.par";  // TODO: use mRichPar_70.par im mcbm2020
   TObjString* parRichFileName = new TObjString(paramFileRich);
   parFileList->Add(parRichFileName);
 
@@ -53,7 +53,7 @@ void MonitorRich(TString inFile    = "/Users/slebedev/Development/cbm/data/mcbm1
     source->SetHostName(sHostname);
   }
 
-  source->AddUnpacker(unpacker_rich, 0x30, kRich);  //RICH trb
+  source->AddUnpacker(unpacker_rich, 0x30, ECbmModuleId::kRich);  //RICH trb
 
   source->SetSubscriberHwm(1000);
 
diff --git a/macro/beamtime/mcbm2019/MonitorRich.C b/macro/beamtime/mcbm2019/MonitorRich.C
index 939336768c..02ae590a31 100644
--- a/macro/beamtime/mcbm2019/MonitorRich.C
+++ b/macro/beamtime/mcbm2019/MonitorRich.C
@@ -42,7 +42,7 @@ void MonitorRich(TString inFile    = "/Users/slebedev/Development/cbm/data/mcbm1
   // --- Set debug level
   gDebug = 0;
 
-  CbmMcbm2018UnpackerTaskRich* unpacker_rich = new CbmMcbm2018UnpackerTaskRich();
+  CbmMcbm2018UnpackerTaskRich2020* unpacker_rich = new CbmMcbm2018UnpackerTaskRich2020();
   unpacker_rich->SetIgnoreOverlapMs();
   unpacker_rich->SetMonitorMode();
   unpacker_rich->DoTotCorr(kFALSE);
diff --git a/macro/beamtime/mcbm2019/unpack_tsa_mcbm.C b/macro/beamtime/mcbm2019/unpack_tsa_mcbm.C
index 59178d4089..7ba1ad7776 100644
--- a/macro/beamtime/mcbm2019/unpack_tsa_mcbm.C
+++ b/macro/beamtime/mcbm2019/unpack_tsa_mcbm.C
@@ -77,11 +77,11 @@ void unpack_tsa_mcbm(TString inFile = "", UInt_t uRunId = 0, UInt_t nrEvents = 0
   std::cout << std::endl;
   std::cout << ">>> unpack_tsa: Initialising..." << std::endl;
 
-  CbmMcbm2018UnpackerTaskSts* unpacker_sts   = new CbmMcbm2018UnpackerTaskSts();
-  CbmMcbm2018UnpackerTaskMuch* unpacker_much = new CbmMcbm2018UnpackerTaskMuch();
-  CbmMcbm2018UnpackerTaskTof* unpacker_tof   = new CbmMcbm2018UnpackerTaskTof();
-  CbmMcbm2018UnpackerTaskRich* unpacker_rich = new CbmMcbm2018UnpackerTaskRich();
-  CbmMcbm2018UnpackerTaskPsd* unpacker_psd   = new CbmMcbm2018UnpackerTaskPsd();
+  CbmMcbm2018UnpackerTaskSts* unpacker_sts       = new CbmMcbm2018UnpackerTaskSts();
+  CbmMcbm2018UnpackerTaskMuch* unpacker_much     = new CbmMcbm2018UnpackerTaskMuch();
+  CbmMcbm2018UnpackerTaskTof* unpacker_tof       = new CbmMcbm2018UnpackerTaskTof();
+  CbmMcbm2018UnpackerTaskRich2020* unpacker_rich = new CbmMcbm2018UnpackerTaskRich2020();
+  CbmMcbm2018UnpackerTaskPsd* unpacker_psd       = new CbmMcbm2018UnpackerTaskPsd();
 
   unpacker_sts->SetMonitorMode();
   unpacker_much->SetMonitorMode();
diff --git a/macro/beamtime/mcbm2020/MonitorRich.C b/macro/beamtime/mcbm2020/MonitorRich.C
index 2f8d61543b..623f383de8 100644
--- a/macro/beamtime/mcbm2020/MonitorRich.C
+++ b/macro/beamtime/mcbm2020/MonitorRich.C
@@ -11,9 +11,9 @@
 // In order to call later Finish, we make this global
 FairRunOnline* run = NULL;
 
-void MonitorRich(TString inFile    = "/Users/slebedev/Development/cbm/data/mcbm18/159_pn02_0000.tsa",
+void MonitorRich(TString inFile    = "/Users/slebedev/Development/cbm/data/mcbm18/2021179204256_0.tsa",
                  TString sHostname = "localhost", Int_t iServerHttpPort = 8080, Int_t iServerRefreshRate = 100,
-                 UInt_t uRunId = 0, UInt_t nrEvents = 0)
+                 UInt_t uRunId = 0, UInt_t nrEvents = 1000000)
 {
   TString srcDir = gSystem->Getenv("VMCWORKDIR");
 
@@ -25,7 +25,7 @@ void MonitorRich(TString inFile    = "/Users/slebedev/Development/cbm/data/mcbm1
 
   // --- Set log output levels
   FairLogger::GetLogger();
-  gLogger->SetLogScreenLevel("INFO");
+  gLogger->SetLogScreenLevel("DEBUG4");
   //gLogger->SetLogScreenLevel("DEBUG");
   gLogger->SetLogVerbosityLevel("MEDIUM");
 
@@ -33,14 +33,14 @@ void MonitorRich(TString inFile    = "/Users/slebedev/Development/cbm/data/mcbm1
   TList* parFileList = new TList();
   TString paramDir   = srcDir + "/macro/beamtime/mcbm2020/";
 
-  TString paramFileRich       = paramDir + "mRichPar.par";
+  TString paramFileRich       = paramDir + "mRichPar_70.par";
   TObjString* parRichFileName = new TObjString(paramFileRich);
   parFileList->Add(parRichFileName);
 
   // --- Set debug level
   gDebug = 0;
 
-  CbmMcbm2018UnpackerTaskRich* unpacker_rich = new CbmMcbm2018UnpackerTaskRich();
+  CbmMcbm2018UnpackerTaskRich2020* unpacker_rich = new CbmMcbm2018UnpackerTaskRich2020();
   unpacker_rich->SetIgnoreOverlapMs();
   unpacker_rich->SetMonitorMode();
   unpacker_rich->DoTotCorr(kFALSE);
diff --git a/macro/beamtime/mcbm2020/unpack_tsa_mcbm.C b/macro/beamtime/mcbm2020/unpack_tsa_mcbm.C
index 2914565401..1c1843096f 100644
--- a/macro/beamtime/mcbm2020/unpack_tsa_mcbm.C
+++ b/macro/beamtime/mcbm2020/unpack_tsa_mcbm.C
@@ -123,12 +123,12 @@ Bool_t unpack_tsa_mcbm(TString inFile       = "",
   std::cout << std::endl;
   std::cout << ">>> unpack_tsa: Initialising..." << std::endl;
 
-  CbmMcbm2018UnpackerTaskSts* unpacker_sts   = new CbmMcbm2018UnpackerTaskSts();
-  CbmMcbm2018UnpackerTaskMuch* unpacker_much = new CbmMcbm2018UnpackerTaskMuch();
-  CbmMcbm2018UnpackerTaskTrdR* unpacker_trdR = new CbmMcbm2018UnpackerTaskTrdR();
-  CbmMcbm2018UnpackerTaskTof* unpacker_tof   = new CbmMcbm2018UnpackerTaskTof();
-  CbmMcbm2018UnpackerTaskRich* unpacker_rich = new CbmMcbm2018UnpackerTaskRich();
-  CbmMcbm2018UnpackerTaskPsd* unpacker_psd   = new CbmMcbm2018UnpackerTaskPsd();
+  CbmMcbm2018UnpackerTaskSts* unpacker_sts       = new CbmMcbm2018UnpackerTaskSts();
+  CbmMcbm2018UnpackerTaskMuch* unpacker_much     = new CbmMcbm2018UnpackerTaskMuch();
+  CbmMcbm2018UnpackerTaskTrdR* unpacker_trdR     = new CbmMcbm2018UnpackerTaskTrdR();
+  CbmMcbm2018UnpackerTaskTof* unpacker_tof       = new CbmMcbm2018UnpackerTaskTof();
+  CbmMcbm2018UnpackerTaskRich2020* unpacker_rich = new CbmMcbm2018UnpackerTaskRich2020();
+  CbmMcbm2018UnpackerTaskPsd* unpacker_psd       = new CbmMcbm2018UnpackerTaskPsd();
 
   /*
  * Do not generate plots by default
diff --git a/macro/beamtime/mcbm2021/MonitorRich.C b/macro/beamtime/mcbm2021/MonitorRich.C
new file mode 100644
index 0000000000..c2828c3166
--- /dev/null
+++ b/macro/beamtime/mcbm2021/MonitorRich.C
@@ -0,0 +1,105 @@
+/* Copyright (C) 2020 Facility for Antiproton and Ion Research in Europe, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pierre-Alain Loizeau [committer] */
+
+/** @file MCBM RICH DATA monitoring
+ ** Based on MonitorT0 by P.-A. Loizeau
+ ** ROOT macro to read tsa files which have been produced with the new data transport
+ ** Convert data into cbmroot format.
+ ** Uses CbmMcbm2018Source as source task.
+ */
+// In order to call later Finish, we make this global
+FairRunOnline* run = NULL;
+
+void MonitorRich(TString inFile    = "/Users/slebedev/Development/cbm/data/mcbm18/2021179204256_0.tsa",
+                 TString sHostname = "localhost", Int_t iServerHttpPort = 8080, Int_t iServerRefreshRate = 100,
+                 UInt_t uRunId = 0, UInt_t nrEvents = 1000000)
+{
+  TString srcDir = gSystem->Getenv("VMCWORKDIR");
+
+  // --- Specify number of events to be produced.
+  // --- -1 means run until the end of the input file.
+  Int_t nEvents = -1;
+  // --- Specify output file name (this is just an example)
+  TString parFile = TString::Format("data/moni_rich_params_%u.root", uRunId);
+
+  // --- Set log output levels
+  FairLogger::GetLogger();
+  gLogger->SetLogScreenLevel("DEBUG4");
+  //gLogger->SetLogScreenLevel("DEBUG");
+  gLogger->SetLogVerbosityLevel("MEDIUM");
+
+  // --- Define parameter files
+  TList* parFileList = new TList();
+  TString paramDir   = srcDir + "/macro/beamtime/mcbm2020/";
+
+  TString paramFileRich       = paramDir + "mRichPar_70.par";
+  TObjString* parRichFileName = new TObjString(paramFileRich);
+  parFileList->Add(parRichFileName);
+
+  // --- Set debug level
+  gDebug = 0;
+
+  CbmMcbm2018UnpackerTaskRich* unpacker_rich = new CbmMcbm2018UnpackerTaskRich();
+  unpacker_rich->SetIgnoreOverlapMs();
+  unpacker_rich->SetMonitorMode();
+  unpacker_rich->DoTotCorr(kFALSE);
+  unpacker_rich->SetWriteOutputFlag(kFALSE);  /// Needed to avoid bug with FairRoot not checking if Data Sink exists
+  // --- Source task
+  CbmMcbm2018Source* source = new CbmMcbm2018Source();
+
+  if ("" != inFile) { source->SetFileName(inFile); }
+  else {
+    source->SetHostName(sHostname);
+  }
+
+  source->AddUnpacker(unpacker_rich, 0x30, ECbmModuleId::kRich);  //RICH trb
+
+  source->SetSubscriberHwm(1000);
+
+  // --- Run
+  run = new FairRunOnline(source);
+  run->ActivateHttpServer(iServerRefreshRate,
+                          iServerHttpPort);  // refresh each 100 events
+  /// To avoid the server sucking all Histos from gROOT when no output file is used
+  /// ===> Need to explicitely add the canvases to the server in the task!
+  run->GetHttpServer()->GetSniffer()->SetScanGlobalDir(kFALSE);
+  run->SetAutoFinish(kFALSE);
+
+
+  // -----   Runtime database   ---------------------------------------------
+  FairRuntimeDb* rtdb       = run->GetRuntimeDb();
+  FairParAsciiFileIo* parIn = new FairParAsciiFileIo();
+  parIn->open(parFileList, "in");
+  rtdb->setFirstInput(parIn);
+
+  run->Init();
+
+  // --- Start run
+  TStopwatch timer;
+  timer.Start();
+  std::cout << ">>> MonitorRich: Starting run..." << std::endl;
+  if (0 == nrEvents) {
+    run->Run(nEvents, 0);  // run until end of input file
+  }
+  else {
+    run->Run(0, nrEvents);  // process  2000 Events
+  }
+  run->Finish();
+
+  timer.Stop();
+
+  std::cout << "Processed " << std::dec << source->GetTsCount() << " timeslices" << std::endl;
+
+  // --- End-of-run info
+  Double_t rtime = timer.RealTime();
+  Double_t ctime = timer.CpuTime();
+  std::cout << std::endl << std::endl;
+  std::cout << ">>> MonitorRich: Macro finished successfully." << std::endl;
+  std::cout << ">>> MonitorRich: Real time " << rtime << " s, CPU time " << ctime << " s" << std::endl;
+  std::cout << std::endl;
+
+  /// --- Screen output for automatic tests
+  std::cout << " Test passed" << std::endl;
+  std::cout << " All ok " << std::endl;
+}
-- 
GitLab