JayBeams  0.1
Another project to have fun coding.
time_delay_estimator.hpp
Go to the documentation of this file.
1 #ifndef jb_fftw_time_delay_estimator_hpp
2 #define jb_fftw_time_delay_estimator_hpp
3 
5 #include <jb/fftw/plan.hpp>
6 #include <jb/complex_traits.hpp>
7 
8 namespace jb {
9 namespace fftw {
10 
11 /**
12  * A simple time delay estimator based on cross-correlation
13  */
14 template <
15  typename timeseries_t,
16  template <typename T> class vector = jb::fftw::aligned_vector>
18 public:
19  //@{
20  /**
21  * @name Type traits
22  */
23  /// The input timeseries type
24  typedef timeseries_t timeseries_type;
25 
26  /// The values stored in the input timeseries
27  typedef typename timeseries_type::value_type value_type;
28 
29  /// Extract T out of std::complex<T>, otherwise simply T.
31 
32  /// The type used to store the DFT of the input timeseries
33  typedef vector<std::complex<precision_type>> frequency_timeseries_type;
34 
35  /// The type used to stored the inverse of the DFT
36  typedef vector<precision_type> output_timeseries_type;
37 
38  /// The execution plan to apply the (forward) DFT
40 
41  /// The execution plan to apply the inverse (aka backward) DFT
44  //@}
45 
46  /**
47  * Constructs a time delay estimator using @param a and @param b as prototypes
48  * for the arguments.
49  *
50  * The optimal algorithm to compute the FFTs used in the cross correlation
51  * depends on the size of the input parameters and their memory alignment.
52  *
53  * The FFTW library modifies the arguments to compute the optimal
54  * execution plan, do not assume the values are unmodified.
55  *
56  * @param a container type (e.g. vector<>) timeseries
57  * @param b container type (e.g. vector<>) timeseries
58  */
59  time_delay_estimator(timeseries_type& a, timeseries_type& b)
60  : tmpa_(a.size())
61  , tmpb_(b.size())
64  , out_(a.size())
66  if (a.size() != b.size()) {
67  throw std::invalid_argument("size mismatch in time_delay_estimator ctor");
68  }
69  }
70 
71  /// Compute the time-delay estimate between two timeseries
72  std::pair<bool, precision_type>
73  estimate_delay(timeseries_type& a, timeseries_type& b) {
74  // Validate the input sizes. For some types of timeseries the
75  // alignment may be different too, but we only use the alignment
76  // when the type of timeseries guarantees to always be aligned.
77  if (a.size() != tmpa_.size() or b.size() != tmpa_.size()) {
78  throw std::invalid_argument(
79  "size mismatch in time_delay_estimator<>::estimate_delay()");
80  }
81  // First we apply the Fourier transform to both inputs ...
82  a2tmpa_.execute(a, tmpa_);
83  b2tmpb_.execute(b, tmpb_);
84  // ... then we compute Conj(A) * B for the transformed inputs ...
85  for (std::size_t i = 0; i != tmpa_.size(); ++i) {
86  tmpa_[i] = std::conj(tmpa_[i]) * tmpb_[i];
87  }
88  // ... then we compute the inverse Fourier transform to the result ...
90  // ... finally we compute (argmax,max) for the result ...
91  precision_type max = std::numeric_limits<precision_type>::min();
92  std::size_t argmax = 0;
93  for (std::size_t i = 0; i != out_.size(); ++i) {
94  if (max < out_[i]) {
95  max = out_[i];
96  argmax = i;
97  }
98  }
99  // TODO(#13) the threshold for acceptance should be configurable,
100  // maybe we want the value to be close to the expected area of 'a'
101  // for example ...
102  if (max <= std::numeric_limits<precision_type>::epsilon()) {
103  return std::make_pair(false, precision_type(0));
104  }
105  return std::make_pair(true, precision_type(argmax));
106  }
107 
108 private:
109  /**
110  * Determine the correct FFTW planning flags given the inputs.
111  */
112  static int planning_flags() {
114  return FFTW_MEASURE;
115  }
116  return FFTW_MEASURE | FFTW_UNALIGNED;
117  }
118 
119 private:
120  frequency_timeseries_type tmpa_;
121  frequency_timeseries_type tmpb_;
122  dplan a2tmpa_;
123  dplan b2tmpb_;
124  output_timeseries_type out_;
126 };
127 
128 } // namespace fftw
129 } // namespace jb
130 
131 #endif // jb_fftw_time_delay_estimator_hpp
std::pair< bool, precision_type > estimate_delay(timeseries_type &a, timeseries_type &b)
Compute the time-delay estimate between two timeseries.
std::vector< T, jb::fftw::allocator< T > > aligned_vector
Alias std::vector with properly allocated storage for FFTW3.
plan< in_array_type, out_array_type > create_forward_plan(in_array_type const &in, out_array_type &out, int flags=default_plan_flags)
Create a plan to compute many DFTs given the input and output arrays.
Definition: plan.hpp:231
time_delay_estimator(timeseries_type &a, timeseries_type &b)
Constructs a time delay estimator using.
jb::extract_value_type< value_type >::precision precision_type
Extract T out of std::complex<T>, otherwise simply T.
vector< std::complex< precision_type > > frequency_timeseries_type
The type used to store the DFT of the input timeseries.
void execute(in_timeseries_type const &in, out_timeseries_type &out) const
Execute the plan for vectors.
Definition: plan.hpp:106
A simple time delay estimator based on cross-correlation.
jb::fftw::plan< timeseries_type, frequency_timeseries_type > dplan
The execution plan to apply the (forward) DFT.
timeseries_t timeseries_type
The input timeseries type.
static int planning_flags()
Determine the correct FFTW planning flags given the inputs.
vector< precision_type > output_timeseries_type
The type used to stored the inverse of the DFT.
jb::fftw::plan< frequency_timeseries_type, output_timeseries_type > iplan
The execution plan to apply the inverse (aka backward) DFT.
timeseries_type::value_type value_type
The values stored in the input timeseries.
plan< in_array_type, out_array_type > create_backward_plan(in_array_type const &in, out_array_type &out, int flags=default_plan_flags)
Create a plan to compute many inverse DFT given the input and output arrays.
Definition: plan.hpp:261
Determine if a timeseries type guarantees alignment suitable for SIMD optimizations.
The top-level namespace for the JayBeams library.
Definition: as_hhmmss.hpp:7