Merge branch 'dfsg-orig'
[debian/gnuradio] / gr-atsc / src / lib / atsci_sssr.cc
diff --git a/gr-atsc/src/lib/atsci_sssr.cc b/gr-atsc/src/lib/atsci_sssr.cc
new file mode 100644 (file)
index 0000000..dc5c01b
--- /dev/null
@@ -0,0 +1,290 @@
+/* -*- 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 <atsci_sssr.h>
+#include <algorithm>
+#include <cmath>
+#include <cstdio>
+#include <assert.h>
+#include <atsci_diag_output.h>
+#include <gr_math.h>
+#include <stdio.h>
+
+/*
+ * ----------------------------------------------------------------
+ * 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 = gr_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;
+}