diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 2473dc39dcea837a57f5757a6547840db620fbc8..1e1684cb5e95b8ddc2d4df1dab25396a2c0a7a49 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -11,6 +11,7 @@ add_subdirectory(detectors/rich) add_subdirectory(detectors/much) add_subdirectory(detectors/tof) add_subdirectory(detectors/sts) +add_subdirectory(detectors/psd) Execute_Process(COMMAND ${ROOT_CONFIG_EXECUTABLE} --has-opengl OUTPUT_VARIABLE EveIsBuild diff --git a/core/detectors/psd/CMakeLists.txt b/core/detectors/psd/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..eab3e6de959af2a7ef92bbd8d4c8eebd06283ba7 --- /dev/null +++ b/core/detectors/psd/CMakeLists.txt @@ -0,0 +1,29 @@ +set(INCLUDE_DIRECTORIES + ${CMAKE_CURRENT_SOURCE_DIR} +) + +include_directories(${INCLUDE_DIRECTORIES}) + +set(SYSTEM_INCLUDE_DIRECTORIES + ${BASE_INCLUDE_DIRECTORIES} +) + +include_directories(SYSTEM ${SYSTEM_INCLUDE_DIRECTORIES}) + +set(LINK_DIRECTORIES + ${ROOT_LIBRARY_DIR} + ${FAIRROOT_LIBRARY_DIR} + ${Boost_LIBRARY_DIRS} +) + +link_directories(${LINK_DIRECTORIES}) + +set(SRCS + PronyFitter.cxx +) + +set(LINKDEF CbmPsdBaseLinkDef.h) +Set(LIBRARY_NAME CbmPsdBase) +Set(DEPENDENCIES) + +GENERATE_LIBRARY() diff --git a/core/detectors/psd/CbmPsdBaseLinkDef.h b/core/detectors/psd/CbmPsdBaseLinkDef.h new file mode 100644 index 0000000000000000000000000000000000000000..88231a3e1530e28e757c0607ca605f487a76c5bc --- /dev/null +++ b/core/detectors/psd/CbmPsdBaseLinkDef.h @@ -0,0 +1,12 @@ +// $Id: TofLinkDef.h,v 1.3 2006/03/07 11:51:55 friese Exp $ + +#ifdef __CINT__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class PsdSignalFitting::PronyFitter; + +#endif + diff --git a/core/detectors/psd/PolynomComplexRoots.h b/core/detectors/psd/PolynomComplexRoots.h new file mode 100644 index 0000000000000000000000000000000000000000..43d71003749a17025e69a2a44f71121f22922816 --- /dev/null +++ b/core/detectors/psd/PolynomComplexRoots.h @@ -0,0 +1,280 @@ +#include <iostream> +#include <iomanip> +#include <math.h> +#include <stdlib.h> + +#define maxiter 500 + +// +// Extract individual real or complex roots from list of quadratic factors +// +int roots(float *a,int n,float *wr,float *wi) +{ + float sq,b2,c,disc; + int m,numroots; + + m = n; + numroots = 0; + while (m > 1) { + b2 = -0.5*a[m-2]; + c = a[m-1]; + disc = b2*b2-c; + if (disc < 0.0) { // complex roots + sq = sqrt(-disc); + wr[m-2] = b2; + wi[m-2] = sq; + wr[m-1] = b2; + wi[m-1] = -sq; + numroots+=2; + } + else { // real roots + sq = sqrt(disc); + wr[m-2] = fabs(b2)+sq; + if (b2 < 0.0) wr[m-2] = -wr[m-2]; + if (wr[m-2] == 0) + wr[m-1] = 0; + else { + wr[m-1] = c/wr[m-2]; + numroots+=2; + } + wi[m-2] = 0.0; + wi[m-1] = 0.0; + } + m -= 2; + } + if (m == 1) { + wr[0] = -a[0]; + wi[0] = 0.0; + numroots++; + } + return numroots; +} +// +// Deflate polynomial 'a' by dividing out 'quad'. Return quotient +// polynomial in 'b' and error metric based on remainder in 'err'. +// +void deflate(float *a,int n,float *b,float *quad,float *err) +{ + float r,s; + int i; + + r = quad[1]; + s = quad[0]; + + b[1] = a[1] - r; + + for (i=2;i<=n;i++){ + b[i] = a[i] - r * b[i-1] - s * b[i-2]; + } + *err = fabs(b[n])+fabs(b[n-1]); +} +// +// Find quadratic factor using Bairstow's method (quadratic Newton method). +// A number of ad hoc safeguards are incorporated to prevent stalls due +// to common difficulties, such as zero slope at iteration point, and +// convergence problems. +// +// Bairstow's method is sensitive to the starting estimate. It is possible +// for convergence to fail or for 'wild' values to trigger an overflow. +// +// It is advisable to institute traps for these problems. (To do!) +// +void find_quad(float *a,int n,float *b,float *quad,float *err, int *iter) +{ + float *c,dn,dr,ds,drn,dsn,eps,r,s; + int i; + + c = new float [n+1]; + c[0] = 1.0; + r = quad[1]; + s = quad[0]; + eps = 1e-15; + *iter = 1; + + do { + if (*iter > maxiter) break; + if (((*iter) % 200) == 0) { + eps *= 10.0; + } + b[1] = a[1] - r; + c[1] = b[1] - r; + + for (i=2;i<=n;i++){ + b[i] = a[i] - r * b[i-1] - s * b[i-2]; + c[i] = b[i] - r * c[i-1] - s * c[i-2]; + } + dn=c[n-1] * c[n-3] - c[n-2] * c[n-2]; + drn=b[n] * c[n-3] - b[n-1] * c[n-2]; + dsn=b[n-1] * c[n-1] - b[n] * c[n-2]; + + if (fabs(dn) < 1e-10) { + if (dn < 0.0) dn = -1e-8; + else dn = 1e-8; + } + dr = drn / dn; + ds = dsn / dn; + r += dr; + s += ds; + (*iter)++; + } while ((fabs(dr)+fabs(ds)) > eps); + quad[0] = s; + quad[1] = r; + *err = fabs(ds)+fabs(dr); + delete [] c; +} +// +// Differentiate polynomial 'a' returning result in 'b'. +// +void diff_poly(float *a,int n,float *b) +{ + float coef; + int i; + + coef = (float)n; + b[0] = 1.0; + for (i=1;i<n;i++) { + b[i] = a[i]*((float)(n-i))/coef; + } +} +// +// Attempt to find a reliable estimate of a quadratic factor using modified +// Bairstow's method with provisions for 'digging out' factors associated +// with multiple roots. +// +// This resursive routine operates on the principal that differentiation of +// a polynomial reduces the order of all multiple roots by one, and has no +// other roots in common with it. If a root of the differentiated polynomial +// is a root of the original polynomial, there must be multiple roots at +// that location. The differentiated polynomial, however, has lower order +// and is easier to solve. +// +// When the original polynomial exhibits convergence problems in the +// neighborhood of some potential root, a best guess is obtained and tried +// on the differentiated polynomial. The new best guess is applied +// recursively on continually differentiated polynomials until failure +// occurs. At this point, the previous polynomial is accepted as that with +// the least number of roots at this location, and its estimate is +// accepted as the root. +// +void recurse(float *a,int n,float *b,int m,float *quad, + float *err,int *iter) +{ + float *c,*x,rs[2],tst; + + if (fabs(b[m]) < 1e-16) m--; // this bypasses roots at zero + if (m == 2) { + quad[0] = b[2]; + quad[1] = b[1]; + *err = 0; + *iter = 0; + return; + } + c = new float [m+1]; + x = new float [n+1]; + c[0] = x[0] = 1.0; + rs[0] = quad[0]; + rs[1] = quad[1]; + *iter = 0; + find_quad(b,m,c,rs,err,iter); + tst = fabs(rs[0]-quad[0])+fabs(rs[1]-quad[1]); + if (*err < 1e-12) { + quad[0] = rs[0]; + quad[1] = rs[1]; + } +// tst will be 'large' if we converge to wrong root + if (((*iter > 5) && (tst < 1e-4)) || ((*iter > 20) && (tst < 1e-1))) { + diff_poly(b,m,c); + recurse(a,n,c,m-1,rs,err,iter); + quad[0] = rs[0]; + quad[1] = rs[1]; + } + delete [] x; + delete [] c; +} +// +// Top level routine to manage the determination of all roots of the given +// polynomial 'a', returning the quadratic factors (and possibly one linear +// factor) in 'x'. +// +void get_quads(float *a,int n,float *quad,float *x) +{ + float *b,*z,err,tmp; + int iter,i,m; + + if ((tmp = a[0]) != 1.0) { + a[0] = 1.0; + for (i=1;i<=n;i++) { + a[i] /= tmp; + } + } + if (n == 2) { + x[0] = a[1]; + x[1] = a[2]; + return; + } + else if (n == 1) { + x[0] = a[1]; + return; + } + m = n; + b = new float [n+1]; + z = new float [n+1]; + b[0] = 1.0; + for (i=0;i<=n;i++) { + z[i] = a[i]; + x[i] = 0.0; + } + do { + if (n > m) { + quad[0] = 3.14159e-1; + quad[1] = 2.78127e-1; + } + do { // This loop tries to assure convergence + for (i=0;i<5;i++) { + find_quad(z,m,b,quad,&err,&iter); + if ((err > 1e-7) || (iter > maxiter)) { + diff_poly(z,m,b); + recurse(z,m,b,m-1,quad,&err,&iter); + } + deflate(z,m,b,quad,&err); + if (err < 0.001) break; + quad[0] = 9999.; + quad[1] = 9999.; + } + if (err > 0.01) { + printf("Error! Convergence failure in quadratic x^2 + r*x + s.\n"); + exit(1); + } + } while (err > 0.01); + x[m-2] = quad[1]; + x[m-1] = quad[0]; + m -= 2; + for (i=0;i<=m;i++) { + z[i] = b[i]; + } + } while (m > 2); + if (m == 2) { + x[0] = b[1]; + x[1] = b[2]; + } + else x[0] = b[1]; + delete [] z; + delete [] b; +} + +void polynomComplexRoots(float *wr,float *wi, int n, float *a, int &numr) +{ + float *quad = new float[2]; + float *x = new float[n]; + + // initialize estimate for 1st root pair + quad[0] = 2.71828e-1; + quad[1] = 3.14159e-1; + + // get roots + get_quads(a,n,quad,x); + numr = roots(x,n,wr,wi); + + delete[] quad; + delete[] x; +} diff --git a/core/detectors/psd/PolynomRealRoots.h b/core/detectors/psd/PolynomRealRoots.h new file mode 100644 index 0000000000000000000000000000000000000000..10bd8498310a4e7e435784341c06a1cd96274eb3 --- /dev/null +++ b/core/detectors/psd/PolynomRealRoots.h @@ -0,0 +1,137 @@ +#include <cmath> + +//************************************************************************* +float polinom(int n,float x,float *k) +{ +float s=1; +for(int i=n-1;i>=0;i--) +s=s*x+k[i]; +return s; +}//polinom + +float dihot(int degree,float edgeNegativ,float edgePositiv,float *kf) +{ +for(;;) +{ +float x=0.5*(edgeNegativ+edgePositiv); +if(std::abs(x-edgeNegativ) < 1e-3 || std::abs(x-edgePositiv) < 1e-3)return x; +if(polinom(degree,x,kf)<0)edgeNegativ=x; +else edgePositiv=x; +} +}//dihot + +void stepUp( +int level, +float **A, +float **B, +int *currentRootsCount +) +{ + +float major=0; +for(int i=0;i<level;i++) +{ +float s=fabs(A[level][i]); +if(s>major)major=s; +} +major+=1.0; + +currentRootsCount[level]=0; + +for(int i=0;i<=currentRootsCount[level-1];i++) +{ +int signLeft,signRight; +float edgeLeft,edgeRight; +float edgeNegativ,edgePositiv; + +if(i==0)edgeLeft=-major; +else edgeLeft=B[level-1][i-1]; + +float rb=polinom(level,edgeLeft,A[level]); + +if(rb==0) +{ +B[level][currentRootsCount[level]]=edgeLeft; +currentRootsCount[level]++; +continue; +} + +if(rb>0)signLeft=1;else signLeft=-1; + +if(i==currentRootsCount[level-1])edgeRight=major; +else edgeRight=B[level-1][i]; + +rb=polinom(level,edgeRight,A[level]); + +if(rb==0) +{ +B[level][currentRootsCount[level]]=edgeRight; +currentRootsCount[level]++; +continue; +} + +if(rb>0)signRight=1;else signRight=-1; +if(signLeft==signRight)continue; + +if(signLeft<0){edgeNegativ=edgeLeft;edgePositiv=edgeRight;} +else {edgeNegativ=edgeRight;edgePositiv=edgeLeft;} + +B[level][currentRootsCount[level]]=dihot(level,edgeNegativ,edgePositiv,A[level]); +currentRootsCount[level]++; +} +return; +}//stepUp + +void polynomRealRoots( +float *rootsArray, +int n, +float *kf_, +int &rootsCount +) +{ + +float *kf = new float [n+1]; + +float **A=new float *[n+1]; +float **B=new float *[n+1]; + +int *currentRootsCount=new int[n+1]; + +for(int i=1;i<=n;i++) +{ +A[i]=new float[i]; +B[i]=new float[i]; +} + +for(int i=0;i<=n;i++)kf[i]=kf_[n-i]; + +for(int i=0;i<n;i++)A[n][i]=kf[i]/kf[n]; + +for(int i1=n,i=n-1;i>0;i1=i,i--) +{ +for(int j1=i,j=i-1;j>=0;j1=j,j--) +{ +A[i][j]=A[i1][j1]*j1/i1; +} +} + +currentRootsCount[1]=1; +B[1][0]=-A[1][0]; + +for(int i=2;i<=n;i++)stepUp(i,A,B,currentRootsCount); + +rootsCount=currentRootsCount[n]; +for(int i=0;i<rootsCount;i++)rootsArray[i]=B[n][i]; + +for(int i=1;i<=n;i++) +{ +delete[]A[i]; +delete[]B[i]; +} +delete[]A; +delete[]B; +delete[]currentRootsCount; +delete[]kf; +}//polynomRealRoots + +//************************************************************************* diff --git a/core/detectors/psd/PronyFitter.cxx b/core/detectors/psd/PronyFitter.cxx new file mode 100644 index 0000000000000000000000000000000000000000..2fcd76f8cba1ae5115930ee913b2240746f96694 --- /dev/null +++ b/core/detectors/psd/PronyFitter.cxx @@ -0,0 +1,976 @@ +#include "PolynomRealRoots.h" +#include "PolynomComplexRoots.h" +#include "PronyFitter.h" + +namespace PsdSignalFitting{ + + +PronyFitter::PronyFitter(int model_order, int exponent_number, int gate_beg, int gate_end) +{ Initialize(model_order, exponent_number, gate_beg, gate_end); } + +void PronyFitter::Initialize(int model_order, int exponent_number, int gate_beg, int gate_end) +{ + fModelOrder = model_order; + fExpNumber = exponent_number; + fGateBeg = gate_beg; + fGateEnd = gate_end; + AllocData(); +} + +void PronyFitter::AllocData() +{ + fz = new std::complex<float>[fExpNumber+1]; + fh = new std::complex<float>[fExpNumber+1]; + for(int i = 0; i < fExpNumber+1; i++) + { + fz[i] = {0.,0.}; + fh[i] = {0.,0.}; + } +} + +void PronyFitter::SetWaveform(std::vector<uint16_t>& uWfm, uint16_t uZeroLevel) +{ + fuWfm = uWfm; + fuZeroLevel = uZeroLevel; + fSampleTotal = fuWfm.size(); + fuFitWfm.resize(fSampleTotal); +} + +int PronyFitter::CalcSignalBegin(float front_time_beg_03, float front_time_end) +{ + return std::ceil((3*front_time_beg_03 - front_time_end)/2.) ; +} + +void PronyFitter::SetSignalBegin(int SignalBeg) +{ + fSignalBegin = SignalBeg; + if(fIsDebug) printf("\nsignal begin %i zero level %u\n", fSignalBegin, fuZeroLevel); +} + +void PronyFitter::CalculateFitHarmonics() +{ + float rho_f = 999; + float rho_b = 999; + std::vector<float> a_f; + std::vector<float> a_b; + + CovarianceDirect(rho_f, a_f, rho_b, a_b); + //CovarianceQRmod(rho_f, a_f, rho_b, a_b); + + if(fIsDebug) + { + printf("LSerr %e, SLE roots forward ", rho_f); + for(uint8_t i =0; i < a_f.size(); i++) printf(" %e ", a_f.at(i)); + printf("\n"); + printf("LSerr %e, SLE roots backward ", rho_b); + for(uint8_t i =0; i < a_b.size(); i++) printf(" %e ", a_b.at(i)); + printf("\n"); + } + + float *a_arr = new float[fModelOrder+1]; + for(int i = 0; i < fModelOrder+1; i++) a_arr[i] = 0.; + + //for(uint8_t i = 0; i < a_f.size(); i++) a_arr[i+1] = 0.5*(a_f.at(i) + a_b.at(i)); + for(uint8_t i = 0; i < a_f.size(); i++) a_arr[i+1] = a_f.at(i); + a_arr[0] = 1.; + + float *zr = new float[fModelOrder]; + float *zi = new float[fModelOrder]; + for(int i = 0; i < fModelOrder; i++) + { + zr[i] = 0.; + zi[i] = 0.; + } + + int total_roots; + //polynomRealRoots(zr, fModelOrder, a_arr, total_roots); + polynomComplexRoots(zr, zi, fModelOrder, a_arr, total_roots); + if(fIsDebug) + { + printf("forward polinom roots "); + for(int i = 0; i < fModelOrder; i++) + printf("%.5f%+.5fi ", zr[i], zi[i]); + printf("\n"); + + printf("forward freqs "); + for(int i = 0; i < fModelOrder; i++) + printf("%.5f ", log(zr[i])); + printf("\n"); + } + + std::complex<float> *z_arr = new std::complex<float>[fExpNumber+1]; + for(int i = 0; i < fExpNumber; i++) + { + if(std::isfinite(zr[i])) z_arr[i+1] = {zr[i], zi[i]}; + else z_arr[i+1] = 0.; + } + z_arr[0] = {1.,0.}; + SetHarmonics(z_arr); + fTotalPolRoots = total_roots; + + delete[] a_arr; + delete[] zr; + delete[] zi; + delete[] z_arr; +} + +void PronyFitter::CovarianceDirect(float &/*rho_f*/, std::vector<float> &a_f, float &/*rho_b*/, std::vector<float> &/*a_b*/) +{ + std::vector<int32_t> kiWfmSignal; + //filtering constant fraction + for(int sample_curr = fSignalBegin; sample_curr <= fGateEnd; sample_curr++) kiWfmSignal.push_back(fuWfm.at(sample_curr)-fuZeroLevel); + int n = kiWfmSignal.size(); + + float **Rkm_arr = new float*[fModelOrder]; + for(int i =0; i < fModelOrder; i++) + { + Rkm_arr[i] = new float[fModelOrder]; + for(int j = 0; j < fModelOrder; j++) Rkm_arr[i][j] = 0.; + } + + float *R0k_arr = new float[fModelOrder]; + for(int i =0; i < fModelOrder; i++) + R0k_arr[i] = 0.; + + //Regression via linear prediction forward + for(int i = 1; i <= fModelOrder; i++) + for(int j = 1; j <= fModelOrder; j++) + for(int sample_curr = fModelOrder; sample_curr < n; sample_curr++) + Rkm_arr[i-1][j-1] += (float) ( kiWfmSignal.at(sample_curr-i)*kiWfmSignal.at(sample_curr-j) ); + + for(int i = 1; i <= fModelOrder; i++) + for(int sample_curr = fModelOrder; sample_curr < n; sample_curr++) + R0k_arr[i-1] -= (float) ( kiWfmSignal.at(sample_curr)*kiWfmSignal.at(sample_curr-i) ); + + if(fIsDebug) + { + printf("system forward\n"); + for(int i =0; i < fModelOrder; i++) + { + for(int j = 0; j < fModelOrder; j++) printf("%e ", Rkm_arr[i][j]); + printf("%e\n", R0k_arr[i]); + } + } + + float *a = new float[fModelOrder]; + for(int i = 0; i < fModelOrder; i++) + a[i] = 0.; + + SolveSLEGauss(a, Rkm_arr, R0k_arr, fModelOrder); + //SolveSLECholesky(a, Rkm_arr, R0k_arr, fModelOrder); + if(fIsDebug) + { + printf("SLE roots "); + for(int i =0; i < fModelOrder; i++) printf(" %e ", a[i]); + printf("\n"); + } + + a_f.resize(fModelOrder); + for(int i = 0; i < fModelOrder; i++) + a_f.at(i) = a[i]; + + for(int i =0; i < fModelOrder; i++) delete[] Rkm_arr[i]; + delete[] Rkm_arr; + delete[] R0k_arr; + delete[] a; +} + +void PronyFitter::CovarianceQRmod(float &rho_f, std::vector<float> &a_f, float &rho_b, std::vector<float> &a_b) +{ + +/* +% Copyright (c) 2019 by S. Lawrence Marple Jr. +% +% +p +-- +order of linear prediction/autoregressive filter +% +x +-- +vector of data samples +% +rho_f +-- +least squares estimate of forward linear prediction variance +% +a_f +-- +vector of forward linear prediction/autoregressive +parameters +% +rho_b +-- +least squares estimate of backward linear prediction +variance +% +a_b +-- +vector of backward linear prediction/autoregressive +parameters + +*/ + + +//******** Initialization ******** + + std::vector<int32_t> kiWfmSignal; + //filtering constant fraction + for(int sample_curr = fSignalBegin; sample_curr <= fGateEnd; sample_curr++) kiWfmSignal.push_back(fuWfm.at(sample_curr)-fuZeroLevel); + int n = kiWfmSignal.size(); + if(2*fModelOrder+1 > n) {if(fIsDebug) printf("ERROR: Order too high; will make solution singular\n"); return; } + + float r1 = 0.; + for(int k = 1; k <= n-2; k++) + r1 += std::pow(kiWfmSignal.at(k), 2); + + float r2 = std::pow(kiWfmSignal.front(), 2); + float r3 = std::pow(kiWfmSignal.back(), 2); + + rho_f = r1 + r3; + rho_b = r1 + r2; + r1 = 1./(rho_b + r3); + + std::vector<float> c, d; + c.push_back( kiWfmSignal.back()*r1 ); + d.push_back( kiWfmSignal.front()*r1 ); + + float gam = 0.; + float del = 0.; + std::vector<float> ef, eb, ec, ed; + std::vector<float> coeffs; + coeffs.resize(6); + + for(int v_iter = 0; v_iter < n; v_iter++) + { + ef.push_back( kiWfmSignal.at(v_iter)); + eb.push_back( kiWfmSignal.at(v_iter)); + ec.push_back(c.at(0)*kiWfmSignal.at(v_iter)); + ed.push_back(d.at(0)*kiWfmSignal.at(v_iter)); + } + +//******** Main Recursion ******** + + for(int k = 1; k <= fModelOrder; k++) + { + if( rho_f <= 0 || rho_b <= 0 ) {if(fIsDebug) printf("PsdPronyFitter::ERROR: prediction squared error was less than or equal to zero\n"); return;} + + gam = 1.-ec.at(n-k); + del = 1.-ed.front(); + if( gam <= 0 || gam > 1 || del <= 0 || del > 1 ) {if(fIsDebug) printf("PsdPronyFitter::ERROR: GAM or DEL gain factor not in expected range 0 to 1\n"); return;} + + // computation for k-th order reflection coefficients + std::vector<float> eff, ebb; + eff.resize(n); + ebb.resize(n); + float delta = 0.; + for(int v_iter = 0; v_iter < n-1; v_iter++) + { + eff.at(v_iter) = ef.at(v_iter+1); + ebb.at(v_iter) = eb.at(v_iter); + delta += eff.at(v_iter) * ebb.at(v_iter); + } + + float k_f = -delta/rho_b; + float k_b = -delta/rho_f; + + //% order updates for squared prediction errors rho_f and rho_b + rho_f = rho_f*(1 - k_f*k_b); + rho_b = rho_b*(1 - k_f*k_b); + + //% order updates for linear prediction parameter arrays a_f and a_b + std::vector<float> temp_af = a_f; + std::reverse(std::begin(a_b), std::end(a_b)); + for(uint8_t i = 0; i < a_f.size(); i++) a_f.at(i) += k_f*a_b.at(i); + a_f.push_back(k_f); + + std::reverse(std::begin(a_b), std::end(a_b)); + std::reverse(std::begin(temp_af), std::end(temp_af)); + for(uint8_t i = 0; i < a_b.size(); i++) a_b.at(i) += k_b*temp_af.at(i); + a_b.push_back(k_b); + + //% check if maximum order has been reached + if(k == fModelOrder) + { + rho_f = rho_f/(n-fModelOrder); + rho_b = rho_b/(n-fModelOrder); + return; + } + + //% order updates for prediction error arrays ef and eb + for(int v_iter = 0; v_iter < n; v_iter++) + { + eb.at(v_iter) = ebb.at(v_iter) + k_b*eff.at(v_iter); + ef.at(v_iter) = eff.at(v_iter) + k_f*ebb.at(v_iter); + } + + //% coefficients for next set of updates + coeffs.at(0) = ec.front(); + coeffs.at(1) = coeffs.at(0)/del; + coeffs.at(2) = coeffs.at(0)/gam; + + //% time updates for gain arrays c' and d" + std::vector<float> temp_c = c; + for(uint8_t v_iter = 0; v_iter < c.size(); v_iter++) + { + c.at(v_iter) += coeffs.at(1)*d.at(v_iter); + d.at(v_iter) += coeffs.at(2)*temp_c.at(v_iter); + } + + //% time updates for ec' and ed" + std::vector<float> temp_ec = ec; + for(int v_iter = 0; v_iter < n; v_iter++) + { + ec.at(v_iter) += coeffs.at(1)*ed.at(v_iter); + ed.at(v_iter) += coeffs.at(2)*temp_ec.at(v_iter); + } + + std::vector<float> ecc, edd; + ecc.resize(n); + edd.resize(n); + for(int v_iter = 0; v_iter < n-1; v_iter++) + { + ecc.at(v_iter) = ec.at(v_iter+1); + edd.at(v_iter) = ed.at(v_iter); + } + + if( rho_f <= 0 || rho_b <= 0 ) {if(fIsDebug) printf("PsdPronyFitter::ERROR2: prediction squared error was less than or equal to zero\n"); return;} + gam = 1 - ecc.at(n-k-1); //n-k? + del = 1 - edd.front(); + if( gam <= 0 || gam > 1 || del <= 0 || del > 1 ) {if(fIsDebug) printf("PsdPronyFitter::ERROR2: GAM or DEL gain factor not in expected range 0 to 1\n"); return;} + + + //% coefficients for next set of updates + coeffs.at(0) = ef.front(); + coeffs.at(1) = eb.at(n-k-1); //n-k? + coeffs.at(2) = coeffs.at(1)/rho_b; + coeffs.at(3) = coeffs.at(0)/rho_f; + coeffs.at(4) = coeffs.at(0)/del; + coeffs.at(5) = coeffs.at(1)/gam; + + //% order updates for c and d; time updates for a_f' and a_b" + std::vector<float> temp_ab = a_b; + std::reverse(std::begin(temp_ab), std::end(temp_ab)); + std::reverse(std::begin(c), std::end(c)); + + for(uint8_t i = 0; i < a_b.size(); i++) a_b.at(i) += coeffs.at(5)*c.at(i); + std::reverse(std::begin(c), std::end(c)); + + for(uint8_t i = 0; i < c.size(); i++) c.at(i) += coeffs.at(2)*temp_ab.at(i); + c.push_back(coeffs.at(2)); + + std::vector<float> temp_af2 = a_f; + for(uint8_t i = 0; i < a_f.size(); i++) a_f.at(i) += coeffs.at(4)*d.at(i); + + for(uint8_t i = 0; i < d.size(); i++) d.at(i) += coeffs.at(3)*temp_af2.at(i); + d.insert(d.begin(), coeffs.at(3)); + + //% time updates for rho_f' and rho_b" + rho_f = rho_f - coeffs.at(4)*coeffs.at(0); + rho_b = rho_b - coeffs.at(5)*coeffs.at(1); + + if( rho_f <= 0 || rho_b <= 0 ) {if(fIsDebug) printf("PsdPronyFitter::ERROR3: prediction squared error was less than or equal to zero\n"); return;} + + //% order updates for ec and ed; time updates for ef' and eb" + for(int v_iter = 0; v_iter < n; v_iter++) + { + ec.at(v_iter) = ecc.at(v_iter) + coeffs.at(2)*eb.at(v_iter); + eb.at(v_iter) = eb.at(v_iter) + coeffs.at(5)*ecc.at(v_iter); + ed.at(v_iter) = edd.at(v_iter) + coeffs.at(3)*ef.at(v_iter); + ef.at(v_iter) = ef.at(v_iter) + coeffs.at(4)*edd.at(v_iter); + } + + } + +} + +void PronyFitter::SetHarmonics(std::complex<float> *z) +{ + std::memcpy(fz, z, (fExpNumber+1)*sizeof(std::complex<float>)); +} + +void PronyFitter::SetExternalHarmonics(std::complex<float> z1, std::complex<float> z2) +{ + std::complex<float> *z_arr = new std::complex<float>[fExpNumber+1]; + for(int i =0; i <= fExpNumber; i++) + z_arr[i] = {0.,0.}; + z_arr[0] = {1.,0.}; + z_arr[1] = z1; + z_arr[2] = z2; + SetHarmonics(z_arr); + delete[] z_arr; +} + +std::complex<float> *PronyFitter::GetHarmonics() +{ + return fz; +} + +int PronyFitter::GetNumberPolRoots() +{ + return fTotalPolRoots; +} + +void PronyFitter::MakePileUpRejection(int /*time_max*/) +{ +} + +void PronyFitter::CalculateFitAmplitudes() +{ + std::complex<float> **Zik_arr = new std::complex<float>*[fExpNumber+1]; + for(int i = 0; i < fExpNumber+1; i++) + { + Zik_arr[i] = new std::complex<float>[fExpNumber+1]; + for(int j = 0; j < fExpNumber+1; j++) Zik_arr[i][j] = {0.,0.}; + } + + std::complex<float> *Zyk_arr = new std::complex<float>[fExpNumber+1]; + for(int i = 0; i < fExpNumber+1; i++) + Zyk_arr[i] = {0.,0.}; + + int samples_in_gate = fGateEnd-fSignalBegin+1; + const std::complex<float> unit = {1.,0.}; + + for(int i = 0; i <= fExpNumber; i++) { + for(int j = 0; j <= fExpNumber; j++) { + std::complex<float> temp = std::conj(fz[i]) * fz[j]; + if( std::abs(temp - unit) > 1e-3) { + Zik_arr[i][j] = static_cast<std::complex<float>>( (std::pow(temp, static_cast<float>(samples_in_gate)) - unit) / (temp - unit) ); + } else { + Zik_arr[i][j] = static_cast<std::complex<float>>(samples_in_gate); + } + } + } + + std::complex<float> *z_power = new std::complex<float>[fExpNumber+1]; + for(int i = 0; i < fExpNumber+1; i++) z_power[i] = unit; + + for(int i = 0; i <= fExpNumber; i++) + { + for(int sample_curr = fSignalBegin; sample_curr <= fGateEnd; sample_curr++) + { + Zyk_arr[i] += (std::complex<float>) ( std::conj(z_power[i]) * (float)fuWfm.at(sample_curr) ); + z_power[i] *= fz[i]; + } + } + + if(fIsDebug) + { + printf("\nampl calculation\n"); + for(int i = 0; i <= fExpNumber; i++) + { + for(int j = 0; j <= fExpNumber; j++) printf("%e%+ei ", std::real(Zik_arr[i][j]), std::imag(Zik_arr[i][j])); + printf(" %e%+ei\n", std::real(Zyk_arr[i]), std::imag(Zyk_arr[i])); + } + } + + SolveSLEGauss(fh, Zik_arr, Zyk_arr, fExpNumber+1); + + if(fIsDebug) + { + printf("amplitudes\n%.0f%+.0fi ", std::real(fh[0]), std::imag(fh[0])); + for(int i = 1; i < fExpNumber+1; i++) printf("%e%+ei ", std::real(fh[i]), std::imag(fh[i])); + printf("\n\n"); + } + + for(int i = 0; i < fExpNumber+1; i++) z_power[i] = unit; + + std::complex<float> fit_ampl_in_sample = {0.,0.}; + fuFitZeroLevel = (uint16_t) std::real(fh[0]); + for(int sample_curr = 0; sample_curr < fSampleTotal; sample_curr++) + { + fit_ampl_in_sample = {0.,0.}; + if((sample_curr>=fSignalBegin)&&(sample_curr<=fGateEnd)) + { + for(int i = 0; i < fExpNumber+1; i++) + { + fit_ampl_in_sample += fh[i] * z_power[i]; + z_power[i] *= fz[i]; + } + fuFitWfm.at(sample_curr) = (uint16_t) std::real(fit_ampl_in_sample); + } + else fuFitWfm.at(sample_curr) = fuFitZeroLevel; + } + + if(fIsDebug) + { + printf("waveform:\n"); + for(uint8_t i =0; i < fuWfm.size(); i++) + printf("%u ", fuWfm.at(i)); + + printf("\nfit waveform:\n"); + for(uint8_t i =0; i < fuFitWfm.size(); i++) + printf("%u ", fuFitWfm.at(i)); + + printf("\nzero level %u\n\n", fuZeroLevel); + } + + for(int i = 0; i < fExpNumber+1; i++) delete[] Zik_arr[i]; + delete[] Zik_arr; + delete[] Zyk_arr; + delete[] z_power; +} + + +std::complex<float> *PronyFitter::GetAmplitudes() +{ + return fh; +} + +float PronyFitter::GetIntegral(int gate_beg, int gate_end) +{ + float integral = 0.; + for(int sample_curr = gate_beg; sample_curr <= gate_end; sample_curr++) + integral += (float) fuFitWfm.at(sample_curr) - fuFitZeroLevel; + + if(std::isfinite(integral)) return integral; + return 0; +} + +uint16_t PronyFitter::GetFitValue(int sample_number) +{ + if(std::isfinite(fuFitWfm.at(sample_number))) return fuFitWfm.at(sample_number); + return 0; +} + +float PronyFitter::GetFitValue(float x) +{ + std::complex<float> amplitude = {0.,0.}; + if(x < GetSignalBeginFromPhase()) return std::real(fh[0]); + amplitude += fh[0]; + for(int i = 1; i < fExpNumber+1; i++) + amplitude += fh[i] * std::pow(fz[i], x-fSignalBegin); + + if(std::isfinite(std::real(amplitude))) return std::real(amplitude); + return 0; +} + +float PronyFitter::GetZeroLevel() +{ + return (float) fuFitZeroLevel; +} + +float PronyFitter::GetSignalMaxTime() +{ + return fSignalBegin + std::real( ( std::log(-fh[2]*std::log(fz[2])) - std::log(fh[1]*log(fz[1])) ) / ( std::log(fz[1]) - std::log(fz[2]) ) ); +} + +float PronyFitter::GetSignalBeginFromPhase() +{ + if(std::real(fh[2]/fh[1]) < 0) return fSignalBegin + std::real( std::log(-fh[2]/fh[1])/std::log(fz[1]/fz[2]) ); + return -999.; +} + +float PronyFitter::GetMaxAmplitude() +{ + return GetFitValue(GetSignalMaxTime()); +} + +float PronyFitter::GetX(float level, int first_sample, int last_sample) +{ + int step = 0; + if(first_sample<last_sample) step = 1; + else step = -1; + float result_sample = 0.; + int sample_to_check = first_sample; + float amplitude = 0.; + float amplitude_prev = GetFitValue(sample_to_check-step); + while( (first_sample-sample_to_check)*(last_sample-sample_to_check) <= 0 ) + { + amplitude = GetFitValue(sample_to_check); + if( (level - amplitude)*(level - amplitude_prev) <= 0 ) + { + result_sample = LevelBy2Points(sample_to_check, amplitude, sample_to_check-step, amplitude_prev, level); + return result_sample; + } + amplitude_prev = amplitude; + sample_to_check += step; + } + + return 0; +} + +float PronyFitter::GetX(float level, int first_sample, int last_sample, float step) +{ + float result_sample = 0.; + float sample_to_check = (float)first_sample; + std::complex<float> amplitude = {0.,0.}; + std::complex<float> amplitude_prev = GetFitValue(sample_to_check-step); + while( (first_sample-sample_to_check)*(last_sample-sample_to_check) <= 0 ) + { + amplitude = GetFitValue(sample_to_check); + if( (level - std::real(amplitude))*(level - std::real(amplitude_prev)) <= 0 ) + { + if(amplitude != amplitude_prev) + result_sample = LevelBy2Points(sample_to_check, std::real(amplitude), sample_to_check-step, std::real(amplitude_prev), level); + return result_sample; + } + amplitude_prev = amplitude; + sample_to_check += step; + } + + return 0; +} + +float PronyFitter::LevelBy2Points(float X1, float Y1, float X2, float Y2, float Y0) +{ + return (X1*Y0 - X1*Y2 - X2*Y0 + X2*Y1) / (Y1-Y2); +} + +float PronyFitter::GetRSquare(int gate_beg, int gate_end) +{ + float R2 = 0.; + float RSS = 0.; + float TSS = 0.; + int m = gate_end-gate_beg+1; + int params_number = 1+2*fModelOrder; + if(m<=params_number) return 999; + float average = 0.; + for(int sample_curr = gate_beg; sample_curr <= gate_end; sample_curr++) average += fuWfm.at(sample_curr); + average /= m; + + for(int sample_curr = gate_beg; sample_curr <= gate_end; sample_curr++) + { + RSS += std::pow( fuFitWfm.at(sample_curr)-fuWfm.at(sample_curr) , 2 ); + TSS += std::pow( fuWfm.at(sample_curr)-average , 2 ); + } + if(TSS == 0) return 999; + R2 = RSS/TSS; // correct definition is R2=1.-RSS/TSS, but R2=RSS/TSS is more convenient + + float R2_adj = R2*(m-1)/(m-params_number); + return R2_adj; +} + +float PronyFitter::GetRSquareSignal() +{ + return GetRSquare(fSignalBegin, fGateEnd); +} + +float PronyFitter::GetChiSquare(int gate_beg, int gate_end, int time_max) +{ + float chi2 = 0.; + int freedom_counter = 0; + int regions_number = 10; + float amplitude_max = std::abs(fuWfm.at(time_max) - fuZeroLevel); + if(amplitude_max == 0) return 999; + + int *probability_exp = new int[regions_number]; + int *probability_theor = new int[regions_number]; + float *amplitude_regions = new float[regions_number+1]; + amplitude_regions[0] = 0.; + for(int i = 0; i < regions_number; i++) + { + probability_exp[i] = 0; + probability_theor[i] = 0; + amplitude_regions[i+1] = (i+1)*amplitude_max/regions_number; + } + + for(int sample_curr = gate_beg; sample_curr <= gate_end; sample_curr++) + { + for(int i = 0; i < regions_number; i++) + { + if((std::abs(fuWfm.at(sample_curr)-fuZeroLevel) > amplitude_regions[i])&&(std::abs(fuWfm.at(sample_curr)-fuZeroLevel) <= amplitude_regions[i+1])) + probability_exp[i]++; + if((std::abs(fuFitWfm.at(sample_curr)-fuFitZeroLevel) > amplitude_regions[i])&&(std::abs(fuFitWfm.at(sample_curr)-fuFitZeroLevel) <= amplitude_regions[i+1])) + probability_theor[i]++; + } + } + + for(int i = 0; i < regions_number; i++) + { + if(probability_exp[i] > 0) + { + chi2 += std::pow(probability_exp[i] - probability_theor[i], 2.) / (probability_exp[i]); + freedom_counter++; + } + } + + if(freedom_counter>0) chi2 /= freedom_counter; + delete[] probability_exp; + delete[] probability_theor; + delete[] amplitude_regions; + + return chi2; +} + +float PronyFitter::GetDeltaInSample(int sample) +{ + return fuFitWfm.at(sample) - fuWfm.at(sample); +} + +/* +void PronyFitter::DrawFit(TObjArray *check_fit_arr, TString hist_title) +{ + float *sample_arr = new float[fSampleTotal]; + for(int i = 0; i < fSampleTotal; i++) + sample_arr[i] = (float) i; + + TGraph* tgr_ptr = new TGraph( fSampleTotal, sample_arr, fuWfm); + TGraph* tgr_ptr_fit = new TGraph( fSampleTotal, sample_arr, fuFitWfm); + TCanvas *canv_ptr = new TCanvas(hist_title.Data()); + tgr_ptr->SetTitle(hist_title.Data()); + tgr_ptr->Draw(); + + tgr_ptr_fit->SetLineColor(kRed); + tgr_ptr_fit->SetLineWidth(2); + tgr_ptr_fit->Draw("same"); + + check_fit_arr->Add(canv_ptr); + + delete[] sample_arr; +} +*/ + +int PronyFitter::ChooseBestSignalBeginHarmonics(int first_sample, int last_sample) +{ + float best_R2 = 0.; + int best_signal_begin = 0; + bool IsReasonableRoot; + bool IsGoodFit = false; + int good_fit_counter = 0; + + for(int signal_begin = first_sample; signal_begin <= last_sample; signal_begin++) + { + SetSignalBegin(signal_begin); + CalculateFitHarmonics(); + IsReasonableRoot = true; + for(int j = 0; j < fExpNumber; j++) IsReasonableRoot = IsReasonableRoot && (std::abs(fz[j+1]) > 1e-6)&&(std::abs(fz[j+1]) < 1e1); + IsGoodFit = (fTotalPolRoots>0)&&(IsReasonableRoot); + + if(IsGoodFit) + { + if(fIsDebug) printf("good fit candidate at signal begin %i\n", signal_begin); + good_fit_counter++; + CalculateFitAmplitudes(); + float R2 = GetRSquare(fGateBeg, fGateEnd); + if(good_fit_counter == 1) {best_R2 = R2; best_signal_begin = signal_begin;} + if(R2<best_R2) + { + best_R2 = R2; + best_signal_begin = signal_begin; + } + } + } + + return best_signal_begin; +} + +int PronyFitter::ChooseBestSignalBegin(int first_sample, int last_sample) +{ + float best_R2 = 0.; + int best_signal_begin = first_sample; + + for(int signal_begin = first_sample; signal_begin <= last_sample; signal_begin++) + { + SetSignalBegin(signal_begin); + CalculateFitAmplitudes(); + float R2 = GetRSquare(fGateBeg, fGateEnd); + if(signal_begin == first_sample) best_R2 = R2; + if(R2<best_R2) + { + best_R2 = R2; + best_signal_begin = signal_begin; + } + } + + return best_signal_begin; +} + +void PronyFitter::SolveSLEGauss(float *x, float **r, float *b, int n) +{ + bool solvable = true; + int maxRow; + float maxEl, tmp, c; + float **a = new float *[n]; + for(int i = 0; i < n; i++) + { + a[i] = new float [n+1]; + for(int j = 0; j < n+1; j++) a[i][j] = 0.; + } + + for(int i = 0; i < n; i++) + { + for(int j = 0; j < n; j++) a[i][j] = r[i][j]; + a[i][n] = b[i]; + } + + for(int i = 0; i < n; i++) + { + maxEl = std::abs(a[i][i]); + maxRow = i; + for(int k = i+1; k < n; k++) + if(abs(a[k][i]) > maxEl) + { + maxEl = std::abs(a[k][i]); + maxRow = k; + } + + if(maxEl == 0) { solvable = false; if(fIsDebug) printf("SLE has not solution\n"); } + + for(int k = i; k < n+1; k++) + { + tmp = a[maxRow][k]; + a[maxRow][k] = a[i][k]; + a[i][k] = tmp; + } + + for(int k = i+1; k < n; k++) + { + c = -a[k][i]/a[i][i]; + for(int j = i; j < n+1; j++) + { + if(i==j) a[k][j] = 0.; + else a[k][j] += c * a[i][j]; + } + } + } + + for(int i = n-1; i >= 0; i--) + { + x[i] = a[i][n]/a[i][i]; + for(int k = i-1; k >= 0; k--) + a[k][n] -= a[k][i] * x[i]; + } + + if(!solvable) { for(int i = n-1; i >= 0; i--) x[i] = 0.;} + + for(int i=0;i<n;i++) delete[] a[i]; + delete[] a; +} + +void PronyFitter::SolveSLEGauss(std::complex<float> *x, std::complex<float> **r, std::complex<float> *b, int n) +{ + bool solvable = true; + int maxRow; + float maxEl; + std::complex<float> tmp, c; + std::complex<float> **a = new std::complex<float> *[n]; + for(int i = 0; i < n; i++) + { + a[i] = new std::complex<float> [n+1]; + for(int j = 0; j < n+1; j++) a[i][j] = {0., 0.}; + } + + for(int i = 0; i < n; i++) + { + for(int j = 0; j < n; j++) a[i][j] = r[i][j]; + a[i][n] = b[i]; + } + + for(int i = 0; i < n; i++) + { + maxEl = std::abs(a[i][i]); + maxRow = i; + for(int k = i+1; k < n; k++) + if(std::abs(a[k][i]) > maxEl) + { + maxEl = std::abs(a[k][i]); + maxRow = k; + } + + if(maxEl == 0) { solvable = false; if(fIsDebug) printf("PsdPronyFitter:: SLE has not solution\n"); } + + for(int k = i; k < n+1; k++) + { + tmp = a[maxRow][k]; + a[maxRow][k] = a[i][k]; + a[i][k] = tmp; + } + + for(int k = i+1; k < n; k++) + { + c = -a[k][i]/a[i][i]; + for(int j = i; j < n+1; j++) + { + if(i==j) a[k][j] = 0.; + else a[k][j] += c * a[i][j]; + } + } + } + + for(int i = n-1; i >= 0; i--) + { + x[i] = a[i][n]/a[i][i]; + for(int k = i-1; k >= 0; k--) + a[k][n] -= a[k][i] * x[i]; + } + + if(!solvable) { for(int i = n-1; i >= 0; i--) x[i] = {0., 0.};} + + for(int i=0;i<n;i++) delete[] a[i]; + delete[] a; +} + +void PronyFitter::SolveSLECholesky(float *x, float **a, float *b, int n) +{ + float temp; + float **u = new float *[n]; + for(int i = 0; i < n; i++) + { + u[i] = new float [n]; + for(int j = 0; j < n; j++) u[i][j] = 0.; + } + + float *y = new float[n]; + for(int i = 0; i < n; i++) y[i] = 0.; + + for(int i = 0; i < n; i++) + { + temp = 0.; + for(int k = 0; k < i; k++) + temp = temp + u[k][i] * u[k][i]; + u[i][i] = std::sqrt(a[i][i] - temp); + for(int j = i; j < n; j++) + { + temp = 0.; + for(int k = 0; k < i; k++) + temp = temp + u[k][i] * u[k][j]; + u[i][j] = (a[i][j] - temp) / u[i][i]; + } + } + + for(int i = 0; i < n; i++) + { + temp = 0.; + for(int k = 0; k < i; k++) + temp = temp + u[k][i] * y[k]; + y[i] = (b[i] - temp) / u[i][i]; + } + + for(int i = n - 1; i >= 0; i--) + { + temp = 0.; + for(int k = i + 1; k < n; k++) + temp = temp + u[i][k] * x[k]; + x[i] = (y[i] - temp) / u[i][i]; + } + + for(int i = 0; i < n; i++) delete[] u[i]; + delete[] u; + delete[] y; +} + +void PronyFitter::DeleteData() +{ + delete[] fz; + delete[] fh; +} + +void PronyFitter::Clear() +{ + fModelOrder = 0; + fExpNumber = 0; + fGateBeg = 0; + fGateEnd = 0; + fSampleTotal = 0; + fuZeroLevel = 0.; + fSignalBegin = 0; + fTotalPolRoots = 0; + fuFitWfm.clear(); + DeleteData(); +} + + + +} + diff --git a/core/detectors/psd/PronyFitter.h b/core/detectors/psd/PronyFitter.h new file mode 100644 index 0000000000000000000000000000000000000000..e0486fb4221d43c84953602b347d0eb28a289f56 --- /dev/null +++ b/core/detectors/psd/PronyFitter.h @@ -0,0 +1,100 @@ +/** @file PronyFitter.h + @class PronyFitter + @author Nikolay Karpushkin (nkarpushkin@mail.ru) + @brief Class to fit waveform using Prony least squares method +*/ + +#ifndef PronyFitter_H +#define PronyFitter_H + +#include <vector> // for std::vector +#include <stdint.h> // for uint16_t +#include <stdio.h> // for printf +#include <cstring> // for memcpy +#include <complex> // for complex numbers +#include <iostream> +#include <algorithm> // for reverse + +namespace PsdSignalFitting{ + class PronyFitter + { + + public: + + /** Default constructor **/ + PronyFitter() {}; + PronyFitter(int model_order, int exponent_number, int gate_beg, int gate_end); + + /** Default destructor **/ + ~PronyFitter() {Clear();}; + + int CalcSignalBegin(float front_time_beg_03, float front_time_end); + int ChooseBestSignalBeginHarmonics(int first_sample, int last_sample) ; + int ChooseBestSignalBegin(int first_sample, int last_sample); + void MakePileUpRejection(int time_max); + void CalculateFitHarmonics(); + void CalculateFitAmplitudes(); + void SolveSLEGauss(float *x, float **r, float *b, int n); + void SolveSLEGauss(std::complex<float> *x, std::complex<float> **r, std::complex<float> *b, int n); + void SolveSLECholesky(float *x, float **a, float *b, int n); + void CovarianceQRmod(float &rho_f, std::vector<float> &a_f, float &rho_b, std::vector<float> &a_b); + void CovarianceDirect(float &rho_f, std::vector<float> &a_f, float &rho_b, std::vector<float> &a_b); + float LevelBy2Points(float X1, float Y1, float X2, float Y2, float Y0); +// +// Setters +// + void SetDebugMode(bool IsDebug) { fIsDebug = IsDebug; }; + void SetWaveform(std::vector<uint16_t>& uWfm, uint16_t uZeroLevel); + void SetSignalBegin(int SignalBeg); + void SetHarmonics(std::complex<float> *z); + void SetExternalHarmonics(std::complex<float> z1, std::complex<float> z2); +// +// Getters +// + std::complex<float> *GetHarmonics(); + std::complex<float> *GetAmplitudes(); + float GetIntegral(int gate_beg, int gate_end); + uint16_t GetFitValue(int sample_number); + float GetFitValue(float x); + float GetZeroLevel(); + float GetX(float level, int first_sample, int last_sample); + float GetX(float level, int first_sample, int last_sample, float step); + float GetRSquare(int gate_beg, int gate_end); + float GetRSquareSignal(); + float GetChiSquare(int gate_beg, int gate_end, int time_max); + float GetDeltaInSample(int sample); + float GetSignalBeginFromPhase(); + float GetSignalMaxTime(); + float GetMaxAmplitude(); + int GetNumberPolRoots(); + std::vector<uint16_t> GetFitWfm(){ return fuFitWfm; } + + private: + + void Initialize(int model_order, int exponent_number, int gate_beg, int gate_end); + void AllocData(); + void DeleteData(); + void Clear(); + + bool fIsDebug = false; + int fModelOrder; + int fExpNumber; + int fGateBeg; + int fGateEnd; + int fSampleTotal; + + int fSignalBegin; + int fTotalPolRoots; + + std::vector<uint16_t> fuWfm; + uint16_t fuZeroLevel; + std::complex<float> *fz; //! + std::complex<float> *fh; //! + std::vector<uint16_t> fuFitWfm; + uint16_t fuFitZeroLevel; + + }; +} + +#endif +