diff options
Diffstat (limited to 'gr-digital')
-rw-r--r-- | gr-digital/lib/Makefile.am | 6 | ||||
-rw-r--r-- | gr-digital/lib/digital_mpsk_receiver_cc.cc | 322 | ||||
-rw-r--r-- | gr-digital/lib/digital_mpsk_receiver_cc.h | 316 | ||||
-rw-r--r-- | gr-digital/python/Makefile.am | 3 | ||||
-rw-r--r-- | gr-digital/python/qa_mpsk_receiver.py | 123 | ||||
-rw-r--r-- | gr-digital/swig/Makefile.am | 3 | ||||
-rw-r--r-- | gr-digital/swig/digital_mpsk_receiver_cc.i | 60 | ||||
-rw-r--r-- | gr-digital/swig/digital_swig.i | 2 |
8 files changed, 831 insertions, 4 deletions
diff --git a/gr-digital/lib/Makefile.am b/gr-digital/lib/Makefile.am index e3cbbe7ea..86b98726b 100644 --- a/gr-digital/lib/Makefile.am +++ b/gr-digital/lib/Makefile.am @@ -37,7 +37,8 @@ grinclude_HEADERS = \ digital_crc32.h \ digital_lms_dd_equalizer_cc.h \ digital_kurtotic_equalizer_cc.h \ - digital_metric_type.h + digital_metric_type.h \ + digital_mpsk_receiver_cc.h lib_LTLIBRARIES = libgnuradio-digital.la @@ -53,7 +54,8 @@ libgnuradio_digital_la_SOURCES = \ digital_cma_equalizer_cc.cc \ digital_crc32.cc \ digital_lms_dd_equalizer_cc.cc \ - digital_kurtotic_equalizer_cc.cc + digital_kurtotic_equalizer_cc.cc \ + digital_mpsk_receiver_cc.cc libgnuradio_digital_la_LIBADD = \ $(GNURADIO_CORE_LA) diff --git a/gr-digital/lib/digital_mpsk_receiver_cc.cc b/gr-digital/lib/digital_mpsk_receiver_cc.cc new file mode 100644 index 000000000..3b2ea9840 --- /dev/null +++ b/gr-digital/lib/digital_mpsk_receiver_cc.cc @@ -0,0 +1,322 @@ +/* -*- 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 <gri_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 alpha, float beta, + 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, + alpha, beta, + fmin, fmax, + mu, gain_mu, + omega, gain_omega, omega_rel)); +} + +digital_mpsk_receiver_cc::digital_mpsk_receiver_cc (unsigned int M, float theta, + float alpha, float beta, + 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))), + d_M(M), d_theta(theta), + d_alpha(alpha), d_beta(beta), d_freq(0), d_max_freq(fmax), d_min_freq(fmin), d_phase(0), + d_current_const_point(0), + 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 gri_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); + + // 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; + } +} + +digital_mpsk_receiver_cc::~digital_mpsk_receiver_cc () +{ + delete d_interp; +} + +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); + + d_freq += d_beta*phase_error; // adjust frequency based on error + d_phase += d_freq + d_alpha*phase_error; // adjust phase based on error + + // Make sure we stay within +-2pi + while(d_phase > M_TWOPI) + d_phase -= M_TWOPI; + while(d_phase < -M_TWOPI) + d_phase += M_TWOPI; + + // Limit the frequency range + d_freq = gr_branchless_clip(d_freq, d_max_freq); + +#if VERBOSE_COSTAS + printf("cl: phase_error: %f phase: %f freq: %f sample: %f+j%f constellation: %f+j%f\n", + phase_error, d_phase, d_freq, sample.real(), sample.imag(), + d_constellation[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/digital_mpsk_receiver_cc.h b/gr-digital/lib/digital_mpsk_receiver_cc.h new file mode 100644 index 000000000..bcb06e17c --- /dev/null +++ b/gr-digital/lib/digital_mpsk_receiver_cc.h @@ -0,0 +1,316 @@ +/* -*- 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 <gruel/attributes.h> +#include <gr_block.h> +#include <gr_complex.h> +#include <fstream> + +class gri_mmse_fir_interpolator_cc; + +class digital_mpsk_receiver_cc; +typedef boost::shared_ptr<digital_mpsk_receiver_cc> digital_mpsk_receiver_cc_sptr; + +// public constructor +digital_mpsk_receiver_cc_sptr +digital_make_mpsk_receiver_cc (unsigned int M, float theta, + float alpha, float beta, + 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 + * + * 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_mpsk_receiver_cc : public gr_block +{ + 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); + + + // Member functions related to the symbol tracking portion of the receiver + //! (M&M) Returns current value of mu + float mu() const { return d_mu;} + + //! (M&M) Returns current value of omega + float omega() const { return d_omega;} + + //! (M&M) Returns mu gain factor + float gain_mu() const { return d_gain_mu;} + + //! (M&M) Returns omega gain factor + float gain_omega() const { return d_gain_omega;} + + //! (M&M) Sets value of mu + void set_mu (float mu) { d_mu = mu; } + + //! (M&M) 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); + } + + //! (M&M) Sets value for mu gain factor + void set_gain_mu (float gain_mu) { d_gain_mu = gain_mu; } + + //! (M&M) Sets value for omega gain factor + void set_gain_omega (float gain_omega) { d_gain_omega = gain_omega; } + + + + // Member function related to the phase/frequency tracking portion of the receiver + //! (CL) Returns the value for alpha (the phase gain term) + float alpha() const { return d_alpha; } + + //! (CL) Returns the value of beta (the frequency gain term) + float beta() const { return d_beta; } + + //! (CL) Returns the current value of the frequency of the NCO in the Costas loop + float freq() const { return d_freq; } + + //! (CL) Returns the current value of the phase of the NCO in the Costal loop + float phase() const { return d_phase; } + + //! (CL) Sets the value for alpha (the phase gain term) + void set_alpha(float alpha) { d_alpha = alpha; } + + //! (CL) Setss the value of beta (the frequency gain term) + void set_beta(float beta) { d_beta = beta; } + + //! (CL) Sets the current value of the frequency of the NCO in the Costas loop + void set_freq(float freq) { d_freq = freq; } + + //! (CL) Setss the current value of the phase of the NCO in the Costal loop + void set_phase(float phase) { d_phase = phase; } + + +protected: + + /*! + * \brief Constructor to synchronize incoming M-PSK symbols + * + * \param M modulation order of the M-PSK modulation + * \param theta any constant phase rotation from the real axis of the constellation + * \param alpha gain parameter to adjust the phase in the Costas loop (~0.01) + * \param beta gain parameter to adjust the frequency in the Costas loop (~alpha^2/4) + * \param fmin minimum normalized frequency value the loop can achieve + * \param fmax maximum normalized frequency value the loop can achieve + * \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 alpha, float beta, + 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; + + // Members related to carrier and phase tracking + float d_alpha; + float d_beta; + float d_freq, d_max_freq, d_min_freq; + float d_phase; + +/*! + * \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 + gri_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_mpsk_receiver_cc_sptr + digital_make_mpsk_receiver_cc (unsigned int M, float theta, + float alpha, float beta, + float fmin, float fmax, + float mu, float gain_mu, + float omega, float gain_omega, float omega_rel); +}; + +#endif diff --git a/gr-digital/python/Makefile.am b/gr-digital/python/Makefile.am index 09be67f3a..eea4dba95 100644 --- a/gr-digital/python/Makefile.am +++ b/gr-digital/python/Makefile.am @@ -41,7 +41,8 @@ noinst_PYTHON = \ qa_correlate_access_code.py \ qa_costas_loop_cc.py \ qa_crc32.py \ - qa_lms_equalizer.py + qa_lms_equalizer.py \ + qa_mpsk_receiver.py digital_PYTHON = \ __init__.py \ diff --git a/gr-digital/python/qa_mpsk_receiver.py b/gr-digital/python/qa_mpsk_receiver.py new file mode 100644 index 000000000..7e9a76e1f --- /dev/null +++ b/gr-digital/python/qa_mpsk_receiver.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python +# +# Copyright 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. +# + +from gnuradio import gr, gr_unittest +import digital_swig, psk2 +import random, cmath + +class test_mpsk_receiver(gr_unittest.TestCase): + + def setUp (self): + self.tb = gr.top_block () + + def tearDown (self): + self.tb = None + + def test01 (self): + # Test BPSK sync + M = 2 + theta = 0 + alpha = 0.1 + beta = 0.25*alpha*alpha + fmin = -0.5 + fmax = 0.5 + mu = 0.25 + gain_mu = 0.01 + omega = 2 + gain_omega = 0.001 + omega_rel = 0.001 + + self.test = digital_swig.mpsk_receiver_cc(M, theta, alpha, beta, + fmin, fmax, mu, gain_mu, + omega, gain_omega, + omega_rel) + + data = 1000*[complex(1,0), complex(1,0), complex(-1,0), complex(-1,0)] + self.src = gr.vector_source_c(data, False) + self.snk = gr.vector_sink_c() + + self.tb.connect(self.src, self.test, self.snk) + self.tb.run() + + expected_result = 1000*[complex(-0.5,0), complex(0.5,0)] + dst_data = self.snk.data() + + # Only compare last Ncmp samples + Ncmp = 100 + len_e = len(expected_result) + len_d = len(dst_data) + expected_result = expected_result[len_e - Ncmp:] + dst_data = dst_data[len_d - Ncmp:] + + #print expected_result + #print dst_data + + self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 1) + + + def test02 (self): + # Test QPSK sync + M = 4 + theta = 0 + alpha = 0.1 + beta = 0.25*alpha*alpha + fmin = -0.5 + fmax = 0.5 + mu = 0.25 + gain_mu = 0.01 + omega = 2 + gain_omega = 0.001 + omega_rel = 0.001 + + self.test = digital_swig.mpsk_receiver_cc(M, theta, alpha, beta, + fmin, fmax, mu, gain_mu, + omega, gain_omega, + omega_rel) + + data = 1000*[complex( 0.707, 0.707), complex( 0.707, 0.707), + complex(-0.707, 0.707), complex(-0.707, 0.707), + complex(-0.707, -0.707), complex(-0.707, -0.707), + complex( 0.707, -0.707), complex( 0.707, -0.707)] + self.src = gr.vector_source_c(data, False) + self.snk = gr.vector_sink_c() + + self.tb.connect(self.src, self.test, self.snk) + self.tb.run() + + expected_result = 1000*[complex(1.2, 0), complex(0, 1.2), + complex(-1.2, 0), complex(0, -1.2)] + dst_data = self.snk.data() + + # Only compare last Ncmp samples + Ncmp = 100 + len_e = len(expected_result) + len_d = len(dst_data) + expected_result = expected_result[len_e - Ncmp:] + dst_data = dst_data[len_d - Ncmp:] + + #print expected_result + #print dst_data + + 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/Makefile.am b/gr-digital/swig/Makefile.am index 0a4176fc1..08fb140ac 100644 --- a/gr-digital/swig/Makefile.am +++ b/gr-digital/swig/Makefile.am @@ -69,7 +69,8 @@ digital_swig_swiginclude_headers = \ digital_cma_equalizer_cc.i \ digital_crc32.i \ digital_lms_dd_equalizer_cc.i \ - digital_kurtotic_equalizer_cc.i + digital_kurtotic_equalizer_cc.i \ + digital_mpsk_receiver_cc.i digital_swig_swig_args = \ -I$(abs_top_srcdir)/gr-digital/lib \ diff --git a/gr-digital/swig/digital_mpsk_receiver_cc.i b/gr-digital/swig/digital_mpsk_receiver_cc.i new file mode 100644 index 000000000..cdc9f661b --- /dev/null +++ b/gr-digital/swig/digital_mpsk_receiver_cc.i @@ -0,0 +1,60 @@ +/* -*- 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 alpha, float beta, + 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 +{ + private: + digital_mpsk_receiver_cc (unsigned int M,float theta, + float alpha, float beta, + float fmin, float fmax, + float mu, float gain_mu, + float omega, float gain_omega, float omega_rel); +public: + 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;} + 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_gain_mu (float gain_mu) { d_gain_mu = gain_mu; } + void set_gain_omega (float gain_omega) { d_gain_omega = gain_omega; } + float alpha() const { return d_alpha; } + float beta() const { return d_beta; } + float freq() const { return d_freq; } + float phase() const { return d_phase; } + void set_alpha(float alpha) { d_alpha = alpha; } + void set_beta(float beta) { d_beta = beta; } + void set_freq(float freq) { d_freq = freq; } + void set_phase(float phase) { d_phase = phase; } +}; diff --git a/gr-digital/swig/digital_swig.i b/gr-digital/swig/digital_swig.i index bc7c9b0d9..cfaadcaea 100644 --- a/gr-digital/swig/digital_swig.i +++ b/gr-digital/swig/digital_swig.i @@ -34,6 +34,7 @@ #include "digital_crc32.h" #include "digital_kurtotic_equalizer_cc.h" #include "digital_lms_dd_equalizer_cc.h" +#include "digital_mpsk_receiver_cc.h" %} %include "digital_binary_slicer_fb.i" @@ -48,6 +49,7 @@ %include "digital_crc32.i" %include "digital_kurtotic_equalizer_cc.i" %include "digital_lms_dd_equalizer_cc.i" +%include "digital_mpsk_receiver_cc.i" #if SWIGGUILE %scheme %{ |