Updated license from GPL version 2 or later to GPL version 3 or later.
[debian/gnuradio] / gr-atsc / src / lib / GrAtscFPLL.cc
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2002 Free Software Foundation, Inc.
4  * 
5  * This file is part of GNU Radio
6  * 
7  * GNU Radio is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3, or (at your option)
10  * any later version.
11  * 
12  * GNU Radio is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with GNU Radio; see the file COPYING.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #include <GrAtscFPLL.h>
24 #include <algorithm>
25 #include "fpll_btloop_coupling.h"
26
27 /*
28  * I strongly suggest that you not mess with these...
29  *
30  * They are strongly coupled into the symbol timing code and
31  * their value also sets the level of the symbols going
32  * into the equalizer and viterbi decoder.
33  */
34 static const float FPLL_AGC_REFERENCE = 2.5 * FPLL_BTLOOP_COUPLING_CONST;
35 static const float FPLL_AGC_RATE = 0.25e-6;
36
37
38 GrAtscFPLL::GrAtscFPLL (double a_initial_freq)
39   : VrSigProc (1, sizeof (iType), sizeof (oType)),
40     initial_phase(0), debug_no_update(false)
41 {
42   initial_freq = a_initial_freq;
43   agc.set_rate (FPLL_AGC_RATE);
44   agc.set_reference (FPLL_AGC_REFERENCE);
45
46   if (_FPLL_DIAG_OUTPUT_){
47     fp = fopen ("fpll.out", "w");
48     if (fp == 0){
49       perror ("fpll.out");
50       exit (1);
51     }
52   }
53
54 }
55
56 void
57 GrAtscFPLL::initialize ()
58 {
59   float Fs = getInputSamplingFrequencyN (0);
60
61   float alpha = 1 - exp(-1.0 / Fs / 5e-6);
62   
63   afci.set_taps (alpha);
64   afcq.set_taps (alpha);
65
66   nco.set_freq (initial_freq / Fs * 2 * M_PI);
67   nco.set_phase (initial_phase);
68 }
69
70 int 
71 GrAtscFPLL::work (VrSampleRange output, void *ao[],
72                   VrSampleRange inputs[], void *ai[])
73 {
74   iType  *in = ((iType **)ai)[0];
75   oType  *out = ((oType **)ao)[0];
76
77   unsigned int  k;
78
79   for (k = 0; k < output.size; k++){
80
81     float a_cos, a_sin;
82
83     float input = agc.scale (in[k]);
84
85     nco.step ();                // increment phase
86     nco.sincos (a_sin, a_cos);  // compute cos and sin
87
88     float I = input * a_sin;
89     float Q = input * a_cos;
90
91     out[k] = I;
92
93     float filtered_I = afci.filter (I);
94     float filtered_Q = afcq.filter (Q);
95
96     // phase detector
97
98     float x = atan2 (filtered_Q, filtered_I);
99
100     // avoid slamming filter with big transitions
101
102     static const float limit = M_PI / 2;
103
104     if (x > limit)
105       x = limit;
106     else if (x < -limit)
107       x = -limit;
108
109     // static const float alpha = 0.037;   // Max value
110     // static const float alpha = 0.005;   // takes about 5k samples to pull in, stddev = 323
111     // static const float alpha = 0.002;   // takes about 15k samples to pull in, stddev =  69
112                                            //  or about 120k samples on noisy data, 
113     static const float alpha = 0.001;
114     static const float beta = alpha * alpha / 4;
115
116
117     if (!debug_no_update){
118       nco.adjust_phase (alpha * x);
119       nco.adjust_freq (beta * x);
120     }
121
122     if (_FPLL_DIAG_OUTPUT_){
123 #if 0   // lots of data...
124       float     iodata[8];
125       iodata[0] = nco.get_freq () * getSamplingFrequency () * (1.0 / (2 * M_PI));
126       iodata[1] = in[k];
127       iodata[2] = input;
128       iodata[3] = I;
129       iodata[4] = Q;
130       iodata[5] = filtered_I;
131       iodata[6] = filtered_Q;
132       iodata[7] = x;
133       if (fwrite (iodata, sizeof (iodata), 1, fp) != 1){
134         perror ("fwrite: fpll");
135         exit (1);
136       }
137 #else   // just the frequency
138       float     iodata[1];
139       iodata[0] = nco.get_freq () * getSamplingFrequency () * (1.0 / (2 * M_PI));
140       if (fwrite (iodata, sizeof (iodata), 1, fp) != 1){
141         perror ("fwrite: fpll");
142         exit (1);
143       }
144 #endif
145     }
146   }
147
148   return output.size;
149 }
150