diff options
author | Tom Rondeau <trondeau@vt.edu> | 2012-08-14 19:23:41 -0400 |
---|---|---|
committer | Tom Rondeau <trondeau@vt.edu> | 2012-08-14 19:23:41 -0400 |
commit | ab479e3d5b5d77eb4133dd06ea3ffc51551fccd6 (patch) | |
tree | aadbda22cf4c7dc3ff808948dc6dea8d9da4b983 /gr-digital | |
parent | 48a95b3469febf4929c74f41beac6b33ab77566e (diff) |
digital: converted mpsk_receiver to new style.
Diffstat (limited to 'gr-digital')
-rw-r--r-- | gr-digital/include/digital/CMakeLists.txt | 2 | ||||
-rw-r--r-- | gr-digital/include/digital/mpsk_receiver_cc.h | 147 | ||||
-rw-r--r-- | gr-digital/include/digital_mpsk_receiver_cc.h | 320 | ||||
-rw-r--r-- | gr-digital/lib/CMakeLists.txt | 2 | ||||
-rw-r--r-- | gr-digital/lib/digital_mpsk_receiver_cc.cc | 329 | ||||
-rw-r--r-- | gr-digital/lib/mpsk_receiver_cc_impl.cc | 331 | ||||
-rw-r--r-- | gr-digital/lib/mpsk_receiver_cc_impl.h | 244 | ||||
-rwxr-xr-x | gr-digital/python/qa_mpsk_receiver.py | 26 | ||||
-rw-r--r-- | gr-digital/swig/digital_mpsk_receiver_cc.i | 57 | ||||
-rw-r--r-- | gr-digital/swig/digital_swig.i | 3 |
10 files changed, 741 insertions, 720 deletions
diff --git a/gr-digital/include/digital/CMakeLists.txt b/gr-digital/include/digital/CMakeLists.txt index 2445a6fe37..27e151e0a5 100644 --- a/gr-digital/include/digital/CMakeLists.txt +++ b/gr-digital/include/digital/CMakeLists.txt @@ -104,7 +104,7 @@ install(FILES # kurtotic_equalizer_cc.h map_bb.h # metric_type.h -# mpsk_receiver_cc.h + mpsk_receiver_cc.h mpsk_snr_est_cc.h # ofdm_cyclic_prefixer.h # ofdm_frame_acquisition.h diff --git a/gr-digital/include/digital/mpsk_receiver_cc.h b/gr-digital/include/digital/mpsk_receiver_cc.h new file mode 100644 index 0000000000..14094e25b7 --- /dev/null +++ b/gr-digital/include/digital/mpsk_receiver_cc.h @@ -0,0 +1,147 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2007,2011,2012 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_DIGITAL_MPSK_RECEIVER_CC_H +#define INCLUDED_DIGITAL_MPSK_RECEIVER_CC_H + +#include <digital/api.h> +#include <gr_block.h> + +namespace gr { + namespace digital { + + /*! + * \brief This block takes care of receiving M-PSK modulated + * signals through phase, frequency, and symbol synchronization. + * \ingroup sync_blk + * \ingroup demod_blk + * \ingroup digital + * + * This block takes care of receiving M-PSK modulated signals + * through phase, frequency, and symbol synchronization. It + * performs carrier frequency and phase locking as well as symbol + * timing recovery. It works with (D)BPSK, (D)QPSK, and (D)8PSK + * as tested currently. It should also work for OQPSK and PI/4 + * DQPSK. + * + * 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. There are + * optimized phase error detectors for BPSK and QPSK, but 8PSK is + * done using a brute-force computation of the constellation + * points to find the minimum. + * + * 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 DIGITAL_API mpsk_receiver_cc : virtual public gr_block + { + public: + // gr::digital::mpsk_receiver_cc::sptr + typedef boost::shared_ptr<mpsk_receiver_cc> sptr; + + /*! + * \brief Buil M-PSK receiver block. + * + * \param M modulation order of the M-PSK modulation + * \param theta any constant phase rotation from the real axis of the constellation + * \param loop_bw Loop bandwidth to set gains of phase/freq tracking loop + * \param fmin minimum normalized frequency value the loop can achieve + * \param fmax maximum normalized frequency value the loop can achieve + * \param mu initial parameter for the interpolator [0,1] + * \param gain_mu gain parameter of the M&M error signal to adjust mu (~0.05) + * \param omega initial value for the number of symbols between samples (~number of samples/symbol) + * \param gain_omega gain parameter to adjust omega based on the error (~omega^2/4) + * \param omega_rel sets the maximum (omega*(1+omega_rel)) and minimum (omega*(1+omega_rel)) omega (~0.005) + * + * The constructor also chooses which phase detector and + * decision maker to use in the work loop based on the value of + * M. + */ + static sptr make(unsigned int M, float theta, + float loop_bw, + float fmin, float fmax, + float mu, float gain_mu, + float omega, float gain_omega, float omega_rel); + + //! Returns the modulation order (M) currently set + virtual float modulation_order() const = 0; + + //! Returns current value of theta + virtual float theta() const = 0; + + //! Returns current value of mu + virtual float mu() const = 0; + + //! Returns current value of omega + virtual float omega() const = 0; + + //! Returns mu gain factor + virtual float gain_mu() const = 0; + + //! Returns omega gain factor + virtual float gain_omega() const = 0; + + //! Returns the relative omega limit + virtual float gain_omega_rel() const = 0; + + //! Sets the modulation order (M) currently + virtual void set_modulation_order(unsigned int M) = 0; + + //! Sets value of theta + virtual void set_theta(float theta) = 0; + + //! Sets value of mu + virtual void set_mu(float mu) = 0; + + //! Sets value of omega and its min and max values + virtual void set_omega(float omega) = 0; + + //! Sets value for mu gain factor + virtual void set_gain_mu(float gain_mu) = 0; + + //! Sets value for omega gain factor + virtual void set_gain_omega(float gain_omega) = 0; + + //! Sets the relative omega limit and resets omega min/max values + virtual void set_gain_omega_rel(float omega_rel) = 0; + }; + + } /* namespace digital */ +} /* namespace gr */ + +#endif /* INCLUDED_DIGITAL_MPSK_RECEIVER_CC_H */ diff --git a/gr-digital/include/digital_mpsk_receiver_cc.h b/gr-digital/include/digital_mpsk_receiver_cc.h deleted file mode 100644 index f3cc373eb7..0000000000 --- a/gr-digital/include/digital_mpsk_receiver_cc.h +++ /dev/null @@ -1,320 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2004,2007,2011 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_DIGITAL_MPSK_RECEIVER_CC_H -#define INCLUDED_DIGITAL_MPSK_RECEIVER_CC_H - -#include <digital_api.h> -#include <gruel/attributes.h> -#include <gri_control_loop.h> -#include <gr_block.h> -#include <gr_complex.h> -#include <fstream> -#include <filter/mmse_fir_interpolator_cc.h> - - -class digital_mpsk_receiver_cc; -typedef boost::shared_ptr<digital_mpsk_receiver_cc> digital_mpsk_receiver_cc_sptr; - -// public constructor -DIGITAL_API digital_mpsk_receiver_cc_sptr -digital_make_mpsk_receiver_cc (unsigned int M, float theta, - float loop_bw, - float fmin, float fmax, - float mu, float gain_mu, - float omega, float gain_omega, float omega_rel); - -/*! - * \brief This block takes care of receiving M-PSK modulated signals - * through phase, frequency, and symbol synchronization. - * \ingroup sync_blk - * \ingroup demod_blk - * \ingroup digital - * - * This block takes care of receiving M-PSK modulated signals through - * phase, frequency, and symbol synchronization. It performs carrier - * frequency and phase locking as well as symbol timing recovery. It - * works with (D)BPSK, (D)QPSK, and (D)8PSK as tested currently. It - * should also work for OQPSK and PI/4 DQPSK. - * - * 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. There are optimized phase error - * detectors for BPSK and QPSK, but 8PSK is done using a brute-force - * computation of the constellation points to find the minimum. - * - * 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 DIGITAL_API digital_mpsk_receiver_cc : public gr_block, public gri_control_loop -{ - public: - ~digital_mpsk_receiver_cc (); - void forecast(int noutput_items, gr_vector_int &ninput_items_required); - int general_work (int noutput_items, - gr_vector_int &ninput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); - - - //! Returns the modulation order (M) currently set - float modulation_order() const { return d_M; } - - //! Returns current value of theta - float theta() const { return d_theta; } - - //! Returns current value of mu - float mu() const { return d_mu; } - - //! Returns current value of omega - float omega() const { return d_omega; } - - //! Returns mu gain factor - float gain_mu() const { return d_gain_mu; } - - //! Returns omega gain factor - float gain_omega() const { return d_gain_omega; } - - //! Returns the relative omega limit - float gain_omega_rel() const {return d_omega_rel; } - - //! Sets the modulation order (M) currently - void set_modulation_order(unsigned int M); - - //! Sets value of theta - void set_theta(float theta) { d_theta = theta; } - - //! Sets value of mu - void set_mu (float mu) { d_mu = mu; } - - //! Sets value of omega and its min and max values - void set_omega (float omega) { - d_omega = omega; - d_min_omega = omega*(1.0 - d_omega_rel); - d_max_omega = omega*(1.0 + d_omega_rel); - d_omega_mid = 0.5*(d_min_omega+d_max_omega); - } - - //! Sets value for mu gain factor - void set_gain_mu (float gain_mu) { d_gain_mu = gain_mu; } - - //! Sets value for omega gain factor - void set_gain_omega (float gain_omega) { d_gain_omega = gain_omega; } - - //! Sets the relative omega limit and resets omega min/max values - void set_gain_omega_rel(float omega_rel); - -protected: - - /*! - * \brief Constructor to synchronize incoming M-PSK symbols - * - * \param M modulation order of the M-PSK modulation - * \param theta any constant phase rotation from the real axis of the constellation - * \param loop_bw Loop bandwidth to set gains of phase/freq tracking loop - * \param fmin minimum normalized frequency value the loop can achieve - * \param fmax maximum normalized frequency value the loop can achieve - * \param mu initial parameter for the interpolator [0,1] - * \param gain_mu gain parameter of the M&M error signal to adjust mu (~0.05) - * \param omega initial value for the number of symbols between samples (~number of samples/symbol) - * \param gain_omega gain parameter to adjust omega based on the error (~omega^2/4) - * \param omega_rel sets the maximum (omega*(1+omega_rel)) and minimum (omega*(1+omega_rel)) omega (~0.005) - * - * The constructor also chooses which phase detector and decision maker to use in the work loop based on the - * value of M. - */ - digital_mpsk_receiver_cc (unsigned int M, float theta, - float loop_bw, - float fmin, float fmax, - float mu, float gain_mu, - float omega, float gain_omega, float omega_rel); - - void make_constellation(); - void mm_sampler(const gr_complex symbol); - void mm_error_tracking(gr_complex sample); - void phase_error_tracking(gr_complex sample); - - - /*! - * \brief Phase error detector for MPSK modulations. - * - * \param sample the I&Q sample from which to determine the phase error - * - * This function determines the phase error for any MPSK signal by - * creating a set of PSK constellation points and doing a - * brute-force search to see which point minimizes the Euclidean - * distance. This point is then used to derotate the sample to the - * real-axis and a atan (using the fast approximation function) to - * determine the phase difference between the incoming sample and - * the real constellation point - * - * This should be cleaned up and made more efficient. - * - * \returns the approximated phase error. - */ - float phase_error_detector_generic(gr_complex sample) const; // generic for M but more costly - - /*! - * \brief Phase error detector for BPSK modulation. - * - * \param sample the I&Q sample from which to determine the phase error - * - * This function determines the phase error using a simple BPSK - * phase error detector by multiplying the real and imaginary (the - * error signal) components together. As the imaginary part goes to - * 0, so does this error. - * - * \returns the approximated phase error. - */ - float phase_error_detector_bpsk(gr_complex sample) const; // optimized for BPSK - - /*! - * \brief Phase error detector for QPSK modulation. - * - * \param sample the I&Q sample from which to determine the phase error - * - * This function determines the phase error using the limiter - * approach in a standard 4th order Costas loop - * - * \returns the approximated phase error. - */ - float phase_error_detector_qpsk(gr_complex sample) const; - - - - /*! - * \brief Decision maker for a generic MPSK constellation. - * - * \param sample the baseband I&Q sample from which to make the decision - * - * This decision maker is a generic implementation that does a - * brute-force search for the constellation point that minimizes the - * error between it and the incoming signal. - * - * \returns the index to d_constellation that minimizes the error/ - */ - unsigned int decision_generic(gr_complex sample) const; - - - /*! - * \brief Decision maker for BPSK constellation. - * - * \param sample the baseband I&Q sample from which to make the decision - * - * This decision maker is a simple slicer function that makes a - * decision on the symbol based on its placement on the real axis of - * greater than 0 or less than 0; the quadrature component is always - * 0. - * - * \returns the index to d_constellation that minimizes the error/ - */ - unsigned int decision_bpsk(gr_complex sample) const; - - - /*! - * \brief Decision maker for QPSK constellation. - * - * \param sample the baseband I&Q sample from which to make the decision - * - * This decision maker is a simple slicer function that makes a - * decision on the symbol based on its placement versus both axes - * and returns which quadrant the symbol is in. - * - * \returns the index to d_constellation that minimizes the error/ - */ - unsigned int decision_qpsk(gr_complex sample) const; - -private: - unsigned int d_M; - float d_theta; - - /*! - * \brief Decision maker function pointer - * - * \param sample the baseband I&Q sample from which to make the decision - * - * This is a function pointer that is set in the constructor to - * point to the proper decision function for the specified - * constellation order. - * - * \return index into d_constellation point that is the closest to the recieved sample - */ - unsigned int (digital_mpsk_receiver_cc::*d_decision)(gr_complex sample) const; // pointer to decision function - - - std::vector<gr_complex> d_constellation; - unsigned int d_current_const_point; - - // Members related to symbol timing - float d_mu, d_gain_mu; - float d_omega, d_gain_omega, d_omega_rel, d_max_omega, d_min_omega, d_omega_mid; - gr_complex d_p_2T, d_p_1T, d_p_0T; - gr_complex d_c_2T, d_c_1T, d_c_0T; - - /*! - * \brief Phase error detector function pointer - * - * \param sample the I&Q sample from which to determine the phase error - * - * This is a function pointer that is set in the constructor to - * point to the proper phase error detector function for the - * specified constellation order. - */ - float (digital_mpsk_receiver_cc::*d_phase_error_detector)(gr_complex sample) const; - - - //! get interpolated value - gr::filter::mmse_fir_interpolator_cc *d_interp; - - //! delay line length. - static const unsigned int DLLEN = 8; - - //! delay line plus some length for overflow protection - __GR_ATTR_ALIGNED(8) gr_complex d_dl[2*DLLEN]; - - //! index to delay line - unsigned int d_dl_idx; - - friend DIGITAL_API digital_mpsk_receiver_cc_sptr - digital_make_mpsk_receiver_cc(unsigned int M, float theta, - float loop_bw, - float fmin, float fmax, - float mu, float gain_mu, - float omega, float gain_omega, float omega_rel); -}; - -#endif diff --git a/gr-digital/lib/CMakeLists.txt b/gr-digital/lib/CMakeLists.txt index 0ae9e3d6bd..9acfff69a7 100644 --- a/gr-digital/lib/CMakeLists.txt +++ b/gr-digital/lib/CMakeLists.txt @@ -130,7 +130,7 @@ list(APPEND digital_sources #lms_dd_equalizer_cc_impl.cc #kurtotic_equalizer_cc_impl.cc map_bb_impl.cc - #mpsk_receiver_cc_impl.cc + mpsk_receiver_cc_impl.cc mpsk_snr_est_cc_impl.cc #ofdm_cyclic_prefixer_impl.cc #ofdm_frame_acquisition_impl.cc diff --git a/gr-digital/lib/digital_mpsk_receiver_cc.cc b/gr-digital/lib/digital_mpsk_receiver_cc.cc deleted file mode 100644 index 78f0589652..0000000000 --- a/gr-digital/lib/digital_mpsk_receiver_cc.cc +++ /dev/null @@ -1,329 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2005,2006,2007,2010,2011 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 <digital_mpsk_receiver_cc.h> -#include <stdexcept> -#include <gr_math.h> -#include <gr_expj.h> -#include <filter/mmse_fir_interpolator_cc.h> - - -#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 - -digital_mpsk_receiver_cc_sptr -digital_make_mpsk_receiver_cc(unsigned int M, float theta, - float loop_bw, - float fmin, float fmax, - float mu, float gain_mu, - float omega, float gain_omega, float omega_rel) -{ - return gnuradio::get_initial_sptr(new digital_mpsk_receiver_cc (M, theta, - loop_bw, - fmin, fmax, - mu, gain_mu, - omega, gain_omega, - omega_rel)); -} - -digital_mpsk_receiver_cc::digital_mpsk_receiver_cc (unsigned int M, float theta, - float loop_bw, - float fmin, float fmax, - float mu, float gain_mu, - float omega, float gain_omega, - float omega_rel) - : gr_block ("mpsk_receiver_cc", - gr_make_io_signature (1, 1, sizeof (gr_complex)), - gr_make_io_signature (1, 1, sizeof (gr_complex))), - gri_control_loop(loop_bw, fmax, fmin), - d_M(M), d_theta(theta), - d_current_const_point(0), - d_mu(mu), d_gain_mu(gain_mu), d_gain_omega(gain_omega), - d_omega_rel(omega_rel), d_max_omega(0), d_min_omega(0), - d_p_2T(0), d_p_1T(0), d_p_0T(0), d_c_2T(0), d_c_1T(0), d_c_0T(0) -{ - d_interp = new gr::filter::mmse_fir_interpolator_cc(); - d_dl_idx = 0; - - set_omega(omega); - - if (omega <= 0.0) - throw std::out_of_range ("clock rate must be > 0"); - if (gain_mu < 0 || gain_omega < 0) - throw std::out_of_range ("Gains must be non-negative"); - - assert(d_interp->ntaps() <= DLLEN); - - // zero double length delay line. - for (unsigned int i = 0; i < 2 * DLLEN; i++) - d_dl[i] = gr_complex(0.0,0.0); - - set_modulation_order(d_M); -} - -digital_mpsk_receiver_cc::~digital_mpsk_receiver_cc () -{ - delete d_interp; -} - -void -digital_mpsk_receiver_cc::set_modulation_order(unsigned int M) -{ - // build the constellation vector from M - make_constellation(); - - // Select a phase detector and a decision maker for the modulation order - switch(d_M) { - case 2: // optimized algorithms for BPSK - d_phase_error_detector = &digital_mpsk_receiver_cc::phase_error_detector_bpsk; //bpsk; - d_decision = &digital_mpsk_receiver_cc::decision_bpsk; - break; - - case 4: // optimized algorithms for QPSK - d_phase_error_detector = &digital_mpsk_receiver_cc::phase_error_detector_qpsk; //qpsk; - d_decision = &digital_mpsk_receiver_cc::decision_qpsk; - break; - - default: // generic algorithms for any M (power of 2?) but not pretty - d_phase_error_detector = &digital_mpsk_receiver_cc::phase_error_detector_generic; - d_decision = &digital_mpsk_receiver_cc::decision_generic; - break; - } -} - -void -digital_mpsk_receiver_cc::set_gain_omega_rel(float omega_rel) -{ - d_omega_rel = omega_rel; - set_omega(d_omega); -} - -void -digital_mpsk_receiver_cc::forecast(int noutput_items, gr_vector_int &ninput_items_required) -{ - unsigned ninputs = ninput_items_required.size(); - for (unsigned i=0; i < ninputs; i++) - ninput_items_required[i] = (int) ceil((noutput_items * d_omega) + d_interp->ntaps()); -} - -// FIXME add these back in an test difference in performance -float -digital_mpsk_receiver_cc::phase_error_detector_qpsk(gr_complex sample) const -{ - float phase_error = 0; - if(fabsf(sample.real()) > fabsf(sample.imag())) { - if(sample.real() > 0) - phase_error = -sample.imag(); - else - phase_error = sample.imag(); - } - else { - if(sample.imag() > 0) - phase_error = sample.real(); - else - phase_error = -sample.real(); - } - - return phase_error; -} - -float -digital_mpsk_receiver_cc::phase_error_detector_bpsk(gr_complex sample) const -{ - return -(sample.real()*sample.imag()); -} - -float digital_mpsk_receiver_cc::phase_error_detector_generic(gr_complex sample) const -{ - //return gr_fast_atan2f(sample*conj(d_constellation[d_current_const_point])); - return -arg(sample*conj(d_constellation[d_current_const_point])); -} - -unsigned int -digital_mpsk_receiver_cc::decision_bpsk(gr_complex sample) const -{ - return (gr_branchless_binary_slicer(sample.real()) ^ 1); - //return gr_binary_slicer(sample.real()) ^ 1; -} - -unsigned int -digital_mpsk_receiver_cc::decision_qpsk(gr_complex sample) const -{ - unsigned int index; - - //index = gr_branchless_quad_0deg_slicer(sample); - index = gr_quad_0deg_slicer(sample); - return index; -} - -unsigned int -digital_mpsk_receiver_cc::decision_generic(gr_complex sample) const -{ - unsigned int min_m = 0; - float min_s = 65535; - - // Develop all possible constellation points and find the one that minimizes - // the Euclidean distance (error) with the sample - for(unsigned int m=0; m < d_M; m++) { - gr_complex diff = norm(d_constellation[m] - sample); - - if(fabs(diff.real()) < min_s) { - min_s = fabs(diff.real()); - min_m = m; - } - } - // Return the index of the constellation point that minimizes the error - return min_m; -} - - -void -digital_mpsk_receiver_cc::make_constellation() -{ - for(unsigned int m=0; m < d_M; m++) { - d_constellation.push_back(gr_expj((M_TWOPI/d_M)*m)); - } -} - -void -digital_mpsk_receiver_cc::mm_sampler(const gr_complex symbol) -{ - gr_complex sample, nco; - - d_mu--; // skip a number of symbols between sampling - d_phase += d_freq; // increment the phase based on the frequency of the rotation - - // Keep phase clamped and not walk to infinity - while(d_phase > M_TWOPI) - d_phase -= M_TWOPI; - while(d_phase < -M_TWOPI) - d_phase += M_TWOPI; - - nco = gr_expj(d_phase+d_theta); // get the NCO value for derotating the current sample - sample = nco*symbol; // get the downconverted symbol - - // Fill up the delay line for the interpolator - d_dl[d_dl_idx] = sample; - d_dl[(d_dl_idx + DLLEN)] = sample; // put this in the second half of the buffer for overflows - d_dl_idx = (d_dl_idx+1) % DLLEN; // Keep the delay line index in bounds -} - -void -digital_mpsk_receiver_cc::mm_error_tracking(gr_complex sample) -{ - gr_complex u, x, y; - float mm_error = 0; - - // Make sample timing corrections - - // set the delayed samples - d_p_2T = d_p_1T; - d_p_1T = d_p_0T; - d_p_0T = sample; - d_c_2T = d_c_1T; - d_c_1T = d_c_0T; - - d_current_const_point = (*this.*d_decision)(d_p_0T); // make a decision on the sample value - d_c_0T = d_constellation[d_current_const_point]; - - x = (d_c_0T - d_c_2T) * conj(d_p_1T); - y = (d_p_0T - d_p_2T) * conj(d_c_1T); - u = y - x; - mm_error = u.real(); // the error signal is in the real part - mm_error = gr_branchless_clip(mm_error, 1.0); // limit mm_val - - d_omega = d_omega + d_gain_omega * mm_error; // update omega based on loop error - d_omega = d_omega_mid + gr_branchless_clip(d_omega-d_omega_mid, d_omega_rel); // make sure we don't walk away - - d_mu += d_omega + d_gain_mu * mm_error; // update mu based on loop error - -#if VERBOSE_MM - printf("mm: mu: %f omega: %f mm_error: %f sample: %f+j%f constellation: %f+j%f\n", - d_mu, d_omega, mm_error, sample.real(), sample.imag(), - d_constellation[d_current_const_point].real(), d_constellation[d_current_const_point].imag()); -#endif -} - - -void -digital_mpsk_receiver_cc::phase_error_tracking(gr_complex sample) -{ - float phase_error = 0; - - // Make phase and frequency corrections based on sampled value - phase_error = (*this.*d_phase_error_detector)(sample); - - advance_loop(phase_error); - phase_wrap(); - frequency_limit(); - -#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[d_current_const_point].real(), d_constellation[d_current_const_point].imag()); -#endif -} - -int -digital_mpsk_receiver_cc::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]; - gr_complex *out = (gr_complex *) output_items[0]; - - int i=0, o=0; - - while((o < noutput_items) && (i < ninput_items[0])) { - while((d_mu > 1) && (i < ninput_items[0])) { - mm_sampler(in[i]); // puts symbols into a buffer and adjusts d_mu - i++; - } - - if(i < ninput_items[0]) { - gr_complex interp_sample = d_interp->interpolate(&d_dl[d_dl_idx], d_mu); - - mm_error_tracking(interp_sample); // corrects M&M sample time - phase_error_tracking(interp_sample); // corrects phase and frequency offsets - - out[o++] = interp_sample; - } - } - - #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; -} diff --git a/gr-digital/lib/mpsk_receiver_cc_impl.cc b/gr-digital/lib/mpsk_receiver_cc_impl.cc new file mode 100644 index 0000000000..31355c5653 --- /dev/null +++ b/gr-digital/lib/mpsk_receiver_cc_impl.cc @@ -0,0 +1,331 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005-2007,2010-2012 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 "mpsk_receiver_cc_impl.h" +#include <gr_io_signature.h> +#include <gr_prefs.h> +#include <gr_math.h> +#include <gr_expj.h> +#include <stdexcept> + +namespace gr { + namespace digital { + +#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 + + mpsk_receiver_cc::sptr + mpsk_receiver_cc::make(unsigned int M, float theta, + float loop_bw, + float fmin, float fmax, + float mu, float gain_mu, + float omega, float gain_omega, float omega_rel) + { + return gnuradio::get_initial_sptr + (new mpsk_receiver_cc_impl(M, theta, + loop_bw, + fmin, fmax, + mu, gain_mu, + omega, gain_omega, + omega_rel)); + } + + mpsk_receiver_cc_impl::mpsk_receiver_cc_impl(unsigned int M, float theta, + float loop_bw, + float fmin, float fmax, + float mu, float gain_mu, + float omega, float gain_omega, + float omega_rel) + : gr_block("mpsk_receiver_cc", + gr_make_io_signature(1, 1, sizeof(gr_complex)), + gr_make_io_signature(1, 1, sizeof(gr_complex))), + gri_control_loop(loop_bw, fmax, fmin), + d_M(M), d_theta(theta), + d_current_const_point(0), + d_mu(mu), d_gain_mu(gain_mu), d_gain_omega(gain_omega), + d_omega_rel(omega_rel), d_max_omega(0), d_min_omega(0), + d_p_2T(0), d_p_1T(0), d_p_0T(0), d_c_2T(0), d_c_1T(0), d_c_0T(0) + { + d_interp = new gr::filter::mmse_fir_interpolator_cc(); + d_dl_idx = 0; + + set_omega(omega); + + if(omega <= 0.0) + throw std::out_of_range("clock rate must be > 0"); + if(gain_mu < 0 || gain_omega < 0) + throw std::out_of_range("Gains must be non-negative"); + + assert(d_interp->ntaps() <= DLLEN); + + // zero double length delay line. + for(unsigned int i = 0; i < 2 * DLLEN; i++) + d_dl[i] = gr_complex(0.0,0.0); + + set_modulation_order(d_M); + } + + mpsk_receiver_cc_impl::~mpsk_receiver_cc_impl() + { + delete d_interp; + } + + void + mpsk_receiver_cc_impl::set_modulation_order(unsigned int M) + { + // build the constellation vector from M + make_constellation(); + + // Select a phase detector and a decision maker for the modulation order + switch(d_M) { + case 2: // optimized algorithms for BPSK + d_phase_error_detector = &mpsk_receiver_cc_impl::phase_error_detector_bpsk; //bpsk; + d_decision = &mpsk_receiver_cc_impl::decision_bpsk; + break; + + case 4: // optimized algorithms for QPSK + d_phase_error_detector = &mpsk_receiver_cc_impl::phase_error_detector_qpsk; //qpsk; + d_decision = &mpsk_receiver_cc_impl::decision_qpsk; + break; + + default: // generic algorithms for any M (power of 2?) but not pretty + d_phase_error_detector = &mpsk_receiver_cc_impl::phase_error_detector_generic; + d_decision = &mpsk_receiver_cc_impl::decision_generic; + break; + } + } + + void + mpsk_receiver_cc_impl::set_gain_omega_rel(float omega_rel) + { + d_omega_rel = omega_rel; + set_omega(d_omega); + } + + void + mpsk_receiver_cc_impl::forecast(int noutput_items, gr_vector_int &ninput_items_required) + { + unsigned ninputs = ninput_items_required.size(); + for(unsigned i=0; i < ninputs; i++) + ninput_items_required[i] = (int) ceil((noutput_items * d_omega) + d_interp->ntaps()); + } + + // FIXME add these back in an test difference in performance + float + mpsk_receiver_cc_impl::phase_error_detector_qpsk(gr_complex sample) const + { + float phase_error = 0; + if(fabsf(sample.real()) > fabsf(sample.imag())) { + if(sample.real() > 0) + phase_error = -sample.imag(); + else + phase_error = sample.imag(); + } + else { + if(sample.imag() > 0) + phase_error = sample.real(); + else + phase_error = -sample.real(); + } + + return phase_error; + } + + float + mpsk_receiver_cc_impl::phase_error_detector_bpsk(gr_complex sample) const + { + return -(sample.real()*sample.imag()); + } + + float mpsk_receiver_cc_impl::phase_error_detector_generic(gr_complex sample) const + { + //return gr_fast_atan2f(sample*conj(d_constellation[d_current_const_point])); + return -arg(sample*conj(d_constellation[d_current_const_point])); + } + + unsigned int + mpsk_receiver_cc_impl::decision_bpsk(gr_complex sample) const + { + return (gr_branchless_binary_slicer(sample.real()) ^ 1); + //return gr_binary_slicer(sample.real()) ^ 1; + } + + unsigned int + mpsk_receiver_cc_impl::decision_qpsk(gr_complex sample) const + { + unsigned int index; + + //index = gr_branchless_quad_0deg_slicer(sample); + index = gr_quad_0deg_slicer(sample); + return index; + } + + unsigned int + mpsk_receiver_cc_impl::decision_generic(gr_complex sample) const + { + unsigned int min_m = 0; + float min_s = 65535; + + // Develop all possible constellation points and find the one that minimizes + // the Euclidean distance (error) with the sample + for(unsigned int m = 0; m < d_M; m++) { + gr_complex diff = norm(d_constellation[m] - sample); + + if(fabs(diff.real()) < min_s) { + min_s = fabs(diff.real()); + min_m = m; + } + } + // Return the index of the constellation point that minimizes the error + return min_m; + } + + void + mpsk_receiver_cc_impl::make_constellation() + { + for(unsigned int m = 0; m < d_M; m++) { + d_constellation.push_back(gr_expj((M_TWOPI/d_M)*m)); + } + } + + void + mpsk_receiver_cc_impl::mm_sampler(const gr_complex symbol) + { + gr_complex sample, nco; + + d_mu--; // skip a number of symbols between sampling + d_phase += d_freq; // increment the phase based on the frequency of the rotation + + // Keep phase clamped and not walk to infinity + while(d_phase > M_TWOPI) + d_phase -= M_TWOPI; + while(d_phase < -M_TWOPI) + d_phase += M_TWOPI; + + nco = gr_expj(d_phase+d_theta); // get the NCO value for derotating the current sample + sample = nco*symbol; // get the downconverted symbol + + // Fill up the delay line for the interpolator + d_dl[d_dl_idx] = sample; + d_dl[(d_dl_idx + DLLEN)] = sample; // put this in the second half of the buffer for overflows + d_dl_idx = (d_dl_idx+1) % DLLEN; // Keep the delay line index in bounds + } + + void + mpsk_receiver_cc_impl::mm_error_tracking(gr_complex sample) + { + gr_complex u, x, y; + float mm_error = 0; + + // Make sample timing corrections + + // set the delayed samples + d_p_2T = d_p_1T; + d_p_1T = d_p_0T; + d_p_0T = sample; + d_c_2T = d_c_1T; + d_c_1T = d_c_0T; + + d_current_const_point = (*this.*d_decision)(d_p_0T); // make a decision on the sample value + d_c_0T = d_constellation[d_current_const_point]; + + x = (d_c_0T - d_c_2T) * conj(d_p_1T); + y = (d_p_0T - d_p_2T) * conj(d_c_1T); + u = y - x; + mm_error = u.real(); // the error signal is in the real part + mm_error = gr_branchless_clip(mm_error, 1.0); // limit mm_val + + d_omega = d_omega + d_gain_omega * mm_error; // update omega based on loop error + d_omega = d_omega_mid + gr_branchless_clip(d_omega-d_omega_mid, d_omega_rel); // make sure we don't walk away + + d_mu += d_omega + d_gain_mu * mm_error; // update mu based on loop error + +#if VERBOSE_MM + printf("mm: mu: %f omega: %f mm_error: %f sample: %f+j%f constellation: %f+j%f\n", + d_mu, d_omega, mm_error, sample.real(), sample.imag(), + d_constellation[d_current_const_point].real(), d_constellation[d_current_const_point].imag()); +#endif + } + + + void + mpsk_receiver_cc_impl::phase_error_tracking(gr_complex sample) + { + float phase_error = 0; + + // Make phase and frequency corrections based on sampled value + phase_error = (*this.*d_phase_error_detector)(sample); + + advance_loop(phase_error); + phase_wrap(); + frequency_limit(); + +#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[d_current_const_point].real(), d_constellation[d_current_const_point].imag()); +#endif +} + + int + mpsk_receiver_cc_impl::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]; + gr_complex *out = (gr_complex*)output_items[0]; + + int i=0, o=0; + + while((o < noutput_items) && (i < ninput_items[0])) { + while((d_mu > 1) && (i < ninput_items[0])) { + mm_sampler(in[i]); // puts symbols into a buffer and adjusts d_mu + i++; + } + + if(i < ninput_items[0]) { + gr_complex interp_sample = d_interp->interpolate(&d_dl[d_dl_idx], d_mu); + + mm_error_tracking(interp_sample); // corrects M&M sample time + phase_error_tracking(interp_sample); // corrects phase and frequency offsets + + out[o++] = interp_sample; + } + } + +#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; + } + + } /* namespace digital */ +} /* namespace gr */ diff --git a/gr-digital/lib/mpsk_receiver_cc_impl.h b/gr-digital/lib/mpsk_receiver_cc_impl.h new file mode 100644 index 0000000000..3db6fa8b62 --- /dev/null +++ b/gr-digital/lib/mpsk_receiver_cc_impl.h @@ -0,0 +1,244 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2007,2011,2012 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_DIGITAL_MPSK_RECEIVER_CC_IMPL_H +#define INCLUDED_DIGITAL_MPSK_RECEIVER_CC_IMPL_H + +#include <digital/mpsk_receiver_cc.h> +#include <gruel/attributes.h> +#include <gri_control_loop.h> +#include <gr_complex.h> +#include <fstream> +#include <filter/mmse_fir_interpolator_cc.h> + +namespace gr { + namespace digital { + + class mpsk_receiver_cc_impl + : public mpsk_receiver_cc, public gri_control_loop + { + public: + mpsk_receiver_cc_impl(unsigned int M, float theta, + float loop_bw, + float fmin, float fmax, + float mu, float gain_mu, + float omega, float gain_omega, float omega_rel); + ~mpsk_receiver_cc_impl(); + + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + int general_work(int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + //! Returns the modulation order (M) currently set + float modulation_order() const { return d_M; } + + //! Returns current value of theta + float theta() const { return d_theta; } + + //! Returns current value of mu + float mu() const { return d_mu; } + + //! Returns current value of omega + float omega() const { return d_omega; } + + //! Returns mu gain factor + float gain_mu() const { return d_gain_mu; } + + //! Returns omega gain factor + float gain_omega() const { return d_gain_omega; } + + //! Returns the relative omega limit + float gain_omega_rel() const {return d_omega_rel; } + + //! Sets the modulation order (M) currently + void set_modulation_order(unsigned int M); + + //! Sets value of theta + void set_theta(float theta) { d_theta = theta; } + + //! Sets value of mu + void set_mu(float mu) { d_mu = mu; } + + //! Sets value of omega and its min and max values + void set_omega(float omega) { + d_omega = omega; + d_min_omega = omega*(1.0 - d_omega_rel); + d_max_omega = omega*(1.0 + d_omega_rel); + d_omega_mid = 0.5*(d_min_omega+d_max_omega); + } + + //! Sets value for mu gain factor + void set_gain_mu(float gain_mu) { d_gain_mu = gain_mu; } + + //! Sets value for omega gain factor + void set_gain_omega(float gain_omega) { d_gain_omega = gain_omega; } + + //! Sets the relative omega limit and resets omega min/max values + void set_gain_omega_rel(float omega_rel); + + protected: + void make_constellation(); + void mm_sampler(const gr_complex symbol); + void mm_error_tracking(gr_complex sample); + void phase_error_tracking(gr_complex sample); + + /*! + * \brief Phase error detector for MPSK modulations. + * + * \param sample the I&Q sample from which to determine the phase error + * + * This function determines the phase error for any MPSK signal + * by creating a set of PSK constellation points and doing a + * brute-force search to see which point minimizes the Euclidean + * distance. This point is then used to derotate the sample to + * the real-axis and a atan (using the fast approximation + * function) to determine the phase difference between the + * incoming sample and the real constellation point + * + * This should be cleaned up and made more efficient. + * + * \returns the approximated phase error. + */ + float phase_error_detector_generic(gr_complex sample) const; + + /*! + * \brief Phase error detector for BPSK modulation. + * + * \param sample the I&Q sample from which to determine the phase error + * + * This function determines the phase error using a simple BPSK + * phase error detector by multiplying the real and imaginary (the + * error signal) components together. As the imaginary part goes to + * 0, so does this error. + * + * \returns the approximated phase error. + */ + float phase_error_detector_bpsk(gr_complex sample) const; + + /*! + * \brief Phase error detector for QPSK modulation. + * + * \param sample the I&Q sample from which to determine the phase error + * + * This function determines the phase error using the limiter + * approach in a standard 4th order Costas loop + * + * \returns the approximated phase error. + */ + float phase_error_detector_qpsk(gr_complex sample) const; + + /*! + * \brief Decision maker for a generic MPSK constellation. + * + * \param sample the baseband I&Q sample from which to make the decision + * + * This decision maker is a generic implementation that does a + * brute-force search for the constellation point that minimizes + * the error between it and the incoming signal. + * + * \returns the index to d_constellation that minimizes the error/ + */ + unsigned int decision_generic(gr_complex sample) const; + + /*! + * \brief Decision maker for BPSK constellation. + * + * \param sample the baseband I&Q sample from which to make the decision + * + * This decision maker is a simple slicer function that makes a + * decision on the symbol based on its placement on the real + * axis of greater than 0 or less than 0; the quadrature + * component is always 0. + * + * \returns the index to d_constellation that minimizes the error/ + */ + unsigned int decision_bpsk(gr_complex sample) const; + + /*! + * \brief Decision maker for QPSK constellation. + * + * \param sample the baseband I&Q sample from which to make the decision + * + * This decision maker is a simple slicer function that makes a + * decision on the symbol based on its placement versus both + * axes and returns which quadrant the symbol is in. + * + * \returns the index to d_constellation that minimizes the error/ + */ + unsigned int decision_qpsk(gr_complex sample) const; + + private: + unsigned int d_M; + float d_theta; + + /*! + * \brief Decision maker function pointer + * + * \param sample the baseband I&Q sample from which to make the decision + * + * This is a function pointer that is set in the constructor to + * point to the proper decision function for the specified + * constellation order. + * + * \return index into d_constellation point that is the closest to the recieved sample + */ + unsigned int (mpsk_receiver_cc_impl::*d_decision)(gr_complex sample) const; + + std::vector<gr_complex> d_constellation; + unsigned int d_current_const_point; + + // Members related to symbol timing + float d_mu, d_gain_mu; + float d_omega, d_gain_omega, d_omega_rel, d_max_omega, d_min_omega, d_omega_mid; + gr_complex d_p_2T, d_p_1T, d_p_0T; + gr_complex d_c_2T, d_c_1T, d_c_0T; + + /*! + * \brief Phase error detector function pointer + * + * \param sample the I&Q sample from which to determine the phase error + * + * This is a function pointer that is set in the constructor to + * point to the proper phase error detector function for the + * specified constellation order. + */ + float (mpsk_receiver_cc_impl::*d_phase_error_detector)(gr_complex sample) const; + + //! get interpolated value + gr::filter::mmse_fir_interpolator_cc *d_interp; + + //! delay line length. + static const unsigned int DLLEN = 8; + + //! delay line plus some length for overflow protection + __GR_ATTR_ALIGNED(8) gr_complex d_dl[2*DLLEN]; + + //! index to delay line + unsigned int d_dl_idx; + }; + + } /* namespace digital */ +} /* namespace gr */ + +#endif /* INCLUDED_DIGITAL_MPSK_RECEIVER_CC_IMPL_H */ diff --git a/gr-digital/python/qa_mpsk_receiver.py b/gr-digital/python/qa_mpsk_receiver.py index 6b6f70adaa..aa2973eb45 100755 --- a/gr-digital/python/qa_mpsk_receiver.py +++ b/gr-digital/python/qa_mpsk_receiver.py @@ -23,17 +23,17 @@ from gnuradio import gr, gr_unittest import digital_swig as digital import filter_swig as filter -import random, cmath +import random, cmath, time class test_mpsk_receiver(gr_unittest.TestCase): - def setUp (self): - self.tb = gr.top_block () + def setUp(self): + self.tb = gr.top_block() - def tearDown (self): + def tearDown(self): self.tb = None - def test01 (self): + def test01(self): # Test BPSK sync M = 2 theta = 0 @@ -60,9 +60,9 @@ class test_mpsk_receiver(gr_unittest.TestCase): nfilts = 32 excess_bw = 0.35 ntaps = 11 * int(omega*nfilts) - rrc_taps0 = gr.firdes.root_raised_cosine( + rrc_taps0 = filter.firdes.root_raised_cosine( nfilts, nfilts, 1.0, excess_bw, ntaps) - rrc_taps1 = gr.firdes.root_raised_cosine( + rrc_taps1 = filter.firdes.root_raised_cosine( 1, omega, 1.0, excess_bw, 11*omega) self.rrc0 = filter.pfb_arb_resampler_ccf(omega, rrc_taps0) self.rrc1 = filter.fir_filter_ccf(1, rrc_taps1) @@ -83,7 +83,7 @@ class test_mpsk_receiver(gr_unittest.TestCase): #for e,d in zip(expected_result, dst_data): # print "{0:+.02f} {1:+.02f}".format(e, d) - self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 1) + self.assertComplexTuplesAlmostEqual(expected_result, dst_data, 1) def test02 (self): @@ -116,9 +116,9 @@ class test_mpsk_receiver(gr_unittest.TestCase): nfilts = 32 excess_bw = 0.35 ntaps = 11 * int(omega*nfilts) - rrc_taps0 = gr.firdes.root_raised_cosine( + rrc_taps0 = filter.firdes.root_raised_cosine( nfilts, nfilts, 1.0, excess_bw, ntaps) - rrc_taps1 = gr.firdes.root_raised_cosine( + rrc_taps1 = filter.firdes.root_raised_cosine( 1, omega, 1.0, excess_bw, 11*omega) self.rrc0 = filter.pfb_arb_resampler_ccf(omega, rrc_taps0) self.rrc1 = filter.fir_filter_ccf(1, rrc_taps1) @@ -128,7 +128,9 @@ class test_mpsk_receiver(gr_unittest.TestCase): expected_result = 10000*[complex(0, +0.5), complex(-0.5, 0), complex(0, -0.5), complex(+0.5, 0)] - dst_data = self.snk.data() + + # get data after a settling period + dst_data = self.snk.data()[100:] # Only compare last Ncmp samples Ncmp = 1000 @@ -140,7 +142,7 @@ class test_mpsk_receiver(gr_unittest.TestCase): #for e,d in zip(expected_result, dst_data): # print "{0:+.02f} {1:+.02f}".format(e, d) - self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 1) + self.assertComplexTuplesAlmostEqual(expected_result, dst_data, 1) if __name__ == '__main__': gr_unittest.run(test_mpsk_receiver, "test_mpsk_receiver.xml") diff --git a/gr-digital/swig/digital_mpsk_receiver_cc.i b/gr-digital/swig/digital_mpsk_receiver_cc.i deleted file mode 100644 index 2338a18547..0000000000 --- a/gr-digital/swig/digital_mpsk_receiver_cc.i +++ /dev/null @@ -1,57 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2004,2011 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(digital,mpsk_receiver_cc); - -digital_mpsk_receiver_cc_sptr digital_make_mpsk_receiver_cc (unsigned int M, float theta, - float loop_bw, - float fmin, float fmax, - float mu, float gain_mu, - float omega, float gain_omega, - float omega_rel); -class digital_mpsk_receiver_cc : public gr_block, public gri_control_loop -{ - private: - digital_mpsk_receiver_cc (unsigned int M,float theta, - float loop_bw, - float fmin, float fmax, - float mu, float gain_mu, - float omega, float gain_omega, float omega_rel); -public: - float modulation_order() const { return d_M; } - float mu() const { return d_mu;} - float omega() const { return d_omega;} - float gain_mu() const { return d_gain_mu;} - float gain_omega() const { return d_gain_omega;} - float gain_omega_rel() const {return d_omega_rel; } - void set_modulation_order(unsigned int M); - void set_mu (float mu) { d_mu = mu; } - void set_omega (float omega) { - d_omega = omega; - d_min_omega = omega*(1.0 - d_omega_rel); - d_max_omega = omega*(1.0 + d_omega_rel); - } - void set_theta(float theta) { d_theta = theta; } - void set_gain_mu (float gain_mu) { d_gain_mu = gain_mu; } - void set_gain_omega (float gain_omega) { d_gain_omega = gain_omega; } - void set_gain_omega_rel(float omega_rel); -}; diff --git a/gr-digital/swig/digital_swig.i b/gr-digital/swig/digital_swig.i index ab38466a9f..51631be327 100644 --- a/gr-digital/swig/digital_swig.i +++ b/gr-digital/swig/digital_swig.i @@ -65,6 +65,7 @@ #include "digital/glfsr_source_b.h" #include "digital/glfsr_source_f.h" #include "digital/map_bb.h" +#include "digital/mpsk_receiver_cc.h" #include "digital/mpsk_snr_est_cc.h" #include "digital/pfb_clock_sync_ccf.h" #include "digital/pfb_clock_sync_fff.h" @@ -100,6 +101,7 @@ %include "digital/glfsr_source_b.h" %include "digital/glfsr_source_f.h" %include "digital/map_bb.h" +%include "digital/mpsk_receiver_cc.h" %include "digital/mpsk_snr_est_cc.h" %include "digital/pfb_clock_sync_ccf.h" %include "digital/pfb_clock_sync_fff.h" @@ -132,6 +134,7 @@ GR_SWIG_BLOCK_MAGIC2(digital, fll_band_edge_cc); GR_SWIG_BLOCK_MAGIC2(digital, glfsr_source_b); GR_SWIG_BLOCK_MAGIC2(digital, glfsr_source_f); GR_SWIG_BLOCK_MAGIC2(digital, map_bb); +GR_SWIG_BLOCK_MAGIC2(digital, mpsk_receiver_cc); GR_SWIG_BLOCK_MAGIC2(digital, mpsk_snr_est_cc); GR_SWIG_BLOCK_MAGIC2(digital, pfb_clock_sync_ccf); GR_SWIG_BLOCK_MAGIC2(digital, pfb_clock_sync_fff); |