1 /* -*- c++ -*- */
2 /*
3  * Gqrx SDR: Software defined radio receiver powered by GNU Radio and Qt
4  *           https://gqrx.dk/
5  *
6  * Copyright 2012 Alexandru Csete OZ9AEC.
7  * FM stereo implementation by Alex Grinkov a.grinkov(at)gmail.com.
8  *
9  * Gqrx is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 3, or (at your option)
12  * any later version.
13  *
14  * Gqrx is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Gqrx; see the file COPYING.  If not, write to
21  * the Free Software Foundation, Inc., 51 Franklin Street,
22  * Boston, MA 02110-1301, USA.
23  */
24 #include <cmath>
25 #include <iostream>
26 #include <QDebug>
27 #include "receivers/wfmrx.h"
28 
29 #define PREF_QUAD_RATE   240e3 // Nominal channel spacing is 200 kHz
30 
make_wfmrx(float quad_rate,float audio_rate)31 wfmrx_sptr make_wfmrx(float quad_rate, float audio_rate)
32 {
33     return gnuradio::get_initial_sptr(new wfmrx(quad_rate, audio_rate));
34 }
35 
wfmrx(float quad_rate,float audio_rate)36 wfmrx::wfmrx(float quad_rate, float audio_rate)
37     : receiver_base_cf("WFMRX"),
38       d_running(false),
39       d_quad_rate(quad_rate),
40       d_audio_rate(audio_rate),
41       d_demod(WFMRX_DEMOD_MONO)
42 {
43     iq_resamp = make_resampler_cc(PREF_QUAD_RATE/d_quad_rate);
44 
45     filter = make_rx_filter(PREF_QUAD_RATE, -80000.0, 80000.0, 20000.0);
46     sql = gr::analog::simple_squelch_cc::make(-150.0, 0.001);
47     meter = make_rx_meter_c(PREF_QUAD_RATE);
48     demod_fm = make_rx_demod_fm(PREF_QUAD_RATE, 75000.0, 0.0);
49     stereo = make_stereo_demod(PREF_QUAD_RATE, d_audio_rate, true);
50     stereo_oirt = make_stereo_demod(PREF_QUAD_RATE, d_audio_rate, true, true);
51     mono = make_stereo_demod(PREF_QUAD_RATE, d_audio_rate, false);
52 
53     /* create rds blocks but dont connect them */
54     rds = make_rx_rds(PREF_QUAD_RATE);
55     rds_decoder = gr::rds::decoder::make(0, 0);
56     rds_parser = gr::rds::parser::make(0, 0, 0);
57     rds_store = make_rx_rds_store();
58     rds_enabled = false;
59 
60     connect(self(), 0, iq_resamp, 0);
61     connect(iq_resamp, 0, filter, 0);
62     connect(filter, 0, meter, 0);
63     connect(filter, 0, sql, 0);
64     connect(sql, 0, demod_fm, 0);
65     connect(demod_fm, 0, mono, 0);
66     connect(mono, 0, self(), 0); // left  channel
67     connect(mono, 1, self(), 1); // right channel
68 }
69 
~wfmrx()70 wfmrx::~wfmrx()
71 {
72 
73 }
74 
start()75 bool wfmrx::start()
76 {
77     d_running = true;
78 
79     return true;
80 }
81 
stop()82 bool wfmrx::stop()
83 {
84     d_running = false;
85 
86     return true;
87 }
88 
set_quad_rate(float quad_rate)89 void wfmrx::set_quad_rate(float quad_rate)
90 {
91     if (std::abs(d_quad_rate-quad_rate) > 0.5)
92     {
93         qDebug() << "Changing WFM RX quad rate:"  << d_quad_rate << "->" << quad_rate;
94         d_quad_rate = quad_rate;
95         lock();
96         iq_resamp->set_rate(PREF_QUAD_RATE/d_quad_rate);
97         unlock();
98     }
99 }
100 
set_audio_rate(float audio_rate)101 void wfmrx::set_audio_rate(float audio_rate)
102 {
103     (void) audio_rate;
104 }
105 
set_filter(double low,double high,double tw)106 void wfmrx::set_filter(double low, double high, double tw)
107 {
108     filter->set_param(low, high, tw);
109 }
110 
get_signal_level()111 float wfmrx::get_signal_level()
112 {
113     return meter->get_level_db();
114 }
115 
116 /*
117 void nbrx::set_nb_on(int nbid, bool on)
118 {
119     if (nbid == 1)
120         nb->set_nb1_on(on);
121     else if (nbid == 2)
122         nb->set_nb2_on(on);
123 }
124 
125 void nbrx::set_nb_threshold(int nbid, float threshold)
126 {
127     if (nbid == 1)
128         nb->set_threshold1(threshold);
129     else if (nbid == 2)
130         nb->set_threshold2(threshold);
131 }
132 */
133 
set_sql_level(double level_db)134 void wfmrx::set_sql_level(double level_db)
135 {
136     sql->set_threshold(level_db);
137 }
138 
set_sql_alpha(double alpha)139 void wfmrx::set_sql_alpha(double alpha)
140 {
141     sql->set_alpha(alpha);
142 }
143 
144 /*
145 void nbrx::set_agc_on(bool agc_on)
146 {
147     agc->set_agc_on(agc_on);
148 }
149 
150 void nbrx::set_agc_hang(bool use_hang)
151 {
152     agc->set_use_hang(use_hang);
153 }
154 
155 void nbrx::set_agc_threshold(int threshold)
156 {
157     agc->set_threshold(threshold);
158 }
159 
160 void nbrx::set_agc_slope(int slope)
161 {
162     agc->set_slope(slope);
163 }
164 
165 void nbrx::set_agc_decay(int decay_ms)
166 {
167     agc->set_decay(decay_ms);
168 }
169 
170 void nbrx::set_agc_manual_gain(int gain)
171 {
172     agc->set_manual_gain(gain);
173 }
174 */
175 
set_demod(int demod)176 void wfmrx::set_demod(int demod)
177 {
178     /* check if new demodulator selection is valid */
179     if ((demod < WFMRX_DEMOD_MONO) || (demod >= WFMRX_DEMOD_NUM))
180         return;
181 
182     if (demod == d_demod) {
183         /* nothing to do */
184         return;
185     }
186 
187     /* lock graph while we reconfigure */
188     lock();
189 
190     /* disconnect current demodulator */
191     switch (d_demod) {
192 
193     case WFMRX_DEMOD_MONO:
194     default:
195         disconnect(demod_fm, 0, mono, 0);
196         disconnect(mono, 0, self(), 0); // left  channel
197         disconnect(mono, 1, self(), 1); // right channel
198         break;
199 
200     case WFMRX_DEMOD_STEREO:
201         disconnect(demod_fm, 0, stereo, 0);
202         disconnect(stereo, 0, self(), 0); // left  channel
203         disconnect(stereo, 1, self(), 1); // right channel
204         break;
205 
206     case WFMRX_DEMOD_STEREO_UKW:
207         disconnect(demod_fm, 0, stereo_oirt, 0);
208         disconnect(stereo_oirt, 0, self(), 0); // left  channel
209         disconnect(stereo_oirt, 1, self(), 1); // right channel
210         break;
211     }
212 
213     switch (demod) {
214 
215     case WFMRX_DEMOD_MONO:
216     default:
217         connect(demod_fm, 0, mono, 0);
218         connect(mono, 0, self(), 0); // left  channel
219         connect(mono, 1, self(), 1); // right channel
220         break;
221 
222     case WFMRX_DEMOD_STEREO:
223         connect(demod_fm, 0, stereo, 0);
224         connect(stereo, 0, self(), 0); // left  channel
225         connect(stereo, 1, self(), 1); // right channel
226         break;
227 
228     case WFMRX_DEMOD_STEREO_UKW:
229         connect(demod_fm, 0, stereo_oirt, 0);
230         connect(stereo_oirt, 0, self(), 0); // left  channel
231         connect(stereo_oirt, 1, self(), 1); // right channel
232         break;
233     }
234     d_demod = (wfmrx_demod) demod;
235 
236     /* continue processing */
237     unlock();
238 }
239 
set_fm_maxdev(float maxdev_hz)240 void wfmrx::set_fm_maxdev(float maxdev_hz)
241 {
242     demod_fm->set_max_dev(maxdev_hz);
243 }
244 
set_fm_deemph(double tau)245 void wfmrx::set_fm_deemph(double tau)
246 {
247     demod_fm->set_tau(tau);
248 }
249 
get_rds_data(std::string & outbuff,int & num)250 void wfmrx::get_rds_data(std::string &outbuff, int &num)
251 {
252     rds_store->get_message(outbuff, num);
253 }
254 
start_rds_decoder()255 void wfmrx::start_rds_decoder()
256 {
257     connect(demod_fm, 0, rds, 0);
258     connect(rds, 0, rds_decoder, 0);
259     msg_connect(rds_decoder, "out", rds_parser, "in");
260     msg_connect(rds_parser, "out", rds_store, "store");
261     rds_enabled=true;
262 }
263 
stop_rds_decoder()264 void wfmrx::stop_rds_decoder()
265 {
266     lock();
267     disconnect(demod_fm, 0, rds, 0);
268     disconnect(rds, 0, rds_decoder, 0);
269     msg_disconnect(rds_decoder, "out", rds_parser, "in");
270     msg_disconnect(rds_parser, "out", rds_store, "store");
271     unlock();
272     rds_enabled=false;
273 }
274 
reset_rds_parser()275 void wfmrx::reset_rds_parser()
276 {
277     rds_parser->reset();
278 }
279 
is_rds_decoder_active()280 bool wfmrx::is_rds_decoder_active()
281 {
282     return rds_enabled;
283 }
284