From 2863f40d5d0bca8713252bb3618f9844fccf673c Mon Sep 17 00:00:00 2001
From: Ben Reynwar <ben@reynwar.net>
Date: Thu, 9 Dec 2010 15:12:14 -0700
Subject: Added support for modulation/demodulation of a generic constellation.
 Not yet robust enough. Inefficient QAM modulation/demodulation also added
 (via the generic implementation).

---
 gnuradio-core/src/lib/general/Makefile.am          |   5 +-
 gnuradio-core/src/lib/general/general.i            |   2 +
 .../lib/general/gr_constellation_receiver_cb.cc    | 146 +++++++++++++++++
 .../src/lib/general/gr_constellation_receiver_cb.h | 172 +++++++++++++++++++++
 .../src/lib/general/gr_constellation_receiver_cb.i |  54 +++++++
 5 files changed, 377 insertions(+), 2 deletions(-)
 create mode 100644 gnuradio-core/src/lib/general/gr_constellation_receiver_cb.cc
 create mode 100644 gnuradio-core/src/lib/general/gr_constellation_receiver_cb.h
 create mode 100644 gnuradio-core/src/lib/general/gr_constellation_receiver_cb.i

(limited to 'gnuradio-core/src/lib')

diff --git a/gnuradio-core/src/lib/general/Makefile.am b/gnuradio-core/src/lib/general/Makefile.am
index 3d8a42805c..daa3c446ef 100644
--- a/gnuradio-core/src/lib/general/Makefile.am
+++ b/gnuradio-core/src/lib/general/Makefile.am
@@ -100,6 +100,7 @@ libgeneral_la_SOURCES = 		\
 	gr_math.cc			\
 	gr_misc.cc			\
 	gr_mpsk_receiver_cc.cc		\
+	gr_constellation_receiver_cb.cc \
 	gr_nlog10_ff.cc			\
 	gr_nop.cc			\
 	gr_null_sink.cc			\
@@ -256,7 +257,7 @@ grinclude_HEADERS = 			\
 	gr_map_bb.h			\
 	gr_math.h			\
 	gr_misc.h			\
-	gr_mpsk_receiver_cc.h		\
+	gr_constellation_receiver_cb.h	\
 	gr_nco.h			\
 	gr_nlog10_ff.h			\
 	gr_nop.h			\
@@ -418,7 +419,7 @@ swiginclude_HEADERS =			\
 	gr_lms_dfe_cc.i			\
 	gr_lms_dfe_ff.i			\
 	gr_map_bb.i			\
-	gr_mpsk_receiver_cc.i		\
+	gr_constellation_receiver_cb.i	\
 	gr_nlog10_ff.i			\
 	gr_nop.i			\
 	gr_null_sink.i			\
diff --git a/gnuradio-core/src/lib/general/general.i b/gnuradio-core/src/lib/general/general.i
index 68cafce2e6..f758211cc6 100644
--- a/gnuradio-core/src/lib/general/general.i
+++ b/gnuradio-core/src/lib/general/general.i
@@ -69,6 +69,7 @@
 #include <gr_fake_channel_coder_pp.h>
 #include <gr_throttle.h>
 #include <gr_mpsk_receiver_cc.h>
+#include <gr_constellation_receiver_cb.h>
 #include <gr_stream_mux.h>
 #include <gr_stream_to_streams.h>
 #include <gr_streams_to_stream.h>
@@ -190,6 +191,7 @@
 %include "gr_fake_channel_coder_pp.i"
 %include "gr_throttle.i"
 %include "gr_mpsk_receiver_cc.i"
+%include "gr_constellation_receiver_cb.i"
 %include "gr_stream_mux.i"
 %include "gr_stream_to_streams.i"
 %include "gr_streams_to_stream.i"
diff --git a/gnuradio-core/src/lib/general/gr_constellation_receiver_cb.cc b/gnuradio-core/src/lib/general/gr_constellation_receiver_cb.cc
new file mode 100644
index 0000000000..0a2309ac0a
--- /dev/null
+++ b/gnuradio-core/src/lib/general/gr_constellation_receiver_cb.cc
@@ -0,0 +1,146 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2005,2006,2007,2010 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gr_io_signature.h>
+#include <gr_prefs.h>
+#include <gr_constellation_receiver_cb.h>
+#include <stdexcept>
+#include <gr_math.h>
+#include <gr_expj.h>
+// For debugging
+#include <iostream>
+
+
+#define M_TWOPI (2*M_PI)
+#define VERBOSE_MM     0     // Used for debugging symbol timing loop
+#define VERBOSE_COSTAS 0     // Used for debugging phase and frequency tracking
+
+// Public constructor
+
+gr_constellation_receiver_cb_sptr 
+gr_make_constellation_receiver_cb(gr_constellation constell,
+				  float alpha, float beta,
+				  float fmin, float fmax)
+{
+  return gnuradio::get_initial_sptr(new gr_constellation_receiver_cb (constell, 
+								      alpha, beta,
+								      fmin, fmax));
+}
+
+gr_constellation_receiver_cb::gr_constellation_receiver_cb (gr_constellation constellation, 
+							    float alpha, float beta,
+							    float fmin, float fmax)
+  : gr_block ("constellation_receiver_cb",
+	      gr_make_io_signature (1, 1, sizeof (gr_complex)),
+	      gr_make_io_signature (1, 1, sizeof (unsigned char))),
+    d_constellation(constellation), 
+    d_alpha(alpha), d_beta(beta), d_freq(0), d_max_freq(fmax), d_min_freq(fmin), d_phase(0),
+    d_current_const_point(0)
+{}
+
+void
+gr_constellation_receiver_cb::phase_error_tracking(float phase_error)
+{
+    
+  d_freq += d_beta*phase_error;             // adjust frequency based on error
+  d_phase += d_freq + d_alpha*phase_error;  // adjust phase based on error
+
+  // Make sure we stay within +-2pi
+  while(d_phase > M_TWOPI)
+    d_phase -= M_TWOPI;
+  while(d_phase < -M_TWOPI)
+    d_phase += M_TWOPI;
+  
+  // Limit the frequency range
+  d_freq = gr_branchless_clip(d_freq, d_max_freq);
+  
+#if VERBOSE_COSTAS
+  printf("cl: phase_error: %f  phase: %f  freq: %f  sample: %f+j%f  constellation: %f+j%f\n",
+	 phase_error, d_phase, d_freq, sample.real(), sample.imag(), 
+	 d_constellation.constellation()[d_current_const_point].real(), d_constellation.constellation()[d_current_const_point].imag());
+#endif
+}
+
+int
+gr_constellation_receiver_cb::general_work (int noutput_items,
+					    gr_vector_int &ninput_items,
+					    gr_vector_const_void_star &input_items,
+					    gr_vector_void_star &output_items)
+{
+  const gr_complex *in = (const gr_complex *) input_items[0];
+  unsigned char *out = (unsigned char *) output_items[0];
+
+  int i=0, o=0;
+
+  float phase_error;
+  unsigned int sym_value;
+  gr_complex sample;
+
+  while((o < noutput_items) && (i < ninput_items[0])) {
+    sample = in[i];
+    sym_value = d_constellation.decision_maker(sample);
+    //std::cout << "sym_value: " << sym_value << " sample: " << sample << std::endl;
+    phase_error = -arg(sample*conj(d_constellation.constellation()[sym_value]));
+    phase_error_tracking(phase_error);  // corrects phase and frequency offsets
+    out[o++] = sym_value;
+    i++;
+  }
+
+  #if 0
+  printf("ninput_items: %d   noutput_items: %d   consuming: %d   returning: %d\n",
+	 ninput_items[0], noutput_items, i, o);
+  #endif
+
+  consume_each(i);
+  return o;
+}
+
+// Base Constellation Class
+
+gr_constellation::gr_constellation (std::vector<gr_complex> constellation) {
+  d_constellation = constellation;
+}
+
+// Chooses points base on shortest distance.
+// Inefficient.
+unsigned int gr_constellation::decision_maker(gr_complex sample)
+{
+  unsigned int table_size = constellation().size();
+  unsigned int min_index = 0;
+  float min_euclid_dist;
+  float euclid_dist;
+    
+  min_euclid_dist = norm(sample - constellation()[0]); 
+  min_index = 0; 
+  for (unsigned int j = 1; j < table_size; j++){
+    euclid_dist = norm(sample - constellation()[j]);
+    if (euclid_dist < min_euclid_dist){
+      min_euclid_dist = euclid_dist;
+      min_index = j;
+    }
+  }
+  return min_index;
+}
diff --git a/gnuradio-core/src/lib/general/gr_constellation_receiver_cb.h b/gnuradio-core/src/lib/general/gr_constellation_receiver_cb.h
new file mode 100644
index 0000000000..ba38bdad60
--- /dev/null
+++ b/gnuradio-core/src/lib/general/gr_constellation_receiver_cb.h
@@ -0,0 +1,172 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2004,2007,2010 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.
+ */
+
+#ifndef INCLUDED_GR_CONSTELLATION_RECEIVER_CB_H
+#define	INCLUDED_GR_CONSTELLATION_RECEIVER_CB_H
+
+#include <gr_block.h>
+#include <gr_complex.h>
+#include <math.h>
+#include <fstream>
+
+class gr_constellation
+{
+ public:
+
+  gr_constellation (std::vector<gr_complex> constellation);
+  
+  //! Returns the set of points in this constellation.
+  std::vector<gr_complex> constellation() { return d_constellation;}
+  
+  //! Returns the constellation point that matches best.
+  //! Also calculates the phase error.
+  unsigned int decision_maker (gr_complex sample);
+  
+  unsigned int bits_per_symbol () {
+    return floor(log(d_constellation.size())/log(2));
+  }
+  
+ private:
+
+  std::vector<gr_complex> d_constellation;
+};
+
+class gri_mmse_fir_interpolator_cc;
+
+class gr_constellation_receiver_cb;
+typedef boost::shared_ptr<gr_constellation_receiver_cb> gr_constellation_receiver_cb_sptr;
+
+// public constructor
+gr_constellation_receiver_cb_sptr 
+gr_make_constellation_receiver_cb (gr_constellation constellation,
+				   float alpha, float beta,
+				   float fmin, float fmax);
+
+/*!
+ * \brief This block takes care of receiving generic modulated signals through phase, frequency, and symbol
+ * synchronization. 
+ * \ingroup sync_blk
+ * \ingroup demod_blk
+ *
+ * This block takes care of receiving generic modulated signals through phase, frequency, and symbol
+ * synchronization. It performs carrier frequency and phase locking as well as symbol timing recovery. 
+ *
+ * The phase and frequency synchronization are based on a Costas loop that finds the error of the incoming
+ * signal point compared to its nearest constellation point. The frequency and phase of the NCO are 
+ * updated according to this error.
+ *
+ * The symbol synchronization is done using a modified Mueller and Muller circuit from the paper:
+ * 
+ *    G. R. Danesfahani, T.G. Jeans, "Optimisation of modified Mueller and Muller 
+ *    algorithm,"  Electronics Letters, Vol. 31, no. 13,  22 June 1995, pp. 1032 - 1033.
+ *
+ * This circuit interpolates the downconverted sample (using the NCO developed by the Costas loop)
+ * every mu samples, then it finds the sampling error based on this and the past symbols and the decision
+ * made on the samples. Like the phase error detector, there are optimized decision algorithms for BPSK
+ * and QPKS, but 8PSK uses another brute force computation against all possible symbols. The modifications
+ * to the M&M used here reduce self-noise.
+ *
+ */
+
+class gr_constellation_receiver_cb : public gr_block
+{
+ public:
+  int general_work (int noutput_items,
+		    gr_vector_int &ninput_items,
+		    gr_vector_const_void_star &input_items,
+		    gr_vector_void_star &output_items);
+
+
+  // Member function related to the phase/frequency tracking portion of the receiver
+  //! (CL) Returns the value for alpha (the phase gain term)
+  float alpha() const { return d_alpha; }
+  
+  //! (CL) Returns the value of beta (the frequency gain term)
+  float beta() const { return d_beta; }
+
+  //! (CL) Returns the current value of the frequency of the NCO in the Costas loop
+  float freq() const { return d_freq; }
+
+  //! (CL) Returns the current value of the phase of the NCO in the Costal loop
+  float phase() const { return d_phase; }
+
+  //! (CL) Sets the value for alpha (the phase gain term)
+  void set_alpha(float alpha) { d_alpha = alpha; }
+  
+  //! (CL) Setss the value of beta (the frequency gain term)
+  void set_beta(float beta) { d_beta = beta; }
+
+  //! (CL) Sets the current value of the frequency of the NCO in the Costas loop
+  void set_freq(float freq) { d_freq = freq; }
+
+  //! (CL) Setss the current value of the phase of the NCO in the Costal loop
+  void set_phase(float phase) { d_phase = phase; }
+
+
+protected:
+
+ /*!
+   * \brief Constructor to synchronize incoming M-PSK symbols
+   *
+   * \param constellation	constellation of points for generic modulation
+   * \param alpha	gain parameter to adjust the phase in the Costas loop (~0.01)
+   * \param beta        gain parameter to adjust the frequency in the Costas loop (~alpha^2/4)	
+   * \param fmin        minimum normalized frequency value the loop can achieve
+   * \param fmax        maximum normalized frequency value the loop can achieve
+   *
+   * The constructor also chooses which phase detector and decision maker to use in the work loop based on the
+   * value of M.
+   */
+  gr_constellation_receiver_cb (gr_constellation constellation, 
+				float alpha, float beta,
+				float fmin, float fmax);
+
+  void phase_error_tracking(float phase_error);
+
+  private:
+  unsigned int d_M;
+
+  // Members related to carrier and phase tracking
+  float d_alpha;
+  float d_beta;
+  float d_freq, d_max_freq, d_min_freq;
+  float d_phase;
+
+  gr_constellation d_constellation;
+  unsigned int d_current_const_point;
+
+  //! delay line length.
+  static const unsigned int DLLEN = 8;
+  
+  //! delay line plus some length for overflow protection
+  gr_complex d_dl[2*DLLEN] __attribute__ ((aligned(8)));
+  
+  //! index to delay line
+  unsigned int d_dl_idx;
+
+  friend gr_constellation_receiver_cb_sptr
+  gr_make_constellation_receiver_cb (gr_constellation constell,
+				     float alpha, float beta,
+				     float fmin, float fmax);
+};
+
+#endif
diff --git a/gnuradio-core/src/lib/general/gr_constellation_receiver_cb.i b/gnuradio-core/src/lib/general/gr_constellation_receiver_cb.i
new file mode 100644
index 0000000000..b48322f456
--- /dev/null
+++ b/gnuradio-core/src/lib/general/gr_constellation_receiver_cb.i
@@ -0,0 +1,54 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2004 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.
+ */
+
+GR_SWIG_BLOCK_MAGIC(gr,constellation_receiver_cb);
+
+%template(gr_complex_vector) std::vector<gr_complex>;
+
+class gr_constellation
+{
+public:
+  gr_constellation (std::vector<gr_complex> constellation);
+  std::vector<gr_complex> constellation();
+  unsigned int decision_maker (gr_complex sample);  
+  unsigned int bits_per_symbol ();
+};
+
+gr_constellation_receiver_cb_sptr gr_make_constellation_receiver_cb (gr_constellation constellation,
+								     float alpha, float beta,
+								     float fmin, float fmax);
+class gr_constellation_receiver_cb : public gr_block
+{
+ private:
+  gr_constellation_receiver_cb (gr_contellation constellation,
+				float alpha, float beta,
+				float fmin, float fmax);
+public:
+  float alpha() const { return d_alpha; }
+  float beta() const { return d_beta; }
+  float freq() const { return d_freq; }
+  float phase() const { return d_phase; }
+  void set_alpha(float alpha) { d_alpha = alpha; }
+  void set_beta(float beta) { d_beta = beta; }
+  void set_freq(float freq) { d_freq = freq; }
+  void set_phase(float phase) { d_phase = phase; } 
+};
-- 
cgit v1.2.3