Skip to content
Snippets Groups Projects
CbmTrdModuleRecT.cxx 29.70 KiB
#include "CbmTrdModuleRecT.h"
#include "CbmTrdCluster.h"
#include "CbmTrdDigi.h"
#include "CbmTrdFASP.h"
#include "CbmTrdHit.h"
#include "CbmTrdParModDigi.h"

#include <FairLogger.h>

#include <TGeoPhysicalNode.h>

#include <TClonesArray.h>
#include <TF1.h>
#include <TGraphErrors.h>

#include <iostream>
#include <vector>

using std::cout;
using std::endl;
using std::vector;

Double_t CbmTrdModuleRecT::fgDT[]      = {4.181e-6, 1586, 24};
TGraphErrors* CbmTrdModuleRecT::fgEdep = NULL;
TGraphErrors* CbmTrdModuleRecT::fgT    = NULL;
TF1* CbmTrdModuleRecT::fgPRF           = NULL;
//_______________________________________________________________________________
CbmTrdModuleRecT::CbmTrdModuleRecT()
  : CbmTrdModuleRec()
  , fConfigMap(0)
  , fT0(0)
  , fBuffer()
  , vs()
  , vse()
  , vt()
  , vx()
  , vxe() {}

//_______________________________________________________________________________
CbmTrdModuleRecT::CbmTrdModuleRecT(Int_t mod, Int_t ly, Int_t rot)
  : CbmTrdModuleRec(mod, ly, rot)
  , fConfigMap(0)
  , fT0(0)
  , fBuffer()
  , vs()
  , vse()
  , vt()
  , vx()
  , vxe() {
  //printf("AddModuleT @ %d\n", mod); Config(1,0);
  SetNameTitle(Form("TrdRecT%d", mod),
               "Reconstructor for triangular read-out.");
}

//_______________________________________________________________________________
CbmTrdModuleRecT::~CbmTrdModuleRecT() {}

//_______________________________________________________________________________
Bool_t CbmTrdModuleRecT::AddDigi(const CbmTrdDigi* d, Int_t id) {
  /* Add digi to cluster fragments. At first clusters are ordered on pad rows and time. 
 * No channel ordering is assumed. The time condition for a digi to enter a cluster 
 * chunk is to have abs(dt)<5 wrt cluster t0 
 */

  if (CWRITE()) cout << "add @" << id << " " << d->ToString();

  Int_t ch = d->GetAddressChannel(), col, row = GetPadRowCol(ch, col), dtime;
  Double_t t, r = d->GetCharge(t, dtime);
  Int_t tm = d->GetTimeDAQ() - fT0, terminator(0);
  if (dtime < 0)
    tm += dtime;  // correct for the difference between tilt and rect
  if (r < 1)
    terminator = 1;
  else if (t < 1)
    terminator = -1;

  if (CWRITE())
    printf(
      "row[%2d] col[%2d] tm[%2d] terminator[%d]\n", row, col, tm, terminator);
  CbmTrdCluster* cl(NULL);

  // get the link to saved clusters
  std::map<Int_t, std::list<CbmTrdCluster*>>::iterator it = fBuffer.find(row);
  // check for saved
  if (it != fBuffer.end()) {
    Bool_t kINSERT(kFALSE);
    std::list<CbmTrdCluster*>::iterator itc = fBuffer[row].begin();
    for (; itc != fBuffer[row].end(); itc++) {
      if (CWRITE()) cout << (*itc)->ToString();

      UShort_t tc = (*itc)->GetStartTime();
      Int_t dt    = tc - tm;

      if (dt < -5)
        continue;
      else if (dt < 5) {
        if (CWRITE()) printf("match time dt=%d\n", dt);
        if ((*itc)->IsChannelInRange(ch) == 0) {
          if (!(*itc)->AddDigi(id, ch, terminator, dt)) break;
          kINSERT = kTRUE;
          if (CWRITE()) cout << "          => Cl (Ad) " << (*itc)->ToString();
          break;
        }
      } else {
        if (CWRITE()) printf("break for time dt=%d\n", dt);
        break;
      }
    }

    if (!kINSERT) {
      if (itc != fBuffer[row].end() && itc != fBuffer[row].begin()) {
        itc--;
        fBuffer[row].insert(
          itc, cl = new CbmTrdCluster(fModAddress, id, ch, row, tm));
        if (CWRITE()) cout << "          => Cl (I) " << cl->ToString();
      } else {
        fBuffer[row].push_back(
          cl = new CbmTrdCluster(fModAddress, id, ch, row, tm));
        if (CWRITE()) cout << "          => Cl (Pb) " << cl->ToString();
      }
      cl->SetTrianglePads();
      if (terminator < 0)
        cl->SetProfileStart();
      else if (terminator > 0)
        cl->SetProfileStop();
    }
  } else {
    fBuffer[row].push_back(cl =
                             new CbmTrdCluster(fModAddress, id, ch, row, tm));
    cl->SetTrianglePads();
    if (terminator < 0)
      cl->SetProfileStart();
    else if (terminator > 0)
      cl->SetProfileStop();
    if (CWRITE()) cout << "          => Cl (Nw) " << cl->ToString();
  }

  return kTRUE;
}
//_______________________________________________________________________________
Int_t CbmTrdModuleRecT::GetOverThreshold() const {
  Int_t nch(0);
  for (std::map<Int_t, std::list<CbmTrdCluster*>>::const_iterator ir =
         fBuffer.cbegin();
       ir != fBuffer.cend();
       ir++) {
    for (std::list<CbmTrdCluster*>::const_iterator itc = (*ir).second.cbegin();
         itc != (*ir).second.cend();
         itc++)
      nch += (*itc)->GetNCols();
  }
  return nch;
}

//_______________________________________________________________________________
Int_t CbmTrdModuleRecT::FindClusters() {
  CbmTrdCluster* cl(NULL);

  // get the link to saved clusters
  Int_t ncl(0);
  std::list<CbmTrdCluster*>::iterator itc0, itc1;
  for (std::map<Int_t, std::list<CbmTrdCluster*>>::iterator ir =
         fBuffer.begin();
       ir != fBuffer.end();
       ir++) {
    itc0 = (*ir).second.begin();
    while ((*ir).second.size() > 1
           && itc0 != (*ir).second.end()) {  // try merge clusters
      itc1 = itc0;
      itc1++;

      Bool_t kMERGE = kFALSE;
      while (itc1 != (*ir).second.end()) {
        if (CWRITE())
          cout << "  base cl[0] : " << (*itc0)->ToString()
               << "     + cl[1] : " << (*itc1)->ToString();
        if ((*itc0)->Merge((*itc1))) {
          if (CWRITE()) cout << "  SUM        : " << (*itc0)->ToString();
          kMERGE = kTRUE;
          delete (*itc1);
          itc1 = (*ir).second.erase(itc1);
          if (itc1 == (*ir).second.end()) break;
        } else
          itc1++;
      }
      if (!kMERGE) itc0++;
    }

    for (itc0 = (*ir).second.begin(); itc0 != (*ir).second.end(); itc0++) {
      cl = (*itc0);
      cl = new ((*fClusters)[ncl++]) CbmTrdCluster(*cl);
      cl->SetTrianglePads();
      delete (*itc0);
    }
  }
  fBuffer.clear();

  //printf("fClusters[%p] nCl[%d]\n", (void*)fClusters, fClusters->GetEntriesFast());
  //LOG(info) << GetName() << "::FindClusters : " << ncl;
  return ncl;
}

//_______________________________________________________________________________
Bool_t CbmTrdModuleRecT::MakeHits() { return kTRUE; }

//_______________________________________________________________________________
Bool_t CbmTrdModuleRecT::Finalize() {
  /*  Steering routine for classifying hits and apply further analysis
 * -> hit deconvolution (see Deconvolute)
 * -> hit merging for row-cross (see RowCross)
 */

  //   Int_t nhits=fHits->GetEntriesFast();
  //   //if(CWRITE())
  //     LOG(info) << "\n"<<GetName()<<"::Finalize("<<nhits<<")";
  //   CbmTrdHit *hit(NULL), *hitp(NULL);
  //   for(Int_t ih(0); ih<nhits; ih++){
  //     hit = (CbmTrdHit*)((*fHits)[ih]);
  //     for(Int_t jh(ih+1); jh<nhits; jh++){
  //       hitp = (CbmTrdHit*)((*fHits)[jh]);
  //       //if(CWRITE())
  //       cout<<ih<<" "<<hit->ToString();
  //       cout<<"-> "<<jh<<"  "<<hitp->ToString();
  //     }
  //   }
  return kTRUE;
}

#include <TCanvas.h>
#include <TH1.h>
#define NANODE 9
//_______________________________________________________________________________
CbmTrdHit* CbmTrdModuleRecT::MakeHit(Int_t ic,
                                     const CbmTrdCluster* cl,
                                     std::vector<const CbmTrdDigi*>* digis) {
  if (!fgEdep) {  // first use initialization of PRF helppers
    LOG(info) << GetName() << "::MakeHit: Init static helpers. ";
    fgEdep = new TGraphErrors;
    fgEdep->SetLineColor(kBlue);
    fgEdep->SetLineWidth(2);
    fgT = new TGraphErrors;
    fgT->SetLineColor(kBlue);
    fgT->SetLineWidth(2);
    fgPRF = new TF1("prf", "[0]*exp(-0.5*((x-[1])/[2])**2)", -10, 10);
    fgPRF->SetLineColor(kRed);
    fgPRF->SetParNames("E", "x", "prf");
  }
  TH1* hf(NULL);
  TCanvas* cvs(NULL);
  if (CDRAW()) {
    cvs = new TCanvas("c", "TRD Anode Hypothesis", 10, 600, 1000, 500);
    cvs->Divide(2, 1, 1.e-5, 1.e-5);
    TVirtualPad* vp = cvs->cd(1);
    vp->SetLeftMargin(0.06904908);
    vp->SetRightMargin(0.009969325);
    vp->SetTopMargin(0.01402806);
    vp = cvs->cd(2);
    vp->SetLeftMargin(0.06904908);
    vp->SetRightMargin(0.009969325);
    vp->SetTopMargin(0.01402806);
    hf = new TH1I("hf", ";x [pw];s [ADC chs]", 100, -3, 3);
    hf->GetYaxis()->SetRangeUser(-50, 4500);
    hf->Draw("p");
  }

  if (CWRITE()) cout << cl->ToString();

  // check cluster integrity and do digi merging if needed
  vector<Bool_t> vmask(digis->size(), 0);  // masks in case of merged Digis
  vector<CbmTrdDigi*> vdgM;
  if (cl->GetNCols() != digis->size() && !MergeDigis(digis, &vdgM, &vmask)) {
    cout << cl->ToString();
    for (vector<const CbmTrdDigi*>::iterator i = digis->begin();
         i != digis->end();
         i++)
      cout << (*i)->ToString();
    return NULL;
  }
  // Read in all digis information()
  ULong64_t t0 = fT0 + cl->GetStartTime();  // absolute hit time (prompt signal)
  Int_t n0(0), ovf(0), cM;
  if (!(n0 = LoadDigis(digis, &vdgM, &vmask, t0, cM))) {
    cout << cl->ToString();
    for (vector<const CbmTrdDigi*>::iterator i = digis->begin();
         i != digis->end();
         i++)
      cout << (*i)->ToString();
    return NULL;
  }
  if (n0 < 0) {
    ovf = 1;
    n0 *= -1;
  }

  // analyse digis topology; no of signal types, maximum, etc
  Bool_t tM(kTRUE);  // maximum type tilt=1; rect=0
  Int_t nL(0);       // signal index for the max signal
  Int_t col, row = GetPadRowCol(cl->GetStartCh(), col);
  Double_t max(0.),  // maximum signal
    LS(0.),          // left side sum of signals
    S(0.);           // sum of signals
  Int_t nr(0), nt(0);
  for (Int_t is(1); is <= n0; is++) {
    if (vxe[is] > 0) {  // select tilted coupling
      nt++;
      S += vs[is];
      if (vs[is] > max) {
        max = vs[is];
        tM  = kTRUE;
        nL  = is;
        LS += vs[is];
      }
    } else {  // select rectangular coupling
      nr++;
      S += vs[is];
      if (vs[is] > max) {
        max = vs[is];
        tM  = kFALSE;
        nL  = is;
        LS += vs[is];
      }
    }
  }
  col += cM;
  S -= LS;
  LS -= max;
  // evaluate asymmetry
  Int_t lr(0),        // max signal left-right asymmetry wrt central pad
    tr(0),            // left-right innequality for symmetric clusters
    nR(n0 + 1 - nL);  // no of signals to the right of maximum
  if (nL < nR)
    lr = 1;
  else if (nL > nR)
    lr = -1;
  if (!lr && (n0 % 2)) tr = (LS < S ? -1 : 1);
  // compute x and y offset from center pad
  Double_t dx(0.), dy(0.), edx(0.21650635),
    edy(0.77942286);  // fixed error parametrization
  switch (n0) {
    case 1:
      if (nt) {
        dx = -0.5;
        dy = 0;
      }  // T
      else {
        dx = 0.5;
        dy = 0;
      }  // R
      break;
    case 2:
      if (cl->HasOpenStart() && cl->HasOpenStop()) {
        dx = cM ? -1. : 0.;
        dy = -0.5;
      }  // RT
      else {
        dx = 0.;
        dy = 0.5;
      }  // TR
      break;
    case 3:
      if (tM && lr) {
        dx = cM ? -1. : 0.;
        dy = GetYoffset(n0);
      }  // TRT asymm
      else if (!tM && !lr) {
        dx = 0.;
        dy = GetYoffset(n0);
      }  // TRT symm
      else if (tM && !lr) {
        dx = GetXoffset(n0);
        dy = 0.;
      }  // RTR symm
      else if (!tM && lr) {
        dx = GetXoffset(n0);
        dy = cM ? 0.5 : -0.5;
      }  // RTR asymm
      break;
    default:
      dx = GetXoffset(n0);
      dy = GetYoffset(n0);
      break;
  }
  if (dx < -0.5
      && cM > 0) {  // shift graph representation to fit dx[pw] in [-0.5, 0.5]
    dx += 1.;
    col -= 1;
    for (UInt_t idx(0); idx < vx.size(); idx++)
      vx[idx] += 1;
  }
  if (dx > 0.5) {  // dirty fix for compound clusters TODO
    Int_t ishift = Int_t(dx - 0.5) + 1;
    dx -= ishift;
    col += ishift;
    for (UInt_t idx(0); idx < vx.size(); idx++)
      vx[idx] -= ishift;
  }
  dy = dx - dy;  // only on natural scalling !
  // go to cm scale
  dx *= fDigiPar->GetPadSizeX(0);
  dy *= fDigiPar->GetPadSizeY(0);

  // apply systematic correction for x (MC derived)
  Int_t typ = 0;                                    // [0] center hit type
                                                    // [1] side hit type
  if ((n0 == 5 && ((tM && !lr) || (!tM && lr))) ||  // TRTRT symm/asymm
      n0 == 4 || (n0 == 3 && ((tM && !lr) || (!tM && lr != 0))))
    typ = 1;  // RTR symm/asymm
  Double_t xcorr(0.);
  Int_t nbins((NBINSCORRX - 1) >> 1), ii = nbins + TMath::Nint(dx / fgCorrXdx),
                                      i0, i1;
  if (ii < 0 || ii >= NBINSCORRX)
    LOG(warn) << GetName() << "::MakeHit : Idx " << ii
              << " outside range for displacement " << dx << ".";
  else {
    if (dx < fgCorrXdx * ii) {
      i0 = TMath::Max(0, ii - 1);
      i1 = ii;
    } else {
      i0 = ii;
      i1 = TMath::Min(NBINSCORRX - 1, ii + 1);
    }
    Double_t DDx = (fgCorrXval[typ][i1] - fgCorrXval[typ][i0]),
             a = DDx / fgCorrXdx, b = fgCorrXval[typ][i0] - DDx * (i0 - nbins);
    xcorr = 0.1 * (b + a * dx);
  }
  dx += xcorr;
  dy += xcorr;
  if (dx > 0.5 * fDigiPar->GetPadSizeX(0))
    dx = 0.5 * fDigiPar->GetPadSizeX(0);
  else if (dx < -0.5 * fDigiPar->GetPadSizeX(0))
    dx = -0.5 * fDigiPar->GetPadSizeX(0);

  if (dy > 0.5 * fDigiPar->GetPadSizeY(0)) {  //
    //printf("BEFORE dy[%+6.4f] dx[%+6.4f] {n[%d] max[%c] lr[%+d]}\n", dy, dx, n0, tM?'T':'R', lr);
    dy -= fDigiPar->GetPadSizeY(0);
  }
  if (dy < -0.5 * fDigiPar->GetPadSizeY(0)) {  //
    //printf("(BEFORE) dy[%+6.4f] dx[%+6.4f] {n[%d] max[%c] lr[%+d]}\n", dy, dx, n0, tM?'T':'R', lr);
    dy += fDigiPar->GetPadSizeY(0);
  }

  // process y offset
  // apply systematic correction for y (MC derived)
  Float_t fdy(1.), yoff(0.);
  switch (n0) {
    case 3:
      fdy  = fgCorrYval[0][0];
      yoff = fgCorrYval[0][1];
      if (tM && !lr)
        dy -= tr * 0.5 * fDigiPar->GetPadSizeY(0);
      else if (lr)
        dy -= 0.5 * fDigiPar->GetPadSizeY(0);
      ;
      break;
    case 4:
      fdy  = fgCorrYval[1][0];
      yoff = fgCorrYval[1][1];
      if ((!tM && lr == 1) || (tM && lr == -1)) yoff *= -1;
      break;
    case 5:
    case 7:
    case 9:
    case 11:
      fdy  = fgCorrYval[2][0];
      yoff = fgCorrYval[2][1];
      break;
    case 6:
    case 8:
    case 10:
      fdy  = fgCorrYval[3][0];
      yoff = fgCorrYval[3][1];
      if ((!tM && lr == 1) || (tM && lr == -1)) yoff *= -1;
      break;
  }
  dy *= fdy;
  dy += yoff;
  if (dy > 0.5 * fDigiPar->GetPadSizeY(0))
    dy = 0.5 * fDigiPar->GetPadSizeY(0);
  else if (dy < -0.5 * fDigiPar->GetPadSizeY(0))
    dy = -0.5 * fDigiPar->GetPadSizeY(0);

  // get anode wire offset
  Int_t ia(0);
  Float_t ya;  //  anode position in local pad coordinates
  for (; ia <= NANODE; ia++) {
    ya = -1.35 + ia * 0.3;
    if (dy > ya + 0.15) continue;
    break;
  }

  // Error parametrization X : parabolic model on cl size
  Double_t parX[] = {0.713724, -0.318667, 0.0366036};
  Int_t nex       = TMath::Min(n0, 7);
  edx             = parX[0] + parX[1] * nex + parX[2] * nex * nex;
  Double_t parY[] = {0.0886413, 0., 0.0435141};
  edy             = parY[0] + parY[2] * dy * dy;

  if (CWRITE()) {
    printf("row[%2d] col[%2d] sz[%d%c] M[%d%c] dx[mm]=%6.3f dy[mm]=%6.3f "
           "t0[clk]=%llu OVF[%c]\n",
           row,
           col,
           n0,
           (lr ? (lr < 0 ? 'L' : 'R') : 'C'),
           cM,
           (tM ? 'T' : 'R'),
           10 * dx,
           10 * dy,
           t0,
           (ovf ? 'y' : 'n'));
    for (UInt_t idx(0); idx < vs.size(); idx++) {
      printf("%2d%cdt[%2d] s[ADC]=%6.1f+-%6.1f x[pw]=%5.2f+-%5.2f\n",
             idx,
             (UInt_t(nL) == idx ? '*' : ' '),
             vt[idx],
             vs[idx],
             vse[idx],
             vx[idx],
             vxe[idx]);
    }
  }

  // compute energy
  for (UInt_t idx(0); idx < vs.size(); idx++) {
    if (vxe[idx] > 0) {
      fgEdep->SetPoint(idx, vx[idx] + dy / fDigiPar->GetPadSizeY(0), vs[idx]);
      fgEdep->SetPointError(idx, vxe[idx], vse[idx]);
    } else {
      fgEdep->SetPoint(idx, vx[idx], vs[idx]);
      fgEdep->SetPointError(idx, vxe[idx], vse[idx]);
    }
  }
  Double_t xc = vx[n0 + 2];
  for (Int_t ip(vs.size()); ip < fgEdep->GetN(); ip++) {
    //fgEdep->RemovePoint(ip);
    xc += 0.5;
    fgEdep->SetPoint(ip, xc, 0);
    fgEdep->SetPointError(ip, 0., 300);
  }
  if (CWRITE()) fgEdep->Print();

  Double_t e(0.), chi(100), xlo(*vx.begin()), xhi(*vx.rbegin());
  fgPRF->SetParameter(0, max);
  fgPRF->FixParameter(1, dx / fDigiPar->GetPadSizeX(0));
  fgPRF->SetParameter(2, 0.65);
  fgPRF->SetParLimits(2, 0.45, 10.5);
  fgEdep->Fit(fgPRF, "QBN", "goff", xlo - 0.5, xhi + 0.5);
  if (!fgPRF->GetNDF()) return NULL;
  chi = fgPRF->GetChisquare() / fgPRF->GetNDF();
  e   = fgPRF->Integral(xlo - 0.5, xhi + 0.5);

  // apply MC correction
  Float_t gain0 = 210.21387;  //(XeCO2 @ 1900V)
  //   Float_t grel[3] = {1., 0.98547803, 0.93164071},
  //           goff[3][3] = {
  //             {0.05714, -0.09, -0.09},
  //             {0., -0.14742, -0.14742},
  //             {0., -0.29, -0.393}
  //           };
  //   Int_t ian=0;
  //   if(TMath::Abs(dy)<=0.3) ian=0;
  //   else if(TMath::Abs(dy)<=0.6) ian=1;
  //   else if(TMath::Abs(dy)<=0.9) ian=2;
  //   Int_t isize=0;
  //   if(n0<=3) isize=0;
  //   else if(n0<=4) isize=1;
  //   else isize=2;
  Float_t gain = gain0;  //*grel[ian];
  e /= gain;             // apply effective gain
  //e+=goff[ian][isize];  // apply missing energy offset

  TVector3 local_pad, local_pad_err;
  Int_t srow, sector = fDigiPar->GetSectorRow(row, srow);
  fDigiPar->GetPadPosition(sector, col, srow, local_pad, local_pad_err);
  //printf("r[%2d] c[%2d] max[%d] lr[%d] n0[%d] cM[%d] nM[%d] dx[%7.4f] dy[%7.4f] loc[%6.2f %6.2f %6.2f] err[%6.2f %6.2f %6.2f] e[%f] chi[%f]\n", row, col, mtyp, lr, n0, cM, nM, dx, dy, local_pad[0], local_pad[1], local_pad[2], local_pad_err[0], local_pad_err[1], local_pad_err[2], e, chi);
  Double_t local[3] = {local_pad[0] + dx, local_pad[1] + dy, local_pad[2]},
           global[3], globalErr[3] = {edx, edy, 0.};
  LocalToMaster(local, global);

  // process time profile
  for (Int_t idx(1); idx <= n0; idx++) {
    Double_t dtFEE = fgDT[0] * (vs[idx] - fgDT[1]) * (vs[idx] - fgDT[1])
                     / CbmTrdDigi::Clk(CbmTrdDigi::kFASP);
    if (vxe[idx] > 0) vx[idx] += dy / fDigiPar->GetPadSizeY(0);
    fgT->SetPoint(idx - 1, vx[idx], vt[idx] - dtFEE);
  }
  xc = vx[n0 + 2];
  for (Int_t ip(n0); ip < fgT->GetN(); ip++) {
    fgT->SetPoint(ip, xc, 0);
    xc += 0.5;
  }

  Double_t time(-21.),
    edt(26.33),   // should be parametrized as function of da TODO
    tdrift(100);  // should depend on Ua
  if (n0 > 1 && (fgT->Fit("pol1", "QC", "goff") == 0)) {
    TF1* f = fgT->GetFunction("pol1");
    time   = f->GetParameter(0) - fgDT[2];
    if (TMath::IsNaN(time)) time = -21;
    //dtime += TMath::Abs(f->GetParameter(1)*(vx[n0+1] - vx[1]));
  }

  if (CDRAW()) {
    Double_t rangex(vx[0] - 0.25), rangeX(vx[n0 + 2] + 0.25);
    cvs->cd(1);
    hf->Draw("p");
    hf->GetXaxis()->SetRangeUser(rangex, rangeX);
    hf->SetTitle(Form(
      "%d[%d] row[%d] col[%2d] an[%+d] m[%+4.2f] s[%4.2f] E[%7.2f] chi2[%7.2f]",
      ic,
      Int_t(vs.size()),
      row,
      col,
      ia,
      fgPRF->GetParameter(1),
      fgPRF->GetParameter(2),
      e,
      chi));
    hf->GetXaxis()->SetRangeUser(rangex, rangeX);
    hf->GetYaxis()->SetRangeUser(-100., 4500);
    fgEdep->Draw("pl");
    fgPRF->Draw("same");

    cvs->cd(2);
    hf = (TH1*) hf->DrawClone("p");
    hf->GetXaxis()->SetRangeUser(rangex, rangeX);
    hf->GetYaxis()->SetRangeUser(-10, 20);
    //hf->SetTitle(Form("%d row[%d] col[%2d] an[%+d] m[%+4.2f] s[%4.2f] E[%7.2f] chi2[%7.2f]", ic, row, col, ia, fgPRF->GetParameter(1), fgPRF->GetParameter(2), fgPRF->Integral(xlo, xhi), fgPRF->GetChisquare()/fgPRF->GetNDF()));
    //       hf->GetXaxis()->SetRangeUser(xlo-0.25, xhi+0.25);
    //       //hf->GetYaxis()->SetRangeUser(0., 4500);
    fgT->Draw("pl");
    cvs->Modified();
    cvs->Update();
    cvs->SaveAs(Form("cl_%02d_A%d.gif", ic, ia));
  }

  Int_t nofHits  = fHits->GetEntriesFast();
  CbmTrdHit* hit = new ((*fHits)[nofHits])
    CbmTrdHit(fModAddress,
              global,
              globalErr,
              0.,  // sxy chi,
              ic,
              e,  // energy
              CbmTrdDigi::Clk(CbmTrdDigi::kFASP) * (t0 + time) - tdrift + 30.29,
              edt);
  hit->SetClassType();
  hit->SetMaxType(tM);
  if (ovf) hit->SetOverFlow();
  if (CWRITE()) cout << hit->ToString();
  return hit;
}

//_______________________________________________________________________________
Double_t CbmTrdModuleRecT::GetXoffset(Int_t n0) const {
  Double_t dx(0.), R(0.);
  for (Int_t ir(1); ir <= n0; ir++) {
    if (vxe[ir] > 0) continue;  // select rectangular coupling
    R += vs[ir];
    dx += vs[ir] * vx[ir];
  }
  if (TMath::Abs(R) > 0) return dx / R;
  LOG(warn) << GetName() << "::GetDx : Unexpected null sum.";
  return 0.;
}

//_______________________________________________________________________________
Double_t CbmTrdModuleRecT::GetYoffset(Int_t n0) const {
  Double_t dy(0.), T(0.);
  for (Int_t it(1); it <= n0; it++) {
    if (vxe[it] > 0) {  // select tilted coupling
      T += vs[it];
      dy += vs[it] * vx[it];
    }
  }
  if (TMath::Abs(T) > 0) return dy / T;
  LOG(warn) << GetName() << "::GetDy : Unexpected null sum.";
  return 0.;
}

//_______________________________________________________________________________
Int_t CbmTrdModuleRecT::LoadDigis(vector<const CbmTrdDigi*>* digis,
                                  vector<CbmTrdDigi*>* vdgM,
                                  vector<Bool_t>* vmask,
                                  ULong64_t& t0,
                                  Int_t& cM) {
  /* Load digis information in working vectors. 
 * The digis as provided by the digitizer are replaced by the merged one 
 * according to the vmask map. Digis are represented in the normal coordinate system of
 * (pad width [pw], DAQ time [clk], signal [ADC chs]) with rectangular signals at integer 
 * positions.  
 */
  vs.clear();
  vse.clear();
  vx.clear();
  vxe.clear();
  vt.clear();
  cM = 0;       // relative position of maximum signal
  Int_t n0(0),  // number of measured signals
    ovf(1),     // over-flow flag for at least one of the digis
    dt;
  Char_t ddt,            // signal time offset wrt prompt
    dt0(0);              // cluster time offset wrt arbitrary t0
  Double_t r, re(100.),  // rect signal
    t, te(100.),         // tilt signal
    err,                 // final error parametrization for signal
    xc(-0.5),            // running signal-pad position
    max(0.);             // max signal
  Int_t j(0), col0(-1), col1(0);
  const CbmTrdDigi* dg(NULL);
  vector<CbmTrdDigi*>::iterator idgM = vdgM->begin();
  for (vector<const CbmTrdDigi*>::iterator i = digis->begin();
       i != digis->end();
       i++, j++) {
    dg = (*i);
    if ((*vmask)[j]) {
      dg = (*idgM);
      idgM++;
    }
    if (CWRITE()) cout << dg->ToString();
    r = dg->GetCharge(t, dt);
    if (t0 == 0)
      t0 = dg->GetTimeDAQ();  // set arbitrary t0 to avoid double digis loop
    if (col0 < 0) GetPadRowCol(dg->GetAddressChannel(), col0);  //  initialilze
    ddt = dg->GetTimeDAQ() - t0;
    if (ddt < dt0) dt0 = ddt;

    // check column wise organization
    GetPadRowCol(dg->GetAddressChannel(), col1);
    if (col0 + j != col1) {
      LOG(error) << GetName()
                 << "::LoadDigis : digis in cluster not in increasing order !";
      return 0;
    }

    // process tilt signal
    if (t > 0) {
      err = te;
      if (t > 4094) {
        ovf = -1;
        err = 150.;
      }
      t -= CbmTrdFASP::GetBaselineCorr();
      n0++;
      if (t > max) {
        max = t;
        cM  = j;
      }
    } else
      err = 300.;
    vt.push_back(ddt);
    vs.push_back(t);
    vse.push_back(err);
    vx.push_back(xc);
    vxe.push_back(0.035);
    xc += 0.5;

    // process rect signal
    ddt += dt;
    if (ddt < dt0) dt0 = ddt;
    if (r > 0) {
      err = re;
      if (r > 4094) {
        ovf = -1;
        err = 150.;
      }
      r -= CbmTrdFASP::GetBaselineCorr();
      n0++;
      if (r > max) {
        max = r;
        cM  = j;
      }
    } else
      err = 300.;
    vt.push_back(ddt);
    vs.push_back(r);
    vse.push_back(err);
    vx.push_back(xc);
    vxe.push_back(0.);
    xc += 0.5;
  }

  // remove merged digis if they were created
  if (idgM != vdgM->end())
    LOG(warn) << GetName()
              << "::LoadDigis : not all merged digis have been consumed !";
  for (idgM = vdgM->begin(); idgM != vdgM->end(); idgM++)
    delete (*idgM);

  // add front and back anchor points if needed
  if (TMath::Abs(vs[0]) > 1.e-3) {
    xc  = vx[0];
    ddt = vt[0];
    vs.insert(vs.begin(), 0);
    vse.insert(vse.begin(), 300);
    vt.insert(vt.begin(), ddt);
    vx.insert(vx.begin(), xc - 0.5);
    vxe.insert(vxe.begin(), 0);
  }
  Int_t n(vs.size() - 1);
  if (TMath::Abs(vs[n]) > 1.e-3) {
    xc  = vx[n] + 0.5;
    ddt = vt[n];
    vs.push_back(0);
    vse.push_back(300);
    vt.push_back(ddt);
    vx.push_back(xc);
    vxe.push_back(0.035);
  }
  // recenter time and space profile
  t0 += dt0;
  for (UInt_t idx(0); idx < vx.size(); idx++) {
    vt[idx] -= dt0;
    vx[idx] -= cM;
  }
  return ovf * n0;
}

//_______________________________________________________________________________
Bool_t CbmTrdModuleRecT::MergeDigis(vector<const CbmTrdDigi*>* digis,
                                    vector<CbmTrdDigi*>* vdgM,
                                    vector<Bool_t>* vmask) {
  /* Merge digis in the cluster if their topology within it allows it although cuts in the 
 * digi merger procedure (CbmTrdFASP::WriteDigi()) were not fulfilled. 
 * Normally this are boundary signals with large time delays wrt neighbors
 */

  CbmTrdDigi* dgM(NULL);
  if (digis->size() < 2) {  // sanity check ... just in case
    LOG(warn) << GetName() << "::MergeDigis : Bad digi config for cluster :";
    return kFALSE;
  }

  Double_t r, t;
  Int_t colR, colT, dt, contor(0);
  Bool_t kFOUND(0);
  for (vector<const CbmTrdDigi*>::iterator idig = digis->begin(),
                                           jdig = idig + 1;
       jdig != digis->end();
       idig++, jdig++, contor++) {
    const CbmTrdDigi* dgT((*idig));  // tilt signal digi
    const CbmTrdDigi* dgR((*jdig));  // rect signal digi
    GetPadRowCol(dgR->GetAddressChannel(), colR);
    GetPadRowCol(dgT->GetAddressChannel(), colT);

    if (colR != colT) continue;

    dgM = new CbmTrdDigi(*dgT);
    r   = dgR->GetCharge(t, dt);
    dgT->GetCharge(t, dt);
    dt = dgR->GetTimeDAQ() - dgT->GetTimeDAQ();
    dgM->SetCharge(t, r, dt);
    Int_t rtrg(dgR->GetTriggerType() & 2), ttrg(dgT->GetTriggerType() & 1);
    dgM->SetTriggerType(rtrg | ttrg);  //merge the triggers
    if (CWRITE()) {
      cout << "MERGE" << endl;
      cout << dgT->ToString();
      cout << dgR->ToString();
      cout << "TO" << endl;
      cout << dgM->ToString();
      cout << "..." << endl;
    }
    kFOUND = 1;

    vdgM->push_back(dgM);
    (*vmask)[contor] = 1;
    jdig             = digis->erase(jdig);
    if (jdig == digis->end()) break;
  }

  if (!kFOUND) {
    LOG(warn) << GetName()
              << "::MergeDigis : Digi to pads matching failed for cluster :";
    return kFALSE;
  }
  return kTRUE;
}

Float_t CbmTrdModuleRecT::fgCorrXdx                 = 0.005;
Float_t CbmTrdModuleRecT::fgCorrXval[2][NBINSCORRX] = {
  {0.000,  0.000,  0.000,  0.000,  0.000,  0.000,  0.000,  0.000,  0.000,
   0.000,  0.000,  0.000,  0.000,  0.000,  0.000,  0.000,  0.000,  0.000,
   0.000,  0.000,  0.000,  -0.144, -0.091, -0.134, -0.185, -0.120, -0.115,
   -0.125, -0.125, -0.124, -0.124, -0.122, -0.120, -0.119, -0.116, -0.114,
   -0.113, -0.111, -0.109, -0.107, -0.105, -0.102, -0.100, -0.098, -0.097,
   -0.093, -0.091, -0.089, -0.087, -0.084, -0.082, -0.079, -0.077, -0.074,
   -0.072, -0.068, -0.065, -0.062, -0.059, -0.056, -0.053, -0.049, -0.046,
   -0.043, -0.039, -0.036, -0.032, -0.029, -0.025, -0.022, -0.018, -0.015,
   -0.011, -0.007, -0.003, 0.000,  0.003,  0.007,  0.011,  0.014,  0.018,
   0.022,  0.025,  0.029,  0.032,  0.036,  0.039,  0.043,  0.046,  0.049,
   0.053,  0.056,  0.059,  0.062,  0.065,  0.068,  0.071,  0.074,  0.077,
   0.080,  0.082,  0.084,  0.087,  0.090,  0.091,  0.094,  0.096,  0.098,
   0.100,  0.103,  0.104,  0.107,  0.108,  0.110,  0.113,  0.115,  0.116,
   0.120,  0.121,  0.121,  0.123,  0.125,  0.124,  0.127,  0.140,  0.119,
   0.114,  0.028,  0.049,  0.000,  0.000,  0.000,  0.000,  0.000,  0.000,
   0.000,  0.000,  0.000,  0.000,  0.000,  0.000,  0.000,  0.000,  0.000,
   0.000,  0.000,  0.000,  0.000,  0.000,  0.000,  0.000},
  {0.003,  0.013,  0.026,  0.039,  0.052,  0.065,  0.078,  0.091,  0.104,
   0.118,  0.132,  0.145,  0.160,  0.174,  0.189,  0.203,  0.219,  0.234,
   0.250,  0.267,  0.283,  0.301,  0.319,  0.338,  0.357,  0.375,  0.398,
   0.419,  0.440,  0.464,  0.491,  0.514,  0.541,  0.569,  0.598,  0.623,
   0.660,  0.696,  0.728,  0.763,  0.804,  0.847,  0.888,  0.930,  0.988,
   1.015,  1.076,  1.128,  1.167,  1.228,  1.297,  1.374,  1.443,  1.511,
   1.564,  0.000,  0.000,  0.000,  0.000,  0.000,  0.000,  0.000,  0.000,
   0.000,  0.000,  0.000,  0.000,  0.000,  0.000,  0.000,  0.000,  0.000,
   0.000,  0.000,  0.000,  0.000,  0.000,  0.000,  0.000,  0.000,  0.000,
   0.000,  0.000,  0.000,  0.000,  0.000,  0.000,  0.000,  0.000,  0.000,
   0.000,  -1.992, -1.884, -1.765, -1.703, -1.609, -1.572, -1.493, -1.426,
   -1.356, -1.309, -1.243, -1.202, -1.109, -1.069, -1.026, -0.970, -0.933,
   -0.881, -0.844, -0.803, -0.767, -0.721, -0.691, -0.659, -0.629, -0.596,
   -0.569, -0.541, -0.514, -0.490, -0.466, -0.441, -0.419, -0.397, -0.377,
   -0.357, -0.337, -0.319, -0.301, -0.283, -0.267, -0.250, -0.234, -0.218,
   -0.203, -0.189, -0.174, -0.160, -0.145, -0.131, -0.119, -0.104, -0.091,
   -0.078, -0.064, -0.052, -0.039, -0.026, -0.013, -0.002}};
Float_t CbmTrdModuleRecT::fgCorrYval[NBINSCORRY][2] = {{2.421729, 0.},
                                                       {1.537359, 0.483472},
                                                       {1.1752, 0.},
                                                       {1.154183, -0.090229}};

ClassImp(CbmTrdModuleRecT)