diff --git a/algo/CMakeLists.txt b/algo/CMakeLists.txt
index dbb8289793b3aeed18deb52e33abccf7edebb082..6a3fef593e8976cd5b4fe579b93d8e10fa67e904 100644
--- a/algo/CMakeLists.txt
+++ b/algo/CMakeLists.txt
@@ -18,6 +18,7 @@ set(SRCS
   evbuild/EventBuilder.cxx
   trigger/TimeClusterTrigger.cxx
   evselector/DigiEventSelector.cxx
+  unpack/Unpack.cxx
   detectors/sts/ReadoutConfig.cxx
   detectors/sts/StsHitfinderChain.cxx
   detectors/sts/StsReadoutConfigLegacy.cxx
@@ -64,6 +65,7 @@ target_include_directories(Algo
          ${CMAKE_CURRENT_SOURCE_DIR}/global
          ${CMAKE_CURRENT_SOURCE_DIR}/trigger
          ${CMAKE_CURRENT_SOURCE_DIR}/evselector
+         ${CMAKE_CURRENT_SOURCE_DIR}/unpack
          ${CMAKE_CURRENT_SOURCE_DIR}/detectors/sts
          ${CMAKE_CURRENT_SOURCE_DIR}/detectors/much
          ${CMAKE_CURRENT_SOURCE_DIR}/detectors/tof
diff --git a/algo/unpack/Unpack.cxx b/algo/unpack/Unpack.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..db3a356ac772cbb957ab06f5b2ee7f2efae103de
--- /dev/null
+++ b/algo/unpack/Unpack.cxx
@@ -0,0 +1,282 @@
+/* Copyright (C) 2022 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Volker Friese [committer] */
+
+
+#include "Unpack.h"
+
+#include <chrono>
+
+#include "AlgoFairloggerCompat.h"
+
+#ifdef WITH_EXECUTION
+#include <execution>
+#endif
+
+using namespace std;
+
+namespace cbm::algo
+{
+  // -----   Execution   -------------------------------------------------------
+  Unpack::resultType Unpack::operator()(const fles::Timeslice* timeslice)
+  {
+    // --- Output data
+    resultType result          = {};
+    CbmDigiTimeslice& digiTs   = result.first;
+    UnpackMonitorData& monitor = result.second;
+
+    // ---  Component loop
+    for (uint64_t comp = 0; comp < timeslice->num_components(); comp++) {
+
+      // System ID of current component
+      const auto systemId = static_cast<fles::SubsystemIdentifier>(timeslice->descriptor(comp, 0).sys_id);
+
+      // Equipment ID of current component
+      const uint16_t equipmentId = timeslice->descriptor(comp, 0).eq_id;
+
+      // The current algorithms work for the format versions hard-coded as parameters to MsLoop() below.
+      // Other versions are not yet supported.
+      // In the future, different data formats will be supported by instantiating different
+      // algorithms depending on the version.
+
+      if (systemId == fles::SubsystemIdentifier::STS) {
+        MsLoop(timeslice, fAlgoSts, comp, equipmentId, &digiTs.fData.fSts.fDigis, monitor, &monitor.fSts, 0x20);
+      }
+      if (systemId == fles::SubsystemIdentifier::MUCH) {
+        MsLoop(timeslice, fAlgoMuch, comp, equipmentId, &digiTs.fData.fMuch.fDigis, monitor, &monitor.fMuch, 0x20);
+      }
+      if (systemId == fles::SubsystemIdentifier::RPC) {
+        MsLoop(timeslice, fAlgoTof, comp, equipmentId, &digiTs.fData.fTof.fDigis, monitor, &monitor.fTof, 0x00);
+      }
+      if (systemId == fles::SubsystemIdentifier::T0) {
+        MsLoop(timeslice, fAlgoBmon, comp, equipmentId, &digiTs.fData.fT0.fDigis, monitor, &monitor.fBmon, 0x00);
+      }
+      if (systemId == fles::SubsystemIdentifier::TRD) {
+        MsLoop(timeslice, fAlgoTrd, comp, equipmentId, &digiTs.fData.fTrd.fDigis, monitor, &monitor.fTrd, 0x01);
+      }
+      if (systemId == fles::SubsystemIdentifier::TRD2D) {
+        MsLoop(timeslice, fAlgoTrd2d, comp, equipmentId, &digiTs.fData.fTrd2d.fDigis, monitor, &monitor.fTrd2d, 0x02);
+      }
+    }  //# component
+
+    // --- Sorting of output digis. Is required by both digi trigger and event builder.
+#ifdef WITH_EXECUTION
+    std::sort(std::execution::par_unseq, digiTs.fData.fSts.fDigis.begin(), digiTs.fData.fSts.fDigis.end(),
+              [](CbmStsDigi digi1, CbmStsDigi digi2) { return digi1.GetTime() < digi2.GetTime(); });
+    std::sort(std::execution::par_unseq, digiTs.fData.fMuch.fDigis.begin(), digiTs.fData.fMuch.fDigis.end(),
+              [](CbmMuchDigi digi1, CbmMuchDigi digi2) { return digi1.GetTime() < digi2.GetTime(); });
+    std::sort(std::execution::par_unseq, digiTs.fData.fTof.fDigis.begin(), digiTs.fData.fTof.fDigis.end(),
+              [](CbmTofDigi digi1, CbmTofDigi digi2) { return digi1.GetTime() < digi2.GetTime(); });
+    std::sort(std::execution::par_unseq, digiTs.fData.fT0.fDigis.begin(), digiTs.fData.fT0.fDigis.end(),
+              [](CbmTofDigi digi1, CbmTofDigi digi2) { return digi1.GetTime() < digi2.GetTime(); });
+    std::sort(std::execution::par_unseq, digiTs.fData.fTrd.fDigis.begin(), digiTs.fData.fTrd.fDigis.end(),
+              [](CbmTrdDigi digi1, CbmTrdDigi digi2) { return digi1.GetTime() < digi2.GetTime(); });
+    std::sort(std::execution::par_unseq, digiTs.fData.fTrd2d.fDigis.begin(), digiTs.fData.fTrd2d.fDigis.end(),
+              [](CbmTrdDigi digi1, CbmTrdDigi digi2) { return digi1.GetTime() < digi2.GetTime(); });
+#else
+    std::sort(digiTs.fData.fSts.fDigis.begin(), digiTs.fData.fSts.fDigis.end(),
+              [](CbmStsDigi digi1, CbmStsDigi digi2) { return digi1.GetTime() < digi2.GetTime(); });
+    std::sort(digiTs.fData.fMuch.fDigis.begin(), digiTs.fData.fMuch.fDigis.end(),
+              [](CbmMuchDigi digi1, CbmMuchDigi digi2) { return digi1.GetTime() < digi2.GetTime(); });
+    std::sort(digiTs.fData.fTof.fDigis.begin(), digiTs.fData.fTof.fDigis.end(),
+              [](CbmTofDigi digi1, CbmTofDigi digi2) { return digi1.GetTime() < digi2.GetTime(); });
+    std::sort(digiTs.fData.fT0.fDigis.begin(), digiTs.fData.fT0.fDigis.end(),
+              [](CbmTofDigi digi1, CbmTofDigi digi2) { return digi1.GetTime() < digi2.GetTime(); });
+    std::sort(digiTs.fData.fTrd.fDigis.begin(), digiTs.fData.fTrd.fDigis.end(),
+              [](CbmTrdDigi digi1, CbmTrdDigi digi2) { return digi1.GetTime() < digi2.GetTime(); });
+    std::sort(digiTs.fData.fTrd2d.fDigis.begin(), digiTs.fData.fTrd2d.fDigis.end(),
+              [](CbmTrdDigi digi1, CbmTrdDigi digi2) { return digi1.GetTime() < digi2.GetTime(); });
+#endif
+    return result;
+  }
+  // ----------------------------------------------------------------------------
+
+  // ----------------- Microslice loop ------------------------------------------
+  template<class Digi, class UnpackAlgo, class MonitorData>
+  void Unpack::MsLoop(const fles::Timeslice* timeslice, std::map<uint16_t, UnpackAlgo>& algoMap, const uint64_t comp,
+                      const uint16_t eqId, std::vector<Digi>* digis, UnpackMonitorData& monitor,
+                      std::vector<MonitorData>* monitorMs, uint8_t sys_ver)
+  {
+    // --- Component log
+    size_t numBytesInComp = 0;
+    size_t numDigisInComp = 0;
+
+    // For profiling
+    const auto starttime = std::chrono::high_resolution_clock::now();
+
+    // Get Unpacker
+    const auto algoIt = algoMap.find(eqId);
+    assert(algoIt != algoMap.end());
+    UnpackAlgo& algo = algoIt->second;
+
+    assert(timeslice->descriptor(comp, 0).sys_ver == sys_ver);
+    const uint64_t numMsInComp = timeslice->num_microslices(comp);
+
+    for (uint64_t mslice = 0; mslice < numMsInComp; mslice++) {
+      const auto msDescriptor = timeslice->descriptor(comp, mslice);
+      const auto msContent    = timeslice->content(comp, mslice);
+      auto result             = algo(msContent, msDescriptor, timeslice->start_time());
+      L_(debug) << "Unpack::MsLoop(): Component " << comp << ", microslice " << mslice << ", digis "
+                << result.first.size() << ", " << result.second.print();
+      numBytesInComp += msDescriptor.size;
+      numDigisInComp += result.first.size();
+      digis->insert(digis->end(), result.first.begin(), result.first.end());
+      monitorMs->push_back(result.second);
+    }
+    // Get elapsed time
+    const auto endtime  = std::chrono::high_resolution_clock::now();
+    const auto duration = std::chrono::duration_cast<std::chrono::microseconds>(endtime - starttime);
+
+    L_(debug) << "Unpack(): Component " << comp << ", subsystem "
+              << fles::to_string(static_cast<fles::SubsystemIdentifier>(timeslice->descriptor(comp, 0).sys_id))
+              << ", microslices " << numMsInComp << " input size " << numBytesInComp << " bytes,"
+              << " digis " << numDigisInComp << ", CPU time " << duration.count() / 1000. << " ms";
+
+    monitor.fNumMs += numMsInComp;
+    monitor.fNumBytes += numBytesInComp;
+    monitor.fNumDigis += numDigisInComp;
+    monitor.fNumCompUsed++;
+  }
+  // ----------------------------------------------------------------------------
+
+  // -----   Initialisation   ---------------------------------------------------
+  bool Unpack::Init()
+  {
+    // --- Common parameters for all components for STS
+    uint32_t numChansPerAsicSts   = 128;  // R/O channels per ASIC for STS
+    uint32_t numAsicsPerModuleSts = 16;   // Number of ASICs per module for STS
+
+    // Create one algorithm per component for STS and configure it with parameters
+    auto equipIdsSts = fStsConfig.GetEquipmentIds();
+    for (auto& equip : equipIdsSts) {
+      std::unique_ptr<UnpackStsPar> par(new UnpackStsPar());
+      par->fNumChansPerAsic   = numChansPerAsicSts;
+      par->fNumAsicsPerModule = numAsicsPerModuleSts;
+      const size_t numElinks  = fStsConfig.GetNumElinks(equip);
+      for (size_t elink = 0; elink < numElinks; elink++) {
+        UnpackStsElinkPar elinkPar;
+        auto mapEntry        = fStsConfig.Map(equip, elink);
+        elinkPar.fAddress    = mapEntry.first;   // Module address for this elink
+        elinkPar.fAsicNr     = mapEntry.second;  // ASIC number within module
+        elinkPar.fTimeOffset = 0.;
+        elinkPar.fAdcOffset  = 1.;
+        elinkPar.fAdcGain    = 1.;
+        // TODO: Add parameters for time and ADC calibration
+        par->fElinkParams.push_back(elinkPar);
+      }
+      fAlgoSts[equip].SetParams(std::move(par));
+      L_(info) << "--- Configured equipment " << equip << " with " << numElinks << " elinks";
+    }  //# equipments
+
+    // Create one algorithm per component for MUCH and configure it with parameters
+    auto equipIdsMuch = fMuchConfig.GetEquipmentIds();
+    for (auto& equip : equipIdsMuch) {
+      std::unique_ptr<UnpackMuchPar> par(new UnpackMuchPar());
+      const size_t numElinks = fMuchConfig.GetNumElinks(equip);
+      for (size_t elink = 0; elink < numElinks; elink++) {
+        UnpackMuchElinkPar elinkPar;
+        elinkPar.fAddress    = fMuchConfig.Map(equip, elink);  // Vector of MUCH addresses for this elink
+        elinkPar.fTimeOffset = 0.;
+        par->fElinkParams.push_back(elinkPar);
+      }
+      fAlgoMuch[equip].SetParams(std::move(par));
+      L_(info) << "--- Configured equipment " << equip << " with " << numElinks << " elinks";
+    }
+
+    // Create one algorithm per component for TOF and configure it with parameters
+    auto equipIdsTof = fTofConfig.GetEquipmentIds();
+    for (auto& equip : equipIdsTof) {
+      std::unique_ptr<UnpackTofPar> par(new UnpackTofPar());
+      const size_t numElinks = fTofConfig.GetNumElinks(equip);
+      for (size_t elink = 0; elink < numElinks; elink++) {
+        UnpackTofElinkPar elinkPar;
+        elinkPar.fChannelUId = fTofConfig.Map(equip, elink);  // Vector of TOF addresses for this elink
+        elinkPar.fTimeOffset = 0.;
+        par->fElinkParams.push_back(elinkPar);
+      }
+      fAlgoTof[equip].SetParams(std::move(par));
+      L_(info) << "--- Configured equipment " << equip << " with " << numElinks << " elinks";
+    }
+
+    // Create one algorithm per component for T0 and configure it with parameters
+    auto equipIdsBmon = fBmonConfig.GetEquipmentIds();
+    for (auto& equip : equipIdsBmon) {
+      std::unique_ptr<UnpackBmonPar> par(new UnpackBmonPar());
+      const size_t numElinks = fBmonConfig.GetNumElinks(equip);
+      for (size_t elink = 0; elink < numElinks; elink++) {
+        UnpackBmonElinkPar elinkPar;
+        elinkPar.fChannelUId = fBmonConfig.Map(equip, elink);  // Vector of T0 addresses for this elink
+        elinkPar.fTimeOffset = 0.;
+        par->fElinkParams.push_back(elinkPar);
+      }
+      fAlgoBmon[equip].SetParams(std::move(par));
+      L_(info) << "--- Configured equipment " << equip << " with " << numElinks << " elinks";
+    }
+
+    // Create one algorithm per component for TRD and configure it with parameters
+    auto equipIdsTrd = fTrdConfig.GetEquipmentIds();
+    for (auto& equip : equipIdsTrd) {
+
+      std::unique_ptr<UnpackTrdPar> par(new UnpackTrdPar());
+      const size_t numCrobs = fTrdConfig.GetNumCrobs(equip);
+
+      for (size_t crob = 0; crob < numCrobs; crob++) {
+        UnpackTrdCrobPar crobPar;
+        const size_t numElinks = fTrdConfig.GetNumElinks(equip, crob);
+
+        for (size_t elink = 0; elink < numElinks; elink++) {
+          UnpackTrdElinkPar elinkPar;
+          auto addresses        = fTrdConfig.Map(equip, crob, elink);
+          elinkPar.fAddress     = addresses.first;   // Asic address for this elink
+          elinkPar.fChanAddress = addresses.second;  // Channel addresses for this elink
+          crobPar.fElinkParams.push_back(elinkPar);
+        }
+        par->fCrobParams.push_back(crobPar);
+      }
+      fAlgoTrd[equip].SetParams(std::move(par));
+      L_(info) << "--- Configured equipment " << equip << " with " << numCrobs << " crobs";
+    }
+
+    // Create one algorithm per component for TRD2D and configure it with parameters
+    auto equipIdsTrd2d = fTrd2dConfig.GetEquipmentIds();
+    for (auto& equip : equipIdsTrd2d) {
+
+      std::unique_ptr<UnpackTrd2dPar> par(new UnpackTrd2dPar());
+      const size_t numAsics = fTrd2dConfig.GetNumAsics(equip);
+
+      for (size_t asic = 0; asic < numAsics; asic++) {
+        UnpackTrd2dAsicPar asicPar;
+        const size_t numChans = fTrd2dConfig.GetNumChans(equip, asic);
+
+        for (size_t chan = 0; chan < numChans; chan++) {
+          UnpackTrd2dChannelPar chanPar;
+          auto pars            = fTrd2dConfig.ChanMap(equip, asic, chan);
+          chanPar.fPadAddress  = std::get<0>(pars);  // Pad address for channel
+          chanPar.fHasPairingR = std::get<1>(pars);  // Flag for R or T compoment
+          chanPar.fDaqOffset   = std::get<2>(pars);  // Time calibration parameter
+          asicPar.fChanParams.push_back(chanPar);
+        }
+        auto comppars = fTrd2dConfig.CompMap(equip);
+        par->fModId   = comppars.first;
+        par->fCrobId  = comppars.second;
+        par->fAsicParams.push_back(asicPar);
+      }
+      fAlgoTrd2d[equip].SetParams(std::move(par));
+      L_(info) << "--- Configured equipment " << equip << " with " << numAsics << " asics";
+    }
+
+    L_(info) << "--- Configured " << fAlgoSts.size() << " unpacker algorithms for STS.";
+    L_(debug) << "Readout map:" << fStsConfig.PrintReadoutMap();
+    L_(info) << "--- Configured " << fAlgoMuch.size() << " unpacker algorithms for MUCH.";
+    L_(info) << "--- Configured " << fAlgoTof.size() << " unpacker algorithms for TOF.";
+    L_(info) << "--- Configured " << fAlgoTrd.size() << " unpacker algorithms for TRD.";
+    L_(info) << "--- Configured " << fAlgoTrd2d.size() << " unpacker algorithms for TRD2D.";
+    L_(info) << "--- Configured " << fAlgoBmon.size() << " unpacker algorithms for T0.";
+    L_(info) << "==================================================";
+
+    return true;
+  }
+  // ----------------------------------------------------------------------------
+
+
+} /* namespace cbm::algo */
diff --git a/algo/unpack/Unpack.h b/algo/unpack/Unpack.h
new file mode 100644
index 0000000000000000000000000000000000000000..7bf24abfb961f75ad9ac3d76d1963192302ca603
--- /dev/null
+++ b/algo/unpack/Unpack.h
@@ -0,0 +1,129 @@
+/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Volker Friese, Dominik Smith [committer] */
+
+
+#ifndef UNPACK_H
+#define UNPACK_H 1
+
+#include "CbmDigiTimeslice.h"
+
+#include "TofReadoutConfig.h"
+#include "Trd2dReadoutConfig.h"
+#include "TrdReadoutConfig.h"
+
+#include <sstream>
+#include <vector>
+
+#include "BmonReadoutConfig.h"
+#include "MuchReadoutConfig.h"
+#include "StsReadoutConfigLegacy.h"
+#include "UnpackBmon.h"
+#include "UnpackMuch.h"
+#include "UnpackSts.h"
+#include "UnpackTof.h"
+#include "UnpackTrd.h"
+#include "UnpackTrd2d.h"
+
+namespace cbm::algo
+{
+
+  /** @struct UnpackMonitorData
+   ** @author Dominik Smith <d.smith@gsi.de>
+   ** @since 2 Jun 2023
+   ** @brief Monitoring data for unpacking
+   **/
+  struct UnpackMonitorData {
+    std::vector<UnpackStsMonitorData> fSts;      ///< Monitoring data for STS
+    std::vector<UnpackMuchMonitorData> fMuch;    ///< Monitoring data for MUCH
+    std::vector<UnpackTofMonitorData> fTof;      ///< Monitoring data for TOF
+    std::vector<UnpackBmonMonitorData> fBmon;    ///< Monitoring data for T0
+    std::vector<UnpackTrdMonitorData> fTrd;      ///< Monitoring data for TRD
+    std::vector<UnpackTrd2dMonitorData> fTrd2d;  ///< Monitoring data for TRD2D
+    size_t fNumMs       = 0;
+    size_t fNumBytes    = 0;
+    size_t fNumDigis    = 0;
+    size_t fNumCompUsed = 0;
+    std::string print()
+    {
+      std::stringstream ss;
+      ss << "TS stats: num MS " << fNumMs << ", bytes " << fNumBytes << ", digis " << fNumDigis << ", components "
+         << fNumCompUsed << std::endl;
+      return ss.str();
+    }
+  };
+
+  /** @class Unpack
+ ** @brief Algo class for unpacking digi timeslicess
+ ** @author Dominik Smith <d.smith@gsi.de>
+ ** @since 02.06.2023
+ **
+ **/
+  class Unpack {
+
+  public:
+    typedef std::pair<CbmDigiTimeslice, UnpackMonitorData> resultType;
+
+    /** @brief Algorithm execution
+     ** @param fles timeslice to unpack
+     ** @return pair: digi timeslice, monitoring data
+     **/
+    resultType operator()(const fles::Timeslice* timeslice);
+
+    /** @brief Default constructor **/
+    Unpack() {};
+
+    /** @brief Destructor **/
+    ~Unpack() {};
+
+    /** @brief Parameters for STS unpackers **/
+    StsReadoutConfigLegacy fStsConfig {};
+
+    /** @brief Parameters for MUCH unpackers **/
+    MuchReadoutConfig fMuchConfig {};
+
+    /** @brief Parameters for TOF unpackers **/
+    TofReadoutConfig fTofConfig {};
+
+    /** @brief Parameters for T0 unpackers **/
+    BmonReadoutConfig fBmonConfig {};
+
+    /** @brief Parameters for TRD unpackers **/
+    TrdReadoutConfig fTrdConfig {};
+
+    /** @brief Parameters for TRD2D unpackers **/
+    Trd2dReadoutConfig fTrd2dConfig {};
+
+    /** @brief Initialize unpackers and fill parameters from config objects **/
+    bool Init();
+
+  private:  // methods
+    /** @brief Microslice loop **/
+    template<class Digi, class UnpackAlgo, class MonitorData>
+    void MsLoop(const fles::Timeslice* timeslice, std::map<uint16_t, UnpackAlgo>& algoMap, const uint64_t comp,
+                const uint16_t eqId, std::vector<Digi>* digis, UnpackMonitorData& monitor,
+                std::vector<MonitorData>* monitorMs, uint8_t sys_ver);
+
+
+  private:  // members
+    /** @brief STS unpackers **/
+    std::map<uint16_t, UnpackSts> fAlgoSts = {};
+
+    /** @brief MUCH unpackers **/
+    std::map<uint16_t, UnpackMuch> fAlgoMuch = {};
+
+    /** @brief TOF unpackers **/
+    std::map<uint16_t, UnpackTof> fAlgoTof = {};
+
+    /** @brief T0 unpackers **/
+    std::map<uint16_t, UnpackBmon> fAlgoBmon = {};
+
+    /** @brief TRD unpackers **/
+    std::map<uint16_t, UnpackTrd> fAlgoTrd = {};
+
+    /** @brief TRD2D unpackers **/
+    std::map<uint16_t, UnpackTrd2d> fAlgoTrd2d = {};
+  };
+}  // namespace cbm::algo
+
+#endif /* UNPACK_H */
diff --git a/reco/tasks/CbmTaskUnpack.cxx b/reco/tasks/CbmTaskUnpack.cxx
index f1e06438ab13c4f78a8ea751089dc9ea4abbead4..6724a37b3c8d8f8b86f6898451fb4d372f7ef6d9 100644
--- a/reco/tasks/CbmTaskUnpack.cxx
+++ b/reco/tasks/CbmTaskUnpack.cxx
@@ -38,21 +38,6 @@
 #include <vector>
 
 using namespace std;
-using cbm::algo::UnpackBmonElinkPar;
-using cbm::algo::UnpackBmonPar;
-using cbm::algo::UnpackMuchElinkPar;
-using cbm::algo::UnpackMuchPar;
-using cbm::algo::UnpackStsElinkPar;
-using cbm::algo::UnpackStsPar;
-using cbm::algo::UnpackTofElinkPar;
-using cbm::algo::UnpackTofPar;
-using cbm::algo::UnpackTrd2dAsicPar;
-using cbm::algo::UnpackTrd2dChannelPar;
-using cbm::algo::UnpackTrd2dPar;
-using cbm::algo::UnpackTrdCrobPar;
-using cbm::algo::UnpackTrdElinkPar;
-using cbm::algo::UnpackTrdPar;
-
 
 // -----   Constructor   -----------------------------------------------------
 CbmTaskUnpack::CbmTaskUnpack() : FairTask("Unpack") {}
@@ -67,29 +52,6 @@ CbmTaskUnpack::~CbmTaskUnpack()
 // ---------------------------------------------------------------------------
 
 
-// ----------------- Microslice loop ------------------------------------------
-template<class Digi, class UnpackAlgo>
-uint64_t CbmTaskUnpack::MsLoop(const fles::Timeslice* timeslice, UnpackAlgo& algo, const uint64_t comp,
-                               std::vector<Digi>* digis, size_t* numBytesInComp, size_t* numDigisInComp)
-{
-  const uint64_t numMsInComp = timeslice->num_microslices(comp);
-
-  for (uint64_t mslice = 0; mslice < numMsInComp; mslice++) {
-    const auto msDescriptor = timeslice->descriptor(comp, mslice);
-    const auto msContent    = timeslice->content(comp, mslice);
-    *numBytesInComp += msDescriptor.size;
-    auto result = algo(msContent, msDescriptor, timeslice->start_time());
-    LOG(debug1) << GetName() << ": Component " << comp << ", microslice " << mslice << ", digis " << result.first.size()
-                << ", " << result.second.print();
-    *numDigisInComp += result.first.size();
-#pragma omp critical(insert_digis)
-    digis->insert(digis->end(), result.first.begin(), result.first.end());
-  }
-  return numMsInComp;
-}
-// ----------------------------------------------------------------------------
-
-
 // -----   Execution   -------------------------------------------------------
 void CbmTaskUnpack::Exec(Option_t*)
 {
@@ -105,179 +67,10 @@ void CbmTaskUnpack::Exec(Option_t*)
   // --- Timer and counters
   TStopwatch timer;
   timer.Start();
-  size_t numMs         = 0;
-  size_t numBytes      = 0;
-  size_t numDigis      = 0;
-  uint64_t numCompUsed = 0;
-
-  // ---  Component loop
-#pragma omp parallel for schedule(dynamic) shared(timeslice) reduction(+ : numMs, numBytes, numDigis, numCompUsed)
-  for (uint64_t comp = 0; comp < timeslice->num_components(); comp++) {
-
-    // --- Component log
-    size_t numBytesInComp = 0;
-    size_t numDigisInComp = 0;
-    uint64_t numMsInComp  = 0;
-
-    TStopwatch compTimer;
-    compTimer.Start();
-
-    auto systemId = static_cast<fles::SubsystemIdentifier>(timeslice->descriptor(comp, 0).sys_id);
-
-    // STS
-    if (systemId == fles::SubsystemIdentifier::STS) {
-      const uint16_t equipmentId = timeslice->descriptor(comp, 0).eq_id;
-      const auto algoIt          = fAlgoSts.find(equipmentId);
-      assert(algoIt != fAlgoSts.end());
-
-      // The current algorithm works for the STS data format version 0x20 used in 2021.
-      // Other versions are not yet supported.
-      // In the future, different data formats will be supported by instantiating different
-      // algorithms depending on the version.
-      assert(timeslice->descriptor(comp, 0).sys_ver == 0x20);
-
-      // --- Microslice loop
-      numMsInComp =
-        MsLoop(timeslice, algoIt->second, comp, &fTimeslice->fData.fSts.fDigis, &numBytesInComp, &numDigisInComp);
-
-      numCompUsed++;
-    }  // system STS
-
-    // MUCH
-    if (systemId == fles::SubsystemIdentifier::MUCH) {
-      const uint16_t equipmentId = timeslice->descriptor(comp, 0).eq_id;
-      const auto algoIt          = fAlgoMuch.find(equipmentId);
-      assert(algoIt != fAlgoMuch.end());
-
-      // The current algorithm works for the MUCH data format version 0x20 used in 2021.
-      // Other versions are not yet supported.
-      // In the future, different data formats will be supported by instantiating different
-      // algorithms depending on the version.
-      assert(timeslice->descriptor(comp, 0).sys_ver == 0x20);
-
-      // --- Microslice loop
-      numMsInComp =
-        MsLoop(timeslice, algoIt->second, comp, &fTimeslice->fData.fMuch.fDigis, &numBytesInComp, &numDigisInComp);
-
-      numCompUsed++;
-    }  // system MUCH
-
-    // TOF
-    if (systemId == fles::SubsystemIdentifier::RPC) {
-      const uint16_t equipmentId = timeslice->descriptor(comp, 0).eq_id;
-      const auto algoIt          = fAlgoTof.find(equipmentId);
-      assert(algoIt != fAlgoTof.end());
-
-      // The current algorithm works for the TOF data format version XXXX used in 2021.
-      // Other versions are not yet supported.
-      // In the future, different data formats will be supported by instantiating different
-      // algorithms depending on the version.
-      //assert(timeslice->descriptor(comp, 0).sys_ver == XXXX);
-
-      // --- Microslice loop
-      numMsInComp =
-        MsLoop(timeslice, algoIt->second, comp, &fTimeslice->fData.fTof.fDigis, &numBytesInComp, &numDigisInComp);
-
-      numCompUsed++;
-    }  // system TOF
-
-    // Bmon
-    if (systemId == fles::SubsystemIdentifier::T0) {
-      const uint16_t equipmentId = timeslice->descriptor(comp, 0).eq_id;
-      const auto algoIt          = fAlgoBmon.find(equipmentId);
-      assert(algoIt != fAlgoBmon.end());
-
-      // The current algorithm works for the T0 data format version XXXX used in 2021.
-      // Other versions are not yet supported.
-      // In the future, different data formats will be supported by instantiating different
-      // algorithms depending on the version.
-      //assert(timeslice->descriptor(comp, 0).sys_ver == XXXX);
-
-      // --- Microslice loop
-      numMsInComp =
-        MsLoop(timeslice, algoIt->second, comp, &fTimeslice->fData.fT0.fDigis, &numBytesInComp, &numDigisInComp);
-
-      numCompUsed++;
-    }  // system T0
-
-    // system TRD
-    if (systemId == fles::SubsystemIdentifier::TRD) {
-      const uint16_t equipmentId = timeslice->descriptor(comp, 0).eq_id;
-      const auto algoIt          = fAlgoTrd.find(equipmentId);
-      assert(algoIt != fAlgoTrd.end());
-
-      // The current algorithm works for the TRD data format version XXX used in 2022.
-      // Other versions are not yet supported.
-      // In the future, different data formats will be supported by instantiating different
-      // algorithms depending on the version.
-      //assert(timeslice->descriptor(comp, 0).sys_ver == XXX);  To do: add something sensible here
-
-      // --- Microslice loop
-      numMsInComp =
-        MsLoop(timeslice, algoIt->second, comp, &fTimeslice->fData.fTrd.fDigis, &numBytesInComp, &numDigisInComp);
-
-      numCompUsed++;
-    }  // system TRD
-
-    // system TRD2D
-    if (systemId == fles::SubsystemIdentifier::TRD2D) {
-      const uint16_t equipmentId = timeslice->descriptor(comp, 0).eq_id;
-      const auto algoIt          = fAlgoTrd2d.find(equipmentId);
-      assert(algoIt != fAlgoTrd2d.end());
-
-      // The current algorithm works for the TRD2D data format version XXX used in 2022.
-      // Other versions are not yet supported.
-      // In the future, different data formats will be supported by instantiating different
-      // algorithms depending on the version.
-      //assert(timeslice->descriptor(comp, 0).sys_ver == XXX);  To do: add something sensible here
-
-      // --- Microslice loop
-      numMsInComp =
-        MsLoop(timeslice, algoIt->second, comp, &fTimeslice->fData.fTrd2d.fDigis, &numBytesInComp, &numDigisInComp);
-
-      numCompUsed++;
-    }  // system TRD2D
-
-
-    compTimer.Stop();
-    LOG(debug) << GetName() << ": Component " << comp << ", microslices " << numMsInComp << " input size "
-               << numBytesInComp << " bytes, "
-               << ", digis " << numDigisInComp << ", CPU time " << compTimer.CpuTime() * 1000. << " ms";
-
-    numBytes += numBytesInComp;
-    numDigis += numDigisInComp;
-    numMs += numMsInComp;
-
-  }  //# component
-
-  // --- Sorting of output digis. Is required by both digi trigger and event builder.
-#ifdef WITH_EXECUTION
-  std::sort(std::execution::par_unseq, fTimeslice->fData.fSts.fDigis.begin(), fTimeslice->fData.fSts.fDigis.end(),
-            [](CbmStsDigi digi1, CbmStsDigi digi2) { return digi1.GetTime() < digi2.GetTime(); });
-  std::sort(std::execution::par_unseq, fTimeslice->fData.fMuch.fDigis.begin(), fTimeslice->fData.fMuch.fDigis.end(),
-            [](CbmMuchDigi digi1, CbmMuchDigi digi2) { return digi1.GetTime() < digi2.GetTime(); });
-  std::sort(std::execution::par_unseq, fTimeslice->fData.fTof.fDigis.begin(), fTimeslice->fData.fTof.fDigis.end(),
-            [](CbmTofDigi digi1, CbmTofDigi digi2) { return digi1.GetTime() < digi2.GetTime(); });
-  std::sort(std::execution::par_unseq, fTimeslice->fData.fT0.fDigis.begin(), fTimeslice->fData.fT0.fDigis.end(),
-            [](CbmTofDigi digi1, CbmTofDigi digi2) { return digi1.GetTime() < digi2.GetTime(); });
-  std::sort(std::execution::par_unseq, fTimeslice->fData.fTrd.fDigis.begin(), fTimeslice->fData.fTrd.fDigis.end(),
-            [](CbmTrdDigi digi1, CbmTrdDigi digi2) { return digi1.GetTime() < digi2.GetTime(); });
-  std::sort(std::execution::par_unseq, fTimeslice->fData.fTrd2d.fDigis.begin(), fTimeslice->fData.fTrd2d.fDigis.end(),
-            [](CbmTrdDigi digi1, CbmTrdDigi digi2) { return digi1.GetTime() < digi2.GetTime(); });
-#else
-  std::sort(fTimeslice->fData.fSts.fDigis.begin(), fTimeslice->fData.fSts.fDigis.end(),
-            [](CbmStsDigi digi1, CbmStsDigi digi2) { return digi1.GetTime() < digi2.GetTime(); });
-  std::sort(fTimeslice->fData.fMuch.fDigis.begin(), fTimeslice->fData.fMuch.fDigis.end(),
-            [](CbmMuchDigi digi1, CbmMuchDigi digi2) { return digi1.GetTime() < digi2.GetTime(); });
-  std::sort(fTimeslice->fData.fTof.fDigis.begin(), fTimeslice->fData.fTof.fDigis.end(),
-            [](CbmTofDigi digi1, CbmTofDigi digi2) { return digi1.GetTime() < digi2.GetTime(); });
-  std::sort(fTimeslice->fData.fT0.fDigis.begin(), fTimeslice->fData.fT0.fDigis.end(),
-            [](CbmTofDigi digi1, CbmTofDigi digi2) { return digi1.GetTime() < digi2.GetTime(); });
-  std::sort(fTimeslice->fData.fTrd.fDigis.begin(), fTimeslice->fData.fTrd.fDigis.end(),
-            [](CbmTrdDigi digi1, CbmTrdDigi digi2) { return digi1.GetTime() < digi2.GetTime(); });
-  std::sort(fTimeslice->fData.fTrd2d.fDigis.begin(), fTimeslice->fData.fTrd2d.fDigis.end(),
-            [](CbmTrdDigi digi1, CbmTrdDigi digi2) { return digi1.GetTime() < digi2.GetTime(); });
-#endif
+
+  // --- Unpack the timeslice
+  auto result = fUnpack(timeslice);
+  *fTimeslice = std::move(result.first);
 
   // --- Timeslice log
   timer.Stop();
@@ -285,16 +78,17 @@ void CbmTaskUnpack::Exec(Option_t*)
   logOut << setw(15) << left << GetName() << " [";
   logOut << fixed << setw(8) << setprecision(1) << right << timer.RealTime() * 1000. << " ms] ";
   logOut << "TS " << fNumTs << " (index " << timeslice->index() << ")";
-  logOut << ", components " << numCompUsed << " / " << timeslice->num_components() << ", microslices " << numMs;
-  logOut << ", input rate " << double(numBytes) / timer.RealTime() / 1.e6 << " MB/s";
-  logOut << ", digis " << numDigis;
+  logOut << ", components " << result.second.fNumCompUsed << " / " << timeslice->num_components();
+  logOut << ", microslices " << result.second.fNumMs;
+  logOut << ", input rate " << double(result.second.fNumBytes) / timer.RealTime() / 1.e6 << " MB/s";
+  logOut << ", digis " << result.second.fNumDigis;
   LOG(info) << logOut.str();
 
   // --- Run statistics
   fNumTs++;
-  fNumMs += numMs;
-  fNumBytes += numBytes;
-  fNumDigis += numDigis;
+  fNumMs += result.second.fNumMs;
+  fNumBytes += result.second.fNumBytes;
+  fNumDigis += result.second.fNumDigis;
   fTime += timer.RealTime();
 }
 // ----------------------------------------------------------------------------
@@ -345,144 +139,21 @@ InitStatus CbmTaskUnpack::Init()
   ioman->RegisterAny("DigiTimeslice.", fTimeslice, IsOutputBranchPersistent("DigiTimeslice."));
   LOG(info) << "--- Registered branch DigiTimeslice.";
 
-  // --- Common parameters for all components for STS
-  uint32_t numChansPerAsicSts   = 128;  // R/O channels per ASIC for STS
-  uint32_t numAsicsPerModuleSts = 16;   // Number of ASICs per module for STS
-
-  // Create one algorithm per component for STS and configure it with parameters
-  auto equipIdsSts = fStsConfig.GetEquipmentIds();
-  for (auto& equip : equipIdsSts) {
-    std::unique_ptr<UnpackStsPar> par(new UnpackStsPar());
-    par->fNumChansPerAsic   = numChansPerAsicSts;
-    par->fNumAsicsPerModule = numAsicsPerModuleSts;
-    const size_t numElinks  = fStsConfig.GetNumElinks(equip);
-    for (size_t elink = 0; elink < numElinks; elink++) {
-      UnpackStsElinkPar elinkPar;
-      auto mapEntry        = fStsConfig.Map(equip, elink);
-      elinkPar.fAddress    = mapEntry.first;   // Module address for this elink
-      elinkPar.fAsicNr     = mapEntry.second;  // ASIC number within module
-      elinkPar.fTimeOffset = 0.;
-      elinkPar.fAdcOffset  = 1.;
-      elinkPar.fAdcGain    = 1.;
-      // TODO: Add parameters for time and ADC calibration
-      par->fElinkParams.push_back(elinkPar);
-    }
-    fAlgoSts[equip].SetParams(std::move(par));
-    LOG(info) << "--- Configured equipment " << equip << " with " << numElinks << " elinks";
-  }  //# equipments
-
-  // Create one algorithm per component and configure it with parameters
-  auto equipIdsMuch = fMuchConfig.GetEquipmentIds();
-  for (auto& equip : equipIdsMuch) {
-    std::unique_ptr<UnpackMuchPar> par(new UnpackMuchPar());
-    const size_t numElinks = fMuchConfig.GetNumElinks(equip);
-    for (size_t elink = 0; elink < numElinks; elink++) {
-      UnpackMuchElinkPar elinkPar;
-      elinkPar.fAddress    = fMuchConfig.Map(equip, elink);  // Vector of MUCH addresses for this elink
-      elinkPar.fTimeOffset = 0.;
-      par->fElinkParams.push_back(elinkPar);
-    }
-    fAlgoMuch[equip].SetParams(std::move(par));
-    LOG(info) << "--- Configured equipment " << equip << " with " << numElinks << " elinks";
-  }
-
-  // Create one algorithm per component and configure it with parameters
-  auto equipIdsTof = fTofConfig.GetEquipmentIds();
-  for (auto& equip : equipIdsTof) {
-    std::unique_ptr<UnpackTofPar> par(new UnpackTofPar());
-    const size_t numElinks = fTofConfig.GetNumElinks(equip);
-    for (size_t elink = 0; elink < numElinks; elink++) {
-      UnpackTofElinkPar elinkPar;
-      elinkPar.fChannelUId = fTofConfig.Map(equip, elink);  // Vector of TOF addresses for this elink
-      elinkPar.fTimeOffset = 0.;
-      par->fElinkParams.push_back(elinkPar);
-    }
-    fAlgoTof[equip].SetParams(std::move(par));
-    LOG(info) << "--- Configured equipment " << equip << " with " << numElinks << " elinks";
-  }
-
-  // Create one algorithm per component and configure it with parameters
-  auto equipIdsBmon = fBmonConfig.GetEquipmentIds();
-  for (auto& equip : equipIdsBmon) {
-    std::unique_ptr<UnpackBmonPar> par(new UnpackBmonPar());
-    const size_t numElinks = fBmonConfig.GetNumElinks(equip);
-    for (size_t elink = 0; elink < numElinks; elink++) {
-      UnpackBmonElinkPar elinkPar;
-      elinkPar.fChannelUId = fBmonConfig.Map(equip, elink);  // Vector of T0 addresses for this elink
-      elinkPar.fTimeOffset = 0.;
-      par->fElinkParams.push_back(elinkPar);
-    }
-    fAlgoBmon[equip].SetParams(std::move(par));
-    LOG(info) << "--- Configured equipment " << equip << " with " << numElinks << " elinks";
-  }
-
-  InitTrdReadoutConfig();
-  auto equipIdsTrd = fTrdConfig.GetEquipmentIds();
-  for (auto& equip : equipIdsTrd) {
-
-    std::unique_ptr<UnpackTrdPar> par(new UnpackTrdPar());
-    const size_t numCrobs = fTrdConfig.GetNumCrobs(equip);
-
-    for (size_t crob = 0; crob < numCrobs; crob++) {
-      UnpackTrdCrobPar crobPar;
-      const size_t numElinks = fTrdConfig.GetNumElinks(equip, crob);
-
-      for (size_t elink = 0; elink < numElinks; elink++) {
-        UnpackTrdElinkPar elinkPar;
-        auto addresses        = fTrdConfig.Map(equip, crob, elink);
-        elinkPar.fAddress     = addresses.first;   // Asic address for this elink
-        elinkPar.fChanAddress = addresses.second;  // Channel addresses for this elink
-        crobPar.fElinkParams.push_back(elinkPar);
-      }
-      par->fCrobParams.push_back(crobPar);
-    }
-    fAlgoTrd[equip].SetParams(std::move(par));
-    LOG(info) << "--- Configured equipment " << equip << " with " << numCrobs << " crobs";
-  }
-
-  InitTrd2dReadoutConfig();
-  auto equipIdsTrd2d = fTrd2dConfig.GetEquipmentIds();
-  for (auto& equip : equipIdsTrd2d) {
-
-    std::unique_ptr<UnpackTrd2dPar> par(new UnpackTrd2dPar());
-    const size_t numAsics = fTrd2dConfig.GetNumAsics(equip);
-
-    for (size_t asic = 0; asic < numAsics; asic++) {
-      UnpackTrd2dAsicPar asicPar;
-      const size_t numChans = fTrd2dConfig.GetNumChans(equip, asic);
-
-      for (size_t chan = 0; chan < numChans; chan++) {
-        UnpackTrd2dChannelPar chanPar;
-        auto pars            = fTrd2dConfig.ChanMap(equip, asic, chan);
-        chanPar.fPadAddress  = std::get<0>(pars);  // Pad address for channel
-        chanPar.fHasPairingR = std::get<1>(pars);  // Flag for R or T compoment
-        chanPar.fDaqOffset   = std::get<2>(pars);  // Time calibration parameter
-        asicPar.fChanParams.push_back(chanPar);
-      }
-      auto comppars = fTrd2dConfig.CompMap(equip);
-      par->fModId   = comppars.first;
-      par->fCrobId  = comppars.second;
-      par->fAsicParams.push_back(asicPar);
-    }
-    fAlgoTrd2d[equip].SetParams(std::move(par));
-    LOG(info) << "--- Configured equipment " << equip << " with " << numAsics << " asics";
-  }
-
-  LOG(info) << "--- Configured " << fAlgoSts.size() << " unpacker algorithms for STS.";
-  LOG(debug) << "Readout map:" << fStsConfig.PrintReadoutMap();
-  LOG(info) << "--- Configured " << fAlgoMuch.size() << " unpacker algorithms for MUCH.";
-  LOG(info) << "--- Configured " << fAlgoTof.size() << " unpacker algorithms for TOF.";
-  LOG(info) << "--- Configured " << fAlgoBmon.size() << " unpacker algorithms for T0.";
-
-  LOG(info) << "==================================================";
+  // ---- Initialize unpacker
+  fUnpack.fTrdConfig   = InitTrdReadoutConfig();
+  fUnpack.fTrd2dConfig = InitTrd2dReadoutConfig();
+  fUnpack.Init();
 
   return kSUCCESS;
 }
 // ----------------------------------------------------------------------------
 
 // -----   Initialisation   ---------------------------------------------------
-void CbmTaskUnpack::InitTrd2dReadoutConfig()
+cbm::algo::Trd2dReadoutConfig CbmTaskUnpack::InitTrd2dReadoutConfig()
 {
+  // Output object
+  cbm::algo::Trd2dReadoutConfig Trd2dConfig;
+
   // Initialize input files
   FairParAsciiFileIo asciiInput;
   std::string digiparfile = Form("%s/parameters/trd/trd_v22h_mcbm.digi.par", std::getenv("VMCWORKDIR"));
@@ -504,7 +175,7 @@ void CbmTaskUnpack::InitTrd2dReadoutConfig()
   memcpy(crob_map[5], cmap, NCROBMOD * sizeof(uint16_t));  // only module Id 5 is used!
 
   // Then pass to Trd2dReadoutConfig, will invert to obain map (equipId) -> (module iq, crob id)
-  fTrd2dConfig.InitComponentMap(crob_map);
+  Trd2dConfig.InitComponentMap(crob_map);
 
   // FASP mapping update wrt the default setting (optional)
   std::map<uint32_t, uint8_t[NFASPMOD]> fasp_map;
@@ -575,12 +246,16 @@ void CbmTaskUnpack::InitTrd2dReadoutConfig()
     }
   }
 
-  fTrd2dConfig.InitChannelMap(channelMap);
+  Trd2dConfig.InitChannelMap(channelMap);
+  return Trd2dConfig;
 }
 
 // -----   Initialisation   ---------------------------------------------------
-void CbmTaskUnpack::InitTrdReadoutConfig()
+cbm::algo::TrdReadoutConfig CbmTaskUnpack::InitTrdReadoutConfig()
 {
+  // Output object
+  cbm::algo::TrdReadoutConfig TrdConfig;
+
   std::string trdparfile = Form("%s/parameters/trd/trd_v21d_mcbm.asic.par", std::getenv("VMCWORKDIR"));
 
   CbmTrdParSetAsic trdpar;
@@ -631,8 +306,9 @@ void CbmTaskUnpack::InitTrdReadoutConfig()
                  << "address " << address << " key " << criId << " " << unsigned(crobId) << " " << unsigned(elinkId);
     }
   }
-  fTrdConfig.Init(addressMap, channelMap);
-  LOG(debug) << fTrdConfig.PrintReadoutMap();
+  TrdConfig.Init(addressMap, channelMap);
+  LOG(debug) << TrdConfig.PrintReadoutMap();
+  return TrdConfig;
 }
 
 
diff --git a/reco/tasks/CbmTaskUnpack.h b/reco/tasks/CbmTaskUnpack.h
index e686a2c8162000d14409480d6cd544d636ed09a6..d4262eb6bb9411987fa936c55dcdec3f318a51fb 100644
--- a/reco/tasks/CbmTaskUnpack.h
+++ b/reco/tasks/CbmTaskUnpack.h
@@ -11,23 +11,13 @@
 
 #include <FairTask.h>
 
-#include "TofReadoutConfig.h"
 #include "Trd2dReadoutConfig.h"
 #include "TrdReadoutConfig.h"
 
 #include <sstream>
 #include <vector>
 
-#include "BmonReadoutConfig.h"
-#include "EventBuilder.h"
-#include "MuchReadoutConfig.h"
-#include "StsReadoutConfigLegacy.h"
-#include "UnpackBmon.h"
-#include "UnpackMuch.h"
-#include "UnpackSts.h"
-#include "UnpackTof.h"
-#include "UnpackTrd.h"
-#include "UnpackTrd2d.h"
+#include "Unpack.h"
 
 class CbmDigiManager;
 class CbmSourceTs;
@@ -76,32 +66,19 @@ private:  // methods
   /** @brief Task initialisation **/
   virtual InitStatus Init();
 
+
   /** @brief Initialisation of address maps for Trd **/
-  virtual void InitTrdReadoutConfig();
+  cbm::algo::TrdReadoutConfig InitTrdReadoutConfig();
+
 
   /** @brief Initialisation of address maps for Trd2d **/
-  virtual void InitTrd2dReadoutConfig();
+  cbm::algo::Trd2dReadoutConfig InitTrd2dReadoutConfig();
 
 private:  // members
   CbmSourceTs* fSource = nullptr;
 
-  std::map<uint16_t, cbm::algo::UnpackSts> fAlgoSts = {};
-  cbm::algo::StsReadoutConfigLegacy fStsConfig {};
-
-  std::map<uint16_t, cbm::algo::UnpackMuch> fAlgoMuch = {};
-  cbm::algo::MuchReadoutConfig fMuchConfig {};
-
-  std::map<uint16_t, cbm::algo::UnpackTof> fAlgoTof = {};
-  cbm::algo::TofReadoutConfig fTofConfig {};
-
-  std::map<uint16_t, cbm::algo::UnpackBmon> fAlgoBmon = {};
-  cbm::algo::BmonReadoutConfig fBmonConfig {};
-
-  std::map<uint16_t, cbm::algo::UnpackTrd> fAlgoTrd = {};
-  cbm::algo::TrdReadoutConfig fTrdConfig {};
-
-  std::map<uint16_t, cbm::algo::UnpackTrd2d> fAlgoTrd2d = {};
-  cbm::algo::Trd2dReadoutConfig fTrd2dConfig {};
+  /** @brief Unpacker algorithm **/
+  cbm::algo::Unpack fUnpack;
 
   size_t fNumTs                = 0;
   size_t fNumMs                = 0;
@@ -110,12 +87,7 @@ private:  // members
   double fTime                 = 0.;
   CbmDigiTimeslice* fTimeslice = nullptr;  ///< Output data
 
-  //Microslice loop
-  template<class Digi, class UnpackAlgo>
-  uint64_t MsLoop(const fles::Timeslice* timeslice, UnpackAlgo& algo, const uint64_t comp, std::vector<Digi>* digis,
-                  size_t* numBytesInComp, size_t* numDigisInComp);
-
-  ClassDef(CbmTaskUnpack, 2);
+  ClassDef(CbmTaskUnpack, 3);
 };
 
 #endif /* CBMTASKUNPACK_H */