LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
SignalShaper.cxx
Go to the documentation of this file.
2 #include "cetlib_except/exception.h"
3 #include <cmath>
4 
5 //----------------------------------------------------------------------
6 // Constructor.
7 //
8 util::SignalShaper::SignalShaper(int fftsize, std::string fftopt)
9  : fResponseLocked(false)
10  , fFilterLocked(false)
11  , fNorm(true)
12  , fFFTSize(fftsize)
13  , fFFTPlan(new util::LArFFTWPlan(fftsize, fftopt))
14  , fFFT(new util::LArFFTW(fftsize, fFFTPlan->fPlan, fFFTPlan->rPlan, 0))
15 {}
16 
17 //----------------------------------------------------------------------
18 // Destructor.
19 //
21 
22 //----------------------------------------------------------------------
23 // Reset this class to its default-constructed state.
25 {
26  fResponseLocked = false;
27  fFilterLocked = false;
28  fResponse.clear();
29  fConvKernel.clear();
30  fFilter.clear();
31  fDeconvKernel.clear();
32  //Set deconvolution polarity to + as default
34 }
35 
36 //----------------------------------------------------------------------
37 // Add a time domain response function.
38 void util::SignalShaper::AddResponseFunction(const std::vector<double>& resp, bool ResetResponse)
39 {
40  // Make sure configuration is not locked.
41 
42  if (fResponseLocked) throw cet::exception("SignalShaper") << "Configuration locked.\n";
43 
44  int nticks = fFFTSize;
45 
46  // Copy new response function into fResponse attribute, and pad or
47  // truncate to correct size.
48 
49  fResponse = resp;
50  fResponse.resize(nticks, 0.);
51 
52  // Is this the first response function?
53 
54  if (fConvKernel.size() == 0 || ResetResponse) {
55 
56  // This is the first response function.
57  // Just calculate the fourier transform.
58 
59  fConvKernel.resize(nticks / 2 + 1);
60  fFFT->DoFFT(fResponse, fConvKernel);
61  }
62  else {
63 
64  // Not the first response function.
65  // Calculate the fourier transform of new response function.
66 
67  std::vector<std::complex<double>> kern(nticks / 2 + 1);
68  fFFT->DoFFT(fResponse, kern);
69 
70  // Update overall convolution kernel.
71 
72  if (kern.size() != fConvKernel.size()) {
73  throw cet::exception("SignalShaper") << __func__ << ": inconsistent kernel size, "
74  << kern.size() << " vs. " << fConvKernel.size();
75  }
76  for (unsigned int i = 0; i < kern.size(); ++i)
77  fConvKernel[i] *= kern[i];
78 
79  // Recalculate overall response function.
80 
81  fFFT->DoInvFFT(fConvKernel, fResponse);
82  }
83 }
84 
85 //----------------------------------------------------------------------
86 // Shift the response function and convolution kernel by the specified
87 // number of ticks.
89 {
90  // Make sure configuration is not locked.
91 
92  if (fResponseLocked) throw cet::exception("SignalShaper") << "Configuration locked.\n";
93 
94  // Update convolution kernel.
95 
96  fFFT->ShiftData(fConvKernel, ticks);
97 
98  // Recalculate overall response functiion.
99 
100  fFFT->DoInvFFT(fConvKernel, fResponse);
101 }
102 
103 //----------------------------------------------------------------------
104 // Set the peak response time to be at the specified tick.
106 {
107  // Make sure configuration is not locked.
108 
109  if (fResponseLocked) throw cet::exception("SignalShaper") << "Configuration locked.\n";
110 
111  // Construct a delta-function response centered at tick zero.
112 
113  std::vector<double> delta(fFFTSize, 0.);
114  delta[0] = 1.;
115 
116  // Figure out peak of current overall response.
117 
118  double peak = fFFT->PeakCorrelation(delta, fResponse);
119 
120  // Shift peak response to desired tick.
121 
122  ShiftResponseTime(tick - peak);
123 }
124 
125 //----------------------------------------------------------------------
126 // Add a frequency domain filter function to cumulative filter function.
127 void util::SignalShaper::AddFilterFunction(const std::vector<std::complex<double>>& filt)
128 {
129  // Make sure configuration is not locked.
130 
131  if (fFilterLocked) throw cet::exception("SignalShaper") << "Configuration locked.\n";
132 
133  // If this is the first filter function, just copy the filter function.
134  // Otherwise, update the overall filter function.
135 
136  if (fFilter.size() == 0) {
137  fFilter = filt;
138  fFilter.resize(fFFTSize / 2 + 1);
139  }
140  else {
141  unsigned int n = std::min(fFilter.size(), filt.size());
142  for (unsigned int i = 0; i < n; ++i)
143  fFilter[i] *= filt[i];
144  for (unsigned int i = n; i < fFilter.size(); ++i)
145  fFilter[i] = 0.;
146  }
147 }
148 
149 //----------------------------------------------------------------------
150 // Add a DeconvKernel Polarity Flag to decide how to normalize
152 {
153 
154  if ((pol != 1) and (pol != -1)) {
155  throw cet::exception("SignalShaper")
156  << __func__ << ": DeconvKernelPolarity should be +1 or -1 (got " << pol
157  << "). Setting to +1\n";
159  }
160 
161  else
162  fDeconvKernelPolarity = pol;
163 }
164 
165 //----------------------------------------------------------------------
166 // Test and lock the response and convolution kernel.
168 {
169  // Do nothing if the response is already locked.
170 
171  if (!fResponseLocked) {
172 
173  // Make sure response has been configured.
174 
175  if (fResponse.size() == 0)
176  throw cet::exception("SignalShaper") << "Response has not been configured.\n";
177 
178  // Make sure response and convolution kernel have the correct
179  // size (should always be the case if we get here).
180 
181  unsigned int n = fFFTSize;
182  if (fResponse.size() != n)
183  throw cet::exception("SignalShaper")
184  << __func__ << ": inconsistent kernel size, " << fResponse.size() << " vs. " << n << "\n";
185  if (2 * (fConvKernel.size() - 1) != n)
186  throw cet::exception("SignalShaper")
187  << __func__ << ": unexpected FFT size, " << n << " vs. expected "
188  << (2 * (fConvKernel.size() - 1)) << "\n";
189 
190  // Set the lock flag.
191 
192  fResponseLocked = true;
193  }
194 }
195 
196 //----------------------------------------------------------------------
197 // Calculate the deconvolution kernel as the ratio
198 // of the filter function and convolution kernel.
200 {
201  // Make sure configuration is not locked.
202 
203  if (fFilterLocked) throw cet::exception("SignalShaper") << "Configuration locked.\n";
204 
205  // Lock response configuration.
206 
207  LockResponse();
208 
209  // Make sure filter function has been configured.
210 
211  if (fFilter.size() == 0)
212  throw cet::exception("SignalShaper") << "Filter function has not been configured.\n";
213 
214  // Make sure filter function has the correct size.
215  // (Should always be the case if we get here.)
216 
217  unsigned int n = fFFTSize;
218  if (2 * (fFilter.size() - 1) != n)
219  if (fFilter.size() != fConvKernel.size()) {
220  throw cet::exception("SignalShaper") << __func__ << ": inconsistent size, " << fFilter.size()
221  << " vs. " << fConvKernel.size() << "\n";
222  }
223 
224  // Calculate deconvolution kernel as the ratio of the
225  // filter function and the convolution kernel.
226 
228  for (unsigned int i = 0; i < fDeconvKernel.size(); ++i) {
229  if (std::abs(fConvKernel[i].real()) <= 0.0001 && std::abs(fConvKernel[i].imag()) <= 0.0001) {
230  fDeconvKernel[i] = 0.;
231  }
232  else {
233  fDeconvKernel[i] /= fConvKernel[i];
234  }
235  }
236 
237  // Normalize the deconvolution kernel.
238 
239  // Calculate the unnormalized deconvoluted response
240  // (inverse FFT of filter function).
241 
242  std::vector<double> deconv(n, 0.);
243  fFFT->DoInvFFT(const_cast<std::vector<std::complex<double>>&>(fFilter), deconv);
244  //fFFT->DoInvFFT(fFilter, deconv);
245 
246  if (fNorm) {
247  // Find the peak value of the response
248  // Should normally be at zero, but don't assume that.
249  // Use DeconvKernelPolairty to find what to normalize to
250  double peak_response = 0;
251  if (fDeconvKernelPolarity == -1) peak_response = 4096;
252  for (unsigned int i = 0; i < fResponse.size(); ++i) {
253  if ((fResponse[i] > peak_response) and (fDeconvKernelPolarity == 1))
254  peak_response = fResponse[i];
255  else if ((fResponse[i] < peak_response) and (fDeconvKernelPolarity == -1))
256  peak_response = fResponse[i];
257  }
258  if (fDeconvKernelPolarity == -1) peak_response *= -1;
259  if (peak_response <= 0.) {
260  throw cet::exception("SignalShaper")
261  << __func__ << ": peak should always be positive (got " << peak_response << ")\n";
262  }
263 
264  // Find the peak value of the deconvoluted response
265  // Should normally be at zero, but don't assume that.
266 
267  double peak_deconv = 0.;
268  for (unsigned int i = 0; i < deconv.size(); ++i) {
269  if (deconv[i] > peak_deconv) peak_deconv = deconv[i];
270  }
271  if (peak_deconv <= 0.) {
272  throw cet::exception("SignalShaper")
273  << __func__ << ": deconvolution peak should always be positive (got " << peak_deconv
274  << ")\n";
275  }
276 
277  // Multiply the deconvolution kernel by a factor such that
278  // (Peak of response) = (Peak of deconvoluted response).
279 
280  double ratio = peak_response / peak_deconv;
281  for (unsigned int i = 0; i < fDeconvKernel.size(); ++i)
282  fDeconvKernel[i] *= ratio;
283  }
284 
285  // Set the lock flag.
286 
287  fFilterLocked = true;
288 }
void CalculateDeconvKernel() const
Namespace for general, non-LArSoft-specific utilities.
Definition: PIDAAlg.h:26
void AddFilterFunction(const std::vector< std::complex< double >> &filt)
void ShiftResponseTime(double ticks)
virtual ~SignalShaper()
constexpr auto abs(T v)
Returns the absolute value of the argument.
void SetPeakResponseTime(double tick)
tick ticks
Alias for common language habits.
Definition: electronics.h:76
void AddResponseFunction(const std::vector< double > &resp, bool ResetResponse=false)
void LockResponse() const
auto vector(Vector const &v)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:289
std::vector< double > fResponse
Definition: SignalShaper.h:83
std::unique_ptr< util::LArFFTW > fFFT
Definition: SignalShaper.h:107
tick_as<> tick
Tick number, represented by std::ptrdiff_t.
Definition: electronics.h:73
std::vector< std::complex< double > > fConvKernel
Definition: SignalShaper.h:87
SignalShaper(int fftsize, std::string fftopt)
Definition: SignalShaper.cxx:8
Char_t n[5]
std::vector< std::complex< double > > fDeconvKernel
Definition: SignalShaper.h:93
void SetDeconvKernelPolarity(int pol)
TString filt[ntarg]
Definition: Style.C:28
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33
std::vector< std::complex< double > > fFilter
Definition: SignalShaper.h:90