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