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