From dc62953a50000f2f62fd2acb3546d2ff93aebb5c Mon Sep 17 00:00:00 2001
From: "s.zharko@gsi.de" <s.zharko@gsi.de>
Date: Thu, 6 Apr 2023 13:29:36 +0200
Subject: [PATCH] CA: Output QA updates

- track type and track fit histogrammers added
- index bug in TimeSliceReader class fixed
---
 core/qa/CMakeLists.txt           |   6 +-
 core/qa/CbmQaConstants.h         |  23 ++++
 core/qa/CbmQaIO.cxx              |  36 +++++
 core/qa/CbmQaIO.h                | 218 +++++++++++++++++++++++++++++++
 core/qa/CbmQaTask.cxx            |  10 +-
 core/qa/CbmQaTask.h              | 137 +------------------
 macro/run/qa_config.cbm.yaml     |  22 ++++
 macro/run/run_qa.C               |   4 +
 reco/L1/CMakeLists.txt           |   3 +
 reco/L1/CbmCaMCModule.cxx        |  15 ++-
 reco/L1/CbmCaTimeSliceReader.cxx | 170 +++++++++++++-----------
 reco/L1/CbmCaTimeSliceReader.h   |  21 +--
 reco/L1/CbmL1Hit.h               |  19 +++
 reco/L1/CbmL1MCTrack.cxx         |  67 ++++++++++
 reco/L1/CbmL1MCTrack.h           |   4 +
 reco/L1/CbmL1Track.cxx           |  60 +++++++++
 reco/L1/CbmL1Track.h             |  32 ++++-
 reco/L1/L1Algo/L1Constants.h     |  22 +++-
 reco/L1/L1Algo/L1Field.cxx       |   4 +-
 reco/L1/L1Algo/L1Fit.h           |   2 +-
 reco/L1/L1Algo/L1TrackPar.h      |  62 ++++++++-
 reco/L1/catools/CaToolsMCPoint.h | 203 +++++++++++++++++-----------
 reco/L1/qa/CbmCaOutputQa.cxx     | 129 ++++++++++++++----
 reco/L1/qa/CbmCaOutputQa.h       |  67 +++++++++-
 reco/L1/qa/CbmCaTrackFitQa.cxx   | 122 +++++++++++++++++
 reco/L1/qa/CbmCaTrackFitQa.h     | 163 +++++++++++++++++++++++
 reco/L1/qa/CbmCaTrackTypeQa.cxx  | 157 ++++++++++++++++++++++
 reco/L1/qa/CbmCaTrackTypeQa.h    | 209 +++++++++++++++++++++++++++++
 28 files changed, 1637 insertions(+), 350 deletions(-)
 create mode 100644 core/qa/CbmQaConstants.h
 create mode 100644 core/qa/CbmQaIO.cxx
 create mode 100644 core/qa/CbmQaIO.h
 create mode 100644 reco/L1/CbmL1Track.cxx
 create mode 100644 reco/L1/qa/CbmCaTrackFitQa.cxx
 create mode 100644 reco/L1/qa/CbmCaTrackFitQa.h
 create mode 100644 reco/L1/qa/CbmCaTrackTypeQa.cxx
 create mode 100644 reco/L1/qa/CbmCaTrackTypeQa.h

diff --git a/core/qa/CMakeLists.txt b/core/qa/CMakeLists.txt
index f71105c8a6..d4a6f8e18c 100644
--- a/core/qa/CMakeLists.txt
+++ b/core/qa/CMakeLists.txt
@@ -9,8 +9,12 @@ set(SRCS
   CbmQaHist.cxx
   CbmQaTable.cxx
   CbmQaTask.cxx
+  CbmQaIO.cxx
   )
 
+set(HEADERS
+  CbmQaConstants.h
+  )
 
 set(LIBRARY_NAME CbmQaBase)
 set(LINKDEF ${LIBRARY_NAME}LinkDef.h)
@@ -29,6 +33,6 @@ set(INTERFACE_DEPENDENCIES
 
 generate_cbm_library()
 
-Install(FILES CbmQaTask.h CbmQaCanvas.h CbmQaTable.h CbmQaHist.h CbmQaEff.h CbmQaPie.h
+Install(FILES CbmQaTask.h CbmQaCanvas.h CbmQaTable.h CbmQaHist.h CbmQaEff.h CbmQaPie.h CbmQaConstants.h
         DESTINATION include
        )
diff --git a/core/qa/CbmQaConstants.h b/core/qa/CbmQaConstants.h
new file mode 100644
index 0000000000..91f62f272f
--- /dev/null
+++ b/core/qa/CbmQaConstants.h
@@ -0,0 +1,23 @@
+/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergei Zharko [committer] */
+
+/// @file   CbmQaConstants.h
+/// @brief  List of constant expressions, common in QA task
+/// @since  04.04.2023
+/// @author Sergei Zharko <s.zharko@gsi.de>
+
+namespace CbmQaConstants
+{
+
+  // Unit
+  namespace unit
+  {
+    constexpr double cm = 1.;
+    constexpr double m  = cm * 1.e+2;
+    constexpr double mm = cm * 1.e-1;
+    constexpr double um = cm * 1.e-4;
+    constexpr double nm = cm * 1.e-7;
+  }  // namespace unit
+
+}  // namespace CbmQaConstants
\ No newline at end of file
diff --git a/core/qa/CbmQaIO.cxx b/core/qa/CbmQaIO.cxx
new file mode 100644
index 0000000000..da8aac99a5
--- /dev/null
+++ b/core/qa/CbmQaIO.cxx
@@ -0,0 +1,36 @@
+/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergei Zharko [committer] */
+
+/// @file   CbmQaIO.cxx
+/// @brief  Module for ROOT objects IO interface (implementation)
+/// @author S.Zharko <s.zharko@gsi.de>
+/// @since  29.03.2023
+
+#include "CbmQaIO.h"
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+CbmQaIO::CbmQaIO(const char* folderName, const char* prefixName, TFolder* pParentFolder)
+  : fsRootFolderName(folderName)
+  , fsPrefix(prefixName)
+  , fpParentFolder(pParentFolder)
+{
+  if (fpParentFolder) { fpFolderRoot = fpParentFolder->AddFolder(fsRootFolderName, fsRootFolderName); }
+  else {
+    fpFolderRoot =
+      new TFolder(fsRootFolderName, fsRootFolderName);  // The name of the folder follows the name of the task
+    fpFolderRoot->SetOwner(true);                       // When true, TFolder owns all added objects
+  }
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+CbmQaIO::~CbmQaIO()
+{
+  // Free memory for fpFolderRoot
+  if (fpFolderRoot && !fpParentFolder) {
+    delete fpFolderRoot;
+    fpFolderRoot = nullptr;
+  }
+}
diff --git a/core/qa/CbmQaIO.h b/core/qa/CbmQaIO.h
new file mode 100644
index 0000000000..b6ea8acdb5
--- /dev/null
+++ b/core/qa/CbmQaIO.h
@@ -0,0 +1,218 @@
+/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergei Zharko [committer] */
+
+/// @file   CbmQaIO.h
+/// @brief  Module for ROOT objects IO interface (header)
+/// @author S.Zharko <s.zharko@gsi.de>
+/// @since   29.03.2023
+
+#ifndef CbmQaIO_h
+#define CbmQaIO_h 1
+
+#include "CbmQaTable.h"
+
+#include "Logger.h"
+
+#include "TCanvas.h"
+#include "TEfficiency.h"
+#include "TFolder.h"
+#include "TH1.h"
+#include "TH2.h"
+#include "TH3.h"
+#include "TParameter.h"
+#include "TProfile.h"
+#include "TProfile2D.h"
+#include "TProfile3D.h"
+#include "TROOT.h"
+#include "TString.h"
+
+/// @brief  ROOT object IO interface for QA
+///
+/// The class provides interface to write ROOT object into resulted files
+class CbmQaIO {
+public:
+  enum class EStoringMode
+  {
+    kSAMEDIR,  ///< Objects will be stored to root directory
+    kSUBDIR    ///< Objects will be stored to corresponding subdirectory
+  };
+
+  /// @brief Constructor
+  /// @param folderName    Name of the root folder
+  /// @param prefixName    Name of the unique prefix
+  /// @param pParentFolder Pointer to parent folder
+  CbmQaIO(const char* folderName, const char* prefixName, TFolder* pParentFolder = nullptr);
+
+  /// @brief Destructor
+  virtual ~CbmQaIO();
+
+  /// @brief Copy constructor
+  CbmQaIO(const CbmQaIO&) = delete;
+
+  /// @brief Move constructor
+  CbmQaIO(CbmQaIO&&) = delete;
+
+  /// @brief Copy assignment operator
+  CbmQaIO& operator=(const CbmQaIO&) = delete;
+
+  /// @brief Move assignment operator
+  CbmQaIO& operator=(CbmQaIO&&) = delete;
+
+  /// @brief Creates, initializes and registers a canvas
+  /// @tparam  T     Type of the canvas: TCanvas or CbmQaCanvas (\note T should not be a pointer)
+  /// @param   name  Name of canvas
+  /// @param   args  The rest of the arguments, which will be passed to the canvas constructor
+  template<typename T, typename... Args>
+  T* MakeCanvas(const char* name, Args... args);
+
+  /// @brief Creates, initializes and registers an efficiency
+  /// @tparam  T     Type of the canvas: either TProfile, TProfile1D or TEfficiency (\note T should not be a pointer)
+  /// @param   name  Name of canvas
+  /// @param   args  The rest of the arguments, which will be passed to the efficiency constructor
+  template<typename T, typename... Args>
+  T* MakeEfficiency(const char* name, Args... args);
+
+  /// @brief Creates, initializes and registers a histogram
+  /// @tparam  T     Type of the histogram (\note T should not be a pointer)
+  /// @param   name  Name of histogram
+  /// @param   args  The rest of the arguments, which will be passed to the histogram constructor
+  template<typename T, typename... Args>
+  T* MakeHisto(const char* name, Args... args);
+
+  /// @brief Creates, initializes and registers a table
+  /// @param  name  Name of the table
+  /// @param  args  The rest of the arguments, which will be passed to the table constructor
+  template<typename... Args>
+  CbmQaTable* MakeTable(const char* name, Args... args);
+
+protected:
+  TString fsRootFolderName = "";  ///< Name of root folder
+  TString fsPrefix         = "";  ///< Unique prefix for all writeable root
+
+  EStoringMode fStoringMode = EStoringMode::kSUBDIR;  ///< Objects storing mode
+  TFolder* fpFolderRoot     = nullptr;                ///< Root folder to store histograms and canvases
+  TFolder* fpFolderHist     = nullptr;                ///< Folder for raw histograms
+  TFolder* fpFolderCanv     = nullptr;                ///< Folder for canvases
+  TFolder* fpFolderEff      = nullptr;                ///< Folder for efficiencies
+  TFolder* fpFolderTable    = nullptr;                ///< Folder for tables
+  TFolder* fpParentFolder   = nullptr;                ///< Pointer to parent folder
+};
+
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+template<typename T, typename... Args>
+T* CbmQaIO::MakeCanvas(const char* nameBase, Args... args)
+{
+  TString name = fsPrefix + "_" + nameBase;
+  if (gROOT->FindObject(name)) {
+    LOG(warn) << fsRootFolderName << ": A previously created canvas \"" << name << "\" will be deleted";
+    T* pCanv = (T*) gROOT->FindObject(name);
+    delete pCanv;
+  }
+
+  // Create a new canvas
+  T* pCanv = new T(name, args...);
+  pCanv->SetLeftMargin(-1.2);
+  pCanv->SetBottomMargin(-1.2);
+
+
+  // Register canvas in the folder
+  if (fStoringMode == EStoringMode::kSUBDIR) {
+    if (!fpFolderCanv) { fpFolderCanv = fpFolderRoot->AddFolder("canvases", "Canvases"); }
+    fpFolderCanv->Add(pCanv);
+  }
+  else if (fStoringMode == EStoringMode::kSAMEDIR) {
+    fpFolderRoot->Add(pCanv);
+  }
+
+  return pCanv;
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+template<typename T, typename... Args>
+T* CbmQaIO::MakeEfficiency(const char* nameBase, Args... args)
+{
+  TString name = fsPrefix + "_" + nameBase;
+  if (gROOT->FindObject(name)) {
+    LOG(warn) << fsRootFolderName << ": A previously created histogram \"" << name << "\" will be deleted";
+    auto* pEff = (T*) gROOT->FindObject(name);
+    delete pEff;
+  }
+
+  // Create a new efficiency
+  auto* pEff = new T(name, args...);
+
+  // Register efficiency in the folder
+  if (fStoringMode == EStoringMode::kSUBDIR) {
+    if (!fpFolderEff) { fpFolderEff = fpFolderRoot->AddFolder("efficiencies", "Efficiencies"); }
+    fpFolderEff->Add(pEff);
+  }
+  else if (fStoringMode == EStoringMode::kSAMEDIR) {
+    fpFolderRoot->Add(pEff);
+  }
+
+  return pEff;
+}
+
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+template<typename T, typename... Args>
+T* CbmQaIO::MakeHisto(const char* nameBase, Args... args)
+{
+  TString name = fsPrefix + "_" + nameBase;
+  // Check, if the histogram with a given name was already created. If so, delete it
+  if (gROOT->FindObject(name)) {
+    LOG(warn) << fsRootFolderName << ": A previously created histogram \"" << name << "\" will be deleted";
+    T* pHist = (T*) gROOT->FindObject(name);
+    delete pHist;
+  }
+
+  T* pHist = new T(name, args...);
+  pHist->SetStats(kTRUE);
+  pHist->Sumw2();
+
+  // Register histogram in the folder
+  if (fStoringMode == EStoringMode::kSUBDIR) {
+    if (!fpFolderHist) { fpFolderHist = fpFolderRoot->AddFolder("histograms", "Histograms"); }
+    fpFolderHist->Add(pHist);
+  }
+  else if (fStoringMode == EStoringMode::kSAMEDIR) {
+    fpFolderRoot->Add(pHist);
+  }
+
+  return pHist;
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+template<typename... Args>
+CbmQaTable* CbmQaIO::MakeTable(const char* nameBase, Args... args)
+{
+  TString name = fsPrefix + "_" + nameBase;
+  // Check, if the table with a given name was already created. If so, delete it
+  if (gROOT->FindObject(name)) {
+    LOG(warn) << fsRootFolderName << ": A previously created table \"" << name << "\" will be deleted";
+    CbmQaTable* pTable = (CbmQaTable*) gROOT->FindObject(name);
+    delete pTable;
+  }
+
+  // Create a new table
+  CbmQaTable* pTable = new CbmQaTable(name, args...);
+
+  // Register table in folder
+  if (fStoringMode == EStoringMode::kSUBDIR) {
+    if (!fpFolderTable) { fpFolderTable = fpFolderRoot->AddFolder("tables", "Tables"); }
+    fpFolderTable->Add(pTable);
+  }
+  else if (fStoringMode == EStoringMode::kSAMEDIR) {
+    fpFolderRoot->Add(pTable);
+  }
+
+  return pTable;
+}
+
+#endif  // CbmQaIO_h
diff --git a/core/qa/CbmQaTask.cxx b/core/qa/CbmQaTask.cxx
index aed9e84f6c..d01349387c 100644
--- a/core/qa/CbmQaTask.cxx
+++ b/core/qa/CbmQaTask.cxx
@@ -26,9 +26,10 @@ ClassImp(CbmQaTask);
 //
 CbmQaTask::CbmQaTask(const char* name, const char* prefix, int verbose, bool isMCUsed)
   : FairTask(name, verbose)
-  , fsPrefix(prefix)
+  , CbmQaIO(name, prefix)
   , fbUseMC(isMCUsed)
 {
+  fStoringMode = CbmQaIO::EStoringMode::kSUBDIR;  // mode of objects arrangement in the output file
 }
 
 // ---------------------------------------------------------------------------------------------------------------------
@@ -57,7 +58,7 @@ void CbmQaTask::Finish()
   // Write the root folder to sinker
   FairSink* pSink = FairRootManager::Instance()->GetSink();
   LOG_IF(fatal, !pSink) << fName << ": sink file was not found";
-  pSink->WriteObject(fpFolderRoot.get(), nullptr);
+  pSink->WriteObject(fpFolderRoot, nullptr);
 }
 
 // ---------------------------------------------------------------------------------------------------------------------
@@ -91,8 +92,6 @@ InitStatus CbmQaTask::Init()
   res = std::max(res, InitDataBranches());
 
   // ----- Initialize I/O
-  fpFolderRoot = std::make_unique<TFolder>(fName, fName);  // The name of the folder follows the name of the task
-  fpFolderRoot->SetOwner(true);                            // When true, TFolder owns all added objects
   fpFolderRoot->Add(&fNofEvents);
 
   // ----- Initialize histograms
@@ -121,9 +120,6 @@ void CbmQaTask::DeInitBase()
   // De-initialize basic data members
   // ...
 
-  delete fpFolderRoot.get();
-  fpFolderRoot = nullptr;
-
   // De-initialize particular QA-task implementation
   DeInit();
 }
diff --git a/core/qa/CbmQaTask.h b/core/qa/CbmQaTask.h
index 143b7a7abd..7f9e39e71b 100644
--- a/core/qa/CbmQaTask.h
+++ b/core/qa/CbmQaTask.h
@@ -5,11 +5,12 @@
 /// \file   CbmQaTask.h
 /// \brief  A base class for CBM QA task logic
 /// \author S.Zharko <s.zharko@gsi.de>
-/// \data   12.01.2022
+/// \data   12.01.2023
 
 #ifndef CbmQaTask_h
 #define CbmQaTask_h 1
 
+#include "CbmQaIO.h"
 #include "CbmQaTable.h"
 
 #include "FairTask.h"
@@ -36,7 +37,7 @@
 
 /// Class CbmQaTask is to be inherited with a particular QA-task. It provides mechanisms for storage and management
 /// of QA canvases and histograms management
-class CbmQaTask : public FairTask {
+class CbmQaTask : public FairTask, public CbmQaIO {
 public:
   /// Constructor from parameters
   /// \param  name     Name of the task
@@ -108,27 +109,6 @@ protected:
   /// Method to fill histograms per event or time-slice
   virtual void FillHistograms() {}
 
-  /// Creates, initializes and registers a canvas
-  /// \tparam  T     Type of the canvas: TCanvas or CbmQaCanvas (\note T should not be a pointer)
-  /// \param   name  Name of canvas
-  /// \param   args  The rest of the arguments, which will be passed to the canvas constructor
-  template<typename T, typename... Args>
-  T* MakeCanvas(const char* name, Args... args);
-
-  /// Creates, initializes and registers an efficiency
-  /// \tparam  T     Type of the canvas: either TProfile, TProfile2D or TEfficiency (\note T should not be a pointer)
-  /// \param   name  Name of canvas
-  /// \param   args  The rest of the arguments, which will be passed to the efficiency constructor
-  template<typename T, typename... Args>
-  T* MakeEfficiency(const char* name, Args... args);
-
-  /// Creates, initializes and registers a histogram
-  /// \tparam  T     Type of the histogram (\note T should not be a pointer)
-  /// \param   name  Name of histogram
-  /// \param   args  The rest of the arguments, which will be passed to the histogram constructor
-  template<typename T, typename... Args>
-  T* MakeHisto(const char* name, Args... args);
-
   /// @brief Creates, initializes and registers a histogram, based on the configuration file
   /// @tparam T    Type of the histogram (@note: should not be a pointer)
   /// @param  name  Name of the histogram, stored in config
@@ -139,12 +119,6 @@ protected:
   template<typename T>
   T* MakeHistoFromConfig(const char* name, int id0 = -1, int id1 = -1, int id2 = -1);
 
-  /// Creates, initializes and registers a table
-  /// \param  name  Name of the table
-  /// \param  args  The rest of the arguments, which will be passed to the table constructor
-  template<typename... Args>
-  CbmQaTable* MakeTable(const char* name, Args... args);
-
   /// Get current event number
   int GetEventNumber() const { return fNofEvents.GetVal(); }
 
@@ -160,21 +134,14 @@ protected:
   /// \param hi    Upper limit of the variable
   /// \return  False, if variable exits the range
   template<typename T>
-  bool CheckRange(const std::string_view& name, const T& var, const T& lo, const T& hi) const;
+  bool CheckRange(std::string_view name, const T& var, const T& lo, const T& hi) const;
 
 
 private:
   /// @brief De-initializes this task
   void DeInitBase();
 
-  // I/O
-  std::unique_ptr<TFolder> fpFolderRoot = nullptr;  ///< Root folder to store histograms and canvases
-  TFolder* fpFolderHist                 = nullptr;  ///< Folder for raw histograms
-  TFolder* fpFolderCanv                 = nullptr;  ///< Folder for canvases
-  TFolder* fpFolderEff                  = nullptr;  ///< Folder for efficiencies
-  TFolder* fpFolderTable                = nullptr;  ///< Folder for tables
 
-  TString fsPrefix = "";  ///< Unique prefix for all writeable root objects to avoid collisions between different tasks
   bool fbUseMC     = false;  ///< Flag, if MC is used
 
   TParameter<int> fNofEvents {"nEvents", 0};  ///< Number of processed events
@@ -191,7 +158,7 @@ private:
 // ---------------------------------------------------------------------------------------------------------------------
 //
 template<typename T>
-bool CbmQaTask::CheckRange(const std::string_view& name, const T& var, const T& lo, const T& hi) const
+bool CbmQaTask::CheckRange(std::string_view name, const T& var, const T& lo, const T& hi) const
 {
   if (std::clamp(var, lo, hi) == lo) {
     LOG(error) << fName << ": " << name << " is found to be under the lower limit (" << var << " < " << lo << ')';
@@ -204,78 +171,6 @@ bool CbmQaTask::CheckRange(const std::string_view& name, const T& var, const T&
   return true;
 }
 
-// ---------------------------------------------------------------------------------------------------------------------
-//
-template<typename T, typename... Args>
-T* CbmQaTask::MakeCanvas(const char* nameBase, Args... args)
-{
-  TString name = fsPrefix + "_" + nameBase;
-  if (gROOT->FindObject(name)) {
-    LOG(warn) << fName << ": A canvas with name \"" << name << "\" was previously created and will be deleted now "
-              << "to avoid memory leaks";
-    T* pCanv = (T*) gROOT->FindObject(name);
-    delete pCanv;
-  }
-
-  // Create a new canvas
-  T* pCanv = new T(name, args...);
-  pCanv->SetLeftMargin(0.2);
-  pCanv->SetBottomMargin(0.2);
-
-
-  // Register canvas in the folder
-  if (!fpFolderCanv) { fpFolderCanv = fpFolderRoot->AddFolder("canvases", "Canvases"); }
-  fpFolderCanv->Add(pCanv);
-
-  return pCanv;
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-//
-template<typename T, typename... Args>
-T* CbmQaTask::MakeEfficiency(const char* nameBase, Args... args)
-{
-  TString name = fsPrefix + "_" + nameBase;
-  if (gROOT->FindObject(name)) {
-    LOG(warn) << fName << ": An efficiency with name \"" << name << "\" was previously created and will be deleted now "
-              << "to avoid memory leaks";
-    auto* pEff = (T*) gROOT->FindObject(name);
-    delete pEff;
-  }
-
-  // Create a new efficiency
-  auto* pEff = new T(name, args...);
-
-  // Register efficiency in the folder
-  if (!fpFolderEff) { fpFolderEff = fpFolderRoot->AddFolder("efficiencies", "Efficiencies"); }
-  fpFolderEff->Add(pEff);
-
-  return pEff;
-}
-
-
-// ---------------------------------------------------------------------------------------------------------------------
-//
-template<typename T, typename... Args>
-T* CbmQaTask::MakeHisto(const char* nameBase, Args... args)
-{
-  TString name = fsPrefix + "_" + nameBase;
-  // Check, if the histogram with a given name was already created. If so, delete it
-  if (gROOT->FindObject(name)) {
-    LOG(warn) << fName << ": A histogram with name \"" << name << "\" was previously created and will be deleted now "
-              << "to avoid memory leaks";
-    T* pHist = (T*) gROOT->FindObject(name);
-    delete pHist;
-  }
-
-  T* pHist = new T(name, args...);
-
-  // Register histogram in the folder
-  if (!fpFolderHist) { fpFolderHist = fpFolderRoot->AddFolder("histograms", "Histograms"); }
-  fpFolderHist->Add(pHist);
-
-  return pHist;
-}
 
 // ---------------------------------------------------------------------------------------------------------------------
 //
@@ -397,29 +292,7 @@ T* CbmQaTask::MakeHistoFromConfig(const char* nameBase, int id0, int id1, int id
   return pHist;
 }
 
-// ---------------------------------------------------------------------------------------------------------------------
-//
-template<typename... Args>
-CbmQaTable* CbmQaTask::MakeTable(const char* nameBase, Args... args)
-{
-  TString name = fsPrefix + "_" + nameBase;
-  // Check, if the table with a given name was already created. If so, delete it
-  if (gROOT->FindObject(name)) {
-    LOG(warn) << fName << ": A table with name \"" << name << "\" was previously created and will be deleted now "
-              << "to avoid memory leaks";
-    CbmQaTable* pTable = (CbmQaTable*) gROOT->FindObject(name);
-    delete pTable;
-  }
-
-  // Create a new table
-  CbmQaTable* pTable = new CbmQaTable(name, args...);
-
-  // Register table in folder
-  if (!fpFolderTable) { fpFolderTable = fpFolderRoot->AddFolder("tables", "Tables"); }
-  fpFolderTable->Add(pTable);
 
-  return pTable;
-}
 
 
 #endif  // CbmQaTask_h
diff --git a/macro/run/qa_config.cbm.yaml b/macro/run/qa_config.cbm.yaml
index 6d23fa8722..baf8957e05 100644
--- a/macro/run/qa_config.cbm.yaml
+++ b/macro/run/qa_config.cbm.yaml
@@ -208,6 +208,28 @@ qa:
         miny:   -0.5
         maxy:   14.5
       #
+      #  -- Reconstructed tracks vs. MC quantities
+      # 
+      reco_pMC:
+        title:  "MC momentum of reconstructed tracks;p_{MC} [GeV/c]"
+        nbinsx: 150
+        minx:   0.
+        maxx:   15.
+      reco_yMC:
+        title:  "MC momentum of reconstructed tracks;p_{MC} [GeV/c]"
+        nbinsy: 40
+        miny:   -4.
+        maxy:   +4.   
+      reco_pMC_vs_yMC:
+        title:  "MC momentum vs. MC rapidity of reconstructed tracks;p_{MC} [GeV/c]"
+        nbinsx: 150
+        minx:   0.
+        maxx:   15.
+        nbinsy: 40
+        miny:   -4.
+        maxy:   +4.   
+
+      #
       #  -- Track residuals and pulls --
       #
       # Residuals and pulls at first station
diff --git a/macro/run/run_qa.C b/macro/run/run_qa.C
index bb9f108253..296094afc5 100644
--- a/macro/run/run_qa.C
+++ b/macro/run/run_qa.C
@@ -249,6 +249,10 @@ void run_qa(TString dataTra = "data/sis100_muon_jpsi_test", TString dataRaw = "d
   // ------------------------------------------------------------------------
 
   // ----- Tracking QA ------------------------------------------------------
+  // KF is currently needed to access magnetic field. In future we will
+  // delegate track fit routines to CbmKF as well.
+  run->AddTask(new CbmKF());
+
   TString caParFile = recFile;
   caParFile.ReplaceAll(".root", ".L1Parameters.dat");
 
diff --git a/reco/L1/CMakeLists.txt b/reco/L1/CMakeLists.txt
index ddad3866d3..60ed071a1c 100644
--- a/reco/L1/CMakeLists.txt
+++ b/reco/L1/CMakeLists.txt
@@ -54,6 +54,7 @@ set(SRCS
   L1Algo/L1Fit.cxx
   L1Algo/L1MCEvent.cxx
   CbmL1MCTrack.cxx
+  CbmL1Track.cxx
   L1Algo/L1Material.cxx
   L1Algo/L1UMeasurementInfo.cxx
   L1Algo/L1XYMeasurementInfo.cxx
@@ -88,6 +89,8 @@ set(SRCS
   qa/CbmCaInputQaTrd.cxx
   qa/CbmCaInputQaTof.cxx
   qa/CbmCaOutputQa.cxx
+  qa/CbmCaTrackTypeQa.cxx
+  qa/CbmCaTrackFitQa.cxx
   qa/CbmTofInteraction.cxx # Tests
 )
 
diff --git a/reco/L1/CbmCaMCModule.cxx b/reco/L1/CbmCaMCModule.cxx
index 3f56f34819..ba105dc7e5 100644
--- a/reco/L1/CbmCaMCModule.cxx
+++ b/reco/L1/CbmCaMCModule.cxx
@@ -427,7 +427,7 @@ void CbmCaMCModule::MatchRecoAndMCTracks()
 
     // ----- Count number of hits from each MC track belonging to this reconstructed track
     auto& mNofHitsVsMCTrkID = trkRe.hitMap;
-    mNofHitsVsMCTrkID.clear();
+    //mNofHitsVsMCTrkID.clear();
     for (int iH : trkRe.Hits) {
       for (int iP : (*fpvQaHits)[iH].mcPointIds) {
         assert(iP > -1);  // Should never be triggered
@@ -436,7 +436,6 @@ void CbmCaMCModule::MatchRecoAndMCTracks()
         else {
           mNofHitsVsMCTrkID[iTmc] += 1;
         }
-
       }  // loop over global point ids stored for hit: end
     }    // loop over hit ids stored for a reconstructed track: end
 
@@ -470,7 +469,9 @@ void CbmCaMCModule::MatchRecoAndMCTracks()
       // Update max purity of the reconstructed track
       trkRe.SetMaxPurity(maxPurity);
     }  // loop over hit map: end
-  }    // loop over reconstructed tracks: end
+    assert(trkRe.GetNofMCTracks() < 2);
+
+  }  // loop over reconstructed tracks: end
 }
 
 
@@ -695,6 +696,7 @@ void CbmCaMCModule::ReadMCTracks()
     if (!pEvtHeader) {
       LOG(fatal) << "CbmCaMCModule: event header is not found for file " << iFile << " and event " << iEvent;
     }
+    double eventTime = fpMCEventList->GetEventTime(iEvent, iFile);
 
     // ----- Loop over MC tracks
     int nTracks = fpMCTracks->Size(iFile, iEvent);
@@ -712,7 +714,7 @@ void CbmCaMCModule::ReadMCTracks()
       aTrk.SetEventId(iEvent);
       aTrk.SetFileId(iFile);
 
-      aTrk.SetStartT(pExtMCTrk->GetStartT());
+      aTrk.SetStartT(pExtMCTrk->GetStartT() + eventTime);
       aTrk.SetStartX(pExtMCTrk->GetStartX());
       aTrk.SetStartY(pExtMCTrk->GetStartY());
       aTrk.SetStartZ(pExtMCTrk->GetStartZ());
@@ -735,11 +737,12 @@ void CbmCaMCModule::ReadMCTracks()
       // Set index of mother track. We assume, that mother track was recorded into the internal array before
       int extMotherId = pExtMCTrk->GetMotherId();
       if (extMotherId < 0) {
-        // This is a primary track, and it's mother ID equals -1 or -2. This value is taken also as internal mother ID
+        // This is a primary track, and it's mother ID equals -1 or -2. This value is taken also as an internal mother
+        // ID.
         aTrk.SetMotherId(extMotherId);
       }
       else {
-        // This is a secondary track, mother ID should be recalculated for the internal array
+        // This is a secondary track, mother ID should be recalculated for the internal track array.
         int motherId = fpMCData->FindInternalTrackIndex(extMotherId, iEvent, iFile);
         if (motherId == -1) { motherId = -3; }  // Mother is neutral particle, which is rejected
         aTrk.SetMotherId(motherId);
diff --git a/reco/L1/CbmCaTimeSliceReader.cxx b/reco/L1/CbmCaTimeSliceReader.cxx
index 1f55a04df6..63607559f2 100644
--- a/reco/L1/CbmCaTimeSliceReader.cxx
+++ b/reco/L1/CbmCaTimeSliceReader.cxx
@@ -16,6 +16,8 @@
 #include "FairRootManager.h"
 #include "Logger.h"
 
+#include <numeric>
+
 #include "L1Constants.h"
 #include "L1InputData.h"
 #include "L1Parameters.h"
@@ -161,76 +163,6 @@ void TimeSliceReader::InitTimeSlice()
   this->ReadRecoTracks();
 }
 
-// ---------------------------------------------------------------------------------------------------------------------
-//
-void TimeSliceReader::ReadHits()
-{
-  fNofHits     = 0;
-  fNofHitKeys  = 0;
-  fFirstHitKey = 0;
-
-  // Get total number of hits
-  int nHitsTot = 0;
-  if (fbUseMvd) { nHitsTot += fpBrMvdHits->GetEntriesFast(); }
-  if (fbUseSts) { nHitsTot += fpBrStsHits->GetEntriesFast(); }
-  if (fbUseMuch) { nHitsTot += fpBrMuchHits->GetEntriesFast(); }
-  if (fbUseTrd) { nHitsTot += fpBrTrdHits->GetEntriesFast(); }
-  if (fbUseTof) { nHitsTot += fpBrTofHits->GetEntriesFast(); }
-
-  // Resize the containers
-  if (fpvHitIds) {
-    fpvHitIds->clear();
-    fpvHitIds->reserve(nHitsTot);
-  }
-  if (fpvQaHits) {
-    fpvQaHits->clear();
-    fpvQaHits->reserve(nHitsTot);
-  }
-  if (fpIODataManager) {
-    fpIODataManager->ResetInputData();
-    fpIODataManager->ReserveNhits(nHitsTot);
-  }
-
-  std::fill(fvHitFirstIndexDet.begin(), fvHitFirstIndexDet.end(), 0);
-
-  // Save number of hits stored
-  std::array<int, L1Constants::size::kMaxNdetectors> vNofHits = {0};
-
-  // Read hits for different detectors
-  if (fbUseMvd) { vNofHits[int(L1DetectorID::kMvd)] += ReadHitsForDetector<L1DetectorID::kMvd>(fpBrMvdHits); }
-  if (fbUseSts) { vNofHits[int(L1DetectorID::kSts)] += ReadHitsForDetector<L1DetectorID::kSts>(fpBrStsHits); }
-  if (fbUseMuch) { vNofHits[int(L1DetectorID::kMuch)] += ReadHitsForDetector<L1DetectorID::kMuch>(fpBrMuchHits); }
-  if (fbUseTrd) { vNofHits[int(L1DetectorID::kTrd)] += ReadHitsForDetector<L1DetectorID::kTrd>(fpBrTrdHits); }
-  if (fbUseTof) { vNofHits[int(L1DetectorID::kTof)] += ReadHitsForDetector<L1DetectorID::kTof>(fpBrTofHits); }
-
-  // Save first hit index for different detector subsystems
-  for (uint32_t iDet = 0; iDet < vNofHits.size(); ++iDet) {
-    fvHitFirstIndexDet[iDet + 1] = fvHitFirstIndexDet[iDet] + vNofHits[iDet];
-    fNofHits += vNofHits[iDet];
-  }
-
-  // Update maps of ext->int hit indexes
-  // NOTE: fvpHitIds must be initialized, if we want to read tracks from the file
-  if (fpvHitIds) {
-    auto UpdateHitIndexMap = [&](L1Vector<int> & vIds, L1DetectorID detID) constexpr
-    {
-      vIds.reset(vNofHits[int(detID)]);
-      for (int iH = fvHitFirstIndexDet[int(detID)]; iH < fvHitFirstIndexDet[int(detID) + 1]; ++iH) {
-        vIds[(*fpvHitIds)[iH].hitId] = iH;
-      }
-    };
-    if (fbUseMvd) { UpdateHitIndexMap(vHitMvdIds, L1DetectorID::kMvd); }
-    if (fbUseSts) { UpdateHitIndexMap(vHitStsIds, L1DetectorID::kSts); }
-    if (fbUseMuch) { UpdateHitIndexMap(vHitMuchIds, L1DetectorID::kMuch); }
-    if (fbUseTrd) { UpdateHitIndexMap(vHitTrdIds, L1DetectorID::kTrd); }
-    if (fbUseTof) { UpdateHitIndexMap(vHitTofIds, L1DetectorID::kTof); }
-  }
-  // Update number of hit keys in input data object
-  if (fpIODataManager) { fpIODataManager->SetNhitKeys(fNofHitKeys); }
-
-  // Sort debug hits
-  if (fpvQaHits) { this->SortQaHits(); }
-}
 
 // ---------------------------------------------------------------------------------------------------------------------
 //
@@ -263,11 +195,13 @@ void TimeSliceReader::ReadRecoTracks()
         track.Hits.clear();
         track.Hits.reserve(pInputTrack->GetNofHits());
         for (int iH = 0; iH < pInputTrack->GetNofMvdHits(); ++iH) {
-          int iHint = vHitMvdIds[pInputTrack->GetMvdHitIndex(iH)];
-          track.Hits.push_back(iHint);  // !!!!
-        }                               // iH
+          int iHext = pInputTrack->GetMvdHitIndex(iH);
+          int iHint = vHitMvdIds[iHext];
+          track.Hits.push_back(iHint);
+        }  // iH
         for (int iH = 0; iH < pInputTrack->GetNofStsHits(); ++iH) {
-          int iHint = vHitStsIds[pInputTrack->GetStsHitIndex(iH)];
+          int iHext = pInputTrack->GetStsHitIndex(iH);
+          int iHint = vHitStsIds[iHext];
           track.Hits.push_back(iHint);
         }  // iH
       }    // iT
@@ -303,7 +237,8 @@ void TimeSliceReader::SetDetector(L1DetectorID detID, bool flag)
 
 // ---------------------------------------------------------------------------------------------------------------------
 //
-void TimeSliceReader::SortQaHits()
+template<>
+void TimeSliceReader::SortQaHits<true>()
 {
   int nStationsActive = fpParameters->GetNstationsActive();
   L1Vector<CbmL1HitDebugInfo> vNewHits(fpvQaHits->size());
@@ -328,6 +263,90 @@ void TimeSliceReader::SortQaHits()
   std::swap(vNewHits, (*fpvQaHits));
 }
 
+// ---------------------------------------------------------------------------------------------------------------------
+//
+template<>
+void TimeSliceReader::SortQaHits<false>()
+{
+  std::sort(fpvQaHits->begin(), fpvQaHits->end(), [](const CbmL1HitDebugInfo& a, const CbmL1HitDebugInfo& b) {
+    return (a.iStation < b.iStation) || (a.iStation == b.iStation) && (a.y < b.y);
+  });
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+void TimeSliceReader::ReadHits()
+{
+  fNofHits     = 0;
+  fNofHitKeys  = 0;
+  fFirstHitKey = 0;
+
+  // Get total number of hits
+  std::array<int, L1Constants::size::kMaxNdetectors> nHitsDet = {0};
+  if (fbUseMvd) { nHitsDet[int(L1DetectorID::kMvd)] = fpBrMvdHits->GetEntriesFast(); }
+  if (fbUseSts) { nHitsDet[int(L1DetectorID::kSts)] = fpBrStsHits->GetEntriesFast(); }
+  if (fbUseMuch) { nHitsDet[int(L1DetectorID::kMuch)] = fpBrMuchHits->GetEntriesFast(); }
+  if (fbUseTrd) { nHitsDet[int(L1DetectorID::kTrd)] = fpBrTrdHits->GetEntriesFast(); }
+  if (fbUseTof) { nHitsDet[int(L1DetectorID::kTof)] = fpBrTofHits->GetEntriesFast(); }
+
+  int nHitsTot = std::accumulate(nHitsDet.begin(), nHitsDet.end(), 0);
+
+  // Resize the containers
+  if (fpvHitIds) {
+    fpvHitIds->clear();
+    fpvHitIds->reserve(nHitsTot);
+  }
+  if (fpvQaHits) {
+    fpvQaHits->clear();
+    fpvQaHits->reserve(nHitsTot);
+  }
+  if (fpIODataManager) {
+    fpIODataManager->ResetInputData();
+    fpIODataManager->ReserveNhits(nHitsTot);
+  }
+
+  std::fill(fvHitFirstIndexDet.begin(), fvHitFirstIndexDet.end(), 0);
+
+  // Save number of hits stored
+  std::array<int, L1Constants::size::kMaxNdetectors> vNofHits = {0};
+
+  // Read hits for different detectors
+  if (fbUseMvd) { vNofHits[int(L1DetectorID::kMvd)] += ReadHitsForDetector<L1DetectorID::kMvd>(fpBrMvdHits); }
+  if (fbUseSts) { vNofHits[int(L1DetectorID::kSts)] += ReadHitsForDetector<L1DetectorID::kSts>(fpBrStsHits); }
+  if (fbUseMuch) { vNofHits[int(L1DetectorID::kMuch)] += ReadHitsForDetector<L1DetectorID::kMuch>(fpBrMuchHits); }
+  if (fbUseTrd) { vNofHits[int(L1DetectorID::kTrd)] += ReadHitsForDetector<L1DetectorID::kTrd>(fpBrTrdHits); }
+  if (fbUseTof) { vNofHits[int(L1DetectorID::kTof)] += ReadHitsForDetector<L1DetectorID::kTof>(fpBrTofHits); }
+
+  // Save first hit index for different detector subsystems
+  for (uint32_t iDet = 0; iDet < vNofHits.size(); ++iDet) {
+    fvHitFirstIndexDet[iDet + 1] = fvHitFirstIndexDet[iDet] + vNofHits[iDet];
+    fNofHits += vNofHits[iDet];
+  }
+
+  // Update number of hit keys in input data object
+  if (fpIODataManager) { fpIODataManager->SetNhitKeys(fNofHitKeys); }
+
+  // Sort debug hits
+  if (fpvQaHits) { this->SortQaHits<true>(); }  // false - old sort, true - new backet sort
+
+  // Update maps of ext->int hit indexes
+  // NOTE: fvpHitIds must be initialized, if we want to read tracks from the file
+  if (fpvHitIds) {
+    auto UpdateHitIndexMap = [&](L1Vector<int> & vIds, L1DetectorID detID) constexpr
+    {
+      vIds.reset(nHitsDet[int(detID)]);
+      for (int iH = fvHitFirstIndexDet[int(detID)]; iH < fvHitFirstIndexDet[int(detID) + 1]; ++iH) {
+        vIds[(*fpvQaHits)[iH].ExtIndex] = iH;
+      }
+    };
+    if (fbUseMvd) { UpdateHitIndexMap(vHitMvdIds, L1DetectorID::kMvd); }
+    if (fbUseSts) { UpdateHitIndexMap(vHitStsIds, L1DetectorID::kSts); }
+    if (fbUseMuch) { UpdateHitIndexMap(vHitMuchIds, L1DetectorID::kMuch); }
+    if (fbUseTrd) { UpdateHitIndexMap(vHitTrdIds, L1DetectorID::kTrd); }
+    if (fbUseTof) { UpdateHitIndexMap(vHitTofIds, L1DetectorID::kTof); }
+  }
+}
+
 // ---------------------------------------------------------------------------------------------------------------------
 //
 void TimeSliceReader::StoreHitRecord(const HitRecord& hitRecord)
@@ -357,6 +376,7 @@ void TimeSliceReader::StoreHitRecord(const HitRecord& hitRecord)
     CbmL1HitDebugInfo hitDbg;
     hitDbg.Det      = hitRecord.fDet;
     hitDbg.ExtIndex = hitRecord.fExtId;
+    hitDbg.IntIndex = static_cast<int>(fpvQaHits->size());
     hitDbg.iStation = hitRecord.fStaId;
     hitDbg.x        = hitRecord.fX;
     hitDbg.y        = hitRecord.fY;
diff --git a/reco/L1/CbmCaTimeSliceReader.h b/reco/L1/CbmCaTimeSliceReader.h
index a92b7809a8..ac4ffa17b7 100644
--- a/reco/L1/CbmCaTimeSliceReader.h
+++ b/reco/L1/CbmCaTimeSliceReader.h
@@ -28,6 +28,7 @@
 
 #include "TClonesArray.h"
 
+#include "L1Constants.h"
 #include "L1Vector.h"
 
 
@@ -162,6 +163,7 @@ namespace cbm::ca
     int ReadHitsForDetector(const TClonesArray* pBrHits);
 
     /// @brief Sorts QA hit objects by stations
+    template<bool IsNewSortingApproach>
     void SortQaHits();
 
     /// @brief Saves hit to data structures
@@ -170,6 +172,7 @@ namespace cbm::ca
     /// Stores recorded hit information into registered hit containers
     void StoreHitRecord(const HitRecord& hitRecord);
 
+
     // Flags for detector subsystems being used
     bool fbUseMvd     = false;  ///< is MVD used
     bool fbUseSts     = false;  ///< is STS used
@@ -212,6 +215,7 @@ namespace cbm::ca
     std::shared_ptr<L1IODataManager> fpIODataManager = nullptr;  ///< Pointer to input data manager
     std::shared_ptr<L1Parameters> fpParameters       = nullptr;  ///< Pointer to tracking parameters object
 
+
     // Maps of hit indexes: ext -> int
     L1Vector<int> vHitMvdIds {"CbmCaTimeSliceReader::vHitMvdIds"};
     L1Vector<int> vHitStsIds {"CbmCaTimeSliceReader::vHitStsIds"};
@@ -219,6 +223,7 @@ namespace cbm::ca
     L1Vector<int> vHitTrdIds {"CbmCaTimeSliceReader::vHitTrdIds"};
     L1Vector<int> vHitTofIds {"CbmCaTimeSliceReader::vHitTofIds"};
 
+
     // Additional
     ECbmTrackingMode fTrackingMode;  ///< Tracking mode
 
@@ -247,7 +252,7 @@ int cbm::ca::TimeSliceReader::ReadHitsForDetector(const TClonesArray* pBrHits)
 
   fFirstHitKey = fNofHitKeys;
 
-  for (int iH = 0; iH < nHitsTot; ++iH) {
+  for (int iHext = 0; iHext < nHitsTot; ++iHext) {
     HitRecord hitRecord;  // Record of hits information
 
     CbmPixelHit* pPixelHit = nullptr;  // Pointer to hit object
@@ -257,7 +262,7 @@ int cbm::ca::TimeSliceReader::ReadHitsForDetector(const TClonesArray* pBrHits)
 
     // Fill out detector specific data
     if constexpr (L1DetectorID::kMvd == DetID) {
-      CbmMvdHit* pMvdHit = static_cast<CbmMvdHit*>(pBrHits->At(iH));
+      CbmMvdHit* pMvdHit = static_cast<CbmMvdHit*>(pBrHits->At(iHext));
       iStGeom            = fpMvdInterface->GetTrackingStationIndex(pMvdHit->GetStationNr());
       phiF               = fpMvdInterface->GetStripsStereoAngleFront(iStGeom);
       phiB               = fpMvdInterface->GetStripsStereoAngleBack(iStGeom);
@@ -266,7 +271,7 @@ int cbm::ca::TimeSliceReader::ReadHitsForDetector(const TClonesArray* pBrHits)
       hitRecord.fDv      = pMvdHit->GetDy();
     }
     else if constexpr (L1DetectorID::kSts == DetID) {
-      CbmStsHit* pStsHit = static_cast<CbmStsHit*>(pBrHits->At(iH));
+      CbmStsHit* pStsHit = static_cast<CbmStsHit*>(pBrHits->At(iHext));
       iStGeom            = fpStsInterface->GetTrackingStationIndex(pStsHit->GetAddress());
       phiF               = fpStsInterface->GetStripsStereoAngleFront(iStGeom);
       phiB               = fpStsInterface->GetStripsStereoAngleBack(iStGeom);
@@ -277,7 +282,7 @@ int cbm::ca::TimeSliceReader::ReadHitsForDetector(const TClonesArray* pBrHits)
       hitRecord.fDv      = pStsHit->GetDv();
     }
     else if constexpr (L1DetectorID::kMuch == DetID) {
-      CbmMuchPixelHit* pMuchHit = static_cast<CbmMuchPixelHit*>(pBrHits->At(iH));
+      CbmMuchPixelHit* pMuchHit = static_cast<CbmMuchPixelHit*>(pBrHits->At(iHext));
       iStGeom                   = fpMuchInterface->GetTrackingStationIndex(pMuchHit->GetAddress());
       phiF                      = fpMuchInterface->GetStripsStereoAngleFront(iStGeom);
       phiB                      = fpMuchInterface->GetStripsStereoAngleBack(iStGeom);
@@ -286,7 +291,7 @@ int cbm::ca::TimeSliceReader::ReadHitsForDetector(const TClonesArray* pBrHits)
       hitRecord.fDv             = pMuchHit->GetDy();
     }
     else if constexpr (L1DetectorID::kTrd == DetID) {
-      CbmTrdHit* pTrdHit = static_cast<CbmTrdHit*>(pBrHits->At(iH));
+      CbmTrdHit* pTrdHit = static_cast<CbmTrdHit*>(pBrHits->At(iHext));
       iStGeom            = fpTrdInterface->GetTrackingStationIndex(pTrdHit->GetAddress());
       phiF               = fpTrdInterface->GetStripsStereoAngleFront(iStGeom);
       phiB               = fpTrdInterface->GetStripsStereoAngleBack(iStGeom);
@@ -295,7 +300,7 @@ int cbm::ca::TimeSliceReader::ReadHitsForDetector(const TClonesArray* pBrHits)
       hitRecord.fDv      = pTrdHit->GetDy();
     }
     else if constexpr (L1DetectorID::kTof == DetID) {
-      CbmTofHit* pTofHit = static_cast<CbmTofHit*>(pBrHits->At(iH));
+      CbmTofHit* pTofHit = static_cast<CbmTofHit*>(pBrHits->At(iHext));
       iStGeom            = fpTofInterface->GetTrackingStationIndex(pTofHit->GetAddress());
       phiF               = fpTofInterface->GetStripsStereoAngleFront(iStGeom);
       phiB               = fpTofInterface->GetStripsStereoAngleBack(iStGeom);
@@ -328,7 +333,7 @@ int cbm::ca::TimeSliceReader::ReadHitsForDetector(const TClonesArray* pBrHits)
     hitRecord.fDataStream = (static_cast<int64_t>(hitRecord.fDet) << 60) | pPixelHit->GetAddress();
     hitRecord.fU          = hitRecord.fX * cos(phiF) + hitRecord.fY * sin(phiF);
     hitRecord.fV          = hitRecord.fX * cos(phiB) + hitRecord.fY * sin(phiB);
-    hitRecord.fExtId      = iH;
+    hitRecord.fExtId      = iHext;
 
     // Update number of hit keys
     if constexpr (L1DetectorID::kSts == DetID) {
@@ -336,7 +341,7 @@ int cbm::ca::TimeSliceReader::ReadHitsForDetector(const TClonesArray* pBrHits)
       if (fNofHitKeys <= hitRecord.fStripB) { fNofHitKeys += hitRecord.fStripB; }
     }
     else {
-      hitRecord.fStripF = fFirstHitKey + iH;
+      hitRecord.fStripF = fFirstHitKey + iHext;
       hitRecord.fStripB = hitRecord.fStripF;
       if (fNofHitKeys <= hitRecord.fStripF) { fNofHitKeys += hitRecord.fStripF; }
     }
diff --git a/reco/L1/CbmL1Hit.h b/reco/L1/CbmL1Hit.h
index 462548ffce..e6a2901336 100644
--- a/reco/L1/CbmL1Hit.h
+++ b/reco/L1/CbmL1Hit.h
@@ -23,6 +23,22 @@ public:
   CbmL1HitId() = default;
   CbmL1HitId(int det, int index) : detId(det), hitId(index) {};
 
+  /// @brief String representation of class object
+  /// @param header   If true, header will be printed
+  std::string ToString(bool header = false) const
+  {
+    std::stringstream msg;
+    if (header) {
+      msg << std::setw(8) << std::setfill(' ') << "det. id" << ' ';
+      msg << std::setw(8) << std::setfill(' ') << "ext. id";
+    }
+    else {
+      msg << std::setw(8) << std::setfill(' ') << detId << ' ';
+      msg << std::setw(8) << std::setfill(' ') << hitId;
+    }
+    return msg.str();
+  }
+
   int detId = -1;  ///< detector ID
   int hitId = -1;  ///< index of hit in the TClonesArray array
 };
@@ -76,6 +92,7 @@ public:
     std::stringstream msg;
     if (header) {
       msg << setw(8) << setfill(' ') << "ext. ID" << ' ';
+      msg << setw(8) << setfill(' ') << "int. ID" << ' ';
       msg << setw(8) << setfill(' ') << "st. ID" << ' ';
       msg << setw(12) << setfill(' ') << "x [cm]" << ' ';
       msg << setw(12) << setfill(' ') << "y [cm]" << ' ';
@@ -84,6 +101,7 @@ public:
     }
     else {
       msg << setw(8) << setfill(' ') << ExtIndex << ' ';
+      msg << setw(8) << setfill(' ') << IntIndex << ' ';
       msg << setw(8) << setfill(' ') << iStation << ' ';
       msg << setw(12) << setfill(' ') << x << ' ';
       msg << setw(12) << setfill(' ') << y << ' ';
@@ -101,6 +119,7 @@ public:
 
   // TODO: SZh 2.03.2023: make the variables private
   int ExtIndex;                                                ///< index of hit in the external branch
+  int IntIndex;                                                ///< index of hit in the internal array
   int iStation;                                                ///< index of station in active stations array
   double x;                                                    ///< x coordinate of position [cm]
   double y;                                                    ///< y coordinate of position [cm]
diff --git a/reco/L1/CbmL1MCTrack.cxx b/reco/L1/CbmL1MCTrack.cxx
index e3330b8a10..cdf746c11e 100644
--- a/reco/L1/CbmL1MCTrack.cxx
+++ b/reco/L1/CbmL1MCTrack.cxx
@@ -23,6 +23,9 @@
 #include "CbmL1.h"
 #include "CbmL1Constants.h"
 
+#include <iomanip>
+#include <sstream>
+
 #include "L1Algo/L1Algo.h"
 #include "L1Algo/L1Hit.h"
 
@@ -193,3 +196,67 @@ void CbmL1MCTrack::CalculateIsReconstructable()
   else
     isAdditional = 0;
 };  // bool CbmL1MCTrack::IsReconstructable()
+
+std::string CbmL1MCTrack::ToString(int verbose, bool header) const
+{
+  using std::setfill;
+  using std::setw;
+  std::stringstream msg;
+  if (header) {
+    if (verbose > 0) {
+      msg << setw(8) << setfill(' ') << "ID" << ' ';
+      msg << setw(8) << setfill(' ') << "Mother" << ' ';
+      msg << setw(8) << setfill(' ') << "PDG" << ' ';
+      if (verbose > 1) {
+        msg << setw(8) << setfill(' ') << "N h." << ' ';
+        msg << setw(8) << setfill(' ') << "N p." << ' ';
+        msg << setw(8) << setfill(' ') << "N r.tr." << ' ';
+        if (verbose > 2) { msg << setw(8) << setfill(' ') << "N t.tr." << ' '; }
+        msg << setw(12) << setfill(' ') << "zVTX [cm]" << ' ';
+        msg << setw(12) << setfill(' ') << "t [ns]" << ' ';
+        msg << setw(12) << setfill(' ') << "p [GeV/c]" << ' ';
+      }
+      msg << setw(8) << setfill(' ') << "rec-able" << ' ';
+      msg << setw(8) << setfill(' ') << "rec-ed" << ' ';
+    }
+  }
+  else {
+    if (verbose > 0) {
+      msg << setw(8) << setfill(' ') << ID << ' ';
+      msg << setw(8) << setfill(' ') << mother_ID << ' ';
+      msg << setw(8) << setfill(' ') << pdg << ' ';
+      if (verbose > 1) {
+        msg << setw(8) << setfill(' ') << Hits.size() << ' ';
+        msg << setw(8) << setfill(' ') << Points.size() << ' ';
+        msg << setw(8) << setfill(' ') << rTracks.size() << ' ';
+        if (verbose > 2) { msg << setw(8) << setfill(' ') << tTracks.size() << ' '; }
+        msg << setw(12) << setfill(' ') << z << ' ';
+        msg << setw(12) << setfill(' ') << time << ' ';
+        msg << setw(12) << setfill(' ') << p << ' ';
+      }
+      msg << setw(8) << setfill(' ') << IsReconstructable() << ' ';
+      msg << setw(8) << setfill(' ') << IsReconstructed() << ' ';
+      if (verbose > 1) {
+        msg << "\n\t- point indexes: ";
+        for (int index : Points) {
+          msg << index << ' ';
+        }
+        msg << "\n\t- hit indexes: ";
+        for (int index : Hits) {
+          msg << index << ' ';
+        }
+        msg << "\n\t- reconstructed track indexes: ";
+        for (auto* index : rTracks) {
+          msg << index << ' ';
+        }
+        if (verbose > 2) {
+          msg << "\n\t- touch track indexes: ";
+          for (auto* index : tTracks) {
+            msg << index << ' ';
+          }
+        }
+      }
+    }
+  }
+  return msg.str();
+}
diff --git a/reco/L1/CbmL1MCTrack.h b/reco/L1/CbmL1MCTrack.h
index 530fa73d2f..b7393a6eed 100644
--- a/reco/L1/CbmL1MCTrack.h
+++ b/reco/L1/CbmL1MCTrack.h
@@ -27,6 +27,7 @@
 #include "TVector3.h"
 
 #include <iostream>
+#include <string>
 
 #include "L1Vector.h"
 
@@ -70,6 +71,9 @@ public:
 
   double pt() { return sqrt(px * px + py * py); }
 
+  /// @brief String representation of the contents
+  std::string ToString(int verbose = 10, bool header = false) const;
+
 private:
   void CalculateMCCont();
   void CountHitStations();
diff --git a/reco/L1/CbmL1Track.cxx b/reco/L1/CbmL1Track.cxx
new file mode 100644
index 0000000000..271fd8649b
--- /dev/null
+++ b/reco/L1/CbmL1Track.cxx
@@ -0,0 +1,60 @@
+/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergei Zharko [committer] */
+
+/// @file   CbmL1Track.cxx
+/// @brief  Ca Tracking track representation in CBM (implementation)
+/// @since  04.04.2023
+/// @author Sergei Zharko <s.zharko@gsi.de>
+
+#include "CbmL1Track.h"
+
+#include <sstream>
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+std::string CbmL1Track::ToString(int verbose, bool header) const
+{
+  using std::setfill;
+  using std::setw;
+  std::stringstream msg;
+  if (header) {
+    msg << setw(12) << setfill(' ') << "p [GeV/c]" << ' ';
+    msg << setw(12) << setfill(' ') << "Tx" << ' ';
+    msg << setw(8) << setfill(' ') << "N hits" << ' ';
+    msg << setw(8) << setfill(' ') << "N MC tracks" << ' ';
+    msg << setw(12) << setfill(' ') << "MC tr (old)" << ' ';
+    msg << setw(12) << setfill(' ') << "MC tr (new)" << ' ';
+    msg << setw(8) << setfill(' ') << "IsGhost" << ' ';
+    msg << setw(8) << setfill(' ') << "Max.pur." << ' ';
+    msg << '\n';
+  }
+  else {
+
+
+    msg << setw(12) << setfill(' ') << GetP() << ' ';
+    msg << setw(12) << setfill(' ') << GetTx() << ' ';
+    msg << setw(8) << setfill(' ') << GetNofHits() << ' ';
+    msg << setw(8) << setfill(' ') << GetNofMCTracks() << ' ';
+    msg << setw(12) << setfill(' ') << GetMatchedMCTrackIndex() << ' ';
+    msg << setw(12) << setfill(' ') << (mcTracks.size() ? mcTracks[0]->ID : -1) << ' ';
+    msg << setw(8) << setfill(' ') << IsGhost() << ' ';
+    msg << setw(8) << setfill(' ') << GetMaxPurity() << ' ';
+    msg << '\n';
+    if (verbose > 0) {
+      msg << "\thits: ";
+      for (int iH : GetHitIndexes()) {
+        msg << iH << ' ';
+      }
+      msg << "\n\tmc tracks: (NEW)";
+      for (int iT : GetMCTrackIndexes()) {
+        msg << iT << ' ';
+      }
+      msg << "\n\tmc tracks: (Old)";
+      for (auto* iT : mcTracks) {
+        msg << iT->ID << ' ';
+      }
+    }
+  }
+  return msg.str();
+}
diff --git a/reco/L1/CbmL1Track.h b/reco/L1/CbmL1Track.h
index 6ea035c7ed..d89fd5cedc 100644
--- a/reco/L1/CbmL1Track.h
+++ b/reco/L1/CbmL1Track.h
@@ -28,7 +28,9 @@
 
 #include "TMath.h"
 
+#include <iterator>
 #include <map>
+#include <string>
 #include <vector>
 using std::map;
 using std::vector;
@@ -52,13 +54,24 @@ public:
     fvMcTrackIndexes.clear();
   }
 
+  int GetNMCTracks() { return mcTracks.size(); }
+
+  bool IsGhost() const { return (maxPurity < CbmL1Constants::MinPurity); }
+
+  /// Gets charge
+  int GetCharge() const { return (T[4] > 0) ? 1 : -1; }
+
   /// Gets Chi-square of track fit model
   double GetChiSq() const { return chi2; }
 
-  /// Gets a reference to hit indexes array
-  const vector<int>& GetHitIndexes() const { return Hits; }
+  /// @brief Gets first hit index
+  int GetFirstHitIndex() const { return Hits.front(); }
 
-  bool IsGhost() const { return (maxPurity < CbmL1Constants::MinPurity); }
+  /// @brief Gets last hit index
+  int GetLastHitIndex() const { return Hits.back(); }
+
+  /// Gets hit indexes
+  const auto& GetHitIndexes() const { return Hits; }
 
   /// Gets NDF of track fit model
   int GetNDF() const { return NDF; }
@@ -80,6 +93,11 @@ public:
   /// Gets pointer of the associated MC track
   CbmL1MCTrack* GetMCTrack() { return mcTracks[0]; }
 
+  /// @brief Gets index of matched MC track
+  /// @note  If two tracks are matched (should not happen, if purity > 50%), the first
+  ///
+  int GetMatchedMCTrackIndex() const { return fvMcTrackIndexes.size() ? fvMcTrackIndexes[0] : -1; }
+
   /// Gets number of hits of the track
   int GetNofHits() const { return Hits.size(); }
 
@@ -90,6 +108,7 @@ public:
   int GetNofStations() const { return nStations; }
 
   /// Gets absolute value of momentum divided by the charge (absolute value) of particle [GeV/ec]
+  // NOTE: 1.e-10 precision is used in the old performance, but in FairTrackParam the 1.e-4 is used
   double GetP() const { return fabs(T[4]) > 1.e-10 ? 1. / fabs(T[4]) : 1.e-10; }
 
   /// Gets transverse momentum
@@ -110,6 +129,9 @@ public:
   /// Gets z-component of momentum divided by the charge (absolute value) of particle [GeV/ec]
   double GetPz() const { return std::sqrt(GetP() * GetP() / (GetTx() * GetTx() + GetTy() * GetTy() + 1)); }
 
+  /// Gets track polar angle
+  double GetTheta() const { return std::acos(1. / std::sqrt(GetTx() * GetTx() + GetTy() * GetTy() + 1)); }
+
   /// Gets track slope along x-axis
   double GetTx() const { return T[2]; }
 
@@ -126,6 +148,10 @@ public:
 
   static bool comparePChi2(const CbmL1Track* a, const CbmL1Track* b) { return (a->chi2 < b->chi2); }
 
+  /// @brief Provides a string representation of object
+  /// @param verbose  Verbosity level
+  /// @param header   If true, header will be printed
+  std::string ToString(int verbose = 10, bool header = false) const;
 
   double Tpv[L1TrackPar::kNparTr];   ///< Track parameters at primary vertex
   double Cpv[L1TrackPar::kNparCov];  ///< Track covariance matrix at primary vertex
diff --git a/reco/L1/L1Algo/L1Constants.h b/reco/L1/L1Algo/L1Constants.h
index af74194f3a..08047172e5 100644
--- a/reco/L1/L1Algo/L1Constants.h
+++ b/reco/L1/L1Algo/L1Constants.h
@@ -41,6 +41,8 @@ namespace L1Constants
     constexpr int kMaxNthreads   = 1u << kThreadBits;   ///< Max number of threads, 2^6  = 64
     constexpr int kMaxNtriplets  = 1u << kTripletBits;  ///< Max number of triplets, 2^20 = 1,048,576
 
+    constexpr uint8_t kDetBits = 4u;  // Max 16 detectors
+
     /// Max number of track groups
     /// NOTE: For a "track group" definition see L1Parameters.h, GetSearchWindow function
     constexpr int kMaxNtrackGroups = 4;
@@ -70,28 +72,36 @@ namespace L1Constants
 
   }  // namespace control
 
-  /// Physics constants
+  // Physics constants
   namespace phys
   {
     /* Particle masses used for track fit */
-    constexpr float kMuonMass     = 0.10565800f;  ///< Muon mass     [GeV/c2]
-    constexpr float kElectronMass = 0.000511f;    ///< Electron mass [GeV/c2]
+    constexpr float kMuonMass      = 0.10565800f;  ///< Muon mass     [GeV/c2]  !! TODO: discrepancy in last two digits
+    constexpr float kPionMass      = 0.13957039f;  ///< Pion mass     [GeV/c2]
+    constexpr float kElectronMass  = 0.000511f;    ///< Electron mass [GeV/c2]
+    constexpr float kProtonMass    = 0.93827209f;  ///< Proton mass [GeV/c2] (0.93827208816 - PDG 11.08.2022)
     constexpr double kSpeedOfLight = 29.9792458;   ///< Speed of light [cm/ns]
-  }                                               // namespace phys
+    constexpr float kSpeedOfLightF = 29.9792;      ///< Speed of light [cm/ns] (single precision)
+    constexpr double kSpeedOfLightInv = 1 / kSpeedOfLight;   ///< Inverse speed of light [cm/ns]
+    constexpr float kSpeedOfLightInvF = 1 / kSpeedOfLightF;  ///< Inverse speed of light [cm/ns] (single precision)
+  }                                                          // namespace phys
 
-  /// Math constants
+  // Math constants
   namespace math
   {
     constexpr double kPi = 3.14159265358979323846;  ///< Value of PI, used in ROOT TMath
   }
 
-  /// Miscellaneous constants
+  // Miscellaneous constants
   namespace misc
   {
     constexpr int kAssertionLevel = 0;   ///< Assertion level
     constexpr int kAlignment      = 16;  ///< Default alignment of data (bytes)
   }                                      // namespace misc
 
+  // Units
+
+
   // Colors of terminal log
   namespace clrs
   {
diff --git a/reco/L1/L1Algo/L1Field.cxx b/reco/L1/L1Algo/L1Field.cxx
index c91cf325b3..2dbaba5840 100644
--- a/reco/L1/L1Algo/L1Field.cxx
+++ b/reco/L1/L1Algo/L1Field.cxx
@@ -182,7 +182,9 @@ void L1FieldRegion::Get(const fvec& x, const fvec& y, const fvec& z, fvec* B) co
     for (size_t i = 0; i < fvec::size(); i++) {
       double inPos[3] = {x[i], y[i], z[i]};
       double outB[3];
-      CbmKF::Instance()->GetMagneticField()->GetFieldValue(inPos, outB);
+      assert(CbmKF::Instance());
+      assert(CbmKF::Instance()->GetMagneticField());
+      CbmKF::Instance()->GetMagneticField()->GetFieldValue(inPos, outB);  // TODO: replace with functional object
       B[0][i] = outB[0];
       B[1][i] = outB[1];
       B[2][i] = outB[2];
diff --git a/reco/L1/L1Algo/L1Fit.h b/reco/L1/L1Algo/L1Fit.h
index 458d9d576a..2be29385f5 100644
--- a/reco/L1/L1Algo/L1Fit.h
+++ b/reco/L1/L1Algo/L1Fit.h
@@ -41,7 +41,7 @@ public:
   }
 
   template<typename T>
-  void SetTrack(const T p[7], const T c[28])
+  void SetTrack(const T p[8], const T c[28])
   {
     fTr.copyFromArrays(p, c);
     fQp0 = fTr.qp;
diff --git a/reco/L1/L1Algo/L1TrackPar.h b/reco/L1/L1Algo/L1TrackPar.h
index 6fe8491644..a8a118b67a 100644
--- a/reco/L1/L1Algo/L1TrackPar.h
+++ b/reco/L1/L1Algo/L1TrackPar.h
@@ -1,12 +1,13 @@
-/* Copyright (C) 2007-2020 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+/* Copyright (C) 2007-2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
    SPDX-License-Identifier: GPL-3.0-only
-   Authors: Igor Kulakov [committer], Maksym Zyzak */
+   Authors: Igor Kulakov [committer], Maksym Zyzak, Sergei Zharko */
 
 #ifndef L1TrackPar_h
 #define L1TrackPar_h 1
 
 #include "CaSimd.h"
 #include "L1Def.h"
+#include "L1Undef.h"
 
 using namespace cbm::algo::ca;
 
@@ -41,7 +42,8 @@ public:
   template<typename T>
   L1TrackPar(const T* tr, const T* C)
   {
-    Set(tr, C);
+    //Set(tr, C); // Not implemented
+    copyFromArrays(tr, C);
   }
 
   //template<typename T>
@@ -63,8 +65,62 @@ public:
     return c[ind];
   }
 
+  // ** Parameter getters **
+
+  /// @brief Gets x position [cm]
+  fscal GetX() const { return x[0]; }
+
+  /// @brief Gets x position error [cm]
+  fscal GetXErr() const { return (std::isfinite(C00[0]) && C00[0] > 0) ? std::sqrt(C00[0]) : undef::kF; }
+
+  /// @brief Gets y position [cm]
+  fscal GetY() const { return y[0]; }
+
+  /// @brief Gets y position error [cm]
+  fscal GetYErr() const { return (std::isfinite(C11[0]) && C11[0] > 0) ? std::sqrt(C11[0]) : undef::kF; }
+
+  /// @brief Gets slope along x-axis
+  fscal GetTx() const { return tx[0]; }
+
+  /// @brief Gets error of slope along x-axis
+  fscal GetTxErr() const { return (std::isfinite(C22[0]) && C22[0] > 0) ? std::sqrt(C22[0]) : undef::kF; }
+
+  /// @brief Gets slope along y-axis
+  fscal GetTy() const { return ty[0]; }
+
+  /// @brief Gets error of slope along y-axis
+  fscal GetTyErr() const { return (std::isfinite(C33[0]) && C33[0] > 0) ? std::sqrt(C33[0]) : undef::kF; }
+
+  /// @brief Gets charge over momentum [ec/GeV]
+  fscal GetQp() const { return qp[0]; }
+
+  /// @brief Gets error of charge over momentum [ec/GeV]
+  fscal GetQpErr() const { return (std::isfinite(C44[0]) && C44[0] > 0) ? std::sqrt(C44[0]) : undef::kF; }
+
+  /// @brief Gets time [ns]
+  fscal GetTime() const { return t[0]; }
+
+  /// @brief Gets time error [ns]
+  fscal GetTimeErr() const { return (std::isfinite(C55[0]) && C55[0] > 0) ? std::sqrt(C55[0]) : undef::kF; }
+
+  /// @brief Gets inverse speed [ns/cm]
+  fscal GetInvSpeed() const { return vi[0]; }
+
+  /// @brief Gets inverse speed error [ns/cm]
+  fscal GetInvSpeedErr() const { return (std::isfinite(C66[0]) && C66[0] > 0) ? std::sqrt(C66[0]) : undef::kF; }
+
+  /// @brief Resets variances of track parameters
+  /// @param c00  Variance of x-position [cm2]
+  /// @param c11  Variance of y-position [cm2]
+  /// @param c22  Variance of slope along x-axis
+  /// @param c33  Variance of slope along y-axis
+  /// @param c44  Variance of charge over momentum [(ec/GeV)2]
+  /// @param c55  Variance of time [ns2]
+  /// @param c66  Variance of inverse speed [1/c2]
   void ResetErrors(fvec c00, fvec c11, fvec c22, fvec c33, fvec c44, fvec c55, fvec c66);
 
+  /// @brief Prints parameters
+  /// @param i  Index of SIMD vector element (if -1, the whole vector is printed out)
   void Print(int i = -1) const;
 
   void PrintCorrelations(int i = -1) const;
diff --git a/reco/L1/catools/CaToolsMCPoint.h b/reco/L1/catools/CaToolsMCPoint.h
index 83aec7054b..d1476138d4 100644
--- a/reco/L1/catools/CaToolsMCPoint.h
+++ b/reco/L1/catools/CaToolsMCPoint.h
@@ -13,38 +13,39 @@
 #include <string>
 
 #include "CaToolsLinkKey.h"
+#include "L1Constants.h"
 #include "L1Undef.h"
 #include "L1Vector.h"
 
-
 enum class L1DetectorID;
 
-/// Class describes a Monte-Carlo point used in CA tracking QA analysis
 namespace ca::tools
 {
+  /// @brief Class describes a unified MC-point, used in CA tracking QA analysis
+  ///
   class MCPoint {
   public:
-    /// Default constructor
+    /// @brief Default constructor
     MCPoint() = default;
 
-    /// Destructor
+    /// @brief Destructor
     ~MCPoint() = default;
 
-    /// Copy constructor
+    /// @brief Copy constructor
     MCPoint(const MCPoint&) = default;
 
-    /// Move constructor
+    /// @brief Move constructor
     MCPoint(MCPoint&&) = default;
 
-    /// Copy assignment operator
+    /// @brief Copy assignment operator
     MCPoint& operator=(const MCPoint&) = default;
 
-    /// Move assignment operator
+    /// @brief Move assignment operator
     MCPoint& operator=(MCPoint&&) = default;
 
 
-    /// Adds index of hits from the container of hits of event/TS
-    /// \param  iH  A hit index in the external hits container of event/TS
+    /// @brief Adds index of hits from the container of hits of event/TS
+    /// @param  iH  A hit index in the external hits container of event/TS
     void AddHitID(int iH) { fvHitIndexes.push_back_no_warning(iH); }
 
 
@@ -52,211 +53,257 @@ namespace ca::tools
     // **     Getters     **
     // *********************
 
-    /// Gets charge of the particle [e]
+    /// @brief Gets charge of the particle [e]
     double GetCharge() const { return fCharge; }
 
-    /// Gets detector ID
+    /// @brief Gets detector ID
     L1DetectorID GetDetectorId() const { return fDetectorId; }
 
-    /// Gets MC event ID
+    /// @brief Gets MC event ID
     int GetEventId() const { return fLinkKey.fEvent; }
 
-    /// Gets MC file ID
+    /// @brief Gets MC file ID
     int GetFileId() const { return fLinkKey.fFile; }
 
-    /// Gets index of this point in internal CA container
+    /// @brief Gets index of this point in internal CA container
     int GetId() const { return fId; }
 
-    /// Gets container of matched hit indexes
+    /// @brief Gets inverse speed at reference z of station [ns/cm]
+    int GetInvSpeed() const
+    {
+      return std::sqrt(1. + GetMass() * GetMass() / GetP() / GetP()) * L1Constants::phys::kSpeedOfLightInv;
+    }
+
+    /// @brief Gets inverse speed at entrance to station [ns/cm]
+    int GetInvSpeedIn() const
+    {
+      return std::sqrt(1. + GetMass() * GetMass() / GetPIn() / GetPIn()) * L1Constants::phys::kSpeedOfLightInv;
+    }
+
+    /// @brief Gets inverse speed at exit of station [ns/cm]
+    int GetInvSpeedOut() const
+    {
+      return std::sqrt(1. + GetMass() * GetMass() / GetPOut() / GetPOut()) * L1Constants::phys::kSpeedOfLightInv;
+    }
+
+    /// @brief Gets container of matched hit indexes
     const auto& GetHitIndexes() const { return fvHitIndexes; }
 
-    /// Gets link key
+    /// @brief Gets link key
     LinkKey GetLinkKey() const { return fLinkKey; }
 
-    /// Gets mass of the particle [GeV/c2]
+    /// @brief Gets mass of the particle [GeV/c2]
     double GetMass() const { return fMass; }
 
-    /// Gets mother ID of the track
+    /// @brief Gets mother ID of the track
     int GetMotherId() const { return fMotherId; }
 
-    /// Gets track momentum absolute value at reference z of station [GeV/c]
+    /// @brief Gets track momentum absolute value at reference z of station [GeV/c]
     double GetP() const { return std::sqrt(fMom[0] * fMom[0] + fMom[1] * fMom[1] + fMom[2] * fMom[2]); }
 
-    /// Gets PDG code of the particle
+    /// @brief Gets PDG code of the particle
     int GetPdgCode() const { return fPdgCode; }
 
-    /// Gets track momentum absolute value at entrance to station [GeV/c]
+    /// @brief Gets track momentum absolute value at entrance to station [GeV/c]
     double GetPIn() const { return std::sqrt(fMomIn[0] * fMomIn[0] + fMomIn[1] * fMomIn[1] + fMomIn[2] * fMomIn[2]); }
 
-    /// Gets track momentum absolute value at exit of station [GeV/c]
+    /// @brief Gets track momentum absolute value at exit of station [GeV/c]
     double GetPOut() const
     {
       return std::sqrt(fMomOut[0] * fMomOut[0] + fMomOut[1] * fMomOut[1] + fMomOut[2] * fMomOut[2]);
     }
 
-    /// Gets track momentum x component at reference z of station [GeV/c]
+    /// @brief Gets track momentum x component at reference z of station [GeV/c]
     double GetPx() const { return fMom[0]; }
 
-    /// Gets track momentum x component at entrance to station [GeV/c]
+    /// @brief Gets track momentum x component at entrance to station [GeV/c]
     double GetPxIn() const { return fMomIn[0]; }
 
-    /// Gets track momentum x component at exit of station [GeV/c]
+    /// @brief Gets track momentum x component at exit of station [GeV/c]
     double GetPxOut() const { return fMomOut[0]; }
 
-    /// Gets track momentum y component at reference z of station [GeV/c]
+    /// @brief Gets track momentum y component at reference z of station [GeV/c]
     double GetPy() const { return fMom[1]; }
 
-    /// Gets track momentum y component at entrance to station [GeV/c]
+    /// @brief Gets track momentum y component at entrance to station [GeV/c]
     double GetPyIn() const { return fMomIn[1]; }
 
-    /// Gets track momentum y component at exit of station [GeV/c]
+    /// @brief Gets track momentum y component at exit of station [GeV/c]
     double GetPyOut() const { return fMomOut[1]; }
 
-    /// Gets track momentum z component at reference z of station [GeV/c]
+    /// @brief Gets track momentum z component at reference z of station [GeV/c]
     double GetPz() const { return fMom[2]; }
 
-    /// Gets track momentum z component at entrance to station [GeV/c]
+    /// @brief Gets track momentum z component at entrance to station [GeV/c]
     double GetPzIn() const { return fMomIn[2]; }
 
-    /// Gets track momentum z component at exit of station [GeV/c]
+    /// @brief Gets track momentum z component at exit of station [GeV/c]
     double GetPzOut() const { return fMomOut[2]; }
 
-    /// Gets global ID of the active tracking station
+    /// @brief Gets track charge over momentum at reference z of station [ec/GeV]
+    double GetQp() const { return fCharge / GetP(); }
+
+    /// @brief Gets track momentum absolute value at entrance to station [ec/GeV]
+    double GetQpIn() const { return fCharge / GetPIn(); }
+
+    /// @brief Gets track momentum absolute value at exit of station [ec/GeV]
+    double GetQpOut() const { return fCharge / GetPOut(); }
+
+    /// @brief Gets global ID of the active tracking station
     int GetStationId() const { return fStationId; }
 
-    /// Gets time [ns]
+    /// @brief Gets time [ns]
     double GetTime() const { return fTime; }
 
-    /// Gets ID of track from the internal CA MC track container (within event/TS)
+    /// @brief Gets ID of track from the internal CA MC track container (within event/TS)
     int GetTrackId() const { return fTrackId; }
 
-    /// Gets x coordinate at reference z of station [cm]
+    /// @brief Gets slope along x-axis at reference z of station
+    double GetTx() const { return fMom[0] / fMom[2]; }
+
+    /// @brief Gets slope along x-axis at entrance to station
+    double GetTxIn() const { return fMomIn[0] / fMomIn[2]; }
+
+    /// @brief Gets slope along x-axis at exit of station
+    double GetTxOut() const { return fMomOut[0] / fMomOut[2]; }
+
+    /// @brief Gets slope along x-axis at reference z of station
+    double GetTy() const { return fMom[1] / fMom[2]; }
+
+    /// @brief Gets slope along x-axis at entrance to station
+    double GetTyIn() const { return fMomIn[1] / fMomIn[2]; }
+
+    /// @brief Gets slope along x-axis at exit of station
+    double GetTyOut() const { return fMomOut[1] / fMomOut[2]; }
+
+    /// @brief Gets x coordinate at reference z of station [cm]
     double GetX() const { return fPos[0]; }
 
-    /// Gets x coordinate at entrance to station [cm]
+    /// @brief Gets x coordinate at entrance to station [cm]
     double GetXIn() const { return fPosIn[0]; }
 
-    /// Gets x coordinate at exit of station [cm]
+    /// @brief Gets x coordinate at exit of station [cm]
     double GetXOut() const { return fPosOut[0]; }
 
-    /// Gets y coordinate at reference z of station [cm]
+    /// @brief Gets y coordinate at reference z of station [cm]
     double GetY() const { return fPos[1]; }
 
-    /// Gets y coordinate at entrance to station [cm]
+    /// @brief Gets y coordinate at entrance to station [cm]
     double GetYIn() const { return fPosIn[1]; }
 
-    /// Gets y coordinate at exit of station [cm]
+    /// @brief Gets y coordinate at exit of station [cm]
     double GetYOut() const { return fPosOut[1]; }
 
-    /// Gets z coordinate at reference z of station [cm]
+    /// @brief Gets z coordinate at reference z of station [cm]
     double GetZ() const { return fPos[2]; }
 
-    /// Gets z coordinate at entrance to station [cm]
+    /// @brief Gets z coordinate at entrance to station [cm]
     double GetZIn() const { return fPosIn[2]; }
 
-    /// Gets z coordinate at exit of station [cm]
+    /// @brief Gets z coordinate at exit of station [cm]
     double GetZOut() const { return fPosOut[2]; }
 
     // *********************
     // **     Setters     **
     // *********************
 
-    /// Sets particle charge [e]
+    /// @brief Sets particle charge [e]
     void SetCharge(double charge) { fCharge = charge; }
 
-    /// Sets detector ID
+    /// @brief Sets detector ID
     void SetDetectorId(L1DetectorID detId) { fDetectorId = detId; }
 
-    /// Sets index of MC event containing this point
+    /// @brief Sets index of MC event containing this point
     void SetEventId(int eventId) { fLinkKey.fEvent = eventId; }
 
-    /// Sets index of this point in external data structures
+    /// @brief Sets index of this point in external data structures
     void SetExternalId(int id) { fLinkKey.fIndex = id; }
 
-    /// Sets index of MC file containing this point
+    /// @brief Sets index of MC file containing this point
     void SetFileId(int fileId) { fLinkKey.fFile = fileId; }
 
-    /// Sets index of this point in the CA internal structure
+    /// @brief Sets index of this point in the CA internal structure
     void SetId(int id) { fId = id; }
 
-    /// Sets particle mass [GeV/c2]
+    /// @brief Sets particle mass [GeV/c2]
     void SetMass(double mass) { fMass = mass; }
 
-    /// Sets index of mother track in the internal CA data structures
+    /// @brief Sets index of mother track in the internal CA data structures
     void SetMotherId(int motherId) { fMotherId = motherId; }
 
-    /// Sets PDG code
+    /// @brief Sets PDG code
     void SetPdgCode(int pdg) { fPdgCode = pdg; }
 
-    /// Sets track momentum x component at reference z of station [GeV/c]
+    /// @brief Sets track momentum x component at reference z of station [GeV/c]
     void SetPx(double px) { fMom[0] = px; }
 
-    /// Sets track momentum x component at entrance to station [GeV/c]
+    /// @brief Sets track momentum x component at entrance to station [GeV/c]
     void SetPxIn(double px) { fMomIn[0] = px; }
 
-    /// Sets track momentum x component at exit of station [GeV/c]
+    /// @brief Sets track momentum x component at exit of station [GeV/c]
     void SetPxOut(double px) { fMomOut[0] = px; }
 
-    /// Sets track momentum y component at reference z of station [GeV/c]
+    /// @brief Sets track momentum y component at reference z of station [GeV/c]
     void SetPy(double py) { fMom[1] = py; }
 
-    /// Sets track momentum y component at entrance to station [GeV/c]
+    /// @brief Sets track momentum y component at entrance to station [GeV/c]
     void SetPyIn(double py) { fMomIn[1] = py; }
 
-    /// Sets track momentum y component at exit of station [GeV/c]
+    /// @brief Sets track momentum y component at exit of station [GeV/c]
     void SetPyOut(double py) { fMomOut[1] = py; }
 
-    /// Sets track momentum z component at reference z of station [GeV/c]
+    /// @brief Sets track momentum z component at reference z of station [GeV/c]
     void SetPz(double pz) { fMom[2] = pz; }
 
-    /// Sets track momentum z component at entrance to station [GeV/c]
+    /// @brief Sets track momentum z component at entrance to station [GeV/c]
     void SetPzIn(double pz) { fMomIn[2] = pz; }
 
-    /// Sets track momentum z component at exit of station [GeV/c]
+    /// @brief Sets track momentum z component at exit of station [GeV/c]
     void SetPzOut(double pz) { fMomOut[2] = pz; }
 
-    /// Sets global index of active station
+
+    /// @brief Sets global index of active station
     void SetStationId(int stationId) { fStationId = stationId; }
 
-    /// Sets time [ns]
+    /// @brief Sets time [ns]
     void SetTime(double time) { fTime = time; }
 
-    /// Sets track ID in the CA internal track container (within event/TS)
+    /// @brief Sets track ID in the CA internal track container (within event/TS)
     void SetTrackId(int trackId) { fTrackId = trackId; }
 
-    /// Sets x coordinate at reference z of station [cm]
+    /// @brief Sets x coordinate at reference z of station [cm]
     void SetX(double x) { fPos[0] = x; }
 
-    /// Sets x coordinate at entrance to station [cm]
+    /// @brief Sets x coordinate at entrance to station [cm]
     void SetXIn(double x) { fPosIn[0] = x; }
 
-    /// Sets x coordinate at exit of station [cm]
+    /// @brief Sets x coordinate at exit of station [cm]
     void SetXOut(double x) { fPosOut[0] = x; }
 
-    /// Sets y coordinate at reference z of station [cm]
+    /// @brief Sets y coordinate at reference z of station [cm]
     void SetY(double y) { fPos[1] = y; }
 
-    /// Sets y coordinate at entrance to station [cm]
+    /// @brief Sets y coordinate at entrance to station [cm]
     void SetYIn(double y) { fPosIn[1] = y; }
 
-    /// Sets x coordinate at exit of station [cm]
+    /// @brief Sets x coordinate at exit of station [cm]
     void SetYOut(double y) { fPosOut[1] = y; }
 
-    /// Sets z coordinate at reference z of station [cm]
+    /// @brief Sets z coordinate at reference z of station [cm]
     void SetZ(double z) { fPos[2] = z; }
 
-    /// Sets z coordinate at entrance to station [cm]
+    /// @brief Sets z coordinate at entrance to station [cm]
     void SetZIn(double z) { fPosIn[2] = z; }
 
-    /// Sets z coordinate at exit of station [cm]
+    /// @brief Sets z coordinate at exit of station [cm]
     void SetZOut(double z) { fPosOut[2] = z; }
 
-    /// Prints content for a given verbosity level
-    /// \param  verbose  Verbosity level:
+    /// @brief Prints content for a given verbosity level
+    /// @param  verbose  Verbosity level:
     ///                  -#0: Prints nothing
     ///                  -#1: Prints track ID, station ID, time and position
     ///                  -#2: Also prints zIn, zOut, absolute momentum, as well as point, event and file IDs
-    /// \param  printHeader  If true, parameter names will be printed instead of the parameters themselves
+    /// @param  printHeader  If true, parameter names will be printed instead of the parameters themselves
     std::string ToString(int verbose, bool printHeader = false) const;
 
     // ****************************
@@ -285,7 +332,7 @@ namespace ca::tools
 
     L1DetectorID fDetectorId;  ///< Detector ID of MC point
 
-    L1Vector<int> fvHitIndexes {"ca::tools::MCPoint::fvHitIndexes"};
+    L1Vector<int> fvHitIndexes {"ca::tools::MCPoint::fvHitIndexes"};  ///< Indexes of hits, assigned to this point
   };
 }  // namespace ca::tools
 
diff --git a/reco/L1/qa/CbmCaOutputQa.cxx b/reco/L1/qa/CbmCaOutputQa.cxx
index 51080c550a..b0c7ba4004 100644
--- a/reco/L1/qa/CbmCaOutputQa.cxx
+++ b/reco/L1/qa/CbmCaOutputQa.cxx
@@ -36,7 +36,8 @@ void OutputQa::FillHistograms()
   // ** Fill distributions for reconstructed tracks (including ghost ones) **
   // ************************************************************************
 
-  for (const auto& recoTrk : fvRecoTracks) {
+  for (size_t iTrkReco = 0; iTrkReco < fvRecoTracks.size(); ++iTrkReco) {
+    const auto& recoTrk = fvRecoTracks[iTrkReco];
     // Reject tracks, which do not contain hits
     if (recoTrk.GetNofHits() < 1) { continue; }
 
@@ -84,40 +85,90 @@ void OutputQa::FillHistograms()
       }
     }  // recoTrk.GetNDF() > 0: end
 
-    if (recoTrk.IsGhost()) {
-      fph_ghost_purity->Fill(100 * recoTrk.GetMaxPurity());
-      fph_ghost_p->Fill(recoTrk.GetP());
-      fph_ghost_pt->Fill(recoTrk.GetPt());
-      fph_ghost_phi->Fill(recoTrk.GetPhi());
-      fph_ghost_nhits->Fill(recoTrk.GetNofHits());
-      fph_ghost_fsta->Fill(hitFst.GetStationId());
-      fph_ghost_fhitR->Fill(hitFst.GetR());
-
-      // z-positions of the first and second hit stations
-      double z0 = fpParameters->GetStation(hitFst.GetStationId()).fZ[0];
-      double z1 = fpParameters->GetStation(hitSnd.GetStationId()).fZ[0];
-
-      if (fabs(z1 - z0) > 1.e-4) {  // test for z1 != z2
-        fph_ghost_tx->Fill((hitSnd.GetX() - hitFst.GetX()) / (z1 - z0));
-        fph_ghost_ty->Fill((hitSnd.GetY() - hitFst.GetY()) / (z1 - z0));
-      }
+    if (IsMCUsed()) {
+      if (recoTrk.IsGhost()) {
+        fph_ghost_purity->Fill(100 * recoTrk.GetMaxPurity());
+        fph_ghost_p->Fill(recoTrk.GetP());
+        fph_ghost_pt->Fill(recoTrk.GetPt());
+        fph_ghost_phi->Fill(recoTrk.GetPhi());
+        fph_ghost_nhits->Fill(recoTrk.GetNofHits());
+        fph_ghost_fsta->Fill(hitFst.GetStationId());
+        fph_ghost_fhitR->Fill(hitFst.GetR());
+
+        // z-positions of the first and second hit stations
+        double z0 = fpParameters->GetStation(hitFst.GetStationId()).fZ[0];
+        double z1 = fpParameters->GetStation(hitSnd.GetStationId()).fZ[0];
+
+        if (fabs(z1 - z0) > 1.e-4) {  // test for z1 != z2
+          fph_ghost_tx->Fill((hitSnd.GetX() - hitFst.GetX()) / (z1 - z0));
+          fph_ghost_ty->Fill((hitSnd.GetY() - hitFst.GetY()) / (z1 - z0));
+        }
+
+        fph_ghost_nhits_vs_p->Fill(recoTrk.GetP(), recoTrk.GetNofHits());
+        fph_ghost_fsta_vs_p->Fill(recoTrk.GetP(), hitFst.GetStationId());
+        if (hitFst.GetStationId() <= hitLst.GetStationId()) {
+          fph_ghost_lsta_vs_fsta->Fill(hitFst.GetStationId(), hitLst.GetStationId());
+        }
+      }  // recoTrk.IsGhost(): end
+    }
+
+    // ********************************
+    // ** Different track categories **
+    // ********************************
+
+    fvpTrackHistograms[ETrackType::kAll]->FillRecoTrack(iTrkReco);
+
+    if (IsMCUsed()) {
+      int iTrkMC = recoTrk.GetMatchedMCTrackIndex();
+      if (iTrkMC > -1) {
+        const auto& mcTrk = fMCData.GetTrack(iTrkMC);
+        int pdg           = mcTrk.GetPdgCode();
+        bool isPrimary    = mcTrk.IsPrimary();
 
-      fph_ghost_nhits_vs_p->Fill(recoTrk.GetP(), recoTrk.GetNofHits());
-      fph_ghost_fsta_vs_p->Fill(recoTrk.GetP(), hitFst.GetStationId());
-      if (hitFst.GetStationId() <= hitLst.GetStationId()) {
-        fph_ghost_lsta_vs_fsta->Fill(hitFst.GetStationId(), hitLst.GetStationId());
+        if (isPrimary) {
+          fvpTrackHistograms[ETrackType::kPrim]->FillRecoTrack(iTrkReco);
+
+          if (pdg == +211) { fvpTrackHistograms[ETrackType::kPrimPIP]->FillRecoTrack(iTrkReco); }
+          if (pdg == -211) { fvpTrackHistograms[ETrackType::kPrimPIM]->FillRecoTrack(iTrkReco); }
+        }
+        else {
+          fvpTrackHistograms[ETrackType::kSec]->FillRecoTrack(iTrkReco);
+
+          if (pdg == +211) { fvpTrackHistograms[ETrackType::kSecPIP]->FillRecoTrack(iTrkReco); }
+          if (pdg == -211) { fvpTrackHistograms[ETrackType::kSecPIM]->FillRecoTrack(iTrkReco); }
+        }
       }
-    }  // recoTrk.IsGhost(): end
-  }    // loop over recoTrk: end
+    }
+
+
+  }  // loop over recoTrk: end
 
 
   // *************************************
   // ** Fill distributions of MC-tracks **
   // *************************************
+  if (IsMCUsed()) {
+    for (int iTrkMC = 0; iTrkMC < fMCData.GetNofTracks(); ++iTrkMC) {
+      const auto& mcTrk = fMCData.GetTrack(iTrkMC);
+      fvpTrackHistograms[ETrackType::kAll]->FillMCTrack(iTrkMC);
+
+      int pdg        = mcTrk.GetPdgCode();
+      bool isPrimary = mcTrk.IsPrimary();
+
+      if (isPrimary) {
+        fvpTrackHistograms[ETrackType::kPrim]->FillMCTrack(iTrkMC);
+
+        if (pdg == +211) { fvpTrackHistograms[ETrackType::kPrimPIP]->FillMCTrack(iTrkMC); }
+        if (pdg == -211) { fvpTrackHistograms[ETrackType::kPrimPIM]->FillMCTrack(iTrkMC); }
+      }
+      else {
+        fvpTrackHistograms[ETrackType::kSec]->FillMCTrack(iTrkMC);
 
-  //for (const auto& mcTrk: fpMCData->GetTrackContainer()) {
-  //
-  //}
+        if (pdg == +211) { fvpTrackHistograms[ETrackType::kSecPIP]->FillMCTrack(iTrkMC); }
+        if (pdg == -211) { fvpTrackHistograms[ETrackType::kSecPIM]->FillMCTrack(iTrkMC); }
+      }
+    }
+  }
 }
 
 // ---------------------------------------------------------------------------------------------------------------------
@@ -199,6 +250,25 @@ InitStatus OutputQa::InitDataBranches()
 //
 InitStatus OutputQa::InitHistograms()
 {
+  auto RegisterTrackQa = [&](const char* typeName, ETrackType type, bool bSuppressMC = false) {
+    bool bUseMC              = IsMCUsed() && !bSuppressMC;
+    fvpTrackHistograms[type] = std::make_unique<TrackTypeQa>(typeName, fsPrefix.Data(), bUseMC, fpFolderRoot);
+    fvpTrackHistograms[type]->RegisterParameters(fpParameters);
+    fvpTrackHistograms[type]->RegisterRecoHits(fvHits);
+    fvpTrackHistograms[type]->RegisterRecoTracks(fvRecoTracks);
+    fvpTrackHistograms[type]->RegisterMCData(fMCData);
+    fvpTrackHistograms[type]->Init();
+  };
+
+  RegisterTrackQa("all", ETrackType::kAll);
+  RegisterTrackQa("prim", ETrackType::kPrim);
+  RegisterTrackQa("sec", ETrackType::kSec);
+  RegisterTrackQa("prim_pip", ETrackType::kPrimPIP);
+  RegisterTrackQa("prim_pim", ETrackType::kPrimPIM);
+  RegisterTrackQa("sec_pip", ETrackType::kSecPIP);
+  RegisterTrackQa("sec_pim", ETrackType::kSecPIM);
+
+
   fph_reco_p     = MakeHistoFromConfig<TH1F>("reco_p");
   fph_reco_pt    = MakeHistoFromConfig<TH1F>("reco_pt");
   fph_reco_phi   = MakeHistoFromConfig<TH1F>("reco_phi");
@@ -230,6 +300,11 @@ InitStatus OutputQa::InitHistograms()
     fph_ghost_fsta_vs_p    = MakeHistoFromConfig<TH2F>("ghost_fsta_vs_p");
     fph_ghost_lsta_vs_fsta = MakeHistoFromConfig<TH2F>("ghost_lsta_vs_fsta");
 
+    // Reconstructed tracks vs. MC quantities
+    fph_reco_pMC        = MakeHistoFromConfig<TH1F>("reco_pMC");
+    fph_reco_yMC        = MakeHistoFromConfig<TH1F>("reco_yMC");
+    fph_reco_pMC_vs_yMC = MakeHistoFromConfig<TH2F>("reco_pMC_vs_yMC");
+
     // Residuals and pools of track parameters
     fph_fst_res_x    = MakeHistoFromConfig<TH1F>("fst_res_x");
     fph_fst_res_y    = MakeHistoFromConfig<TH1F>("fst_res_y");
diff --git a/reco/L1/qa/CbmCaOutputQa.h b/reco/L1/qa/CbmCaOutputQa.h
index 91b54aca2d..930134f741 100644
--- a/reco/L1/qa/CbmCaOutputQa.h
+++ b/reco/L1/qa/CbmCaOutputQa.h
@@ -12,10 +12,14 @@
 
 #include "CbmCaMCModule.h"
 #include "CbmCaTimeSliceReader.h"
+#include "CbmCaTrackTypeQa.h"
 #include "CbmL1DetectorID.h"
 #include "CbmL1Hit.h"
 #include "CbmQaTask.h"
 
+#include <array>
+#include <memory>
+
 #include "CaToolsDebugger.h"
 #include "L1Parameters.h"
 
@@ -25,6 +29,52 @@ namespace cbm::ca
   ///
   class OutputQa : public CbmQaTask {
   public:
+    /// @brief Enumeration of track category
+    enum ETrackType
+    {
+      kAll,   ///< all tracks
+      kPrim,  ///< primary tracks
+      kSec,   ///< secondary tracks
+      // kPrimEP,  ///< primary electron tracks
+      // kPrimEM,  ///< primary positron tracks
+      // kSecEP,   ///< secondary electron tracks
+      // kSecEM,   ///< secondary positron tracks
+      // kPrimMUP, ///< primary .....
+      // kPrimMUM,
+      // kSecMUP,
+      // kSecMUM,
+      kPrimPIP,
+      kPrimPIM,
+      kSecPIP,
+      kSecPIM,
+      // kPrimKP,
+      // kPrimKM,
+      // kSecKP,
+      // kSecKM,
+      // kPrimP,
+      // kPrimPBAR,
+      // kSecP,
+      // kSecPBAR,
+      // kPrimD,
+      // kPrimDBAR,
+      // kSecD,
+      // kSecDBAR,
+      // kPrimT,
+      // kPrimTBAR,
+      // kSecT,
+      // kSecTBAR,
+      // kPrim3HE,
+      // kPrim3HEBAR,
+      // kSec3HE,
+      // kSec3HEBAR,
+      // kPrim4HE,
+      // kPrim4HEBAR,
+      // kSec4HE,
+      // kSec4HEBAR,
+      kEND
+    };
+
+
     /// @brief  Constructor from parameters
     /// @param  verbose   Verbosity level
     /// @param  isMCUsed  Flag, if MC information is available for this task
@@ -122,7 +172,15 @@ namespace cbm::ca
     ::ca::tools::MCData fMCData;  ///< Input MC data (points and tracks)
 
 
-    // **  Pointers to histogram objects **
+    // *************************
+    // **  List of histograms **
+    // *************************
+
+    /// Histograms of different track types
+    std::array<std::unique_ptr<TrackTypeQa>, ETrackType::kEND> fvpTrackHistograms;
+
+    std::array<std::tuple<Style_t, Marker_t, Color_t>, ETrackType::kEND> fvpTrackHistogramStyles;
+
     // Reconstructed tracks
     TH1F* fph_reco_p        = nullptr;  ///< Total momentum over charge of reconstructed tracks
     TH1F* fph_reco_pt       = nullptr;  ///< Transverse momentum over charge of reconstructed tracks
@@ -132,7 +190,6 @@ namespace cbm::ca
     TH1F* fph_reco_fhitR    = nullptr;  ///< Distance of the first hit from z-axis for reconstructed tracks
     TH1F* fph_reco_nhits    = nullptr;  ///< Hit number of reconstructed tracks
     TH1F* fph_reco_fsta     = nullptr;  ///< First station index of reconstructed tracks
-    TH1F* fph_reco_purity   = nullptr;  ///< Purity of reconstructed tracks (\note purity requires MC information)
     TH1F* fph_reco_chi2_ndf = nullptr;  ///< Fit chi2 over NDF of reconstructed tracks
     TH1F* fph_reco_prob     = nullptr;  ///< Fit probability of reconstructed tracks
     TH1F* fph_rest_chi2_ndf = nullptr;  ///< Fit chi2 over NDF of non-reconstructable tracks
@@ -154,6 +211,12 @@ namespace cbm::ca
     TH2F* fph_ghost_fsta_vs_p    = nullptr;  ///< First station index vs. total momentum over charge for ghost tracks
     TH2F* fph_ghost_lsta_vs_fsta = nullptr;  ///< Last station index vs. first station index of ghost tracks
 
+    // Reco tracks vs MC variables
+    TH1F* fph_reco_pMC        = nullptr;  ///< Reconstructed track distribution vs MC momemntum
+    TH1F* fph_reco_yMC        = nullptr;  ///< Reconstructed track distribution vs MC rapidity
+    TH2F* fph_reco_pMC_vs_yMC = nullptr;  ///< Reconstructed track phase space (MC mom vs rapidity)
+    TH1F* fph_reco_purity     = nullptr;  ///< Purity of reconstructed tracks (\note purity requires MC information)
+
     // Residuals and pulls at the first track point
     TH1F* fph_fst_res_x    = nullptr;  ///< Residual of x at first track point [cm]
     TH1F* fph_fst_res_y    = nullptr;  ///< Residual of y at first track point [cm]
diff --git a/reco/L1/qa/CbmCaTrackFitQa.cxx b/reco/L1/qa/CbmCaTrackFitQa.cxx
new file mode 100644
index 0000000000..7769050a36
--- /dev/null
+++ b/reco/L1/qa/CbmCaTrackFitQa.cxx
@@ -0,0 +1,122 @@
+/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergei Zharko [committer] */
+
+/// @file   CbmCaTrackFitQa.cxx
+/// @brief  QA submodule for track fit results (residuals and puls) at selected z-coordinate (implementation)
+/// @since  03.04.2023
+/// @author Sergei Zharko <s.zharko@gsi.de>
+
+#include "CbmCaTrackFitQa.h"
+
+#include "CbmL1Track.h"
+
+#include "CaToolsMCData.h"
+#include "L1Field.h"
+#include "L1Fit.h"
+
+using cbm::ca::TrackFitQa;
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+TrackFitQa::TrackFitQa(const char* pointTag, const char* prefixName, TFolder* pParentFolder)
+  : CbmQaIO(pointTag, Form("%s_%s", prefixName, pointTag), pParentFolder)
+{
+  fStoringMode = EStoringMode::kSAMEDIR;
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+void TrackFitQa::Init()
+{
+  fph_res_x  = MakeHisto<TH1F>("res_x", "", fBinsRESX, fLoRESX, fUpRESX);
+  fph_res_y  = MakeHisto<TH1F>("res_y", "", fBinsRESY, fLoRESY, fUpRESY);
+  fph_res_t  = MakeHisto<TH1F>("res_t", "", fBinsREST, fLoREST, fUpREST);
+  fph_res_tx = MakeHisto<TH1F>("res_tx", "", fBinsRESTX, fLoRESTX, fUpRESTX);
+  fph_res_ty = MakeHisto<TH1F>("res_ty", "", fBinsRESTY, fLoRESTY, fUpRESTY);
+  fph_res_qp = MakeHisto<TH1F>("res_qp", "", fBinsRESQP, fLoRESQP, fUpRESQP);
+  fph_res_vi = MakeHisto<TH1F>("res_vi", "", fBinsRESVI, fLoRESVI, fUpRESVI);
+
+  fph_pull_x  = MakeHisto<TH1F>("pull_x", "", fBinsPULLX, fLoPULLX, fUpPULLX);
+  fph_pull_y  = MakeHisto<TH1F>("pull_y", "", fBinsPULLY, fLoPULLY, fUpPULLY);
+  fph_pull_t  = MakeHisto<TH1F>("pull_t", "", fBinsPULLT, fLoPULLT, fUpPULLT);
+  fph_pull_tx = MakeHisto<TH1F>("pull_tx", "", fBinsPULLTX, fLoPULLTX, fUpPULLTX);
+  fph_pull_ty = MakeHisto<TH1F>("pull_ty", "", fBinsPULLTY, fLoPULLTY, fUpPULLTY);
+  fph_pull_qp = MakeHisto<TH1F>("pull_qp", "", fBinsPULLQP, fLoPULLQP, fUpPULLQP);
+  fph_pull_vi = MakeHisto<TH1F>("pull_vi", "", fBinsPULLVI, fLoPULLVI, fUpPULLVI);
+
+  // Set histogram titles
+  TString sPrefix = (fsTitle.Length() > 0) ? fsTitle + " point " : "";
+  fph_res_x->SetTitle(sPrefix + " residual for x-coordinate;x_{reco} - x_{MC} [cm]");
+  fph_res_y->SetTitle(sPrefix + " residual for y-coordinate;y_{reco} - y_{MC} [cm]");
+  fph_res_t->SetTitle(sPrefix + " residual for time;t_{reco} - t_{MC} [ns]");
+  fph_res_tx->SetTitle(sPrefix + " residual for slope along x-axis;T_{x}^{reco} - T_{x}^{MC}");
+  fph_res_ty->SetTitle(sPrefix + " residual for slope along y-axis;T_{y}^{reco} - T_{y}^{MC}");
+  fph_res_qp->SetTitle(sPrefix + " residual for q/p;(q/p)_{reco} - (q/p)_{MC} [ec/GeV]");
+  fph_res_vi->SetTitle(sPrefix + " residual for inverse speed;v^{-1}_{reco} - v^{-1}_{MC} [c^{-1}]");
+
+  fph_pull_x->SetTitle(sPrefix + " pull for x-coordinate;(x_{reco} - x_{MC}) / #sigma_{x}");
+  fph_pull_y->SetTitle(sPrefix + " pull for y-coordinate;(y_{reco} - y_{MC}) / #sigma_{y}");
+  fph_pull_t->SetTitle(sPrefix + " pull for time;(t_{reco} - t_{MC}) / #sigma_{t}");
+  fph_pull_tx->SetTitle(sPrefix + " pull for slope along x-axis;(T_{x}^{reco} - T_{x}^{MC}) / #sigma_{T_{x}}");
+  fph_pull_ty->SetTitle(sPrefix + " pull for slope along y-axis;(T_{y}^{reco} - T_{y}^{MC}) / #sigma_{T_{y}}");
+  fph_pull_qp->SetTitle(sPrefix + " pull for q/p;((q/p)_{reco} - (q/p)_{MC}) / #sigma_{q/p}");
+  fph_pull_vi->SetTitle(sPrefix + " pull for inverse speed;(v^{-1}_{reco} - v^{-1}_{MC}) / #sigma_{v^{-1}}");
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+void TrackFitQa::Fill(const L1TrackPar& trPar, const ::ca::tools::MCPoint& mcPoint, bool bTimeMeasured, double weight)
+{
+  // Probably, a bottleneck
+  L1Fit fitter;
+  fitter.SetParticleMass(fMass);
+  fitter.SetMask(fmask::One());
+  fitter.SetDoFitVelocity(true);
+  fitter.SetTrack(trPar);
+  L1FieldRegion fieldRegion;
+  fieldRegion.SetUseOriginalField();  // Precised magnetic field is used
+  fitter.Extrapolate(mcPoint.GetZOut(), fieldRegion);
+
+  const L1TrackPar& trParExtr = fitter.Tr();  // Track parameters extrapolated to given MC point
+
+  // ** Time-independent measurements **
+
+  double resX  = trParExtr.GetX() - mcPoint.GetXOut();    // residual of x-position [cm]
+  double resY  = trParExtr.GetY() - mcPoint.GetYOut();    // residual of y-position [cm]
+  double resTx = trParExtr.GetTx() - mcPoint.GetTxOut();  // residual of slope along x-axis [1]
+  double resTy = trParExtr.GetTy() - mcPoint.GetTyOut();  // residual of slope along y-axis [1]
+  double resQp = trParExtr.GetQp() - mcPoint.GetQpOut();  // residual of q/p [ec/GeV]
+
+  double pullX  = resX / trParExtr.GetXErr();    // pull of x-position
+  double pullY  = resY / trParExtr.GetYErr();    // pull of y-position
+  double pullTx = resTx / trParExtr.GetTxErr();  // pull of slope along x-axis
+  double pullTy = resTy / trParExtr.GetTyErr();  // pull of slope along y-axis
+  double pullQp = resQp / trParExtr.GetQpErr();  // pull of q/p
+
+  fph_res_x->Fill(resX, weight);
+  fph_res_y->Fill(resY, weight);
+  fph_res_tx->Fill(resTx, weight);
+  fph_res_ty->Fill(resTy, weight);
+  fph_res_qp->Fill(resQp, weight);
+
+  fph_pull_x->Fill(pullX, weight);
+  fph_pull_y->Fill(pullY, weight);
+  fph_pull_tx->Fill(pullTx, weight);
+  fph_pull_ty->Fill(pullTy, weight);
+  fph_pull_qp->Fill(pullQp, weight);
+
+
+  // ** Time-dependent measurements **
+  if (bTimeMeasured) {
+    double resT   = trParExtr.GetTime() - mcPoint.GetTime();  // residual of time [ns]
+    double resVi  = (trParExtr.GetInvSpeed() - mcPoint.GetInvSpeedOut()) * L1Constants::phys::kSpeedOfLightInv;
+    double pullT  = resT / trParExtr.GetTimeErr();       // pull of time
+    double pullVi = resVi / trParExtr.GetInvSpeedErr();  // pull of inverse speed
+
+    fph_res_t->Fill(resT, weight);
+    fph_res_vi->Fill(resVi, weight);
+    fph_pull_t->Fill(pullT, weight);
+    fph_pull_vi->Fill(pullVi, weight);
+  }
+}
diff --git a/reco/L1/qa/CbmCaTrackFitQa.h b/reco/L1/qa/CbmCaTrackFitQa.h
new file mode 100644
index 0000000000..4f2ce84f43
--- /dev/null
+++ b/reco/L1/qa/CbmCaTrackFitQa.h
@@ -0,0 +1,163 @@
+/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergei Zharko [committer] */
+
+/// @file   CbmCaTrackFitQa.h
+/// @brief  QA submodule for track fit results (residuals and puls) at selected z-coordinate (header)
+/// @since  03.04.2023
+/// @author Sergei Zharko <s.zharko@gsi.de>
+
+#ifndef CbmCaTrackFitQa_h
+#define CbmCaTrackFitQa_h 1
+
+#include "CbmQaIO.h"
+
+#include "L1Constants.h"
+#include "L1Field.h"
+#include "L1Fit.h"
+#include "L1Vector.h"
+
+// Forward declarations
+namespace ca::tools
+{
+  class MCData;
+  class MCPoint;
+}  // namespace ca::tools
+class CbmL1Track;
+class CbmL1HitDebugInfo;
+
+class TFolder;
+class TH1F;
+
+namespace cbm::ca
+{
+  /// @brief Set of histograms to monitor track parameters
+  ///
+  /// Class provides histograms to monitor track fit parameters at a selected z-coordinate.
+  /// The parameters include x, y, tx, ty, time, q/p, vi (inverse speed).
+  class TrackFitQa : public CbmQaIO {
+  public:
+    /// @brief Constructor
+    /// @param pointTag       Tag for point, in which the parameters are analyzed
+    /// @param prefixName     Name of unique prefix
+    /// @param pParentFolder  Pointer to parent folder
+    TrackFitQa(const char* pointTag, const char* prefixName, TFolder* pParentFolder);
+
+    /// @brief Destructor
+    ~TrackFitQa() = default;
+
+    /// @brief Copy constructor
+    TrackFitQa(const TrackFitQa&) = delete;
+
+    /// @brief Move constructor
+    TrackFitQa(TrackFitQa&&) = delete;
+
+    /// @brief Copy assignment operator
+    TrackFitQa& operator=(const TrackFitQa&) = delete;
+
+    /// @brief Move assignment operator
+    TrackFitQa& operator=(TrackFitQa&&) = delete;
+
+    /// @brief Gets title of fit parameters
+    const char* GetTitle() const { return fsTitle; }
+
+    /// @brief Initializes histograms
+    void Init();
+
+    /// @brief Fills pull and residual histograms
+    /// @note  To be called in the loop over reconstructed tracks full sample
+    /// @param iTrkReco  Index of reconstructed track
+    /// @param weight    Weight
+    void Fill(const L1TrackPar& trPar, const ::ca::tools::MCPoint& mcPoint, bool bTimeMeasured, double weight = 1);
+
+    /// @brief Sets particle mass, used for fitting a track
+    /// @param mass  Particle mass [GeV/c2]
+    void SetParticleMass(double mass) { fMass = mass; }
+
+    /// @brief Sets title, which is to be reflected on legends and titles
+    /// @param title  Title of fit distributions
+    void SetTitle(const char* title) { fsTitle = title; }
+
+    // ************************
+    // ** List of histograms **
+    // ************************
+
+    // ** Residuals **
+    TH1F* fph_res_x  = nullptr;  ///< Residual of x-coordinate [cm]
+    TH1F* fph_res_y  = nullptr;  ///< Residual of y-coordinate [cm]
+    TH1F* fph_res_tx = nullptr;  ///< Residual of slope along x-axis
+    TH1F* fph_res_ty = nullptr;  ///< Residual of slope along y-axis
+    TH1F* fph_res_qp = nullptr;  ///< Residual of q/p [ec/GeV]
+    TH1F* fph_res_t  = nullptr;  ///< Residual of time [ns]
+    TH1F* fph_res_vi = nullptr;  ///< Residual of inverse speed [1/c]
+
+    // ** Pulls **
+    TH1F* fph_pull_x  = nullptr;  ///< Pull of x-coordinate
+    TH1F* fph_pull_y  = nullptr;  ///< Pull of y-coordinate
+    TH1F* fph_pull_tx = nullptr;  ///< Pull of slope along x-axis
+    TH1F* fph_pull_ty = nullptr;  ///< Pull of slope along y-axis
+    TH1F* fph_pull_qp = nullptr;  ///< Pull of q/p
+    TH1F* fph_pull_t  = nullptr;  ///< Pull of time
+    TH1F* fph_pull_vi = nullptr;  ///< Pull of inverse speed
+
+
+    // **************************
+    // ** Histogram properties **
+    // **************************
+
+    // ** Binning **
+    int fBinsRESX   = 200;     ///< Number of bins, residual of x
+    double fLoRESX  = -4.e-3;  ///< Lower boundary, residual of x [cm]
+    double fUpRESX  = +4.e-3;  ///< Upper boundary, residual of x [cm]
+    int fBinsRESY   = 200;     ///< Number of bins, residual of y
+    double fLoRESY  = -4.e-2;  ///< Lower boundary, residual of y [cm]
+    double fUpRESY  = +4.e-2;  ///< Upper boundary, residual of y [cm]
+    int fBinsRESTX  = 200;     ///< Number of bins, residual of slope along x-axis
+    double fLoRESTX = -4.e-3;  ///< Lower boundary, residual of slope along x-axis
+    double fUpRESTX = +4.e-3;  ///< Upper boundary, residual of slope along x-axis
+    int fBinsRESTY  = 200;     ///< Number of bins, residual of slope along y-axis
+    double fLoRESTY = -4.e-3;  ///< Lower boundary, residual of slope along y-axis
+    double fUpRESTY = +4.e-3;  ///< Upper boundary, residual of slope along y-axis
+    int fBinsRESQP  = 200;     ///< Number of bins, residual of q/p
+    double fLoRESQP = -10.;    ///< Lower boundary, residual of q/p [ec/GeV]
+    double fUpRESQP = +10.;    ///< Upper boundary, residual of q/p [ec/GeV]
+    int fBinsREST   = 200;     ///< Number of bins, residual of time
+    double fLoREST  = -10.;    ///< Lower boundary, residual of time [ns]
+    double fUpREST  = +10.;    ///< Upper boundary, residual of time [ns]
+    int fBinsRESVI  = 200;     ///< Number of bins, residual of inverse speed
+    double fLoRESVI = -2.;     ///< Lower boundary, residual of inverse speed [1/c]
+    double fUpRESVI = +2.;     ///< Upper boundary, residual of inverse speed [1/c]
+
+    int fBinsPULLX   = 200;   ///< Number of bins, pull of x
+    double fLoPULLX  = -1.;   ///< Lower boundary, pull of x [cm]
+    double fUpPULLX  = +1.;   ///< Upper boundary, pull of x [cm]
+    int fBinsPULLY   = 200;   ///< Number of bins, pull of y
+    double fLoPULLY  = -1.;   ///< Lower boundary, pull of y [cm]
+    double fUpPULLY  = +1.;   ///< Upper boundary, pull of y [cm]
+    int fBinsPULLTX  = 200;   ///< Number of bins, pull of slope along x-axis
+    double fLoPULLTX = -4.;   ///< Lower boundary, pull of slope along x-axis
+    double fUpPULLTX = +4.;   ///< Upper boundary, pull of slope along x-axis
+    int fBinsPULLTY  = 200;   ///< Number of bins, pull of slope along y-axis
+    double fLoPULLTY = -4.;   ///< Lower boundary, pull of slope along y-axis
+    double fUpPULLTY = +4.;   ///< Upper boundary, pull of slope along y-axis
+    int fBinsPULLQP  = 200;   ///< Number of bins, pull of q/p
+    double fLoPULLQP = -10.;  ///< Lower boundary, pull of q/p [ec/GeV]
+    double fUpPULLQP = +10.;  ///< Upper boundary, pull of q/p [ec/GeV]
+    int fBinsPULLT   = 200;   ///< Number of bins, pull of time
+    double fLoPULLT  = -10.;  ///< Lower boundary, pull of time [ns]
+    double fUpPULLT  = +10.;  ///< Upper boundary, pull of time [ns]
+    int fBinsPULLVI  = 200;   ///< Number of bins, pull of inverse speed
+    double fLoPULLVI = -2.;   ///< Lower boundary, pull of inverse speed [1/c]
+    double fUpPULLVI = +2.;   ///< Upper boundary, pull of inverse speed [1/c]
+
+  private:
+    TString fsTitle = "";  ///< Title of the point
+
+    double fMass = L1Constants::phys::kMuonMass;  /// Mass of particle
+  };
+
+
+}  // namespace cbm::ca
+
+
+#endif  // CbmCaTrackFitQa_h
diff --git a/reco/L1/qa/CbmCaTrackTypeQa.cxx b/reco/L1/qa/CbmCaTrackTypeQa.cxx
new file mode 100644
index 0000000000..c69ce7aa16
--- /dev/null
+++ b/reco/L1/qa/CbmCaTrackTypeQa.cxx
@@ -0,0 +1,157 @@
+/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergei Zharko [committer] */
+
+/// @file   CbmCaTrackTypeQa.h
+/// @brief  QA submodule for different track types (header)
+/// @since  27.03.2023
+/// @author Sergei Zharko <s.zharko@gsi.de>
+
+#include "CbmCaTrackTypeQa.h"
+
+#include "CbmCaTrackFitQa.h"
+#include "CbmL1Track.h"
+
+#include "CaToolsMCData.h"
+
+using ca::tools::MCPoint;
+using ca::tools::MCTrack;
+using cbm::ca::TrackTypeQa;
+
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+TrackTypeQa::TrackTypeQa(const char* typeName, const char* prefixName, bool bUseMC, TFolder* pParentFolder)
+  : CbmQaIO(typeName, Form("%s_%s", prefixName, typeName), pParentFolder)
+  , fbUseMC(bUseMC)
+{
+  fStoringMode = EStoringMode::kSAMEDIR;
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+void TrackTypeQa::Init()
+{
+  // TODO: Replace assertions with exceptions
+  assert(fpvRecoTracks);
+  assert(fpvHits);
+  assert(!fbUseMC || fpMCData);
+
+  fph_reco_p     = MakeHisto<TH1F>("reco_p", "", kBinsP, kLoP, kUpP);
+  fph_reco_pt    = MakeHisto<TH1F>("reco_pt", "", kBinsPT, kLoPT, kUpPT);
+  fph_reco_eta   = MakeHisto<TH1F>("reco_eta", "", kBinsETA, kLoETA, kUpETA);
+  fph_reco_phi   = MakeHisto<TH1F>("reco_phi", "", kBinsPHI, kLoPHI, kUpPHI);
+  fph_reco_theta = MakeHisto<TH1F>("reco_theta", "", kBinsTHETA, kLoTHETA, kUpTHETA);
+  fph_reco_tx    = MakeHisto<TH1F>("reco_tx", "", kBinsTX, kLoTX, kUpTX);
+  fph_reco_ty    = MakeHisto<TH1F>("reco_ty", "", kBinsTY, kLoTY, kUpTY);
+  fph_reco_fhitR = MakeHisto<TH1F>("reco_fhitR", "", kBinsFHITR, kLoFHITR, kUpFHITR);
+  fph_reco_nhits = MakeHisto<TH1F>("reco_nhits", "", kBinsNHITS, kLoNHITS, kUpNHITS);
+  // TODO: ...
+
+  fph_reco_p->SetTitle("Total momentum of reconstructed track;p_{reco} [GeV/c]");
+  fph_reco_pt->SetTitle("Transverse momentum of reconstructed track;p_{T}_{reco} [GeV/c]");
+  fph_reco_phi->SetTitle("Azimuthal angle of reconstructed track;#phi_{reco} [rad]");
+  fph_reco_theta->SetTitle("Polar angle of reconstructed track;#theta_{reco} [rad]");
+  fph_reco_tx->SetTitle("Slope along x-axis of reconstructed tracks;t_{x}");
+  fph_reco_ty->SetTitle("Slope along y-axis of reconstructed tracks;t_{y}");
+  fph_reco_fhitR->SetTitle("Distance of the first hit from z-axis for reconstructed tracks");
+  fph_reco_nhits->SetTitle("Hit number of reconstructed tracks");
+
+  if (fbUseMC) {
+    fph_reco_pMC     = MakeHisto<TH1F>("reco_pMC", "", kBinsP, kLoP, kUpP);
+    fph_reco_yMC     = MakeHisto<TH1F>("reco_yMC", "", kBinsY, kLoY, kUpY);
+    fph_reco_pMC_yMC = MakeHisto<TH2F>("reco_pMC_yMC", "", kBinsY, kLoY, kUpY, kBinsP, kLoP, kUpP);
+    fph_reco_phiMC   = MakeHisto<TH1F>("reco_phiMC", "", kBinsPHI, kLoPHI, kUpPHI);
+    fph_reco_thetaMC = MakeHisto<TH1F>("reco_thetaMC", "", kBinsTHETA, kLoTHETA, kUpTHETA);
+
+    fph_reco_pMC->SetTitle("MC total momentum of reconstructed track;p_{MC} [GeV/c]");
+    fph_reco_yMC->SetTitle("MC rapidity of reconstructed track;y_{MC}");
+    fph_reco_pMC_yMC->SetTitle("Transverse momentum of reconstructed track;y_{MC};p_{T}_{MC} [GeV/c]");
+    fph_reco_phiMC->SetTitle("Azimuthal angle of reconstructed track;#phi_{reco} [rad]");
+    fph_reco_thetaMC->SetTitle("Polar angle of reconstructed track;#theta_{reco} [rad]");
+
+    fph_mc_pMC     = MakeHisto<TH1F>("mc_pMC", "", kBinsP, kLoP, kUpP);
+    fph_mc_yMC     = MakeHisto<TH1F>("mc_yMC", "", kBinsY, kLoY, kUpY);
+    fph_mc_pMC_yMC = MakeHisto<TH2F>("mc_pMC_yMC", "", kBinsY, kLoY, kUpY, kBinsP, kLoP, kUpP);
+
+    fph_mc_pMC->SetTitle("MC total momentum of MC tracks;p_{MC} [GeV/c]");
+    fph_mc_yMC->SetTitle("MC rapidity of MC tracks;y_{MC}");
+    fph_mc_pMC_yMC->SetTitle("MC total momentum vs. MC rapidity of MC tracks;y_{MC};p_{MC} [GeV/c]");
+
+    fpFitQaFirstHit = std::make_unique<TrackFitQa>("fst_hit", fsPrefix, fpFolderRoot);
+    fpFitQaFirstHit->SetTitle("First hit");
+    fpFitQaFirstHit->Init();
+
+    fpFitQaLastHit = std::make_unique<TrackFitQa>("lst_hit", fsPrefix, fpFolderRoot);
+    fpFitQaLastHit->SetTitle("Last hit");
+    fpFitQaLastHit->Init();
+
+    fpFitQaVertex = std::make_unique<TrackFitQa>("vertex", fsPrefix, fpFolderRoot);
+    fpFitQaVertex->SetTitle("Vertex");
+    fpFitQaVertex->Init();
+  }
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+void TrackTypeQa::FillRecoTrack(int iTrkReco, double weight)
+{
+  const auto& recoTrack = (*fpvRecoTracks)[iTrkReco];
+  fph_reco_p->Fill(recoTrack.GetP(), weight);
+  fph_reco_pt->Fill(recoTrack.GetPt(), weight);
+  fph_reco_phi->Fill(recoTrack.GetPhi(), weight);
+  fph_reco_theta->Fill(recoTrack.GetTheta(), weight);
+  fph_reco_tx->Fill(recoTrack.GetTx(), weight);
+  fph_reco_ty->Fill(recoTrack.GetTy(), weight);
+  if (fbUseMC) {
+    int iTrkMC = recoTrack.GetMatchedMCTrackIndex();
+    if (iTrkMC > -1) {
+      /// TODO: fill mass hypothesis into CbmL1Track
+      const auto& mcTrack = fpMCData->GetTrack(iTrkMC);
+      fph_reco_pMC->Fill(mcTrack.GetP(), weight);
+      fph_reco_yMC->Fill(mcTrack.GetRapidity(), weight);
+      fph_reco_pMC_yMC->Fill(mcTrack.GetRapidity(), mcTrack.GetP(), weight);
+
+      // *****************************
+      // ** Track fit parameters QA **
+      // *****************************
+
+      int nTimeMeasurements = 0;
+      for (int iH : recoTrack.GetHitIndexes()) {
+        int iSt = (*fpvHits)[iH].GetStationId();
+        if (fpParameters->GetStation(iSt).timeInfo) { ++nTimeMeasurements; }
+      }
+      bool isTimeFitted = (nTimeMeasurements > 1);
+
+      // ** First hit **
+      int iHfst = recoTrack.GetFirstHitIndex();
+      int iPfst = (*fpvHits)[iHfst].GetMCPointIndex();
+      if (iPfst > -1) {
+        const auto& mcPoint = fpMCData->GetPoint(iPfst);
+        L1TrackPar trPar(recoTrack.T, recoTrack.C);
+        fpFitQaFirstHit->Fill(trPar, mcPoint, isTimeFitted, weight);
+      }
+
+      // ** Last hit **
+      int iHlst = recoTrack.GetLastHitIndex();
+      int iPlst = (*fpvHits)[iHlst].GetMCPointIndex();
+      if (iPlst > -1) {
+        const auto& mcPoint = fpMCData->GetPoint(iPlst);
+        L1TrackPar trPar(recoTrack.TLast, recoTrack.CLast);
+        fpFitQaLastHit->Fill(trPar, mcPoint, isTimeFitted, weight);
+      }
+    }
+  }
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+void TrackTypeQa::FillMCTrack(int iTrkMC, double weight)
+{
+  assert(fbUseMC);
+
+  const MCTrack& mcTrack = fpMCData->GetTrack(iTrkMC);
+  fph_mc_pMC->Fill(mcTrack.GetP(), weight);
+  fph_mc_yMC->Fill(mcTrack.GetRapidity(), weight);
+  fph_mc_pMC_yMC->Fill(mcTrack.GetRapidity(), mcTrack.GetP(), weight);
+}
diff --git a/reco/L1/qa/CbmCaTrackTypeQa.h b/reco/L1/qa/CbmCaTrackTypeQa.h
new file mode 100644
index 0000000000..c54479019f
--- /dev/null
+++ b/reco/L1/qa/CbmCaTrackTypeQa.h
@@ -0,0 +1,209 @@
+/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergei Zharko [committer] */
+
+/// @file   CbmCaTrackTypeQa.h
+/// @brief  QA submodule for different track types (header)
+/// @since  27.03.2023
+/// @author Sergei Zharko <s.zharko@gsi.de>
+
+#ifndef CbmCaTrackTypeQa_h
+#define CbmCaTrackTypeQa_h 1
+
+#include "CbmCaTrackFitQa.h"
+#include "CbmL1Hit.h"
+#include "CbmQaIO.h"
+
+#include <map>
+#include <string>
+
+#include "L1Parameters.h"
+
+// Forward declarations
+namespace ca::tools
+{
+  class MCData;
+}
+class CbmL1Track;
+class CbmL1HitDebugInfo;
+
+class TH1F;
+class TH2F;
+class TProfile;
+
+namespace cbm::ca
+{
+  /// @brief Unified QA for a group of tracks
+  ///
+  /// The class provides set of histograms, counters, and efficiencies for monitoring tracks of a particular type
+  /// (reco_fast_long, mc_pi, reco_ghost)
+  class TrackTypeQa : public CbmQaIO {
+  public:
+    /// @brief Constructor
+    /// @param typeName   Name of tracks type (used as a prefix for histogram)
+    /// @note  Track type is stored as a root directory name in the CbmQaIO base class
+    /// @param prefixName Name of unique prefix
+    /// @param bUseMC     Flag: true - MC is used
+    /// @param pParentFolder Pointer to parent folder
+    TrackTypeQa(const char* typeName, const char* prefixName, bool bUseMC, TFolder* pParentFolder);
+
+    /// @brief Destructor
+    ~TrackTypeQa() = default;
+
+    /// @brief Copy constructor
+    TrackTypeQa(const TrackTypeQa&) = delete;
+
+    /// @brief Move constructor
+    TrackTypeQa(TrackTypeQa&&) = delete;
+
+    /// @brief Copy assignment operator
+    TrackTypeQa& operator=(const TrackTypeQa&) = delete;
+
+    /// @brief Move assignment operator
+    TrackTypeQa& operator=(TrackTypeQa&&) = delete;
+
+    /// @brief Gets title of track group
+    const char* GetTitle() const { return fsTitle; }
+
+    /// @brief Initializes histograms
+    void Init();
+
+    /// @brief Fills histograms with various track information
+    /// @note  To be called in the loop over reconstructed tracks full sample
+    /// @param iTrkReco  Index of reconstructed track
+    /// @param weight    Weight
+    void FillRecoTrack(int iTrkReco, double weight = 1);
+
+    /// @brief Fills histograms with mc track information
+    /// @note  To be called in the loop over MC tracks full sample
+    /// @param iTrkMC  Index of MC track
+    /// @param weight  Weight
+    void FillMCTrack(int iTrkMC, double weight = 1);
+
+    /// @brief Registers the reconstructed tracks container
+    /// @param vTracks  Reference to reconstructed tracks container
+    void RegisterRecoTracks(L1Vector<CbmL1Track>& vTracks) { fpvRecoTracks = &vTracks; }
+
+    /// @brief Register the reconstructed hits container
+    /// @param vHits  Reference to reconstructed hits container
+    void RegisterRecoHits(L1Vector<CbmL1HitDebugInfo>& vHits) { fpvHits = &vHits; }
+
+    /// @brief Registers the MC data
+    /// @param mcData  Reference to MC data object
+    void RegisterMCData(::ca::tools::MCData& mcData) { fpMCData = &mcData; }
+
+    /// @brief Registers CA parameters object
+    /// @param pParameters  A shared pointer to the parameters object
+    void RegisterParameters(std::shared_ptr<L1Parameters>& pParameters) { fpParameters = pParameters; }
+
+    /// @brief Sets title, which is to be reflected on legends and titles
+    /// @param title  Title of track type
+    void SetTitle(const char* title) { fsTitle = title; }
+
+    // ************************
+    // ** List of histograms **
+    // ************************
+
+    // NOTE: Naming convention:
+    //  "fph_" + <"reco"/"mc"> + <quantity> + <""/"MC">. Here <"reco"/"mc"> stands for reconstructed track or true MC
+    //  track, <quantity> is a name of a quantity, versus which one wants to build the dependency, <""/"MC"> stands
+    //  for an origin of the quantity - reconstructed or true Monte-Carlo. If there are several quantities, they are
+    //  to be separated with "_" (example: "p_yMC" -- reconstructed momentum vs MC rapidity)
+
+    // ** Histograms from reconstructed information only **
+    TH1F* fph_reco_p        = nullptr;  ///< Total momentum over charge of reconstructed tracks
+    TH1F* fph_reco_pt       = nullptr;  ///< Transverse momentum over charge of reconstructed tracks
+    TH1F* fph_reco_phi      = nullptr;  ///< Azimuthal angle of reconstructed tracks
+    TH1F* fph_reco_theta    = nullptr;  ///< Polar angle of reconstructed tracks
+    TH1F* fph_reco_tx       = nullptr;  ///< Slope along x-axis of reconstructed tracks
+    TH1F* fph_reco_ty       = nullptr;  ///< Slope along y-axis of reconstructed tracks
+    TH1F* fph_reco_eta      = nullptr;  ///< Pseudo-rapidity of reconstructed tracks
+    TH1F* fph_reco_fhitR    = nullptr;  ///< Distance of the first hit from z-axis for reconstructed tracks
+    TH1F* fph_reco_nhits    = nullptr;  ///< Hit number of reconstructed tracks
+    TH1F* fph_reco_fsta     = nullptr;  ///< First station index of reconstructed tracks
+    TH1F* fph_reco_chi2_ndf = nullptr;  ///< Fit chi2 over NDF of reconstructed tracks
+
+    // ** Reconstructed track distributions, requiring MC information **
+    TH1F* fph_reco_pMC     = nullptr;  ///< MC total momentum over charge of reconstructed tracks
+    TH1F* fph_reco_yMC     = nullptr;  ///< MC rapidity of reconstructed tracks
+    TH1F* fph_reco_etaMC   = nullptr;  ///< MC pseudo-rapidity of reconstructed tracks
+    TH2F* fph_reco_p_yMC   = nullptr;  ///< Total momentum vs MC rapidity of reconstructed tracks
+    TH2F* fph_reco_pMC_yMC = nullptr;  ///< MC total momentum vs MC rapidity of reconstructed tracks
+    TH1F* fph_reco_ptMC    = nullptr;  ///< Transverse momentum over charge of reconstructed tracks
+    TH1F* fph_reco_phiMC   = nullptr;  ///< Azimuthal angle of reconstructed tracks
+    TH1F* fph_reco_thetaMC = nullptr;  ///< Polar angle of reconstructed tracks
+    TH1F* fph_reco_txMC    = nullptr;  ///< Slope along x-axis of reconstructed tracks
+    TH1F* fph_reco_tyMC    = nullptr;  ///< Slope along y-axis of reconstructed tracks
+
+    // ** MC track distributions **
+    TH1F* fph_mc_pMC     = nullptr;  ///< MC total momentum over charge of MC tracks
+    TH1F* fph_mc_yMC     = nullptr;  ///< MC rapidity of MC tracks
+    TH1F* fph_mc_etaMC   = nullptr;  ///< MC pseudo-rapidity of MC tracks
+    TH2F* fph_mc_pMC_yMC = nullptr;  ///< MC total momentum vs. MC rapidity of MC tracks
+    TH1F* fph_mc_ptMC    = nullptr;  ///< Transverse momentum over charge of reconstructed tracks
+    TH1F* fph_mc_phiMC   = nullptr;  ///< Azimuthal angle of reconstructed tracks
+    TH1F* fph_mc_thetaMC = nullptr;  ///< Polar angle of reconstructed tracks
+    TH1F* fph_mc_txMC    = nullptr;  ///< Slope along x-axis of reconstructed tracks
+    TH1F* fph_mc_tyMC    = nullptr;  ///< Slope along y-axis of reconstructed tracks
+
+    // ** Efficiencies **
+    TProfile* fph_eff_p     = nullptr;  ///< Track efficiency
+    TProfile* fph_eff_nhits = nullptr;
+    TProfile* fph_eff_pMC   = nullptr;
+
+    // ** Fit QA **
+    std::unique_ptr<TrackFitQa> fpFitQaFirstMCpoint = nullptr;
+    std::unique_ptr<TrackFitQa> fpFitQaFirstHit     = nullptr;
+    std::unique_ptr<TrackFitQa> fpFitQaLastHit      = nullptr;
+    std::unique_ptr<TrackFitQa> fpFitQaVertex       = nullptr;
+
+    // TODO: Provide integrated efficiencies
+
+  private:
+    bool fbUseMC    = false;  ///< Flag: true - MC information is used
+    TString fsTitle = "";     ///< Title of the track category
+
+    L1Vector<CbmL1Track>* fpvRecoTracks        = nullptr;  ///< Pointer to vector of reconstructed tracks
+    L1Vector<CbmL1HitDebugInfo>* fpvHits       = nullptr;  ///< Pointer to vector of reconstructed hits
+    ::ca::tools::MCData* fpMCData              = nullptr;  ///< Pointer to MC data object
+    std::shared_ptr<L1Parameters> fpParameters = nullptr;  ///< Pointer to parameters object
+
+    // **************************
+    // ** Histogram properties **
+    // **************************
+
+    // ** Binning **
+    static constexpr int kBinsP      = 150;   ///< Number of bins, total momentum
+    static constexpr double kLoP     = 0.;    ///< Lower boundary, total momentum [GeV/c]
+    static constexpr double kUpP     = 15.;   ///< Upper boundary, total momentum [GeV/c]
+    static constexpr int kBinsPT     = 100;   ///< Number of bins, transverse momentum
+    static constexpr double kLoPT    = 0.;    ///< Lower boundary, transverse momentum [GeV/c]
+    static constexpr double kUpPT    = 10.;   ///< Upper boundary, transverse momentum [GeV/c]
+    static constexpr int kBinsETA    = 40;    ///< Number of bins, pseudo-rapidity
+    static constexpr double kLoETA   = 0.;    ///< Lower boundary, pseudo-rapidity
+    static constexpr double kUpETA   = 4.;    ///< Upper boundary, pseudo-rapidity
+    static constexpr int kBinsY      = 40;    ///< Number of bins, rapidity
+    static constexpr double kLoY     = 0.;    ///< Lower boundary, rapidity
+    static constexpr double kUpY     = 4.;    ///< Upper boundary, rapidity
+    static constexpr int kBinsPHI    = 68;    ///< Number of bins, azimuthal angle
+    static constexpr double kLoPHI   = -3.2;  ///< Lower boundary, azimuthal angle [rad]
+    static constexpr double kUpPHI   = +3.2;  ///< Upper boundary, azimuthal angle [rad]
+    static constexpr int kBinsTHETA  = 68;    ///< Number of bins, polar angle
+    static constexpr double kLoTHETA = 0.;    ///< Lower boundary, polar angle [rad]
+    static constexpr double kUpTHETA = 3.2;   ///< Upper boundary, polar angle [rad]
+    static constexpr int kBinsTX     = 80;    ///< Number of bins, slope along x
+    static constexpr double kLoTX    = -2.;   ///< Lower boundary, slope along x
+    static constexpr double kUpTX    = +2.;   ///< Upper boundary, slope along x
+    static constexpr int kBinsTY     = 80;    ///< Number of bins, slope along y
+    static constexpr double kLoTY    = -2.;   ///< Lower boundary, slope along y
+    static constexpr double kUpTY    = +2.;   ///< Upper boundary, slope along y
+    static constexpr int kBinsNHITS  = 15;    ///< Number of bins, number of hits
+    static constexpr double kLoNHITS = -0.5;  ///< Lower boundary, number of hits
+    static constexpr double kUpNHITS = 14.5;  ///< Upper boundary, number of hits
+    static constexpr int kBinsFHITR  = 50;    ///< Number of bins, transverse distance of the 1st hit from z-axis
+    static constexpr double kLoFHITR = 0.;    ///< Lower boundary, transverse distance of the 1st hit from z-axis [cm]
+    static constexpr double kUpFHITR = 150.;  ///< Upper boundary, transverse distance of the 1st hit from z-axis [cm]
+  };
+}  // namespace cbm::ca
+
+#endif  // CbmCaOutput
\ No newline at end of file
-- 
GitLab