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