/* -*- c++ -*- */ /* * Copyright 2002,2008 Free Software Foundation, Inc. * * This file is part of GNU Radio * * GNU Radio is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3, or (at your option) * any later version. * * GNU Radio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Radio; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include /* * ---------------------------------------------------------------- * Segment Integrator Pre-Processor * * Compute weight passed to integrator based on * correlation result and ncorr_gain2 */ inline static int sipp (bool digcorr_output) { if (digcorr_output) return +2; // positive correlation else return -1; // no correlation } /* * ---------------------------------------------------------------- * Segment Sync Integrator... */ static const int SSI_MIN = -16; static const int SSI_MAX = 15; void sssr::seg_sync_integrator::reset () { for (int i = 0; i < ATSC_DATA_SEGMENT_LENGTH; i++) d_integrator[i] = SSI_MIN; } int sssr::seg_sync_integrator::update (int weight, int index) { int t = d_integrator[index] + weight; t = std::max (t, SSI_MIN); t = std::min (t, SSI_MAX); d_integrator[index] = t; return t; } int sssr::seg_sync_integrator::find_max (int *v) { int best_value = d_integrator[0]; int best_index = 0; for (int i = 1; i < ATSC_DATA_SEGMENT_LENGTH; i++) if (d_integrator[i] > best_value){ best_value = d_integrator[i]; best_index = i; } *v = best_value; return best_index; } /* * ---------------------------------------------------------------- * Segment Sync and Symbol recovery */ static const int SYMBOL_INDEX_OFFSET = 3; static const int MIN_SEG_LOCK_CORRELATION_VALUE = 5; atsci_sssr::atsci_sssr () : d_debug_fp(0) { reset (); if (_SSSR_DIAG_OUTPUT_){ const char *file = "sssr.ssout"; if ((d_debug_fp = fopen (file, "wb")) == 0){ perror (file); exit (1); } } } atsci_sssr::~atsci_sssr () { if (d_debug_fp) fclose (d_debug_fp); } void atsci_sssr::reset () { d_correlator.reset (); d_integrator.reset (); d_quad_filter.reset (); for (int i = 0; i < ATSC_DATA_SEGMENT_LENGTH; i++) d_quad_output[i] = 0; d_timing_adjust = 0; d_counter = 0; d_symbol_index = 0; d_seg_locked = false; } void atsci_sssr::update (sssr::sample_t sample_in, // input bool *seg_locked, // are we seeing segment syncs? int *symbol_index, // 0..831 double *timing_adjust) // how much to adjust timing { double qo = d_quad_filter.update (sample_in); d_quad_output[d_counter] = qo; int bit = boost::math::signbit (sample_in) ^ 1; // slice on sign: + => 1, - => 0 int corr_out = d_correlator.update (bit); int weight = sipp (corr_out); int corr_value = d_integrator.update (weight, d_counter); int best_correlation_index = -1; incr_symbol_index (); if (incr_counter ()){ // counter just wrapped... int best_correlation_value; best_correlation_index = d_integrator.find_max (&best_correlation_value); d_seg_locked = best_correlation_value >= MIN_SEG_LOCK_CORRELATION_VALUE; d_timing_adjust = d_quad_output[best_correlation_index]; d_symbol_index = SYMBOL_INDEX_OFFSET - 1 - best_correlation_index; if (d_symbol_index < 0) d_symbol_index += ATSC_DATA_SEGMENT_LENGTH; } *seg_locked = d_seg_locked; *symbol_index = d_symbol_index; *timing_adjust = d_timing_adjust; if (_SSSR_DIAG_OUTPUT_){ float iodata[7]; iodata[0] = bit; iodata[1] = corr_value; iodata[2] = qo; iodata[3] = d_counter; iodata[4] = d_symbol_index; iodata[5] = best_correlation_index; iodata[6] = d_timing_adjust; if (fwrite (iodata, sizeof (iodata), 1, d_debug_fp) != 1){ perror ("fwrite: sssr"); exit (1); } } } /* * ---------------------------------------------------------------- * Interpolator control for Seg & Symbol Sync Recovery */ static const double LOOP_FILTER_TAP = 0.00025; // 0.0005 works static const double ADJUSTMENT_GAIN = 1.0e-5 / (10 * ATSC_DATA_SEGMENT_LENGTH); atsci_interpolator::atsci_interpolator (double nominal_ratio_of_rx_clock_to_symbol_freq) : d_debug_fp(0) { // Tweaked ratio from 1.8 to 1.78 to support input rate of 19.2MHz assert (nominal_ratio_of_rx_clock_to_symbol_freq >= 1.78); d_nominal_ratio_of_rx_clock_to_symbol_freq = nominal_ratio_of_rx_clock_to_symbol_freq; d_loop.set_taps (LOOP_FILTER_TAP); reset (); if (_SSSR_DIAG_OUTPUT_){ const char *file = "interp.ssout"; if ((d_debug_fp = fopen (file, "wb")) == 0){ perror (file); exit (1); } } } atsci_interpolator::~atsci_interpolator () { if (d_debug_fp) fclose (d_debug_fp); } void atsci_interpolator::reset () { d_w = d_nominal_ratio_of_rx_clock_to_symbol_freq; d_mu = 0.5; d_incr = 0; d_loop.reset (); } bool atsci_interpolator::update ( const sssr::sample_t input_samples[], // I: vector of samples int nsamples, // I: total number of samples int *index, // I/O: current input index double timing_adjustment, // I: how much to bump timing sssr::sample_t *output_sample) { if (*index + (int) ntaps () > nsamples) return false; // FIXME Confirm that this is right. I think it is. It was (1-d_mu) *output_sample = d_interp.interpolate (&input_samples[*index], d_mu); double filter_out = 0; #if 0 filter_out = d_loop.filter (timing_adjustment); d_w = d_w + ADJUSTMENT_GAIN * filter_out * 1e-3; #elif 1 filter_out = d_loop.filter (timing_adjustment); d_mu = d_mu + ADJUSTMENT_GAIN * 10e3 * filter_out; #else static const double alpha = 0.01; static const double beta = alpha * alpha / 16; double x = ADJUSTMENT_GAIN * 10e3 * timing_adjustment; d_mu = d_mu + alpha * x; // conceptually "phase" d_w = d_w + beta * x; // conceptually "frequency" #endif double s = d_mu + d_w; double float_incr = floor (s); d_mu = s - float_incr; d_incr = (int) float_incr; assert (d_incr >= 1 && d_incr <= 3); *index += d_incr; if (_SSSR_DIAG_OUTPUT_){ float iodata[6]; iodata[0] = timing_adjustment; iodata[1] = filter_out; iodata[2] = d_w; iodata[3] = d_mu; iodata[4] = d_incr; iodata[5] = *output_sample; if (fwrite (iodata, sizeof (iodata), 1, d_debug_fp) != 1){ perror ("fwrite: interpolate"); exit (1); } } return true; }