From b24d66b11fbb6f96d25fcf2d0d1489923e1091d2 Mon Sep 17 00:00:00 2001
From: P-A Loizeau <p.-a.loizeau@gsi.de>
Date: Mon, 19 Jul 2021 22:34:23 +0200
Subject: [PATCH] Add TOF CRI unpacker in new scheme. Include temp fix for
 strange epoch offset.

Changed severity for large amount of LOG statements

Changed default from info to debug for a larger amount of tof log messages. Otherwise, it makes info reading with all subsystems almost impossible.
---
 core/data/CbmTsEventHeader.h                  |   5 +
 core/data/raw/CriGet4Mess001.cxx              |  16 +-
 core/data/raw/CriGet4Mess001.h                |  10 +-
 core/detectors/tof/CbmMcbm2018TofPar.cxx      |  62 ++-
 core/detectors/tof/CbmMcbm2018TofPar.h        |  28 +-
 fles/mcbm2018/unpacker/CbmCriGet4RawPrint.cxx | 141 +++---
 macro/beamtime/mcbm2021/.gitignore            |   1 +
 macro/beamtime/mcbm2021/check_timing_any.C    | 137 ++++++
 macro/beamtime/mcbm2021/mTofCriPar.par        |  35 ++
 macro/run/run_unpack_tsa.C                    |  17 +-
 .../detectors/sts/unpack/CbmStsUnpackAlgo.cxx |   1 -
 reco/detectors/tof/CMakeLists.txt             |  28 +-
 reco/detectors/tof/CbmTofRecoLinkDef.h        |   3 +
 .../detectors/tof/unpack/CbmTofUnpackAlgo.cxx | 403 ++++++++++++++++++
 reco/detectors/tof/unpack/CbmTofUnpackAlgo.h  | 191 +++++++++
 .../tof/unpack/CbmTofUnpackConfig.cxx         |  67 +++
 .../detectors/tof/unpack/CbmTofUnpackConfig.h |  90 ++++
 reco/steer/CMakeLists.txt                     |   4 +-
 reco/steer/CbmRecoUnpack.cxx                  |  12 +-
 reco/steer/CbmRecoUnpack.h                    |  20 +-
 20 files changed, 1112 insertions(+), 159 deletions(-)
 create mode 100644 macro/beamtime/mcbm2021/check_timing_any.C
 create mode 100644 macro/beamtime/mcbm2021/mTofCriPar.par
 create mode 100644 reco/detectors/tof/unpack/CbmTofUnpackAlgo.cxx
 create mode 100644 reco/detectors/tof/unpack/CbmTofUnpackAlgo.h
 create mode 100644 reco/detectors/tof/unpack/CbmTofUnpackConfig.cxx
 create mode 100644 reco/detectors/tof/unpack/CbmTofUnpackConfig.h

diff --git a/core/data/CbmTsEventHeader.h b/core/data/CbmTsEventHeader.h
index dbc8a2b456..70bed2b20b 100644
--- a/core/data/CbmTsEventHeader.h
+++ b/core/data/CbmTsEventHeader.h
@@ -34,6 +34,8 @@ public:
   ULong64_t GetNDigisTrd(ULong64_t) { return fNDigisTrd; }
   /** @brief Get the number of digis in this Ts */
   ULong64_t GetNDigisTrd2D(ULong64_t) { return fNDigisTrd2D; }
+  /** @brief Get the number of digis in this Ts */
+  ULong64_t GetNDigisTof(ULong64_t) { return fNDigisTof; }
 
   /** @brief Set the Ts Start Time @param value Start time of the TS */
   void SetTsStartTime(uint64_t value) { fTsStartTime = value; }
@@ -48,6 +50,8 @@ public:
   void SetNDigisTrd(ULong64_t value) { fNDigisTrd = value; }
   /** @brief Set the number of digis in this Ts */
   void SetNDigisTrd2D(ULong64_t value) { fNDigisTrd2D = value; }
+  /** @brief Set the number of digis in this Ts */
+  void SetNDigisTof(ULong64_t value) { fNDigisTof = value; }
 
 
 protected:
@@ -59,6 +63,7 @@ protected:
   ULong64_t fNDigisSts   = 0;
   ULong64_t fNDigisTrd   = 0;
   ULong64_t fNDigisTrd2D = 0;
+  ULong64_t fNDigisTof   = 0;
 
 
   ClassDef(CbmTsEventHeader, 3)
diff --git a/core/data/raw/CriGet4Mess001.cxx b/core/data/raw/CriGet4Mess001.cxx
index 4728a47069..562462c4dc 100644
--- a/core/data/raw/CriGet4Mess001.cxx
+++ b/core/data/raw/CriGet4Mess001.cxx
@@ -166,7 +166,10 @@ void critof001::Message::printDataCout(unsigned kind, uint32_t epoch) const { pr
  * documentation.
  */
 
-void critof001::Message::printDataLog(unsigned kind, uint32_t epoch) const { printData(msg_print_FairLog, kind, epoch); }
+void critof001::Message::printDataLog(unsigned kind, uint32_t epoch) const
+{
+  printData(msg_print_FairLog, kind, epoch);
+}
 
 //----------------------------------------------------------------------------
 //! Print message in binary or human readable format to a stream.
@@ -207,9 +210,8 @@ void critof001::Message::printData(unsigned outType, unsigned kind, uint32_t epo
              arr[0], arr[1], arr[2], arr[3], arr[4], arr[5], arr[6], arr[7], arr[7], arr[6], arr[5], arr[4], arr[3],
              arr[2], arr[1], arr[0]);
     */
-    snprintf(buf, sizeof(buf),
-             "LE= %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X ",
-             arr[7], arr[6], arr[5], arr[4], arr[3], arr[2], arr[1], arr[0]);
+    snprintf(buf, sizeof(buf), "LE= %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X ", arr[7], arr[6], arr[5], arr[4], arr[3],
+             arr[2], arr[1], arr[0]);
 
 
     if (msg_print_Cout == outType) std::cout << buf;
@@ -234,8 +236,8 @@ void critof001::Message::printData(unsigned outType, unsigned kind, uint32_t epo
         snprintf(buf, sizeof(buf),
                  "EPOCH @%17.11f Get4:%2d Epoche2:%10u 0x%08x Sync:%x "
                  "Dataloss:%x Epochloss:%x Epochmissmatch:%x",
-                 timeInSec, getGet4Idx(), getGdpbEpEpochNb(), getGdpbEpEpochNb(), getGdpbEpSync(),
-                 getGdpbEpDataLoss(), getGdpbEpEpochLoss(), getGdpbEpMissmatch());
+                 timeInSec, getGet4Idx(), getGdpbEpEpochNb(), getGdpbEpEpochNb(), getGdpbEpSync(), getGdpbEpDataLoss(),
+                 getGdpbEpEpochLoss(), getGdpbEpMissmatch());
 
         if (msg_print_Cout == outType) std::cout << buf << std::endl;
         else if (msg_print_File == outType)
@@ -275,7 +277,7 @@ void critof001::Message::printData(unsigned outType, unsigned kind, uint32_t epo
   if (kind & msg_print_Data) {
     //      const uint8_t* arr = reinterpret_cast<const uint8_t*> ( &data );
     switch (getMessageType()) {
-/*
+        /*
       case MSG_HIT: {
         snprintf(buf, sizeof(buf),
                  "Get4 32 bits, Get4:%3d Channel %1d Ts:0x%03x Ft:0x%02x "
diff --git a/core/data/raw/CriGet4Mess001.h b/core/data/raw/CriGet4Mess001.h
index aea57694a0..69ede4b8f5 100644
--- a/core/data/raw/CriGet4Mess001.h
+++ b/core/data/raw/CriGet4Mess001.h
@@ -39,7 +39,7 @@ namespace critof001
   // alternatively: (kiCoarseTime>>kiCtShift + 1)*kdClockCycleSize
   const double kdEpochInPs = static_cast<double>(kuCoarseCounterSize) * kdClockCycleSize;
   const double kdEpochInNs = kdEpochInPs / 1000.0;
-  const double kuEpochInNs = static_cast<uint64_t>(kdEpochInNs); /// Works as epoch integer in ns!
+  const double kuEpochInNs = static_cast<uint64_t>(kdEpochInNs);  /// Works as epoch integer in ns!
 
   // Epoch counter size in epoch
   const uint32_t kuEpochCounterSz = 0xFFFFFF;
@@ -65,10 +65,10 @@ namespace critof001
 
   enum MessageTypes
   {
-    MSG_HIT        = 0,
-    MSG_EPOCH      = 1,
-    MSG_SLOWC      = 2,
-    MSG_SYST       = 3
+    MSG_HIT   = 0,
+    MSG_EPOCH = 1,
+    MSG_SLOWC = 2,
+    MSG_SYST  = 3
   };
 
   enum SysMessageTypes
diff --git a/core/detectors/tof/CbmMcbm2018TofPar.cxx b/core/detectors/tof/CbmMcbm2018TofPar.cxx
index 145ab9684d..371274b81a 100644
--- a/core/detectors/tof/CbmMcbm2018TofPar.cxx
+++ b/core/detectors/tof/CbmMcbm2018TofPar.cxx
@@ -9,6 +9,9 @@
 
 #include "CbmMcbm2018TofPar.h"
 
+#include "CbmTofAddress.h"
+#include "CbmTofDetectorId_v14a.h"  // in cbmdata/tof
+
 #include "FairDetParIo.h"
 #include "FairParIo.h"
 #include "FairParamList.h"
@@ -17,8 +20,6 @@
 #include "TString.h"
 
 #include "gDpbMessv100.h"
-#include "CbmTofAddress.h"
-#include "CbmTofDetectorId_v14a.h"  // in cbmdata/tof
 
 // -----   Standard constructor   ------------------------------------------
 CbmMcbm2018TofPar::CbmMcbm2018TofPar(const char* name, const char* title, const char* context)
@@ -205,8 +206,8 @@ Double_t CbmMcbm2018TofPar::GetPadiThresholdVal(UInt_t uCode)
 // -------------------------------------------------------------------------
 void CbmMcbm2018TofPar::BuildChannelsUidMap()
 {
-  UInt_t uNrOfGbtx = fiNrOfGbtx;
-  UInt_t uNrOfGet4 = fiNrOfGdpb * fiNrOfFeesPerGdpb * fiNrOfGet4PerFee;
+  UInt_t uNrOfGbtx     = fiNrOfGbtx;
+  UInt_t uNrOfGet4     = fiNrOfGdpb * fiNrOfFeesPerGdpb * fiNrOfGet4PerFee;
   UInt_t uNrOfChannels = uNrOfGet4 * fiNrOfChannelsPerGet4;
   fviRpcChUId.resize(uNrOfChannels);
 
@@ -258,18 +259,17 @@ void CbmMcbm2018TofPar::BuildChannelsUidMap()
       default: {
         LOG(error) << "Invalid Tof Type  specifier ";
       }
-    } // switch (fiRpcType[uGbtx])
-  } // for (UInt_t uGbtx = 0; uGbtx < uNrOfGbtx; ++uGbtx)
-
+    }  // switch (fiRpcType[uGbtx])
+  }    // for (UInt_t uGbtx = 0; uGbtx < uNrOfGbtx; ++uGbtx)
 }
 // -------------------------------------------------------------------------
-void CbmMcbm2018TofPar::BuildChannelsUidMapCbm(UInt_t & uCh, UInt_t uGbtx)
+void CbmMcbm2018TofPar::BuildChannelsUidMapCbm(UInt_t& uCh, UInt_t uGbtx)
 {
   if (fiRpcSide[uGbtx] < 2) {  // mTof modules
     LOG(info) << " Map mTof box " << fiModuleId[uGbtx] << " at GBTX  -  uCh = " << uCh;
     const Int_t RpcMap[5] = {4, 2, 0, 3, 1};
     for (Int_t iRpc = 0; iRpc < fiNrOfRpc[uGbtx]; iRpc++) {
-      Int_t iStrMax = 32;
+      Int_t iStrMax  = 32;
       UInt_t uChNext = 1;
 
       for (Int_t iStr = 0; iStr < iStrMax; iStr++) {
@@ -278,18 +278,18 @@ void CbmMcbm2018TofPar::BuildChannelsUidMapCbm(UInt_t & uCh, UInt_t uGbtx)
 
         if (fiRpcSide[uGbtx] == 0) iStrMap = 31 - iStr;
         if (fiModuleId[uGbtx] > -1)
-          fviRpcChUId[uCh] = CbmTofAddress::GetUniqueAddress(fiModuleId[uGbtx], iRpcMap, iStrMap,
-                                                             fiRpcSide[uGbtx], fiRpcType[uGbtx]);
+          fviRpcChUId[uCh] =
+            CbmTofAddress::GetUniqueAddress(fiModuleId[uGbtx], iRpcMap, iStrMap, fiRpcSide[uGbtx], fiRpcType[uGbtx]);
         else
           fviRpcChUId[uCh] = 0;
         //  LOG(debug)<<Form("Map Ch %d to Address 0x%08x",uCh,fviRpcChUId[uCh]);
         uCh += uChNext;
-      } // for (Int_t iStr = 0; iStr < iStrMax; iStr++)
-    } // for (Int_t iRpc = 0; iRpc < fiNrOfRpc[uGbtx]; iRpc++)
-  } // if (fiRpcSide[uGbtx] < 2)
+      }  // for (Int_t iStr = 0; iStr < iStrMax; iStr++)
+    }    // for (Int_t iRpc = 0; iRpc < fiNrOfRpc[uGbtx]; iRpc++)
+  }      // if (fiRpcSide[uGbtx] < 2)
 }
 // -------------------------------------------------------------------------
-void CbmMcbm2018TofPar::BuildChannelsUidMapStar(UInt_t & uCh, UInt_t uGbtx)
+void CbmMcbm2018TofPar::BuildChannelsUidMapStar(UInt_t& uCh, UInt_t uGbtx)
 {
   if (fiRpcSide[uGbtx] < 2) {
     // mTof modules
@@ -305,8 +305,8 @@ void CbmMcbm2018TofPar::BuildChannelsUidMapStar(UInt_t & uCh, UInt_t uGbtx)
 
         if (fiRpcSide[uGbtx] == 0) iStrMap = 31 - iStr;
         if (fiModuleId[uGbtx] > -1)
-          fviRpcChUId[uCh] = CbmTofAddress::GetUniqueAddress(fiModuleId[uGbtx], iRpcMap, iStrMap,
-                                                             fiRpcSide[uGbtx], fiRpcType[uGbtx]);
+          fviRpcChUId[uCh] =
+            CbmTofAddress::GetUniqueAddress(fiModuleId[uGbtx], iRpcMap, iStrMap, fiRpcSide[uGbtx], fiRpcType[uGbtx]);
         else
           fviRpcChUId[uCh] = 0;
         //  LOG(DEBUG)<<Form("Map Ch %d to Address 0x%08x",uCh,fviRpcChUId[uCh]);
@@ -317,7 +317,7 @@ void CbmMcbm2018TofPar::BuildChannelsUidMapStar(UInt_t & uCh, UInt_t uGbtx)
   uCh += 64;
 }
 // -------------------------------------------------------------------------
-void CbmMcbm2018TofPar::BuildChannelsUidMapT0(UInt_t & uCh, UInt_t uGbtx)
+void CbmMcbm2018TofPar::BuildChannelsUidMapT0(UInt_t& uCh, UInt_t uGbtx)
 {
   LOG(info) << " Map diamond  at GBTX  -  uCh = " << uCh;
   for (UInt_t uFee = 0; uFee < kuNbFeePerGbtx; ++uFee) {
@@ -333,8 +333,7 @@ void CbmMcbm2018TofPar::BuildChannelsUidMapT0(UInt_t & uCh, UInt_t uGbtx)
             /// => 1 T0 channel per GET4
             /// => 1-2 eLinks per GET4 => GET4 ID = GET4 * 2 (+ 1)
             UInt_t uChannelT0 = uFeeCh / 8 + 4 * fiRpcSide[uGbtx];
-            fviRpcChUId[uCh] =
-              CbmTofAddress::GetUniqueAddress(fiModuleId[uGbtx], 0, uChannelT0, 0, fiRpcType[uGbtx]);
+            fviRpcChUId[uCh]  = CbmTofAddress::GetUniqueAddress(fiModuleId[uGbtx], 0, uChannelT0, 0, fiRpcType[uGbtx]);
             LOG(info) << Form("T0 channel: %u from GBTx %2u Fee %2u "
                               "Channel %2u, indx %d address %08x",
                               uChannelT0, uGbtx, uFeeCh, uCh, uCh, fviRpcChUId[uCh]);
@@ -351,7 +350,7 @@ void CbmMcbm2018TofPar::BuildChannelsUidMapT0(UInt_t & uCh, UInt_t uGbtx)
   }    // for( UInt_t uFee = 0; uFee < kuNbChannelsPerFee; ++uFee )
 }
 // -------------------------------------------------------------------------
-void CbmMcbm2018TofPar::BuildChannelsUidMapCern(UInt_t & uCh, UInt_t /*uGbtx*/)
+void CbmMcbm2018TofPar::BuildChannelsUidMapCern(UInt_t& uCh, UInt_t /*uGbtx*/)
 {
   LOG(info) << " Map CERN 20 gap  at GBTX  -  uCh = " << uCh;
   // clang-format off
@@ -376,7 +375,7 @@ void CbmMcbm2018TofPar::BuildChannelsUidMapCern(UInt_t & uCh, UInt_t /*uGbtx*/)
   LOG(info) << " Map end CERN 20 gap  at GBTX  -  uCh = " << uCh;
 }
 // -------------------------------------------------------------------------
-void CbmMcbm2018TofPar::BuildChannelsUidMapCera(UInt_t & uCh, UInt_t /*uGbtx*/)
+void CbmMcbm2018TofPar::BuildChannelsUidMapCera(UInt_t& uCh, UInt_t /*uGbtx*/)
 {
   Int_t iModuleId   = 0;
   Int_t iModuleType = 8;
@@ -388,7 +387,7 @@ void CbmMcbm2018TofPar::BuildChannelsUidMapCera(UInt_t & uCh, UInt_t /*uGbtx*/)
   LOG(info) << " Map end ceramics  box  at GBTX  -  uCh = " << uCh;
 }
 // -------------------------------------------------------------------------
-void CbmMcbm2018TofPar::BuildChannelsUidMapStar2(UInt_t & uCh, UInt_t uGbtx)
+void CbmMcbm2018TofPar::BuildChannelsUidMapStar2(UInt_t& uCh, UInt_t uGbtx)
 {
   LOG(info) << " Map Star2 box " << fiModuleId[uGbtx] << " at GBTX  -  uCh = " << uCh;
   const Int_t iRpc[5]  = {1, -1, 1, 0, 0};
@@ -429,11 +428,11 @@ void CbmMcbm2018TofPar::BuildChannelsUidMapStar2(UInt_t & uCh, UInt_t uGbtx)
   }
 }
 // -------------------------------------------------------------------------
-void CbmMcbm2018TofPar::BuildChannelsUidMapBuc(UInt_t & uCh, UInt_t uGbtx)
+void CbmMcbm2018TofPar::BuildChannelsUidMapBuc(UInt_t& uCh, UInt_t uGbtx)
 {
   LOG(info) << " Map Buc box  at GBTX  -  uCh = " << uCh;
 
-  Int_t iModuleIdMap = fiModuleId[uGbtx];
+  Int_t iModuleIdMap   = fiModuleId[uGbtx];
   const Int_t iRpc[5]  = {0, -1, 0, 1, 1};
   const Int_t iSide[5] = {1, -1, 0, 1, 0};
   for (Int_t iFeet = 0; iFeet < 5; iFeet++) {
@@ -517,20 +516,19 @@ void CbmMcbm2018TofPar::BuildChannelsUidMapBuc(UInt_t & uCh, UInt_t uGbtx)
           }
           iModuleIdMap = fiModuleId[uGbtx];
           LOG(info) << "Buc of GBTX " << uGbtx << " Ch " << uCh
-                    << Form(", Feet %1d, Str %2d, Ind %3d, i %3d, FeetInd %1d, Rpc %1d, Side %1d, Str %2d ",
-                            iFeet, iStr, iInd, i, iFeetInd, iRpcMap, iSideMap, iStrMap);
+                    << Form(", Feet %1d, Str %2d, Ind %3d, i %3d, FeetInd %1d, Rpc %1d, Side %1d, Str %2d ", iFeet,
+                            iStr, iInd, i, iFeetInd, iRpcMap, iSideMap, iStrMap);
         } break;
         default:;
-      } // switch (fiRpcSide[uGbtx])
+      }  // switch (fiRpcSide[uGbtx])
       if (iSideMap > -1)
-        fviRpcChUId[uCh] =
-          CbmTofAddress::GetUniqueAddress(iModuleIdMap, iRpcMap, iStrMap, iSideMap, fiRpcType[uGbtx]);
+        fviRpcChUId[uCh] = CbmTofAddress::GetUniqueAddress(iModuleIdMap, iRpcMap, iStrMap, iSideMap, fiRpcType[uGbtx]);
       else
         fviRpcChUId[uCh] = 0;
 
       uCh++;
-    } // for (Int_t iStr = 0; iStr < 32; iStr++)
-  } // for (Int_t iFeet = 0; iFeet < 5; iFeet++)
+    }  // for (Int_t iStr = 0; iStr < 32; iStr++)
+  }    // for (Int_t iFeet = 0; iFeet < 5; iFeet++)
 }
 // -------------------------------------------------------------------------
 
diff --git a/core/detectors/tof/CbmMcbm2018TofPar.h b/core/detectors/tof/CbmMcbm2018TofPar.h
index 6657d5224c..e134514187 100644
--- a/core/detectors/tof/CbmMcbm2018TofPar.h
+++ b/core/detectors/tof/CbmMcbm2018TofPar.h
@@ -72,7 +72,7 @@ public:
   inline Int_t GetRpcType(Int_t i) { return fiRpcType[i]; }
   inline Int_t GetRpcSide(Int_t i) { return fiRpcSide[i]; }
   inline Int_t GetModuleId(Int_t i) { return fiModuleId[i]; }
-  inline std::vector< Int_t > GetRpcChUidMap() { return fviRpcChUId; }
+  inline std::vector<Int_t> GetRpcChUidMap() { return fviRpcChUId; }
 
   inline Int_t GetNbMsTot() { return fiNbMsTot; }
   inline Int_t GetNbMsOverlap() { return fiNbMsOverlap; }
@@ -85,13 +85,13 @@ public:
 
 private:
   void BuildChannelsUidMap();
-  void BuildChannelsUidMapCbm(UInt_t & uCh, UInt_t uGbtx);
-  void BuildChannelsUidMapStar(UInt_t & uCh, UInt_t uGbtx);
-  void BuildChannelsUidMapT0(UInt_t & uCh, UInt_t uGbtx);
-  void BuildChannelsUidMapCern(UInt_t & uCh, UInt_t uGbtx);
-  void BuildChannelsUidMapCera(UInt_t & uCh, UInt_t uGbtx);
-  void BuildChannelsUidMapStar2(UInt_t & uCh, UInt_t uGbtx);
-  void BuildChannelsUidMapBuc(UInt_t & uCh, UInt_t uGbtx);
+  void BuildChannelsUidMapCbm(UInt_t& uCh, UInt_t uGbtx);
+  void BuildChannelsUidMapStar(UInt_t& uCh, UInt_t uGbtx);
+  void BuildChannelsUidMapT0(UInt_t& uCh, UInt_t uGbtx);
+  void BuildChannelsUidMapCern(UInt_t& uCh, UInt_t uGbtx);
+  void BuildChannelsUidMapCera(UInt_t& uCh, UInt_t uGbtx);
+  void BuildChannelsUidMapStar2(UInt_t& uCh, UInt_t uGbtx);
+  void BuildChannelsUidMapBuc(UInt_t& uCh, UInt_t uGbtx);
 
   /// Constants
   /// Data format
@@ -146,12 +146,12 @@ private:
   Int_t fiNrOfGet4PerFee;       // Number of GET4 chips which are connected to one FEB
   Int_t fiNrOfChannelsPerGet4;  // Number of channels per GET4
 
-  Int_t fiNrOfGbtx;    // Total number of Gbtx links
-  Int_t fiNrOfModule;  // Total number of Modules
-  TArrayI fiNrOfRpc;   // number of Rpcs connected to Gbtx link, i.e. 3 or 5
-  TArrayI fiRpcType;   // type of Rpcs connected to Gbtx link
-  TArrayI fiRpcSide;   // side of Rpcs connected to Gbtx link, i.e. 0 or 1
-  TArrayI fiModuleId;  // Module Identifier connected to Gbtx link, has to match geometry
+  Int_t fiNrOfGbtx;                     // Total number of Gbtx links
+  Int_t fiNrOfModule;                   // Total number of Modules
+  TArrayI fiNrOfRpc;                    // number of Rpcs connected to Gbtx link, i.e. 3 or 5
+  TArrayI fiRpcType;                    // type of Rpcs connected to Gbtx link
+  TArrayI fiRpcSide;                    // side of Rpcs connected to Gbtx link, i.e. 0 or 1
+  TArrayI fiModuleId;                   // Module Identifier connected to Gbtx link, has to match geometry
   std::vector<Int_t> fviRpcChUId = {};  // UID/address for each channel, build from type, side and module
 
   Int_t fiNbMsTot;        // Total number of MS per link in TS
diff --git a/fles/mcbm2018/unpacker/CbmCriGet4RawPrint.cxx b/fles/mcbm2018/unpacker/CbmCriGet4RawPrint.cxx
index bd8f30be67..fd0713209b 100644
--- a/fles/mcbm2018/unpacker/CbmCriGet4RawPrint.cxx
+++ b/fles/mcbm2018/unpacker/CbmCriGet4RawPrint.cxx
@@ -44,10 +44,7 @@ Bool_t CbmCriGet4RawPrint::Init()
   return kTRUE;
 }
 
-void CbmCriGet4RawPrint::SetParContainers()
-{
-  LOG(info) << "Setting parameter containers for " << GetName();
-}
+void CbmCriGet4RawPrint::SetParContainers() { LOG(info) << "Setting parameter containers for " << GetName(); }
 
 Bool_t CbmCriGet4RawPrint::InitContainers()
 {
@@ -80,7 +77,7 @@ Bool_t CbmCriGet4RawPrint::DoUnpack(const fles::Timeslice& ts, size_t /*componen
 {
 
   static const uint8_t NGET4 = 80;
-  static const uint8_t NERROR = 0x16;
+  //   static const uint8_t NERROR = 0x16;
 
   char buf[256];
 
@@ -88,11 +85,11 @@ Bool_t CbmCriGet4RawPrint::DoUnpack(const fles::Timeslice& ts, size_t /*componen
   uint32_t nGet4, epoch, msgType, errorCode;
   static int32_t pEpochDiff[NGET4];
   int32_t epochDiff;
-  static uint32_t pErrorCnt[NGET4]={ 0 };
-  static uint32_t pHitsCnt[NGET4]={ 0 };
-  static uint32_t pTotCnt[NGET4]={ 0 };
+  //   static uint32_t pErrorCnt[NGET4] = {0};
+  //   static uint32_t pHitsCnt[NGET4]  = {0};
+  //   static uint32_t pTotCnt[NGET4]   = {0};
 
-  static uint32_t pErrorCntMatrix[NGET4][NERROR];
+  //   static uint32_t pErrorCntMatrix[NGET4][NERROR];
 
   static uint32_t procEpochUntilError = 0;
 
@@ -162,74 +159,66 @@ Bool_t CbmCriGet4RawPrint::DoUnpack(const fles::Timeslice& ts, size_t /*componen
         //mess.printDataCout( critof001::msg_print_Hex | critof001::msg_print_Prefix | critof001::msg_print_Data );
 
         msgType = ulData & 0xF;
-        nGet4 = (ulData>>40) & 0xFF;
-        epoch = (ulData>>8) & 0xFFFFFF;
-		epoch &= 0xFFFFFF;
-		errorCode = (ulData>>4) & 0x7F;
-		/*snprintf(buf, sizeof(buf),
+        nGet4   = (ulData >> 40) & 0xFF;
+        epoch   = (ulData >> 8) & 0xFFFFFF;
+        epoch &= 0xFFFFFF;
+        errorCode = (ulData >> 4) & 0x7F;
+        /*snprintf(buf, sizeof(buf),
                  "Data: 0x%016lx - %d - 0x06%X ",
                  ulData, nGet4, epoch);
 
 				std::cout << buf << std::endl;
 		*/
 
-		//if (fuCurrentEquipmentId == 0xabc0)
-		{
-			//------------------- TLAST ----------------------------//
-			if ((ulData & 0xFFFFFFFFFFFF)==0xdeadbeeeeeef)
-			{
-			}
-			//------------------- EPOCH ----------------------------//
-			else if (msgType == 0x01)
-			{
-				if (nGet4 == 0xFF) {
-
-					procEpochUntilError++;
-					if (lastGlobalEpoch!=0xFFFFFF){
-						if ((lastGlobalEpoch + 1) != epoch){
-							snprintf(buf, sizeof(buf),
-							 "Error global epoch, last epoch, current epoch, diff  0x%06x 0x%06x %d 0x%016lx %d",
-							 lastGlobalEpoch, epoch, lastGlobalEpoch - epoch, ulData, procEpochUntilError);
-
-							 std::cout << buf << std::endl;
-							 procEpochUntilError=0;
-						}
-					}
-					else{
-						snprintf(buf, sizeof(buf),
-							 "Global epoch overflow, last epoch, current epoch  0x%06x 0x%06x",
-							 lastGlobalEpoch, epoch);
-
-							 std::cout << buf << std::endl;
-					}
-
-
-					lastGlobalEpoch = epoch;
-					snprintf(buf, sizeof(buf),"Global epoch %d",epoch);
-					std::cout << Form("%5d/%5d ", uIdx, uNbMessages) <<  buf << std::endl;
-
-
-				}
-				else if (nGet4 <= 120){
-
-					if (lastGlobalEpoch > epoch)
-						epochDiff = lastGlobalEpoch - epoch;
-					else
-						epochDiff = 0xFFFFFF + lastGlobalEpoch - epoch;
-
-					if (epochDiff != pEpochDiff[nGet4]){
-						snprintf(buf, sizeof(buf),
-						 "eTime %d - Error epoch drift Get4 %3d , last epoch diff, current epoch  diff  0x%06x 0x%06x %d",
-						 lastGlobalEpoch, nGet4, pEpochDiff[nGet4], epochDiff, pEpochDiff[nGet4]-epochDiff);
-						 std::cout << buf << std::endl;
-						 mess.printDataCout( critof001::msg_print_Hex | critof001::msg_print_Prefix | critof001::msg_print_Data );
-
-					}
-					pEpochDiff[nGet4] = epochDiff;
-
-				}
-			}
-			/*
+        //if (fuCurrentEquipmentId == 0xabc0)
+        {
+          //------------------- TLAST ----------------------------//
+          if ((ulData & 0xFFFFFFFFFFFF) == 0xdeadbeeeeeef) {}
+          //------------------- EPOCH ----------------------------//
+          else if (msgType == 0x01) {
+            if (nGet4 == 0xFF) {
+
+              procEpochUntilError++;
+              if (lastGlobalEpoch != 0xFFFFFF) {
+                if ((lastGlobalEpoch + 1) != epoch) {
+                  snprintf(buf, sizeof(buf),
+                           "Error global epoch, last epoch, current epoch, diff  0x%06x 0x%06x %d 0x%016lx %d",
+                           lastGlobalEpoch, epoch, lastGlobalEpoch - epoch, ulData, procEpochUntilError);
+
+                  std::cout << buf << std::endl;
+                  procEpochUntilError = 0;
+                }
+              }
+              else {
+                snprintf(buf, sizeof(buf), "Global epoch overflow, last epoch, current epoch  0x%06x 0x%06x",
+                         lastGlobalEpoch, epoch);
+
+                std::cout << buf << std::endl;
+              }
+
+
+              lastGlobalEpoch = epoch;
+              snprintf(buf, sizeof(buf), "Global epoch %d", epoch);
+              std::cout << Form("%5d/%5d ", uIdx, uNbMessages) << buf << std::endl;
+            }
+            else if (nGet4 <= 120) {
+
+              if (lastGlobalEpoch > epoch) epochDiff = lastGlobalEpoch - epoch;
+              else
+                epochDiff = 0xFFFFFF + lastGlobalEpoch - epoch;
+
+              if (epochDiff != pEpochDiff[nGet4]) {
+                snprintf(
+                  buf, sizeof(buf),
+                  "eTime %d - Error epoch drift Get4 %3d , last epoch diff, current epoch  diff  0x%06x 0x%06x %d",
+                  lastGlobalEpoch, nGet4, pEpochDiff[nGet4], epochDiff, pEpochDiff[nGet4] - epochDiff);
+                std::cout << buf << std::endl;
+                mess.printDataCout(critof001::msg_print_Hex | critof001::msg_print_Prefix | critof001::msg_print_Data);
+              }
+              pEpochDiff[nGet4] = epochDiff;
+            }
+          }
+          /*
 			//------------------- CTRL ----------------------------//
 			else if (msgType == 0x02)
 			{
@@ -269,12 +258,12 @@ Bool_t CbmCriGet4RawPrint::DoUnpack(const fles::Timeslice& ts, size_t /*componen
 					pHitsCnt[nGet4]=pHitsCnt[nGet4]+1;
 			}
 			 */
-			/*snprintf(buf, sizeof(buf),
+          /*snprintf(buf, sizeof(buf),
 					 "Data: 0x%016lx",
 					 ulData);
 
 			std::cout << buf << std::endl;*/
-		}
+        }
 
       }  // for (uint32_t uIdx = 0; uIdx < uNbMessages; uIdx ++)
     }    // for( fuMsIndex = 0; fuMsIndex < uNbMsLoop; fuMsIndex ++ )
@@ -282,7 +271,7 @@ Bool_t CbmCriGet4RawPrint::DoUnpack(const fles::Timeslice& ts, size_t /*componen
 
   if (0 == fulCurrentTsIdx % 10000) LOG(info) << "Processed TS " << fulCurrentTsIdx;
 
-	/*
+  /*
 	uint32_t nPulses = 4*10000;
 	float effi;
 	for(uint32_t i =0; i < NGET4 ; i++)
@@ -307,9 +296,7 @@ Bool_t CbmCriGet4RawPrint::DoUnpack(const fles::Timeslice& ts, size_t /*componen
   return kTRUE;
 }
 
-void CbmCriGet4RawPrint::Reset()
-{
-}
+void CbmCriGet4RawPrint::Reset() {}
 
 void CbmCriGet4RawPrint::Finish() {}
 
diff --git a/macro/beamtime/mcbm2021/.gitignore b/macro/beamtime/mcbm2021/.gitignore
index 4b257ff75e..425dde88f8 100644
--- a/macro/beamtime/mcbm2021/.gitignore
+++ b/macro/beamtime/mcbm2021/.gitignore
@@ -1,3 +1,4 @@
 .root_hist
 .rootrc
 rootlogon.C
+*.root
diff --git a/macro/beamtime/mcbm2021/check_timing_any.C b/macro/beamtime/mcbm2021/check_timing_any.C
new file mode 100644
index 0000000000..e3b2cef535
--- /dev/null
+++ b/macro/beamtime/mcbm2021/check_timing_any.C
@@ -0,0 +1,137 @@
+/* Copyright (C) 2020 Facility for Antiproton and Ion Research in Europe, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pierre-Alain Loizeau [committer] */
+
+void check_timing_any(TString fileName, UInt_t uRunId = 0, Int_t nEvents = 0, TString outDir = "data/")
+{
+
+  // ========================================================================
+  //          Adjust this part according to your requirements
+
+  // Verbosity level (0=quiet, 1=event level, 2=track level, 3=debug)
+  Int_t iVerbose = 1;
+
+  // MC file
+
+  TString srcDir = gSystem->Getenv("VMCWORKDIR");
+
+  // -----   Timer   --------------------------------------------------------
+  TStopwatch timer;
+  timer.Start();
+  // ------------------------------------------------------------------------
+
+  // -----  Analysis run   --------------------------------------------------
+  FairRunOnline* fRun = new FairRunOnline();
+  fRun->ActivateHttpServer(100, 8080);  // refresh each 100 events
+  fRun->SetSink(new FairRootFileSink("SinkFile.root"));
+  FairFileSource* inputSource = new FairFileSource(fileName);
+  fRun->SetSource(inputSource);
+
+  // Define output file for FairMonitor histograms
+  //  TString monitorFile{outFile};
+  //  monitorFile.ReplaceAll("qa","qa.monitor");
+  FairMonitor::GetMonitor()->EnableMonitor(kFALSE);
+  // ------------------------------------------------------------------------
+
+  CbmMcbmCheckTimingTask* timeChecker = new CbmMcbmCheckTimingTask();
+  /// Default is using T0 as reference
+  /// With Pulser rejection
+  /*
+  timeChecker->SetReferenceDetector( ECbmModuleId::kT0, "T0",
+                                     -1000., 1000., 320.,
+                                     182, 190 );
+*/
+  /// With pulser selection
+  /*
+  timeChecker->SetReferenceDetector( ECbmModuleId::kT0, "T0",
+                                     -1000., 1000., 320.,
+                                     190, 182 );
+*/
+  /// Here swapping with TOF
+
+  timeChecker->SetReferenceDetector(ECbmModuleId::kPsd, "Psd", -300000, 300000, 320 * 300);
+  timeChecker->RemoveCheckDetector(ECbmModuleId::kPsd);
+  //timeChecker->AddCheckDetector(ECbmModuleId::kT0, "T0");
+
+  /// Here swapping with MUCH
+  /*
+  timeChecker->SetReferenceDetector(ECbmModuleId::kMuch, "Much");
+  timeChecker->RemoveCheckDetector(ECbmModuleId::kMuch);
+  timeChecker->AddCheckDetector(ECbmModuleId::kT0, "T0");
+*/
+
+  /// Remove detectors not present in 2021
+  timeChecker->RemoveCheckDetector(ECbmModuleId::kT0);
+  timeChecker->RemoveCheckDetector(ECbmModuleId::kMuch);
+
+  /// Remove detectors not yet in common unpacker
+  timeChecker->RemoveCheckDetector(ECbmModuleId::kSts);
+  timeChecker->RemoveCheckDetector(ECbmModuleId::kRich);
+
+  /// Add detectors with wider range
+  timeChecker->RemoveCheckDetector(ECbmModuleId::kSts);
+  timeChecker->AddCheckDetector(ECbmModuleId::kSts, "Sts");
+  timeChecker->RemoveCheckDetector(ECbmModuleId::kTrd);
+  timeChecker->AddCheckDetector(ECbmModuleId::kTrd, "Trd");
+  timeChecker->RemoveCheckDetector(ECbmModuleId::kTof);
+  //timeChecker->AddCheckDetector(ECbmModuleId::kTof, "Tof", -150000, 150000, 320*150);
+  timeChecker->AddCheckDetector(ECbmModuleId::kTof, "Tof", -2000, 2000, 320 * 2);
+
+  if (0 < uRunId) timeChecker->SetOutFilename(Form("%s/HistosTimeCheck_%03u.root", outDir.Data(), uRunId));
+  fRun->AddTask(timeChecker);
+
+  // -----  Parameter database   --------------------------------------------
+  //  FairRuntimeDb* rtdb = fRun->GetRuntimeDb();
+  //  FairParRootFileIo* parIo1 = new FairParRootFileIo();
+  //  parIo1->open(parFile.Data(),"UPDATE");
+  //  rtdb->setFirstInput(parIo1);
+  // ------------------------------------------------------------------------
+
+
+  // -----   Intialise and run   --------------------------------------------
+  fRun->Init();
+
+  //  rtdb->setOutput(parIo1);
+  //  rtdb->saveOutput();
+  //  rtdb->print();
+
+  cout << "Starting run" << endl;
+  if (0 == nEvents) {
+    fRun->Run(0, 0);  // run until end of input file
+  }
+  else {
+    fRun->Run(0, nEvents);  // process  N Events
+  }
+  // ------------------------------------------------------------------------
+
+
+  // -----   Finish   -------------------------------------------------------
+  timer.Stop();
+  Double_t rtime = timer.RealTime();
+  Double_t ctime = timer.CpuTime();
+  cout << endl << endl;
+  cout << "Macro finished succesfully." << endl;
+  cout << "Real time " << rtime << " s, CPU time " << ctime << " s" << endl;
+  cout << endl;
+  // ------------------------------------------------------------------------
+
+  // Extract the maximal used memory an add is as Dart measurement
+  // This line is filtered by CTest and the value send to CDash
+  FairSystemInfo sysInfo;
+  Float_t maxMemory = sysInfo.GetMaxMemory();
+  cout << "<DartMeasurement name=\"MaxMemory\" type=\"numeric/double\">";
+  cout << maxMemory;
+  cout << "</DartMeasurement>" << endl;
+
+  Float_t cpuUsage = ctime / rtime;
+  cout << "<DartMeasurement name=\"CpuLoad\" type=\"numeric/double\">";
+  cout << cpuUsage;
+  cout << "</DartMeasurement>" << endl;
+  /*
+  FairMonitor* tempMon = FairMonitor::GetMonitor();
+  tempMon->Print();
+*/
+  //  RemoveGeoManager();
+  cout << " Test passed" << endl;
+  cout << " All ok " << endl;
+}
diff --git a/macro/beamtime/mcbm2021/mTofCriPar.par b/macro/beamtime/mcbm2021/mTofCriPar.par
new file mode 100644
index 0000000000..db7993665d
--- /dev/null
+++ b/macro/beamtime/mcbm2021/mTofCriPar.par
@@ -0,0 +1,35 @@
+####################################################################################################
+[CbmMcbm2018TofPar]
+//----------------------------------------------------------------------------
+NrOfGdpbs: Int_t 4
+GdpbIdArray: Int_t \
+0xabc0 0xabc1 0xabc2 0xabc3
+//0x5b7b 0x55c4 0x18c5 0x5f64 0x1889 0x181c 0x1922 0x1925 0x1902
+NrOfFeesPerGdpb: Int_t 10
+NrOfGet4PerFee:  Int_t 8
+NrOfChannelsPerGet4: Int_t 4
+NrOfGbtx: Int_t   8
+NrOfModule: Int_t 4
+NrOfRpc: Int_t \
+  5  5  5  5  5  5  5  5
+RpcType: Int_t \
+  0  0  0  0  2  2  0  0
+RpcSide: Int_t \
+  1  0  1  0  1  0  1  0
+ModuleId: Int_t \
+  0  0  1  1  0  0  2  2
+NbMsTot: Int_t 100
+NbMsOverlap: Int_t 1
+SizeMsInNs: Double_t 102400.0
+//SizeMsInNs: Double_t 1638400
+StarTriggerDeadtime:  Double_t \
+ 1000.0  1000.0  1000.0  1000.0  1000.0
+StarTriggerDelay: Double_t \
+ 2000.0  2000.0  2000.0  2000.0  2000.0
+//  2000.0  2000.0  2000.0  2000.0  2000.0
+//-23000.0  -23000.0  -23000.0  -23000.0  -23000.0
+StarTriggerWinSize: Double_t \
+ 2000.0  2000.0  2000.0  2000.0  2000.0
+TsDeadtimePeriod: Double_t 62.5
+
+####################################################################################################
diff --git a/macro/run/run_unpack_tsa.C b/macro/run/run_unpack_tsa.C
index 221ea7edce..de0cecd813 100644
--- a/macro/run/run_unpack_tsa.C
+++ b/macro/run/run_unpack_tsa.C
@@ -62,6 +62,7 @@ void run_unpack_tsa(std::string infile = "test.tsa", UInt_t runid = 0, const cha
   if (outpath.empty()) { outpath = infile.substr(0, filenamepos); }
   outfilename = outpath + filename;
   outfilename.replace(outfilename.find(".tsa"), 4, ".digi.root");
+  std::cout << "-I- " << myName << ": Output file will be " << outfilename << std::endl;
   // ------------------------------------------------------------------------
 
 
@@ -150,6 +151,17 @@ void run_unpack_tsa(std::string infile = "test.tsa", UInt_t runid = 0, const cha
   }
   // -------------
 
+  // ---- TOF ----
+  auto tofconfig = std::make_shared<CbmTofUnpackConfig>("", runid);
+  // tofconfig->SetDebugState();
+  tofconfig->SetDoWriteOutput();
+  // tofconfig->SetDoWriteOptOutA("CbmTofErrors");
+  std::string parfilesbasepathTof = Form("%s/macro/beamtime/mcbm2021/", srcDir.Data());
+  tofconfig->SetParFilesBasePath(parfilesbasepathTof);
+  //tofconfig->SetSystemTimeOffset(-2221);  // [ns] value to be updated
+  // -------------
+
+
   // ------------------------------------------------------------------------
 
   // In general, the following parts need not be touched
@@ -170,6 +182,7 @@ void run_unpack_tsa(std::string infile = "test.tsa", UInt_t runid = 0, const cha
   if (stsconfig) unpack->SetUnpackConfig(stsconfig);
   if (trd1Dconfig) unpack->SetUnpackConfig(trd1Dconfig);
   if (trdfasp2dconfig) unpack->SetUnpackConfig(trdfasp2dconfig);
+  if (tofconfig) unpack->SetUnpackConfig(tofconfig);
   // ------------------------------------------------------------------------
 
 
@@ -213,7 +226,7 @@ void run_unpack_tsa(std::string infile = "test.tsa", UInt_t runid = 0, const cha
 
 /**
  * @brief Get the Trd Monitor. Extra function to keep default macro part more silent.
- * @return std::shared_ptr<CbmTrdUnpackMonitor> 
+ * @return std::shared_ptr<CbmTrdUnpackMonitor>
 */
 std::shared_ptr<CbmTrdUnpackMonitor> GetTrdMonitor(std::string treefilename)
 {
@@ -270,7 +283,7 @@ std::shared_ptr<CbmTrdUnpackMonitor> GetTrdMonitor(std::string treefilename)
 
 /**
  * @brief Get the Trd Spadic
- * @return std::shared_ptr<CbmTrdSpadic> 
+ * @return std::shared_ptr<CbmTrdSpadic>
 */
 std::shared_ptr<CbmTrdSpadic> GetTrdSpadic(bool useAvgBaseline)
 {
diff --git a/reco/detectors/sts/unpack/CbmStsUnpackAlgo.cxx b/reco/detectors/sts/unpack/CbmStsUnpackAlgo.cxx
index 1479468a99..279e6f0dec 100644
--- a/reco/detectors/sts/unpack/CbmStsUnpackAlgo.cxx
+++ b/reco/detectors/sts/unpack/CbmStsUnpackAlgo.cxx
@@ -499,7 +499,6 @@ void CbmStsUnpackAlgo::processHitInfo(const stsxyter::Message& mess)
       // Get the time relative to the Timeslice time, I hope that the cast here works as expected. Otherwise Sts will also get into trouble with the size of UTC here
       auto tsreltime =
         static_cast<uint64_t>((ulHitTime - (fTsStartTime / stsxyter::kdClockCycleNs)) * stsxyter::kdClockCycleNs);
-      -fTsStartTime;
       double dTimeInNs = tsreltime - fSystemTimeOffset;
       if (uAsicIdx < fvdTimeOffsetNsAsics.size()) dTimeInNs -= fvdTimeOffsetNsAsics[uAsicIdx];
 
diff --git a/reco/detectors/tof/CMakeLists.txt b/reco/detectors/tof/CMakeLists.txt
index 2426c496a8..998a6cd8e6 100644
--- a/reco/detectors/tof/CMakeLists.txt
+++ b/reco/detectors/tof/CMakeLists.txt
@@ -1,25 +1,30 @@
 set(INCLUDE_DIRECTORIES
 
   ${CMAKE_CURRENT_SOURCE_DIR}
+  ${CMAKE_CURRENT_SOURCE_DIR}/unpack
 
   ${CBMDETECTORBASE_DIR}/tof
 
   ${CBMDATA_DIR}
   ${CBMDATA_DIR}/base
   ${CBMDATA_DIR}/tof
+  ${CBMDATA_DIR}/raw
   ${CBMDATA_DIR}/global
 
-  ${CBMBASE_DIR} 
+  ${CBMBASE_DIR}
 
   ${CBMROOT_SOURCE_DIR}/reco/base
 
   ${CBMROOT_SOURCE_DIR}/core/eventdisplay
+
+  ${CBMROOT_SOURCE_DIR}/fles/flestools # for Timeslice/Microslice printout formatting tools
 )
 
 include_directories(${INCLUDE_DIRECTORIES})
 
 set(SYSTEM_INCLUDE_DIRECTORIES
-  ${BASE_INCLUDE_DIRECTORIES} 
+  ${BASE_INCLUDE_DIRECTORIES}
+  ${IPC_INCLUDE_DIRECTORY} # for fles infos for unpacker
 )
 
 include_directories(SYSTEM ${SYSTEM_INCLUDE_DIRECTORIES})
@@ -37,14 +42,17 @@ set(SRCS
   CbmTofHitMaker.cxx
   CbmTofCosmicClusterizer.cxx
   CbmTofEventClusterizer.cxx
-  CbmTofFindTracks.cxx 
-  CbmTofExtendTracks.cxx 
-  CbmTofSimpClusterizer.cxx 
-  CbmTofTrackFinderNN.cxx 
-  CbmTofTestBeamClusterizer.cxx 
-  CbmTofTrackletTools.cxx 
-  CbmTofCalibrator.cxx 
-  LKFMinuit.cxx 
+  CbmTofFindTracks.cxx
+  CbmTofExtendTracks.cxx
+  CbmTofSimpClusterizer.cxx
+  CbmTofTrackFinderNN.cxx
+  CbmTofTestBeamClusterizer.cxx
+  CbmTofTrackletTools.cxx
+  CbmTofCalibrator.cxx
+  LKFMinuit.cxx
+
+  unpack/CbmTofUnpackAlgo.cxx
+  unpack/CbmTofUnpackConfig.cxx
 )
 
 set(LINKDEF CbmTofRecoLinkDef.h)
diff --git a/reco/detectors/tof/CbmTofRecoLinkDef.h b/reco/detectors/tof/CbmTofRecoLinkDef.h
index 1d7c5db420..d96636bbec 100644
--- a/reco/detectors/tof/CbmTofRecoLinkDef.h
+++ b/reco/detectors/tof/CbmTofRecoLinkDef.h
@@ -21,4 +21,7 @@
 #pragma link C++ class CbmTofCalibrator + ;
 #pragma link C++ class LKFMinuit + ;
 
+#pragma link C++ class CbmTofUnpackAlgo + ;
+#pragma link C++ class CbmTofUnpackConfig + ;
+
 #endif
diff --git a/reco/detectors/tof/unpack/CbmTofUnpackAlgo.cxx b/reco/detectors/tof/unpack/CbmTofUnpackAlgo.cxx
new file mode 100644
index 0000000000..5719ae8da4
--- /dev/null
+++ b/reco/detectors/tof/unpack/CbmTofUnpackAlgo.cxx
@@ -0,0 +1,403 @@
+/* Copyright (C) 2010 - 2021 Goethe-University Frankfurt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pascal Raisig */
+
+#include "CbmTofUnpackAlgo.h"
+
+#include "CbmFormatDecHexPrintout.h"
+#include "CbmFormatMsHeaderPrintout.h"
+
+#include <FairParGenericSet.h>
+#include <FairTask.h>
+#include <Logger.h>
+
+#include <Rtypes.h>
+#include <RtypesCore.h>
+
+
+CbmTofUnpackAlgo::CbmTofUnpackAlgo() : CbmRecoUnpackAlgo("CbmTofUnpackAlgo") {}
+
+CbmTofUnpackAlgo::~CbmTofUnpackAlgo() {}
+
+// ---- GetParContainerRequest ----
+std::vector<std::pair<std::string, std::shared_ptr<FairParGenericSet>>>*
+  CbmTofUnpackAlgo::GetParContainerRequest(std::string /*geoTag*/, std::uint32_t /*runId*/)
+{
+  // Basepath for default Trd parameter sets (those connected to a geoTag)
+  std::string basepath = Form("%s", fParFilesBasePath.data());
+  std::string temppath = "";
+
+  // // Get parameter container
+  temppath = basepath + "mTofCriPar.par";
+  LOG(info) << fName << "::GetParContainerRequest - Trying to open file " << temppath;
+  fParContVec.emplace_back(std::make_pair(temppath, std::make_shared<CbmMcbm2018TofPar>()));
+
+  return &fParContVec;
+}
+
+// ---- init
+Bool_t CbmTofUnpackAlgo::init() { return kTRUE; }
+
+// ---- initParSet(FairParGenericSet* parset) ----
+Bool_t CbmTofUnpackAlgo::initParSet(FairParGenericSet* parset)
+{
+  LOG(info) << fName << "::initParSet - for container " << parset->ClassName();
+  if (parset->IsA() == CbmMcbm2018TofPar::Class()) return initParSet(static_cast<CbmMcbm2018TofPar*>(parset));
+
+  // If we do not know the derived ParSet class we return false
+  LOG(error)
+    << fName << "::initParSet - for container " << parset->ClassName()
+    << " failed, since CbmTofUnpackAlgo::initParSet() does not know the derived ParSet and what to do with it!";
+  return kFALSE;
+}
+
+// ---- initParSet(CbmTrdParSetAsic* parset) ----
+Bool_t CbmTofUnpackAlgo::initParSet(CbmMcbm2018TofPar* parset)
+{
+  fUnpackPar = parset;
+
+  LOG(debug) << "InitParameters from " << parset;
+
+  fuNrOfGdpbs = parset->GetNrOfGdpbs();
+  LOG(debug) << "Nr. of Tof GDPBs: " << fuNrOfGdpbs;
+
+  fuNrOfFeePerGdpb = parset->GetNrOfFeesPerGdpb();
+  LOG(debug) << "Nr. of FEES per Tof GDPB: " << fuNrOfFeePerGdpb;
+
+  fuNrOfGet4PerFee = parset->GetNrOfGet4PerFee();
+  LOG(debug) << "Nr. of GET4 per Tof FEE: " << fuNrOfGet4PerFee;
+
+  fuNrOfChannelsPerGet4 = parset->GetNrOfChannelsPerGet4();
+  LOG(debug) << "Nr. of channels per GET4: " << fuNrOfChannelsPerGet4;
+
+  fuNrOfChannelsPerFee = fuNrOfGet4PerFee * fuNrOfChannelsPerGet4;
+  LOG(debug) << "Nr. of channels per FEE: " << fuNrOfChannelsPerFee;
+
+  fuNrOfGet4 = fuNrOfGdpbs * fuNrOfFeePerGdpb * fuNrOfGet4PerFee;
+  LOG(debug) << "Nr. of GET4s: " << fuNrOfGet4;
+
+  fuNrOfGet4PerGdpb = fuNrOfFeePerGdpb * fuNrOfGet4PerFee;
+  LOG(debug) << "Nr. of GET4s per GDPB: " << fuNrOfGet4PerGdpb;
+
+  fuNrOfChannelsPerGdpb = fuNrOfGet4PerGdpb * fuNrOfChannelsPerGet4;
+  LOG(debug) << "Nr. of channels per GDPB: " << fuNrOfChannelsPerGdpb;
+
+  fGdpbIdIndexMap.clear();
+  for (UInt_t i = 0; i < fuNrOfGdpbs; ++i) {
+    fGdpbIdIndexMap[parset->GetGdpbId(i)] = i;
+    LOG(debug) << "GDPB Id of TOF  " << i << " : " << std::hex << parset->GetGdpbId(i) << std::dec;
+  }  // for( UInt_t i = 0; i < fuNrOfGdpbs; ++i )
+
+  fuNrOfGbtx = parset->GetNrOfGbtx();
+  LOG(debug) << "Nr. of GBTx: " << fuNrOfGbtx;
+
+  fviRpcType.resize(fuNrOfGbtx);
+  fviModuleId.resize(fuNrOfGbtx);
+  fviNrOfRpc.resize(fuNrOfGbtx);
+  fviRpcSide.resize(fuNrOfGbtx);
+  for (UInt_t uGbtx = 0; uGbtx < fuNrOfGbtx; ++uGbtx) {
+    fviNrOfRpc[uGbtx]  = parset->GetNrOfRpc(uGbtx);
+    fviRpcType[uGbtx]  = parset->GetRpcType(uGbtx);
+    fviRpcSide[uGbtx]  = parset->GetRpcSide(uGbtx);
+    fviModuleId[uGbtx] = parset->GetModuleId(uGbtx);
+  }  // for( UInt_t uGbtx = 0; uGbtx < fuNrOfGbtx; ++uGbtx)
+
+  UInt_t uNrOfChannels = fuNrOfGet4 * fuNrOfChannelsPerGet4;
+  LOG(debug) << "Nr. of possible Tof channels: " << uNrOfChannels;
+
+  //   CbmTofDetectorId* fTofId = new CbmTofDetectorId_v14a();
+  fviRpcChUId = parset->GetRpcChUidMap();
+
+  TString sPrintout = "";
+  for (UInt_t uCh = 0; uCh < uNrOfChannels; ++uCh) {
+    if (0 == uCh % 8) sPrintout += "\n";
+    if (0 == uCh % fuNrOfChannelsPerGdpb) sPrintout += Form("\n Gdpb %u\n", uCh / fuNrOfChannelsPerGdpb);
+    sPrintout += Form(" 0x%08x", fviRpcChUId[uCh]);
+  }  // for( UInt_t i = 0; i < uNrOfChannels; ++i)
+  LOG(debug) << sPrintout;
+
+  LOG(info) << fName << "::initParSetTofMcbm2018 - Successfully initialized TOF settings";
+
+  return kTRUE;
+}
+
+bool CbmTofUnpackAlgo::unpack(const fles::Timeslice* ts, std::uint16_t icomp, UInt_t imslice)
+{
+  auto msDescriptor        = ts->descriptor(icomp, imslice);
+  fuCurrentEquipmentId     = msDescriptor.eq_id;
+  const uint8_t* msContent = reinterpret_cast<const uint8_t*>(ts->content(icomp, imslice));
+
+  fulCurrentTsIdx = ts->index();
+  uint32_t uSize  = msDescriptor.size;
+  fulCurrentMsIdx = msDescriptor.idx;
+  //   Double_t dMsTime = (1e-9) * static_cast<double>(fulCurrentMsIdx);
+  LOG(debug) << "Microslice: " << fulCurrentMsIdx << " from EqId " << std::hex << fuCurrentEquipmentId << std::dec
+             << " has size: " << uSize;
+
+  if (0 == fvbMaskedComponents.size()) fvbMaskedComponents.resize(ts->num_components(), false);
+
+  fuCurrDpbId = static_cast<uint32_t>(fuCurrentEquipmentId & 0xFFFF);
+  //   fuCurrDpbIdx = fDpbIdIndexMap[ fuCurrDpbId ];
+
+  /// Check if this sDPB ID was declared in parameter file and stop there if not
+  auto it = fGdpbIdIndexMap.find(fuCurrDpbId);
+  if (it == fGdpbIdIndexMap.end()) {
+    if (false == fvbMaskedComponents[icomp]) {
+      LOG(debug) << "---------------------------------------------------------------";
+      LOG(debug) << FormatMsHeaderPrintout(msDescriptor);
+      LOG(warning) << fName << "::unpack => "
+                   << "Could not find the gDPB index for FLIM id 0x" << std::hex << fuCurrDpbId << std::dec
+                   << " in timeslice " << fulCurrentTsIdx << " in microslice " << imslice << " component " << icomp
+                   << std::endl
+                   << "If valid this index has to be added in the TOF parameter file in the DbpIdArray field";
+      fvbMaskedComponents[icomp] = true;
+    }  // if( false == fvbMaskedComponents[ uMsComp ] )
+    else
+      return true;
+
+    return false;
+  }  // if( it == fGdpbIdIndexMap.end() )
+  else
+    fuCurrDpbIdx = fGdpbIdIndexMap[fuCurrDpbId];
+
+  fuCurrentMsSysId = static_cast<unsigned int>(msDescriptor.sys_id);
+
+  // If not integer number of message in input buffer, print warning/error
+  if (0 != (uSize % sizeof(critof001::Message)))
+    LOG(error) << fName << "::unpack => "
+               << "The input microslice buffer does NOT contain only complete gDPB messages!";
+
+  // Compute the number of complete messages in the input microslice buffer
+  uint32_t uNbMessages = (uSize - (uSize % sizeof(critof001::Message))) / sizeof(critof001::Message);
+
+  // Prepare variables for the loop on contents
+  Int_t messageType     = -111;
+  fbLastEpochGood       = false;
+  fuProcEpochUntilError = 0;
+
+  if (0 == imslice) {
+    /// Extract the time base only on MS 0, assuming that we get all TS of a component in order
+    ExtractTsStartEpoch(fTsStartTime);
+  }
+
+  const uint64_t* pInBuff         = reinterpret_cast<const uint64_t*>(msContent);  // for epoch cycle
+  const critof001::Message* pMess = reinterpret_cast<const critof001::Message*>(pInBuff);
+
+  for (uint32_t uIdx = 0; uIdx < uNbMessages; uIdx++) {
+    /// Due to buffer bug some messages are duplicated
+    if (0 < uIdx && pMess[uIdx] == pMess[uIdx - 1]) {
+      /// Ignore duplicate message
+      continue;
+    }
+
+    /// Get message type
+    messageType = pMess[uIdx].getMessageType();
+
+    if (uNbMessages - 1 == uIdx) {
+      if (pMess[uIdx].isEndOfMs()) {
+        /// Tricking clang to avoid one liner
+        continue;
+      }
+      else {
+        LOG(warning) << fName << "::unpack => "
+                     << "In timeslice " << fulCurrentTsIdx << " in microslice " << imslice << " component " << icomp
+                     << " last message is not an EndOfMs: type " << messageType
+                     << Form(" dump: 0x%16lX", pMess[uIdx].getData());
+      }  // else of if( pMess[uIdx].isEndOfMs() )
+    }    // if( uNbMessages - 1 == uIdx )
+         /*
+    if( 0 == imslice )
+        LOG(debug) << fName << "::unpack => "
+                   << "Message type " << std::hex << std::setw(2) << static_cast<uint16_t>(messageType) << std::dec;
+*/
+    fuGet4Id = fUnpackPar->ElinkIdxToGet4Idx(pMess[uIdx].getGet4Idx());
+    if (0x90 == fuCurrentMsSysId) fuGet4Id = pMess[uIdx].getGet4Idx();
+    fuGet4Nr = (fuCurrDpbIdx * fuNrOfGet4PerGdpb) + fuGet4Id;
+
+    if (fuNrOfGet4PerGdpb <= fuGet4Id && (critof001::kuChipIdMergedEpoch != fuGet4Id))
+      LOG(warning) << fName << "::unpack => "
+                   << "Message with Get4 ID too high: " << fuGet4Id << " VS " << fuNrOfGet4PerGdpb << " for FLIM "
+                   << fuCurrDpbIdx << " set in parameters.";
+
+
+    if (0 == uIdx && critof001::MSG_EPOCH != messageType) {
+      LOG(warning) << fName << "::unpack => "
+                   << "In timeslice " << fulCurrentTsIdx << " in microslice " << imslice << " component " << icomp
+                   << " first message is not an epoch: type " << messageType
+                   << Form(" dump: 0x%16lX", pMess[uIdx].getData());
+      LOG(warning) << fName << "::unpack => "
+                   << "Ignoring this microslice.";
+      return false;
+    }
+
+    switch (messageType) {
+      case critof001::MSG_HIT: {
+        if (fbLastEpochGood) {
+          /// Epoch OK
+          ProcessHit(pMess[uIdx]);
+        }
+        break;
+      }  // case critof001::MSG_HIT:
+      case critof001::MSG_EPOCH: {
+        if (critof001::kuChipIdMergedEpoch == fuGet4Id) {
+          ProcessEpoch(pMess[uIdx], uIdx);
+        }  // if this epoch message is a merged one valid for all chips
+        else {
+          /// Should be checked in monitor task, here we just jump it
+          LOG(debug2) << fName << "::unpack => "
+                      << "This unpacker does not support unmerged epoch messages!!!.";
+          continue;
+        }  // if single chip epoch message
+        break;
+      }  // case critof001::MSG_EPOCH:
+      case critof001::MSG_SLOWC:
+      case critof001::MSG_SYST: {
+        /// Ignored messages
+        /// TODO,FIXME: should be filled into fOptOutAVec as CbmErrorMessage
+        break;
+      }  // case critof001::MSG_ERROR
+      default:
+        LOG(error) << fName << "::unpack => "
+                   << "Message type " << std::hex << std::setw(2) << static_cast<uint16_t>(messageType) << std::dec
+                   << " not included in Get4 unpacker.";
+    }  // switch( mess.getMessageType() )
+  }    // for (uint32_t uIdx = 0; uIdx < uNbMessages; uIdx ++)
+
+  return true;
+}
+
+// -------------------------------------------------------------------------
+void CbmTofUnpackAlgo::ExtractTsStartEpoch(const uint64_t& ulTsStart)
+{
+  fulTsStartInEpoch = static_cast<uint64_t>(ulTsStart / critof001::kuEpochInNs) % critof001::kulEpochCycleEp;
+
+  /// FIXME: seems there is an offset of +4 Epoch between data and header
+  ///        from dt to PSD, the epoch seem to be right => placed in wrong MS!
+  if (fulTsStartInEpoch < 4) { fulTsStartInEpoch = critof001::kulEpochCycleEp + fulTsStartInEpoch - 4; }
+  else {
+    fulTsStartInEpoch -= 4;
+  }
+}
+
+void CbmTofUnpackAlgo::ProcessEpoch(const critof001::Message& mess, uint32_t uMesgIdx)
+{
+  /// FIXME: seems there is an offset of +4 Epoch between data and header
+  ///        from dt to PSD, the epoch seem to be right => placed in wrong MS!
+  ULong64_t ulEpochNr = mess.getGdpbEpEpochNb();
+  //ULong64_t ulEpochNr = (mess.getGdpbEpEpochNb() + 4) % critof001::kulEpochCycleEp;
+
+  if (0 == uMesgIdx) {
+    uint64_t ulMsStartInEpoch =
+      static_cast<uint64_t>(fulCurrentMsIdx / critof001::kuEpochInNs) % critof001::kulEpochCycleEp;
+    /// FIXME: seems there is an offset of +4 Epoch between data and header
+    ///        from dt to PSD, the epoch seem to be right => placed in wrong MS!
+    if (ulMsStartInEpoch < 4) { ulMsStartInEpoch = critof001::kulEpochCycleEp + ulMsStartInEpoch - 4; }
+    else {
+      ulMsStartInEpoch -= 4;
+    }
+
+    if (ulEpochNr != ulMsStartInEpoch) {
+      LOG(error) << fName << "::ProcessEpoch => Error first global epoch, "
+                 << Form(
+                      "from MS index 0x%06lx, current 0x%06llx, diff %lld, raw 0x%016lx, NoErr %d, current 0x%06lx  %f",
+                      ulMsStartInEpoch, ulEpochNr, ulEpochNr - ulMsStartInEpoch, mess.getData(), fuProcEpochUntilError,
+                      static_cast<uint64_t>(fulCurrentMsIdx / critof001::kuEpochInNs),
+                      fulCurrentMsIdx / critof001::kuEpochInNs);
+      LOG(error) << fName << "::ProcessEpoch => Ignoring data until next valid epoch";
+
+      fbLastEpochGood       = false;
+      ulEpochNr             = ulMsStartInEpoch;
+      fuProcEpochUntilError = 0;
+    }  // if( ulEpochNr != ulMsStartInEpoch )
+    else {
+      fbLastEpochGood = true;
+      fuProcEpochUntilError++;
+    }  // else of if( ulEpochNr != ulMsStartInEpoch )
+  }    // if( 0 < uMesgIdx )
+  else if (((fulCurrentEpoch + 1) % critof001::kulEpochCycleEp) != ulEpochNr) {
+    LOG(error) << fName << "::ProcessEpoch => Error global epoch, "
+               << Form("last 0x%06llx, current 0x%06llx, diff %lld, raw 0x%016lx, NoErr %d", fulCurrentEpoch, ulEpochNr,
+                       ulEpochNr - fulCurrentEpoch, mess.getData(), fuProcEpochUntilError);
+    LOG(error) << fName << "::ProcessEpoch => Ignoring data until next valid epoch";
+
+    ulEpochNr             = (fulCurrentEpoch + 1) % critof001::kulEpochCycleEp;
+    fbLastEpochGood       = false;
+    fuProcEpochUntilError = 0;
+  }  // if( ( (fulCurrentEpoch + 1) % critof001::kuEpochCounterSz ) != ulEpochNr )
+  else {
+    fbLastEpochGood = true;
+    fuProcEpochUntilError++;
+  }
+
+  fulCurrentEpoch = ulEpochNr;
+  if (fulTsStartInEpoch <= ulEpochNr) { fulEpochIndexInTs = ulEpochNr - fulTsStartInEpoch; }
+  else {
+    fulEpochIndexInTs = ulEpochNr + critof001::kulEpochCycleEp - fulTsStartInEpoch;
+  }
+  if (10e9 < critof001::kuEpochInNs * fulEpochIndexInTs)
+    LOG(debug) << fName << "::ProcessEpoch => "
+               << Form("Raw Epoch: 0x%06llx, Epoch offset 0x%06lx, Epoch in Ts: 0x%07lx, time %f ns (%f * %lu)",
+                       ulEpochNr, fulTsStartInEpoch, fulEpochIndexInTs, critof001::kuEpochInNs * fulEpochIndexInTs,
+                       critof001::kuEpochInNs, fulEpochIndexInTs);
+}
+
+void CbmTofUnpackAlgo::ProcessHit(const critof001::Message& mess)
+{
+  UInt_t uChannel = mess.getGdpbHitChanId();
+  UInt_t uTot     = mess.getGdpbHit32Tot();
+
+  UInt_t uChannelNr      = fuGet4Id * fuNrOfChannelsPerGet4 + uChannel;
+  UInt_t uChannelNrInFee = (fuGet4Id % fuNrOfGet4PerFee) * fuNrOfChannelsPerGet4 + uChannel;
+  UInt_t uFeeNr          = (fuGet4Id / fuNrOfGet4PerFee);
+  UInt_t uFeeNrInSys     = fuCurrDpbIdx * fuNrOfFeePerGdpb + uFeeNr;
+  // UInt_t uRemappedChannelNr = uFeeNr * fuNrOfChannelsPerFee + fUnpackPar->Get4ChanToPadiChan(uChannelNrInFee);
+
+  UInt_t uRemappedChannelNrInSys = fuCurrDpbIdx * fuNrOfChannelsPerGdpb + uFeeNr * fuNrOfChannelsPerFee
+                                   + fUnpackPar->Get4ChanToPadiChan(uChannelNrInFee);
+  /// Diamond FEE have straight connection from Get4 to eLink and from PADI to GET4
+  if (0x90 == fuCurrentMsSysId) {
+    // uRemappedChannelNr      = uChannelNr;
+    uRemappedChannelNrInSys = fuCurrDpbIdx * fUnpackPar->GetNrOfChannelsPerGdpb() + uChannelNr;
+  }  // if(0x90 == fuCurrentMsSysId)
+
+  Double_t dHitTime = mess.getMsgFullTimeD(fulEpochIndexInTs);
+  Double_t dHitTot  = uTot;  // in bins
+
+  if (fviRpcChUId.size() < uRemappedChannelNrInSys) {
+    LOG(fatal) << fName << "::unpack => "
+               << "Invalid mapping index " << uRemappedChannelNrInSys << " VS " << fviRpcChUId.size() << ", from FLIM "
+               << fuCurrDpbIdx << ", Get4 " << fuGet4Id << ", Ch " << uChannel << ", ChNr " << uChannelNr << ", ChNrIF "
+               << uChannelNrInFee << ", FiS " << uFeeNrInSys;
+    return;
+  }  // if( fviRpcChUId.size() < uRemappedChannelNrInSys )
+
+  UInt_t uChanUId = fviRpcChUId[uRemappedChannelNrInSys];
+
+  if (0 == uChanUId) {
+    if (0 < fuMapWarnToPrint--)
+      LOG(warning) << fName << "::unpack => "
+                   << "Unused data item at " << uRemappedChannelNrInSys << ", from FLIM " << fuCurrDpbIdx << ", Get4 "
+                   << fuGet4Id << ", Ch " << uChannel << ", ChNr " << uChannelNr << ", ChNrIF " << uChannelNrInFee
+                   << ", FiS " << uFeeNrInSys;
+    return;  // Hit not mapped to digi
+  }
+
+  /// Apply offset to T0 only to TOF digis
+  if (0x90 != fuCurrentMsSysId) {
+    /// Tricking clang to avoid one liner
+    dHitTime -= fdTimeOffsetNs;
+  }
+
+  /// FIXME: seems there is an offset of +4 Epoch between data and header
+  ///        from dt to PSD, the epoch are probably the one off, not the MS time!
+  dHitTime -= 4.0 * critof001::kuEpochInNs;
+
+  LOG(debug) << Form("Insert 0x%08x digi with time ", uChanUId) << dHitTime << Form(", Tot %4.0f", dHitTot)
+             << " at epoch " << fulEpochIndexInTs;
+
+  /// Create output object and store it
+  std::unique_ptr<CbmTofDigi> digi(new CbmTofDigi(uChanUId, dHitTime, dHitTot));
+  if (digi) fOutputVec.emplace_back(*std::move(digi));
+}
+
+ClassImp(CbmTofUnpackAlgo)
diff --git a/reco/detectors/tof/unpack/CbmTofUnpackAlgo.h b/reco/detectors/tof/unpack/CbmTofUnpackAlgo.h
new file mode 100644
index 0000000000..e773bd76a4
--- /dev/null
+++ b/reco/detectors/tof/unpack/CbmTofUnpackAlgo.h
@@ -0,0 +1,191 @@
+/* Copyright (C) 2010 - 2021 Goethe-University Frankfurt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pascal Raisig */
+
+/**
+ * @file CbmTofUnpackAlgo.h
+ * @author Pascal Raisig (praisig@ikf.uni-frankfurt.de)
+ * @brief Baseclass for the TrdR unpacker algorithms
+ * @version 0.1
+ * @date 2021-04-21
+ *
+ * @copyright Copyright (c) 2021
+ *
+ * This is the base class for the algorithmic part of the tsa data unpacking
+ * processes of the CbmTrd.
+ * The actual translation from tsa to digi happens in the derived classes.
+ *
+ *
+*/
+
+#ifndef CbmTofUnpackAlgo_H
+#define CbmTofUnpackAlgo_H
+
+#include "CbmErrorMessage.h"
+#include "CbmMcbm2018TofPar.h"
+#include "CbmRecoUnpackAlgo.tmpl"
+#include "CbmTofDigi.h"
+
+#include "Timeslice.hpp"  // timeslice
+
+#include <Rtypes.h>  // for types
+#include <RtypesCore.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <utility>
+
+#include "CriGet4Mess001.h"
+
+class CbmTofUnpackAlgo : public CbmRecoUnpackAlgo<CbmTofDigi, CbmErrorMessage> {
+public:
+  /** @brief Create the Cbm Trd Unpack AlgoBase object */
+  CbmTofUnpackAlgo();
+
+  /** @brief Destroy the Cbm Trd Unpack Task object */
+  virtual ~CbmTofUnpackAlgo();
+
+  /** @brief Copy constructor - not implemented **/
+  CbmTofUnpackAlgo(const CbmTofUnpackAlgo&) = delete;
+
+  /** @brief Assignment operator - not implemented **/
+  CbmTofUnpackAlgo& operator=(const CbmTofUnpackAlgo&) = delete;
+
+  /**
+   * @brief Get the requested parameter containers. To be defined in the derived classes!
+   * Return the required parameter containers together with the paths to the ascii
+   * files to.
+   *
+   * @param[in] std::string geoTag as used in CbmSetup
+   * @param[in] std::uint32_t runId for runwise defined parameters
+   * @return fParContVec
+  */
+  virtual std::vector<std::pair<std::string, std::shared_ptr<FairParGenericSet>>>*
+  GetParContainerRequest(std::string geoTag, std::uint32_t runId);
+
+protected:
+  /** @brief Finish function for this algorithm base clase */
+  void finish()
+  {
+    finishDerived();
+    // Finish the monitor if we have one
+    // if (fMonitor) fMonitor->Finish();
+  }
+
+  /** @brief Function that allows special calls during Finish in the derived algos */
+  virtual void finishDerived() { return; }
+
+  /**
+   * @brief Initialisation at begin of run. Special inits of the derived algos.
+   *
+   * @retval Bool_t initOk
+  */
+  Bool_t init();
+
+  /**
+   * @brief Handles the distribution of the hidden derived classes to their explicit functions.
+   *
+   * @param parset
+   * @return Bool_t initOk
+  */
+  Bool_t initParSet(FairParGenericSet* parset);
+
+  /**
+   * @brief Handles the distribution of the hidden derived classes to their explicit functions.
+   *
+   * @param parset
+   * @return Bool_t initOk
+  */
+  Bool_t initParSet(CbmMcbm2018TofPar* parset);
+
+  /**
+   * @brief Set the Derived Ts Parameters
+   *
+   * In this function parameters required by the explicit algo connected to the timeslice can be set.
+   *
+   * @param itimeslice
+   * @return true
+   * @return false
+  */
+  bool setDerivedTsParameters(size_t itimeslice) { return true; }
+
+  /**
+   * @brief Unpack a given microslice. To be implemented in the derived unpacker algos.
+   *
+   * @param ts timeslice pointer
+   * @param icomp index to the component to be unpacked
+   * @param imslice index of the microslice to be unpacked
+   * @return true
+   * @return false
+   *
+   * @remark The content of the µslice can only be accessed via the timeslice. Hence, we need to pass the pointer to the full timeslice
+  */
+  bool unpack(const fles::Timeslice* ts, std::uint16_t icomp, UInt_t imslice);
+
+private:
+  /// Buffers processing
+  void ProcessEpSupprBuffer();
+
+  /// Message processing methods
+  void ExtractTsStartEpoch(const uint64_t& ulTsStart);
+  void ProcessEpoch(const critof001::Message& mess, uint32_t uMesgIdx);
+  void ProcessEndOfMsEpoch();
+  void ProcessHit(const critof001::Message& mess);
+
+  inline Int_t GetArrayIndex(Int_t gdpbId, Int_t get4Id) { return gdpbId * fuNrOfGet4PerGdpb + get4Id; }
+
+
+  /// Settings from parameter file
+  CbmMcbm2018TofPar* fUnpackPar = nullptr;  //! For static/inline mapping functions
+
+  /// Readout chain dimensions and mapping
+  UInt_t fuNrOfGdpbs                       = 0;   //! Total number of GDPBs in the system
+  std::map<UInt_t, UInt_t> fGdpbIdIndexMap = {};  //! gDPB ID to index map
+  UInt_t fuNrOfFeePerGdpb                  = 0;   //! Number of FEBs per GDPB
+  UInt_t fuNrOfGet4PerFee                  = 0;   //! Number of GET4s per FEE
+  UInt_t fuNrOfChannelsPerGet4             = 0;   //! Number of channels in each GET4
+  UInt_t fuNrOfChannelsPerFee              = 0;   //! Number of channels in each FEE
+  UInt_t fuNrOfGet4                        = 0;   //! Total number of Get4 chips in the system
+  UInt_t fuNrOfGet4PerGdpb                 = 0;   //! Number of GET4s per GDPB
+  UInt_t fuNrOfChannelsPerGdpb             = 0;   //! Number of channels per GDPB
+
+  /// Detector Mapping
+  UInt_t fuNrOfGbtx              = 0;
+  UInt_t fuNrOfModules           = 0;
+  std::vector<Int_t> fviNrOfRpc  = {};
+  std::vector<Int_t> fviRpcType  = {};
+  std::vector<Int_t> fviRpcSide  = {};
+  std::vector<Int_t> fviModuleId = {};
+  std::vector<Int_t> fviRpcChUId = {};
+
+  /// User settings: Data correction parameters
+  Double_t fdTimeOffsetNs;
+
+  /// Running indices
+  UInt_t fuMapWarnToPrint     = 100;
+  ULong64_t fulCurrentTsIdx   = 0;  //! Idx of the current TS
+  ULong64_t fulCurrentMsIdx   = 0;  //! Idx of the current MS in TS (0 to fuTotalMsNb)
+  size_t fuCurrentMsSysId     = 0;  //! SysId of the current MS in TS (0 to fuTotalMsNb)
+  UInt_t fuCurrentEquipmentId = 0;  //! Current equipment ID, tells from which DPB the current MS is originating
+  UInt_t fuCurrDpbId          = 0;  //! Temp holder until Current equipment ID is properly filled in MS
+  UInt_t fuCurrDpbIdx         = 0;  //! Index of the DPB from which the MS currently unpacked is coming
+  UInt_t fuGet4Id =
+    0;  //! running number (0 to fuNrOfGet4PerGdpb) of the Get4 chip of a unique GDPB for current message
+  UInt_t fuGet4Nr = 0;  //! running number (0 to fuNrOfGet4) of the Get4 chip in the system for current message
+  /// Data format control: Current time references for each GDPB: merged epoch marker, epoch cycle, full epoch [fuNrOfGdpbs]
+  ULong64_t fulCurrentEpoch = 0;  //! Current epoch index
+
+  /// Control flags
+  std::vector<bool> fvbMaskedComponents = {};
+  bool fbLastEpochGood                  = false;
+
+  /// Book-keeping members
+  uint32_t fuProcEpochUntilError = 0;
+  uint64_t fulTsStartInEpoch     = 0;
+  uint64_t fulEpochIndexInTs     = 0;
+
+  ClassDef(CbmTofUnpackAlgo, 1)
+};
+
+#endif  // CbmTofUnpackAlgo_H
diff --git a/reco/detectors/tof/unpack/CbmTofUnpackConfig.cxx b/reco/detectors/tof/unpack/CbmTofUnpackConfig.cxx
new file mode 100644
index 0000000000..b61bf318a1
--- /dev/null
+++ b/reco/detectors/tof/unpack/CbmTofUnpackConfig.cxx
@@ -0,0 +1,67 @@
+/* Copyright (C) 2010 - 2021 Goethe-University Frankfurt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pascal Raisig */
+
+#include "CbmTofUnpackConfig.h"
+
+#include "CbmTofUnpackAlgo.h"
+
+#include <Logger.h>
+
+#include <Rtypes.h>
+#include <RtypesCore.h>
+
+#include <memory>
+#include <vector>
+
+CbmTofUnpackConfig::CbmTofUnpackConfig(std::string detGeoSetupTag, UInt_t runid)
+  : CbmRecoUnpackConfig("CbmTofUnpackConfig")
+  , fGeoSetupTag(detGeoSetupTag)
+  , fRunId(runid)
+{
+}
+
+CbmTofUnpackConfig::~CbmTofUnpackConfig() {}
+
+// ---- Init ----
+void CbmTofUnpackConfig::InitUnpacker()
+{
+  LOG(info) << fName << "::Init -";
+
+  auto initOk = kTRUE;
+
+  // First choose the derived unpacking algorithm to be used and pass the raw to digi method
+  auto algo = chooseAlgo();
+
+  if (fDoLog) LOG(info) << fName << "::Init - SetParFilesBasePath";
+  algo->SetParFilesBasePath(fParFilesBasePath);
+
+  // Initialise the parameter containers required by the unpacker algo. Includes loading the corresponding ascii files
+  auto reqparvec = algo->GetParContainerRequest(fGeoSetupTag, fRunId);
+  initOk &= initParContainers(reqparvec);
+
+  // Now we have all information required to initialise the algorithm
+  algo->Init();
+
+  // Pass the algo to its member in the base class
+  fAlgo = algo;
+}
+
+// ---- chooseAlgo ----
+std::shared_ptr<CbmTofUnpackAlgo> CbmTofUnpackConfig::chooseAlgo()
+{
+  if (fDoLog) LOG(info) << fName << "::Init - chooseAlgo";
+
+  // Default unpacker selection
+  // Unpacker algo from mcbm 2021 on and hopefully default for a long time.
+  auto algo = std::make_shared<CbmTofUnpackAlgo>();
+  LOG(info) << fName << "::chooseAlgo() - selected algo = " << algo->Class_Name();
+  return algo;
+
+  LOG(error) << fName
+             << "::Init - chooseAlgo() - no algorithm created something went wrong. We can not work like this!";
+  return nullptr;
+}
+
+
+ClassImp(CbmTofUnpackConfig)
diff --git a/reco/detectors/tof/unpack/CbmTofUnpackConfig.h b/reco/detectors/tof/unpack/CbmTofUnpackConfig.h
new file mode 100644
index 0000000000..bc92d0731f
--- /dev/null
+++ b/reco/detectors/tof/unpack/CbmTofUnpackConfig.h
@@ -0,0 +1,90 @@
+/* Copyright (C) 2010 - 2021 Goethe-University Frankfurt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pascal Raisig */
+
+/**
+ * @file CbmTofUnpackConfig.h
+ * @author Pascal Raisig (praisig@ikf.uni-frankfurt.de)
+ * @brief Configuration class for an unpacker algorithm
+ * @version 0.1
+ * @date 2021-04-21
+ *
+ * @copyright Copyright (c) 2021
+ *
+ * This is the common configuration class for unpacking algorithms
+ *
+*/
+
+#ifndef CbmTofUnpackConfig_H
+#define CbmTofUnpackConfig_H
+
+#include "CbmErrorMessage.h"
+#include "CbmRecoUnpackConfig.tmpl"
+#include "CbmTofDigi.h"
+#include "CbmTofUnpackAlgo.h"
+
+#include <FairLogger.h>
+#include <Logger.h>
+
+#include <Rtypes.h>
+#include <RtypesCore.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+class CbmTofUnpackConfig : public CbmRecoUnpackConfig<CbmTofUnpackAlgo, CbmTofDigi, CbmErrorMessage> {
+
+public:
+  /**
+   * @brief Create the Cbm Tof Unpack Task object
+   *
+   * @param geoSetupTag Geometry setup tag for the given detector as used by CbmSetup objects
+   * @param runid set if unpacker is rerun on a special run with special parameters
+   *@remark We use the string instead of CbmSetup here, to not having to link against sim/steer...
+  */
+  CbmTofUnpackConfig(std::string detGeoSetupTag, UInt_t runid = 0);
+
+  /**
+   * @brief Destroy the Cbm Tof Unpack Task object
+   *
+  */
+  virtual ~CbmTofUnpackConfig();
+
+  /** @brief Copy constructor - not implemented **/
+  CbmTofUnpackConfig(const CbmTofUnpackConfig&) = delete;
+
+  /** @brief Assignment operator - not implemented **/
+  CbmTofUnpackConfig& operator=(const CbmTofUnpackConfig&) = delete;
+
+  // Getters
+
+
+  /**
+   * @brief Prepare the unpacker to be ready to run.
+   * In this function all initialization steps of the unpacker algorithms happen.
+  */
+  void InitUnpacker();
+
+  // Setters
+
+protected:
+  /**
+   * @brief Choose the derived unpacker algorithm to be used for the DAQ output to Digi translation. If algo was already set manually by the user this algorithm is used.
+   *
+   * @return Bool_t initOk
+  */
+  virtual std::shared_ptr<CbmTofUnpackAlgo> chooseAlgo();
+
+  /** @brief Geometry setup tag for the given detector as used by CbmSetup objects */
+  std::string fGeoSetupTag = "";
+
+  /** @brief RunId of the current run, if not known 0 is a valid runtime case. Used runId based parameter loading. */
+  UInt_t fRunId = 0;
+
+private:
+  ClassDef(CbmTofUnpackConfig, 1)
+};
+
+#endif  // CbmTofUnpackConfig_H
diff --git a/reco/steer/CMakeLists.txt b/reco/steer/CMakeLists.txt
index 236f0c4d2e..14a50d7cf1 100644
--- a/reco/steer/CMakeLists.txt
+++ b/reco/steer/CMakeLists.txt
@@ -39,13 +39,13 @@ ${CBMROOT_SOURCE_DIR}/core/data/base
 ${CBMROOT_SOURCE_DIR}/core/data/psd
 ${CBMROOT_SOURCE_DIR}/core/data/rich
 ${CBMROOT_SOURCE_DIR}/core/data/sts
-# ${CBMROOT_SOURCE_DIR}/core/data/tof
+${CBMROOT_SOURCE_DIR}/core/data/tof
 ${CBMROOT_SOURCE_DIR}/core/data/trd
 
 ${CBMROOT_SOURCE_DIR}/core/detectors/psd
 ${CBMROOT_SOURCE_DIR}/core/detectors/rich
 ${CBMROOT_SOURCE_DIR}/core/detectors/sts
-# ${CBMROOT_SOURCE_DIR}/core/detectors/tof
+${CBMROOT_SOURCE_DIR}/core/detectors/tof
 ${CBMROOT_SOURCE_DIR}/core/detectors/trd
 
 
diff --git a/reco/steer/CbmRecoUnpack.cxx b/reco/steer/CbmRecoUnpack.cxx
index 68b1e08887..a75a6aa76d 100644
--- a/reco/steer/CbmRecoUnpack.cxx
+++ b/reco/steer/CbmRecoUnpack.cxx
@@ -40,7 +40,7 @@ void CbmRecoUnpack::Finish()
   if (fPsdConfig) fPsdConfig->GetUnpacker()->Finish();
   if (fRichConfig) fRichConfig->GetUnpacker()->Finish();
   if (fStsConfig) fStsConfig->GetUnpacker()->Finish();
-  // if (fTofConfig) fTofConfig->GetUnpacker()->Finish();
+  if (fTofConfig) fTofConfig->GetUnpacker()->Finish();
   if (fTrdConfig) fTrdConfig->GetUnpacker()->Finish();
   if (fTrdConfig2D) fTrdConfig2D->GetUnpacker()->Finish();
 }
@@ -79,6 +79,8 @@ Bool_t CbmRecoUnpack::Init()
     if (fTrdConfig2D) fTrdConfig2D->Init(ioman);
   }
   // This is an ugly work around, because the TRD and TRD2D want to access the same vector and there is no function to retrieve a writeable vector<obj> from the FairRootManager, especially before the branches are created, as far as I am aware. The second option workaround is in in Init() to look for the fasp config and create a separate branch for fasp created CbmTrdDigis PR 072021
+  // --- Tof
+  if (fTofConfig) fTofConfig->Init(ioman);
 
   return kTRUE;
 }
@@ -102,6 +104,8 @@ void CbmRecoUnpack::Reset()
   if (fTrdConfig) fTrdConfig->Reset();
   // ---- Trd2D ----
   if (fTrdConfig2D) fTrdConfig2D->Reset();
+  // ---- Tof ----
+  if (fTofConfig) fTofConfig->Reset();
 }
 
 // ----------------------------------------------------------------------------
@@ -153,6 +157,12 @@ void CbmRecoUnpack::Unpack(unique_ptr<Timeslice> ts)
             unpack(&timeslice, component, fTrdConfig2D, fTrdConfig2D->GetOptOutAVec(), fTrdConfig2D->GetOptOutBVec()));
         break;
       }
+      case fkFlesTof: {
+        if (fTofConfig)
+          fCbmTsEventHeader->SetNDigisTof(
+            unpack(&timeslice, component, fTofConfig, fTofConfig->GetOptOutAVec(), fTofConfig->GetOptOutBVec()));
+        break;
+      }
 
       default: {
         if (fDoDebugPrints) LOG(error) << "Unpack: Unknown system ID " << systemId << " for component " << component;
diff --git a/reco/steer/CbmRecoUnpack.h b/reco/steer/CbmRecoUnpack.h
index 062618127f..7f9441ac31 100644
--- a/reco/steer/CbmRecoUnpack.h
+++ b/reco/steer/CbmRecoUnpack.h
@@ -11,6 +11,7 @@
 #include "CbmPsdUnpackConfig.h"
 #include "CbmRichUnpackConfig.h"
 #include "CbmStsUnpackConfig.h"
+#include "CbmTofUnpackConfig.h"
 #include "CbmTrdUnpackConfig.h"
 #include "CbmTrdUnpackConfigFasp2D.h"
 #include "CbmTsEventHeader.h"
@@ -65,8 +66,8 @@ public:
 
   /**
    * @brief Set the Debug Printout Flag
-   * 
-   * @param value 
+   *
+   * @param value
   */
   void SetDebugPrintout(bool value = true) { fDoDebugPrints = value; }
 
@@ -88,6 +89,9 @@ public:
   /** @brief Set the Trd2D Unpack Config @param config */
   void SetUnpackConfig(std::shared_ptr<CbmTrdUnpackConfigFasp2D> config) { fTrdConfig2D = config; }
 
+  /** @brief Set the Tof Unpack Config @param config */
+  void SetUnpackConfig(std::shared_ptr<CbmTofUnpackConfig> config) { fTofConfig = config; }
+
   /** @brief Trigger the unpacking procedure **/
   void Unpack(std::unique_ptr<fles::Timeslice> ts);
 
@@ -134,7 +138,7 @@ private:
 
   /**
    * @brief Template for the unpacking call of a given algorithm.
-   * 
+   *
    * @tparam TAlgo Algorithm to be called
    * @tparam TOutput Output element types
    * @tparam TOptoutputs Optional output element types
@@ -195,17 +199,17 @@ private:
   }
   // ----------------------------------------------------------------------------
 
-  /** @brief Configuration of the Trd unpacker. Provides the configured algorithm */
+  /** @brief Configuration of the Psd unpacker. Provides the configured algorithm */
   std::shared_ptr<CbmPsdUnpackConfig> fPsdConfig = nullptr;  //!
 
-  /** @brief Configuration of the Trd unpacker. Provides the configured algorithm */
+  /** @brief Configuration of the Rich unpacker. Provides the configured algorithm */
   std::shared_ptr<CbmRichUnpackConfig> fRichConfig = nullptr;  //!
 
-  /** @brief Configuration of the Trd unpacker. Provides the configured algorithm */
+  /** @brief Configuration of the Sts unpacker. Provides the configured algorithm */
   std::shared_ptr<CbmStsUnpackConfig> fStsConfig = nullptr;  //!
 
-  // /** @brief Configuration of the Trd unpacker. Provides the configured algorithm */
-  // std::shared_ptr<CbmTofUnpackConfig> fTofConfig = nullptr;  //!
+  /** @brief Configuration of the Tof unpacker. Provides the configured algorithm */
+  std::shared_ptr<CbmTofUnpackConfig> fTofConfig = nullptr;  //!
 
   /** @brief Configuration of the Trd unpacker. Provides the configured algorithm */
   std::shared_ptr<CbmTrdUnpackConfig> fTrdConfig = nullptr;  //!
-- 
GitLab