SDRAngel  4.11.5
Developer docs for <a href="https://github.com/f4exb/sdrangel">SDRangel<\a>, an Open Source Qt5 / OpenGL 3.0+ SDR and signal analyzer frontend to various hardware.
rdsdemod.cpp
Go to the documentation of this file.
1 // Copyright (C) 2015 F4EXB //
3 // written by Edouard Griffiths //
4 // //
5 // This program is free software; you can redistribute it and/or modify //
6 // it under the terms of the GNU General Public License as published by //
7 // the Free Software Foundation as version 3 of the License, or //
8 // (at your option) any later version. //
9 // //
10 // This program is distributed in the hope that it will be useful, //
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of //
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
13 // GNU General Public License V3 for more details. //
14 // //
15 // You should have received a copy of the GNU General Public License //
16 // along with this program. If not, see <http://www.gnu.org/licenses/>. //
18 
19 #include "../../channelrx/demodbfm/rdsdemod.h"
20 
21 #include <QDebug>
22 #include <math.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 
26 #undef M_PI
27 #define M_PI 3.14159265358979323846
28 #undef M_PI_2
29 #define M_PI_2 1.57079632679489661923
30 
31 const Real RDSDemod::m_pllBeta = 50;
32 const Real RDSDemod::m_fsc = 1187.5;
33 
35  // : m_udpDebug(this, 1472, 9995) // UDP debug
36 {
37  m_srate = 250000;
38 
39  m_parms.subcarr_phi = 0;
40  memset(m_parms.subcarr_bb, 0, sizeof(m_parms.subcarr_bb));
41  m_parms.clock_offset = 0;
42  m_parms.clock_phi = 0;
43  m_parms.prev_clock_phi = 0;
44  m_parms.lo_clock = 0;
45  m_parms.prev_lo_clock = 0;
46  m_parms.prev_bb = 0;
47  m_parms.d_cphi = 0;
48  m_parms.acc = 0;
49  m_parms.numsamples = 0;
50  m_parms.prev_acc = 0;
51  m_parms.counter = 0;
52  m_parms.reading_frame = 0;
53  memset(m_parms.tot_errs, 0, sizeof(m_parms.tot_errs));
54  m_parms.dbit = 0;
55  m_prev = 0.0f;
56  memset(m_xv, 0, 6*sizeof(Real));
57  memset(m_yv, 0, 6*sizeof(Real));
58  memset(m_xw, 0, 2*sizeof(Real));
59  memset(m_yw, 0, 2*sizeof(Real));
60  m_report.acc = 0.0f;
61  m_report.fclk = 0.0f;
62  m_report.qua = 0.0f;
63 }
64 
66 {
67  //delete m_socket;
68 }
69 
70 void RDSDemod::setSampleRate(int srate)
71 {
72  (void) srate;
73 }
74 
75 bool RDSDemod::process(Real demod, bool& bit)
76 {
77  bool ret = false;
78 
79  //m_udpDebug.write(m_parms.lo_clock * m_parms.subcarr_bb[0]); // UDP debug
80 
81  // Subcarrier downmix & phase recovery
82 
83  m_parms.subcarr_bb[0] = filter_lp_2400_iq(demod, 0);
84 
85  // 1187.5 Hz clock
86 
87  /*
88  if (m_parms.subcarr_phi > 1e9) // ~ every 37 hours => not really useful
89  {
90  qDebug("RDSDemod::process: reset 1187.5 Hz clock");
91  m_parms.subcarr_phi = 0;
92  m_parms.clock_offset = 0;
93  }*/
94 
95  m_parms.subcarr_phi += (2 * M_PI * m_fsc) / (Real) m_srate;
96  m_parms.clock_phi = m_parms.subcarr_phi + m_parms.clock_offset;
97 
98  // Clock phase recovery
99 
100  if (sign(m_parms.prev_bb) != sign(m_parms.subcarr_bb[0]))
101  {
102  m_parms.d_cphi = std::fmod(m_parms.clock_phi, M_PI);
103 
104  if (m_parms.d_cphi >= M_PI_2)
105  {
106  m_parms.d_cphi -= M_PI;
107  }
108 
109  m_parms.clock_offset -= 0.005 * m_parms.d_cphi;
110  }
111 
112  m_parms.clock_phi = std::fmod(m_parms.clock_phi, 2 * M_PI);
113  m_parms.lo_clock = (m_parms.clock_phi < M_PI ? 1 : -1);
114 
115  /* Decimate band-limited signal */
116  if (m_parms.numsamples % 8 == 0)
117  {
118  /* biphase symbol integrate & dump */
119  m_parms.acc += m_parms.subcarr_bb[0] * m_parms.lo_clock;
120 
121  if (sign(m_parms.lo_clock) != sign(m_parms.prev_lo_clock))
122  {
123  ret = biphase(m_parms.acc, bit, m_parms.clock_phi - m_parms.prev_clock_phi);
124  m_parms.acc = 0;
125  }
126 
127  m_parms.prev_lo_clock = m_parms.lo_clock;
128  }
129 
130  m_parms.numsamples++;
131  m_parms.prev_bb = m_parms.subcarr_bb[0];
132  m_parms.prev_clock_phi = m_parms.clock_phi;
133  m_prev = demod;
134 
135  return ret;
136 }
137 
139 {
140  bool ret = false;
141 
142  if (sign(acc) != sign(m_parms.prev_acc)) // two successive of different sign: error detected
143  {
144  m_parms.tot_errs[m_parms.counter % 2]++;
145  }
146 
147  if (m_parms.counter % 2 == m_parms.reading_frame) // two successive of the same sing: OK
148  {
149  // new bit found
150  int b = sign(m_parms.acc + m_parms.prev_acc);
151  bit = b ^ m_parms.dbit;
152  m_parms.dbit = b;
153  ret = true;
154  }
155 
156  if (m_parms.counter == 0)
157  {
158  if (m_parms.tot_errs[1 - m_parms.reading_frame] < m_parms.tot_errs[m_parms.reading_frame])
159  {
160  m_parms.reading_frame = 1 - m_parms.reading_frame;
161  }
162 
163  m_report.qua = (1.0 * abs(m_parms.tot_errs[0] - m_parms.tot_errs[1]) / (m_parms.tot_errs[0] + m_parms.tot_errs[1])) * 100;
164  m_report.acc = acc;
165  m_report.fclk = (d_cphi / (2 * M_PI)) * m_srate;
166 
167  /*
168  qDebug("RDSDemod::biphase: frame: %d acc: %+6.3f errs: %3d %3d qual: %3.0f%% clk: %7.2f",
169  m_parms.reading_frame,
170  acc,
171  m_parms.tot_errs[0],
172  m_parms.tot_errs[1],
173  m_report.qua,
174  m_report.fclk);*/
175 
176  m_parms.tot_errs[0] = 0;
177  m_parms.tot_errs[1] = 0;
178  }
179 
180  m_parms.prev_acc = acc; // memorize (z^-1)
181  m_parms.counter = (m_parms.counter + 1) % 800;
182 
183  return ret;
184 }
185 
187 {
188  /* Digital filter designed by mkfilter/mkshape/gencode A.J. Fisher
189  Command line: /www/usr/fisher/helpers/mkfilter -Bu -Lp -o 10
190  -a 4.8000000000e-03 0.0000000000e+00 -l */
191 
192  m_xv[iqIndex][0] = m_xv[iqIndex][1]; m_xv[iqIndex][1] = m_xv[iqIndex][2];
193  m_xv[iqIndex][2] = input / 4.491730007e+03;
194  m_yv[iqIndex][0] = m_yv[iqIndex][1]; m_yv[iqIndex][1] = m_yv[iqIndex][2];
195  m_yv[iqIndex][2] = (m_xv[iqIndex][0] + m_xv[iqIndex][2]) + 2 * m_xv[iqIndex][1]
196  + ( -0.9582451124 * m_yv[iqIndex][0]) + ( 1.9573545869 * m_yv[iqIndex][1]);
197 
198  return m_yv[iqIndex][2];
199 }
200 
202 {
203  m_xw[0] = m_xw[1];
204  m_xw[1] = input / 3.716236217e+01;
205  m_yw[0] = m_yw[1];
206  m_yw[1] = (m_xw[0] + m_xw[1])
207  + ( 0.9461821078 * m_yw[0]);
208  return m_yw[1];
209 }
210 
212 {
213  return (a >= 0 ? 1 : 0);
214 }
bool biphase(Real acc, bool &bit, Real d_cphi)
Definition: rdsdemod.cpp:138
bool process(Real rdsSample, bool &bit)
Definition: rdsdemod.cpp:75
Real filter_lp_pll(Real input)
Definition: rdsdemod.cpp:201
static const Real m_fsc
Definition: rdsdemod.h:80
struct RDSDemod::@1 m_report
#define M_PI_2
Definition: rdsdemod.cpp:29
Fixed< IntType, IntBits > abs(Fixed< IntType, IntBits > const &x)
Definition: fixed.h:2313
#define M_PI
Definition: rdsdemod.cpp:27
Real filter_lp_2400_iq(Real in, int iqIndex)
Definition: rdsdemod.cpp:186
Real m_yw[1+1]
Definition: rdsdemod.h:74
int sign(Real a)
Definition: rdsdemod.cpp:211
Real acc
Definition: rdsdemod.h:39
Real m_prev
Definition: rdsdemod.h:75
Real m_xv[2][2+1]
Definition: rdsdemod.h:71
int m_srate
Definition: rdsdemod.h:77
Real m_yv[2][2+1]
Definition: rdsdemod.h:72
RDSDemod()
Definition: rdsdemod.cpp:34
static const Real m_pllBeta
Definition: rdsdemod.h:79
~RDSDemod()
Definition: rdsdemod.cpp:65
struct RDSDemod::@2 m_parms
Real m_xw[1+1]
Definition: rdsdemod.h:73
void setSampleRate(int srate)
FIXME: fix rate for now.
Definition: rdsdemod.cpp:70
float Real
Definition: dsptypes.h:42
double d_cphi
Definition: rdsdemod.h:61