FFTDataContainerAlgorithm.h
Go to the documentation of this file.
1 
10 #ifndef _utl_FFTDataContainerAlgorithm_h_
11 #define _utl_FFTDataContainerAlgorithm_h_
12 
13 #include <utl/fft++.h>
14 #include <cmath>
15 #include <complex>
16 #include <boost/rational.hpp>
17 #include <utl/AugerException.h>
18 #include <utl/FFTDataContainer.h>
19 #include <utl/MathConstants.h>
20 #include <utl/Test.h>
21 #include <utl/TimeInterval.h>
22 
23 
24 class testFFTDataContainerAlgorithm; // forward declaration needed for declaration of friend class
25 
26 
27 namespace utl {
28 
41  public:
43 
51  template<template<typename X> class C, typename T, typename F>
52  static
53  void
55  {
56  // Get the frequency spectrum
57  C<F>& spectrum = container.GetFrequencySpectrum();
58 
59  // multiply phase gradient to each value of the spectrum
60  for (unsigned int i = 0; i < spectrum.GetSize(); ++i)
61  spectrum[i] *=
62  std::exp(-std::complex<double>(0,1) * kTwoPi * container.GetFrequencyOfBin(i) * shift.GetInterval());
63  }
64 
66  template<template<typename X> class C, typename T, typename F>
67  static
68  void
69  UpsampleTimeSeries(FFTDataContainer<C, T, F>& container, const unsigned int upsamplingFactor, const bool removeOffset)
70  {
71  // Get the frequency spectrum
72  C<F>& spectrum = container.GetFrequencySpectrum();
73 
74  // remove constant offset from trace (bin 1)
75  // store offset from trace (first bin) to later readd it (if wanted)
76  const F offset = spectrum[0];
77  spectrum[0] *= 0;
78 
79  // construct upsampled trace:
80  // start with a new spectrum of zeros
81  // then fill in the values of the old spectrum at the correct position
82  // the bin size is reduced by the upsamplingFactor so the new
83  // spectrum has a size of (originalSize-1)*upsamplingFactor+1
84  // as there is only one first and one last value
85  C<F> upsampledSpectrum((spectrum.GetSize()-1)*upsamplingFactor+1, spectrum.GetBinning(), 0);
86 
87  // as the values must be filled in in the proper order there are two cases
88  // as the order of the frequencies depends on the Nyquist zone
89  if (container.GetNyquistZone() == 1) {
90  for (unsigned int i = 0; i < spectrum.GetSize(); ++i)
91  upsampledSpectrum[i] = spectrum[i];
92  } else if (container.GetNyquistZone() == 2) { // WARNING: Check missing if data are sorted as assumed
93  const int lastentry = 2*(spectrum.GetSize()-1);
94  for (int i = spectrum.GetSize()-1; i >= 0; --i)
95  upsampledSpectrum[lastentry-i] = std::conj(spectrum[i]); // complex conjugate avoids time inversion
96  upsampledSpectrum[lastentry] *= 0.5;
97  upsampledSpectrum[spectrum.GetSize()-1] *= 0.5;
98  } else {
99  throw utl::DoesNotComputeException("Upsampling for Nyquist zones greater than 2 not implemented!");
100  }
101 
102  // replace the original spectrum by the new one (after zero padding)
103  spectrum.Swap(upsampledSpectrum);
104 
105  // Add old mean to retain offset, if substraction is not requested
106  if (!removeOffset)
107  spectrum[0] = offset;
108 
109  // the new spectrum is allways in the 1st Nyquist zone
110  container.SetNyquistZone(1);
111  }
112 
114  template<template<typename X> class C, typename T, typename F>
115  static
116  void
117  zeroPad(FFTDataContainer<C, T, F>& container, const unsigned int newSize, const bool removeOffset)
118  {
119  // Get the frequency spectrum
120  C<F>& spectrum = container.GetFrequencySpectrum();
121 
122  // remove constant offset from trace (bin 1)
123  // store offset from trace (first bin) to later readd it (if wanted)
124  const F offset = spectrum[0];
125  spectrum[0] *= 0;
126 
127  // construct upsampled trace:
128  // start with a new spectrum of zeros
129  // then fill in the values of the old spectrum at the correct position
130  // the bin size is reduced by the upsamplingFactor so the new
131  // spectrum has a size of (originalSize-1)*upsamplingFactor+1
132  // as there is only one first and one last value
133  C<F> upsampledSpectrum(newSize, spectrum.GetBinning(), 0);
134 
135  if (container.GetNyquistZone() == 1) {
136  for (unsigned int i = 0; i < spectrum.GetSize(); ++i)
137  upsampledSpectrum[i] = spectrum[i];
138  } else {
139  throw utl::DoesNotComputeException("Zero Padding for Nyquist zones greater than 1 not implemented!");
140  }
141 
142  // replace the original spectrum by the new one (after zero padding)
143  spectrum.Swap(upsampledSpectrum);
144 
145  // Add old mean to retain offset, if substraction is not requested
146  if (!removeOffset)
147  spectrum[0] = offset;
148 
149  // the new spectrum is allways in the 1st Nyquist zone
150  container.SetNyquistZone(1);
151  }
152 
154  template<template<typename X> class C, typename T, typename F>
155  static
156  void
157  ResampleTimeSeries(FFTDataContainer<C, T, F>& container, const double resampleBinning)
158  {
159  if (resampleBinning <= 0)
160  throw utl::DoesNotComputeException("Requested illegal resampling factor, aborting ...");
161  const long maxUpsamplingFactor = 500;
162  // first we have to convert the time bases to rationals
163  double origBinning = container.GetConstTimeSeries().GetBinning();
164  double targetBinning = resampleBinning;
165  const boost::rational<long> origBinningRational = ConvertDecimalDoubleToRational(origBinning, false);
166  const boost::rational<long> targetBinningRational = ConvertDecimalDoubleToRational(targetBinning, false);
167  const boost::rational<long> resamplingFactor = origBinningRational / targetBinningRational;
168  if (resamplingFactor.numerator() > maxUpsamplingFactor)
169  throw utl::DoesNotComputeException("Requested upsampling factor is too large, aborting ...");
170  if (resamplingFactor.numerator() != 1)
171  UpsampleTimeSeries(container, resamplingFactor.numerator(), false);
172  if (resamplingFactor.denominator() != 1)
173  ThinOutTimeSeries(container, resamplingFactor.denominator());
174  }
175 
177  template<template<typename X> class C, typename T, typename F>
178  static
179  void
180  ThinOutTimeSeries(FFTDataContainer<C, T, F>& container, const long thinOutFactor)
181  {
182  // Get the time series
183  C<T>& timeseries = container.GetTimeSeries();
184  C<T> thinnedtimeseries(static_cast<unsigned int>(timeseries.GetSize()/thinOutFactor), timeseries.GetBinning()*thinOutFactor);
185 
186  // fill the thinned out time series
187  for (unsigned int i = 0; i < thinnedtimeseries.GetSize(); ++i)
188  thinnedtimeseries[i] = timeseries[i * thinOutFactor];
189 
190  // overwrite original time series
191  timeseries.Swap(thinnedtimeseries);
192  }
193 
195  template<template<typename X> class C, typename T, typename F>
196  static void HilbertEnvelope(FFTDataContainer<C, T, F>& container);
197 
199  template<template<typename X> class C, typename T, typename F>
200  static
201  void
203 
204 
205  private:
211  static
212  boost::rational<long>
213  ConvertDecimalDoubleToRational(double fpn, const bool secondcall)
214  {
215  const double abortprecision = 1e-5; // limit number of significant digits
216  const double orgfpn = fpn;
217  long denominator = 1; // starting value for denominator is 1
218  double fpn_int; // double to store the integer part of fpn
219  while (std::modf(fpn, &fpn_int)/fpn > abortprecision) {
220  if (Test<CloseTo>(fpn - fpn_int, 1.0)) // floating point rounding error, we are finished
221  break;
222  fpn *= 10.0;
223  denominator *= 10;
224  }
225 
226  if (secondcall)
227  return boost::rational<long>(denominator, long(fpn+0.5));
228 
229  // if this is the first call, try the conversion again with the inverse
230  const boost::rational<long> firstresult =
231  boost::rational<long>(long(fpn+0.5), denominator);
232  const boost::rational<long> secondresult =
233  ConvertDecimalDoubleToRational(1.0/orgfpn, true);
234 
235  // return the result with the smaller denominator
236  return (firstresult.denominator() < secondresult.denominator()) ? firstresult : secondresult;
237  }
238 
239  friend class ::testFFTDataContainerAlgorithm; // so that we can test private functions
240 
241  };
242 
243 } // utl
244 
245 #endif
static void HilbertEnvelope(FFTDataContainer< C, T, F > &container)
applies a HilbertEnvelope to the time series data
unsigned int GetNyquistZone() const
get the Nyquist zone
static void UpsampleTimeSeries(FFTDataContainer< C, T, F > &container, const unsigned int upsamplingFactor, const bool removeOffset)
upsamples a trace by the given upsampling factor, removes DC offset if instructed so ...
static void ThinOutTimeSeries(FFTDataContainer< C, T, F > &container, const long thinOutFactor)
thins out a time series by a given factor
C< F > & GetFrequencySpectrum()
read out the frequency spectrum (write access)
static void ResampleTimeSeries(FFTDataContainer< C, T, F > &container, const double resampleBinning)
resamples a time series to a given sampling timebase
static void ShiftTimeSeries(FFTDataContainer< C, T, F > &container, const TimeInterval &shift)
shifts the time series of the FFTDataContainer in time
constexpr double kTwoPi
Definition: MathConstants.h:27
C< T > & GetTimeSeries()
read out the time series (write access)
static void zeroPad(FFTDataContainer< C, T, F > &container, const unsigned int newSize, const bool removeOffset)
zero padding to a given number
Algorithms working on FFTDataContainer objects.
Base class for inconsistency/illogicality exceptions.
static boost::rational< long > ConvertDecimalDoubleToRational(double fpn, const bool secondcall)
double GetInterval() const
Get the time interval as a double (in Auger base units)
Definition: TimeInterval.h:69
Template class for a data container that offers and takes both time series and corresponding frequenc...
A TimeInterval is used to represent time elapsed between two events.
Definition: TimeInterval.h:43
static void HilbertTransform(FFTDataContainer< C, T, F > &container)
aplies a HilbertTransform in the data
void SetNyquistZone(const unsigned int zone)
set the Nyquist zone

, generated on Tue Sep 26 2023.