1 /* -*- c++ -*- */
2 /*
3  * Copyright 2010,2012,2014 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 3, 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 <gnuradio/filter/fft_filter.h>
28 #include <volk/volk.h>
29 #include <cstring>
30 #include <iostream>
31 
32 namespace gr {
33 namespace filter {
34 namespace kernel {
35 
36 #define VERBOSE 0
37 
fft_filter_fff(int decimation,const std::vector<float> & taps,int nthreads)38 fft_filter_fff::fft_filter_fff(int decimation,
39                                const std::vector<float>& taps,
40                                int nthreads)
41     : d_fftsize(-1),
42       d_decimation(decimation),
43       d_fwdfft(NULL),
44       d_invfft(NULL),
45       d_nthreads(nthreads),
46       d_xformed_taps(NULL)
47 {
48     set_taps(taps);
49 }
50 
~fft_filter_fff()51 fft_filter_fff::~fft_filter_fff()
52 {
53     delete d_fwdfft;
54     delete d_invfft;
55     if (d_xformed_taps != NULL)
56         volk_free(d_xformed_taps);
57 }
58 
59 /*
60  * determines d_ntaps, d_nsamples, d_fftsize, d_xformed_taps
61  */
set_taps(const std::vector<float> & taps)62 int fft_filter_fff::set_taps(const std::vector<float>& taps)
63 {
64     int i = 0;
65     d_taps = taps;
66     compute_sizes(taps.size());
67 
68     d_tail.resize(tailsize());
69     for (i = 0; i < tailsize(); i++)
70         d_tail[i] = 0;
71 
72     float* in = d_fwdfft->get_inbuf();
73     gr_complex* out = d_fwdfft->get_outbuf();
74 
75     float scale = 1.0 / d_fftsize;
76 
77     // Compute forward xform of taps.
78     // Copy taps into first ntaps slots, then pad with zeros
79     for (i = 0; i < d_ntaps; i++)
80         in[i] = taps[i] * scale;
81 
82     for (; i < d_fftsize; i++)
83         in[i] = 0;
84 
85     d_fwdfft->execute(); // do the xform
86 
87     // now copy output to d_xformed_taps
88     for (i = 0; i < d_fftsize / 2 + 1; i++)
89         d_xformed_taps[i] = out[i];
90 
91     return d_nsamples;
92 }
93 
94 // determine and set d_ntaps, d_nsamples, d_fftsize
compute_sizes(int ntaps)95 void fft_filter_fff::compute_sizes(int ntaps)
96 {
97     int old_fftsize = d_fftsize;
98     d_ntaps = ntaps;
99     d_fftsize = (int)(2 * pow(2.0, ceil(log(double(ntaps)) / log(2.0))));
100     d_nsamples = d_fftsize - d_ntaps + 1;
101 
102     if (VERBOSE) {
103         std::cerr << "fft_filter_fff: ntaps = " << d_ntaps << " fftsize = " << d_fftsize
104                   << " nsamples = " << d_nsamples << std::endl;
105     }
106 
107     // compute new plans
108     if (d_fftsize != old_fftsize) {
109         delete d_fwdfft;
110         delete d_invfft;
111         if (d_xformed_taps != NULL)
112             volk_free(d_xformed_taps);
113         d_fwdfft = new fft::fft_real_fwd(d_fftsize);
114         d_invfft = new fft::fft_real_rev(d_fftsize);
115         d_xformed_taps = (gr_complex*)volk_malloc(
116             sizeof(gr_complex) * (d_fftsize / 2 + 1), volk_get_alignment());
117     }
118 }
119 
set_nthreads(int n)120 void fft_filter_fff::set_nthreads(int n)
121 {
122     d_nthreads = n;
123     if (d_fwdfft)
124         d_fwdfft->set_nthreads(n);
125     if (d_invfft)
126         d_invfft->set_nthreads(n);
127 }
128 
taps() const129 std::vector<float> fft_filter_fff::taps() const { return d_taps; }
130 
ntaps() const131 unsigned int fft_filter_fff::ntaps() const { return d_ntaps; }
132 
nthreads() const133 int fft_filter_fff::nthreads() const { return d_nthreads; }
134 
filter(int nitems,const float * input,float * output)135 int fft_filter_fff::filter(int nitems, const float* input, float* output)
136 {
137     int dec_ctr = 0;
138     int j = 0;
139     int ninput_items = nitems * d_decimation;
140 
141     for (int i = 0; i < ninput_items; i += d_nsamples) {
142 
143         memcpy(d_fwdfft->get_inbuf(), &input[i], d_nsamples * sizeof(float));
144 
145         for (j = d_nsamples; j < d_fftsize; j++)
146             d_fwdfft->get_inbuf()[j] = 0;
147 
148         d_fwdfft->execute(); // compute fwd xform
149 
150         gr_complex* a = d_fwdfft->get_outbuf();
151         gr_complex* b = d_xformed_taps;
152         gr_complex* c = d_invfft->get_inbuf();
153 
154         volk_32fc_x2_multiply_32fc_a(c, a, b, d_fftsize / 2 + 1);
155 
156         d_invfft->execute(); // compute inv xform
157 
158         // add in the overlapping tail
159         for (j = 0; j < tailsize(); j++)
160             d_invfft->get_outbuf()[j] += d_tail[j];
161 
162         // copy nsamples to output
163         j = dec_ctr;
164         while (j < d_nsamples) {
165             *output++ = d_invfft->get_outbuf()[j];
166             j += d_decimation;
167         }
168         dec_ctr = (j - d_nsamples);
169 
170         // stash the tail
171         if (!d_tail.empty()) {
172             memcpy(&d_tail[0],
173                    d_invfft->get_outbuf() + d_nsamples,
174                    tailsize() * sizeof(float));
175         }
176     }
177 
178     return nitems;
179 }
180 
181 
182 /**************************************************************/
183 
184 
fft_filter_ccc(int decimation,const std::vector<gr_complex> & taps,int nthreads)185 fft_filter_ccc::fft_filter_ccc(int decimation,
186                                const std::vector<gr_complex>& taps,
187                                int nthreads)
188     : d_fftsize(-1),
189       d_decimation(decimation),
190       d_fwdfft(NULL),
191       d_invfft(NULL),
192       d_nthreads(nthreads),
193       d_xformed_taps(NULL)
194 {
195     set_taps(taps);
196 }
197 
~fft_filter_ccc()198 fft_filter_ccc::~fft_filter_ccc()
199 {
200     delete d_fwdfft;
201     delete d_invfft;
202     if (d_xformed_taps != NULL)
203         volk_free(d_xformed_taps);
204 }
205 
206 /*
207  * determines d_ntaps, d_nsamples, d_fftsize, d_xformed_taps
208  */
set_taps(const std::vector<gr_complex> & taps)209 int fft_filter_ccc::set_taps(const std::vector<gr_complex>& taps)
210 {
211     int i = 0;
212     d_taps = taps;
213     compute_sizes(taps.size());
214 
215     d_tail.resize(tailsize());
216     for (i = 0; i < tailsize(); i++)
217         d_tail[i] = 0;
218 
219     gr_complex* in = d_fwdfft->get_inbuf();
220     gr_complex* out = d_fwdfft->get_outbuf();
221 
222     float scale = 1.0 / d_fftsize;
223 
224     // Compute forward xform of taps.
225     // Copy taps into first ntaps slots, then pad with zeros
226     for (i = 0; i < d_ntaps; i++)
227         in[i] = taps[i] * scale;
228 
229     for (; i < d_fftsize; i++)
230         in[i] = 0;
231 
232     d_fwdfft->execute(); // do the xform
233 
234     // now copy output to d_xformed_taps
235     for (i = 0; i < d_fftsize; i++)
236         d_xformed_taps[i] = out[i];
237 
238     return d_nsamples;
239 }
240 
241 // determine and set d_ntaps, d_nsamples, d_fftsize
compute_sizes(int ntaps)242 void fft_filter_ccc::compute_sizes(int ntaps)
243 {
244     int old_fftsize = d_fftsize;
245     d_ntaps = ntaps;
246     d_fftsize = (int)(2 * pow(2.0, ceil(log(double(ntaps)) / log(2.0))));
247     d_nsamples = d_fftsize - d_ntaps + 1;
248 
249     if (VERBOSE) {
250         std::cerr << "fft_filter_ccc: ntaps = " << d_ntaps << " fftsize = " << d_fftsize
251                   << " nsamples = " << d_nsamples << std::endl;
252     }
253 
254     // compute new plans
255     if (d_fftsize != old_fftsize) {
256         delete d_fwdfft;
257         delete d_invfft;
258         if (d_xformed_taps != NULL)
259             volk_free(d_xformed_taps);
260         d_fwdfft = new fft::fft_complex(d_fftsize, true, d_nthreads);
261         d_invfft = new fft::fft_complex(d_fftsize, false, d_nthreads);
262         d_xformed_taps = (gr_complex*)volk_malloc(sizeof(gr_complex) * d_fftsize,
263                                                   volk_get_alignment());
264     }
265 }
266 
set_nthreads(int n)267 void fft_filter_ccc::set_nthreads(int n)
268 {
269     d_nthreads = n;
270     if (d_fwdfft)
271         d_fwdfft->set_nthreads(n);
272     if (d_invfft)
273         d_invfft->set_nthreads(n);
274 }
275 
taps() const276 std::vector<gr_complex> fft_filter_ccc::taps() const { return d_taps; }
277 
ntaps() const278 unsigned int fft_filter_ccc::ntaps() const { return d_ntaps; }
279 
nthreads() const280 int fft_filter_ccc::nthreads() const { return d_nthreads; }
281 
filter(int nitems,const gr_complex * input,gr_complex * output)282 int fft_filter_ccc::filter(int nitems, const gr_complex* input, gr_complex* output)
283 {
284     int dec_ctr = 0;
285     int j = 0;
286     int ninput_items = nitems * d_decimation;
287 
288     for (int i = 0; i < ninput_items; i += d_nsamples) {
289         memcpy(d_fwdfft->get_inbuf(), &input[i], d_nsamples * sizeof(gr_complex));
290 
291         for (j = d_nsamples; j < d_fftsize; j++)
292             d_fwdfft->get_inbuf()[j] = 0;
293 
294         d_fwdfft->execute(); // compute fwd xform
295 
296         gr_complex* a = d_fwdfft->get_outbuf();
297         gr_complex* b = d_xformed_taps;
298         gr_complex* c = d_invfft->get_inbuf();
299 
300         volk_32fc_x2_multiply_32fc_a(c, a, b, d_fftsize);
301 
302         d_invfft->execute(); // compute inv xform
303 
304         // add in the overlapping tail
305 
306         for (j = 0; j < tailsize(); j++)
307             d_invfft->get_outbuf()[j] += d_tail[j];
308 
309         // copy nsamples to output
310         j = dec_ctr;
311         while (j < d_nsamples) {
312             *output++ = d_invfft->get_outbuf()[j];
313             j += d_decimation;
314         }
315         dec_ctr = (j - d_nsamples);
316 
317         // stash the tail
318         if (!d_tail.empty()) {
319             memcpy(&d_tail[0],
320                    d_invfft->get_outbuf() + d_nsamples,
321                    tailsize() * sizeof(gr_complex));
322         }
323     }
324 
325     return nitems;
326 }
327 
328 
329 /**************************************************************/
330 
331 
fft_filter_ccf(int decimation,const std::vector<float> & taps,int nthreads)332 fft_filter_ccf::fft_filter_ccf(int decimation,
333                                const std::vector<float>& taps,
334                                int nthreads)
335     : d_fftsize(-1),
336       d_decimation(decimation),
337       d_fwdfft(NULL),
338       d_invfft(NULL),
339       d_nthreads(nthreads),
340       d_xformed_taps(NULL)
341 {
342     set_taps(taps);
343 }
344 
~fft_filter_ccf()345 fft_filter_ccf::~fft_filter_ccf()
346 {
347     delete d_fwdfft;
348     delete d_invfft;
349     if (d_xformed_taps != NULL)
350         volk_free(d_xformed_taps);
351 }
352 
353 /*
354  * determines d_ntaps, d_nsamples, d_fftsize, d_xformed_taps
355  */
set_taps(const std::vector<float> & taps)356 int fft_filter_ccf::set_taps(const std::vector<float>& taps)
357 {
358     int i = 0;
359     d_taps = taps;
360     compute_sizes(taps.size());
361 
362     d_tail.resize(tailsize());
363     for (i = 0; i < tailsize(); i++)
364         d_tail[i] = 0;
365 
366     gr_complex* in = d_fwdfft->get_inbuf();
367     gr_complex* out = d_fwdfft->get_outbuf();
368 
369     float scale = 1.0 / d_fftsize;
370 
371     // Compute forward xform of taps.
372     // Copy taps into first ntaps slots, then pad with zeros
373     for (i = 0; i < d_ntaps; i++)
374         in[i] = gr_complex(taps[i] * scale, 0.0f);
375 
376     for (; i < d_fftsize; i++)
377         in[i] = gr_complex(0.0f, 0.0f);
378 
379     d_fwdfft->execute(); // do the xform
380 
381     // now copy output to d_xformed_taps
382     for (i = 0; i < d_fftsize; i++)
383         d_xformed_taps[i] = out[i];
384 
385     return d_nsamples;
386 }
387 
388 // determine and set d_ntaps, d_nsamples, d_fftsize
compute_sizes(int ntaps)389 void fft_filter_ccf::compute_sizes(int ntaps)
390 {
391     int old_fftsize = d_fftsize;
392     d_ntaps = ntaps;
393     d_fftsize = (int)(2 * pow(2.0, ceil(log(double(ntaps)) / log(2.0))));
394     d_nsamples = d_fftsize - d_ntaps + 1;
395 
396     if (VERBOSE) {
397         std::cerr << "fft_filter_ccf: ntaps = " << d_ntaps << " fftsize = " << d_fftsize
398                   << " nsamples = " << d_nsamples << std::endl;
399     }
400 
401     // compute new plans
402     if (d_fftsize != old_fftsize) {
403         delete d_fwdfft;
404         delete d_invfft;
405         if (d_xformed_taps != NULL)
406             volk_free(d_xformed_taps);
407         d_fwdfft = new fft::fft_complex(d_fftsize, true, d_nthreads);
408         d_invfft = new fft::fft_complex(d_fftsize, false, d_nthreads);
409         d_xformed_taps = (gr_complex*)volk_malloc(sizeof(gr_complex) * d_fftsize,
410                                                   volk_get_alignment());
411     }
412 }
413 
set_nthreads(int n)414 void fft_filter_ccf::set_nthreads(int n)
415 {
416     d_nthreads = n;
417     if (d_fwdfft)
418         d_fwdfft->set_nthreads(n);
419     if (d_invfft)
420         d_invfft->set_nthreads(n);
421 }
422 
taps() const423 std::vector<float> fft_filter_ccf::taps() const { return d_taps; }
424 
ntaps() const425 unsigned int fft_filter_ccf::ntaps() const { return d_ntaps; }
426 
filtersize() const427 unsigned int fft_filter_ccf::filtersize() const { return d_fftsize; }
428 
nthreads() const429 int fft_filter_ccf::nthreads() const { return d_nthreads; }
430 
filter(int nitems,const gr_complex * input,gr_complex * output)431 int fft_filter_ccf::filter(int nitems, const gr_complex* input, gr_complex* output)
432 {
433     int dec_ctr = 0;
434     int j = 0;
435     int ninput_items = nitems * d_decimation;
436 
437     for (int i = 0; i < ninput_items; i += d_nsamples) {
438         memcpy(d_fwdfft->get_inbuf(), &input[i], d_nsamples * sizeof(gr_complex));
439 
440         for (j = d_nsamples; j < d_fftsize; j++)
441             d_fwdfft->get_inbuf()[j] = 0;
442 
443         d_fwdfft->execute(); // compute fwd xform
444 
445         gr_complex* a = d_fwdfft->get_outbuf();
446         gr_complex* b = d_xformed_taps;
447         gr_complex* c = d_invfft->get_inbuf();
448 
449         volk_32fc_x2_multiply_32fc_a(c, a, b, d_fftsize);
450 
451         d_invfft->execute(); // compute inv xform
452 
453         // add in the overlapping tail
454 
455         for (j = 0; j < tailsize(); j++)
456             d_invfft->get_outbuf()[j] += d_tail[j];
457 
458         // copy nsamples to output
459         j = dec_ctr;
460         while (j < d_nsamples) {
461             *output++ = d_invfft->get_outbuf()[j];
462             j += d_decimation;
463         }
464         dec_ctr = (j - d_nsamples);
465 
466         // stash the tail
467         if (!d_tail.empty()) {
468             memcpy(&d_tail[0],
469                    d_invfft->get_outbuf() + d_nsamples,
470                    tailsize() * sizeof(gr_complex));
471         }
472     }
473 
474     return nitems;
475 }
476 
477 
478 } /* namespace kernel */
479 } /* namespace filter */
480 } /* namespace gr */
481