1 /* -*- c++ -*- */
2 /*
3 * Gqrx SDR: Software defined radio receiver powered by GNU Radio and Qt
4 * https://gqrx.dk/
5 *
6 * Copyright 2011-2013 Alexandru Csete OZ9AEC.
7 *
8 * Gqrx is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3, or (at your option)
11 * any later version.
12 *
13 * Gqrx is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with Gqrx; see the file COPYING. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street,
21 * Boston, MA 02110-1301, USA.
22 */
23 #include <math.h>
24 #include <gnuradio/io_signature.h>
25 #include <gnuradio/filter/firdes.h>
26 #include <gnuradio/gr_complex.h>
27 #include <gnuradio/fft/fft.h>
28 #include "dsp/rx_fft.h"
29 #include <algorithm>
30
31
make_rx_fft_c(unsigned int fftsize,double quad_rate,int wintype)32 rx_fft_c_sptr make_rx_fft_c (unsigned int fftsize, double quad_rate, int wintype)
33 {
34 return gnuradio::get_initial_sptr(new rx_fft_c (fftsize, quad_rate, wintype));
35 }
36
37 /*! \brief Create receiver FFT object.
38 * \param fftsize The FFT size.
39 * \param wintype The window type (see gr::fft::window::win_type).
40 *
41 */
rx_fft_c(unsigned int fftsize,double quad_rate,int wintype)42 rx_fft_c::rx_fft_c(unsigned int fftsize, double quad_rate, int wintype)
43 : gr::sync_block ("rx_fft_c",
44 gr::io_signature::make(1, 1, sizeof(gr_complex)),
45 gr::io_signature::make(0, 0, 0)),
46 d_fftsize(fftsize),
47 d_quadrate(quad_rate),
48 d_wintype(-1)
49 {
50
51 /* create FFT object */
52 #if GNURADIO_VERSION < 0x030900
53 d_fft = new gr::fft::fft_complex(d_fftsize, true);
54 #else
55 d_fft = new gr::fft::fft_complex_fwd(d_fftsize);
56 #endif
57
58 /* allocate circular buffer */
59 d_cbuf.set_capacity(d_fftsize + d_quadrate);
60
61 /* create FFT window */
62 set_window_type(wintype);
63
64 d_lasttime = std::chrono::steady_clock::now();
65 }
66
~rx_fft_c()67 rx_fft_c::~rx_fft_c()
68 {
69 delete d_fft;
70 }
71
72 /*! \brief Receiver FFT work method.
73 * \param noutput_items
74 * \param input_items
75 * \param output_items
76 *
77 * This method does nothing except throwing the incoming samples into the
78 * circular buffer.
79 * FFT is only executed when the GUI asks for new FFT data via get_fft_data().
80 */
work(int noutput_items,gr_vector_const_void_star & input_items,gr_vector_void_star & output_items)81 int rx_fft_c::work(int noutput_items,
82 gr_vector_const_void_star &input_items,
83 gr_vector_void_star &output_items)
84 {
85 int i;
86 const gr_complex *in = (const gr_complex*)input_items[0];
87 (void) output_items;
88
89 /* just throw new samples into the buffer */
90 std::lock_guard<std::mutex> lock(d_mutex);
91 for (i = 0; i < noutput_items; i++)
92 {
93 d_cbuf.push_back(in[i]);
94 }
95
96 return noutput_items;
97
98 }
99
100 /*! \brief Get FFT data.
101 * \param fftPoints Buffer to copy FFT data
102 * \param fftSize Current FFT size (output).
103 */
get_fft_data(std::complex<float> * fftPoints,unsigned int & fftSize)104 void rx_fft_c::get_fft_data(std::complex<float>* fftPoints, unsigned int &fftSize)
105 {
106 std::lock_guard<std::mutex> lock(d_mutex);
107
108 if (d_cbuf.size() < d_fftsize)
109 {
110 // not enough samples in the buffer
111 fftSize = 0;
112
113 return;
114 }
115
116 std::chrono::time_point<std::chrono::steady_clock> now = std::chrono::steady_clock::now();
117 std::chrono::duration<double> diff = now - d_lasttime;
118 d_lasttime = now;
119
120 /* perform FFT */
121 d_cbuf.erase_begin(std::min((unsigned int)(diff.count() * d_quadrate * 1.001), (unsigned int)d_cbuf.size() - d_fftsize));
122 do_fft(d_fftsize);
123 //d_cbuf.clear();
124
125 /* get FFT data */
126 memcpy(fftPoints, d_fft->get_outbuf(), sizeof(gr_complex)*d_fftsize);
127 fftSize = d_fftsize;
128 }
129
130 /*! \brief Compute FFT on the available input data.
131 * \param data_in The data to compute FFT on.
132 * \param size The size of data_in.
133 *
134 * Note that this function does not lock the mutex since the caller, get_fft_data()
135 * has already locked it.
136 */
do_fft(unsigned int size)137 void rx_fft_c::do_fft(unsigned int size)
138 {
139 /* apply window, if any */
140 if (d_window.size())
141 {
142 gr_complex *dst = d_fft->get_inbuf();
143 for (unsigned int i = 0; i < size; i++)
144 dst[i] = d_cbuf[i] * d_window[i];
145 }
146 else
147 {
148 memcpy(d_fft->get_inbuf(), d_cbuf.linearize(), sizeof(gr_complex)*size);
149 }
150
151 /* compute FFT */
152 d_fft->execute();
153 }
154
155 /*! \brief Update circular buffer and FFT object. */
set_params()156 void rx_fft_c::set_params()
157 {
158 std::lock_guard<std::mutex> lock(d_mutex);
159
160 /* clear and resize circular buffer */
161 d_cbuf.clear();
162 d_cbuf.set_capacity(d_fftsize + d_quadrate);
163
164 /* reset window */
165 int wintype = d_wintype; // FIXME: would be nicer with a window_reset()
166 d_wintype = -1;
167 set_window_type(wintype);
168
169 /* reset FFT object (also reset FFTW plan) */
170 delete d_fft;
171 #if GNURADIO_VERSION < 0x030900
172 d_fft = new gr::fft::fft_complex(d_fftsize, true);
173 #else
174 d_fft = new gr::fft::fft_complex_fwd(d_fftsize);
175 #endif
176 }
177
178 /*! \brief Set new FFT size. */
set_fft_size(unsigned int fftsize)179 void rx_fft_c::set_fft_size(unsigned int fftsize)
180 {
181 if (fftsize != d_fftsize)
182 {
183 d_fftsize = fftsize;
184 set_params();
185 }
186
187 }
188
189 /*! \brief Set new quadrature rate. */
set_quad_rate(double quad_rate)190 void rx_fft_c::set_quad_rate(double quad_rate)
191 {
192 if (quad_rate != d_quadrate) {
193 d_quadrate = quad_rate;
194 set_params();
195 }
196 }
197
198 /*! \brief Get currently used FFT size. */
get_fft_size() const199 unsigned int rx_fft_c::get_fft_size() const
200 {
201 return d_fftsize;
202 }
203
204 /*! \brief Set new window type. */
set_window_type(int wintype)205 void rx_fft_c::set_window_type(int wintype)
206 {
207 if (wintype == d_wintype)
208 {
209 /* nothing to do */
210 return;
211 }
212
213 d_wintype = wintype;
214
215 if ((d_wintype < gr::fft::window::WIN_HAMMING) || (d_wintype > gr::fft::window::WIN_FLATTOP))
216 {
217 d_wintype = gr::fft::window::WIN_HAMMING;
218 }
219
220 d_window.clear();
221 d_window = gr::fft::window::build((gr::fft::window::win_type)d_wintype, d_fftsize, 6.76);
222 }
223
224 /*! \brief Get currently used window type. */
get_window_type() const225 int rx_fft_c::get_window_type() const
226 {
227 return d_wintype;
228 }
229
230
231 /** rx_fft_f **/
232
make_rx_fft_f(unsigned int fftsize,double audio_rate,int wintype)233 rx_fft_f_sptr make_rx_fft_f(unsigned int fftsize, double audio_rate, int wintype)
234 {
235 return gnuradio::get_initial_sptr(new rx_fft_f (fftsize, audio_rate, wintype));
236 }
237
238 /*! \brief Create receiver FFT object.
239 * \param fftsize The FFT size.
240 * \param wintype The window type (see gr::fft::window::win_type).
241 *
242 */
rx_fft_f(unsigned int fftsize,double audio_rate,int wintype)243 rx_fft_f::rx_fft_f(unsigned int fftsize, double audio_rate, int wintype)
244 : gr::sync_block ("rx_fft_f",
245 gr::io_signature::make(1, 1, sizeof(float)),
246 gr::io_signature::make(0, 0, 0)),
247 d_fftsize(fftsize),
248 d_audiorate(audio_rate),
249 d_wintype(-1)
250 {
251
252 /* create FFT object */
253 #if GNURADIO_VERSION < 0x030900
254 d_fft = new gr::fft::fft_complex(d_fftsize, true);
255 #else
256 d_fft = new gr::fft::fft_complex_fwd(d_fftsize);
257 #endif
258
259 /* allocate circular buffer */
260 d_cbuf.set_capacity(d_fftsize + d_audiorate);
261
262 /* create FFT window */
263 set_window_type(wintype);
264 }
265
~rx_fft_f()266 rx_fft_f::~rx_fft_f()
267 {
268 delete d_fft;
269 }
270
271 /*! \brief Audio FFT work method.
272 * \param noutput_items
273 * \param input_items
274 * \param output_items
275 *
276 * This method does nothing except throwing the incoming samples into the
277 * circular buffer.
278 * FFT is only executed when the GUI asks for new FFT data via get_fft_data().
279 */
work(int noutput_items,gr_vector_const_void_star & input_items,gr_vector_void_star & output_items)280 int rx_fft_f::work(int noutput_items,
281 gr_vector_const_void_star &input_items,
282 gr_vector_void_star &output_items)
283 {
284 int i;
285 const float *in = (const float*)input_items[0];
286 (void) output_items;
287
288 /* just throw new samples into the buffer */
289 std::lock_guard<std::mutex> lock(d_mutex);
290 for (i = 0; i < noutput_items; i++)
291 {
292 d_cbuf.push_back(in[i]);
293 }
294
295 return noutput_items;
296 }
297
298 /*! \brief Get FFT data.
299 * \param fftPoints Buffer to copy FFT data
300 * \param fftSize Current FFT size (output).
301 */
get_fft_data(std::complex<float> * fftPoints,unsigned int & fftSize)302 void rx_fft_f::get_fft_data(std::complex<float>* fftPoints, unsigned int &fftSize)
303 {
304 std::lock_guard<std::mutex> lock(d_mutex);
305
306 if (d_cbuf.size() < d_fftsize)
307 {
308 // not enough samples in the buffer
309 fftSize = 0;
310
311 return;
312 }
313
314 std::chrono::time_point<std::chrono::steady_clock> now = std::chrono::steady_clock::now();
315 std::chrono::duration<double> diff = now - d_lasttime;
316 d_lasttime = now;
317
318 /* perform FFT */
319 d_cbuf.erase_begin(std::min((unsigned int)(diff.count() * d_audiorate * 1.001), (unsigned int)d_cbuf.size() - d_fftsize));
320 do_fft(d_fftsize);
321 //d_cbuf.clear();
322
323 /* get FFT data */
324 memcpy(fftPoints, d_fft->get_outbuf(), sizeof(gr_complex)*d_fftsize);
325 fftSize = d_fftsize;
326 }
327
328 /*! \brief Compute FFT on the available input data.
329 * \param data_in The data to compute FFT on.
330 * \param size The size of data_in.
331 *
332 * Note that this function does not lock the mutex since the caller, get_fft_data()
333 * has already locked it.
334 */
do_fft(unsigned int size)335 void rx_fft_f::do_fft(unsigned int size)
336 {
337 gr_complex *dst = d_fft->get_inbuf();
338 unsigned int i;
339
340 /* apply window, and convert to complex */
341 if (d_window.size())
342 {
343 for (i = 0; i < size; i++)
344 dst[i] = d_cbuf[i] * d_window[i];
345 }
346 else
347 {
348 for (i = 0; i < size; i++)
349 dst[i] = d_cbuf[i];
350 }
351
352 /* compute FFT */
353 d_fft->execute();
354 }
355
356
357 /*! \brief Set new FFT size. */
set_fft_size(unsigned int fftsize)358 void rx_fft_f::set_fft_size(unsigned int fftsize)
359 {
360 if (fftsize != d_fftsize)
361 {
362 std::lock_guard<std::mutex> lock(d_mutex);
363
364 d_fftsize = fftsize;
365
366 /* clear and resize circular buffer */
367 d_cbuf.clear();
368 d_cbuf.set_capacity(d_fftsize);
369
370 /* reset window */
371 int wintype = d_wintype; // FIXME: would be nicer with a window_reset()
372 d_wintype = -1;
373 set_window_type(wintype);
374
375 /* reset FFT object (also reset FFTW plan) */
376 delete d_fft;
377 #if GNURADIO_VERSION < 0x030900
378 d_fft = new gr::fft::fft_complex(d_fftsize, true);
379 #else
380 d_fft = new gr::fft::fft_complex_fwd(d_fftsize);
381 #endif
382 }
383 }
384
385 /*! \brief Get currently used FFT size. */
get_fft_size() const386 unsigned int rx_fft_f::get_fft_size() const
387 {
388 return d_fftsize;
389 }
390
391 /*! \brief Set new window type. */
set_window_type(int wintype)392 void rx_fft_f::set_window_type(int wintype)
393 {
394 if (wintype == d_wintype)
395 {
396 /* nothing to do */
397 return;
398 }
399
400 d_wintype = wintype;
401
402 if ((d_wintype < gr::fft::window::WIN_HAMMING) || (d_wintype > gr::fft::window::WIN_FLATTOP))
403 {
404 d_wintype = gr::fft::window::WIN_HAMMING;
405 }
406
407 d_window.clear();
408 d_window = gr::fft::window::build((gr::fft::window::win_type)d_wintype, d_fftsize, 6.76);
409 }
410
411 /*! \brief Get currently used window type. */
get_window_type() const412 int rx_fft_f::get_window_type() const
413 {
414 return d_wintype;
415 }
416