Updated license from GPL version 2 or later to GPL version 3 or later.
[debian/gnuradio] / gr-atsc / src / lib / atsci_sssr.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 <atsci_sssr.h>
24 #include <algorithm>
25 #include <cmath>
26 #include <cstdio>
27 #include <assert.h>
28 #include <atsci_diag_output.h>
29 #include <gr_math.h>
30
31 /*
32  * ----------------------------------------------------------------
33  * Segment Integrator Pre-Processor
34  *
35  * Compute weight passed to integrator based on
36  * correlation result and ncorr_gain2
37  */
38
39 inline static int
40 sipp (bool digcorr_output)
41 {
42   if (digcorr_output)
43     return +2;                  // positive correlation
44   else
45     return -1;                  // no correlation
46 }
47
48 /* 
49  * ----------------------------------------------------------------
50  * Segment Sync Integrator...
51  */
52
53 static const int SSI_MIN = -16;
54 static const int SSI_MAX =  15;
55
56 void
57 sssr::seg_sync_integrator::reset ()
58 {
59   for (int i = 0; i < ATSC_DATA_SEGMENT_LENGTH; i++)
60     d_integrator[i] = SSI_MIN;
61 }
62   
63 int
64 sssr::seg_sync_integrator::update (int weight, int index)
65 {
66   int   t = d_integrator[index] + weight;
67   t = std::max (t, SSI_MIN);
68   t = std::min (t, SSI_MAX);
69   d_integrator[index] = t;
70
71   return t;
72 }
73
74 int
75 sssr::seg_sync_integrator::find_max (int *v)
76 {
77   int   best_value = d_integrator[0];
78   int   best_index = 0;
79   
80   for (int i = 1; i < ATSC_DATA_SEGMENT_LENGTH; i++)
81     if (d_integrator[i] > best_value){
82       best_value = d_integrator[i];
83       best_index = i;
84     }
85
86   *v = best_value;
87   return best_index;
88 }
89
90 /*
91  * ----------------------------------------------------------------
92  * Segment Sync and Symbol recovery
93  */
94
95 static const int        SYMBOL_INDEX_OFFSET = 3;
96 static const int        MIN_SEG_LOCK_CORRELATION_VALUE = 5;
97
98 atsci_sssr::atsci_sssr ()
99 {
100   reset ();
101   
102   if (_SSSR_DIAG_OUTPUT_){
103     const char *file = "sssr.ssout";
104     if ((d_debug_fp = fopen (file, "wb")) == 0){
105       perror (file);
106       exit (1);
107     }
108   }
109 }
110
111 atsci_sssr::~atsci_sssr ()
112 {
113   if (d_debug_fp)
114     fclose (d_debug_fp);
115 }
116
117 void
118 atsci_sssr::reset ()
119 {
120   d_correlator.reset ();
121   d_integrator.reset ();
122   d_quad_filter.reset ();
123
124   for (int i = 0; i < ATSC_DATA_SEGMENT_LENGTH; i++)
125     d_quad_output[i] = 0;
126   
127   d_timing_adjust = 0;
128   d_counter = 0;
129   d_symbol_index = 0;
130   d_seg_locked = false;
131 }
132
133 void
134 atsci_sssr::update (sssr::sample_t sample_in,    // input
135                    bool          *seg_locked,    // are we seeing segment syncs?
136                    int           *symbol_index,  // 0..831
137                    double        *timing_adjust) // how much to adjust timing
138 {
139   double qo = d_quad_filter.update (sample_in);
140   d_quad_output[d_counter] = qo;
141
142   int bit = gr_signbit (sample_in) ^ 1; // slice on sign: + => 1, - => 0
143   int corr_out = d_correlator.update (bit);
144   int weight = sipp (corr_out);
145   int corr_value = d_integrator.update (weight, d_counter);
146   int best_correlation_index = -1;
147
148   incr_symbol_index ();
149   if (incr_counter ()){         // counter just wrapped...
150     int best_correlation_value;
151     best_correlation_index = d_integrator.find_max (&best_correlation_value);
152     d_seg_locked = best_correlation_value >= MIN_SEG_LOCK_CORRELATION_VALUE;
153     d_timing_adjust = d_quad_output[best_correlation_index];
154
155     d_symbol_index = SYMBOL_INDEX_OFFSET - 1 - best_correlation_index;
156     if (d_symbol_index < 0)
157       d_symbol_index += ATSC_DATA_SEGMENT_LENGTH;
158   }
159
160   *seg_locked = d_seg_locked;
161   *symbol_index = d_symbol_index;
162   *timing_adjust = d_timing_adjust;
163
164   if (_SSSR_DIAG_OUTPUT_){
165     float       iodata[7];
166     iodata[0] = bit;
167     iodata[1] = corr_value;
168     iodata[2] = qo;
169     iodata[3] = d_counter;
170     iodata[4] = d_symbol_index;
171     iodata[5] = best_correlation_index;
172     iodata[6] = d_timing_adjust;
173
174     if (fwrite (iodata, sizeof (iodata), 1, d_debug_fp) != 1){
175       perror ("fwrite: sssr");
176       exit (1);
177     }
178   }
179
180 }
181
182 /*
183  * ----------------------------------------------------------------
184  * Interpolator control for Seg & Symbol Sync Recovery
185  */
186
187 static const double     LOOP_FILTER_TAP = 0.00025;      // 0.0005 works
188 static const double     ADJUSTMENT_GAIN = 1.0e-5 / (10 * ATSC_DATA_SEGMENT_LENGTH);
189   
190 atsci_interpolator::atsci_interpolator (double nominal_ratio_of_rx_clock_to_symbol_freq)
191 {
192 // Tweaked ratio from 1.8 to 1.78 to support input rate of 19.2MHz
193   assert (nominal_ratio_of_rx_clock_to_symbol_freq >= 1.78);
194   d_nominal_ratio_of_rx_clock_to_symbol_freq =
195     nominal_ratio_of_rx_clock_to_symbol_freq;
196
197   d_loop.set_taps (LOOP_FILTER_TAP);
198
199   reset ();
200
201   if (_SSSR_DIAG_OUTPUT_){
202     const char *file = "interp.ssout";
203     if ((d_debug_fp = fopen (file, "wb")) == 0){
204       perror (file);
205       exit (1);
206     }
207   }
208
209 }
210
211 atsci_interpolator::~atsci_interpolator ()
212 {
213   if (d_debug_fp)
214     fclose (d_debug_fp);
215 }
216
217 void
218 atsci_interpolator::reset ()
219 {
220   d_w = d_nominal_ratio_of_rx_clock_to_symbol_freq;
221   d_mu = 0.5;
222   d_incr = 0;
223   d_loop.reset ();
224 }
225
226 bool
227 atsci_interpolator::update (
228            const sssr::sample_t input_samples[],        // I: vector of samples
229            int                  nsamples,               // I: total number of samples
230            int                 *index,                  // I/O: current input index
231            double               timing_adjustment,      // I: how much to bump timing
232            sssr::sample_t      *output_sample)
233 {
234   if (*index + (int) ntaps () > nsamples)
235     return false;
236
237   // FIXME Confirm that this is right.  I think it is.  It was (1-d_mu)
238   *output_sample = d_interp.interpolate (&input_samples[*index], d_mu);  
239
240   double filter_out = 0;
241   
242 #if 0
243
244   filter_out = d_loop.filter (timing_adjustment);
245   d_w = d_w + ADJUSTMENT_GAIN * filter_out * 1e-3;
246
247 #elif 1
248
249   filter_out = d_loop.filter (timing_adjustment);
250   d_mu = d_mu + ADJUSTMENT_GAIN * 10e3 * filter_out;
251
252 #else
253
254   static const double alpha = 0.01;
255   static const double beta = alpha * alpha / 16;
256
257   double x = ADJUSTMENT_GAIN * 10e3 * timing_adjustment;
258
259   d_mu = d_mu + alpha * x;      // conceptually "phase"
260   d_w  = d_w  + beta * x;       // conceptually "frequency"
261   
262 #endif
263   
264   double s = d_mu + d_w;
265   double float_incr = floor (s);
266   d_mu = s - float_incr;
267   d_incr = (int) float_incr;
268
269   assert (d_incr >= 1 && d_incr <= 3);
270   *index += d_incr;
271
272   if (_SSSR_DIAG_OUTPUT_){
273     float       iodata[6];
274     iodata[0] = timing_adjustment;
275     iodata[1] = filter_out;
276     iodata[2] = d_w;
277     iodata[3] = d_mu;
278     iodata[4] = d_incr;
279     iodata[5] = *output_sample;
280     if (fwrite (iodata, sizeof (iodata), 1, d_debug_fp) != 1){
281       perror ("fwrite: interpolate");
282       exit (1);
283     }
284   }
285
286   return true;
287 }