BaselineFinder.cc

00001 #include "Event.hh"
00002 #include "BaselineFinder.hh"
00003 #include "ConvertData.hh"
00004 #include "SumChannels.hh"
00005 #include "intarray.hh"
00006 #include "RootWriter.hh"
00007 #include "TGraph.h"
00008 #include <vector>
00009 #include <cmath>
00010 #include <string>
00011 #include <iomanip>
00012 #include <iostream>
00013 #include <functional>
00014 #include <algorithm>
00015 
00016 BaselineFinder::BaselineFinder():
00017   ChannelModule(GetDefaultName(), "Find the baseline (zero) of the channel in the samples read before the trigger")
00018 {
00019   AddDependency<ConvertData>();
00020   
00021   //Register all the config handler parameters
00022   RegisterParameter("fixed_baseline", fixed_baseline = false,
00023                     "Search for a flat baseline in pre-trigger window, otherwise search for a drifting baseline");
00024   RegisterParameter("signal_begin_time", signal_begin_time = 0,
00025                     "Search for baseline before this time [us] ");
00026 
00027   //parameters for fixed baseline
00028   RegisterParameter("segment_samps", segment_samps = 15,
00029                     "Samples in each baseline segment");
00030   RegisterParameter("min_valid_samps", min_valid_samps = 50,
00031                     "Minimum samples for a baseline to be valid");
00032   RegisterParameter("max_sigma", max_sigma = 1.2,
00033                     "Maximum standard deviation for a baseline segment to be accepted");
00034   RegisterParameter("max_sigma_diff", max_sigma_diff = 1,
00035                     "Maximum difference between baseline sigma and the sigma of a new valid segment");
00036   RegisterParameter("max_mean_diff", max_mean_diff = 1,
00037                     "Maximum difference between baseline mean and the mean of a new valid segment");
00038         
00039   //parameters for linear baseline with few baseline points
00040   RegisterParameter("linear_interpolation", linear_interpolation = false,
00041                                         "Compute further baseline estimates throughout trigger window and perform interpolation");
00042   RegisterParameter("avg_samps", avg_samps = 1000,
00043                                         "Number of contiguous samples to use to perform new baseline estimate");
00044   RegisterParameter("max_sigma_factor", max_sigma_factor = 3,
00045                                         "Samples withing this number of flat baseline sigmas will be considered for baseline estimate");
00046   RegisterParameter("pulse_threshold", pulse_threshold = 4,
00047                                         "Threshold (in number of max_sigmas) to consider a pulse and ignore cooldown samples from baseline estimate");
00048   RegisterParameter("cooldown", cooldown = 2050,
00049                                         "Total number of samples around pulses to ignore for baseline estimate");
00050   RegisterParameter("pre_cooldown", pre_cooldown = 50,
00051                                         "Number of samples before pulse to ignore for baseline estimate");
00052 
00053   //parameters for drifting baseline
00054   RegisterParameter("max_amplitude", max_amplitude = 5,
00055                     "Max amplitude for sample to be considered in baseline");
00056   RegisterParameter("max_sum_amplitude", max_sum_amplitude = 0.2,
00057                     "max_amplitude for sum channel");
00058   RegisterParameter("pre_samps", pre_samps = 5,
00059                     "Samples before to include in moving average");
00060   RegisterParameter("post_samps", post_samps = 5,
00061                     "Samples after to include in average");
00062   RegisterParameter("laserwindow_begin_time", laserwindow_begin_time = -999,
00063                     "Specify the beginning time of a laser window.");           
00064 
00065   RegisterParameter("laserwindow_end_time", laserwindow_end_time = -999,
00066                     "Specify the ending time of a laser window.");                              
00067   RegisterParameter("laserwindow_freeze", laserwindow_freeze = true,
00068                     "Specify whether to always interpolate the baseline in the laser window.");                         
00069   RegisterParameter("save_interpolations", save_interpolations = false, "Save identified interpolation regions as spe");
00070 }
00071 
00072 BaselineFinder::~BaselineFinder()
00073 {
00074   Finalize();
00075 }
00076 
00077 int BaselineFinder::Initialize()
00078 {
00079   return 0;
00080 }
00081 
00082 int BaselineFinder::Finalize()
00083 {   
00084   return 0;
00085 }
00086 
00087 int BaselineFinder::Process(ChannelData* chdata)
00088 {
00089   if(fixed_baseline) return FixedBaseline(chdata);
00090   else return DriftingBaseline(chdata);
00091 }
00092 
00093 int BaselineFinder::DriftingBaseline(ChannelData* chdata)
00094 {
00095   Baseline& baseline = chdata->baseline;
00096   double* wave = chdata->GetWaveform();
00097   std::vector<double>& baseform = chdata->subtracted_waveform;
00098   const int nsamps = chdata->nsamps;
00099   baseform.resize(nsamps);
00100   
00101   //find the maximum sample value within the pre_trigger area
00102   int pre_trig_samp = chdata->TimeToSample(signal_begin_time);
00103   if(pre_trig_samp < 0) pre_trig_samp = pre_samps+post_samps;
00104   if(pre_trig_samp >= nsamps) pre_trig_samp = nsamps-1;
00105   double max_pre_trig = *std::max_element(wave,wave+pre_trig_samp);
00106   if( std::abs(chdata->GetVerticalRange() - max_pre_trig ) < 0.01)
00107     baseline.saturated = true;
00108   //loop through the data, calculating a moving average as we go,
00109   //but only for samples < 2* max_amplitude away from max_pre_trig
00110   //until we find a good baseline
00111   double var = max_amplitude;
00112   if(chdata->channel_id == ChannelData::CH_SUM)
00113     var = max_sum_amplitude;
00114   
00115   double sum = 0;
00116   int sum_samps = 0;
00117   int samp = -1;
00118   int window_samps = pre_samps + post_samps + 1;
00119   int last_good_samp = -1;
00120   double moving_base = -1;
00121   baseline.found_baseline = false;
00122   int laserwindow_begin_samp = chdata->TimeToSample(laserwindow_begin_time);
00123   int laserwindow_end_samp = chdata->TimeToSample(laserwindow_end_time);
00124   while(++samp < chdata->nsamps){
00125     bool pass_amp = false;
00126     if(!baseline.found_baseline && max_pre_trig - wave[samp] < 2*var)
00127       pass_amp = true;
00128     else if(baseline.found_baseline && std::abs(wave[samp]-moving_base)<var)
00129       pass_amp = true;
00130     else if(samp>=laserwindow_begin_samp && samp <= laserwindow_end_samp)
00131       baseline.laserskip = true;
00132     if(laserwindow_freeze && samp>=laserwindow_begin_samp && samp <= laserwindow_end_samp ){
00133       pass_amp = false;
00134       }
00135   
00136     if(pass_amp){
00137       //this sample is part of the baseline
00138       sum += wave[samp];
00139       sum_samps++;
00140       if(sum_samps > window_samps){
00141         //we have collected too many samples, so remove the earliest one
00142         sum -= wave[samp-window_samps];
00143         sum_samps--;
00144       }
00145       if(sum_samps == window_samps){
00146         //we have a validly averaged sample
00147         double mean = sum/sum_samps;
00148         moving_base = mean;
00149         baseform[samp - post_samps] = mean;
00150         if(last_good_samp < samp-post_samps-1){
00151           //linearly interpolate the baseline to fill this region
00152           double preval = mean;
00153           if(last_good_samp >=0)
00154             preval = baseform[last_good_samp];
00155           double slope = (mean-preval)/((samp-post_samps)-last_good_samp);
00156           for(int backsamp = last_good_samp+1; backsamp < samp; backsamp++){
00157             baseform[backsamp] = preval + slope*(backsamp-last_good_samp);
00158           }
00159           if(save_interpolations && last_good_samp >=0){
00160             // locate pe region
00161             Spe pe;
00162             pe.start_time = chdata->SampleToTime(last_good_samp);
00163             pe.length = chdata->SampleToTime(samp-post_samps)-pe.start_time;
00164             int peak_samp = std::min_element(wave+last_good_samp, wave+samp-post_samps)-wave;
00165             pe.peak_time = chdata->SampleToTime(peak_samp);
00166             pe.amplitude = baseform[peak_samp]-wave[peak_samp];
00167             baseline.interpolations.push_back(pe);
00168             baseline.ninterpolations++;
00169           }
00170         }
00171         last_good_samp = samp-post_samps;
00172         if(!baseline.found_baseline){
00173           baseline.found_baseline = true;
00174           baseline.mean = mean;
00175           baseline.search_start_index = samp - window_samps;
00176           baseline.length = window_samps;
00177           //calculate the variance
00178           double sum2 = 0;
00179           for(int backsamp = samp-window_samps+1; backsamp<=samp; backsamp++)
00180             sum2 += wave[backsamp]*wave[backsamp];
00181           baseline.variance = sum2/window_samps - mean*mean;
00182         }
00183       }
00184     }
00185     else{  
00186       //we are part of a real signal now
00187       sum = 0; 
00188       sum_samps = 0;
00189       if(!baseline.found_baseline && samp > pre_trig_samp){
00190         //can't find baseline in pre-trigger region! abort!
00191         return 0;
00192       }
00193       //continue;
00194     }
00195   } // end loop over samples
00196   //fill in the missing part at the end
00197   for(samp = last_good_samp+1; samp<nsamps; samp++){
00198     baseform[samp] = baseform[last_good_samp];
00199   }
00200   //subtract off the baseline
00201   for(samp=0; samp<nsamps; samp++){
00202     baseform[samp] = wave[samp]-baseform[samp];
00203   }
00204       
00205   return 0;
00206 }
00207 
00208 //search for a flat baseline in the pre-trigger window
00209 int BaselineFinder::FixedBaseline(ChannelData* chdata){
00210         
00211         Baseline & baseline = chdata->baseline;
00212         double * wave = chdata->GetWaveform();
00213         const int nsamps = chdata->nsamps;
00214         
00215         //find the maximum sample value within the pre_trigger area
00216         int pre_trig_samp = chdata->TimeToSample(signal_begin_time);
00217         if(pre_trig_samp <= 0 || pre_trig_samp >= nsamps) return 0;
00218         double max_pre_trig = *std::max_element(wave,wave+pre_trig_samp);
00219         double min_pre_trig = *std::min_element(wave,wave+pre_trig_samp);
00220         if( std::abs(chdata->GetVerticalRange() - max_pre_trig ) < 0.01
00221            || min_pre_trig < 0.01)
00222                 baseline.saturated = true;
00223         
00224         double sum=0, sum2=0, mean=0, sigma=0;
00225         double seg_sum=0, seg_sum2=0, seg_mean=0, seg_sigma=0;
00226         int sum_samps = 0;
00227         
00228         baseline.found_baseline = false;
00229         int samp = -1;
00230         while(++samp < chdata->nsamps){
00231                 if(samp>=pre_trig_samp) break;
00232                 
00233                 if(!(samp % segment_samps) && samp){ // another segment recorded
00234                         seg_mean = seg_sum/segment_samps;
00235                         seg_sigma = std::sqrt(seg_sum2/segment_samps-seg_mean*seg_mean);
00236                         
00237                         if(seg_sigma>max_sigma){ //baseline variance is too big, skip this segment
00238                                 // do nothing
00239                         }
00240                         else if(sum<1e-6 && sum2<1e-6) { //initialize the baseline
00241                                 sum  = seg_sum;
00242                                 sum2 = seg_sum2;
00243                                 mean = seg_mean;
00244                                 sigma = seg_sigma;
00245                                 sum_samps = segment_samps;
00246                                 baseline.search_start_index = samp - segment_samps;
00247                         }
00248                         else if(std::fabs(sigma-seg_sigma)<max_sigma_diff && std::fabs(mean-seg_mean)<max_mean_diff){
00249                                 //add new segment to baseline calculateion
00250                                 sum += seg_sum;
00251                                 sum2 += seg_sum2;
00252                                 sum_samps += segment_samps;
00253                                 mean = sum/sum_samps;
00254                                 sigma = sqrt(sum2/sum_samps-mean*mean);
00255                         }
00256                         else if(sum_samps>=min_valid_samps) break; //baseline is valid, ignore the remaining part
00257                         else if(sigma>seg_sigma){ //start new baseline if the new segment has much smaller sigma
00258                                 sum = seg_sum;
00259                                 sum2 = seg_sum2;
00260                                 mean = seg_mean;
00261                                 sigma = seg_sigma;
00262                                 sum_samps = segment_samps;
00263                                 baseline.search_start_index = samp - segment_samps;
00264                         }//end new baseline
00265                         else{ //reset baseline and start over if everything is a mess
00266                                 sum = 0;
00267                                 sum2 = 0;
00268                                 mean = 0;
00269                                 sigma = 0;
00270                                 sum_samps = 0;
00271                         }
00272                         seg_sum = 0;
00273                         seg_sum2 = 0;
00274                 }
00275                 seg_sum  += wave[samp];
00276                 seg_sum2 += wave[samp]*wave[samp];
00277         }//end while loop
00278         
00279         if(sum_samps>=min_valid_samps){
00280                 baseline.found_baseline = true;
00281                 baseline.mean = mean;
00282                 baseline.variance = sigma*sigma;
00283                 baseline.length = sum_samps;
00284                 
00285                 std::vector<double>& baseform = chdata->subtracted_waveform;
00286                 baseform.resize(nsamps);
00287                 
00288                 if(linear_interpolation){
00289                         //new vector to hold events to evaluate baseline
00290                         double max_sigma_lin = max_sigma_factor*1.4142*sigma;
00291                         int cooldown_timer = 0;
00292                         
00293                         std::vector<double> vx;
00294                         std::vector<double> vy;
00295                         double totx=0, toty=0, asamps=0;
00296                         
00297                         //subtract off the baseline
00298                         for(samp=0; samp<nsamps-1; samp++){
00299                                 
00300                                 bool reset = false;
00301                                 
00302                                 if(std::fabs(wave[samp]-wave[samp+1]) < max_sigma_lin && cooldown_timer==0){
00303                                         asamps++;
00304                                         totx+=samp;
00305                                         toty+=wave[samp];
00306                                 }
00307                                 
00308                                 if(asamps>=avg_samps){
00309                                         vx.push_back(totx/asamps);
00310                                         vy.push_back(toty/asamps);
00311                                         reset = true;
00312                                 }
00313                                 
00314                                 if(samp<nsamps-pre_cooldown && std::fabs(wave[samp+pre_cooldown]-mean)>pulse_threshold*max_sigma_lin){
00315                                         cooldown_timer = cooldown;
00316                                         reset = true;
00317                                 }
00318                                 else if(cooldown_timer>0) cooldown_timer--;
00319                                 
00320                                 if(reset){
00321                                         asamps=0;
00322                                         totx=0;
00323                                         toty=0;
00324                                 }
00325                         }
00326                         
00327                         TGraph* gbase = new TGraph( vx.size(), &vx[0], &vy[0]);
00328                         
00329                         for(samp=0; samp<nsamps; samp++){
00330                                 
00331                                 if(samp<nsamps-pre_cooldown && std::fabs(wave[samp+pre_cooldown]-mean)>pulse_threshold*max_sigma_lin) cooldown_timer = cooldown;
00332                                 else if(cooldown_timer>0) cooldown_timer--;
00333                                 
00334                                 double meanp = mean;
00335                                 if(gbase->GetN()>1) meanp = gbase->Eval(samp);
00336                                 
00337                                 if(std::fabs(wave[samp]-meanp) < max_sigma_lin && cooldown_timer == 0) baseform[samp] = 0;
00338                                 else {
00339                                         
00340                                         baseform[samp] = wave[samp]-meanp;
00341                                         
00342                                         if(samp>0 && std::fabs(wave[samp-1]-meanp) < max_sigma_lin) baseform[samp-1] = wave[samp-1]-meanp;
00343                                         if(samp<nsamps-1 && std::fabs(wave[samp+1]-meanp) < max_sigma_lin){
00344                                                 baseform[samp+1] = wave[samp+1]-meanp;
00345                                                 samp++;
00346                                                 if(cooldown_timer>0) cooldown_timer--;
00347                                         }
00348                                 }
00349                         }
00350                         
00351                         gbase->Delete();
00352                 }
00353                 
00354                 else{
00355                         //subtract off the baseline
00356                         for(samp=0; samp<nsamps; samp++){
00357                                 baseform[samp] = wave[samp]-mean;
00358                         }
00359                 }
00360                 
00361                 return 0;
00362         }
00363         else return 0; 
00364 }
00365 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines

Generated on 20 Jun 2014 for daqman by  doxygen 1.6.1