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