1 /* -*- c++ -*- */
2 /*
3 * Gqrx SDR: Software defined radio receiver powered by GNU Radio and Qt
4 * https://gqrx.dk/
5 *
6 * Copyright 2011-2014 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 <cmath>
24 #include <iomanip>
25 #include <iostream>
26 #include <sstream>
27 #include <QDebug>
28
29 #include <gnuradio/prefs.h>
30 #include <gnuradio/top_block.h>
31 #include <osmosdr/source.h>
32 #include <osmosdr/ranges.h>
33
34 #include "applications/gqrx/receiver.h"
35 #include "dsp/correct_iq_cc.h"
36 #include "dsp/filter/fir_decim.h"
37 #include "dsp/rx_fft.h"
38 #include "receivers/nbrx.h"
39 #include "receivers/wfmrx.h"
40
41 #ifdef WITH_PULSEAUDIO
42 #include "pulseaudio/pa_sink.h"
43 #elif WITH_PORTAUDIO
44 #include "portaudio/portaudio_sink.h"
45 #else
46 #include <gnuradio/audio/sink.h>
47 #endif
48
49 #define DEFAULT_AUDIO_GAIN -6.0
50 #define TARGET_QUAD_RATE 1e6
51
52 /**
53 * @brief Public constructor.
54 * @param input_device Input device specifier.
55 * @param audio_device Audio output device specifier,
56 * e.g. hw:0 when using ALSA or Portaudio.
57 */
receiver(const std::string input_device,const std::string audio_device,unsigned int decimation)58 receiver::receiver(const std::string input_device,
59 const std::string audio_device,
60 unsigned int decimation)
61 : d_running(false),
62 d_input_rate(96000.0),
63 d_audio_rate(48000),
64 d_decim(decimation),
65 d_rf_freq(144800000.0),
66 d_filter_offset(0.0),
67 d_cw_offset(0.0),
68 d_recording_iq(false),
69 d_recording_wav(false),
70 d_sniffer_active(false),
71 d_iq_rev(false),
72 d_dc_cancel(false),
73 d_iq_balance(false),
74 d_demod(RX_DEMOD_OFF)
75 {
76
77 tb = gr::make_top_block("gqrx");
78
79 if (input_device.empty())
80 {
81 src = osmosdr::source::make("file="+escape_filename(get_zero_file())+",freq=428e6,rate=96000,repeat=true,throttle=true");
82 }
83 else
84 {
85 input_devstr = input_device;
86 src = osmosdr::source::make(input_device);
87 }
88
89 // input decimator
90 if (d_decim >= 2)
91 {
92 try
93 {
94 input_decim = make_fir_decim_cc(d_decim);
95 }
96 catch (std::range_error &e)
97 {
98 std::cout << "Error creating input decimator " << d_decim
99 << ": " << e.what() << std::endl
100 << "Using decimation 1." << std::endl;
101 d_decim = 1;
102 }
103
104 d_decim_rate = d_input_rate / (double)d_decim;
105 }
106 else
107 {
108 d_decim_rate = d_input_rate;
109 }
110
111 d_ddc_decim = std::max(1, (int)(d_decim_rate / TARGET_QUAD_RATE));
112 d_quad_rate = d_decim_rate / d_ddc_decim;
113 ddc = make_downconverter_cc(d_ddc_decim, 0.0, d_decim_rate);
114 rx = make_nbrx(d_quad_rate, d_audio_rate);
115
116 iq_swap = make_iq_swap_cc(false);
117 dc_corr = make_dc_corr_cc(d_decim_rate, 1.0);
118 iq_fft = make_rx_fft_c(8192u, d_decim_rate, gr::fft::window::WIN_HANN);
119
120 audio_fft = make_rx_fft_f(8192u, d_audio_rate, gr::fft::window::WIN_HANN);
121 audio_gain0 = gr::blocks::multiply_const_ff::make(0);
122 audio_gain1 = gr::blocks::multiply_const_ff::make(0);
123 set_af_gain(DEFAULT_AUDIO_GAIN);
124
125 audio_udp_sink = make_udp_sink_f();
126
127 #ifdef WITH_PULSEAUDIO
128 audio_snk = make_pa_sink(audio_device, d_audio_rate, "GQRX", "Audio output");
129 #elif WITH_PORTAUDIO
130 audio_snk = make_portaudio_sink(audio_device, d_audio_rate, "GQRX", "Audio output");
131 #else
132 audio_snk = gr::audio::sink::make(d_audio_rate, audio_device, true);
133 #endif
134
135 output_devstr = audio_device;
136
137 /* wav sink and source is created when rec/play is started */
138 audio_null_sink0 = gr::blocks::null_sink::make(sizeof(float));
139 audio_null_sink1 = gr::blocks::null_sink::make(sizeof(float));
140 sniffer = make_sniffer_f();
141 /* sniffer_rr is created at each activation. */
142
143 set_demod(RX_DEMOD_NFM);
144
145 gr::prefs pref;
146 qDebug() << "Using audio backend:"
147 << pref.get_string("audio", "audio_module", "N/A").c_str();
148 }
149
~receiver()150 receiver::~receiver()
151 {
152 tb->stop();
153 }
154
155
156 /** Start the receiver. */
start()157 void receiver::start()
158 {
159 if (!d_running)
160 {
161 tb->start();
162 d_running = true;
163 }
164 }
165
166 /** Stop the receiver. */
stop()167 void receiver::stop()
168 {
169 if (d_running)
170 {
171 tb->stop();
172 tb->wait(); // If the graph is needed to run again, wait() must be called after stop
173 d_running = false;
174 }
175 }
176
177 /**
178 * @brief Select new input device.
179 * @param device
180 */
set_input_device(const std::string device)181 void receiver::set_input_device(const std::string device)
182 {
183 qDebug() << "Set input device:";
184 qDebug() << " old:" << input_devstr.c_str();
185 qDebug() << " new:" << device.c_str();
186
187 std::string error = "";
188
189 if (device.empty())
190 return;
191
192 input_devstr = device;
193
194 // tb->lock() can hang occasionally
195 if (d_running)
196 {
197 tb->stop();
198 tb->wait();
199 }
200
201 if (d_decim >= 2)
202 {
203 tb->disconnect(src, 0, input_decim, 0);
204 tb->disconnect(input_decim, 0, iq_swap, 0);
205 }
206 else
207 {
208 tb->disconnect(src, 0, iq_swap, 0);
209 }
210
211 src.reset();
212
213 try
214 {
215 src = osmosdr::source::make(device);
216 }
217 catch (std::exception &x)
218 {
219 error = x.what();
220 src = osmosdr::source::make("file="+escape_filename(get_zero_file())+",freq=428e6,rate=96000,repeat=true,throttle=true");
221 }
222
223 if(src->get_sample_rate() != 0)
224 set_input_rate(src->get_sample_rate());
225
226 if (d_decim >= 2)
227 {
228 tb->connect(src, 0, input_decim, 0);
229 tb->connect(input_decim, 0, iq_swap, 0);
230 }
231 else
232 {
233 tb->connect(src, 0, iq_swap, 0);
234 }
235
236 if (d_running)
237 tb->start();
238
239 if (error != "")
240 {
241 throw std::runtime_error(error);
242 }
243 }
244
245
246 /**
247 * @brief Select new audio output device.
248 * @param device
249 */
set_output_device(const std::string device)250 void receiver::set_output_device(const std::string device)
251 {
252 qDebug() << "Set output device:";
253 qDebug() << " old:" << output_devstr.c_str();
254 qDebug() << " new:" << device.c_str();
255
256 output_devstr = device;
257
258 tb->lock();
259
260 if (d_demod != RX_DEMOD_OFF)
261 {
262 tb->disconnect(audio_gain0, 0, audio_snk, 0);
263 tb->disconnect(audio_gain1, 0, audio_snk, 1);
264 }
265 audio_snk.reset();
266
267 try {
268 #ifdef WITH_PULSEAUDIO
269 audio_snk = make_pa_sink(device, d_audio_rate, "GQRX", "Audio output");
270 #elif WITH_PORTAUDIO
271 audio_snk = make_portaudio_sink(device, d_audio_rate, "GQRX", "Audio output");
272 #else
273 audio_snk = gr::audio::sink::make(d_audio_rate, device, true);
274 #endif
275
276 if (d_demod != RX_DEMOD_OFF)
277 {
278 tb->connect(audio_gain0, 0, audio_snk, 0);
279 tb->connect(audio_gain1, 0, audio_snk, 1);
280 }
281
282 tb->unlock();
283
284 } catch (std::exception &x) {
285 tb->unlock();
286 // handle problems on non-freeing devices
287 throw x;
288 }
289 }
290
291 /** Get a list of available antenna connectors. */
get_antennas(void) const292 std::vector<std::string> receiver::get_antennas(void) const
293 {
294 return src->get_antennas();
295 }
296
297 /** Select antenna connector. */
set_antenna(const std::string & antenna)298 void receiver::set_antenna(const std::string &antenna)
299 {
300 if (!antenna.empty())
301 {
302 src->set_antenna(antenna);
303 }
304 }
305
306 /**
307 * @brief Set new input sample rate.
308 * @param rate The desired input rate
309 * @return The actual sample rate set or 0 if there was an error with the
310 * device.
311 */
set_input_rate(double rate)312 double receiver::set_input_rate(double rate)
313 {
314 double current_rate;
315 bool rate_has_changed;
316
317 current_rate = src->get_sample_rate();
318 rate_has_changed = !(rate == current_rate ||
319 std::abs(rate - current_rate) < std::abs(std::min(rate, current_rate))
320 * std::numeric_limits<double>::epsilon());
321
322 tb->lock();
323 try
324 {
325 d_input_rate = src->set_sample_rate(rate);
326 }
327 catch (std::runtime_error &e)
328 {
329 d_input_rate = 0;
330 }
331
332 if (d_input_rate == 0)
333 {
334 // This can be the case when no device is attached and gr-osmosdr
335 // puts in a null_source with rate 100 ksps or if the rate has not
336 // changed
337 if (rate_has_changed)
338 {
339 std::cerr << std::endl;
340 std::cerr << "Failed to set RX input rate to " << rate << std::endl;
341 std::cerr << "Your device may not be working properly." << std::endl;
342 std::cerr << std::endl;
343 }
344 d_input_rate = rate;
345 }
346
347 d_decim_rate = d_input_rate / (double)d_decim;
348 d_ddc_decim = std::max(1, (int)(d_decim_rate / TARGET_QUAD_RATE));
349 d_quad_rate = d_decim_rate / d_ddc_decim;
350 dc_corr->set_sample_rate(d_decim_rate);
351 ddc->set_decim_and_samp_rate(d_ddc_decim, d_decim_rate);
352 rx->set_quad_rate(d_quad_rate);
353 iq_fft->set_quad_rate(d_decim_rate);
354 tb->unlock();
355
356 return d_input_rate;
357 }
358
359 /** Set input decimation */
set_input_decim(unsigned int decim)360 unsigned int receiver::set_input_decim(unsigned int decim)
361 {
362 if (decim == d_decim)
363 return d_decim;
364
365 if (d_running)
366 {
367 tb->stop();
368 tb->wait();
369 }
370
371 if (d_decim >= 2)
372 {
373 tb->disconnect(src, 0, input_decim, 0);
374 tb->disconnect(input_decim, 0, iq_swap, 0);
375 }
376 else
377 {
378 tb->disconnect(src, 0, iq_swap, 0);
379 }
380
381 input_decim.reset();
382 d_decim = decim;
383 if (d_decim >= 2)
384 {
385 try
386 {
387 input_decim = make_fir_decim_cc(d_decim);
388 }
389 catch (std::range_error &e)
390 {
391 std::cout << "Error opening creating input decimator " << d_decim
392 << ": " << e.what() << std::endl
393 << "Using decimation 1." << std::endl;
394 d_decim = 1;
395 }
396
397 d_decim_rate = d_input_rate / (double)d_decim;
398 }
399 else
400 {
401 d_decim_rate = d_input_rate;
402 }
403
404 // update quadrature rate
405 d_ddc_decim = std::max(1, (int)(d_decim_rate / TARGET_QUAD_RATE));
406 d_quad_rate = d_decim_rate / d_ddc_decim;
407 dc_corr->set_sample_rate(d_decim_rate);
408 ddc->set_decim_and_samp_rate(d_ddc_decim, d_decim_rate);
409 rx->set_quad_rate(d_quad_rate);
410 iq_fft->set_quad_rate(d_decim_rate);
411
412 if (d_decim >= 2)
413 {
414 tb->connect(src, 0, input_decim, 0);
415 tb->connect(input_decim, 0, iq_swap, 0);
416 }
417 else
418 {
419 tb->connect(src, 0, iq_swap, 0);
420 }
421
422 #ifdef CUSTOM_AIRSPY_KERNELS
423 if (input_devstr.find("airspy") != std::string::npos)
424 src->set_bandwidth(d_decim_rate);
425 #endif
426
427 if (d_running)
428 tb->start();
429
430 return d_decim;
431 }
432
433 /**
434 * @brief Set new analog bandwidth.
435 * @param bw The new bandwidth.
436 * @return The actual bandwidth.
437 */
set_analog_bandwidth(double bw)438 double receiver::set_analog_bandwidth(double bw)
439 {
440 return src->set_bandwidth(bw);
441 }
442
443 /** Get current analog bandwidth. */
get_analog_bandwidth(void) const444 double receiver::get_analog_bandwidth(void) const
445 {
446 return src->get_bandwidth();
447 }
448
449 /** Set I/Q reversed. */
set_iq_swap(bool reversed)450 void receiver::set_iq_swap(bool reversed)
451 {
452 if (reversed == d_iq_rev)
453 return;
454
455 d_iq_rev = reversed;
456 iq_swap->set_enabled(d_iq_rev);
457 }
458
459 /**
460 * @brief Get current I/Q reversed setting.
461 * @retval true I/Q swappign is enabled.
462 * @retval false I/Q swapping is disabled.
463 */
get_iq_swap(void) const464 bool receiver::get_iq_swap(void) const
465 {
466 return d_iq_rev;
467 }
468
469 /**
470 * @brief Enable/disable automatic DC removal in the I/Q stream.
471 * @param enable Whether DC removal should enabled or not.
472 */
set_dc_cancel(bool enable)473 void receiver::set_dc_cancel(bool enable)
474 {
475 if (enable == d_dc_cancel)
476 return;
477
478 d_dc_cancel = enable;
479
480 // until we have a way to switch on/off
481 // inside the dc_corr_cc we do a reconf
482 set_demod(d_demod, true);
483 }
484
485 /**
486 * @brief Get auto DC cancel status.
487 * @retval true Automatic DC removal is enabled.
488 * @retval false Automatic DC removal is disabled.
489 */
get_dc_cancel(void) const490 bool receiver::get_dc_cancel(void) const
491 {
492 return d_dc_cancel;
493 }
494
495 /**
496 * @brief Enable/disable automatic I/Q balance.
497 * @param enable Whether automatic I/Q balance should be enabled.
498 */
set_iq_balance(bool enable)499 void receiver::set_iq_balance(bool enable)
500 {
501 if (enable == d_iq_balance)
502 return;
503
504 d_iq_balance = enable;
505
506 src->set_iq_balance_mode(enable ? 2 : 0);
507 }
508
509 /**
510 * @brief Get auto I/Q balance status.
511 * @retval true Automatic I/Q balance is enabled.
512 * @retval false Automatic I/Q balance is disabled.
513 */
get_iq_balance(void) const514 bool receiver::get_iq_balance(void) const
515 {
516 return d_iq_balance;
517 }
518
519 /**
520 * @brief Set RF frequency.
521 * @param freq_hz The desired frequency in Hz.
522 * @return RX_STATUS_ERROR if an error occurs, e.g. the frequency is out of range.
523 * @sa get_rf_freq()
524 */
set_rf_freq(double freq_hz)525 receiver::status receiver::set_rf_freq(double freq_hz)
526 {
527 d_rf_freq = freq_hz;
528
529 src->set_center_freq(d_rf_freq);
530 // FIXME: read back frequency?
531
532 return STATUS_OK;
533 }
534
535 /**
536 * @brief Get RF frequency.
537 * @return The current RF frequency.
538 * @sa set_rf_freq()
539 */
get_rf_freq(void)540 double receiver::get_rf_freq(void)
541 {
542 d_rf_freq = src->get_center_freq();
543
544 return d_rf_freq;
545 }
546
547 /**
548 * @brief Get the RF frequency range of the current input device.
549 * @param start The lower limit of the range in Hz.
550 * @param stop The upper limit of the range in Hz.
551 * @param step The frequency step in Hz.
552 * @returns STATUS_OK if the range could be retrieved, STATUS_ERROR if an error has occurred.
553 */
get_rf_range(double * start,double * stop,double * step)554 receiver::status receiver::get_rf_range(double *start, double *stop, double *step)
555 {
556 osmosdr::freq_range_t range;
557
558 range = src->get_freq_range();
559
560 // currently range is empty for all but E4000
561 if (!range.empty())
562 {
563 if (range.start() < range.stop())
564 {
565 *start = range.start();
566 *stop = range.stop();
567 *step = range.step(); /** FIXME: got 0 for rtl-sdr? **/
568
569 return STATUS_OK;
570 }
571 }
572
573 return STATUS_ERROR;
574 }
575
576 /** Get the names of available gain stages. */
get_gain_names()577 std::vector<std::string> receiver::get_gain_names()
578 {
579 return src->get_gain_names();
580 }
581
582 /**
583 * @brief Get gain range for a specific stage.
584 * @param[in] name The name of the gain stage.
585 * @param[out] start Lower limit for this gain setting.
586 * @param[out] stop Upper limit for this gain setting.
587 * @param[out] step The resolution for this gain setting.
588 *
589 * This function returns the range for the requested gain stage.
590 */
get_gain_range(std::string & name,double * start,double * stop,double * step) const591 receiver::status receiver::get_gain_range(std::string &name, double *start,
592 double *stop, double *step) const
593 {
594 osmosdr::gain_range_t range;
595
596 range = src->get_gain_range(name);
597 *start = range.start();
598 *stop = range.stop();
599 *step = range.step();
600
601 return STATUS_OK;
602 }
603
set_gain(std::string name,double value)604 receiver::status receiver::set_gain(std::string name, double value)
605 {
606 src->set_gain(value, name);
607
608 return STATUS_OK;
609 }
610
get_gain(std::string name) const611 double receiver::get_gain(std::string name) const
612 {
613 return src->get_gain(name);
614 }
615
616 /**
617 * @brief Set RF gain.
618 * @param gain_rel The desired relative gain between 0.0 and 1.0 (use -1 for
619 * AGC where supported).
620 * @return RX_STATUS_ERROR if an error occurs, e.g. the gain is out of valid range.
621 */
set_auto_gain(bool automatic)622 receiver::status receiver::set_auto_gain(bool automatic)
623 {
624 src->set_gain_mode(automatic);
625
626 return STATUS_OK;
627 }
628
629 /**
630 * @brief Set filter offset.
631 * @param offset_hz The desired filter offset in Hz.
632 * @return RX_STATUS_ERROR if the tuning offset is out of range.
633 *
634 * This method sets a new tuning offset for the receiver. The tuning offset is used
635 * to tune within the passband, i.e. select a specific channel within the received
636 * spectrum.
637 *
638 * The valid range for the tuning is +/- 0.5 * the bandwidth although this is just a
639 * logical limit.
640 *
641 * @sa get_filter_offset()
642 */
set_filter_offset(double offset_hz)643 receiver::status receiver::set_filter_offset(double offset_hz)
644 {
645 d_filter_offset = offset_hz;
646 ddc->set_center_freq(d_filter_offset - d_cw_offset);
647
648 return STATUS_OK;
649 }
650
651 /**
652 * @brief Get filter offset.
653 * @return The current filter offset.
654 * @sa set_filter_offset()
655 */
get_filter_offset(void) const656 double receiver::get_filter_offset(void) const
657 {
658 return d_filter_offset;
659 }
660
661 /* CW offset can serve as a "BFO" if the GUI needs it */
set_cw_offset(double offset_hz)662 receiver::status receiver::set_cw_offset(double offset_hz)
663 {
664 d_cw_offset = offset_hz;
665 ddc->set_center_freq(d_filter_offset - d_cw_offset);
666 rx->set_cw_offset(d_cw_offset);
667
668 return STATUS_OK;
669 }
670
get_cw_offset(void) const671 double receiver::get_cw_offset(void) const
672 {
673 return d_cw_offset;
674 }
675
set_filter(double low,double high,filter_shape shape)676 receiver::status receiver::set_filter(double low, double high, filter_shape shape)
677 {
678 double trans_width;
679
680 if ((low >= high) || (std::abs(high-low) < RX_FILTER_MIN_WIDTH))
681 return STATUS_ERROR;
682
683 switch (shape) {
684
685 case FILTER_SHAPE_SOFT:
686 trans_width = std::abs(high - low) * 0.5;
687 break;
688
689 case FILTER_SHAPE_SHARP:
690 trans_width = std::abs(high - low) * 0.1;
691 break;
692
693 case FILTER_SHAPE_NORMAL:
694 default:
695 trans_width = std::abs(high - low) * 0.2;
696 break;
697
698 }
699
700 rx->set_filter(low, high, trans_width);
701
702 return STATUS_OK;
703 }
704
set_freq_corr(double ppm)705 receiver::status receiver::set_freq_corr(double ppm)
706 {
707 src->set_freq_corr(ppm);
708
709 return STATUS_OK;
710 }
711
712 /**
713 * @brief Get current signal power.
714 * @param dbfs Whether to use dbfs or absolute power.
715 * @return The current signal power.
716 *
717 * This method returns the current signal power detected by the receiver. The detector
718 * is located after the band pass filter. The full scale is 1.0
719 */
get_signal_pwr() const720 float receiver::get_signal_pwr() const
721 {
722 return rx->get_signal_level();
723 }
724
725 /** Set new FFT size. */
set_iq_fft_size(int newsize)726 void receiver::set_iq_fft_size(int newsize)
727 {
728 iq_fft->set_fft_size(newsize);
729 }
730
set_iq_fft_window(int window_type)731 void receiver::set_iq_fft_window(int window_type)
732 {
733 iq_fft->set_window_type(window_type);
734 }
735
736 /** Get latest baseband FFT data. */
get_iq_fft_data(std::complex<float> * fftPoints,unsigned int & fftsize)737 void receiver::get_iq_fft_data(std::complex<float>* fftPoints, unsigned int &fftsize)
738 {
739 iq_fft->get_fft_data(fftPoints, fftsize);
740 }
741
742 /** Get latest audio FFT data. */
get_audio_fft_data(std::complex<float> * fftPoints,unsigned int & fftsize)743 void receiver::get_audio_fft_data(std::complex<float>* fftPoints, unsigned int &fftsize)
744 {
745 audio_fft->get_fft_data(fftPoints, fftsize);
746 }
747
set_nb_on(int nbid,bool on)748 receiver::status receiver::set_nb_on(int nbid, bool on)
749 {
750 if (rx->has_nb())
751 rx->set_nb_on(nbid, on);
752
753 return STATUS_OK; // FIXME
754 }
755
set_nb_threshold(int nbid,float threshold)756 receiver::status receiver::set_nb_threshold(int nbid, float threshold)
757 {
758 if (rx->has_nb())
759 rx->set_nb_threshold(nbid, threshold);
760
761 return STATUS_OK; // FIXME
762 }
763
764 /**
765 * @brief Set squelch level.
766 * @param level_db The new level in dBFS.
767 */
set_sql_level(double level_db)768 receiver::status receiver::set_sql_level(double level_db)
769 {
770 if (rx->has_sql())
771 rx->set_sql_level(level_db);
772
773 return STATUS_OK; // FIXME
774 }
775
776 /** Set squelch alpha */
set_sql_alpha(double alpha)777 receiver::status receiver::set_sql_alpha(double alpha)
778 {
779 if (rx->has_sql())
780 rx->set_sql_alpha(alpha);
781
782 return STATUS_OK; // FIXME
783 }
784
785 /**
786 * @brief Enable/disable receiver AGC.
787 *
788 * When AGC is disabled a fixed manual gain is used, see set_agc_manual_gain().
789 */
set_agc_on(bool agc_on)790 receiver::status receiver::set_agc_on(bool agc_on)
791 {
792 if (rx->has_agc())
793 rx->set_agc_on(agc_on);
794
795 return STATUS_OK; // FIXME
796 }
797
798 /** Enable/disable AGC hang. */
set_agc_hang(bool use_hang)799 receiver::status receiver::set_agc_hang(bool use_hang)
800 {
801 if (rx->has_agc())
802 rx->set_agc_hang(use_hang);
803
804 return STATUS_OK; // FIXME
805 }
806
807 /** Set AGC threshold. */
set_agc_threshold(int threshold)808 receiver::status receiver::set_agc_threshold(int threshold)
809 {
810 if (rx->has_agc())
811 rx->set_agc_threshold(threshold);
812
813 return STATUS_OK; // FIXME
814 }
815
816 /** Set AGC slope. */
set_agc_slope(int slope)817 receiver::status receiver::set_agc_slope(int slope)
818 {
819 if (rx->has_agc())
820 rx->set_agc_slope(slope);
821
822 return STATUS_OK; // FIXME
823 }
824
825 /** Set AGC decay time. */
set_agc_decay(int decay_ms)826 receiver::status receiver::set_agc_decay(int decay_ms)
827 {
828 if (rx->has_agc())
829 rx->set_agc_decay(decay_ms);
830
831 return STATUS_OK; // FIXME
832 }
833
834 /** Set fixed gain used when AGC is OFF. */
set_agc_manual_gain(int gain)835 receiver::status receiver::set_agc_manual_gain(int gain)
836 {
837 if (rx->has_agc())
838 rx->set_agc_manual_gain(gain);
839
840 return STATUS_OK; // FIXME
841 }
842
set_demod(rx_demod demod,bool force)843 receiver::status receiver::set_demod(rx_demod demod, bool force)
844 {
845 status ret = STATUS_OK;
846
847 if (!force && (demod == d_demod))
848 return ret;
849
850 // tb->lock() seems to hang occasioanlly
851 if (d_running)
852 {
853 tb->stop();
854 tb->wait();
855 }
856
857 tb->disconnect_all();
858
859 switch (demod)
860 {
861 case RX_DEMOD_OFF:
862 connect_all(RX_CHAIN_NONE);
863 break;
864
865 case RX_DEMOD_NONE:
866 connect_all(RX_CHAIN_NBRX);
867 rx->set_demod(nbrx::NBRX_DEMOD_NONE);
868 break;
869
870 case RX_DEMOD_AM:
871 connect_all(RX_CHAIN_NBRX);
872 rx->set_demod(nbrx::NBRX_DEMOD_AM);
873 break;
874
875 case RX_DEMOD_AMSYNC:
876 connect_all(RX_CHAIN_NBRX);
877 rx->set_demod(nbrx::NBRX_DEMOD_AMSYNC);
878 break;
879
880 case RX_DEMOD_NFM:
881 connect_all(RX_CHAIN_NBRX);
882 rx->set_demod(nbrx::NBRX_DEMOD_FM);
883 break;
884
885 case RX_DEMOD_WFM_M:
886 connect_all(RX_CHAIN_WFMRX);
887 rx->set_demod(wfmrx::WFMRX_DEMOD_MONO);
888 break;
889
890 case RX_DEMOD_WFM_S:
891 connect_all(RX_CHAIN_WFMRX);
892 rx->set_demod(wfmrx::WFMRX_DEMOD_STEREO);
893 break;
894
895 case RX_DEMOD_WFM_S_OIRT:
896 connect_all(RX_CHAIN_WFMRX);
897 rx->set_demod(wfmrx::WFMRX_DEMOD_STEREO_UKW);
898 break;
899
900 case RX_DEMOD_SSB:
901 connect_all(RX_CHAIN_NBRX);
902 rx->set_demod(nbrx::NBRX_DEMOD_SSB);
903 break;
904
905 default:
906 ret = STATUS_ERROR;
907 break;
908 }
909
910 d_demod = demod;
911
912 if (d_running)
913 tb->start();
914
915 return ret;
916 }
917
918 /**
919 * @brief Set maximum deviation of the FM demodulator.
920 * @param maxdev_hz The new maximum deviation in Hz.
921 */
set_fm_maxdev(float maxdev_hz)922 receiver::status receiver::set_fm_maxdev(float maxdev_hz)
923 {
924 if (rx->has_fm())
925 rx->set_fm_maxdev(maxdev_hz);
926
927 return STATUS_OK;
928 }
929
set_fm_deemph(double tau)930 receiver::status receiver::set_fm_deemph(double tau)
931 {
932 if (rx->has_fm())
933 rx->set_fm_deemph(tau);
934
935 return STATUS_OK;
936 }
937
set_am_dcr(bool enabled)938 receiver::status receiver::set_am_dcr(bool enabled)
939 {
940 if (rx->has_am())
941 rx->set_am_dcr(enabled);
942
943 return STATUS_OK;
944 }
945
set_amsync_dcr(bool enabled)946 receiver::status receiver::set_amsync_dcr(bool enabled)
947 {
948 if (rx->has_amsync())
949 rx->set_amsync_dcr(enabled);
950
951 return STATUS_OK;
952 }
953
set_amsync_pll_bw(float pll_bw)954 receiver::status receiver::set_amsync_pll_bw(float pll_bw)
955 {
956 if (rx->has_amsync())
957 rx->set_amsync_pll_bw(pll_bw);
958
959 return STATUS_OK;
960 }
961
set_af_gain(float gain_db)962 receiver::status receiver::set_af_gain(float gain_db)
963 {
964 float k;
965
966 /* convert dB to factor */
967 k = pow(10.0, gain_db / 20.0);
968 //std::cout << "G:" << gain_db << "dB / K:" << k << std::endl;
969 audio_gain0->set_k(k);
970 audio_gain1->set_k(k);
971
972 return STATUS_OK;
973 }
974
975
976 /**
977 * @brief Start WAV file recorder.
978 * @param filename The filename where to record.
979 *
980 * A new recorder object is created every time we start recording and deleted every time
981 * we stop recording. The idea of creating one object and starting/stopping using different
982 * file names does not work with WAV files (the initial /tmp/gqrx.wav will not be stopped
983 * because the wav file can not be empty). See https://github.com/gqrx-sdr/gqrx/issues/36
984 */
start_audio_recording(const std::string filename)985 receiver::status receiver::start_audio_recording(const std::string filename)
986 {
987 if (d_recording_wav)
988 {
989 /* error - we are already recording */
990 std::cout << "ERROR: Can not start audio recorder (already recording)" << std::endl;
991
992 return STATUS_ERROR;
993 }
994 if (!d_running)
995 {
996 /* receiver is not running */
997 std::cout << "Can not start audio recorder (receiver not running)" << std::endl;
998
999 return STATUS_ERROR;
1000 }
1001
1002 // if this fails, we don't want to go and crash now, do we
1003 try {
1004 #if GNURADIO_VERSION < 0x030900
1005 wav_sink = gr::blocks::wavfile_sink::make(filename.c_str(), 2,
1006 (unsigned int) d_audio_rate,
1007 16);
1008 #else
1009 wav_sink = gr::blocks::wavfile_sink::make(filename.c_str(), 2,
1010 (unsigned int) d_audio_rate,
1011 gr::blocks::FORMAT_WAV, gr::blocks::FORMAT_PCM_16);
1012 #endif
1013 }
1014 catch (std::runtime_error &e) {
1015 std::cout << "Error opening " << filename << ": " << e.what() << std::endl;
1016 return STATUS_ERROR;
1017 }
1018
1019 tb->lock();
1020 tb->connect(rx, 0, wav_sink, 0);
1021 tb->connect(rx, 1, wav_sink, 1);
1022 tb->unlock();
1023 d_recording_wav = true;
1024
1025 std::cout << "Recording audio to " << filename << std::endl;
1026
1027 return STATUS_OK;
1028 }
1029
1030 /** Stop WAV file recorder. */
stop_audio_recording()1031 receiver::status receiver::stop_audio_recording()
1032 {
1033 if (!d_recording_wav) {
1034 /* error: we are not recording */
1035 std::cout << "ERROR: Can not stop audio recorder (not recording)" << std::endl;
1036
1037 return STATUS_ERROR;
1038 }
1039 if (!d_running)
1040 {
1041 /* receiver is not running */
1042 std::cout << "Can not stop audio recorder (receiver not running)" << std::endl;
1043
1044 return STATUS_ERROR;
1045 }
1046
1047 // not strictly necessary to lock but I think it is safer
1048 tb->lock();
1049 wav_sink->close();
1050 tb->disconnect(rx, 0, wav_sink, 0);
1051 tb->disconnect(rx, 1, wav_sink, 1);
1052 tb->unlock();
1053 wav_sink.reset();
1054 d_recording_wav = false;
1055
1056 std::cout << "Audio recorder stopped" << std::endl;
1057
1058 return STATUS_OK;
1059 }
1060
1061 /** Start audio playback. */
start_audio_playback(const std::string filename)1062 receiver::status receiver::start_audio_playback(const std::string filename)
1063 {
1064 if (!d_running)
1065 {
1066 /* receiver is not running */
1067 std::cout << "Can not start audio playback (receiver not running)" << std::endl;
1068
1069 return STATUS_ERROR;
1070 }
1071
1072 try {
1073 // output ports set automatically from file
1074 wav_src = gr::blocks::wavfile_source::make(filename.c_str(), false);
1075 }
1076 catch (std::runtime_error &e) {
1077 std::cout << "Error loading " << filename << ": " << e.what() << std::endl;
1078 return STATUS_ERROR;
1079 }
1080
1081 /** FIXME: We can only handle native rate (should maybe use the audio_rr)? */
1082 unsigned int audio_rate = (unsigned int) d_audio_rate;
1083 if (wav_src->sample_rate() != audio_rate)
1084 {
1085 std::cout << "BUG: Can not handle sample rate " << wav_src->sample_rate() << std::endl;
1086 wav_src.reset();
1087
1088 return STATUS_ERROR;
1089 }
1090
1091 /** FIXME: We can only handle stereo files */
1092 if (wav_src->channels() != 2)
1093 {
1094 std::cout << "BUG: Can not handle other than 2 channels. File has " << wav_src->channels() << std::endl;
1095 wav_src.reset();
1096
1097 return STATUS_ERROR;
1098 }
1099
1100 stop();
1101 /* route demodulator output to null sink */
1102 tb->disconnect(rx, 0, audio_gain0, 0);
1103 tb->disconnect(rx, 1, audio_gain1, 0);
1104 tb->disconnect(rx, 0, audio_fft, 0);
1105 tb->disconnect(rx, 0, audio_udp_sink, 0);
1106 tb->disconnect(rx, 1, audio_udp_sink, 1);
1107 tb->connect(rx, 0, audio_null_sink0, 0); /** FIXME: other channel? */
1108 tb->connect(rx, 1, audio_null_sink1, 0); /** FIXME: other channel? */
1109 tb->connect(wav_src, 0, audio_gain0, 0);
1110 tb->connect(wav_src, 1, audio_gain1, 0);
1111 tb->connect(wav_src, 0, audio_fft, 0);
1112 tb->connect(wav_src, 0, audio_udp_sink, 0);
1113 tb->connect(wav_src, 1, audio_udp_sink, 1);
1114 start();
1115
1116 std::cout << "Playing audio from " << filename << std::endl;
1117
1118 return STATUS_OK;
1119 }
1120
1121 /** Stop audio playback. */
stop_audio_playback()1122 receiver::status receiver::stop_audio_playback()
1123 {
1124 /* disconnect wav source and reconnect receiver */
1125 stop();
1126 tb->disconnect(wav_src, 0, audio_gain0, 0);
1127 tb->disconnect(wav_src, 1, audio_gain1, 0);
1128 tb->disconnect(wav_src, 0, audio_fft, 0);
1129 tb->disconnect(wav_src, 0, audio_udp_sink, 0);
1130 tb->disconnect(wav_src, 1, audio_udp_sink, 1);
1131 tb->disconnect(rx, 0, audio_null_sink0, 0);
1132 tb->disconnect(rx, 1, audio_null_sink1, 0);
1133 tb->connect(rx, 0, audio_gain0, 0);
1134 tb->connect(rx, 1, audio_gain1, 0);
1135 tb->connect(rx, 0, audio_fft, 0); /** FIXME: other channel? */
1136 tb->connect(rx, 0, audio_udp_sink, 0);
1137 tb->connect(rx, 1, audio_udp_sink, 1);
1138 start();
1139
1140 /* delete wav_src since we can not change file name */
1141 wav_src.reset();
1142
1143 return STATUS_OK;
1144 }
1145
1146 /** Start UDP streaming of audio. */
start_udp_streaming(const std::string host,int port,bool stereo)1147 receiver::status receiver::start_udp_streaming(const std::string host, int port, bool stereo)
1148 {
1149 audio_udp_sink->start_streaming(host, port, stereo);
1150 return STATUS_OK;
1151 }
1152
1153 /** Stop UDP streaming of audio. */
stop_udp_streaming()1154 receiver::status receiver::stop_udp_streaming()
1155 {
1156 audio_udp_sink->stop_streaming();
1157 return STATUS_OK;
1158 }
1159
1160 /**
1161 * @brief Start I/Q data recorder.
1162 * @param filename The filename where to record.
1163 */
start_iq_recording(const std::string filename)1164 receiver::status receiver::start_iq_recording(const std::string filename)
1165 {
1166 receiver::status status = STATUS_OK;
1167
1168 if (d_recording_iq) {
1169 std::cout << __func__ << ": already recording" << std::endl;
1170 return STATUS_ERROR;
1171 }
1172
1173 try
1174 {
1175 iq_sink = gr::blocks::file_sink::make(sizeof(gr_complex), filename.c_str(), true);
1176 }
1177 catch (std::runtime_error &e)
1178 {
1179 std::cout << __func__ << ": couldn't open I/Q file" << std::endl;
1180 return STATUS_ERROR;
1181 }
1182
1183 tb->lock();
1184 if (d_decim >= 2)
1185 tb->connect(input_decim, 0, iq_sink, 0);
1186 else
1187 tb->connect(src, 0, iq_sink, 0);
1188 d_recording_iq = true;
1189 tb->unlock();
1190
1191 return status;
1192 }
1193
1194 /** Stop I/Q data recorder. */
stop_iq_recording()1195 receiver::status receiver::stop_iq_recording()
1196 {
1197 if (!d_recording_iq) {
1198 /* error: we are not recording */
1199 return STATUS_ERROR;
1200 }
1201
1202 tb->lock();
1203 iq_sink->close();
1204
1205 if (d_decim >= 2)
1206 tb->disconnect(input_decim, 0, iq_sink, 0);
1207 else
1208 tb->disconnect(src, 0, iq_sink, 0);
1209
1210 tb->unlock();
1211 iq_sink.reset();
1212 d_recording_iq = false;
1213
1214 return STATUS_OK;
1215 }
1216
1217 /**
1218 * @brief Seek to position in IQ file source.
1219 * @param pos Byte offset from the beginning of the file.
1220 */
seek_iq_file(long pos)1221 receiver::status receiver::seek_iq_file(long pos)
1222 {
1223 receiver::status status = STATUS_OK;
1224
1225 tb->lock();
1226
1227 if (src->seek(pos, SEEK_SET))
1228 {
1229 status = STATUS_OK;
1230 }
1231 else
1232 {
1233 status = STATUS_ERROR;
1234 }
1235
1236 tb->unlock();
1237
1238 return status;
1239 }
1240
1241 /**
1242 * @brief Start data sniffer.
1243 * @param buffsize The buffer that should be used in the sniffer.
1244 * @return STATUS_OK if the sniffer was started, STATUS_ERROR if the sniffer is already in use.
1245 */
start_sniffer(unsigned int samprate,int buffsize)1246 receiver::status receiver::start_sniffer(unsigned int samprate, int buffsize)
1247 {
1248 if (d_sniffer_active) {
1249 /* sniffer already in use */
1250 return STATUS_ERROR;
1251 }
1252
1253 sniffer->set_buffer_size(buffsize);
1254 sniffer_rr = make_resampler_ff((float)samprate/(float)d_audio_rate);
1255 tb->lock();
1256 tb->connect(rx, 0, sniffer_rr, 0);
1257 tb->connect(sniffer_rr, 0, sniffer, 0);
1258 tb->unlock();
1259 d_sniffer_active = true;
1260
1261 return STATUS_OK;
1262 }
1263
1264 /**
1265 * @brief Stop data sniffer.
1266 * @return STATUS_ERROR i the sniffer is not currently active.
1267 */
stop_sniffer()1268 receiver::status receiver::stop_sniffer()
1269 {
1270 if (!d_sniffer_active) {
1271 return STATUS_ERROR;
1272 }
1273
1274 tb->lock();
1275 tb->disconnect(rx, 0, sniffer_rr, 0);
1276 tb->disconnect(sniffer_rr, 0, sniffer, 0);
1277 tb->unlock();
1278 d_sniffer_active = false;
1279
1280 /* delete resampler */
1281 sniffer_rr.reset();
1282
1283 return STATUS_OK;
1284 }
1285
1286 /** Get sniffer data. */
get_sniffer_data(float * outbuff,unsigned int & num)1287 void receiver::get_sniffer_data(float * outbuff, unsigned int &num)
1288 {
1289 sniffer->get_samples(outbuff, num);
1290 }
1291
1292 /** Convenience function to connect all blocks. */
connect_all(rx_chain type)1293 void receiver::connect_all(rx_chain type)
1294 {
1295 gr::basic_block_sptr b;
1296
1297 // Setup source
1298 b = src;
1299
1300 // Pre-processing
1301 if (d_decim >= 2)
1302 {
1303 tb->connect(b, 0, input_decim, 0);
1304 b = input_decim;
1305 }
1306
1307 if (d_recording_iq)
1308 {
1309 // We record IQ with minimal pre-processing
1310 tb->connect(b, 0, iq_sink, 0);
1311 }
1312
1313 tb->connect(b, 0, iq_swap, 0);
1314 b = iq_swap;
1315
1316 if (d_dc_cancel)
1317 {
1318 tb->connect(b, 0, dc_corr, 0);
1319 b = dc_corr;
1320 }
1321
1322 // Visualization
1323 tb->connect(b, 0, iq_fft, 0);
1324
1325 // RX demod chain
1326 switch (type)
1327 {
1328 case RX_CHAIN_NBRX:
1329 if (rx->name() != "NBRX")
1330 {
1331 rx.reset();
1332 rx = make_nbrx(d_quad_rate, d_audio_rate);
1333 }
1334 break;
1335
1336 case RX_CHAIN_WFMRX:
1337 if (rx->name() != "WFMRX")
1338 {
1339 rx.reset();
1340 rx = make_wfmrx(d_quad_rate, d_audio_rate);
1341 }
1342 break;
1343
1344 default:
1345 break;
1346 }
1347
1348 // Audio path (if there is a receiver)
1349 if (type != RX_CHAIN_NONE)
1350 {
1351 tb->connect(b, 0, ddc, 0);
1352 tb->connect(ddc, 0, rx, 0);
1353 tb->connect(rx, 0, audio_fft, 0);
1354 tb->connect(rx, 0, audio_udp_sink, 0);
1355 tb->connect(rx, 1, audio_udp_sink, 1);
1356 tb->connect(rx, 0, audio_gain0, 0);
1357 tb->connect(rx, 1, audio_gain1, 0);
1358 tb->connect(audio_gain0, 0, audio_snk, 0);
1359 tb->connect(audio_gain1, 0, audio_snk, 1);
1360 }
1361
1362 // Recorders and sniffers
1363 if (d_recording_wav)
1364 {
1365 tb->connect(rx, 0, wav_sink, 0);
1366 tb->connect(rx, 1, wav_sink, 1);
1367 }
1368
1369 if (d_sniffer_active)
1370 {
1371 tb->connect(rx, 0, sniffer_rr, 0);
1372 tb->connect(sniffer_rr, 0, sniffer, 0);
1373 }
1374 }
1375
get_rds_data(std::string & outbuff,int & num)1376 void receiver::get_rds_data(std::string &outbuff, int &num)
1377 {
1378 rx->get_rds_data(outbuff, num);
1379 }
1380
start_rds_decoder(void)1381 void receiver::start_rds_decoder(void)
1382 {
1383 if (d_running)
1384 {
1385 stop();
1386 rx->start_rds_decoder();
1387 start();
1388 }
1389 else
1390 {
1391 rx->start_rds_decoder();
1392 }
1393 }
1394
stop_rds_decoder(void)1395 void receiver::stop_rds_decoder(void)
1396 {
1397 if (d_running)
1398 {
1399 stop();
1400 rx->stop_rds_decoder();
1401 start();
1402 }
1403 else
1404 {
1405 rx->stop_rds_decoder();
1406 }
1407 }
1408
is_rds_decoder_active(void) const1409 bool receiver::is_rds_decoder_active(void) const
1410 {
1411 return rx->is_rds_decoder_active();
1412 }
1413
reset_rds_parser(void)1414 void receiver::reset_rds_parser(void)
1415 {
1416 rx->reset_rds_parser();
1417 }
1418
escape_filename(std::string filename)1419 std::string receiver::escape_filename(std::string filename)
1420 {
1421 std::stringstream ss1;
1422 std::stringstream ss2;
1423
1424 ss1 << std::quoted(filename, '\'', '\\');
1425 ss2 << std::quoted(ss1.str(), '\'', '\\');
1426 return ss2.str();
1427 }
1428