diff --git a/algo/ca/TrackingChain.cxx b/algo/ca/TrackingChain.cxx
index 8e16a8f2c40adc1a5a04479d034d4442625e86eb..e44a7341af6bfffcae385f0484485d3c3271cf3a 100644
--- a/algo/ca/TrackingChain.cxx
+++ b/algo/ca/TrackingChain.cxx
@@ -81,6 +81,7 @@ void TrackingChain::Init()
   fbDetUsed.fill(false);
   fbDetUsed[EDetectorID::Sts] = Opts().Has(fles::Subsystem::STS) && parameters.IsActive(EDetectorID::Sts);
   fbDetUsed[EDetectorID::Tof] = Opts().Has(fles::Subsystem::TOF) && parameters.IsActive(EDetectorID::Tof);
+  fbDetUsed[EDetectorID::Trd] = Opts().Has(fles::Subsystem::TRD) && parameters.IsActive(EDetectorID::Trd);
 
   // ------ Initialize CA framework
   fCaMonitor.Reset();
@@ -141,7 +142,7 @@ void TrackingChain::Finalize()
 void TrackingChain::PrepareInput(Input_t recoResults)
 {
   fNofHitKeys  = 0;
-  int nHitsTot = recoResults.stsHits.NElements() + recoResults.tofHits.NElements();
+  int nHitsTot = recoResults.stsHits.NElements() + recoResults.tofHits.NElements() + recoResults.trdHits.NElements();
   L_(info) << "Tracking chain: input has " << nHitsTot << " hits";
   fCaDataManager.ResetInputData(nHitsTot);
   faHitExternalIndices.clear();
@@ -152,9 +153,12 @@ void TrackingChain::PrepareInput(Input_t recoResults)
   if (fbDetUsed[EDetectorID::Tof]) {
     ReadHits<EDetectorID::Tof>(recoResults.tofHits);
   }
+  if (fbDetUsed[EDetectorID::Trd]) {
+    ReadHits<EDetectorID::Trd>(recoResults.trdHits);
+  }
   faHitExternalIndices.shrink_to_fit();
   fCaDataManager.SetNhitKeys(fNofHitKeys);
-  L_(info) << "Tracking chain:" << fCaDataManager.GetNofHits() << " hits will be passed to the ca::Framework";
+  L_(info) << "Tracking chain: " << fCaDataManager.GetNofHits() << " hits will be passed to the ca::Framework";
   fCaFramework.ReceiveInputData(fCaDataManager.TakeInputData());
 }
 
@@ -168,11 +172,13 @@ TrackingChain::Output_t TrackingChain::PrepareOutput()
 
   output.stsHitIndices.reset(nTracks);
   output.tofHitIndices.reset(nTracks);
+  output.trdHitIndices.reset(nTracks);
 
   int trackFirstHit = 0;
   for (int iTrk = 0; iTrk < nTracks; ++iTrk) {
     output.stsHitIndices[iTrk].clear();
     output.tofHitIndices[iTrk].clear();
+    output.trdHitIndices[iTrk].clear();
     int nHits = output.tracks[iTrk].fNofHits;
     for (int iHit = 0; iHit < nHits; ++iHit) {
       int iHitInternal = fCaFramework.GetInputData().GetHit(fCaFramework.fRecoHits[trackFirstHit + iHit]).Id();
@@ -180,18 +186,22 @@ TrackingChain::Output_t TrackingChain::PrepareOutput()
       switch (detID) {
         case ca::EDetectorID::Sts: output.stsHitIndices[iTrk].push_back(std::make_pair(iPartition, iPartHit)); break;
         case ca::EDetectorID::Tof: output.tofHitIndices[iTrk].push_back(std::make_pair(iPartition, iPartHit)); break;
+        case ca::EDetectorID::Trd: output.trdHitIndices[iTrk].push_back(std::make_pair(iPartition, iPartHit)); break;
         default: break;
       }
     }
     fCaMonitorData.IncrementCounter(ca::ECounter::RecoStsHit, output.stsHitIndices[iTrk].size());
     fCaMonitorData.IncrementCounter(ca::ECounter::RecoTofHit, output.tofHitIndices[iTrk].size());
+    fCaMonitorData.IncrementCounter(ca::ECounter::RecoTrdHit, output.trdHitIndices[iTrk].size());
+
     trackFirstHit += nHits;
   }
 
   L_(info) << "TrackingChain: Timeslice contains " << fCaMonitorData.GetCounterValue(ca::ECounter::RecoTrack)
            << " tracks, with " << fCaMonitorData.GetCounterValue(ca::ECounter::RecoStsHit) << " sts hits, "
-           << fCaMonitorData.GetCounterValue(ca::ECounter::RecoTofHit) << " tof hits; the FindTracks routine ran "
-           << fCaMonitorData.GetTimer(ca::ETimer::FindTracks).GetTotal() << " s";
+           << fCaMonitorData.GetCounterValue(ca::ECounter::RecoTofHit) << " tof hits, "
+           << fCaMonitorData.GetCounterValue(ca::ECounter::RecoTrdHit) << " trd hits"
+           << "; the FindTracks routine ran " << fCaMonitorData.GetTimer(ca::ETimer::FindTracks).GetTotal() << " s";
 
 
   // QA
@@ -254,8 +264,8 @@ void TrackingChain::ReadHits(PartitionedSpan<const ca::HitTypes_t::at<DetID>> hi
 
   xpu::t_add_bytes(hits.NElements() * sizeof(Hit_t));  // Assumes call from Run, for existence of timer!
 
-  int64_t dataStreamDet         = static_cast<int64_t>(DetID) << 60;  // detector part of the data stream
-  int64_t dataStream            = 0;
+  int64_t dataStreamDet = static_cast<int64_t>(DetID) << 60;  // detector part of the data stream
+  int64_t dataStream    = 0;
   for (size_t iPartition = 0; iPartition < hits.NPartitions(); ++iPartition, ++dataStream) {
     const auto& [vHits, extHitAddress] = hits.Partition(iPartition);
     // ---- Define data stream and station index
@@ -269,8 +279,12 @@ void TrackingChain::ReadHits(PartitionedSpan<const ca::HitTypes_t::at<DetID>> hi
     if constexpr (IsTof) {
       iStLocal = fpSetup->GetTrackingStation<ca::ToFlesSubsystem<DetID>()>(extHitAddress);
     }
+    if constexpr (IsTrd) {
+      // TODO: is TRD Layer == station?
+      iStLocal = CbmTrdAddress::GetLayerId(extHitAddress);
+    }
 
-    int iStActive  = (iStLocal != -1) ? fCaFramework.GetParameters().GetStationIndexActive(iStLocal, DetID) : -1;
+    int iStActive = (iStLocal != -1) ? fCaFramework.GetParameters().GetStationIndexActive(iStLocal, DetID) : -1;
     //size_t iOffset = hits.Offsets()[iPartition];
     if (iStActive < 0) {
       continue;  // legit
@@ -309,6 +323,19 @@ void TrackingChain::ReadHits(PartitionedSpan<const ca::HitTypes_t::at<DetID>> hi
       caHit.SetRangeX(3.5 * hit.Dx());
       caHit.SetRangeY(3.5 * hit.Dy());
       caHit.SetRangeT(3.5 * hit.TimeError());
+      if constexpr (IsTrd) {
+        if (iStLocal == 0) {
+          caHit.SetRangeX(1.7);
+          caHit.SetRangeY(3.5);
+          caHit.SetRangeT(200.);
+        }
+        else if (iStLocal == 1) {
+          caHit.SetRangeY(sqrt(3.) * hit.Dy());
+        }
+        else {
+          caHit.SetRangeX(sqrt(3.) * hit.Dx());
+        }
+      }
       //L_(info) << ">>>>>>>>>>>> " << iStActive;
       caHit.SetStation(iStActive);
       caHit.SetId(fCaDataManager.GetNofHits());
diff --git a/algo/ca/TrackingChain.h b/algo/ca/TrackingChain.h
index d6ec739a6e955a2dbbc00bf68d898ec435e9cf65..d9e852a73246fa6749140c4d0bfa19b80e00516f 100644
--- a/algo/ca/TrackingChain.h
+++ b/algo/ca/TrackingChain.h
@@ -46,6 +46,7 @@ namespace cbm::algo
     struct Input_t {
       PartitionedSpan<sts::Hit> stsHits;
       PartitionedSpan<tof::Hit> tofHits;
+      PartitionedSpan<trd::Hit> trdHits;
     };
 
     /// \struct Output_t
@@ -62,6 +63,10 @@ namespace cbm::algo
       /// \note  Indexing: [trackID][localHit], value: (partitionID, hitIDinPartition)
       ca::Vector<std::vector<std::pair<uint32_t, uint32_t>>> tofHitIndices;
 
+      /// \brief TRD hit indices
+      /// \note  Indexing: [trackID][localHit], value: (partitionID, hitIDinPartition)
+      ca::Vector<std::vector<std::pair<uint32_t, uint32_t>>> trdHitIndices;
+
       /// \brief Monitor data
       ca::TrackingMonitorData monitorData;
     };
diff --git a/algo/detectors/trd/Hit.cxx b/algo/detectors/trd/Hit.cxx
index 363bf40f8b165ba34b6cfc37e7e367e03c28c138..c82d5f8b1dbcd8da91614b1526ec97b7ea39953d 100644
--- a/algo/detectors/trd/Hit.cxx
+++ b/algo/detectors/trd/Hit.cxx
@@ -61,9 +61,9 @@ namespace cbm::algo::trd
     return *this;
   }
 
-  void Hit::Position(ROOT::Math::XYZVector& pos) const { pos.SetXYZ(GetX(), GetY(), GetZ()); }
+  void Hit::Position(ROOT::Math::XYZVector& pos) const { pos.SetXYZ(X(), Y(), Z()); }
 
-  void Hit::PositionError(ROOT::Math::XYZVector& dpos) const { dpos.SetXYZ(GetDx(), GetDy(), GetDz()); }
+  void Hit::PositionError(ROOT::Math::XYZVector& dpos) const { dpos.SetXYZ(Dx(), Dy(), Dz()); }
 
   void Hit::SetPosition(const ROOT::Math::XYZVector& pos)
   {
diff --git a/algo/detectors/trd/Hit.h b/algo/detectors/trd/Hit.h
index 2f99d8e6fee80d21c3e3b61be4d7bdf56ad51f4f..844dfde9fd5dda4ff8317681d74e98bd2ea48673 100644
--- a/algo/detectors/trd/Hit.h
+++ b/algo/detectors/trd/Hit.h
@@ -18,6 +18,8 @@
 #include <Rtypes.h>      // for CLRBIT, SETBIT, TESTBIT, ClassDef
 #include <RtypesCore.h>  // for Double32_t
 
+#include <boost/serialization/access.hpp>
+
 #include <cstdint>
 #include <string>  // for string
 
@@ -72,8 +74,8 @@ namespace cbm::algo::trd
 	 * \param[in] dz Z position error of the hit [cm].
 	 * \param[in] dxy X-Y covariance of the hit [cm**2].
 	 * \param[in] refId Some reference ID.
-         * \param[in] time Hit time [ns].   
-         * \param[in] timeError Error of hit time [ns].         
+         * \param[in] time Hit time [ns].
+         * \param[in] timeError Error of hit time [ns].
  	 **/
     Hit(int32_t address, double x, double y, double z, double dx, double dy, double dz, double dxy, int32_t refId,
         double time = -1., double timeError = -1.);
@@ -83,24 +85,24 @@ namespace cbm::algo::trd
     ~Hit(){};
 
     /* Accessors */
-    int32_t GetPlaneId() const { return CbmTrdAddress::GetLayerId(GetAddress()); }
+    int32_t GetPlaneId() const { return CbmTrdAddress::GetLayerId(Address()); }
     double GetELoss() const { return fELoss; }
     bool GetClassType() const { return TESTBIT(fDefine, kType); }
     bool GetMaxType() const { return TESTBIT(fDefine, kMaxType); }
     bool HasOverFlow() const { return TESTBIT(fDefine, kOvfl); }
     bool IsRowCross() const { return TESTBIT(fDefine, kRowCross); }
     bool IsUsed() const { return (GetRefId() < 0); }
-    double GetX() const { return fX; }
-    double GetY() const { return fY; }
-    double GetZ() const { return fZ; }
-    double GetDx() const { return fDx; }
-    double GetDy() const { return fDy; }
-    double GetDz() const { return fDz; }
-    double GetDxy() const { return fDxy; }
+    double X() const { return fX; }
+    double Y() const { return fY; }
+    double Z() const { return fZ; }
+    double Dx() const { return fDx; }
+    double Dy() const { return fDy; }
+    double Dz() const { return fDz; }
+    double Dxy() const { return fDxy; }
     int32_t GetRefId() const { return fRefId; }
-    int32_t GetAddress() const { return fAddress; }
-    double GetTime() const { return fTime; }
-    double GetTimeError() const { return fTimeError; }
+    int32_t Address() const { return fAddress; }
+    double Time() const { return fTime; }
+    double TimeError() const { return fTimeError; }
 
     /**
 	 * \brief Copies hit position to pos.
@@ -166,6 +168,28 @@ namespace cbm::algo::trd
     uint8_t fDefine;      // hit extra info
     int32_t fNeighborId;  // refId in case of row cross clusters
     Double32_t fELoss;    // Energy deposit due to TR + dEdx
+
+   private:
+    friend class boost::serialization::access;
+
+    template<class Archive>
+    void serialize(Archive& ar, unsigned int /*version*/)
+    {
+      ar& fX;
+      ar& fY;
+      ar& fZ;
+      ar& fDx;
+      ar& fDy;
+      ar& fDz;
+      ar& fDxy;
+      ar& fRefId;
+      ar& fAddress;
+      ar& fTime;
+      ar& fTimeError;
+      ar& fDefine;
+      ar& fNeighborId;
+      ar& fELoss;
+    }
   };
 
 
diff --git a/algo/detectors/trd/HitFinder.cxx b/algo/detectors/trd/HitFinder.cxx
index 236dd779a95b357eb02cde7b4ae24d7748422ecb..84a57e6287866e0fd16768ad52bc40665fd6dbc4 100644
--- a/algo/detectors/trd/HitFinder.cxx
+++ b/algo/detectors/trd/HitFinder.cxx
@@ -23,7 +23,7 @@ namespace cbm::algo::trd
     for (int icluster = 0; icluster < nclusters; icluster++) {
       Cluster* cluster = &clusters->at(icluster);
       auto hit         = MakeHit(icluster, cluster, &cluster->GetDigis(), nclusters);
-      if (hit.GetAddress() == -1) continue;
+      if (hit.Address() == -1) continue;
       hits.push_back(hit);
     }
     return hits;
diff --git a/algo/detectors/trd/HitFinder2D.cxx b/algo/detectors/trd/HitFinder2D.cxx
index d3b8a0628f40d34842bbdbc444569e980361184d..0adb74bb54cff675218a961e35d673d2c7e58dd6 100644
--- a/algo/detectors/trd/HitFinder2D.cxx
+++ b/algo/detectors/trd/HitFinder2D.cxx
@@ -30,7 +30,7 @@ namespace cbm::algo::trd
     for (int icluster = 0; icluster < nclusters; icluster++) {
       Cluster2D* cluster = &clusters->at(icluster);
       auto hit           = MakeHit(icluster, cluster, &cluster->GetDigis(), nclusters);
-      if (hit.GetAddress() == -1) continue;
+      if (hit.Address() == -1) continue;
       hits.push_back(hit);
     }
 
@@ -45,15 +45,15 @@ namespace cbm::algo::trd
         if (h1->IsUsed()) continue;
 
         // basic check on Time
-        if (h1->GetTime() < 4000 - h0->GetTime()) continue;  // TODO check with preoper time calibration
+        if (h1->Time() < 4000 - h0->Time()) continue;  // TODO check with preoper time calibration
         // skip next hits for being too far (10us) in the future
-        if (h1->GetTime() > 10000 + h0->GetTime()) break;
+        if (h1->Time() > 10000 + h0->Time()) break;
 
         // basic check on Row
-        if (std::abs(h1->GetY() - h0->GetY()) > Dy) continue;
+        if (std::abs(h1->Y() - h0->Y()) > Dy) continue;
 
         // basic check on Col
-        if (std::abs(h1->GetX() - h0->GetX()) > Dx) continue;
+        if (std::abs(h1->X() - h0->X()) > Dx) continue;
 
         // go to topologic checks
         if (!(a0 = CheckMerge(h0->GetRefId(), h1->GetRefId()))) continue;
@@ -87,7 +87,7 @@ namespace cbm::algo::trd
   {
     /** Check topologic conditions if the 2 clusters (in digi representation) can be merged.
  * The first pair is always from the bottom row
- * return the anode candidate wrt boundary 1, 2, 3 for the first 3 anodes of the upper row; -1, -2, -3 for the bottom row (r0) or 0 if the check fails  
+ * return the anode candidate wrt boundary 1, 2, 3 for the first 3 anodes of the upper row; -1, -2, -3 for the bottom row (r0) or 0 if the check fails
  */
 
     bool on(kFALSE);
@@ -410,8 +410,8 @@ namespace cbm::algo::trd
   int HitFinder2D::ProjectDigis(int cid, int cjd)
   {
     /** Load digis information in working vectors Digis are represented in the normal coordinate system of
-   * (pad width [pw], DAQ time [clk], signal [ADC chs]) with rectangular signals at integer 
-   * positions.  
+   * (pad width [pw], DAQ time [clk], signal [ADC chs]) with rectangular signals at integer
+   * positions.
    */
 
     if (fDigis[cid].empty()) {
@@ -792,8 +792,8 @@ namespace cbm::algo::trd
   //_______________________________________________________________________________
   double HitFactory2D::GetXcorr(double dxIn, int typ, int cls) const
   {
-    /** Give the linear interpolation of SYS correction for current position offset "dx" based 
- * on LUT calculated wrt MC EbyE data. The position offset is expresed in [pw] units 
+    /** Give the linear interpolation of SYS correction for current position offset "dx" based
+ * on LUT calculated wrt MC EbyE data. The position offset is expresed in [pw] units
  * while the output is in [cm]
  */
 
diff --git a/algo/detectors/trd/Hitfind.cxx b/algo/detectors/trd/Hitfind.cxx
index 3a6dfc010141951a3a73665a49733d923f9a03b2..a72229d1eb062ebd6f0bc5f56f8a50ec42071321 100644
--- a/algo/detectors/trd/Hitfind.cxx
+++ b/algo/detectors/trd/Hitfind.cxx
@@ -101,6 +101,8 @@ namespace cbm::algo::trd
   // -----   Execution   -------------------------------------------------------
   Hitfind::resultType Hitfind::operator()(gsl::span<CbmTrdDigi> digiIn)
   {
+    constexpr bool DebugCheckInput = false;
+
     // --- Output data
     resultType result = {};
     auto& hitsOut     = std::get<0>(result);
@@ -114,7 +116,23 @@ namespace cbm::algo::trd
     xpu::push_timer("TrdHitfindModuleSort");
     for (size_t idigi = 0; idigi < digiIn.size(); idigi++) {
       const CbmTrdDigi* digi = &digiIn[idigi];
-      digiBuffer[digi->GetAddressModule()].emplace_back(*digi, idigi);
+      auto addrModule        = digi->GetAddressModule();
+
+      if constexpr (DebugCheckInput) {
+        auto modInfo = std::find_if(fModList.begin(), fModList.end(), [&](auto m) { return m.first == addrModule; });
+        if (modInfo == fModList.end()) {
+          L_(error) << "TRD: Unknown module ID";
+          continue;
+        }
+        bool digiIs2D = digi->IsFASP();
+        if (modInfo->second != digiIs2D) {
+          L_(error) << "TRD: Module + Digi type mismatch: " << modInfo->first << ": " << modInfo->second << " "
+                    << digiIs2D;
+          continue;
+        }
+      }
+
+      digiBuffer[addrModule].emplace_back(*digi, idigi);
     }
     monitor.fSortTime = xpu::pop_timer();
 
@@ -180,6 +198,14 @@ namespace cbm::algo::trd
 
     // Create ouput vector
     hitsOut = PartitionedVector(std::move(hitsFlat), modSizes, modAddresses);
+
+    // Ensure hits are time sorted
+    CBM_PARALLEL_FOR(schedule(dynamic))
+    for (size_t i = 0; i < hitsOut.NPartitions(); i++) {
+      auto part = hitsOut[i];
+      std::sort(part.begin(), part.end(), [](const auto& h0, const auto& h1) { return h0.Time() < h1.Time(); });
+    }
+
     return result;
   }
   // ----------------------------------------------------------------------------
diff --git a/algo/global/Reco.cxx b/algo/global/Reco.cxx
index d1cbf1bf948b5c08885dd93502691f2efae41a27..9f7c0350c1142e5284b3d7569c4138307a7703a8 100644
--- a/algo/global/Reco.cxx
+++ b/algo/global/Reco.cxx
@@ -24,6 +24,7 @@
 #include "tof/HitfinderChain.h"
 #include "tof/Unpack.h"
 #include "tof/config/ReadoutPars.h"
+#include "trd/Hitfind.h"
 #include "trd/Unpack.h"
 #include "trd2d/Unpack.h"
 #include "util/TimingsFormat.h"
@@ -190,6 +191,13 @@ void Reco::Init(const Options& opts)
     fTofHitFinder->Init();
   }
 
+  if (Opts().Has(fles::Subsystem::TRD) && Opts().Has(Step::LocalReco)) {
+    // TODO load setup files!!!
+    auto setup   = yaml::ReadFromFile<trd::HitfindSetup>(opts.ParamsDir() / "TrdHitfinderPar.yaml");
+    auto setup2d = yaml::ReadFromFile<trd::Hitfind2DSetup>(opts.ParamsDir() / "TrdHitfinder2DPar.yaml");
+    fTrdHitfind  = std::make_unique<trd::Hitfind>(setup, setup2d);
+  }
+
   // Tracking
   if (Opts().Has(Step::Tracking)) {
     fTracking = std::make_unique<TrackingChain>(fSender);
@@ -281,11 +289,26 @@ RecoResults Reco::Run(const fles::Timeslice& ts)
       QueueTofRecoMetrics(hitmonitor);
     }
 
+    PartitionedVector<trd::Hit> trdHits;
+    if (fTrdHitfind) {
+      // FIXME: additional copy of digis, figure out how to pass 1d + 2d digis at once to hitfinder
+      const auto& digis1d = digis.fTrd;
+      const auto& digis2d = digis.fTrd2d;
+      PODVector<CbmTrdDigi> allDigis{};
+      allDigis.reserve(digis1d.size() + digis2d.size());
+      std::copy(digis1d.begin(), digis1d.end(), std::back_inserter(allDigis));
+      std::copy(digis2d.begin(), digis2d.end(), std::back_inserter(allDigis));
+      auto trdResults = (*fTrdHitfind)(allDigis);
+      trdHits         = std::move(std::get<0>(trdResults));
+      L_(info) << "TS has " << trdHits.NElements() << " TRD hits.";
+    }
+
     // --- Tracking
     if (Opts().Has(Step::Tracking)) {
       TrackingChain::Input_t input{
         .stsHits = stsHits,
         .tofHits = tofHits,
+        .trdHits = trdHits,
       };
       TrackingChain::Output_t trackingOutput = fTracking->Run(input);
       if (Opts().HasOutput(RecoData::Track)) {
@@ -310,6 +333,7 @@ RecoResults Reco::Run(const fles::Timeslice& ts)
     if (Opts().HasOutput(RecoData::Hit)) {
       results.stsHits = std::move(stsHits);
       results.tofHits = std::move(tofHits);
+      results.trdHits = std::move(trdHits);
     }
   }
   PrintTimings(procMon.time);
diff --git a/algo/global/Reco.h b/algo/global/Reco.h
index 8da3eeefc55b2ac0657f5a72a40e03b6e8c53aca..18be6d475e5106a2930522d776306303607b6cb1 100644
--- a/algo/global/Reco.h
+++ b/algo/global/Reco.h
@@ -59,6 +59,7 @@ namespace cbm::algo
   namespace trd
   {
     class Unpack;
+    class Hitfind;
   }
 
   namespace trd2d
@@ -135,9 +136,8 @@ namespace cbm::algo
 
     // TRD
     std::unique_ptr<trd::Unpack> fTrdUnpack;
-
-    // TRD2D
     std::unique_ptr<trd2d::Unpack> fTrd2dUnpack;
+    std::unique_ptr<trd::Hitfind> fTrdHitfind;
 
     // Eventbuilding
     std::unique_ptr<evbuild::EventbuildChain> fEventBuild;
diff --git a/algo/global/RecoResults.h b/algo/global/RecoResults.h
index f40212ae113249651f4e47bf16a93b6a45b8e655..eb64fa2368569a398c21da0137a4678b13e46490 100644
--- a/algo/global/RecoResults.h
+++ b/algo/global/RecoResults.h
@@ -17,6 +17,7 @@
 #include "sts/Cluster.h"
 #include "sts/Hit.h"
 #include "tof/Hit.h"
+#include "trd/Hit.h"
 
 #include <vector>
 
@@ -39,6 +40,7 @@ namespace cbm::algo
 
     PartitionedSpan<sts::Hit> stsHits;
     PartitionedVector<tof::Hit> tofHits;
+    PartitionedVector<trd::Hit> trdHits;
 
     ca::Vector<ca::Track> tracks;
     ca::Vector<std::vector<std::pair<uint32_t, uint32_t>>> trackStsHitIndices;
diff --git a/algo/global/StorableRecoResults.h b/algo/global/StorableRecoResults.h
index dc44a3acff62ec369f085a116e01d77ab03c7640..962f5b28f5e53b0fdd82131860f242757598c6ad 100644
--- a/algo/global/StorableRecoResults.h
+++ b/algo/global/StorableRecoResults.h
@@ -11,6 +11,7 @@
 #include "sts/Cluster.h"
 #include "sts/Hit.h"
 #include "tof/Hit.h"
+#include "trd/Hit.h"
 
 #include <boost/serialization/access.hpp>
 #include <boost/serialization/utility.hpp>
@@ -76,6 +77,9 @@ namespace cbm::algo
     PartitionedVector<tof::Hit>& TofHits() { return fTofHits; }
     const PartitionedVector<tof::Hit>& TofHits() const { return fTofHits; }
 
+    PartitionedVector<trd::Hit>& TrdHits() { return fTrdHits; }
+    const PartitionedVector<trd::Hit>& TrdHits() const { return fTrdHits; }
+
     ca::Vector<ca::Track>& Tracks() { return fTracks; }
     const ca::Vector<ca::Track>& Tracks() const { return fTracks; }
 
@@ -105,6 +109,7 @@ namespace cbm::algo
     PartitionedVector<sts::Cluster> fStsClusters;
     PartitionedVector<sts::Hit> fStsHits;
     PartitionedVector<tof::Hit> fTofHits;
+    PartitionedVector<trd::Hit> fTrdHits;
 
     // Tracking output
     ca::Vector<ca::Track> fTracks;
@@ -138,6 +143,7 @@ namespace cbm::algo
       ar& fStsClusters;
       ar& fStsHits;
       ar& fTofHits;
+      ar& fTrdHits;
 
       ar& fTracks;
       ar& fTrackStsHitIndices;
diff --git a/algo/test/realdata_test.sh b/algo/test/realdata_test.sh
index 9b462bb291b06a808dbecd660c3e209232e09368..be5b2feeb8e99515bce5214963f09b3ea50266d8 100755
--- a/algo/test/realdata_test.sh
+++ b/algo/test/realdata_test.sh
@@ -87,6 +87,7 @@ ensure_gt_zero "$log" "Triggers: %1, events %2"
 # Check Hits
 ensure_gt_zero "$log" "TS contains %1 TOF Hits"
 ensure_gt_zero "$log" "Timeslice contains %1 STS hits and %2 STS clusters"
+ensure_gt_zero "$log" "TS has %1 TRD hits."
 
 # Check Tracks
-ensure_gt_zero "$log" "TrackingChain: Timeslice contains %1 tracks, with %2 sts hits, %3 tof hits;"
+ensure_gt_zero "$log" "TrackingChain: Timeslice contains %1 tracks, with %2 sts hits, %3 tof hits, 0 trd hits;"
diff --git a/core/data/trd/CbmTrdDigi.h b/core/data/trd/CbmTrdDigi.h
index c8a5fcd2801c9fcf5f26421bac3a34822cad301b..664e0cfcb3c9588d01c89a3374a1f68a3bbd19dd 100644
--- a/core/data/trd/CbmTrdDigi.h
+++ b/core/data/trd/CbmTrdDigi.h
@@ -175,6 +175,9 @@ public:
     return ((fInfo >> fgkTypOffset) & 0x1) ? eCbmTrdAsicType::kFASP : eCbmTrdAsicType::kSPADIC;
   }
 
+  /** \brief Shortcut to check if FASP digi */
+  bool IsFASP() const { return GetType() == eCbmTrdAsicType::kFASP; }
+
   /** \brief Query digi mask (FASP only)*/
   bool IsMasked() const { return (GetType() == eCbmTrdAsicType::kFASP) && IsFlagged(kFlag3); }
   /** \brief Query digi pile-up (FASP only)*/
diff --git a/reco/app/cbmreco/main.cxx b/reco/app/cbmreco/main.cxx
index b078661f1ac7d1ba4d7bd6096e0c0eb7fb1a1160..b6c06bbde93a0fba5371e25f1f00c3f4269c211b 100644
--- a/reco/app/cbmreco/main.cxx
+++ b/reco/app/cbmreco/main.cxx
@@ -43,6 +43,7 @@ std::shared_ptr<StorableRecoResults> makeStorableRecoResults(const fles::Timesli
   storable->StsClusters() = results.stsClusters;
   storable->StsHits()     = results.stsHits;
   storable->TofHits()     = results.tofHits;
+  storable->TrdHits()     = results.trdHits;
 
   storable->Tracks()             = results.tracks;
   storable->TrackStsHitIndices() = results.trackStsHitIndices;
diff --git a/reco/tasks/CbmTaskTrdHitFinder.cxx b/reco/tasks/CbmTaskTrdHitFinder.cxx
index cefdaa6c6732df1b7779421e310aa91fc07a4a9e..3902bbd450419c2c2e31809a20369feb9ffd280d 100644
--- a/reco/tasks/CbmTaskTrdHitFinder.cxx
+++ b/reco/tasks/CbmTaskTrdHitFinder.cxx
@@ -108,12 +108,12 @@ void CbmTaskTrdHitFinder::Exec(Option_t* /*option*/)
 void CbmTaskTrdHitFinder::AddHits(gsl::span<cbm::algo::trd::Hit> hits)
 {
   for (auto& hit : hits) {
-    TVector3 pos(hit.GetX(), hit.GetY(), hit.GetZ());
-    TVector3 dpos(hit.GetDx(), hit.GetDy(), hit.GetDz());
+    TVector3 pos(hit.X(), hit.Y(), hit.Z());
+    TVector3 dpos(hit.Dx(), hit.Dy(), hit.Dz());
 
     CbmTrdHit& hitSave =
-      fHits->emplace_back(hit.GetAddress(), pos, dpos, hit.GetDxy(), hit.GetRefId(), hit.GetELoss(), hit.GetTime(),
-                          hit.GetTimeError());  // TODO implement copy constructor
+      fHits->emplace_back(hit.Address(), pos, dpos, hit.Dxy(), hit.GetRefId(), hit.GetELoss(), hit.Time(),
+                          hit.TimeError());  // TODO implement copy constructor
     hitSave.SetOverFlow(hit.HasOverFlow());
     hitSave.SetRowCross(hit.IsRowCross());
     hitSave.SetClassType(hit.GetClassType());
diff --git a/reco/tasks/CbmTaskTrdHitFinderParWrite.cxx b/reco/tasks/CbmTaskTrdHitFinderParWrite.cxx
index 9d23f6aa9989f0e483e7124ff35168003d5cc69b..901d53871e030d7c9afbf784fbda53ece1987d93 100644
--- a/reco/tasks/CbmTaskTrdHitFinderParWrite.cxx
+++ b/reco/tasks/CbmTaskTrdHitFinderParWrite.cxx
@@ -61,6 +61,8 @@ InitStatus CbmTaskTrdHitFinderParWrite::Init()
                << " have different number of modules.";
   }
 
+  LOG(info) << "Creating TRD + TRD2D parameters";
+
   // Sets of module IDs
   std::vector<uint16_t> trdModules;
   std::vector<uint16_t> trd2DModules;