From bffa335c433610c1002ff6220407fdf9c76cb9b4 Mon Sep 17 00:00:00 2001 From: max Date: Wed, 16 Jun 2010 19:16:46 +0000 Subject: [PATCH] add modified costas git-svn-id: http://op25.osmocom.org/svn/trunk@213 65a5c917-d112-43f1-993d-58c26a4786be --- repeater/src/lib/Makefile.am | 2 + repeater/src/lib/repeater.i | 26 +++ repeater/src/lib/repeater_costas_loop_cc.cc | 189 ++++++++++++++++++++ repeater/src/lib/repeater_costas_loop_cc.h | 142 +++++++++++++++ 4 files changed, 359 insertions(+) create mode 100644 repeater/src/lib/repeater_costas_loop_cc.cc create mode 100644 repeater/src/lib/repeater_costas_loop_cc.h diff --git a/repeater/src/lib/Makefile.am b/repeater/src/lib/Makefile.am index d454d59..7895a33 100644 --- a/repeater/src/lib/Makefile.am +++ b/repeater/src/lib/Makefile.am @@ -67,6 +67,7 @@ _repeater_la_SOURCES = repeater\ repeater_ctcss_squelch_ff.cc \ repeater_squelch_base_ff.cc \ repeater_gardner_symbol_recovery_cc.cc \ + repeater_costas_loop_cc.cc \ bch.cc # magic flags @@ -88,6 +89,7 @@ grinclude_HEADERS = \ repeater_imbe_decode_fb.h \ repeater_pipe.h \ repeater_gardner_symbol_recovery_cc.h \ + repeater_costas_loop_cc.h \ repeater_ctcss_squelch_ff.h diff --git a/repeater/src/lib/repeater.i b/repeater/src/lib/repeater.i index 1688417..031d930 100644 --- a/repeater/src/lib/repeater.i +++ b/repeater/src/lib/repeater.i @@ -14,6 +14,7 @@ #include "repeater_pipe.h" #include "repeater_ctcss_squelch_ff.h" #include "repeater_gardner_symbol_recovery_cc.h" +#include "repeater_costas_loop_cc.h" #include %} @@ -126,3 +127,28 @@ class repeater_gardner_symbol_recovery_cc : public gr_sync_block private: repeater_gardner_symbol_recovery_cc (int samples_per_symbol, float timing_error_gain); }; + +// ---------------------------------------------------------------- + +GR_SWIG_BLOCK_MAGIC(repeater,costas_loop_cc); + +repeater_costas_loop_cc_sptr +repeater_make_costas_loop_cc (float alpha, float beta, + float max_freq, float min_freq, + int order + ) throw (std::invalid_argument); + + +class repeater_costas_loop_cc : public gr_sync_block +{ + private: + repeater_costas_loop_cc (float alpha, float beta, + float max_freq, float min_freq, int order); + + public: + void set_alpha(float alpha); + float alpha(); + void set_beta(float beta); + float beta(); + float freq(); +}; diff --git a/repeater/src/lib/repeater_costas_loop_cc.cc b/repeater/src/lib/repeater_costas_loop_cc.cc new file mode 100644 index 0000000..b208d10 --- /dev/null +++ b/repeater/src/lib/repeater_costas_loop_cc.cc @@ -0,0 +1,189 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006 Free Software Foundation, Inc. + * + * PI/4 DQPSK hack Copyright 2010 KA1RBI + * + * 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 +#include +#include +#include +#include + +#define M_TWOPI (2*M_PI) +static const gr_complex PT_45 = gr_expj( M_PI / 4.0 ); + +repeater_costas_loop_cc_sptr +repeater_make_costas_loop_cc (float alpha, float beta, + float max_freq, float min_freq, + int order + ) throw (std::invalid_argument) +{ + return repeater_costas_loop_cc_sptr (new repeater_costas_loop_cc (alpha, beta, + max_freq, min_freq, + order)); +} + +repeater_costas_loop_cc::repeater_costas_loop_cc (float alpha, float beta, + float max_freq, float min_freq, + int order + ) throw (std::invalid_argument) + : gr_sync_block ("costas_loop_cc", + gr_make_io_signature (1, 1, sizeof (gr_complex)), + gr_make_io_signature (1, 2, sizeof (gr_complex))), + d_alpha(alpha), d_beta(beta), + d_max_freq(max_freq), d_min_freq(min_freq), + d_phase(0), d_freq((max_freq+min_freq)/2), + d_order(order), d_phase_detector(0), + d_interp_counter(0), d_rot45(false) +{ + if (d_order < 0) { + d_rot45 = true; + d_order = 0 - d_order; + } + switch(d_order) { + case 2: + d_phase_detector = &repeater_costas_loop_cc::phase_detector_2; + break; + + case 4: + d_phase_detector = &repeater_costas_loop_cc::phase_detector_4; + break; + + default: + throw std::invalid_argument("order must be 2 or 4"); + break; + } +} + + +float +repeater_costas_loop_cc::phase_detector_4(gr_complex sample) const +{ + + return ((sample.real()>0 ? 1.0 : -1.0) * sample.imag() - + (sample.imag()>0 ? 1.0 : -1.0) * sample.real()); +} + +float +repeater_costas_loop_cc::phase_detector_2(gr_complex sample) const +{ + return (sample.real()*sample.imag()); +} + +void +repeater_costas_loop_cc::set_alpha(float alpha) +{ + d_alpha = alpha; +} + +void +repeater_costas_loop_cc::set_beta(float beta) +{ + d_beta = beta; +} + +int +repeater_costas_loop_cc::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const gr_complex *iptr = (gr_complex *) input_items[0]; + gr_complex *optr = (gr_complex *) output_items[0]; + gr_complex *foptr = (gr_complex *) output_items[1]; + + bool write_foptr = output_items.size() >= 2; + + float error; + gr_complex nco_out; + gr_complex rotated_sample; + + if (write_foptr) { + + for (int i = 0; i < noutput_items; i++){ + nco_out = gr_expj(-d_phase); + rotated_sample = optr[i] = iptr[i] * nco_out; + + if (d_rot45) { + if (d_interp_counter & 1) // every other symbol + rotated_sample = rotated_sample * PT_45; // rotate by +45 deg + d_interp_counter++; + } + + error = (*this.*d_phase_detector)(rotated_sample); + if (error > 1) + error = 1; + else if (error < -1) + error = -1; + + d_freq = d_freq + d_beta * error; + d_phase = d_phase + d_freq + d_alpha * error; + + while(d_phase>M_TWOPI) + d_phase -= M_TWOPI; + while(d_phase<-M_TWOPI) + d_phase += M_TWOPI; + + if (d_freq > d_max_freq) + d_freq = d_max_freq; + else if (d_freq < d_min_freq) + d_freq = d_min_freq; + + foptr[i] = gr_complex(d_freq,0); + } + } else { + for (int i = 0; i < noutput_items; i++){ + nco_out = gr_expj(-d_phase); + rotated_sample = optr[i] = iptr[i] * nco_out; + + if (d_rot45) { + if (d_interp_counter & 1) // every other symbol + rotated_sample = rotated_sample * PT_45; // rotate by +45 deg + d_interp_counter++; + } + + error = (*this.*d_phase_detector)(rotated_sample); + if (error > 1) + error = 1; + else if (error < -1) + error = -1; + + d_freq = d_freq + d_beta * error; + d_phase = d_phase + d_freq + d_alpha * error; + + while(d_phase>M_TWOPI) + d_phase -= M_TWOPI; + while(d_phase<-M_TWOPI) + d_phase += M_TWOPI; + + if (d_freq > d_max_freq) + d_freq = d_max_freq; + else if (d_freq < d_min_freq) + d_freq = d_min_freq; + + } + } + return noutput_items; +} diff --git a/repeater/src/lib/repeater_costas_loop_cc.h b/repeater/src/lib/repeater_costas_loop_cc.h new file mode 100644 index 0000000..f82d0ec --- /dev/null +++ b/repeater/src/lib/repeater_costas_loop_cc.h @@ -0,0 +1,142 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006 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_REPEATER_COSTAS_LOOP_CC_H +#define INCLUDED_REPEATER_COSTAS_LOOP_CC_H + +#include +#include +#include + + +/*! \brief A Costas loop carrier recovery module. + * \ingroup sync_blk + * + * The Costas loop locks to the center frequency of a signal and + * downconverts it to baseband. The second (order=2) order loop is + * used for BPSK where the real part of the output signal is the + * baseband BPSK signal and the imaginary part is the error + * signal. When order=4, it can be used for quadrature modulations + * where both I and Q (real and imaginary) are outputted. + * + * More details can be found online: + * + * J. Feigin, "Practical Costas loop design: Designing a simple and inexpensive + * BPSK Costas loop carrier recovery circuit," RF signal processing, pp. 20-36, + * 2002. + * + * http://rfdesign.com/images/archive/0102Feigin20.pdf + * + * \param alpha the loop gain used for phase adjustment + * \param beta the loop gain for frequency adjustments + * \param max_freq the maximum frequency deviation (radians/sample) the loop can handle + * \param min_freq the minimum frequency deviation (radians/sample) the loop can handle + * \param order the loop order, either 2 or 4 (enable PI/4 hack if order < 0) + */ +class repeater_costas_loop_cc; +typedef boost::shared_ptr repeater_costas_loop_cc_sptr; + + +repeater_costas_loop_cc_sptr +repeater_make_costas_loop_cc (float alpha, float beta, + float max_freq, float min_freq, + int order + ) throw (std::invalid_argument); + + +/*! + * \brief Carrier tracking PLL for QPSK + * \ingroup sync_blk + * input: complex; output: complex + *
The Costas loop can have two output streams: + * stream 1 is the baseband I and Q; + * stream 2 is the normalized frequency of the loop + * + * \p order must be 2 or 4. + */ +class repeater_costas_loop_cc : public gr_sync_block +{ + friend repeater_costas_loop_cc_sptr repeater_make_costas_loop_cc (float alpha, float beta, + float max_freq, float min_freq, + int order + ) throw (std::invalid_argument); + + float d_alpha, d_beta, d_max_freq, d_min_freq, d_phase, d_freq; + int d_order; + + repeater_costas_loop_cc (float alpha, float beta, + float max_freq, float min_freq, + int order + ) throw (std::invalid_argument); + + /*! \brief the phase detector circuit for fourth-order loops + * \param sample complex sample + * \return the phase error + */ + float phase_detector_4(gr_complex sample) const; // for QPSK + + /*! \brief the phase detector circuit for second-order loops + * \param sample a complex sample + * \return the phase error + */ + float phase_detector_2(gr_complex sample) const; // for BPSK + + + float (repeater_costas_loop_cc::*d_phase_detector)(gr_complex sample) const; + + unsigned int d_interp_counter; + bool d_rot45; + +public: + + /*! \brief set the first order gain + * \param alpha + */ + void set_alpha(float alpha); + + /*! \brief get the first order gain + * + */ + float alpha() const { return d_alpha; } + + /*! \brief set the second order gain + * \param beta + */ + void set_beta(float beta); + + /*! \brief get the second order gain + * + */ + float beta() const { return d_beta; } + + int work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + /*! \brief returns the current NCO frequency in radians/sample + * + */ + float freq() const { return d_freq; } +}; + +#endif