GNU Radio 3.6.5 C++ API

digital_mpsk_receiver_cc.h

Go to the documentation of this file.
00001 /* -*- c++ -*- */
00002 /*
00003  * Copyright 2004,2007,2011 Free Software Foundation, Inc.
00004  *
00005  * This file is part of GNU Radio
00006  *
00007  * GNU Radio is free software; you can redistribute it and/or modify
00008  * it under the terms of the GNU General Public License as published by
00009  * the Free Software Foundation; either version 3, or (at your option)
00010  * any later version.
00011  *
00012  * GNU Radio is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with GNU Radio; see the file COPYING.  If not, write to
00019  * the Free Software Foundation, Inc., 51 Franklin Street,
00020  * Boston, MA 02110-1301, USA.
00021  */
00022 
00023 #ifndef INCLUDED_DIGITAL_MPSK_RECEIVER_CC_H
00024 #define INCLUDED_DIGITAL_MPSK_RECEIVER_CC_H
00025 
00026 #include <digital_api.h>
00027 #include <gruel/attributes.h>
00028 #include <gri_control_loop.h>
00029 #include <gr_block.h>
00030 #include <gr_complex.h>
00031 #include <fstream>
00032 
00033 class gri_mmse_fir_interpolator_cc;
00034 
00035 class digital_mpsk_receiver_cc;
00036 typedef boost::shared_ptr<digital_mpsk_receiver_cc> digital_mpsk_receiver_cc_sptr;
00037 
00038 // public constructor
00039 DIGITAL_API digital_mpsk_receiver_cc_sptr 
00040 digital_make_mpsk_receiver_cc (unsigned int M, float theta, 
00041                                float loop_bw,
00042                                float fmin, float fmax,
00043                                float mu, float gain_mu, 
00044                                float omega, float gain_omega, float omega_rel);
00045 
00046 /*!
00047  * \brief This block takes care of receiving M-PSK modulated signals
00048  * through phase, frequency, and symbol synchronization.
00049  * \ingroup synchronizers_blk
00050  *
00051  * \details
00052  * This block takes care of receiving M-PSK modulated signals through
00053  * phase, frequency, and symbol synchronization. It performs carrier
00054  * frequency and phase locking as well as symbol timing recovery.  It
00055  * works with (D)BPSK, (D)QPSK, and (D)8PSK as tested currently. It
00056  * should also work for OQPSK and PI/4 DQPSK.
00057  *
00058  * The phase and frequency synchronization are based on a Costas loop
00059  * that finds the error of the incoming signal point compared to its
00060  * nearest constellation point. The frequency and phase of the NCO are
00061  * updated according to this error. There are optimized phase error
00062  * detectors for BPSK and QPSK, but 8PSK is done using a brute-force
00063  * computation of the constellation points to find the minimum.
00064  *
00065  * The symbol synchronization is done using a modified Mueller and
00066  * Muller circuit from the paper:
00067  *
00068  * "G. R. Danesfahani, T. G. Jeans, "Optimisation of modified Mueller
00069  * and Muller algorithm," Electronics Letters, Vol. 31, no. 13, 22
00070  * June 1995, pp. 1032 - 1033."
00071  *
00072  * This circuit interpolates the downconverted sample (using the NCO
00073  * developed by the Costas loop) every mu samples, then it finds the
00074  * sampling error based on this and the past symbols and the decision
00075  * made on the samples. Like the phase error detector, there are
00076  * optimized decision algorithms for BPSK and QPKS, but 8PSK uses
00077  * another brute force computation against all possible symbols. The
00078  * modifications to the M&M used here reduce self-noise.
00079  */
00080 class DIGITAL_API digital_mpsk_receiver_cc : public gr_block, public gri_control_loop
00081 {
00082  public:
00083   ~digital_mpsk_receiver_cc ();
00084   void forecast(int noutput_items, gr_vector_int &ninput_items_required);
00085   int general_work (int noutput_items,
00086                     gr_vector_int &ninput_items,
00087                     gr_vector_const_void_star &input_items,
00088                     gr_vector_void_star &output_items);
00089 
00090 
00091   //! Returns the modulation order (M) currently set
00092   float modulation_order() const { return d_M; }
00093 
00094   //! Returns current value of theta
00095   float theta() const { return d_theta; }
00096 
00097   //! Returns current value of mu
00098   float mu() const { return d_mu; }
00099 
00100   //! Returns current value of omega
00101   float omega() const { return d_omega; }
00102 
00103   //! Returns mu gain factor
00104   float gain_mu() const { return d_gain_mu; }
00105 
00106   //! Returns omega gain factor
00107   float gain_omega() const { return d_gain_omega; }
00108 
00109   //! Returns the relative omega limit
00110   float gain_omega_rel() const {return d_omega_rel; }
00111 
00112   //! Sets the modulation order (M) currently
00113   void set_modulation_order(unsigned int M);
00114 
00115   //! Sets value of theta
00116   void set_theta(float theta) { d_theta = theta; }
00117 
00118   //! Sets value of mu
00119   void set_mu (float mu) { d_mu = mu; }
00120   
00121   //! Sets value of omega and its min and max values 
00122   void set_omega (float omega) { 
00123     d_omega = omega;
00124     d_min_omega = omega*(1.0 - d_omega_rel);
00125     d_max_omega = omega*(1.0 + d_omega_rel);
00126     d_omega_mid = 0.5*(d_min_omega+d_max_omega);
00127   }
00128 
00129   //! Sets value for mu gain factor
00130   void set_gain_mu (float gain_mu) { d_gain_mu = gain_mu; }
00131 
00132   //! Sets value for omega gain factor
00133   void set_gain_omega (float gain_omega) { d_gain_omega = gain_omega; }
00134 
00135   //! Sets the relative omega limit and resets omega min/max values
00136   void set_gain_omega_rel(float omega_rel);
00137   
00138 protected:
00139   
00140   /*!
00141    * \brief Constructor to synchronize incoming M-PSK symbols
00142    *
00143    * \param M           modulation order of the M-PSK modulation
00144    * \param theta       any constant phase rotation from the real axis of the constellation
00145    * \param loop_bw     Loop bandwidth to set gains of phase/freq tracking loop
00146    * \param fmin        minimum normalized frequency value the loop can achieve
00147    * \param fmax        maximum normalized frequency value the loop can achieve
00148    * \param mu          initial parameter for the interpolator [0,1]
00149    * \param gain_mu     gain parameter of the M&M error signal to adjust mu (~0.05)
00150    * \param omega       initial value for the number of symbols between samples (~number of samples/symbol)
00151    * \param gain_omega  gain parameter to adjust omega based on the error (~omega^2/4)
00152    * \param omega_rel   sets the maximum (omega*(1+omega_rel)) and minimum (omega*(1+omega_rel)) omega (~0.005)
00153    *
00154    * The constructor also chooses which phase detector and decision maker to use in the work loop based on the
00155    * value of M.
00156    */
00157   digital_mpsk_receiver_cc (unsigned int M, float theta, 
00158                             float loop_bw,
00159                             float fmin, float fmax,
00160                             float mu, float gain_mu, 
00161                             float omega, float gain_omega, float omega_rel);
00162 
00163   void make_constellation();
00164   void mm_sampler(const gr_complex symbol);
00165   void mm_error_tracking(gr_complex sample);
00166   void phase_error_tracking(gr_complex sample);
00167 
00168 
00169   /*!
00170    * \brief Phase error detector for MPSK modulations.
00171    *
00172    * \param sample   the I&Q sample from which to determine the phase error
00173    *
00174    * This function determines the phase error for any MPSK signal by
00175    * creating a set of PSK constellation points and doing a
00176    * brute-force search to see which point minimizes the Euclidean
00177    * distance. This point is then used to derotate the sample to the
00178    * real-axis and a atan (using the fast approximation function) to
00179    * determine the phase difference between the incoming sample and
00180    * the real constellation point
00181    *
00182    * This should be cleaned up and made more efficient.
00183    *
00184    * \returns the approximated phase error.
00185    */
00186   float phase_error_detector_generic(gr_complex sample) const; // generic for M but more costly
00187 
00188   /*!
00189    * \brief Phase error detector for BPSK modulation.
00190    *
00191    * \param sample   the I&Q sample from which to determine the phase error
00192    *
00193    * This function determines the phase error using a simple BPSK
00194    * phase error detector by multiplying the real and imaginary (the
00195    * error signal) components together. As the imaginary part goes to
00196    * 0, so does this error.
00197    *
00198    * \returns the approximated phase error.
00199    */
00200   float phase_error_detector_bpsk(gr_complex sample) const;    // optimized for BPSK
00201 
00202   /*!
00203    * \brief Phase error detector for QPSK modulation.
00204    *
00205    * \param sample   the I&Q sample from which to determine the phase error
00206    *
00207    * This function determines the phase error using the limiter
00208    * approach in a standard 4th order Costas loop
00209    *
00210    * \returns the approximated phase error.
00211    */
00212   float phase_error_detector_qpsk(gr_complex sample) const;
00213 
00214 
00215 
00216   /*!
00217    * \brief Decision maker for a generic MPSK constellation.
00218    *
00219    * \param sample   the baseband I&Q sample from which to make the decision
00220    *
00221    * This decision maker is a generic implementation that does a
00222    * brute-force search for the constellation point that minimizes the
00223    * error between it and the incoming signal.
00224    *
00225    * \returns the index to d_constellation that minimizes the error/
00226  */
00227   unsigned int decision_generic(gr_complex sample) const;
00228 
00229 
00230  /*!
00231    * \brief Decision maker for BPSK constellation.
00232    *
00233    * \param sample   the baseband I&Q sample from which to make the decision
00234    *
00235    * This decision maker is a simple slicer function that makes a
00236    * decision on the symbol based on its placement on the real axis of
00237    * greater than 0 or less than 0; the quadrature component is always
00238    * 0.
00239    *
00240    * \returns the index to d_constellation that minimizes the error/
00241    */
00242   unsigned int decision_bpsk(gr_complex sample) const;
00243   
00244 
00245   /*!
00246    * \brief Decision maker for QPSK constellation.
00247    *
00248    * \param sample   the baseband I&Q sample from which to make the decision
00249    *
00250    * This decision maker is a simple slicer function that makes a
00251    * decision on the symbol based on its placement versus both axes
00252    * and returns which quadrant the symbol is in.
00253    *
00254    * \returns the index to d_constellation that minimizes the error/
00255    */
00256   unsigned int decision_qpsk(gr_complex sample) const;
00257 
00258 private:
00259   unsigned int d_M;
00260   float        d_theta;
00261 
00262   /*!
00263    * \brief Decision maker function pointer 
00264    *
00265    * \param sample   the baseband I&Q sample from which to make the decision
00266    *
00267    * This is a function pointer that is set in the constructor to
00268    * point to the proper decision function for the specified
00269    * constellation order.
00270    *
00271    * \return index into d_constellation point that is the closest to the recieved sample
00272    */
00273   unsigned int (digital_mpsk_receiver_cc::*d_decision)(gr_complex sample) const; // pointer to decision function
00274 
00275 
00276   std::vector<gr_complex> d_constellation;
00277   unsigned int d_current_const_point;
00278 
00279   // Members related to symbol timing
00280   float d_mu, d_gain_mu;
00281   float d_omega, d_gain_omega, d_omega_rel, d_max_omega, d_min_omega, d_omega_mid;
00282   gr_complex d_p_2T, d_p_1T, d_p_0T;
00283   gr_complex d_c_2T, d_c_1T, d_c_0T;
00284 
00285   /*!
00286    * \brief Phase error detector function pointer 
00287    *
00288    * \param sample   the I&Q sample from which to determine the phase error
00289    *
00290    * This is a function pointer that is set in the constructor to
00291    * point to the proper phase error detector function for the
00292    * specified constellation order.
00293    */
00294   float (digital_mpsk_receiver_cc::*d_phase_error_detector)(gr_complex sample) const;
00295 
00296 
00297   //! get interpolated value
00298   gri_mmse_fir_interpolator_cc  *d_interp;
00299   
00300   //! delay line length.
00301   static const unsigned int DLLEN = 8;
00302   
00303   //! delay line plus some length for overflow protection
00304   __GR_ATTR_ALIGNED(8) gr_complex d_dl[2*DLLEN];
00305   
00306   //! index to delay line
00307   unsigned int d_dl_idx;
00308 
00309   friend DIGITAL_API digital_mpsk_receiver_cc_sptr
00310   digital_make_mpsk_receiver_cc (unsigned int M, float theta,
00311                                  float loop_bw,
00312                                  float fmin, float fmax,
00313                                  float mu, float gain_mu, 
00314                                  float omega, float gain_omega, float omega_rel);
00315 };
00316 
00317 #endif