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 <gnuradio/io_signature.h>
25 #include <math.h>
26 #include <iostream>
27 #include <dsp/stereo_demod.h>
28
29
30 /* Create a new instance of stereo_demod and return a boost shared_ptr. */
make_stereo_demod(float quad_rate,float audio_rate,bool stereo,bool oirt)31 stereo_demod_sptr make_stereo_demod(float quad_rate, float audio_rate,
32 bool stereo, bool oirt)
33 {
34 return gnuradio::get_initial_sptr(new stereo_demod(quad_rate,
35 audio_rate, stereo, oirt));
36 }
37
38
39 static const int MIN_IN = 1; /* Minimum number of input streams. */
40 static const int MAX_IN = 1; /* Maximum number of input streams. */
41 static const int MIN_OUT = 2; /* Minimum number of output streams. */
42 static const int MAX_OUT = 2; /* Maximum number of output streams. */
43
44 /*! \brief Create stereo demodulator object.
45 *
46 * Use make_stereo_demod() instead.
47 */
stereo_demod(float input_rate,float audio_rate,bool stereo,bool oirt)48 stereo_demod::stereo_demod(float input_rate, float audio_rate, bool stereo, bool oirt)
49 : gr::hier_block2("stereo_demod",
50 gr::io_signature::make (MIN_IN, MAX_IN, sizeof (float)),
51 gr::io_signature::make (MIN_OUT, MAX_OUT, sizeof (float))),
52 d_input_rate(input_rate),
53 d_audio_rate(audio_rate),
54 d_stereo(stereo),
55 d_oirt(oirt)
56 {
57 double cutof_freq = d_oirt ? 15e3 : 17e3;
58 lpf0 = make_lpf_ff(d_input_rate, cutof_freq, 2e3); // FIXME
59 audio_rr0 = make_resampler_ff(d_audio_rate/d_input_rate);
60 deemph0 = make_fm_deemph(d_audio_rate, 50.0e-6);
61
62 if (d_stereo)
63 {
64 lpf1 = make_lpf_ff(d_input_rate, cutof_freq, 2e3, -2.1); // FIXME
65 audio_rr1 = make_resampler_ff(d_audio_rate/d_input_rate);
66 deemph1 = make_fm_deemph(d_audio_rate, 50.0e-6);
67
68 if (!d_oirt)
69 {
70 d_tone_taps = gr::filter::firdes::complex_band_pass(
71 20.0, // gain,
72 d_input_rate, // sampling_freq
73 18980., // low_cutoff_freq
74 19020., // high_cutoff_freq
75 1000.); // transition_width
76 pll = gr::analog::pll_refout_cc::make(0.001, // loop_bw FIXME
77 2*M_PI * 19020 / input_rate, // max_freq
78 2*M_PI * 18980 / input_rate); // min_freq
79 subtone = gr::blocks::multiply_cc::make();
80 } else {
81 d_tone_taps = gr::filter::firdes::complex_band_pass(
82 1.0, // gain,
83 d_input_rate, // sampling_freq
84 31200., // low_cutoff_freq
85 31300., // high_cutoff_freq
86 100.); // transition_width
87 pll = gr::analog::pll_refout_cc::make(0.001, // loop_bw FIXME
88 2*M_PI * 31200 / input_rate, // max_freq
89 2*M_PI * 31300 / input_rate); // min_freq
90 }
91
92 tone = gr::filter::fir_filter_fcc::make(1, d_tone_taps);
93 delay = gr::blocks::delay::make(sizeof(float), (d_tone_taps.size() - 1) / 2);
94
95 lo = gr::blocks::complex_to_imag::make();
96
97 mixer = gr::blocks::multiply_ff::make();
98
99 add = gr::blocks::add_ff::make();
100 sub = gr::blocks::sub_ff::make();
101
102 /* connect block */
103 if (!d_oirt) {
104 connect(self(), 0, tone, 0);
105 connect(self(), 0, delay, 0);
106 connect(tone, 0, pll, 0);
107 connect(pll, 0, subtone, 0);
108 connect(pll, 0, subtone, 1);
109 connect(subtone, 0, lo, 0);
110
111 connect(lo, 0, mixer, 0);
112 } else {
113 connect(self(), 0, tone, 0);
114 connect(self(), 0, delay, 0);
115 connect(tone, 0, pll, 0);
116 connect(pll, 0, lo, 0);
117 connect(lo, 0, mixer, 0);
118 }
119
120 connect(delay, 0, mixer, 1);
121
122 connect(delay, 0, lpf0, 0);
123 connect(mixer, 0, lpf1, 0);
124
125 connect(lpf0, 0, audio_rr0, 0); // sum
126 connect(lpf1, 0, audio_rr1, 0); // delta
127
128 connect(audio_rr0, 0, add, 0);
129 connect(audio_rr1, 0, add, 1);
130 connect(add, 0, deemph0, 0);
131 connect(deemph0, 0, self(), 0); // left = sum + delta
132
133 connect(audio_rr0, 0, sub, 0);
134 connect(audio_rr1, 0, sub, 1);
135 connect(sub, 0, deemph1, 0);
136 connect(deemph1, 0, self(), 1); // right = sum - delta
137 }
138 else // if (!d_stereo)
139 {
140 /* connect block */
141 connect(self(), 0, lpf0, 0);
142 connect(lpf0, 0, audio_rr0, 0);
143 connect(audio_rr0, 0, deemph0, 0);
144 connect(deemph0, 0, self(), 0);
145 connect(deemph0, 0, self(), 1);
146 }
147 }
148
149
~stereo_demod()150 stereo_demod::~stereo_demod()
151 {
152
153 }
154