Imported Upstream version 3.0
[debian/gnuradio] / gnuradio-core / src / lib / filter / qa_gr_fir_fff.cc
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2002 Free Software Foundation, Inc.
4  * 
5  * This file is part of GNU Radio
6  * 
7  * GNU Radio is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2, or (at your option)
10  * any later version.
11  * 
12  * GNU Radio is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with GNU Radio; see the file COPYING.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <qa_gr_fir_fff.h>
28 #include <gr_fir_fff.h>
29 #include <gr_fir_util.h>
30 #include <string.h>
31 #include <iostream>
32 #include <cmath>
33 #include <cppunit/TestAssert.h>
34 #include <random.h>
35
36 using std::vector;
37
38 typedef float   i_type;
39 typedef float   o_type;
40 typedef float   tap_type;
41 typedef float   acc_type;
42
43 #define ERR_DELTA       (1e-6)
44
45 #define NELEM(x) (sizeof (x) / sizeof (x[0]))
46
47
48 //
49 // typedef for something logically "pointer to constructor".
50 // there may be a better way, please let me know...
51 //
52 typedef gr_fir_fff* (*fir_maker_t)(const std::vector<tap_type> &taps);
53
54
55 // ----------------------------------------------------------------
56
57 const static i_type input_1[] = {
58   234,  -4,  23,  -56,  45,    98,  -23,  -7
59 };
60
61 const static tap_type taps_1a[] = {
62   -3
63 };
64
65 const static o_type expected_1a[] = {
66   -702, 12, -69,   168, -135, -294,  69,  21
67 };
68
69 const static tap_type taps_1b[] = {
70   -4, 5
71 };
72
73 const static o_type expected_1b[] = {
74   1186, -112, 339, -460, -167, 582, -87
75 };
76
77 // ----------------------------------------------------------------
78
79 static void
80 test_known_io (fir_maker_t maker)
81 {
82   vector<tap_type> t1a (&taps_1a[0], &taps_1a[NELEM (taps_1a)]);
83   vector<tap_type> t1b (&taps_1b[0], &taps_1b[NELEM (taps_1b)]);
84   
85   gr_fir_fff *f1 = maker (t1a);         // create filter
86   CPPUNIT_ASSERT_EQUAL ((unsigned) 1, f1->ntaps ());    // check ntaps
87
88   // check filter output
89   int n = NELEM (input_1) - f1->ntaps () + 1;
90   for (int i = 0; i < n; i++)
91     CPPUNIT_ASSERT_DOUBLES_EQUAL (expected_1a[i], f1->filter (&input_1[i]), ERR_DELTA);
92
93   f1->set_taps (t1b);                   // set new taps
94   CPPUNIT_ASSERT_EQUAL ((unsigned) 2, f1->ntaps ());    // check ntaps
95   
96   // check filter output
97   n = NELEM (input_1) - f1->ntaps () + 1;
98   for (int i = 0; i < n; i++)
99     CPPUNIT_ASSERT_DOUBLES_EQUAL (expected_1b[i], f1->filter (&input_1[i]), ERR_DELTA);
100
101   // test filterN interface
102
103   o_type output[NELEM (expected_1b)];
104   memset (output, 0, sizeof (output));
105
106   f1->filterN (output, input_1, n);
107   for (int i = 0; i < n; i++)
108     CPPUNIT_ASSERT_DOUBLES_EQUAL (expected_1b[i], output[i], ERR_DELTA);
109
110   delete f1;
111 }
112
113 //
114 // Test for ntaps in [0,9], and input lengths in [0,17].
115 // This ensures that we are building the shifted taps correctly,
116 // and exercises all corner cases on input alignment and length.
117 //
118
119 static float
120 uniform ()
121 {
122   return 2.0 * ((float) random () / RANDOM_MAX - 0.5);  // uniformly (-1, 1)
123 }
124
125 static void
126 random_floats (float *buf, unsigned n)
127 {
128   for (unsigned i = 0; i < n; i++)
129     buf[i] = rint (uniform () * 32768);
130 }
131
132 static o_type
133 ref_dotprod (const i_type input[], const tap_type taps[], int ntaps)
134 {
135   acc_type      sum = 0;
136   for (int i = 0; i < ntaps; i++)
137     sum += input[i] * taps[ntaps - i - 1];
138
139   return sum;
140 }
141
142 static void
143 test_random_io (fir_maker_t maker)  
144 {
145   const int     MAX_TAPS        = 9;
146   const int     OUTPUT_LEN      = 17;
147   const int     INPUT_LEN       = MAX_TAPS + OUTPUT_LEN;
148
149   i_type        input[INPUT_LEN];
150   o_type        expected_output[OUTPUT_LEN];
151   o_type        actual_output[OUTPUT_LEN];
152   tap_type      taps[MAX_TAPS];
153
154
155   srandom (0);  // we want reproducibility
156
157   for (int n = 0; n <= MAX_TAPS; n++){
158     for (int ol = 0; ol <= OUTPUT_LEN; ol++){
159
160       // cerr << "@@@ n:ol " << n << ":" << ol << endl;
161
162       // build random test case
163       random_floats (input, INPUT_LEN);
164       random_floats (taps, MAX_TAPS);
165
166       // compute expected output values
167       for (int o = 0; o < ol; o++){
168         expected_output[o] = ref_dotprod (&input[o], taps, n);
169       }
170
171       // build filter
172
173       vector<tap_type> f1_taps (&taps[0], &taps[n]);
174       gr_fir_fff *f1 = maker (f1_taps);
175
176       // zero the output, then do the filtering
177       memset (actual_output, 0, sizeof (actual_output));
178       f1->filterN (actual_output, input, ol);
179
180       // check results
181       //
182       // we use a sloppy error margin because on the x86 architecture,
183       // our reference implementation is using 80 bit floating point
184       // arithmetic, while the SSE version is using 32 bit float point
185       // arithmetic.
186       
187       for (int o = 0; o < ol; o++){
188         CPPUNIT_ASSERT_DOUBLES_EQUAL (expected_output[o], actual_output[o],
189                             fabs (expected_output[o]) * 1e-4);
190       }
191
192       delete f1;
193     }
194   }
195 }
196
197
198 static void
199 for_each (void (*f)(fir_maker_t))
200 {
201   std::vector<gr_fir_fff_info>          info;
202   gr_fir_util::get_gr_fir_fff_info (&info);     // get all known fff implementations 
203
204   for (std::vector<gr_fir_fff_info>::iterator p = info.begin ();
205        p != info.end ();
206        ++p){
207
208     std::cerr << " [" << p->name << "]";
209     f (p->create);
210   }
211
212   std::cerr << std::endl;
213 }
214
215 void
216 qa_gr_fir_fff::t1 ()
217 {
218   for_each (test_known_io);
219 }
220
221 void
222 qa_gr_fir_fff::t2 ()
223 {
224   for_each (test_random_io);
225 }