Cheetah - SKA - PSS - Prototype Time Domain Search Pipeline
FftTester.cpp
1 /*
2  * The MIT License (MIT)
3  *
4  * Copyright (c) 2016 The SKA organisation
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24 #include "cheetah/fft/test_utils/FftTester.h"
25 #include "cheetah/data/TimeSeries.h"
26 #include "cheetah/data/FrequencySeries.h"
27 #include "cheetah/data/ComplexTypeTraits.h"
28 #include "panda/Log.h"
29 #include "panda/TypeTraits.h"
30 
31 #include <functional>
32 
33 namespace ska {
34 namespace cheetah {
35 namespace fft {
36 namespace test {
37 
38 namespace {
39  // Utilities to determine which process methods ar instantiated
40  template<typename TraitsT
41  , typename InputType
42  , typename OutputType>
43  class HasFftProcessMethod
44  {
45  private:
46  template<typename Algo>
47  using HasC2C_t = decltype(std::declval<Algo>().process(std::declval<typename TraitsT::DeviceType&>(),
48  std::declval<InputType const&>(),
49  std::declval<OutputType&>()
50  ));
51  typedef typename TraitsT::Algo Algorithm;
52 
53  public:
54  static const bool value = panda::HasMethod<Algorithm, HasC2C_t>::value;
55  };
56 
57  template<typename TypeParam, typename InputValueT, typename OutputValueT>
58  using HasTime2Frequency = HasFftProcessMethod< TypeParam
59  , data::TimeSeries<typename TypeParam::Arch
60  , InputValueT>
61  , data::FrequencySeries<typename TypeParam::Arch
62  , OutputValueT>
63  >;
64  template<typename TypeParam, typename InputValueT, typename OutputValueT>
65  using HasFrequency2Time = HasFftProcessMethod< TypeParam
66  , data::FrequencySeries<typename TypeParam::Arch
67  , InputValueT>
68  , data::TimeSeries<typename TypeParam::Arch
69  , OutputValueT>
70  >;
71  // --- helper tests
72  struct TestDevice {};
73  struct TestArch {};
74  struct A {
75  };
76  struct B{
77  void process( TestDevice&
78  , data::TimeSeries<TestArch, float> const&
79  , data::FrequencySeries<TestArch, std::complex<float>>&) {}
80  };
81  template<class T>
82  struct TypeParam {
83  typedef TestDevice DeviceType;
84  typedef TestArch Arch;
85  typedef T Algo;
86  };
87  static_assert(!HasTime2Frequency<TypeParam<A>, float, std::complex<float>>::value, "not detecting process method");
88  static_assert(HasTime2Frequency<TypeParam<B>, float, std::complex<float>>::value, "not detecting process method");
89 
90  // -- Base type for Real to complex type tests
91  template<class DerivedType, typename TypeParam, typename Enable=void>
92  struct R2C_Base
93  {
94  static inline
95  void exec(typename TypeParam::DeviceType& device)
96  {
97  DerivedType::run_test(device);
98  }
99  };
100 
101  template<class DerivedType, typename TypeParam>
102  struct R2C_Base<DerivedType, TypeParam
103  , typename std::enable_if<
104  !HasTime2Frequency<TypeParam
105  , typename TypeParam::NumericalRep
106  , typename TypeParam::ComplexT
107  >::value
108  >::type>
109  {
110  static inline
111  void exec(typename TypeParam::DeviceType&)
112  {
113  std::cout << "Real to Complex FT not defined for this device";
114  }
115  };
116 
117  template<typename TypeParam, class InputType, class OutputType
118  , typename Enable=void>
119  struct ProcessHelper
120  {
121  static inline
122  bool exec(TypeParam& traits
123  , typename TypeParam::DeviceType& device
124  , InputType const& input
125  , OutputType& output
126  )
127  {
128  traits.api().process(device, input, output);
129  return true;
130  }
131  };
132 
133  template<typename TypeParam, class InputType, class OutputType>
134  struct ProcessHelper<TypeParam, InputType, OutputType
135  , typename std::enable_if<
136  !HasFftProcessMethod<TypeParam, InputType, OutputType>::value>::type>
137  {
138  static inline
139  bool exec(TypeParam&
140  , typename TypeParam::DeviceType&
141  , InputType const&
142  , OutputType&
143  )
144  {
145  return false;
146  }
147  };
148 
149  /*
150  * @brief attemps to invoke the process() method on the API
151  * @details if method exists it will be executed
152  * @returns true : if process method exists
153  * false: otherwise
154  */
155  template<typename TypeParam, class InputType, class OutputType>
156  bool process(TypeParam& traits
157  , typename TypeParam::DeviceType& device
158  , InputType const& input
159  , OutputType& output)
160  {
161  return ProcessHelper<TypeParam, InputType, OutputType>::exec(traits, device, input, output);
162  }
163 
164 } // namespace
165 
166 template<typename Algorithm, typename NumericalT>
167 FftTesterTraits<Algorithm, NumericalT>::FftTesterTraits()
168  : _api(_config)
169 {
170 }
171 
172 template<typename Algorithm, typename NumericalT>
173 fft::Fft& FftTesterTraits<Algorithm, NumericalT>::api()
174 {
175  return _api;
176 }
177 
178 template <typename TestTraits>
179 FftTester<TestTraits>::FftTester()
180  : cheetah::utils::test::AlgorithmTester<TestTraits>()
181 {
182 }
183 
184 template <typename TestTraits>
185 FftTester<TestTraits>::~FftTester()
186 {
187 }
188 
189 template<typename TestTraits>
190 void FftTester<TestTraits>::SetUp()
191 {
192 
193 }
194 
195 template<typename TestTraits>
196 void FftTester<TestTraits>::TearDown()
197 {
198 }
199 
200 template<typename TypeParam>
201 struct FftR2C_ZeroTest : public R2C_Base<FftR2C_ZeroTest<TypeParam>, TypeParam>
202 {
203  static inline
204  void run_test(typename TypeParam::DeviceType& device)
205  {
206  TypeParam traits;
207  typedef typename TypeParam::NumericalRep T;
208  typedef typename TypeParam::Arch Arch;
209  typedef typename TypeParam::ComplexT ComplexT;
210 
211  typedef data::TimeSeries<Arch, T> InputType;
212  typedef data::FrequencySeries<Arch, ComplexT> OutputType;
213  typedef data::TimeSeries<Cpu, T> InputTypeHost;
214  typedef data::FrequencySeries<Cpu, ComplexT> OutputTypeHost;
215  std::size_t fft_len=TypeParam::fft_trial_length;
216 
217  InputTypeHost data_in(data::TimeType(0.003 * data::seconds), fft_len);
218  std::fill(data_in.begin(), data_in.end(), 0);
219  InputType input(data_in, traits.template allocator<InputType>(device));
220  OutputType output(traits.template allocator<OutputType>(device));
221  traits.api().process(device, input, output);
222  ASSERT_EQ(output.size(), input.size()/2 + 1);
223  ASSERT_EQ(output.frequency_step().value(), (1.0f/(input.sampling_interval().value() * input.size())));
224  OutputTypeHost data_out(output);
225  for(auto const& elm : data_out){
226  ASSERT_FLOAT_EQ(elm.real(), 0.0);
227  ASSERT_FLOAT_EQ(elm.imag(), 0.0);
228  }
229 
230  // perform inverse transform
231  InputType inverse(traits.template allocator<InputType>(device));
232  if(! process(traits, device, output, inverse))
233  {
234  std::cout << "Inverse transform not availaable - skipping test";
235  return;
236  }
237 
238  InputTypeHost inverse_host(inverse);
239  ASSERT_EQ(inverse_host.size(), data_in.size());
240  auto inv_it=inverse_host.begin();
241  std::size_t count=0;
242  for(auto const& elm : data_in)
243  {
244  SCOPED_TRACE(std::to_string(count));
245  ASSERT_FLOAT_EQ(elm, *inv_it);
246  ++inv_it;
247  ++count;
248  }
249  }
250 };
251 
252 ALGORITHM_TYPED_TEST_P(FftTester, fft_r2c_zero)
253 {
255 }
256 
262 template<typename TypeParam>
263 struct FftR2C_DeltaTest : public R2C_Base<FftR2C_DeltaTest<TypeParam>, TypeParam>
264 {
265  static inline
266  void run_test(typename TypeParam::DeviceType& device)
267  {
268  TypeParam traits;
269  typedef typename TypeParam::NumericalRep T;
270  typedef typename TypeParam::Arch Arch;
271  typedef typename TypeParam::ComplexT ComplexT;
272 
273  typedef data::TimeSeries<Arch, T> InputType;
274  typedef data::FrequencySeries<Arch, ComplexT> OutputType;
275  typedef data::TimeSeries<Cpu, T> InputTypeHost;
276  typedef data::FrequencySeries<Cpu, ComplexT> OutputTypeHost;
277  std::size_t fft_len=TypeParam::fft_trial_length;
278 
279  InputTypeHost data_in(data::TimeType(0.003 * data::seconds), fft_len);
280  *(data_in.begin())=1;
281  std::fill(data_in.begin()+1, data_in.end(), 0);
282  InputType input(data_in, traits.template allocator<InputType>(device));
283  OutputType output(traits.template allocator<OutputType>(device));
284  traits.api().process(device, input, output);
285  ASSERT_EQ(output.size(), input.size()/2 + 1);
286  ASSERT_EQ(output.frequency_step().value(), (1.0f/(input.sampling_interval().value() * input.size())));
287  OutputTypeHost data_out(output);
288  std::size_t count=0;
289  for(auto const& elm : data_out){
290  SCOPED_TRACE(std::to_string(count));
291  ASSERT_FLOAT_EQ(round(elm.real()), 1); // n.b. output types are not normalised by default
292  ASSERT_FLOAT_EQ(round(elm.imag()), 0.0);
293  ASSERT_TRUE(std::abs(elm.real()-1.0) < 1e-6);
294  ASSERT_TRUE(std::abs(elm.imag()-0.0) < 1e-6);
295  ++count;
296  }
297 
298  // perform inverse transform (i.e a c2r transform)
299  InputType inverse(traits.template allocator<InputType>(device));
300  if(! process(traits, device, output, inverse))
301  {
302  std::cout << "Inverse transform not availaable - skipping test";
303  return;
304  }
305 
306  ASSERT_EQ(inverse.size(), 2*(output.size() - 1));
307  ASSERT_EQ(inverse.sampling_interval().value(), (1.0f/(output.frequency_step().value() * inverse.size())));
308  InputTypeHost inverse_host(inverse);
309  ASSERT_EQ(inverse_host.size(), data_in.size());
310 
311  // verfiy inverse has returned the original data
312  auto inv_it=inverse_host.begin();
313  count=0;
314  for(auto const& elm : data_in)
315  {
316  SCOPED_TRACE(std::to_string(count));
317  ASSERT_TRUE(std::abs(elm*output.size() - *inv_it) < TypeParam::accuracy()) << "expected:" << elm*output.size() << " got:" << *inv_it << " accuracy:" << TypeParam::accuracy();
318  ++inv_it;
319  ++count;
320  }
321  }
322 };
323 
324 ALGORITHM_TYPED_TEST_P(FftTester, fft_r2c_c2r_delta)
325 {
327 }
328 
329 
335 template<typename TypeParam>
336 struct FftR2C_ShiftTest : public R2C_Base<FftR2C_ShiftTest<TypeParam>, TypeParam>
337 {
338  static inline
339  void run_test(typename TypeParam::DeviceType& device)
340  {
341  TypeParam traits;
342  typedef typename TypeParam::NumericalRep T;
343  typedef typename TypeParam::Arch Arch;
344  typedef typename TypeParam::ComplexT ComplexT;
345 
346  typedef data::TimeSeries<Arch, T> InputType;
347  typedef data::FrequencySeries<Arch, ComplexT> OutputType;
348  typedef data::TimeSeries<Cpu, T> InputTypeHost;
349  typedef data::FrequencySeries<Cpu, ComplexT> OutputTypeHost;
350  std::size_t fft_len=TypeParam::fft_trial_length;
351 
352  InputTypeHost data_in(data::TimeType(0.003 * data::seconds), fft_len);
353  *(data_in.begin())=0;
354  *(data_in.begin()+1)=1;
355  std::fill(data_in.begin()+3, data_in.end(), 0);
356  InputType input(data_in, traits.template allocator<InputType>(device));
357  OutputType output(traits.template allocator<OutputType>(device));
358  traits.api().process(device, input, output);
359  ASSERT_EQ(output.size(), input.size()/2 + 1);
360  ASSERT_EQ(output.frequency_step().value(), (1.0f/(input.sampling_interval().value() * input.size())));
361  OutputTypeHost data_out(output);
362  size_t count=0;
363  for(auto const& elm : data_out){
364  SCOPED_TRACE(std::to_string(count));
365  ASSERT_FLOAT_EQ((T)1, (T)round(sqrt(elm.real()*elm.real()+elm.imag()*elm.imag())));
366  ASSERT_TRUE(std::abs((T)(sqrt(elm.real()*elm.real()+elm.imag()*elm.imag()))-(T)1) < 1e-6);
367  }
368  }
369 };
370 
371 ALGORITHM_TYPED_TEST_P(FftTester, fft_r2c_shift)
372 {
374 }
375 
376 template<typename TypeParam, typename Enable=void>
378 {
380  static inline
381  void exec(typename TypeParam::DeviceType& device)
382  {
383  TypeParam traits;
384  typedef typename TypeParam::NumericalRep T;
385  typedef typename TypeParam::Arch Arch;
386  typedef typename TypeParam::ComplexT ComplexT;
387 
388  typedef data::TimeSeries<Arch, ComplexT> InputType;
389  typedef data::FrequencySeries<Arch, ComplexT> OutputType;
390  InputType input(0.066 * data::seconds, 1024, traits.template allocator<InputType>(device));
391  OutputType output(traits.template allocator<OutputType>(device));
392  traits.api().process(device, input, output);
393  ASSERT_EQ(output.size(), input.size());
394  ASSERT_EQ(output.frequency_step().value(), (1.0f/(input.sampling_interval().value() * input.size())));
395  }
396 };
397 
398 template<typename TypeParam>
399 struct FftC2C_Fwd_Test<TypeParam
400  , typename std::enable_if<
401  !HasTime2Frequency<TypeParam
402  , typename TypeParam::ComplexT
403  , typename TypeParam::ComplexT
404  >::value
405  >::type>
406 {
407  static inline
408  void exec(typename TypeParam::DeviceType&)
409  {
410  std::cout << "Complex to Complex FT not defined for this device";
411  }
412 };
413 
414 ALGORITHM_TYPED_TEST_P(FftTester, fft_c2c_fwd)
415 {
417 }
418 
419 template<typename TypeParam, typename Enable=void>
421 {
422  static inline
423  void exec(typename TypeParam::DeviceType& device)
424  {
425  TypeParam traits;
426  typedef typename TypeParam::NumericalRep T;
427  typedef typename TypeParam::Arch Arch;
428  typedef typename TypeParam::ComplexT ComplexT;
429 
430  typedef data::FrequencySeries<Arch, ComplexT> InputType;
431  typedef data::TimeSeries<Arch, ComplexT> OutputType;
432  InputType input(0.0035 * data::hz, 1024, traits.template allocator<InputType>(device));
433  OutputType output(traits.template allocator<OutputType>(device));
434  traits.api().process(device, input, output);
435  ASSERT_EQ(output.size(), input.size());
436  ASSERT_EQ(output.sampling_interval().value(), (1.0f/(input.frequency_step().value() * output.size())));
437  }
438 };
439 
440 template<typename TypeParam>
441 struct FftC2C_Inv_Test<TypeParam
442  , typename std::enable_if<
443  !HasFrequency2Time<TypeParam
444  , typename TypeParam::ComplexT
445  , typename TypeParam::ComplexT
446  >::value
447  >::type>
448 {
449  static inline
450  void exec(typename TypeParam::DeviceType&)
451  {
452  std::cout << "Complex to Complex Inverse FT not defined for this device";
453  }
454 };
455 
456 ALGORITHM_TYPED_TEST_P(FftTester, fft_c2c_inv)
457 {
459 }
460 
463 REGISTER_TYPED_TEST_CASE_P(FftTester, fft_r2c_zero, fft_r2c_c2r_delta, fft_r2c_shift, fft_c2c_fwd, fft_c2c_inv);
464 } // namespace test
465 } // namespace fft
466 } // namespace cheetah
467 } // namespace ska
A container of Fourier series data.
Some limits and constants for FLDO.
Definition: Brdz.h:35
Class for time series data.
Definition: TimeSeries.h:47
static void exec(typename TypeParam::DeviceType &device)
TimeSeries<Complex> -> FrequencySeries<Complex>
Definition: FftTester.cpp:381