All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SampledWaveformFunction.h
Go to the documentation of this file.
1 /**
2  * @file icaruscode/PMT/Algorithms/SampledWaveformFunction.h
3  * @brief Pulse from one photoelectron as a train of samples.
4  * @author Gianluca Petrillo (petrillo@slac.stanford.edu)
5  * @date April 6, 2022
6  *
7  * This library is header only.
8  *
9  */
10 
11 #ifndef ICARUSCODE_PMT_ALGORITHMS_SAMPLEDWAVEFORMFUNCTION_H
12 #define ICARUSCODE_PMT_ALGORITHMS_SAMPLEDWAVEFORMFUNCTION_H
13 
14 // library header
16 
17 // LArSoft libraries
18 #include "lardataalg/Utilities/quantities/spacetime.h" // nanoseconds
19 
20 // C++ standard library
21 #include <ostream> // std::ostream
22 #include <vector>
23 #include <string>
24 #include <algorithm> // std::transform()
25 #include <numeric> // std::reduce()
26 #include <cmath> // std::round(), std::floor(), ...
27 #include <cstddef> // std::ptrdiff_t, std::size_t
28 #include <cassert>
29 
30 
31 // -----------------------------------------------------------------------------
32 namespace icarus::opdet {
33  using namespace util::quantities::time_literals; // ""_ns
34  template <typename T> class SampledWaveformFunction;
35 }
36 
37 // -----------------------------------------------------------------------------
38 /**
39  * @brief Describes the waveform from a single photoelectron.
40  * @tparam T type of time unit to be used
41  *
42  * This functor (class behaving like a function) describes the shape of the
43  * response to a single photoelectron in a non-analytical form from a sequence
44  * of samples.
45  *
46  * The peak time is assigned to the sample with the largest value.
47  *
48  * See more details in the constructor.
49  */
50 template <typename T>
53 {
55 
56  public:
57  /// Type for ADC counts (floating point).
58  using ADCcount = typename Base_t::ADCcount;
59 
60  using Time = typename Base_t::Time; ///< Type of time being used.
61 
62  /// Specifies the waveform shape and features.
63  struct WaveformSpecs_t {
64  std::string name { "<unknown>" }; ///< Name of this waveform (short).
65  std::string description; ///< Description of this waveform.
66  std::string date { "n/a" }; ///< An indication of the date of the waveform.
67  unsigned int version { 1 }; ///< Version number.
68  std::vector<float> samples; ///< Samples [mV]
69  Time sampleDuration; ///< Time extension of one sample.
70  float gain { 0.0 }; ///< Gain of this waveform.
71  };
72 
73  /**
74  * @brief Constructor: initializes from an hard-coded shape.
75  * @param waveformSpecs all information on the single photoelectron response
76  * @param peakTime time to assign to the peak
77  * @param gain the gain of the optical detector
78  *
79  * The shape described in waveformSpecs is shifted in time so that evaluation
80  * at `peakTime` (`evaluateAt(peakTime)`) returns the peak amplitude; more
81  * precisely, `peakTime` is set to match the start of the largest sample.
82  *
83  * The `gain` is rescaled starting from the one in the waveform
84  * specifications.
85  *
86  * The polarity of the waveform is deduced by the value at peak.
87  *
88  */
89  SampledWaveformFunction(WaveformSpecs_t specs, Time peakTime, float gain);
90 
91  /// @{
92  /// @name Parameter accessors.
93 
94  /// Returns the gain the waveform is representing.
95  float gain() const { return fGain; }
96 
97  /// @}
98 
99  private:
100 
101  WaveformSpecs_t const fSource; ///< Waveform information.
102 
103  std::vector<ADCcount> const fSamples; ///< All samples.
104 
105  float const fGain; ///< The gain this waveform represents.
106 
107  std::size_t const fPeakSample; ///< The sample with the absolute peak.
108 
109  Time const fRefTime; ///< The time of the start of sample #0.
110 
111  /// The duration of each sample.
112  Time sampleDuration() const { return fSource.sampleDuration; }
113 
114 
115  // --- BEGIN -- Interface implementation -------------------------------------
116  /**
117  * @brief Evaluates the pulse at the given time.
118  * @param time time to evaluate the shape at
119  *
120  * The scale of the time is defined by the peak time passed at construction.
121  */
122  virtual ADCcount doEvaluateAt(Time time) const override;
123 
124  /// Returns the time at which the first peak is found.
125  virtual Time doPeakTime() const override
126  { return fRefTime + fPeakSample * sampleDuration(); }
127 
128  /// Returns the amplitude of the first peak in ADC.
129  virtual ADCcount doPeakAmplitude() const override
130  { return fSamples[fPeakSample]; }
131 
132  /**
133  * @brief Prints on stream the parameters of this shape.
134  * @param out the stream to write into
135  * @param indent indentation string, prepended to all lines except first
136  * @param indentFirst indentation string prepended to the first line
137  */
138  virtual void doDump(
139  std::ostream& out,
140  std::string const& indent, std::string const& firstIndent
141  ) const override;
142 
143  // --- END -- Interface implementation -------------------------------------
144 
145 
146  /// Returns whether a sample with the specified index is within the range.
147  bool hasSample(std::ptrdiff_t index) const
148  { return (index >= 0) && (std::size_t(index) < fSamples.size()); }
149 
150 
151  /// Returns the integral of the waveform.
152  ADCcount integral() const;
153 
154  /**
155  * @brief Transforms the input waveform.
156  * @param waveform input waveform (in millivolt and for a known gain)
157  * @param targetGain the desired gain
158  * @return a sequence of samples in ADC
159  *
160  * The returned waveform has the same time domain as the input one, but is
161  * expressed in ADC instead of voltage (conversion is perfectly linear,
162  * 2 V across 14 bits), and rescaled to meet the target gain.
163  */
164  std::vector<ADCcount> buildSamples(float targetGain) const;
165 
166  /// Returns the index of the sample under the peak of the waveform.
167  static std::size_t findPeak(std::vector<ADCcount> const& samples);
168 
169 }; // class icarus::opdet::SampledWaveformFunction<>
170 
171 
172 // -----------------------------------------------------------------------------
173 // --- template implementation
174 // -----------------------------------------------------------------------------
175 template <typename T>
177  (WaveformSpecs_t waveformSpecs, Time peakTime, float gain)
178  : fSource { std::move(waveformSpecs) }
179  , fSamples { buildSamples(gain) }
180  , fGain { gain }
181  , fPeakSample{ findPeak(fSamples) }
182  , fRefTime { peakTime - fPeakSample * sampleDuration() }
183  {}
184 
185 
186 // -----------------------------------------------------------------------------
187 template <typename T>
189  -> ADCcount
190 {
191  std::ptrdiff_t const iSample = static_cast<std::ptrdiff_t>
192  (std::floor((time - fRefTime)/sampleDuration()));
193  return hasSample(iSample)? fSamples[iSample]: ADCcount{ 0 };
194 } // icarus::opdet::SampledWaveformFunction<>::doEvaluateAt()
195 
196 
197 // -----------------------------------------------------------------------------
198 template <typename T>
200  std::ostream& out,
201  std::string const& indent, std::string const& firstIndent
202  ) const
203 {
204 
205  out << firstIndent
206  << "Pulse '" << fSource.name << "' (v. " << fSource.version
207  << ", " << fSource.date << "):"
208  << "\n" << indent << " " << fSource.description
209  << "\n" << indent
210  << " from " << fSamples.size() << "x " << sampleDuration() << " samples"
211  << ", peak at " << Base_t::peakTime()
212  << " with amplitude " << Base_t::peakAmplitude()
213  << "\n" << indent
214  << " start at " << fRefTime << ", gain " << fGain
215  << " (integral: " << integral() << ")"
216  << '\n'
217  ;
218 
219 } // icarus::opdet::SampledWaveformFunction<>::doDump()
220 
221 
222 // -----------------------------------------------------------------------------
223 template <typename T>
225  { return std::reduce(fSamples.begin(), fSamples.end()); }
226 
227 
228 // -----------------------------------------------------------------------------
229 template <typename T>
231  (float targetGain) const -> std::vector<ADCcount>
232 {
233 
234  /*
235  * Sample conversion
236  * ------------------
237  *
238  * The waveform is expected in millivolt, and it expresses the response with/
239  * the photodetector set at a known gain.
240  * Our target is a waveform in ADC and the target gain in argument.
241  * The conversion factor is based on the full range of the digitizer,
242  * that is 2 V across 14 bit.
243  */
244  constexpr float VoltageRange = 2'000.0; // millivolt
245  constexpr unsigned short int ADCbits = 14;
246 
247  // 2 V in 14 bits (=> 8.192):
248  constexpr float mVtoADC = (1 << ADCbits) / VoltageRange;
249 
250  // if either the starting gain is unknown or the target gain is not specified,
251  // do not scale the gain
252  float const gainFactor = (fSource.gain != 0.0 && targetGain != 0.0)
253  ? (targetGain / fSource.gain): 1.0;
254  float const factor = gainFactor * mVtoADC;
255 
256  auto voltageToADC = [factor](float mV)
257  { return static_cast<ADCcount>(factor * mV); };
258 
259  std::vector<ADCcount> samples;
260  samples.reserve(fSource.samples.size());
261  std::transform(fSource.samples.begin(), fSource.samples.end(),
262  back_inserter(samples), voltageToADC);
263 
264  return samples;
265 
266 } // icarus::opdet::SampledWaveformFunction<>::buildSamples()
267 
268 
269 // -----------------------------------------------------------------------------
270 template <typename T>
271 std::size_t icarus::opdet::SampledWaveformFunction<T>::findPeak
272  (std::vector<ADCcount> const& samples)
273 {
274  assert(!samples.empty());
275  auto const sbegin = samples.begin();
276  auto const [ min, max ] = std::minmax_element(sbegin, samples.end());
277  // assume baseline 0:
278  return ((min->abs() > max->abs())? min: max) - sbegin;
279 } // icarus::opdet::SampledWaveformFunction<T>::findPeak()
280 
281 
282 // -----------------------------------------------------------------------------
283 
284 #endif // ICARUSCODE_PMT_ALGORITHMS_SAMPLEDWAVEFORMFUNCTION_H
Time const fRefTime
The time of the start of sample #0.
float const fGain
The gain this waveform represents.
bool hasSample(std::ptrdiff_t index) const
Returns whether a sample with the specified index is within the range.
Describes the waveform from a single photoelectron.
Time sampleDuration() const
The duration of each sample.
float gain() const
Returns the gain the waveform is representing.
std::size_t const fPeakSample
The sample with the absolute peak.
Abstract interface of shape of a pulse from one photoelectron.
ADCcount integral() const
Returns the integral of the waveform.
A value measured in the specified unit.
Definition: quantities.h:566
std::string description
Description of this waveform.
virtual void doDump(std::ostream &out, std::string const &indent, std::string const &firstIndent) const override
Prints on stream the parameters of this shape.
Interface for a function describing a pulse from a photoelectron.
std::vector< ADCcount > const fSamples
All samples.
Dimensioned variables representing space or time quantities.
SampledWaveformFunction(WaveformSpecs_t specs, Time peakTime, float gain)
Constructor: initializes from an hard-coded shape.
virtual Time doPeakTime() const override
Returns the time at which the first peak is found.
then echo fcl name
virtual ADCcount doEvaluateAt(Time time) const override
Evaluates the pulse at the given time.
std::vector< ADCcount > buildSamples(float targetGain) const
Transforms the input waveform.
WaveformSpecs_t const fSource
Waveform information.
virtual ADCcount doPeakAmplitude() const override
Returns the amplitude of the first peak in ADC.