All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
RawDigitCorrelatedCorrectionAlg.cc
Go to the documentation of this file.
1 
2 #include <cmath>
3 #include <algorithm>
4 #include <vector>
5 
7 
8 #include "art/Framework/Core/ModuleMacros.h"
9 #include "messagefacility/MessageLogger/MessageLogger.h"
12 
13 #include <cmath>
14 #include <algorithm>
15 #include <numeric> // std::accumulate and std::inner_product
16 
17 #include "TVirtualFFT.h"
18 
19 namespace caldata
20 {
21 //----------------------------------------------------------------------------
22 /// Constructor.
23 ///
24 /// Arguments:
25 ///
26 /// pset - Fcl parameters.
27 ///
29 {
30  reconfigure(pset);
31 
32  // Report.
33  mf::LogInfo("RawDigitCorrelatedCorrectionAlg") << "RawDigitCorrelatedCorrectionAlg configured\n";
34 }
35 
36 //----------------------------------------------------------------------------
37 /// Destructor.
39 {}
40 
41 //----------------------------------------------------------------------------
42 /// Reconfigure method.
43 ///
44 /// Arguments:
45 ///
46 /// pset - Fcl parameter set.
47 ///
48 void RawDigitCorrelatedCorrectionAlg::reconfigure(fhicl::ParameterSet const & pset)
49 {
50  fTruncMeanFraction = pset.get<float> ("TruncMeanFraction", 0.15);
51  fApplyCorSmoothing = pset.get<bool> ("ApplyCorSmoothing", true);
52  fApplyFFTCorrection = pset.get<bool> ("ApplyFFTCorrection", true);
53  fFillFFTHistograms = pset.get<bool> ("FillFFTHistograms", false);
54  fFFTHistsWireGroup = pset.get<std::vector<size_t>>("FFTHistsWireGroup", std::vector<size_t>() = {1, 33, 34});
55  fFFTNumHists = pset.get<std::vector<size_t>>("FFTNumWaveHistograms", std::vector<size_t>() = {10,48,48});
56  fFFTHistsStartTick = pset.get<std::vector<double>>("FFTWaveHistsStartTick", std::vector<double>() = {96.,96.,7670.});
57  fFFTMinPowerThreshold = pset.get<std::vector<double>>("FFTPowerThreshold", std::vector<double>() = {100.,75.,500.});
58  fNumWiresToGroup = pset.get<std::vector<size_t>>("NumWiresToGroup", std::vector<size_t>() = {48, 48, 96});
59  fFillHistograms = pset.get<bool> ("FillHistograms", false);
60  fRunFFTCorrected = pset.get<bool> ("RunFFTCorrectedWires", false);
61  fNumRmsToSmoothVec = pset.get<std::vector<float>> ("NumRmsToSmooth", std::vector<float>() = {3.6, 3.6, 4.});
62 }
63 
64 //----------------------------------------------------------------------------
65 /// Begin job method.
66 void RawDigitCorrelatedCorrectionAlg::initializeHists(art::ServiceHandle<art::TFileService>& tfs)
67 {
68 }
69 
70 void RawDigitCorrelatedCorrectionAlg::smoothCorrectionVec(std::vector<float>& corValVec, unsigned int& viewIdx) const
71 {
72  // First get the truncated mean and rms for the input vector (noting that it is not in same format as raw data)
73  // We need a local copy so we can sort it
74  std::vector<float> localCorValVec = corValVec;
75 
76  std::sort(localCorValVec.begin(),localCorValVec.end());
77 
78  int nTruncVal = (1. - fTruncMeanFraction) * localCorValVec.size();
79  float corValSum = std::accumulate(localCorValVec.begin(),localCorValVec.begin() + nTruncVal,0.);
80  float meanCorVal = corValSum / float(nTruncVal);
81 
82  std::vector<float> diffVec(nTruncVal);
83  std::transform(localCorValVec.begin(),localCorValVec.begin() + nTruncVal, diffVec.begin(), std::bind(std::minus<float>(),std::placeholders::_1,meanCorVal));
84 
85  float rmsValSq = std::inner_product(diffVec.begin(),diffVec.end(),diffVec.begin(),0.);
86  float rmsVal = std::sqrt(rmsValSq / float(nTruncVal));
87 
88  // Now set up to run through and do a "simple" interpolation over outliers
89  std::vector<float>::iterator lastGoodItr = corValVec.begin();
90 
91  bool wasOutlier(false);
92 
93  for(std::vector<float>::iterator corValItr = lastGoodItr+1; corValItr != corValVec.end(); corValItr++)
94  {
95  if (fabs(*corValItr - meanCorVal) < fNumRmsToSmoothVec.at(viewIdx)*rmsVal)
96  {
97  if (wasOutlier)
98  {
99  float lastVal = *lastGoodItr;
100  float curVal = *corValItr;
101  float numTicks = std::distance(lastGoodItr,corValItr);
102  float slope = (curVal - lastVal) / numTicks;
103 
104  while(lastGoodItr != corValItr)
105  {
106  *lastGoodItr++ = (numTicks - std::distance(lastGoodItr,corValItr)) * slope + lastVal;
107  }
108  }
109 
110  wasOutlier = false;
111  lastGoodItr = corValItr;
112  }
113  else wasOutlier = true;
114  }
115 
116  return;
117 }
118 
120  unsigned int planeIdx,
121  std::vector<float>& truncMeanWireVec,
122  std::vector<float>& truncRmsWireVec,
123  std::vector<short>& minMaxWireVec,
124  std::vector<short>& meanWireVec,
125  std::vector<float>& skewnessWireVec,
126  std::vector<float>& neighborRatioWireVec,
127  std::vector<float>& pedCorWireVec,
128  unsigned int& fftSize, unsigned int& halfFFTSize,
129  void* fplan, void* rplan) const
130 {
131  // This method represents and enhanced implementation of "Corey's Algorithm" for correcting the
132  // correlated noise across a group of wires. The primary enhancement involves using a FFT to
133  // "fit" for the underlying noise as a way to reduce the impact on the signal.
134  WireToRawDigitVecMap& wireToRawDigitVecMap = digitIdxPair.first;
135  WireToAdcIdxMap& wireToAdcIdxMap = digitIdxPair.second;
136 
137  size_t maxTimeSamples(wireToRawDigitVecMap.begin()->second.size());
138  size_t baseWireIdx(wireToRawDigitVecMap.begin()->first - wireToRawDigitVecMap.begin()->first % fNumWiresToGroup[planeIdx]);
139 
140  std::vector<float> corValVec(maxTimeSamples);
141 
142  // First step is to get the correction values to apply to this set of input waveforms
143  // Don't try to do correction if too few wires unless they have gaps
144  if (wireToAdcIdxMap.size() > 2) // || largestGapSize > 2)
145  {
146  // Zero? This is probably not necessary
147  std::fill(corValVec.begin(),corValVec.end(),0.);
148 
149  // Build the vector of corrections for each time bin
150  for(size_t sampleIdx = 0; sampleIdx < maxTimeSamples; sampleIdx++)
151  {
152  // Define a vector for accumulating values...
153  // Loop over the wires at this time bin and get their pedestal corrected ADC values
154  // We'll use a simple stl vector for this
155  std::vector<float> adcValuesVec;
156 
157  for(const auto& wireAdcItr : wireToAdcIdxMap)
158  {
159  // Check that we should be doing something in this range
160  // Note that if the wire is not to be considered then the "start" bin will be after the last bin
161  if (sampleIdx < wireAdcItr.second.first || sampleIdx >= wireAdcItr.second.second) continue;
162 
163  int wireIdx(wireAdcItr.first - baseWireIdx);
164 
165  // Accumulate
166  adcValuesVec.push_back(float(wireToRawDigitVecMap.at(wireAdcItr.first)[sampleIdx]) - truncMeanWireVec[wireIdx]);
167  }
168 
169 // float medianValue = getMedian(adcValuesVec, float(-10000.));
170  float aveValue = std::accumulate(adcValuesVec.begin(),adcValuesVec.end(),0.) / float(adcValuesVec.size());
171 
172 // corValVec[sampleIdx] = medianValue;
173  corValVec[sampleIdx] = aveValue;
174  }
175 
176  // Try to eliminate any real outliers
177  if (fApplyCorSmoothing) smoothCorrectionVec(corValVec, planeIdx);
178 
179  // Get the FFT correction
180  if (fApplyFFTCorrection) {
181  std::vector<std::complex<double>> fftOutputVec(halfFFTSize);
182  util::LArFFTW lfftw(fftSize, fplan, rplan, 0);
183  lfftw.DoFFT(corValVec, fftOutputVec);
184 
185  std::vector<double> powerVec(halfFFTSize);
186  std::transform(fftOutputVec.begin(), fftOutputVec.begin() + halfFFTSize, powerVec.begin(), [](const auto& val){return std::abs(val);});
187 
188  // Want the first derivative
189  std::vector<double> firstDerivVec(powerVec.size(), 0.);
190 
191  //fWaveformTool->firstDerivative(powerVec, firstDerivVec);
192  for(size_t idx = 1; idx < firstDerivVec.size() - 1; idx++)
193  firstDerivVec.at(idx) = 0.5 * (powerVec.at(idx + 1) - powerVec.at(idx - 1));
194 
195  // Find the peaks
196  std::vector<std::tuple<size_t,size_t,size_t>> peakTupleVec;
197 
198  findPeaks(firstDerivVec.begin(),firstDerivVec.end(),peakTupleVec,fFFTMinPowerThreshold[planeIdx],0);
199 
200  if (!peakTupleVec.empty())
201  {
202  for(const auto& peakTuple : peakTupleVec)
203  {
204  size_t startTick = std::get<0>(peakTuple);
205  size_t stopTick = std::get<2>(peakTuple);
206 
207  if (stopTick > startTick)
208  {
209  std::complex<double> slope = (fftOutputVec[stopTick] - fftOutputVec[startTick]) / double(stopTick - startTick);
210 
211  for(size_t tick = startTick; tick < stopTick; tick++)
212  {
213  std::complex<double> interpVal = fftOutputVec[startTick] + double(tick - startTick) * slope;
214 
215  fftOutputVec[tick] = interpVal;
216  //fftOutputVec[fftDataSize - tick - 1] = interpVal;
217  }
218  }
219  }
220 
221  std::vector<double> tmpVec(corValVec.size());
222 
223  lfftw.DoInvFFT(fftOutputVec, tmpVec);
224 
225  std::transform(corValVec.begin(),corValVec.end(),tmpVec.begin(),corValVec.begin(),std::minus<double>());
226  }
227  } // fApplyFFTCorrection
228 
229  // Now go through and apply the correction
230  for(size_t sampleIdx = 0; sampleIdx < maxTimeSamples; sampleIdx++)
231  {
232  // Now run through and apply correction
233  for (const auto& wireAdcItr : wireToAdcIdxMap)
234  {
235  float corVal(corValVec[sampleIdx]);
236  int wireIdx(wireAdcItr.first - baseWireIdx);
237 
238  // If the "start" bin is after the "stop" bin then we are meant to skip this wire in the averaging process
239  // Or if the sample index is in a chirping section then no correction is applied.
240  // Both cases are handled by looking at the sampleIdx
241  if (sampleIdx < wireAdcItr.second.first || sampleIdx >= wireAdcItr.second.second)
242  corVal = 0.;
243 
244  //RawDigitVector& rawDataTimeVec = wireToRawDigitVecMap.at(wireIdx);
245  short& rawDataTimeVal = wireToRawDigitVecMap.at(wireAdcItr.first)[sampleIdx];
246 
247  // Probably doesn't matter, but try to get slightly more accuracy by doing float math and rounding
248  float newAdcValueFloat = float(rawDataTimeVal) - corVal - pedCorWireVec[wireIdx];
249  rawDataTimeVal = std::round(newAdcValueFloat);
250  }
251  }
252  }
253  return;
254 }
255 
256 template<class T> T RawDigitCorrelatedCorrectionAlg::getMedian(std::vector<T>& valuesVec, T defaultValue) const
257 {
258  T medianValue(defaultValue);
259 
260  if (!valuesVec.empty())
261  {
262  std::sort(valuesVec.begin(),valuesVec.end());
263 
264  size_t medianIdx = valuesVec.size() / 2;
265 
266  medianValue = valuesVec[medianIdx];
267 
268  if (valuesVec.size() > 1 && medianIdx % 2) medianValue = (medianValue + valuesVec[medianIdx+1]) / 2;
269  }
270 
271  return std::max(medianValue,defaultValue);
272 }
273 
274 template <typename T> void RawDigitCorrelatedCorrectionAlg::findPeaks(typename std::vector<T>::iterator startItr,
275  typename std::vector<T>::iterator stopItr,
276  std::vector<std::tuple<size_t,size_t,size_t>>& peakTupleVec,
277  T threshold,
278  size_t firstTick) const
279 {
280  // Need a minimum distance or else nothing to do
281  if (std::distance(startItr,stopItr) > 4)
282  {
283  // This is a divide and conquer algorithm, start by finding the maximum element.
284  typename std::vector<T>::iterator firstItr = std::max_element(startItr,stopItr,[](float left, float right){return std::fabs(left) < std::fabs(right);});
285 
286  // Are we over threshold?
287  if (std::fabs(*firstItr) > threshold)
288  {
289  // What am I thinking?
290  // First task is to find the "other" lobe max point
291  // Set one to the "first", the other to the "second"
292  // Search backward from first to find start point, forward from second to find end point
293  // Set mid point between first and second as "peak"?
294  typename std::vector<T>::iterator secondItr = firstItr;
295 
296  // Assume if max bin is positive then second lobe is later
297  if (*firstItr > 0)
298  {
299  typename std::vector<T>::iterator tempItr = secondItr;
300 
301  while(tempItr != stopItr)
302  {
303  if (*++tempItr < -threshold)
304  {
305  if (*tempItr < *secondItr) secondItr = tempItr;
306  }
307  else if (secondItr != firstItr) break;
308  }
309  }
310  // Otherwise it goes the other way
311  else
312  {
313  typename std::vector<T>::iterator tempItr = secondItr;
314 
315  while(tempItr != startItr)
316  {
317  if (*--tempItr > threshold)
318  {
319  if (*tempItr > *secondItr) secondItr = tempItr;
320  }
321  else if (secondItr != firstItr) break;
322  }
323 
324  std::swap(firstItr,secondItr);
325  }
326 
327  // It might that no real pulse was found
328  if (firstItr != secondItr)
329  {
330  // Get the "peak" position
331  size_t peakBin = std::distance(startItr,firstItr) + std::distance(firstItr,secondItr) / 2;
332 
333  // Advance (forward or backward) the first and second iterators to get back to zero crossing
334  while(firstItr != startItr) if (*--firstItr < 0.) break;
335  while(secondItr != stopItr) if (*++secondItr > 0.) break;
336 
337  size_t firstBin = std::distance(startItr,firstItr);
338  size_t lastBin = std::distance(startItr,secondItr);
339 
340  // Find leading peaks
341  findPeaks(startItr, firstItr, peakTupleVec, threshold, firstTick);
342 
343  // Save this peak
344  peakTupleVec.push_back(std::tuple<size_t,size_t,size_t>(firstBin+firstTick,peakBin+firstTick,lastBin+firstTick));
345 
346  // Find downstream peaks
347  findPeaks(secondItr, stopItr, peakTupleVec, threshold, firstTick + std::distance(startItr,secondItr));
348  }
349  }
350  }
351 
352  return;
353 }
354 
355 
356 
357 }
bool fApplyFFTCorrection
Use an FFT to get the correlated noise correction.
static constexpr Sample_t transform(Sample_t sample)
std::vector< double > fFFTHistsStartTick
Starting tick for histograms.
walls no right
Definition: selectors.fcl:105
bool fFillFFTHistograms
Fill associated FFT histograms.
std::pair< WireToRawDigitVecMap, WireToAdcIdxMap > RawDigitAdcIdxPair
bool fApplyCorSmoothing
Attempt to smooth the correlated noise correction?
std::vector< size_t > fFFTHistsWireGroup
Wire Group to pick on.
RawDigitCorrelatedCorrectionAlg(fhicl::ParameterSet const &pset)
float fTruncMeanFraction
Fraction for truncated mean.
auto vector(Vector const &v)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:265
T abs(T value)
void removeCorrelatedNoise(RawDigitAdcIdxPair &digitIdxPair, unsigned int viewIdx, std::vector< float > &truncMeanWireVec, std::vector< float > &truncRmsWireVec, std::vector< short > &minMaxWireVec, std::vector< short > &meanWireVec, std::vector< float > &skewnessWireVec, std::vector< float > &neighborRatioWireVec, std::vector< float > &pedCorWireVec, unsigned int &fftSize, unsigned int &halfFFTSize, void *fplan, void *rplan) const
void findPeaks(typename std::vector< T >::iterator startItr, typename std::vector< T >::iterator stopItr, std::vector< std::tuple< size_t, size_t, size_t >> &peakTupleVec, T threshold, size_t firstTick) const
double distance(geo::Point_t const &point, CathodeDesc_t const &cathode)
Returns the distance of a point from the cathode.
bool fFillHistograms
if true then will fill diagnostic hists
void smoothCorrectionVec(std::vector< float > &, unsigned int &) const
tick_as<> tick
Tick number, represented by std::ptrdiff_t.
Definition: electronics.h:75
void initializeHists(art::ServiceHandle< art::TFileService > &)
Begin job method.
void fill(const art::PtrVector< recob::Hit > &hits, int only_plane)
walls no left
Definition: selectors.fcl:105
bool fRunFFTCorrected
Should we run FFT&#39;s on corrected wires?
std::vector< size_t > fFFTNumHists
Number of hists total per view.
art::ServiceHandle< art::TFileService > tfs
std::vector< size_t > fNumWiresToGroup
If smoothing, the number of wires to look at.
void DoInvFFT(std::vector< T > &output)
Definition: LArFFTW.h:113
std::vector< float > fNumRmsToSmoothVec
&quot;sigma&quot; to smooth correlated correction vec
std::map< size_t, RawDigitVector & > WireToRawDigitVecMap
std::vector< double > fFFTMinPowerThreshold
Threshold for trimming FFT power spectrum.
void DoFFT(std::vector< T > &input)
Definition: LArFFTW.h:76
process_name can override from command line with o or output caldata
Definition: pid.fcl:40
void reconfigure(fhicl::ParameterSet const &pset)
std::map< size_t, RawDigitVectorIdxPair > WireToAdcIdxMap