PulseFinder.cc

00001 #include "PulseFinder.hh"
00002 #include "BaselineFinder.hh"
00003 #include "SumChannels.hh"
00004 #include "Integrator.hh"
00005 #include "ConvertData.hh"
00006 #include "RootWriter.hh"
00007 #include "intarray.hh"
00008 #include "TMath.h"
00009 #include <algorithm>
00010 #include <numeric>
00011 #include <stdexcept>
00012 #include <cmath>
00013 
00014 std::ostream& operator<<(std::ostream& out, const PulseFinder::SEARCH_MODE& m)
00015 {
00016   switch(m){
00017   case PulseFinder::VARIANCE:
00018     out<<"VARIANCE";
00019     break;
00020   case PulseFinder::DISCRIMINATOR:
00021     out<<"DISCRIMINATOR";
00022     break;
00023   case PulseFinder::INTEGRAL:
00024     out<<"INTEGRAL";
00025     break;
00026   case PulseFinder::CURVATURE:
00027     out<<"CURVATURE";
00028     break;
00029   }
00030   return out;    
00031 }
00032 
00033 std::istream& operator>>(std::istream& in, PulseFinder::SEARCH_MODE& m)
00034 {
00035   std::string dummy;
00036   in>>dummy;
00037   if(dummy == "VARIANCE" || dummy == "variance")
00038     m = PulseFinder::VARIANCE;
00039   else if (dummy == "DISCRIMINATOR" || dummy == "discriminator")
00040     m = PulseFinder::DISCRIMINATOR;
00041   else if (dummy == "INTEGRAL" || dummy == "integral")
00042     m = PulseFinder::INTEGRAL;
00043   else if (dummy == "CURVATURE" || dummy == "curvature")
00044     m = PulseFinder::CURVATURE;
00045   else{
00046     throw std::invalid_argument(dummy+"is not a valid value for search_mode!");
00047   }
00048   return in;
00049 }
00050 
00051 
00052 PulseFinder::PulseFinder() : 
00053   BaseModule(GetDefaultName(), "Search for individual physical scintillation pulses within the hardware trigger")
00054 {
00055   AddDependency<ConvertData>();
00056   AddDependency<BaselineFinder>();
00057   AddDependency<Integrator>();
00058   
00060   RegisterParameter("align_pulses", align_pulses = true,
00061                     "Have one set of pulse edges for all channels ? (Search done on sum channel in case of multiple channels)"); 
00062   RegisterParameter("search_mode", mode=INTEGRAL);
00063   RegisterParameter("normalize", normalize=true,
00064                     "normalize amplitude to spe before searching?");
00065   RegisterParameter("start_window", start_window = 5);
00066   RegisterParameter("min_start_variance", min_start_variance = 100);
00067   RegisterParameter("min_resolution", min_resolution = 300);
00068   RegisterParameter("min_start_peak_sep", min_start_peak_sep = 5);
00069   RegisterParameter("discriminator_relative", discriminator_relative = false,
00070                     "Is the discriminator value relative to the baseline?");
00071   RegisterParameter("discriminator_value", discriminator_value = 0,
00072                     "Value sample must cross to mark the start of the pulse");
00073   RegisterParameter("discriminator_start_add", discriminator_start_add = 2,
00074                     "Number of samples to add before the detected start of the pulse");
00075   RegisterParameter("discriminator_end_add", discriminator_end_add = 2,
00076                     "Number of samples to add after the detected end of the pulse");
00077   
00078   RegisterParameter("integral_start_time", integral_start_time = 3, 
00079                     "time in us over which photons arrive");
00080   RegisterParameter("integral_end_time", integral_end_time = 3,
00081                     "time over which to evaluate end of pulse to return to 0");
00082   RegisterParameter("integral_start_threshold", integral_start_threshold = 5,
00083                     "amount in npe integral must fall");
00084   RegisterParameter("integral_end_threshold", integral_end_threshold = 3,
00085                     "amount in npe integral must fall");
00086   RegisterParameter("min_sep_time", min_sep_time = 2, 
00087                     "minimum time between starts of two pulses");
00088   RegisterParameter("multipulse_thresh_value", multipulse_thresh_value = 2,
00089                     "secondary must be > this*previous integral+start_thresh");
00090   RegisterParameter("amplitude_start_threshold",amplitude_start_threshold = 0.3,
00091                     "Raw signal must go below this at actual pulse start");  
00092   RegisterParameter("amplitude_end_threshold", amplitude_end_threshold = 0.3,
00093                     "Amplitude must fall below this to end pulse");
00094   RegisterParameter("min_pulse_time", min_pulse_time = 7,
00095                     "Minimum pulse width before it can be considered over");
00096   RegisterParameter("lookback_time", lookback_time = 0.5,
00097                     "Time to compare against for pileup");
00098 
00099   // Curvature Search
00100   RegisterParameter("down_sample_factor", down_sample_factor = 250,
00101                     "Reduce the integral vector size by this factor");
00102   RegisterParameter("pulse_start_curvature", pulse_start_curvature = -4,
00103                     "Curvature threshold to start a new pulse");
00104   RegisterParameter("pile_up_curvature", pile_up_curvature = -20,
00105                     "Curvature threshold to start a pile up pulse");
00106   RegisterParameter("pulse_end_slope", pulse_end_slope = -0.25,
00107                     "Slope threshold to end a pulse");
00108   
00109   // fixed time to evaluate integral
00110   RegisterParameter("fixed_time1", fixed_time1 = 7.,
00111                     "Fixed time at which to evaluate integral for s1 pulses");
00112   RegisterParameter("fixed_time2", fixed_time2 = 30.,
00113                     "Fixed time at which to evaluate integral for s2 pulses");
00114   
00115 }
00116   
00117 PulseFinder::~PulseFinder()
00118 {
00119 }
00120 
00121 int PulseFinder::Initialize()
00122 {
00123   return 0;
00124 }
00125 
00126 int PulseFinder::Finalize()
00127 {
00128   return 0;
00129 }
00130 
00131 int PulseFinder::Process(EventPtr evt)
00132 {
00133     EventDataPtr event = evt->GetEventData();
00134     ChannelData* sum_ch = event->GetChannelByID(ChannelData::CH_SUM);
00135 
00136     //Start search for pulse edges (start and end) ************************************************************************************
00137     std::map<int, std::vector<int> > start_index;
00138     std::map<int, std::vector<int> > end_index;
00139     
00140     if (align_pulses == true
00141         && event->channels.size() > 1)
00142     {
00143         if (! sum_ch)
00144         {
00145             Message(ERROR)<<"PulseFinder.cc: Request to align pulses across channels but sum channels disabled"<<std::endl;
00146             return 0;
00147         }
00148 
00149         event->pulses_aligned = true;
00150 
00151         //Search for pulse edges ONLY on the sum channel
00152         if(! sum_ch->baseline.found_baseline)
00153             return 0;
00154         
00155         start_index[ChannelData::CH_SUM].clear();
00156         end_index[ChannelData::CH_SUM].clear();
00157         
00158         if(mode == DISCRIMINATOR)
00159             DiscriminatorSearch(sum_ch, start_index[ChannelData::CH_SUM], end_index[ChannelData::CH_SUM]);
00160         else if (mode == VARIANCE)
00161             VarianceSearch(sum_ch, start_index[ChannelData::CH_SUM], end_index[ChannelData::CH_SUM]);
00162         else if (mode == INTEGRAL)
00163             IntegralSearch(sum_ch, start_index[ChannelData::CH_SUM], end_index[ChannelData::CH_SUM]);
00164         else if (mode == CURVATURE)
00165             CurvatureSearch(sum_ch, start_index[ChannelData::CH_SUM], end_index[ChannelData::CH_SUM]);
00166 
00167         //Start check for wheteher the start found is too far from the peak when the pulse is s1 ***************************
00168         //(Messy code! Should eventually be moved to search functions)
00169         double* subtracted = sum_ch->GetBaselineSubtractedWaveform();
00170         double* integral = sum_ch->GetIntegralWaveform();
00171         int ratio_samps = (int)(0.02*sum_ch->sample_rate);
00172         for (size_t i = 0; i < start_index[ChannelData::CH_SUM].size();  i++)
00173         {
00174             double pulse_integral = (sum_ch->integral[end_index[ChannelData::CH_SUM][i]] 
00175                                      - sum_ch->integral[start_index[ChannelData::CH_SUM][i]]);
00176             int peak_index = std::min_element(subtracted + start_index[ChannelData::CH_SUM][i], 
00177                                               subtracted + end_index[ChannelData::CH_SUM][i]) - subtracted;
00178             
00179             double ratio1 = 0, ratio2 = 0;
00180             if (peak_index >= ratio_samps && 
00181                 peak_index < sum_ch->nsamps - ratio_samps)
00182             {
00183                 ratio1 = ((integral[peak_index+ratio_samps] - 
00184                            integral[peak_index-ratio_samps]) / 
00185                           pulse_integral);
00186                 ratio2 = ((integral[peak_index-ratio_samps] - 
00187                            integral[start_index[ChannelData::CH_SUM][i]]) / 
00188                           pulse_integral);
00189             }
00190             
00191             if (ratio1 > 0.05 && ratio2 < 0.02)
00192             {//Pulse looks like S1
00193                 double max_s1_peak_sep_time = 0.04;
00194                 int max_sep_samps = (int)(max_s1_peak_sep_time*sum_ch->sample_rate);
00195                 double default_peak_sep_time = 0.02;
00196                 int default_sep_samps = 
00197                     (int)(default_peak_sep_time*sum_ch->sample_rate);
00198                 if (std::abs(start_index[ChannelData::CH_SUM][i] - peak_index) > max_sep_samps)
00199                 {
00200                     //Move start index closer to peak
00201                     start_index[ChannelData::CH_SUM][i] = peak_index - default_sep_samps;
00202                 } 
00203             }
00204         }
00205         //End check for wheteher the start found is too far from the peak when the pulse is s1 ******************************
00206 
00207          //Loop over all other real channels and copy pulse edges from sum channel
00208         for (size_t ch = 0; ch < event->channels.size(); ch++)
00209         {
00210             ChannelData& chdata = event->channels[ch];
00211             //skip channels we've been told to explicitly skip
00212             if(_skip_channels.find(chdata.channel_id) != _skip_channels.end())
00213                 continue;
00214             if (chdata.channel_id == ChannelData::CH_SUM)
00215                 continue;
00216 
00217             start_index[chdata.channel_id] = start_index[ChannelData::CH_SUM];
00218             end_index[chdata.channel_id] = end_index[ChannelData::CH_SUM];
00219         }
00220     }
00221 
00222     else //align_pulses is false or just one channel
00223     {
00224         //Loop over all channels and evaluate pulse edges individually
00225         for (size_t ch = 0; ch < event->channels.size(); ch++)
00226         {
00227             ChannelData& chdata = event->channels[ch];
00228             //skip channels we've been told to explicitly skip
00229             if(_skip_channels.find(chdata.channel_id) != _skip_channels.end())
00230                 continue;
00231             if(! chdata.baseline.found_baseline)
00232                 continue;
00233         
00234             start_index[chdata.channel_id].clear();
00235             end_index[chdata.channel_id].clear();
00236         
00237             if(mode == DISCRIMINATOR)
00238                 DiscriminatorSearch(&chdata, start_index[chdata.channel_id], end_index[chdata.channel_id]);
00239             else if (mode == VARIANCE)
00240                 VarianceSearch(&chdata, start_index[chdata.channel_id], end_index[chdata.channel_id]);
00241             else if (mode == INTEGRAL)
00242                 IntegralSearch(&chdata, start_index[chdata.channel_id], end_index[chdata.channel_id]);
00243             else if (mode == CURVATURE)
00244                 CurvatureSearch(&chdata, start_index[chdata.channel_id], end_index[chdata.channel_id]);
00245 
00246             //Start check for wheteher the start found is too far from the peak when the pulse is s1 ***************************
00247             //(Messy code! Should eventually be moved to search functions)
00248             double* subtracted = chdata.GetBaselineSubtractedWaveform();
00249             double* integral = chdata.GetIntegralWaveform();
00250             int ratio_samps = (int)(0.02*chdata.sample_rate);
00251             for (size_t i = 0; i < start_index[chdata.channel_id].size();  i++)
00252             {
00253                 double pulse_integral = (chdata.integral[end_index[chdata.channel_id][i]] 
00254                                          - chdata.integral[start_index[chdata.channel_id][i]]);
00255                 int peak_index = std::min_element(subtracted + start_index[chdata.channel_id][i], 
00256                                                   subtracted + end_index[chdata.channel_id][i]) - subtracted;
00257                 
00258                 double ratio1 = 0, ratio2 = 0;
00259                 if (peak_index >= ratio_samps && 
00260                     peak_index < chdata.nsamps - ratio_samps)
00261                 {
00262                     ratio1 = ((integral[peak_index+ratio_samps] - 
00263                                integral[peak_index-ratio_samps]) / 
00264                               pulse_integral);
00265                     ratio2 = ((integral[peak_index-ratio_samps] - 
00266                                integral[start_index[chdata.channel_id][i]]) / 
00267                           pulse_integral);
00268                 }
00269                 
00270                 if (ratio1 > 0.05 && ratio2 < 0.02)
00271                 {//Pulse looks like S1
00272                     double max_s1_peak_sep_time = 0.04;
00273                     int max_sep_samps = (int)(max_s1_peak_sep_time*chdata.sample_rate);
00274                     double default_peak_sep_time = 0.02;
00275                     int default_sep_samps = 
00276                         (int)(default_peak_sep_time*chdata.sample_rate);
00277                     if (std::abs(start_index[chdata.channel_id][i] - peak_index) > max_sep_samps)
00278                     {
00279                         //Move start index closer to peak
00280                         start_index[chdata.channel_id][i] = peak_index - default_sep_samps;
00281                     } 
00282                 }
00283             }
00284             //End check for wheteher the start found is too far from the peak when the pulse is s1 ******************************
00285         }
00286     }
00287     //End search for pulse edges (start and end) ************************************************************************************
00288 
00289     //Start evaluation of pulse variables for each pulse on each channel*************************************************************
00290     for (size_t ch = 0; ch < event->channels.size(); ch++)
00291     {
00292         ChannelData& chdata = event->channels[ch];
00293         //skip channels we've been told to explicitly skip
00294         if(_skip_channels.find(chdata.channel_id) != _skip_channels.end())
00295             continue;
00296 
00297         int ch_id = chdata.channel_id;
00298 
00299         for (size_t i = 0; i < start_index[ch_id].size();  i++)
00300         {
00301             if (start_index[ch_id][i] >= end_index[ch_id][i]) 
00302                 return -1;
00303             Pulse pulse;
00304             EvaluatePulse(pulse, &chdata, start_index[ch_id][i], end_index[ch_id][i]);
00305       
00306             if( !chdata.integral.empty())
00307             {
00308                 // Determine if the pulse is clean
00309                 // check front
00310                 if (i > 0)
00311                 {
00312                     pulse.start_clean = (start_index[ch_id][i] > end_index[ch_id][i-1]);
00313                 } 
00314                 else
00315                 {
00316                     // do we want to do it this way?
00317                     pulse.start_clean = true;
00318                 }
00319           
00320                 // check back
00321                 if (i < start_index[ch_id].size() - 1)
00322                 {
00323                     pulse.dt = (start_index[ch_id][i+1] - start_index[ch_id][i])/chdata.sample_rate;
00324                     pulse.end_clean =  (end_index[ch_id][i] < start_index[ch_id][i+1]);
00325                 } 
00326                 else 
00327                 {
00328                     pulse.dt = (chdata.nsamps - 1 - start_index[ch_id][i])/chdata.sample_rate;
00329                     pulse.end_clean = (end_index[ch_id][i] < chdata.nsamps-1);
00330                 }
00331             
00332                 pulse.fixed_int1_valid = (pulse.start_clean && 
00333                                           fixed_time1 < pulse.dt);
00334                 pulse.fixed_int2_valid = (pulse.start_clean && 
00335                                           fixed_time2 < pulse.dt);
00336             
00337                 pulse.is_clean = pulse.start_clean && pulse.end_clean;
00338             }
00339             chdata.pulses.push_back(pulse);
00340             
00341         } // end for loop over pulses
00342         chdata.npulses = chdata.pulses.size();
00343     } //end loop over channels
00344     //End evaluation of pulse variables for each pulse on each channel*************************************************************
00345     return 0;
00346 }
00347 
00348 int PulseFinder::EvaluatePulse(Pulse& pulse, ChannelData* chdata,
00349                                int start_index, int end_index) const
00350 {
00351   if(!chdata->baseline.found_baseline)
00352     return 1;
00353   double* subtracted = chdata->GetBaselineSubtractedWaveform();
00354   int min_index = std::min_element(subtracted + start_index, 
00355                                    subtracted + end_index) - subtracted;
00356   pulse.found_start = true;
00357   pulse.start_index = start_index;
00358   pulse.start_time = chdata->SampleToTime(pulse.start_index);
00359   pulse.found_end = (subtracted[end_index] > 0.);
00360   pulse.end_index = end_index;
00361   pulse.end_time = chdata->SampleToTime(pulse.end_index);
00362   pulse.found_peak = true;
00363   pulse.peak_index = min_index;
00364   pulse.peak_time = chdata->SampleToTime(pulse.peak_index);
00365   pulse.peak_amplitude = -subtracted[min_index]; //pulse are neg, amplitude pos
00366   if(!chdata->integral.empty()){
00367     pulse.integral = chdata->integral[pulse.end_index] - 
00368       chdata->integral[pulse.start_index];
00369     //also look at fparameter
00370     //fparam goes from 10 to 100 ns
00371     
00372     //First reset vector
00373     pulse.f_param.clear();
00374     for(int ft=10; ft <=100; ft+=10)
00375     {
00376       int fsamp = (int)(pulse.start_index+0.001*ft*chdata->sample_rate);
00377       if(fsamp >= chdata->nsamps)
00378         break;
00379       double fp = 
00380         ( chdata->integral[fsamp] - chdata->integral[pulse.start_index] ) / 
00381         pulse.integral;
00382       pulse.f_param.push_back(fp);
00383       if(ft == 90)
00384           pulse.f90 = fp;
00385     }
00386     //look for the time it takes to reach X% of total integral
00387     //remember, integral is negative
00388     int samp = pulse.start_index;
00389     while( samp<pulse.end_index && 
00390            std::abs(chdata->integral[samp]-chdata->integral[pulse.start_index])<
00391            std::abs(pulse.integral)*0.05 ) samp++;
00392     pulse.t05 = chdata->SampleToTime(samp)-pulse.start_time;
00393     while( samp<pulse.end_index && 
00394            std::abs(chdata->integral[samp]-chdata->integral[pulse.start_index])<
00395            std::abs(pulse.integral)*0.10 ) samp++;
00396     pulse.t10 = chdata->SampleToTime(samp)-pulse.start_time;
00397     samp = pulse.end_index-1;
00398     while( samp>=pulse.start_index && 
00399            std::abs(chdata->integral[samp]-chdata->integral[pulse.start_index])>
00400            std::abs(pulse.integral)*0.95 ) samp--;
00401     pulse.t95 = chdata->SampleToTime(++samp)-pulse.start_time;
00402     while( samp>=pulse.start_index && 
00403            std::abs(chdata->integral[samp]-chdata->integral[pulse.start_index])>
00404            std::abs(pulse.integral)*0.90 ) samp--;
00405     pulse.t90 = chdata->SampleToTime(++samp)-pulse.start_time;
00406     
00407     //evaluate the fixed integrals
00408     samp = chdata->TimeToSample(pulse.start_time+fixed_time1,true);
00409     pulse.fixed_int1 = chdata->integral[samp] - 
00410       chdata->integral[pulse.start_index];
00411     samp = chdata->TimeToSample(pulse.start_time+fixed_time2,true);
00412     pulse.fixed_int2 = chdata->integral[samp] - 
00413       chdata->integral[pulse.start_index];
00414     
00415   }
00416   pulse.npe = -pulse.integral/chdata->spe_mean;
00417   //Check to see if peak is saturated
00418   double* wave = chdata->GetWaveform();
00419   if(wave[min_index] == 0){
00420     pulse.peak_saturated = true;
00421     int min_end_index = min_index + 1;
00422     while (wave[min_end_index] == 0 && min_end_index < end_index)
00423       {
00424         min_end_index++;
00425       }
00426     pulse.peak_index = (int)(min_index + min_end_index)/2;
00427   }
00428   return 0;
00429 }
00430                                 
00431 
00432 void PulseFinder::VarianceSearch(ChannelData* chdata, 
00433                                  std::vector<int>& start_index,
00434                                  std::vector<int>& end_index)
00435 {
00436   Baseline& baseline = chdata->baseline;
00437   int index=start_window;
00438   double* wave = chdata->GetWaveform();
00439   double start_baseline;
00440   bool found_start;
00441   for(index = start_window; index < chdata->nsamps; index++)
00442     {
00443       if(start_index.size() > 5)
00444         {
00445           break;
00446         }
00447       //look for the starts of pulses 
00448       //pulse must decrease by more than baseline variance for start_window 
00449       //consecutive samples, and be less than start_baseline-minvariance*var at end
00450       
00451       found_start = true;
00452       for(int samp=index-start_window; samp < index; samp++)
00453         {
00454           if( wave[samp]-wave[samp+1] < baseline.variance )
00455             {
00456               found_start = false;
00457               break;
00458             }
00459         }
00460       if (start_index.size() == 0)
00461         start_baseline = baseline.mean;
00462       else
00463         start_baseline = wave[index-start_window];
00464       
00465       if(!found_start || 
00466          wave[index] > start_baseline-min_start_variance * baseline.variance )
00467         continue;
00468       //if we get here, we have found a start point
00469       start_index.push_back(index-start_window);
00470       index = index + min_resolution;
00471     }//end loop through index
00472   
00473   //Look for ends of pulses
00474   
00475   for (size_t i = 0; i < start_index.size(); i++)
00476     {
00477       int limit_index;
00478       if (i == start_index.size() - 1)
00479         limit_index = chdata->nsamps;
00480       else
00481         limit_index = start_index[i+1];
00482       
00483       int min_index = std::min_element(wave + start_index[i], wave + limit_index) - wave;
00484       if(wave[min_index] > baseline.mean)
00485         end_index.push_back(min_index);
00486       else
00487         end_index.push_back(limit_index - 1);
00488     }
00489   
00490 }
00491 
00492 void PulseFinder::DiscriminatorSearch( ChannelData* chdata,
00493                                        std::vector<int>& start_index,
00494                                        std::vector<int>& end_index)
00495 {
00496   double* wave = chdata->GetWaveform();
00497   double check_val = discriminator_value;
00498   if(discriminator_relative)
00499     wave = chdata->GetBaselineSubtractedWaveform();
00500 
00501   for(int index = discriminator_start_add; 
00502       index < chdata->nsamps - discriminator_end_add ; index++){
00503     if(wave[index] < check_val){
00504       start_index.push_back( index - discriminator_start_add );
00505       while(++index < chdata->nsamps-discriminator_end_add-1 && 
00506             wave[index] < check_val && 
00507             wave[index + discriminator_end_add] < check_val) {}
00508       index += discriminator_end_add;
00509       end_index.push_back( index );
00510       index += discriminator_start_add;
00511     }
00512   }
00513   
00514 }
00515 
00516 void LinearRegression(double* start, double* end, 
00517                       double& slope, double& intercept)
00518 {
00519   //assume x starts at 0
00520   int N = (end - start);
00521   double delta = 1.*N*N/12. * (N*N-1);
00522   double sumx=0, sumy=0, sumxy=0, sumx2=0;
00523   for(int x=0; x<N; x++){
00524     sumx += x;
00525     sumx2 += x*x;
00526     sumy += *(start+x);
00527     sumxy += *(start+x)*x;
00528   }
00529   slope = 1./delta * (N*sumxy - sumx*sumy);
00530   intercept = 1./delta * (sumx2*sumy - sumx*sumxy);
00531 }
00532 
00533 void PulseFinder::IntegralSearch( ChannelData* chdata,
00534                                   std::vector<int>& start_index,
00535                                   std::vector<int>& end_index)
00536 {
00537   double scale_factor = normalize ? chdata->spe_mean : 1;
00538   
00539   double* integral = chdata->GetIntegralWaveform();
00540   double* wave = chdata->GetBaselineSubtractedWaveform();
00541   int start_samps = (int)(integral_start_time * chdata->sample_rate); 
00542   int end_samps = (int)(integral_end_time * chdata->sample_rate);
00543   int min_pulse_samps = (int)(min_pulse_time * chdata->sample_rate);
00544   if(start_samps <= 0) start_samps = 1;
00545   int samp = -1;
00546   //threshold is updated continuously if we are in a pulse based on the 
00547   // expected arrival of photons
00548   bool in_pulse = false;
00549   while ( ++samp < chdata->nsamps){
00550     int lookback_samps = std::min(samp,
00551                                   (int)(lookback_time*chdata->sample_rate));
00552     double int_thresh = integral_start_threshold;
00553     double amp_thresh = amplitude_start_threshold;
00554     //actions are different depending on whether we are already in a pulse
00555     if(in_pulse){
00556       //first, test to see if we've hit the end of the pulse
00557       if(samp - start_index.back() >= min_pulse_samps){ //is it long enough?
00558         int test_samp = std::min(chdata->nsamps-1, samp+end_samps);
00559         if( (integral[samp] - integral[test_samp] )/scale_factor < 
00560             integral_end_threshold  && 
00561             (*std::min_element(wave+samp,wave+test_samp))/scale_factor > 
00562             -amplitude_end_threshold ){
00563           //we are at the end of this pulse
00564           in_pulse = false;
00565           end_index.push_back(samp);
00566           continue;
00567         }
00568       }
00569       
00570       //now check for pileup
00571       
00572       //see if the amplitude is generally still increasing - if so skip
00573       if(integral[samp-lookback_samps] - integral[samp] > 
00574          (integral[samp-2*lookback_samps] - integral[samp-lookback_samps]) * 
00575          multipulse_thresh_value)
00576         continue;
00577       //look for an excursion greater than the maximum in lookback samps
00578       //and with integral > max expected over that region
00579       double prev_max = -(*std::min_element(wave+samp-lookback_samps,
00580                                             wave+samp)) / scale_factor;
00581       //ratio between lookback and next must be ratio of sizes * factor
00582       double halfback =  (integral[samp-lookback_samps/2] - integral[samp] );
00583       double ratio = halfback / 
00584         (integral[samp-lookback_samps] - 
00585          integral[samp-lookback_samps/2] ) ;
00586       
00587       double integral_est = halfback*ratio*
00588         (2*start_samps/lookback_samps ) / scale_factor;
00589       amp_thresh += prev_max*multipulse_thresh_value;
00590       int_thresh += integral_est*multipulse_thresh_value;
00591     }
00592 
00593     //first look for something that crosses the signal threshold in amplitude
00594     if(wave[samp]/scale_factor > -amp_thresh) continue;
00595     //if we get here, signal is past threshold.  Make sure it's not isolated
00596     int test_samp = std::min(chdata->nsamps-1, samp+start_samps);
00597     if((integral[samp]-integral[test_samp])/scale_factor > 
00598        int_thresh){
00599       //we have found a pulse!
00600       int good_samp = std::max(0,samp-2);
00601       if(in_pulse)
00602         end_index.push_back(good_samp);
00603       in_pulse = true;
00604       start_index.push_back(good_samp);
00605       samp += (int)(min_sep_time * chdata->sample_rate);
00606       continue;
00607     }
00608   
00609   
00610   }//end loop over samples
00611   if(in_pulse)
00612     end_index.push_back(chdata->nsamps-1);
00613   
00614 }
00615 
00616 void PulseFinder::CurvatureSearch( ChannelData* chdata,
00617                                    std::vector<int>& start_index,
00618                                    std::vector<int>& end_index) {
00619   double* integral = chdata->GetIntegralWaveform();
00620 
00621   int df = down_sample_factor;
00622   int n = chdata->nsamps/df;
00623   std::vector<double> sm(n);
00624   for(int i=0; i<n; i++){
00625     sm[i] = integral[i*df];
00626   }
00627 
00628   std::vector<double> diff(n);
00629   diff[0]= sm[1];
00630   for (int i=1; i<n-1; i++){
00631     diff[i] = sm[i+1]-sm[i-1];
00632   }
00633 
00634   std::vector<double> curve(n);
00635   curve[0] = diff[1];
00636   for (int i=1; i<n-1; i++){
00637     curve[i] = diff[i+1]-diff[i-1];
00638   }
00639 
00640   bool in_pulse = false;
00641   bool before_peak = true;  // before peak of diff
00642   int last = -1; // index of last local maximum on diff
00643   for (int i=0; i<n-1; i++){
00644     if (!in_pulse){
00645       if (curve[i] < pulse_start_curvature){
00646         in_pulse = true;
00647         int start = i*df;
00648         //while (integral[start+1] >= integral[start]){
00649         //start++;
00650         //}
00651         int loopcount=0;
00652         int maxloop = df;
00653         if(i<n-2) maxloop+= df;
00654         double* sub = chdata->GetBaselineSubtractedWaveform();
00655         while( ++loopcount<maxloop && -sub[start] < amplitude_start_threshold)
00656           start++;
00657         start_index.push_back(start-2 > 0 ? start-2 : 0);
00658       }
00659     }
00660     else{ // in pulse
00661       if (before_peak){ // look for peak of diff
00662         if (curve[i] > 0){
00663           before_peak = false;
00664         }
00665       }
00666       else { // after peak of diff
00667         if (curve[i] < 0 && last < 0){ // keep track of last diff maximum
00668           last = i;
00669         }
00670         if (curve[i] > 0 && last > 0){ // last diff maximum not start of pileup
00671           last = -1;
00672         }
00673 
00674         if (curve[i] < pile_up_curvature){  // found pile up
00675           end_index.push_back(last*df);
00676           start_index.push_back(last*df);
00677           before_peak = true;
00678           last = -1;
00679         }
00680         else if (diff[i] > pulse_end_slope){ // pulse gradually ends
00681           end_index.push_back(i*df);
00682           in_pulse = false;
00683           before_peak = true;
00684           last = -1;
00685         }
00686       }
00687     }
00688   }
00689   if (in_pulse){
00690     end_index.push_back(chdata->nsamps-1);
00691   }  
00692 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines

Generated on 20 Jun 2014 for daqman by  doxygen 1.6.1