3 * Copyright 2002 Free Software Foundation, Inc.
5 * This file is part of GNU Radio
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)
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.
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.
23 #include <atsci_equalizer_lms2.h>
26 #include <atsci_pnXXX.h>
35 static const int NFFTAPS = 64;
36 static const int NFBTAPS = 192;
38 // the length of the field sync pattern that we know unequivocally
39 static const int KNOWN_FIELD_SYNC_LENGTH = 4 + 511 + 3 * 63;
41 // static const float *get_data_seg_sync_training_sequence (int offset);
42 static int get_field_sync_training_sequence_length (int offset);
43 static const float *get_field_sync_training_sequence (int which_field, int offset);
48 assert (d >= 0 && d <= (2 * NFBTAPS));
76 atsci_equalizer_lms2::atsci_equalizer_lms2 ()
77 : d_taps_ff (NFFTAPS), d_taps_fb (NFBTAPS), d_old_output (NFBTAPS)
79 for (int i = 0; i < NFFTAPS; i++) {
82 for (int i = 0; i < NFBTAPS; i++) {
84 d_old_output[i] = 0.0;
87 trainingfile=fopen("taps.txt","w");
90 atsci_equalizer_lms2::~atsci_equalizer_lms2 ()
95 atsci_equalizer_lms2::reset ()
97 atsci_equalizer::reset (); // invoke superclass
98 for (int i = 0; i < NFFTAPS; i++) {
101 for (int i = 0; i < NFBTAPS; i++) {
103 d_old_output[i] = 0.0;
110 atsci_equalizer_lms2::ntaps () const
112 return NFFTAPS + NFBTAPS;
116 atsci_equalizer_lms2::npretaps () const
122 * Input range is known NOT TO CONTAIN data segment syncs
123 * or field syncs. This should be the fast path. In the
124 * non decicion directed case, this just runs the input
125 * through the filter without adapting it.
127 * \p input_samples has (nsamples + ntaps() - 1) valid entries.
128 * input_samples[0] .. input_samples[nsamples - 1 + ntaps() - 1] may be
129 * referenced to compute the output values.
132 atsci_equalizer_lms2::filter_normal (const float *input_samples,
133 float *output_samples,
137 filterN (input_samples, output_samples, nsamples);
142 * Input range is known to consist of only a data segment sync or a
143 * portion of a data segment sync. \p nsamples will be in [1,4].
144 * \p offset will be in [0,3]. \p offset is the offset of the input
145 * from the beginning of the data segment sync pattern.
147 * \p input_samples has (nsamples + ntaps() - 1) valid entries.
148 * input_samples[0] .. input_samples[nsamples - 1 + ntaps() - 1] may be
149 * referenced to compute the output values.
152 atsci_equalizer_lms2::filter_data_seg_sync (const float *input_samples,
153 float *output_samples,
158 // adaptN (input_samples, get_data_seg_sync_training_sequence (offset),
159 // output_samples, nsamples);
160 filterN (input_samples, output_samples, nsamples);
162 // cerr << "Seg Sync: offset " << offset << "\tnsamples\t" << nsamples << "\t pre, 5 -5 -5 5\t" <<
163 // output_samples[0] << "\t" << output_samples[1] << "\t" << output_samples[2] << "\t" << output_samples[3] << endl;
169 * Input range is known to consist of only a field sync segment or a
170 * portion of a field sync segment. \p nsamples will be in [1,832].
171 * \p offset will be in [0,831]. \p offset is the offset of the input
172 * from the beginning of the data segment sync pattern. We consider the
173 * 4 symbols of the immediately preceding data segment sync to be the
174 * first symbols of the field sync segment. \p which_field is in [0,1]
175 * and specifies which field (duh).
177 * \p input_samples has (nsamples + ntaps() - 1) valid entries.
178 * input_samples[0] .. input_samples[nsamples - 1 + ntaps() - 1] may be
179 * referenced to compute the output values.
182 atsci_equalizer_lms2::filter_field_sync (const float *input_samples,
183 float *output_samples,
188 // Only the first 4 + 511 + 3 * 63 symbols are completely defined.
189 // Those after that the symbols are bilevel, so we could use decision feedback and use
190 // that to train, but for now, don't train on them.
192 int n = min (nsamples, get_field_sync_training_sequence_length (offset));
194 // handle known training sequence
195 adaptN (input_samples, get_field_sync_training_sequence (which_field, offset),
198 // just filter any unknown portion
200 filterN (&input_samples[n], &output_samples[n], nsamples - n);
202 if (offset == 0 && nsamples > 0){
203 for (int i = 0; i < NFFTAPS; i++)
204 fprintf(trainingfile,"%f ",d_taps_ff[i]);
205 for (int i = 0; i < NFBTAPS; i++)
206 fprintf(trainingfile,"%f ",d_taps_fb[i]);
207 fprintf (trainingfile,"\n");
212 // ----------------------------------------------------------------
215 // filter a single output
218 atsci_equalizer_lms2::filter1 (const float input[])
220 static const int N_UNROLL = 4;
230 unsigned n = (NFFTAPS / N_UNROLL) * N_UNROLL;
232 for (i = 0; i < n; i += N_UNROLL){
233 acc0 += d_taps_ff[i + 0] * input[i + 0];
234 acc1 += d_taps_ff[i + 1] * input[i + 1];
235 acc2 += d_taps_ff[i + 2] * input[i + 2];
236 acc3 += d_taps_ff[i + 3] * input[i + 3];
239 for (; i < (unsigned) NFFTAPS; i++)
240 acc0 += d_taps_ff[i] * input[i];
242 acc = (acc0 + acc1 + acc2 + acc3);
244 d_output_ptr = wrap (d_output_ptr + 1);
246 for (int i = 0; i < NFBTAPS; i++) {
247 acc -= d_taps_fb[i] * d_old_output[wrap(i + d_output_ptr)];
254 d_old_output[d_output_ptr] = slice (acc);
259 // filter and adapt a single output
267 atsci_equalizer_lms2::adapt1 (const float input[], float ideal_output)
269 static const double BETA = 0.00005; // FIXME figure out what this ought to be
270 // FIXME add gear-shifting
272 double y = filter1 (input);
273 double e = y - ideal_output;
276 for (int i = 0; i < NFFTAPS; i++){
277 d_taps_ff[i] = d_taps_ff[i] - BETA * e * (double)(input[i]);
280 for (int i = 0; i < NFBTAPS; i++){
281 // d_taps_fb[i] = d_taps_fb[i] - BETA * e * (double)d_old_output[wrap(i+d_output_ptr)];
282 d_taps_fb[i] = d_taps_fb[i] - kludge() * e * (double)d_old_output[wrap(i+d_output_ptr)];
289 atsci_equalizer_lms2::filterN (const float *input_samples,
290 float *output_samples,
293 for (int i = 0; i < nsamples; i++)
294 output_samples[i] = filter1 (&input_samples[i]);
299 atsci_equalizer_lms2::adaptN (const float *input_samples,
300 const float *training_pattern,
301 float *output_samples,
304 for (int i = 0; i < nsamples; i++)
305 output_samples[i] = adapt1 (&input_samples[i], training_pattern[i]);
308 // ----------------------------------------------------------------
313 return bit ? +5 : -5;
317 init_field_sync_common (float *p, int mask)
322 p[i++] = bin_map (1); // data segment sync pulse
323 p[i++] = bin_map (0);
324 p[i++] = bin_map (0);
325 p[i++] = bin_map (1);
327 for (int j = 0; j < 511; j++) // PN511
328 p[i++] = bin_map (atsc_pn511[j]);
330 for (int j = 0; j < 63; j++) // PN63
331 p[i++] = bin_map (atsc_pn63[j]);
333 for (int j = 0; j < 63; j++) // PN63, toggled on field 2
334 p[i++] = bin_map (atsc_pn63[j] ^ mask);
336 for (int j = 0; j < 63; j++) // PN63
337 p[i++] = bin_map (atsc_pn63[j]);
339 assert (i == KNOWN_FIELD_SYNC_LENGTH);
344 get_data_seg_sync_training_sequence (int offset)
346 static const float training_data[4] = { +5, -5, -5, +5 };
347 return &training_data[offset];
352 get_field_sync_training_sequence_length (int offset)
354 return max (0, KNOWN_FIELD_SYNC_LENGTH - offset);
358 get_field_sync_training_sequence (int which_field, int offset)
360 static float *field_1 = 0;
361 static float *field_2 = 0;
364 field_1 = new float[KNOWN_FIELD_SYNC_LENGTH];
365 field_2 = new float[KNOWN_FIELD_SYNC_LENGTH];
366 init_field_sync_common (field_1, 0);
367 init_field_sync_common (field_2, 1);
370 if (which_field == 0)
371 return &field_1[offset];
373 return &field_2[offset];