GammaWare  Head Version for release 0.9
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
RecalEnergy.cpp
Go to the documentation of this file.
1 //
2 // cpp version of recal_cob
3 // improved search of the peaks
4 // using user-defined sources
5 //
6 // Dino Bazzacco, August 2014
7 //
9 
10 #include "TSystem.h"
11 
12 #include "RecalEnergy.h"
13 
17 
19  fSpectraFolderName(""),
20  fSourceName("60Co"),
21  Peaks(),
22  specName(""),
23  specFormat(""),
24  specLength(0),
25  specData(NULL),
26  specOffset(0),
27  Spek(),
28  specNN(1),
29  specN0(0),
30  specNs(1),
31  specFromDef(0),
32  specToDef(0),
33  specFrom(NULL),
34  specTo(NULL),
35  specFWHMdef(15),
36  specAMPLdef(5),
37  fCurrentspecFWHMdef(0),
38  fCurrentspecAMPLdef(0),
39  specFWHM(NULL),
40  specAMPL(NULL),
41  testFP(NULL),
42  testData(NULL),
43  testGain(1),
44  testFileName(""),
45  testSuff(""),
46  testResults(false),
47  specPeaks(),
48  Energies(),
49  Delendae(),
50  eBhead(0.0),
51  eBstep(0.0),
52  eBnumb(0),
53  refEner(0),
54  useTL(true),
55  useTR(true),
56  fFlatBg(1),
57  fAffineBg(0),
58  nwa(),
59  nlim(),
60  bTwoLines(false),
61  bOneLine(false),
62  doSlope(true),
63  doPoly1(false),
64  doPoly2(false),
65  numZero(0),
66  modXtalk(0),
67  useErrors(false),
68  errE(0.01),
69  Dmode(1),
70  xP_0(0), xP_1(0), xP_2(0), xP_3(0), xP_4(0), xP_5(0), xP_6(0),
71  xP_minAmpl(0.0),
72  xP_fThreshCFA(0.0),
73  xP_nMinWidthP(0),
74  xP_nMinWidthN(0),
75  xP_nMinWidthP1(0),
76  xP_nMinWidthP2(0),
77  m_pCFit(NULL),
78  slope(0),
79  tshift(0),
80  offset1(0),
81  slope1(0),
82  offset2(0),
83  slope2(0),
84  curv2(0),
85  hGain(1),
86  Verbosity(0),
87  gterminate(0)
88 {
90 }
91 
93 
95 {
96  initialize(); // initialize parameters related to digitisers and detectors
97 
98  //gterminate = 0;
99  //signal(SIGINT, terminate);
100  //cout << "\nAnalysis can be stopped by typing CTRL_C\n" << endl << flush;
101 
102 // int evnum = 0;
103 // bool evok = true;
104  startTime = clock();
105 
106  for(int nspe = 0; nspe < specNN; nspe++) {
107 
108  int ispec = specN0 + nspe*specNs;
109  if( !Spek.Read(specLength, ispec) )
110  break;
111  specData = Spek.Data();
112 
115 
117  fCurrentspecToDef = specTo[nspe];
118 
119  int np = 0;
120  int ngood = 0;
121 
122  if(bOneLine)
124  else if(bTwoLines)
126  else if(Dmode == 1)
128  else
130 
131  //cout << specName << " # " << setw(2) /*<< setfill('0')*/ << nspe /*<< setfill(' ')*/ << setw(5) << np << " ";
132  //if(Verbosity) cout << endl;
133  //cout<<Form("%5d %s # %5d ( %3d", nspe, specName.c_str(), ispec, np);
134  //cout<<Form("%5d # %5d %3d", nspe, ispec, np);
135 
136  if(Verbosity & 1) {
137  cout<<Form("#1 %5d %6d %4d", nspe, ispec, np);
138  }
139 
140  slope=0, tshift = 0;
141  offset1=0, slope1=0; // should be generalized to polynomials
142  offset2=0, slope2=0, curv2=0;
143 
144  if(np) {
145  int fp = FitPeaks( Verbosity );
146  if(fp) {
147  if(specOffset) { // correct peak positions for specOffs
148  for(size_t np_ = 0; np_ < Peaks.size(); np_++) {
149  Peaks[np_].posi -= specOffset;
150  }
151  }
152  if(doSlope){
154  if(slope) {
155  for(size_t np_ = 0; np_ < Peaks.size(); np_++) {
156  if( Peaks[np_].good )
157  ngood++;
158  }
159  }
160  }
161  else {
162  tshift = refEner - Peaks[0].posi;
163  ngood = 1;
164  }
165  }
166  }
167  else {
168  Peaks.clear();
169  if(Verbosity & 1)
170  cout<<Form("\n");
171  }
172 
173  if(doSlope) {
174  double refPeakPosi=0, refPeakEner=0;
175  int iref = -1;;
176  if(refEner > 0 && slope) {
177  refPeakPosi = refEner/slope; // position of the reference peak derived from calibration
178  for(size_t irp = 0; irp < Peaks.size(); irp++) {
179  if( (abs(Peaks[irp].posi-refPeakPosi) < fCurrentspecFWHMdef)/* && Peaks[irp].good*/ ) { // not restricted to the good peaks
180  iref = irp;
181  refPeakPosi = Peaks[irp].posi; // actual peak position
182  refPeakEner = slope*refPeakPosi;
183  }
184  }
185  }
186  if(modXtalk==0)
187  cout<<Form("%6d %6d %4d %3d", nspe, ispec, np, ngood);
188  else
189  cout<<Form("%6d %6d %4d %3d", nspe%modXtalk, ispec/modXtalk, np, ngood);
190  double chi2 = 0;
191  int nused = 0;
192  for(size_t np_ = 0; np_ < Peaks.size(); np_++) {
193  if(Peaks[np_].good) {
194  nused++;
195  double ee = slope*(Peaks[np_].posi);
196  chi2 += pow(Energies[Peaks[np_].eref]-ee, 2);
197  }
198  }
199  chi2 = (nused-1) ? chi2/(nused-1) : 0;
200  if(iref>=0) {
201  if(modXtalk==0) {
202  cout<<Form(" %9.2f %8.3f %8.3f %9.0f %9.2f %6.1f %7.0f %7.3f %7.3f",
203  refPeakEner, Peaks[iref].fw05*slope, Peaks[iref].fw01*slope,
204  Peaks[iref].area, Peaks[iref].posi, Peaks[iref].fwhm, Peaks[iref].ampli, Peaks[iref].tailL, Peaks[iref].tailR);
205  }
206  else {
207  cout<<Form(" %9.2f %8.3f %8.3f %9.0f %9.2f %6.1f %7.0f %7.3f %7.3f",
208  refPeakEner, Peaks[iref].fw05, Peaks[iref].fw01,
209  Peaks[iref].area, Peaks[iref].posi, Peaks[iref].fwhm, Peaks[iref].ampli, Peaks[iref].tailL, Peaks[iref].tailR);
210  }
211  }
212  else {
213  cout<<Form(" %9.2f %8.3f %8.3f %9.0f %9.2f %6.1f %7.0f %7.3f %7.3f", 0., 0., 0., 0., 0., 0., 0., 0., 0.);
214  }
215  if(modXtalk==0)
216  cout<<Form(" %10.6f %6.2f", slope*hGain, min(999.99, chi2*100));
217  else {
218  if(slope)
219  cout<<Form(" %12.7f", 1./(slope*hGain));
220  else
221  cout<<Form(" %12.7f", 0.);
222  }
223  if(doPoly1) {
224  chi2 = 0;
225  nused = 0;
226  for(size_t np_ = 0; np_ < Peaks.size(); np_++) {
227  if(Peaks[np_].good) {
228  nused++;
229  double ee = offset1 + slope1*(Peaks[np_].posi);
230  chi2 += pow(Energies[Peaks[np_].eref]-ee, 2);
231  }
232  }
233  chi2 = (nused-2)>0 ? chi2/(nused-2) : 0;
234  cout<<Form(" %9.3f %10.6f %6.2f", offset1, slope1*hGain, min(999.99, chi2*100));
235  }
236  if(doPoly1 && doPoly2) {
237  chi2 = 0;
238  nused = 0;
239  for(size_t np_ = 0; np_ < Peaks.size(); np_++) {
240  if(Peaks[np_].good) {
241  nused++;
242  double ee = offset2 + slope2*Peaks[np_].posi + curv2*Peaks[np_].posi*Peaks[np_].posi;
243  chi2 += pow(Energies[Peaks[np_].eref]-ee, 2);
244  }
245  }
246  chi2 = (nused-3)>0 ? chi2/(nused-3) : 0;
247  cout<<Form(" %9.3f %10.6f % 10.4e %6.2f", offset2, slope2*hGain, curv2*hGain*hGain, min(999.99, chi2*100));
248  }
249  }
250  else {
251  cout<<Form("%6d %6d %4d %3d", nspe, ispec, np, ngood);
252  if(Peaks.size())
253  cout<<Form(" %9.2f %8.3f %8.3f %9.0f %9.2f %6.1f %7.0f %7.3f %7.3f %11.3f",
254  Peaks[0].posi, Peaks[0].fw05, Peaks[0].fw01,
255  Peaks[0].area, Peaks[0].posi, Peaks[0].fwhm, Peaks[0].ampli, Peaks[0].tailL, Peaks[0].tailR, tshift*hGain);
256  else
257  cout<<Form(" %9.2f %8.3f %8.3f %9.0f %9.2f %6.1f %7.0f %7.3f %7.3f %11.3f", 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.);
258  }
259  cout<<Form("\n");
260 
261  if(testResults) {
262  if(!TestResults()) {
263  cout << "Error writing " << testFileName << endl;
264  break;
265  }
266  }
267  }
268 
269  // TODO : write the calibration files in the needed formats
270 
271  stopTime = clock();
272 // double realTime = (double)(stopTime - startTime) / CLOCKS_PER_SEC;
273 
274  Spek.Close();
275 }
276 
278 
280 {
281  m_pCFit = new CFitSpek();
282  int npars = m_pCFit->GFitParNumber();
283  int* vpars = (int *) new int [npars];
284  m_pCFit->GFitParGet(vpars);
285 
286  if(specFormat.empty()) {
287 // int spSize = 0;
289  specFormat = "UI"; // hopefully
290  specLength = 16384;
291  }
292  }
293 
294  if( !Spek.Open(specName, specFormat) ) {
295  cerr << "Error opening " << specName << endl;
296  }
297 
298  // exclude the very first and last part of the spectrum
299  if(specFromDef == specToDef) {
300  specFromDef = int(5*specFWHMdef);
302  }
303 
304  specFrom = new int[specNN];
305  specTo = new int[specNN];
306  for(int nn = 0; nn < specNN; nn++) {
307  specFrom[nn] = specFromDef;
308  specTo[nn] = specToDef;
309  }
310  for(size_t nn = 0; nn < nlim.size(); nn++) {
311  int ii = nlim[nn].index;
312  if(specNN==1 && specN0==nlim[nn].index)
313  {
314  specFrom[0] = nlim[nn].from;
315  specTo[0] = nlim[nn].to;
316  }
317  else if(ii >= 0 && ii < specNN) {
318  specFrom[ii] = nlim[nn].from;
319  specTo[ii] = nlim[nn].to;
320  }
321  }
322 
323  cout << endl;
324  cout << "# ***********************************"<<endl;
325  cout << "# ***** *****"<<endl;
326  cout << "# *** GWRecal Fit results ***"<<endl;
327  cout << "# ***** *****"<<endl;
328  cout << "# ***********************************"<<endl;
329  cout << "# " << endl;
330 
331  cout << "# Data format " << specFormat << " " << specLength <<endl;
332 
333  cout<< "# Global channel range : [ "<<specFromDef<<" ; "<<specToDef<<" ]"<<endl;
334  if(nlim.size()) cout<< "# Specific channel ranges:"<<endl;
335  for(size_t i=0 ; i<nlim.size() ; i++)
336  {
337  NLimits_t lim = nlim[i];
338  int LibNumber = lim.index/38;
339  int HistNbr = lim.index%38;
340  cout<< "# ==> Seg nbr "<<Form("%2.0d in library %d",HistNbr,LibNumber)<<": [ "<<lim.from<<" ; "<<lim.to<<" ]"<<endl;
341  }
342  cout << "# " << endl;
343 
344  cout<< "# Global Search peak limits : FWHM < "<< specFWHMdef<<" ; Amplitude > "<<specAMPLdef<<endl;
345  if(nwa.size()) cout<< "# Specific peak limits:"<<endl;
346  for(size_t i=0 ; i<nwa.size() ; i++)
347  {
348  NWA_t tnwa = nwa[i];
349  int LibNumber = tnwa.index/38;
350  int HistNbr = tnwa.index%38;
351  cout<< "# ==> Seg nbr "<<Form("%2.0d in library %d",HistNbr,LibNumber)<<": FWHM < "<<tnwa.width<<" ; Amplitude > "<<tnwa.ampli<<endl;
352  }
353  cout << "# " << endl;
354 
355  if(fFlatBg)
356  {
357  vpars[0] = 1;
358  vpars[1] = 0;
359  fAffineBg = false;
360  cout << "# Flat Background estimation " << endl;
361  }
362  else if(fAffineBg)
363  {
364  vpars[0] = 1;
365  vpars[1] = 1;
366  fFlatBg = false;
367  cout << "# Affine Background estimation " << endl;
368  }
369  else if(!fAffineBg && ! fFlatBg)
370  {
371  vpars[0] = 0;
372  vpars[1] = 0;
373  fFlatBg = false;
374  fAffineBg = false;
375  cout << "# No Background estimation " << endl;
376  }
377  cout << "# "<< endl;
378 
379  if(Dmode==1) cout << "# Using 1st-derivative search"<<endl;
380  else if(Dmode==2) cout << "# Using 2nd-derivative search"<<endl;
381  cout << "# " << endl;
382 
383  if(!useTL) cout << "# Left Tail in peak fit disabled"<<endl<< "# " << endl;
384  if(!useTR) cout << "# Right Tail in peak fit disabled"<<endl<< "# " << endl;
385 
386  if(numZero) cout << "# Linear calibration enabled (no offset)"<<endl;
387  else if(doPoly1 && doPoly2)cout << "# Parabolic calibration enabled (y = offset + slope*x + curv*x*x)"<<endl;
388  else if(doPoly1) cout << "# Affine calibration enabled (y = offset + slope*x)"<<endl;
389  cout<< "# Scaling factor for the slope = "<<hGain<<endl;
390  cout<< "# " << endl;
391 
392  cout << "# Selected source : " << fSourceName<<endl;
393  cout << "# " << endl;
394 
395  // enabling the tails should be decided from the command line
396  useTL ? vpars[6] |= 1 : vpars[6] &= ~1; // control of the Tail to the Left
397  useTR ? vpars[7] |= 1 : vpars[7] &= ~1; // control of the Tail to the Right
398  if(!doSlope)
399  vpars[5] = 0; // no step for time shifts
400  m_pCFit->GFitParSet(vpars);
401 
402  specFWHM = new float[specNN];
403  specAMPL = new float[specNN];
404  for(int nn = 0; nn < specNN; nn++) {
405  specFWHM[nn] = specFWHMdef;
406  specAMPL[nn] = specAMPLdef;
407  }
408  for(size_t nn = 0; nn < nwa.size(); nn++) {
409  int ii = nwa[nn].index;
410  if(specNN==1 && specN0==nwa[nn].index)
411  {
412  specFWHM[0] = nwa[nn].width;
413  specAMPL[0] = nwa[nn].ampli;
414  }
415  else if(ii >= 0 && ii < specNN) {
416  specFWHM[ii] = nwa[nn].width;
417  specAMPL[ii] = nwa[nn].ampli;
418  }
419  }
420 
421  for(int nn = 0; nn < eBnumb; nn++)
422  Energies.push_back(eBhead + eBstep*nn);
423 
424  if(Energies.size() < 1) {
425  Energies.push_back(1173.238);
426  Energies.push_back(1332.513);
427  }
428  if(Delendae.size()) {
429  for(size_t dd = 0; dd < Delendae.size(); dd++) {
430  size_t closest = 1;
431  double delta = fabs(Delendae[dd]-Energies[0]);
432  for (size_t nn = 1; nn < Energies.size(); nn++) {
433  if(fabs(Delendae[dd]-Energies[nn]) < delta) {
434  closest = nn;
435  delta = fabs(Delendae[dd]-Energies[nn]);
436  }
437  }
438  Energies.erase(Energies.begin()+closest);
439  }
440  }
441  sort( Energies.begin( ), Energies.end( ) );
442 
443  if(Energies.size()==1) {
444  bOneLine = true;
445  bTwoLines = false;
446  refEner = Energies[0];
447  }
448  else if(Energies.size()==2) {
449  bOneLine = false;
450  bTwoLines = true;
451  if(!refEner) {
452  refEner = Energies[1];
453  }
454  else {
455  if(abs(refEner-Energies[0])>1. && abs(refEner-Energies[1])>1.)
456  refEner = Energies[1];
457  }
458  }
459  else {
460  bOneLine = false;
461  bTwoLines = false;
462  if(!refEner)
463  refEner = Energies[Energies.size()-1];
464  }
465 
466  if(doSlope)
467  cout << "# " << "Energies used for the calibration " << endl;
468  else
469  cout << "# " << "Position used for the calibration " << endl;
470  for (size_t nn = 0; nn < Energies.size(); nn++)
471  {
472  cout << "# " << setw(3) << nn << fixed << setprecision(3) << setw(10) << Energies[nn];
473  if(refEner==Energies[nn]) cout<<" ==> Reference peak for extended printouts";
474  cout<<endl;
475  }
476 
477  if(testResults) {
478  gSystem->mkdir(fSpectraFolderName);
479  ostringstream name;
480  name << fSpectraFolderName << "/Recal__" << specNN << "-" << specLength << "-F__" << testSuff << ".dat" ;
481  testFileName = name.str();
482  testFP = fopen(testFileName.c_str(), "wb");
483  if(!testFP) {
484  cerr << "Error opening " << testFileName << endl;
485  }
486  testData = new float[specLength];
487  memset(testData, 0, sizeof(float)*specLength);
488  }
489 
490  cout << "# " << endl;
491  cout << "# " << "Spectra taken from file : " << specName << endl;
492  if(testResults)
493  cout << "# Calibrated spectra written to : " << testFileName << endl;
494 
495 
496  cout << "# " << endl;
497  if(doSlope) {
498  if(modXtalk==0) {
499  cout << "# indx #spec #pks #ok rEnergy FW05 FW01 Area Position Width Ampli WTML WTMR slope*gain rChi2%";
500  if(doPoly1)
501  cout << " offs1*g slope1*g rChi2%";
502  if(doPoly2)
503  cout << " offs2*g slope2*g coeff2*g rChi2%";
504  }
505  else {
506  cout << "# indx #spec #pks #ok rEnergy FW05 FW01 Area Position Width Ampli WTML WTMR cross-talk";
507  }
508 
509  }
510  else {
511  cout << "# indx #spec #pks #ok rEnergy FW05 FW01 Area Position Width Ampli WTML WTMR shift*gain";
512  }
513  cout << "\n# " << endl;
514 
515 }
517 
519 {
520  Energies.clear();
521 
522  if(fSourceName.Contains("22Na",TString::kIgnoreCase))
523  {
524  fSourceName = "22Na";
525  Energies.push_back( 511.006);
526  Energies.push_back(1274.545);
527  }
528  if(fSourceName.Contains("56Co",TString::kIgnoreCase))
529  {
530  fSourceName = "56Co";
531  Energies.push_back( 846.772);
532  Energies.push_back(1037.840);
533  Energies.push_back(1175.102);
534  Energies.push_back(1238.282);
535  Energies.push_back(1360.250);
536  Energies.push_back(1771.351);
537  Energies.push_back(2015.181);
538  Energies.push_back(2034.755);
539  Energies.push_back(2598.458);
540  Energies.push_back(3201.962);
541  Energies.push_back(3253.416);
542  Energies.push_back(3272.990);
543  Energies.push_back(3451.152);
544  Energies.push_back(3547.925);
545  }
546  if(fSourceName.Contains("40K",TString::kIgnoreCase))
547  {
548  fSourceName = "40K";
549  Energies.push_back( 122.0614);
550  Energies.push_back( 136.4743);
551  }
552  if(fSourceName.Contains("57Co",TString::kIgnoreCase))
553  {
554  fSourceName = "57Co";
555  //Energies.push_back( 14.4130);
556  Energies.push_back( 122.0614);
557  Energies.push_back( 136.4743);
558  }
559  if(fSourceName.Contains("60Co",TString::kIgnoreCase))
560  {
561  fSourceName = "60Co";
562  Energies.push_back(1173.238);
563  Energies.push_back(1332.513);
564  }
565  if(fSourceName.Contains("88Y",TString::kIgnoreCase))
566  {
567  fSourceName = "88Y";
568  Energies.push_back( 898.045);
569  Energies.push_back(1836.062);
570  //Energies.push_back(2734.087);
571  }
572  if(fSourceName.Contains("133Ba",TString::kIgnoreCase))
573  {
574  fSourceName = "133Ba";
575  Energies.push_back( 53.156);
576  Energies.push_back( 79.623);
577  Energies.push_back( 80.999);
578  Energies.push_back( 160.609);
579  Energies.push_back( 223.116);
580  Energies.push_back( 276.404);
581  Energies.push_back( 302.858);
582  Energies.push_back( 356.014);
583  Energies.push_back( 383.859);
584  }
585  if(fSourceName.Contains("134Cs",TString::kIgnoreCase))
586  {
587  fSourceName = "134Cs";
588  Energies.push_back( 475.36);
589  Energies.push_back( 563.27);
590  Energies.push_back( 569.30);
591  Energies.push_back( 604.68);
592  Energies.push_back( 795.78);
593  Energies.push_back( 801.86);
594  Energies.push_back(1038.53);
595  Energies.push_back(1167.89);
596  Energies.push_back(1365.17);
597  }
598  if(fSourceName.Contains("137Cs",TString::kIgnoreCase))
599  {
600  fSourceName = "137Cs";
601  Energies.push_back( 661.661);
602  }
603  if(fSourceName.Contains("152Eu",TString::kIgnoreCase))
604  {
605  fSourceName = "152Eu";
606  Energies.push_back( 121.7793);
607  Energies.push_back( 244.6927);
608  Energies.push_back( 344.2724);
609  Energies.push_back( 411.111);
610  Energies.push_back( 443.979);
611  Energies.push_back( 778.890);
612  Energies.push_back( 964.014);
613  Energies.push_back(1085.793);
614  //Energies.push_back(1089.700);
615  Energies.push_back(1112.070);
616  Energies.push_back(1407.993);
617  }
618  if(fSourceName.Contains("208Pb",TString::kIgnoreCase))
619  {
620  fSourceName = "208Pb";
621  Energies.push_back(2614.5); // increase precision
622  }
623  if(fSourceName.Contains("226Ra",TString::kIgnoreCase))
624  {
625  fSourceName = "226Ra";
626  Energies.push_back( 186.211);
627  Energies.push_back( 241.981);
628  Energies.push_back( 295.213);
629  Energies.push_back( 351.921);
630  Energies.push_back( 609.312);
631  Energies.push_back( 768.356);
632  Energies.push_back( 934.061);
633  Energies.push_back(1120.287);
634  Energies.push_back(1238.110);
635  Energies.push_back(1377.669);
636  Energies.push_back(1509.228);
637  Energies.push_back(1729.595);
638  Energies.push_back(1764.494);
639  Energies.push_back(1847.420);
640  Energies.push_back(2118.551);
641  Energies.push_back(2204.215);
642  Energies.push_back(2447.810);
643  }
644  if(fSourceName.Contains("241Am",TString::kIgnoreCase))
645  {
646  fSourceName = "241Am";
647  Energies.push_back( 26.345);
648  Energies.push_back( 59.537);
649  }
650 }
651 
653 
654 void GWRecal::numparams(char * cmd, int nn)
655 {
656  if(nn >= 0) return;
657  nn = -nn;
658  cout<<Form("error decoding command %s ==> %d parameter(s) missing \n", cmd, nn);
659  exit(EXIT_FAILURE);
660 }
661 
663 
665 {
666  // delete previous
667  memset(testData, 0, sizeof(float)*specLength);
668 
669  // fill with calibrated spectrum
670  double e0(0), e1(0); // calibrated beginning and end of channel
671  e1 = Calibrated(0)*testGain;
672  for(int nn = 0; nn < specLength; nn++) {
673  e0 = e1;
674  e1 = Calibrated(nn)*testGain;
675  double val = specData[nn];
676  int n0 = int(e0);
677  int n1 = int(e1);
678  if(n0 == n1) { // all in one channel
679  if(n0 >=0 && n0 < specLength)
680  testData[n0] += float(val);
681  }
682  else if (n0 < n1) { // spread linearly in the target range
683  val /= (e1-e0);
684  if(n0 >=0 && n0 < specLength)
685  testData[n0] += float(val*(n0+1-e0));
686  for(int ii = n0+1 ; ii < n1; ii++) {
687  if(ii >=0 && ii < specLength)
688  testData[ii] += float(val);
689  }
690  if(n1 >=0 && n1 < specLength)
691  testData[n1] += float(val*(e1-n1));
692  }
693  else { // spread linearly in the target range, in reverse order
694  val /= (e0-e1);
695  {int nt = n0; n0 = n1; n1 = nt;}
696  if(n0 >=0 && n0 < specLength)
697  testData[n0] += float(val*(n0+1-e1));
698  for(int ii = n0+1; ii < n1; ii++) {
699  if(ii >=0 && ii < specLength)
700  testData[ii] += float(val);
701  }
702  if(n1 >=0 && n1 < specLength)
703  testData[n1] += float(val*(e0-n1));
704  }
705  }
706 
707  // write result
708  int nn = fwrite(testData, sizeof(float), specLength, testFP);
709 
710  return (nn == specLength);
711 }
712 
714 
715 // - smooth the spectrum with a gaussian kernel
716 // - differentiate ONCE
717 // Loop (maxNum times)
718 // find the minimum of the derivative and the associated +++--- pattern
719 // accept it with conditions with the cross-over as peak position
720 // erase the region
721 //
722 int GWRecal::LargePeaks1(float *m_pSpek, int chA, int chB, float fwhm, float minAmpl, std::vector<double>&vPeaks, int maxNum)
723 {
724  //CheckLimits(chA, chB);
725  int nChan = chB-chA+1;
726 
727  double sigma = fwhm/s2fwhm;
728  float *tSpek = NULL; // the smoothed and differentiated spectrum
729  int xMin = 0; // position of chA in tSpek
730  SmoothSpek(m_pSpek+chA, nChan, sigma, 1, tSpek, xMin);
731  int xMax = xMin + nChan; // position of chB+1 in tSpek
732 
733  // ready to search peaks from xMin to xMax
734  vPeaks.clear();
735 
736  // put here sensible values
737  xP_minAmpl = float(0.5*abs(minAmpl*exp(-.5)/sigma)); // max(g') = |g'(sigma)| = g(0)*exp(-0.5)/sigma
738  xP_fThreshCFA = 0;
739  xP_nMinWidthP = max(2, int(fwhm)-2);
740  xP_nMinWidthN = max(2, int(fwhm)-2);
741 
742  while(vPeaks.size() < size_t(maxNum)) {
743  // variables named as in xP_Next1
744  xP_0 = xP_1 = xP_2 = -1; // positive lobe
745  xP_3 = xP_4 = xP_5 = -1; // negative lobe
746  // find the largest negative peak
747  float yMin = 0;
748  for(int nn = xMin+1; nn < xMax; nn++) {
749  if(tSpek[nn] < yMin) {
750  yMin = tSpek[nn];
751  xP_4 = nn;
752  }
753  }
754  if(xP_4 < 0)
755  break; // empty?
756  // find the zero crossing at the left of the minimum
757  for(int nn = xP_4; nn > xMin; nn--) {
758  if(tSpek[nn] > 0) {
759  xP_2 = nn;
760  break;
761  }
762  }
763  if(xP_2 < 0)
764  break;
765  xP_3 = xP_2 + 1;
766  // find the end of the negative lobe
767  for(int nn = xP_3; nn < xMax; nn++) {
768  if(tSpek[nn] >= 0) {
769  xP_5 = nn;
770  break;
771  }
772  }
773  if(xP_5 < 0)
774  break;
775  // find the beginning of the positive lobe, and the maximum
776  float yMax = 0;
777  for(int nn = xP_2; nn > xMin; nn--) {
778  if(tSpek[nn] <= 0) {
779  xP_0 = nn;
780  break;
781  }
782  if(tSpek[nn] > yMax) {
783  yMax = tSpek[nn];
784  xP_1 = nn;
785  }
786  }
787  if(xP_0 < 0)
788  break;
789  int widthP = xP_2-xP_0;
790  int widthN = xP_5-xP_3;
791  bool good = true;
792  if(widthP < xP_nMinWidthP || widthN < xP_nMinWidthN)
793  good = false;
794  if(yMax < xP_minAmpl || yMin > -xP_minAmpl)
795  good = false;
796  float cmp = abs(yMax/yMin);
797  if(cmp < 0.2f || cmp > 2.f)
798  good = false;
799  if(good) {
800  // peak position is transition from positive to negative
801  double xPos = 0.5f*(xP_2+xP_3); // position of peak in tSpek coordinates
802  vPeaks.push_back(xPos-xMin+chA+1); // "" m_pSpek ""
803  }
804  // zero the region
805  for(int nn = xP_0; nn <xP_5; nn++)
806  tSpek[nn] = 0;
807  }
808 
809 #if 0
810  // Copy back (the remaining part of) the working spectrum
811  memcpy(m_pSpek+chA, tSpek+xMin, sizeof(float)*nChan);
812  float scale = float(sigma/exp(-.5));
813  for(int nn = chA; nn < chA+nChan; nn++)
814  m_pSpek[nn] *= scale;
815 #endif
816 
817  delete [] tSpek;
818 
819  return vPeaks.size();
820 }
821 
823 
824 // imported from TkT
825 // - smooth the spectrum with a gaussian kernel
826 // - differentiate ONCE
827 // Loop
828 // look for the +++--- pattern with conditions on its shape and amplitude
829 // test significance of peak before accepting it (nothing yet)
830 //
831 int GWRecal::PeakSearch1(float *m_pSpek, int chA, int chB, float fwhm, float minAmpl, std::vector<double>&vPeaks)
832 {
833  //CheckLimits(chA, chB);
834  int nChan = chB-chA+1;
835 
836  double sigma = fwhm/s2fwhm;
837  float *tSpek = NULL; // the smoothed and differentiated spectrum
838  int xMin = 0; // position of chA in tSpek
839  SmoothSpek(m_pSpek+chA, nChan, sigma, 1, tSpek, xMin);
840  int xMax = xMin + nChan; // position of chB+1 in tSpek
841 
842  // ready to search peaks from xMin to xMax
843  vPeaks.clear();
844 
845  // put here sensible values
846  xP_minAmpl = float(0.5*abs(minAmpl*exp(-.5)/sigma)); // max(g') = |g'(sigma)| = g(0)*exp(-0.5)/sigma
847  xP_fThreshCFA = 0;
848  xP_nMinWidthP = max(2, int(fwhm)-2);
849  xP_nMinWidthN = max(2, int(fwhm)-2);
850 
851  int xx = xMin;
852  while(xx < xMax) {
853  if( xP_Next1(tSpek+xx, nChan-xx) <= 0 )
854  break;
855  // peak position is transition from positive to negative
856  double xPos = xx + 0.5f*(xP_2+xP_3); // position of peak in tSpek coordinates
857  vPeaks.push_back(xPos + chA + 1 - xMin); // "" m_pSpek ""
858  xx += xP_5;
859  }
860 
861 #if 0
862  memcpy(m_pSpek+chA, tSpek+xMin, sizeof(float)*nChan);
868  float scale = float(sigma/exp(-.5));
869  for(int nn = chA; nn < chA+nChan; nn++)
870  m_pSpek[nn] *= scale;
871 #endif
872 
873  delete [] tSpek;
874 
875  return vPeaks.size();
876 }
877 
879 
880 // imported from TkT
881 // - smooth the spectrum with a gaussian kernel
882 // - differentiate TWICE
883 // Loop
884 // look for the next mexican-hat pattern with conditions on its shape and amplitude
885 // test significance of peak before accepting it (nothing yet)
886 //
887 int GWRecal::PeakSearch2(float *m_pSpek, int chA, int chB, float fwhm, float minAmpl, std::vector<double>&vPeaks)
888 {
889  //CheckLimits(chA, chB);
890  int nChan = chB-chA+1;
891 
892  double sigma = fwhm/s2fwhm;
893  float *tSpek = NULL; // the smoothed and differentiated spectrum
894  int xMin = 0; // position of chA in tSpek
895  SmoothSpek(m_pSpek+chA, nChan, sigma, 2, tSpek, xMin);
896  int xMax = xMin + nChan; // position of chB+1 in tSpek
897 
898  // ready to search peaks from xMin to xMax
899  vPeaks.clear();
900 
901  // put here sensible values
902  xP_minAmpl = float(abs(minAmpl/(sigma*sigma))); // max(g'') = |g''(0)| = |g(0)|/sigma**2
903  xP_fThreshCFA = 0;
904  xP_nMinWidthN = max(2, int(fwhm)-2);
905  xP_nMinWidthP1 = max(2, xP_nMinWidthN-1);
906  xP_nMinWidthP2 = max(2, (xP_nMinWidthN+1)/2);
907 
908  int xx = xMin;
909  while(xx < xMax) {
910  if( xP_Next2(tSpek+xx, xMax-xx) <= 0 )
911  break;
912  // first moments of the negative lobe to get the peak position
913  float xP0 = 0, xP1 = 0;
914  int nn0 = xx + xP_2 + 1;
915  int nn1 = xx + xP_4 + 1;
916  for(int nn = nn0; nn < nn1; nn++) {
917  float x = float(nn-nn0)+0.5f;
918  float y = tSpek[nn];
919  xP0 += y;
920  xP1 += y*x;
921  }
922  float xPos = nn0 + xP1/xP0; // position of peak in tSpek coordinates
923  vPeaks.push_back(xPos + chA - xMin); // "" m_pSpek ""
924  xx += xP_4;
925  }
926 
927 #if 0
928  memcpy(m_pSpek+chA, tSpek+xMin, sizeof(float)*nChan);
934  float scale = float(sigma*sigma);
935  for(int nn = chA; nn < chA+nChan; nn++)
936  m_pSpek[nn] *= scale;
937 #endif
938 
939  delete [] tSpek;
940 
941  return vPeaks.size();
942 }
943 
945 
946 // generates a smoothed and differentiated version of the input data
947 // returns the pointer and the position of the first valid channel
948 // the caller has to free tSpek
949 void GWRecal::SmoothSpek(float *spek, int nchan, double sigma, int ndiff, float *&tSpek, int &start)
950 {
951  // generate the gaussian kernel for the smoothing operation
952  int nKern = 2*int(5.5*sigma)+1; // < 1ppm
953  int zKern = nKern/2;
954  double gfact1 = -1./(2.*sigma*sigma);
955  double gfact2 = 1./(sigma*sqrt(2.*m_pi));
956 
957  float *gKern = new float [nKern];
958  for(int nn = 0; nn <= zKern; nn++) {
959  double gvalue = gfact2*exp( ((nn-zKern)*(nn-zKern))*gfact1 );
960  gKern[nn] = gKern[nKern-1-nn] = float(gvalue);
961  }
962 
963  // the temporary space for the data
964  int tLen = nKern + nchan + nKern;
965 
966  start = nKern;
967  tSpek = new float [tLen];
968 
969  // copy the original spectrum to the temporary storage,
970  // leaving nKern empty channels on both sides
971  memset(tSpek, 0, sizeof(float)*nKern);
972  memcpy(tSpek+nKern, spek, nchan*sizeof(float));
973  memset(tSpek+nKern+nchan, 0, sizeof(float)*nKern);
974 
975  // convolution --> the resulting spectrum is right shifted by zKern channels
976  for(int ii = tLen-1; ii >= nKern; ii--) {
977  float csum = 0;
978  for(int jj = 0; jj < nKern; jj++)
979  csum += gKern[jj]*tSpek[ii-jj];
980  tSpek[ii] = csum;
981  }
982  start += zKern;
983 
984  // differentiate as requested
985  for(int nd = 0; nd < ndiff; nd++) {
986  for(int ii = tLen-1; ii >0; ii--)
987  tSpek[ii] -= tSpek[ii-1];
988  }
989  //start += ndiff/2;
990 
991  delete [] gKern;
992 }
993 
995 
996 // trigger on a +++--- sequence, first derivative of a gaussian
997 // with conditions on the width and amplitude of the lobes
998 int GWRecal::xP_Next1(float *yDat, int yLen)
999 {
1000  enum {
1001  stateUnknown, // initial or negative before starting a new sequence
1002  stateP, // positive: xP_0(first positive chan),xP_1(max), xP_2(last positive)
1003  stateN // negative: xP_3(first negative), xP_4(min), xP_5(last negative)
1004  };
1005 
1006  int state = stateUnknown; // start in an indefinite state
1007  int widthP = 0, widthN = 0;
1008  float yMax=0;
1009  float yMin=0;
1010 
1011  for(int nn = 0; nn < yLen; nn++) {
1012  float yInp = yDat[nn];
1013  switch (state) {
1014  case stateUnknown: // was below CF threshold in an indefinite state
1015  if(yInp > xP_fThreshCFA) { // and goes above
1016  state = stateP; // initiate a possible trigger sequence
1017  xP_0 = xP_1 = xP_2 = nn;
1018  yMax = yInp;
1019  widthP = 1; // start checking width
1020  } // if it stays below there is nothing to do
1021  break;
1022  case stateP: // was above threshold
1023  if(yInp >= xP_fThreshCFA) { // and stays above
1024  if(yInp > yMax) {
1025  xP_1 = nn; // record maximum of first positive lobe
1026  yMax = yInp;
1027  }
1028  xP_2 = nn;
1029  widthP++; // incr positive width and stay in this state
1030  }
1031  else { // transition to negative
1032  if(widthP >= xP_nMinWidthP && // positive lobe wide enough
1033  //widthP < 4*xP_nMinWidthP && // but not too wide
1034  xP_1 > xP_0 && // and has a maximum
1035  yMax > xP_minAmpl) { // which is large enough
1036  state = stateN; // initiate a negative lobe sequence
1037  xP_3 = xP_4 = xP_5 = nn;
1038  yMin = yInp;
1039  widthN = 1; // start counting width of negative lobe
1040  }
1041  else
1042  state = stateUnknown; // go back to indefinite below threshold
1043  }
1044  break;
1045  case stateN: // below threshold in a valid-sequence state
1046  if(yInp < xP_fThreshCFA) { // and stays below
1047  if(yInp < yMin) {
1048  xP_4 = nn; // record minimum
1049  yMin = yInp;
1050  }
1051  xP_5 = nn;
1052  widthN++; // incr negative width and stay in this state
1053  if(widthN > xP_nMinWidthN && // second positive lobe wide enough for a valid sequence
1054  yInp > yMin && // and signal is beyond minimum
1055  yMin < -xP_minAmpl) { // and minimum large enough
1056  float cmp = abs(yMax/yMin);
1057  if(cmp < 0.2f || cmp > 2.f)
1058  state = stateUnknown;
1059  else
1060  return nn; // this is a good trigger
1061  }
1062  break;
1063  }
1064  else { // goes positive without having triggered
1065  state = stateP; // initiate a new possible trigger sequence
1066  xP_0 = xP_1 = xP_2 = nn;
1067  yMax = yInp;
1068  widthP = 1; // start checking width
1069  break;
1070  }
1071  }
1072  }
1073  return -1;
1074 }
1075 
1077 
1078 // trigger on a +++---++++ sequence
1079 // with conditions on the width and amplitude of the lobes
1080 int GWRecal::xP_Next2(float *yDat, int yLen)
1081 {
1082  enum {
1083  stateUnknown, // initial or negative before starting a new sequence
1084  stateP1, // positive: xP_0(first positive chan),xP_1(max), xP_2(last positive)
1085  stateN, // negative: xP_3(min), xP_4(last negative)
1086  stateP2 // positive: xP_5(max), xP_6(last)
1087  };
1088 
1089  int state = stateUnknown; // start in an indefinite state
1090  int widthP1 = 0, widthN = 0, widthP2 = 0;
1091  float yMax1=0;
1092  float yMin=0;
1093  float yMax2=0;
1094 
1095  for(int nn = 0; nn < yLen; nn++) {
1096  float yInp = yDat[nn];
1097  switch (state) {
1098  case stateUnknown: // was below CF threshold in an indefinite state
1099  if(yInp > xP_fThreshCFA) { // and goes above
1100  state = stateP1; // initiate a possible trigger sequence
1101  xP_0 = xP_1 = xP_2 = nn;
1102  yMax1 = yMin = yInp;
1103  widthP1 = 1; // start checking width
1104  } // if it stays below there is nothing to do
1105  break;
1106  case stateP1: // was above threshold
1107  if(yInp >= xP_fThreshCFA) { // and stays above
1108  if(yInp > yMax1) {
1109  xP_1 = nn; // record maximum of first positive lobe
1110  yMax1 = yInp;
1111  }
1112  xP_2 = nn;
1113  widthP1++; // incr positive width and stay in this state
1114  }
1115  else { // transition to negative
1116  if(widthP1 >= xP_nMinWidthP1 && // positive lobe wide enough
1117  xP_1 > xP_0) { // and has a maximum
1118  state = stateN; // initiate a negative lobe sequence
1119  xP_3 = xP_4 = nn;
1120  yMin = yInp;
1121  widthN = 1; // start counting width of negative lobe
1122  }
1123  else
1124  state = stateUnknown; // go back to indefinite below threshold
1125  }
1126  break;
1127  case stateN: // below threshold in a valid-sequence state
1128  if(yInp < xP_fThreshCFA) { // and stays below
1129  if(yInp < yMin) {
1130  xP_3 = nn; // record minimum
1131  yMin = yInp;
1132  }
1133  xP_4 = nn;
1134  widthN++; // incr positive width and stay in this state
1135  }
1136  else { // transition to positive
1137  if(widthN >= xP_nMinWidthN && // negative lobe wide enough
1138  yMin < -xP_minAmpl && // its amplitude large enough
1139  yMin < -1.1*yMax1 && // and minimum-to-maximum is acceptable
1140  yMin > -4.5*yMax1) { // (exact value is -0.5*exp(1.5)) = -2.2408)
1141  state = stateP2; // initiate the second positive lobe sequence
1142  xP_5 = xP_6 = nn;
1143  yMax2 = yInp;
1144  widthP2 = 1; // start counting width of second positive lobe
1145  }
1146  else {
1147  state = stateP1; // restart a new sequence
1148  xP_0 = xP_1 = xP_2 = nn;
1149  yMax1 = yMin = yInp;
1150  widthP1 = 1; // start checking width
1151  }
1152  }
1153  break;
1154  case stateP2: // above threshold in a valid-sequence state
1155  if(yInp > xP_fThreshCFA) { // and is still above
1156  if(yInp > yMax2) {
1157  xP_5 = nn; // record max of second positive lobe
1158  yMax2 = yInp;
1159  }
1160  xP_6 = nn;
1161  widthP2++; // incr positive width and stay in this state
1162  if(widthP2 > xP_nMinWidthP2 && // second positive lobe wide enough for a valid sequence
1163  yInp < yMax2) { // and signal is beyond maximum
1164  return nn; // this is a good trigger
1165  }
1166  }
1167  else { // goes positive (with or without trigger)
1168  state = stateUnknown; // ready to start a possible new trigger sequence
1169  }
1170  break;
1171  }
1172  }
1173  return -1;
1174 }
1175 
1177 
1178 int GWRecal::FitPeaks(int verbose)
1179 {
1180  if(specPeaks.size() < 1)
1181  return 0;
1182 
1183  // order peaks (needed to merge correctly close neighbours)
1184  sort( specPeaks.begin( ), specPeaks.end( ) );
1185 
1186  const float nwLe = 4.f; // define left border
1187  const float nwOv = 2.f; // check region overlap
1188  const float nwRi = 3.f; // define right border
1189 
1190  Peaks.clear();
1191 
1192  // instead of specFWHMdef, we should use realistic width to define regions and overlaps
1193 
1194  int nmult = 0;
1195  int numpk = int(specPeaks.size());
1196  for(int nn = 0; nn < numpk; nn += 1 + nmult) {
1197  int chanA = int(specPeaks[nn]-nwLe*fCurrentspecFWHMdef);
1198  int chanB = int(specPeaks[nn]+nwRi*fCurrentspecFWHMdef);
1199  chanA = max(1,chanA);
1200  chanB = min(chanB, specLength-2);
1201  nmult = 0;
1202  if(nn < numpk-2) {
1203  // check if the next peaks are in the same region
1204  for(size_t jj = nn+1; jj < specPeaks.size(); jj++) {
1205  if(specPeaks[jj]-nwOv*fCurrentspecFWHMdef >= chanB)
1206  break;
1207  nmult++;
1208  chanB = int(specPeaks[jj]+nwRi*fCurrentspecFWHMdef);
1209  chanB = min(chanB, specLength-2);
1210  }
1211  }
1212 
1213  int np = m_pCFit->CalcGaussianFit(specData, chanA, chanB, specPeaks);
1214 
1215  for(int jj = 0; jj < np; jj++) {
1216  Fitted res;
1217 
1218  res.NSubPeaks = np;
1219  res.BgFrom= m_pCFit->BgFrom();
1220  res.BgTo = m_pCFit->BgTo();
1221  res.BgdOff= m_pCFit->Back0();
1222  res.BgdSlope= m_pCFit->Back1();
1223 
1224  // cout<<"[ "<<res.BgFrom<<" ; "<<res.BgTo<<" ] ==> Bg = "<<res.BgdSlope<<"*x + "<<res.BgdOff<<endl;
1225 
1226  res.area = m_pCFit->Area(jj);
1227  res.ampli = m_pCFit->Amplitude(jj);
1228  res.posi = m_pCFit->Position(jj);
1229  res.fw05 = m_pCFit->Fw05(jj);
1230  res.fw01 = m_pCFit->Fw01(jj);
1231  res.fwhm = m_pCFit->Fwhm(jj);
1232  res.tailL = m_pCFit->W01L(jj);
1233  res.tailR = m_pCFit->W01R(jj);
1234  res.Lambda = m_pCFit->TailLeft(jj);
1235  res.Rho = m_pCFit->TailRight(jj);
1236  res.S = m_pCFit->Step(jj);
1237 
1238  bool bad = (res.area < 10) || (res.fwhm >= 5*fCurrentspecFWHMdef) || (res.tailR > 5.f*res.tailL);
1239  if(verbose&1) {
1240  if(nn || jj) cout<<Form("#1 ");
1241  if(jj == 0) cout<<Form(" %3d ", nn+jj);
1242  else cout<<Form(" %3d", nn+jj);
1243  cout<<Form(" %10.1f %10.2f %10.2f %8.3f %8.3f %8.3f %8.3f %8.3f", res.area, res.ampli, res.posi, res.fw05, res.fw01, res.fwhm, res.tailL, res.tailR);
1244  if(bad)
1245  cout<<Form(" ***");
1246  cout<<Form("\n");
1247  }
1248  if(!bad) {
1249  Peaks.push_back(res);
1250  }
1251  }
1252  }
1253  return Peaks.size();
1254 }
1255 
1257 
1258 // the energies should be already sorted in ascending order
1259 // the peaks will be sorted in largest area order and the
1260 // first Energies.size() of them will be connected to all energies, checking
1261 // how many other peaks match the specific gain
1262 double GWRecal::ECalibration(int nspe, double& offset1_, double& slope1_, double& offset2_, double& slope2_, double& curv2_, int verbose_)
1263 {
1264  vector <Fitted>::iterator Iter;
1265 
1266  if(Energies.size() == 1) {
1267  // select the peak with the largest area
1268  int pn_max = 0;
1269  for(size_t nn = 1; nn < Peaks.size(); nn++) {
1270  if(Peaks[nn].area > Peaks[pn_max].area) {
1271  pn_max = nn;
1272  }
1273  }
1274  offset1_ = slope1_ = 0;
1275  double slope_ = Energies[0] / Peaks[pn_max].posi;
1276  Peaks[pn_max].good = true;
1277  Peaks[pn_max].eref = 0;
1278  if(verbose_&2)
1279  cout<<Form("#2 %5d Slope = %10.6f\n", nspe, slope_);
1280  return slope_;
1281  }
1282 
1283  if( Peaks.size() < 2 || Energies.size() < 2) {
1284  if(verbose_&2)
1285  cout << "# Number of peaks (" << Peaks.size() << ") or number of energies (" << Energies.size() << ") too small" << endl;
1286  return 0;
1287  }
1288 
1289 #if 0
1290  // all peaks used as pivot
1291  int npmax = Peaks.size();
1292  for(int np = 0; np < npmax; np++)
1293  Peaks[np].good = true;
1294 #else
1295  // only the peak with largest area used as pivot
1296  sort( Peaks.begin( ), Peaks.end( ), largerAmplitude() );
1297  int npmax = min( min(Peaks.size(), size_t(4)), Energies.size() ); // limit the number of peaks to min(np,ne,4)
1298  for(int np = 0; np < npmax; np++)
1299  Peaks[np].good = true;
1300  for(size_t np = npmax; np < Peaks.size(); np++)
1301  Peaks[np].good = false;
1302 #endif
1303 
1304  // (re)order the peaks
1305  sort( Peaks.begin( ), Peaks.end( ), smallerPosi() );
1306  if(verbose_&4) {
1307  cout<<Form("# Sorted Peak Positions = (");
1308  for ( Iter = Peaks.begin( ) ; Iter != Peaks.end( ) ; Iter++ ) cout<<Form( " %d", int((*Iter).posi) );
1309  cout<<Form(" )\n");
1310  }
1311 
1312  // Connect every (large) peak with every energy and check how many other (peak,energy) pairs agree with this slope
1313  // The combination with the largest number of agreeing pairs provides the reference gain for the final average
1314  // Equal number of matches are ordered by chi2
1315  int np_max = 0; // number of matches
1316  int np_ind = 0; // reference peak
1317  int np_ref = 0; // reference energy
1318  double np_chi = 1.e40; // to distinguish among equal matchers
1319  double dpos2max = fCurrentspecFWHMdef*fCurrentspecFWHMdef;
1320  for(size_t np = 0; np < Peaks.size(); np++) {
1321  if(!Peaks[np].good)
1322  continue;
1323  int ec_max = 0; // number of matches for peak np
1324  int ec_ref = 0; // reference energy
1325  double ec_chi = 1.e30; // its chi2
1326  for(size_t ne = 0; ne < Energies.size(); ne++) { // connecting peak np with energy ne
1327  double lgain = (Peaks[np].posi)/Energies[ne]; // gives this gain
1328  int match = 0;
1329  double chi2 = 0;
1330  for(size_t nee = 0; nee < Energies.size(); nee++) { // count the number of matches for this combination
1331  double epos = Energies[nee]*lgain; // expected position
1332  for(size_t ip = 0; ip < Peaks.size(); ip++) {
1333  double dpos = Peaks[ip].posi-epos;
1334  double dpos2 = dpos*dpos;
1335  if(dpos2 < dpos2max) {
1336  match++;
1337  chi2 += dpos2;
1338  break;
1339  }
1340  }
1341  }
1342  if( (match > ec_max) || // more matches
1343  ((match == ec_max) && (chi2 < ec_chi)) ) { // or better chi2
1344  ec_max = match; // record the best combination so far
1345  ec_ref = ne;
1346  ec_chi = chi2;
1347  }
1348  }
1349  if( (ec_max > np_max) || // more matches
1350  ((ec_max == np_max) && (ec_chi < np_chi)) ) { // or better chi2
1351  np_max = ec_max; // record the best combination so far
1352  np_ind = np;
1353  np_ref = ec_ref;
1354  np_chi = ec_chi;
1355  }
1356  //if(np_max == Energies.size())
1357  // break; // cannot do better than this ?
1358  }
1359 
1360  double bestSlope = Energies[np_ref]/(Peaks[np_ind].posi);
1361  if(verbose_&4) {
1362  cout<<Form("# Best-match slope %g [p=%d e=%d] with %d values\n", bestSlope, np_ind, np_ref, np_max);
1363  }
1364 
1365  // find the good peaks
1366  for(size_t np = 0; np < Peaks.size(); np++) {
1367  Peaks[np].eref = -1;
1368  Peaks[np].good = false;
1369  }
1370  int match = 0;
1371  for(size_t ne = 0; ne < Energies.size(); ne++) {
1372  double epos = Energies[ne]/bestSlope;
1373  for(size_t np = 0; np < Peaks.size(); np++) {
1374  if(abs(Peaks[np].posi-epos) < fCurrentspecFWHMdef && !Peaks[np].good) {
1375  match++;
1376  Peaks[np].eref = ne;
1377  Peaks[np].good = true;
1378  break;
1379  }
1380  }
1381  }
1382 
1383  // fit slope using the good peaks, starting without errors
1384  double val_xx = 0;
1385  double val_xy = 0;
1386  int used = 0;
1387  for(size_t np = 0; np < Peaks.size(); np++) {
1388  if(Peaks[np].good) {
1389  ++used;
1390  double x = Peaks[np].posi;
1391  double y = Energies[Peaks[np].eref];
1392  val_xx += x*x;
1393  val_xy += x*y;
1394  }
1395  }
1396  double slope0 = val_xy / val_xx;
1397  double slope_ = slope0;
1398 
1399  // if enabled, error**2 for chi1 is: errPos**2 + errEner**2
1400  // errP = slope * sigma/sqrt(Area) Approx for a gaussian peak. Scaled to keV
1401  // errE taken from command line -errors errE (should be specific of each calibration line)
1402 
1403  // Not really worthwhile in actual practice.
1404 
1405  if(useErrors) {
1406  // repete, using errors
1407  val_xx = 0;
1408  val_xy = 0;
1409  used = 0;
1410  for(size_t np = 0; np < Peaks.size(); np++) {
1411  if(Peaks[np].good) {
1412  ++used;
1413  double x = Peaks[np].posi;
1414  double y = Energies[Peaks[np].eref];
1415  double errP = slope0* pow(Peaks[np].fwhm/s2fwhm, 2.)/Peaks[np].area;
1416  double w2 = 1. / (errP*errP+errE*errE);
1417  val_xx += w2*x*x;
1418  val_xy += w2*x*y;
1419  }
1420  }
1421  slope_ = val_xy / val_xx;
1422  }
1423 
1424  bool fit1Done = false;
1425  offset1_ = slope1_ = 0;
1426  if(match>=2 && doPoly1) {
1427  // fit a stright line through the good peaks
1428  double sx0 = 0., sx1 = 0., sx2 = 0., syx0 = 0., syx1 = 0.;
1429  double w2s = 0; // to record the average weight
1430  used = 0;
1431  double w2 = 1.;
1432  for(size_t np = 0; np < Peaks.size(); np++) {
1433  if(Peaks[np].good) {
1434  ++used;
1435  double x = Peaks[np].posi;
1436  double y = Energies[Peaks[np].eref];
1437  if(useErrors) {
1438  double errP = slope_* pow(Peaks[np].fwhm/s2fwhm, 2.)/Peaks[np].area;
1439  w2 = 1. / (errP*errP+errE*errE);
1440  }
1441  sx0 += w2;
1442  sx1 += w2*x;
1443  sx2 += w2*x*x;
1444  syx0 += w2*y;
1445  syx1 += w2*y*x;
1446  w2s += w2;
1447  }
1448  }
1449  if(numZero > 0 ) {
1450  // multiple artificial peaks at (0,0) to help system pass through origin
1451  //double x=0, y=0; // --> only sx0 affected
1452  w2 = w2s/used; // ??
1453  sx0 += w2*numZero; // with multiplicity
1454  }
1455  double deter = sx0*sx2-sx1*sx1;
1456  if(deter > 0.) {
1457  offset1_ = ( sx2*syx0 - sx1*syx1)/deter;
1458  slope1_ = (-sx1*syx0 + sx0*syx1)/deter;
1459 // double err_offset1 = sqrt(sx2/deter);
1460 // double err_slope1 = sqrt(sx0/deter);
1461  fit1Done = true;
1462  }
1463  }
1464 
1465  bool fit2Done = false;
1466  offset2_ = slope2_ = curv2_ = 0;
1467  if(match>=3 && doPoly2) {
1468  // fit a parabola line through the good peaks
1469  double sx0 = 0., sx1 = 0., sx2 = 0., sx3 = 0., sx4 = 0., syx0 = 0., syx1 = 0., syx2 = 0.;
1470  double w2s = 0; // to record the average weight
1471  used = 0;
1472  double w2 = 1.;
1473  for(size_t np = 0; np < Peaks.size(); np++) {
1474  if(Peaks[np].good) {
1475  ++used;
1476  double x = Peaks[np].posi;
1477  double y = Energies[Peaks[np].eref];
1478  if(useErrors) {
1479  double errP = slope_* pow(Peaks[np].fwhm/s2fwhm, 2.)/Peaks[np].area;
1480  w2 = 1. / (errP*errP+errE*errE);
1481  }
1482  sx0 += w2;
1483  sx1 += w2*x;
1484  sx2 += w2*x*x;
1485  sx3 += w2*x*x*x;
1486  sx4 += w2*x*x*x*x;
1487  syx0 += w2*y;
1488  syx1 += w2*y*x;
1489  syx2 += w2*y*x*x;
1490  w2s += w2;
1491  }
1492  }
1493  if(numZero > 0 ) {
1494  // multiple artificial peaks at (0,0) to help system pass through origin
1495  //double x=0, y=0; // --> only sx0 affected
1496  w2 = w2s/used; // ??
1497  sx0 += w2*numZero; // with multiplicity
1498  }
1499  double dd[9]; memset(dd, 0, sizeof(dd)); // row-wise
1500  double xd[3]; memset(xd, 0, sizeof(xd));
1501  // [sx0] [sx1] [sx2] [b0] [ex0]
1502  // [sx1] [sx2] [sx3] * [b1] = [ex1]
1503  // [sx2] [sx3] [sx4] [b2] [ex3]
1504 
1505  dd[0] = sx0; dd[1] = sx1; dd[2] = sx2; xd[0] = syx0;
1506  dd[3] = sx1; dd[4] = sx2; dd[5] = sx3; xd[1] = syx1;
1507  dd[6] = sx2; dd[7] = sx3; dd[8] = sx4; xd[2] = syx2;
1508 
1509  double DD[9];
1510  if(InvertMatrix3(dd, DD)) {
1511  offset2_ = DD[0]*xd[0] + DD[1]*xd[1] + DD[2]*xd[2];
1512  slope2_ = DD[3]*xd[0] + DD[4]*xd[1] + DD[5]*xd[2];
1513  curv2_ = DD[6]*xd[0] + DD[7]*xd[1] + DD[8]*xd[2];
1514  fit2Done = true;
1515  }
1516  }
1517 
1518  if(verbose_&2) {
1519  cout<<Form("#2 %5d Slope = %1.6f", nspe, slope_);
1520  if(fit1Done)
1521  cout<<Form(" Cal1=[ %7.4f %1.6f ]", offset1_, slope1_);
1522  if(fit1Done && fit2Done)
1523  cout<<Form(" Cal2=[ %7.4f %1.6f % .5e ]", offset2_, slope2_, curv2_);
1524  cout<<Form("\n");
1525  size_t nn = 0;
1526  double chi2[5] = {0};
1527  int nused = 0;
1528  for(size_t np = 0; np < Peaks.size(); np++) {
1529  if(Peaks[np].good) {
1530  nused++;
1531  double lineEner = Energies[Peaks[np].eref];
1532  double seenPosi = Peaks[np].posi;
1533  double seenEner = seenPosi*slope_;
1534  double diffEner = seenEner-lineEner;
1535  chi2[0] += diffEner*diffEner;
1536  cout<<Form("#2 %5d %4d %4d %15.1f %10.2f %9.3f %9.3f %11.3f %7.3f",
1537  nspe, int(nn++), int(np), Peaks[np].area, Peaks[np].posi, Peaks[np].fwhm, Peaks[np].fwhm*slope_, lineEner, diffEner);
1538  if(fit1Done) {
1539  seenEner = offset1_ + slope1_*seenPosi;
1540  diffEner = seenEner-lineEner;
1541  cout<<Form(" %7.3f", diffEner);
1542  chi2[1] += diffEner*diffEner;
1543  }
1544  if(fit1Done && fit2Done) {
1545  seenEner = offset2_ + slope2_*seenPosi + curv2_*seenPosi*seenPosi;
1546  diffEner = seenEner-lineEner;
1547  cout<<Form(" %7.3f", diffEner);
1548  chi2[2] += diffEner*diffEner;
1549  }
1550  cout<<Form("\n");
1551  }
1552  }
1553  chi2[0] = (nused-1) ? chi2[0]/(nused-1) : 0;
1554  cout<<Form("#2 %5d Chi2 %73.3f", nspe, chi2[0]);
1555  if(fit1Done) {
1556  chi2[1] = (nused-2) ? chi2[1]/(nused-2) : 0;
1557  cout<<Form(" %7.3f", chi2[1]);
1558  }
1559  if(fit1Done && fit2Done) {
1560  chi2[2] = (nused-3) ? chi2[2]/(nused-3) : 0;
1561  cout<<Form(" %7.3f", chi2[2]);
1562  }
1563  cout<<Form("\n");
1564  }
1565 
1566  return slope_;
1567 }
1568 
1570 
1572 {
1573  return 0;
1574 }
1575 
1577 
1578 bool GWRecal::InvertMatrix3(const double m[9], double invOut[9])
1579 {
1580  double d00 = m[0];
1581  double d01 = m[1];
1582  double d02 = m[2];
1583  double d10 = m[3];
1584  double d11 = m[4];
1585  double d12 = m[5];
1586  double d20 = m[6];
1587  double d21 = m[7];
1588  double d22 = m[8];
1589 
1590  double Det = d00*(d11*d22 - d12*d21) + d01*(d12*d20 - d10*d22) + d02*(d10*d21 - d11*d20);
1591  if (Det == 0)
1592  return false;
1593 
1594  double D00 = d11*d22 - d12*d21; double D01 = d12*d20 - d10*d22; double D02 = d10*d21 - d11*d20;
1595  double D10 = d21*d02 - d22*d01; double D11 = d22*d00 - d20*d02; double D12 = d20*d01 - d21*d00;
1596  double D20 = d01*d12 - d02*d11; double D21 = d02*d10 - d00*d12; double D22 = d00*d11 - d01*d10;
1597 
1598  Det = 1/Det;
1599 
1600  invOut[0] = D00 * Det;
1601  invOut[1] = D01 * Det;
1602  invOut[2] = D02 * Det;
1603  invOut[3] = D10 * Det;
1604  invOut[4] = D11 * Det;
1605  invOut[5] = D12 * Det;
1606  invOut[6] = D20 * Det;
1607  invOut[7] = D21 * Det;
1608  invOut[8] = D22 * Det;
1609 
1610 #if 0
1611 #define indx(n1, n2) (n1*3+n2)
1612  double unit[9]; memset(unit, 0, sizeof(unit));
1613  for(int i1 = 0; i1 < 3; i1++) {
1614  for(int i2 = 0; i2 < 3; i2++) {
1615  double accu = 0;
1616  for(int ii = 0; ii < 3; ii++) {
1617  accu += m[indx(i1,ii)]*invOut[indx(ii,i2)];
1618  }
1619  unit[indx(i1,i2)] = accu;
1620  }
1621  }
1622 #undef indx
1623 #endif
1624 
1625  return true;
1626 }
1627 
1629 
1630 double GWRecal::Calibrated(double x)
1631 {
1632  if(!doSlope) return slope*x;
1633  else if(doPoly2) return offset1 + slope1*x;
1634  else if(doPoly1) return offset2 + (slope2 + curv2*x)*x;
1635  else return tshift + x;
1636 }
1637 
1639 
1640 void GWRecal::SetHistChannelLimits(int SpecNbr, float ChFrom, float ChTo)
1641 {
1642  bool IndexAlreadyDefined = false;
1643  for(size_t i=0 ; i<nlim.size() ; i++)
1644  {
1645  NLimits_t lim = nlim[i];
1646  if(lim.index == SpecNbr)
1647  {
1648  lim.from = (int)ChFrom;
1649  lim.to = (int)ChTo;
1650  IndexAlreadyDefined = true;
1651  }
1652  }
1653  if(!IndexAlreadyDefined)
1654  {
1655  NLimits_t lim;
1656  lim.index = SpecNbr;
1657  lim.from = (int)ChFrom;
1658  lim.to = (int)ChTo;
1659  nlim.push_back(lim);
1660  }
1661 }
1662 
1664 
1665 void GWRecal::SetHistPeaksLimits(int SpecNbr, float FWHM, float Ampli)
1666 {
1667  bool IndexAlreadyDefined = false;
1668  for(size_t i=0 ; i<nwa.size() ; i++)
1669  {
1670  NWA_t tnwa = nwa[i];
1671  if(tnwa.index == SpecNbr)
1672  {
1673  tnwa.width = FWHM;
1674  tnwa.ampli = Ampli;
1675  IndexAlreadyDefined = true;
1676  }
1677  nwa[i] = tnwa;
1678  }
1679  if(!IndexAlreadyDefined)
1680  {
1681  NWA_t tnwa;
1682  tnwa.index = SpecNbr;
1683  tnwa.width = FWHM;
1684  tnwa.ampli = Ampli;
1685  nwa.push_back(tnwa);
1686  }
1687 }
int xP_1
Definition: RecalEnergy.h:212
int numZero
Definition: RecalEnergy.h:205
float width
Definition: RecalEnergy.h:53
int GFitParNumber()
Definition: FitSpek.h:147
double curv2
Definition: RecalEnergy.h:224
int xP_2
Definition: RecalEnergy.h:212
double fwhm
Definition: RecalEnergy.h:39
double tshift
Definition: RecalEnergy.h:222
double Position(int np)
Definition: FitSpek.h:87
double offset2
Definition: RecalEnergy.h:224
string specFormat
Definition: RecalEnergy.h:152
bool useTR
Definition: RecalEnergy.h:192
int Dmode
Definition: RecalEnergy.h:211
double slope2
Definition: RecalEnergy.h:224
void numparams(char *, int)
******************************************************************************************/// ...
double BgFrom
Definition: RecalEnergy.h:32
bool doPoly2
Definition: RecalEnergy.h:204
int fCurrentspecToDef
Definition: RecalEnergy.h:164
void SetHistChannelLimits(int SpecNbr, float ChFrom, float ChTo)
******************************************************************************************/// ...
bool useTL
Definition: RecalEnergy.h:191
int specNN
Definition: RecalEnergy.h:159
double errE
Definition: RecalEnergy.h:209
double Back0()
Definition: FitSpek.h:72
TString fSpectraFolderName
Definition: RecalEnergy.h:99
void GFitParGet(int *pP)
Definition: FitSpek.h:156
int specToDef
Definition: RecalEnergy.h:163
int xP_0
Definition: RecalEnergy.h:212
double posi
Definition: RecalEnergy.h:36
clock_t stopTime
Definition: RecalEnergy.h:255
double slope1
Definition: RecalEnergy.h:223
bool InvertMatrix3(const double m[9], double invOut[9])
******************************************************************************************/// ...
const double m_pi
Definition: FitSpek.h:41
bool bOneLine
Definition: RecalEnergy.h:201
int PeakSearch2(float *data, int chA, int chB, float fwhm, float minAmpl, std::vector< double > &vPeaks)
******************************************************************************************/// ...
double TailLeft(int np)
Definition: FitSpek.h:105
double BgTo
Definition: RecalEnergy.h:33
bool useErrors
Definition: RecalEnergy.h:208
string testFileName
Definition: RecalEnergy.h:178
int CalcGaussianFit(float *pSpek, int chanA, int chanB, std::vector< double > &vPeaks)
Definition: FitSpek.cpp:426
int BgFrom()
Definition: FitSpek.h:64
double tailL
Definition: RecalEnergy.h:40
float fCurrentspecFWHMdef
Definition: RecalEnergy.h:170
int specNs
Definition: RecalEnergy.h:161
double TCalibration()
******************************************************************************************/// ...
double BgdOff
Definition: RecalEnergy.h:30
void SetHistPeaksLimits(int SpecNbr, float FWHM, float Ampli)
******************************************************************************************/// ...
int specFromDef
Definition: RecalEnergy.h:163
int BgTo()
Definition: FitSpek.h:65
int specOffset
Definition: RecalEnergy.h:155
double slope
Definition: RecalEnergy.h:222
double W01R(int np)
Definition: FitSpek.h:100
float specAMPLdef
Definition: RecalEnergy.h:169
double eBstep
Definition: RecalEnergy.h:187
float specFWHMdef
Definition: RecalEnergy.h:168
double area
Definition: RecalEnergy.h:34
int PeakSearch1(float *data, int chA, int chB, float fwhm, float minAmpl, std::vector< double > &vPeaks)
******************************************************************************************/// ...
int FitPeaks(int verbose)
******************************************************************************************/// ...
double Fw01(int np)
Definition: FitSpek.h:97
int xP_5
Definition: RecalEnergy.h:212
double Calibrated(double x)
******************************************************************************************/// ...
FILE * testFP
Definition: RecalEnergy.h:175
bool SpecNameDecode(std::string sName, std::string &sForm, int &slen)
Definition: ReadSpek.cpp:376
int Verbosity
Definition: RecalEnergy.h:228
int xP_6
Definition: RecalEnergy.h:212
bool fFlatBg
Definition: RecalEnergy.h:194
bool doPoly1
Definition: RecalEnergy.h:203
int * specFrom
Definition: RecalEnergy.h:165
string testSuff
Definition: RecalEnergy.h:179
int modXtalk
Definition: RecalEnergy.h:206
float * testData
Definition: RecalEnergy.h:176
bool Read(int slen, int snum=0)
Definition: ReadSpek.cpp:58
bool fAffineBg
Definition: RecalEnergy.h:195
int xP_3
Definition: RecalEnergy.h:212
double fw01
Definition: RecalEnergy.h:38
bool Open(std::string sName, std::string sForm)
Definition: ReadSpek.cpp:10
int index
Definition: RecalEnergy.h:52
int xP_nMinWidthP2
Definition: RecalEnergy.h:218
float * specFWHM
Definition: RecalEnergy.h:172
int xP_nMinWidthP1
Definition: RecalEnergy.h:217
bool TestResults()
******************************************************************************************/// ...
int LargePeaks1(float *data, int chA, int chB, float fwhm, float minAmpl, std::vector< double > &vPeaks, int maxNum)
******************************************************************************************/// ...
ReadSpek Spek
Definition: RecalEnergy.h:157
double S
Definition: RecalEnergy.h:44
bool testResults
Definition: RecalEnergy.h:180
int xP_Next1(float *yVal, int yLen)
******************************************************************************************/// ...
double Fw05(int np)
Definition: FitSpek.h:96
double Area(int np)
Definition: FitSpek.h:81
float testGain
Definition: RecalEnergy.h:177
vector< double > specPeaks
Definition: RecalEnergy.h:182
float xP_minAmpl
Definition: RecalEnergy.h:213
double Rho
Definition: RecalEnergy.h:43
TString fSourceName
Definition: RecalEnergy.h:110
CFitSpek * m_pCFit
Definition: RecalEnergy.h:220
double eBhead
Definition: RecalEnergy.h:186
float xP_fThreshCFA
Definition: RecalEnergy.h:214
float * specAMPL
Definition: RecalEnergy.h:173
void StartCalib()
******************************************************************************************/// ...
Definition: RecalEnergy.cpp:94
bool Close()
Definition: ReadSpek.cpp:36
double Back1()
Definition: FitSpek.h:75
vector< double > Energies
Definition: RecalEnergy.h:183
void AnalyseSources()
******************************************************************************************/// ...
double Step(int np)
Definition: FitSpek.h:102
int NSubPeaks
Definition: RecalEnergy.h:29
int xP_nMinWidthN
Definition: RecalEnergy.h:216
int xP_4
Definition: RecalEnergy.h:212
double fw05
Definition: RecalEnergy.h:37
vector< double > Delendae
Definition: RecalEnergy.h:184
vector< Fitted > Peaks
void SetFormat(string type, int nchan){specFormat = type; specLength = nchan;} // format of the data ...
Definition: RecalEnergy.h:148
ADF::LogMessage & endl(ADF::LogMessage &log)
bool doSlope
Definition: RecalEnergy.h:202
string specName
Definition: RecalEnergy.h:151
float fCurrentspecAMPLdef
Definition: RecalEnergy.h:171
const double s2fwhm
Definition: FitSpek.h:44
double refEner
Definition: RecalEnergy.h:190
double Fwhm(int np)
Definition: FitSpek.h:93
int eBnumb
Definition: RecalEnergy.h:188
double offset1
Definition: RecalEnergy.h:223
void GFitParSet(int *pP)
Definition: FitSpek.h:157
vector< NWA_t > nwa
Definition: RecalEnergy.h:197
GWRecal()
*/
Definition: RecalEnergy.cpp:18
double Amplitude(int np)
Definition: FitSpek.h:84
float * Data()
Definition: ReadSpek.h:38
int specN0
Definition: RecalEnergy.h:160
float hGain
Definition: RecalEnergy.h:226
double TailRight(int np)
Definition: FitSpek.h:108
void SmoothSpek(float *spek, int nchan, double sigma, int ndiff, float *&tSpek, int &start)
******************************************************************************************/// ...
bool bTwoLines
Definition: RecalEnergy.h:200
double Lambda
Definition: RecalEnergy.h:42
int fCurrentspecFromDef
Definition: RecalEnergy.h:164
int xP_nMinWidthP
Definition: RecalEnergy.h:215
double ampli
Definition: RecalEnergy.h:35
double BgdSlope
Definition: RecalEnergy.h:31
clock_t startTime
Definition: RecalEnergy.h:255
int xP_Next2(float *yVal, int yLen)
******************************************************************************************/// ...
void initialize()
******************************************************************************************/// ...
double ECalibration(int nspe, double &offset1, double &slope1_, double &offset2_, double &slope2_, double &curv2_, int verbose_)
******************************************************************************************/// ...
int * specTo
Definition: RecalEnergy.h:166
float * specData
Definition: RecalEnergy.h:154
float ampli
Definition: RecalEnergy.h:54
int specLength
Definition: RecalEnergy.h:153
double tailR
Definition: RecalEnergy.h:41
double W01L(int np)
Definition: FitSpek.h:99
vector< NLimits_t > nlim
Definition: RecalEnergy.h:198