GNU Radio 3.5.1 C++ API
|
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 // Member functions related to the symbol tracking portion of the receiver 00095 //! (M&M) Returns current value of mu 00096 float mu() const { return d_mu;} 00097 00098 //! (M&M) Returns current value of omega 00099 float omega() const { return d_omega;} 00100 00101 //! (M&M) Returns mu gain factor 00102 float gain_mu() const { return d_gain_mu;} 00103 00104 //! (M&M) Returns omega gain factor 00105 float gain_omega() const { return d_gain_omega;} 00106 00107 //! (M&M) Sets value of mu 00108 void set_mu (float mu) { d_mu = mu; } 00109 00110 //! (M&M) Sets value of omega and its min and max values 00111 void set_omega (float omega) { 00112 d_omega = omega; 00113 d_min_omega = omega*(1.0 - d_omega_rel); 00114 d_max_omega = omega*(1.0 + d_omega_rel); 00115 d_omega_mid = 0.5*(d_min_omega+d_max_omega); 00116 } 00117 00118 //! (M&M) Sets value for mu gain factor 00119 void set_gain_mu (float gain_mu) { d_gain_mu = gain_mu; } 00120 00121 //! (M&M) Sets value for omega gain factor 00122 void set_gain_omega (float gain_omega) { d_gain_omega = gain_omega; } 00123 00124 protected: 00125 00126 /*! 00127 * \brief Constructor to synchronize incoming M-PSK symbols 00128 * 00129 * \param M modulation order of the M-PSK modulation 00130 * \param theta any constant phase rotation from the real axis of the constellation 00131 * \param loop_bw Loop bandwidth to set gains of phase/freq tracking loop 00132 * \param fmin minimum normalized frequency value the loop can achieve 00133 * \param fmax maximum normalized frequency value the loop can achieve 00134 * \param mu initial parameter for the interpolator [0,1] 00135 * \param gain_mu gain parameter of the M&M error signal to adjust mu (~0.05) 00136 * \param omega initial value for the number of symbols between samples (~number of samples/symbol) 00137 * \param gain_omega gain parameter to adjust omega based on the error (~omega^2/4) 00138 * \param omega_rel sets the maximum (omega*(1+omega_rel)) and minimum (omega*(1+omega_rel)) omega (~0.005) 00139 * 00140 * The constructor also chooses which phase detector and decision maker to use in the work loop based on the 00141 * value of M. 00142 */ 00143 digital_mpsk_receiver_cc (unsigned int M, float theta, 00144 float loop_bw, 00145 float fmin, float fmax, 00146 float mu, float gain_mu, 00147 float omega, float gain_omega, float omega_rel); 00148 00149 void make_constellation(); 00150 void mm_sampler(const gr_complex symbol); 00151 void mm_error_tracking(gr_complex sample); 00152 void phase_error_tracking(gr_complex sample); 00153 00154 00155 /*! 00156 * \brief Phase error detector for MPSK modulations. 00157 * 00158 * \param sample the I&Q sample from which to determine the phase error 00159 * 00160 * This function determines the phase error for any MPSK signal by 00161 * creating a set of PSK constellation points and doing a 00162 * brute-force search to see which point minimizes the Euclidean 00163 * distance. This point is then used to derotate the sample to the 00164 * real-axis and a atan (using the fast approximation function) to 00165 * determine the phase difference between the incoming sample and 00166 * the real constellation point 00167 * 00168 * This should be cleaned up and made more efficient. 00169 * 00170 * \returns the approximated phase error. 00171 */ 00172 float phase_error_detector_generic(gr_complex sample) const; // generic for M but more costly 00173 00174 /*! 00175 * \brief Phase error detector for BPSK modulation. 00176 * 00177 * \param sample the I&Q sample from which to determine the phase error 00178 * 00179 * This function determines the phase error using a simple BPSK 00180 * phase error detector by multiplying the real and imaginary (the 00181 * error signal) components together. As the imaginary part goes to 00182 * 0, so does this error. 00183 * 00184 * \returns the approximated phase error. 00185 */ 00186 float phase_error_detector_bpsk(gr_complex sample) const; // optimized for BPSK 00187 00188 /*! 00189 * \brief Phase error detector for QPSK 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 the limiter 00194 * approach in a standard 4th order Costas loop 00195 * 00196 * \returns the approximated phase error. 00197 */ 00198 float phase_error_detector_qpsk(gr_complex sample) const; 00199 00200 00201 00202 /*! 00203 * \brief Decision maker for a generic MPSK constellation. 00204 * 00205 * \param sample the baseband I&Q sample from which to make the decision 00206 * 00207 * This decision maker is a generic implementation that does a 00208 * brute-force search for the constellation point that minimizes the 00209 * error between it and the incoming signal. 00210 * 00211 * \returns the index to d_constellation that minimizes the error/ 00212 */ 00213 unsigned int decision_generic(gr_complex sample) const; 00214 00215 00216 /*! 00217 * \brief Decision maker for BPSK constellation. 00218 * 00219 * \param sample the baseband I&Q sample from which to make the decision 00220 * 00221 * This decision maker is a simple slicer function that makes a 00222 * decision on the symbol based on its placement on the real axis of 00223 * greater than 0 or less than 0; the quadrature component is always 00224 * 0. 00225 * 00226 * \returns the index to d_constellation that minimizes the error/ 00227 */ 00228 unsigned int decision_bpsk(gr_complex sample) const; 00229 00230 00231 /*! 00232 * \brief Decision maker for QPSK constellation. 00233 * 00234 * \param sample the baseband I&Q sample from which to make the decision 00235 * 00236 * This decision maker is a simple slicer function that makes a 00237 * decision on the symbol based on its placement versus both axes 00238 * and returns which quadrant the symbol is in. 00239 * 00240 * \returns the index to d_constellation that minimizes the error/ 00241 */ 00242 unsigned int decision_qpsk(gr_complex sample) const; 00243 00244 private: 00245 unsigned int d_M; 00246 float d_theta; 00247 00248 /*! 00249 * \brief Decision maker function pointer 00250 * 00251 * \param sample the baseband I&Q sample from which to make the decision 00252 * 00253 * This is a function pointer that is set in the constructor to 00254 * point to the proper decision function for the specified 00255 * constellation order. 00256 * 00257 * \return index into d_constellation point that is the closest to the recieved sample 00258 */ 00259 unsigned int (digital_mpsk_receiver_cc::*d_decision)(gr_complex sample) const; // pointer to decision function 00260 00261 00262 std::vector<gr_complex> d_constellation; 00263 unsigned int d_current_const_point; 00264 00265 // Members related to symbol timing 00266 float d_mu, d_gain_mu; 00267 float d_omega, d_gain_omega, d_omega_rel, d_max_omega, d_min_omega, d_omega_mid; 00268 gr_complex d_p_2T, d_p_1T, d_p_0T; 00269 gr_complex d_c_2T, d_c_1T, d_c_0T; 00270 00271 /*! 00272 * \brief Phase error detector function pointer 00273 * 00274 * \param sample the I&Q sample from which to determine the phase error 00275 * 00276 * This is a function pointer that is set in the constructor to 00277 * point to the proper phase error detector function for the 00278 * specified constellation order. 00279 */ 00280 float (digital_mpsk_receiver_cc::*d_phase_error_detector)(gr_complex sample) const; 00281 00282 00283 //! get interpolated value 00284 gri_mmse_fir_interpolator_cc *d_interp; 00285 00286 //! delay line length. 00287 static const unsigned int DLLEN = 8; 00288 00289 //! delay line plus some length for overflow protection 00290 __GR_ATTR_ALIGNED(8) gr_complex d_dl[2*DLLEN]; 00291 00292 //! index to delay line 00293 unsigned int d_dl_idx; 00294 00295 friend DIGITAL_API digital_mpsk_receiver_cc_sptr 00296 digital_make_mpsk_receiver_cc (unsigned int M, float theta, 00297 float loop_bw, 00298 float fmin, float fmax, 00299 float mu, float gain_mu, 00300 float omega, float gain_omega, float omega_rel); 00301 }; 00302 00303 #endif