diff options
author | Tom Rondeau | 2011-07-30 11:28:47 -0400 |
---|---|---|
committer | Tom Rondeau | 2011-07-30 11:28:47 -0400 |
commit | e1b4e60823c2b938500932b0868d88698e48d525 (patch) | |
tree | 290554eb452d906b073aaec3896985628a483bc5 | |
parent | 8b3c4ccf922c602ae77dad7f3911b16bdd0112d3 (diff) | |
download | gnuradio-e1b4e60823c2b938500932b0868d88698e48d525.tar.gz gnuradio-e1b4e60823c2b938500932b0868d88698e48d525.tar.bz2 gnuradio-e1b4e60823c2b938500932b0868d88698e48d525.zip |
core: made significant improvements in FLL algorithm. Now converges very nicely.
-rw-r--r-- | gnuradio-core/src/lib/general/gr_fll_band_edge_cc.cc | 259 | ||||
-rw-r--r-- | gnuradio-core/src/lib/general/gr_fll_band_edge_cc.h | 195 | ||||
-rw-r--r-- | gnuradio-core/src/lib/general/gr_fll_band_edge_cc.i | 23 |
3 files changed, 384 insertions, 93 deletions
diff --git a/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.cc b/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.cc index c32398e6d..65e273e8a 100644 --- a/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.cc +++ b/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2009,2010 Free Software Foundation, Inc. + * Copyright 2009-2011 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -25,9 +25,6 @@ #endif #include <gr_fll_band_edge_cc.h> -#include <gr_fir_ccc.h> -#include <gr_fir_util.h> -#include <gri_fft.h> #include <gr_io_signature.h> #include <gr_expj.h> #include <gr_math.h> @@ -45,110 +42,246 @@ float sinc(float x) -gr_fll_band_edge_cc_sptr gr_make_fll_band_edge_cc (float samps_per_sym, float rolloff, - int filter_size, float gain_alpha, float gain_beta) +gr_fll_band_edge_cc_sptr +gr_make_fll_band_edge_cc (float samps_per_sym, float rolloff, + int filter_size, float bandwidth) { return gnuradio::get_initial_sptr(new gr_fll_band_edge_cc (samps_per_sym, rolloff, - filter_size, gain_alpha, gain_beta)); + filter_size, bandwidth)); } -static int ios[] = {sizeof(gr_complex), sizeof(float), sizeof(float), sizeof(gr_complex)}; +static int ios[] = {sizeof(gr_complex), sizeof(float), sizeof(float), sizeof(float)}; static std::vector<int> iosig(ios, ios+sizeof(ios)/sizeof(int)); gr_fll_band_edge_cc::gr_fll_band_edge_cc (float samps_per_sym, float rolloff, - int filter_size, float alpha, float beta) + int filter_size, float bandwidth) : gr_sync_block ("fll_band_edge_cc", gr_make_io_signature (1, 1, sizeof(gr_complex)), gr_make_io_signaturev (1, 4, iosig)), - d_alpha(alpha), d_beta(beta), d_updated (false) + d_updated (false) { + // Initialize samples per symbol + if(samps_per_sym <= 0) { + throw std::out_of_range ("gr_fll_band_edge_cc: invalid number of sps. Must be > 0."); + } + d_sps = samps_per_sym; + + // Initialize rolloff factor + if(rolloff < 0 || rolloff > 1.0) { + throw std::out_of_range ("gr_fll_band_edge_cc: invalid rolloff factor. Must be in [0,1]."); + } + d_rolloff = rolloff; + + // Initialize filter length + if(filter_size <= 0) { + throw std::out_of_range ("gr_fll_band_edge_cc: invalid filter size. Must be > 0."); + } + d_filter_size = filter_size; + + // Initialize loop bandwidth + if(bandwidth < 0) { + throw std::out_of_range ("gr_fll_band_edge_cc: invalid bandwidth. Must be >= 0."); + } + d_loop_bw = bandwidth; + // base this on the number of samples per symbol d_max_freq = M_TWOPI * (2.0/samps_per_sym); d_min_freq = -M_TWOPI * (2.0/samps_per_sym); + // Set the damping factor for a critically damped system + d_damping = sqrt(2.0)/2.0; + + // Set the bandwidth, which will then call update_gains() + set_loop_bandwidth(bandwidth); + + // Build the band edge filters + design_filter(d_sps, d_rolloff, d_filter_size); + + // Initialize loop values d_freq = 0; d_phase = 0; +} - set_alpha(alpha); +gr_fll_band_edge_cc::~gr_fll_band_edge_cc () +{ +} - design_filter(samps_per_sym, rolloff, filter_size); +void +gr_fll_band_edge_cc::set_loop_bandwidth(float bw) +{ + if(bw < 0) { + throw std::out_of_range ("gr_fll_band_edge_cc: invalid bandwidth. Must be >= 0."); + } + + d_loop_bw = bw; + update_gains(); } -gr_fll_band_edge_cc::~gr_fll_band_edge_cc () +void +gr_fll_band_edge_cc::set_damping_factor(float df) { - delete d_filter_lower; - delete d_filter_upper; + if(df < 0 || df > 1.0) { + throw std::out_of_range ("gr_fll_band_edge_cc: invalid damping factor. Must be in [0,1]."); + } + + d_damping = df; + update_gains(); } + void -gr_fll_band_edge_cc::set_alpha(float alpha) -{ - //float eta = sqrt(2.0)/2.0; - //float theta = alpha; - //d_alpha = (4*eta*theta) / (1.0 + 2.0*eta*theta + theta*theta); - //d_beta = (4*theta*theta) / (1.0 + 2.0*eta*theta + theta*theta); +gr_fll_band_edge_cc::set_alpha(float alpha) +{ + if(alpha < 0 || alpha > 1.0) { + throw std::out_of_range ("gr_fll_band_edge_cc: invalid alpha. Must be in [0,1]."); + } d_alpha = alpha; } void +gr_fll_band_edge_cc::set_beta(float beta) +{ + if(beta < 0 || beta > 1.0) { + throw std::out_of_range ("gr_fll_band_edge_cc: invalid beta. Must be in [0,1]."); + } + d_beta = beta; +} + +void +gr_fll_band_edge_cc::set_samples_per_symbol(float sps) +{ + if(sps <= 0) { + throw std::out_of_range ("gr_fll_band_edge_cc: invalid number of sps. Must be > 0."); + } + d_sps = sps; + design_filter(d_sps, d_rolloff, d_filter_size); +} + +void +gr_fll_band_edge_cc::set_rolloff(float rolloff) +{ + if(rolloff < 0 || rolloff > 1.0) { + throw std::out_of_range ("gr_fll_band_edge_cc: invalid rolloff factor. Must be in [0,1]."); + } + d_rolloff = rolloff; + design_filter(d_sps, d_rolloff, d_filter_size); +} + +void +gr_fll_band_edge_cc::set_filter_size(int filter_size) +{ + if(filter_size <= 0) { + throw std::out_of_range ("gr_fll_band_edge_cc: invalid filter size. Must be > 0."); + } + d_filter_size = filter_size; + design_filter(d_sps, d_rolloff, d_filter_size); +} + +float +gr_fll_band_edge_cc::get_loop_bandwidth() +{ + return d_loop_bw; +} + +float +gr_fll_band_edge_cc::get_damping_factor() +{ + return d_damping; +} + +float +gr_fll_band_edge_cc::get_alpha() +{ + return d_alpha; +} + +float +gr_fll_band_edge_cc::get_beta() +{ + return d_beta; +} + +float +gr_fll_band_edge_cc::get_samples_per_symbol() +{ + return d_sps; +} + +float +gr_fll_band_edge_cc::get_rolloff() +{ + return d_rolloff; +} + +int +gr_fll_band_edge_cc:: get_filter_size() +{ + return d_filter_size; +} + +void +gr_fll_band_edge_cc::update_gains() +{ + float denom = (1.0 + 2.0*d_damping*d_loop_bw + d_loop_bw*d_loop_bw); + d_alpha = (4*d_damping*d_loop_bw) / denom; + d_beta = (4*d_loop_bw*d_loop_bw) / denom; +} + +void gr_fll_band_edge_cc::design_filter(float samps_per_sym, float rolloff, int filter_size) { int M = rint(filter_size / samps_per_sym); float power = 0; + + // Create the baseband filter by adding two sincs together std::vector<float> bb_taps; for(int i = 0; i < filter_size; i++) { float k = -M + i*2.0/samps_per_sym; float tap = sinc(rolloff*k - 0.5) + sinc(rolloff*k + 0.5); power += tap; - + bb_taps.push_back(tap); } + d_taps_lower.resize(filter_size); + d_taps_upper.resize(filter_size); + + // Create the band edge filters by spinning the baseband + // filter up and down to the right places in frequency. + // Also, normalize the power in the filters int N = (bb_taps.size() - 1.0)/2.0; - std::vector<gr_complex> taps_lower; - std::vector<gr_complex> taps_upper; - for(unsigned int i = 0; i < bb_taps.size(); i++) { + for(int i = 0; i < filter_size; i++) { float tap = bb_taps[i] / power; - + float k = (-N + (int)i)/(2.0*samps_per_sym); - gr_complex t1 = tap * gr_expj(-2*M_PI*(1+rolloff)*k); - gr_complex t2 = tap * gr_expj(2*M_PI*(1+rolloff)*k); - - taps_lower.push_back(t1); - taps_upper.push_back(t2); + gr_complex t1 = tap * gr_expj(-M_TWOPI*(1+rolloff)*k); + gr_complex t2 = tap * gr_expj(M_TWOPI*(1+rolloff)*k); + + d_taps_lower[filter_size-i-1] = t1; + d_taps_upper[filter_size-i-1] = t2; } - - std::vector<gr_complex> vtaps(0, taps_lower.size()); - d_filter_upper = gr_fir_util::create_gr_fir_ccc(vtaps); - d_filter_lower = gr_fir_util::create_gr_fir_ccc(vtaps); - - d_filter_lower->set_taps(taps_lower); - d_filter_upper->set_taps(taps_upper); - + d_updated = true; // Set the history to ensure enough input items for each filter set_history(filter_size+1); - } void gr_fll_band_edge_cc::print_taps() { unsigned int i; - std::vector<gr_complex> taps_upper = d_filter_upper->get_taps(); - std::vector<gr_complex> taps_lower = d_filter_lower->get_taps(); printf("Upper Band-edge: ["); - for(i = 0; i < taps_upper.size(); i++) { - printf(" %.4e + %.4ej,", taps_upper[i].real(), taps_upper[i].imag()); + for(i = 0; i < d_taps_upper.size(); i++) { + printf(" %.4e + %.4ej,", d_taps_upper[i].real(), d_taps_upper[i].imag()); } printf("]\n\n"); printf("Lower Band-edge: ["); - for(i = 0; i < taps_lower.size(); i++) { - printf(" %.4e + %.4ej,", taps_lower[i].real(), taps_lower[i].imag()); + for(i = 0; i < d_taps_lower.size(); i++) { + printf(" %.4e + %.4ej,", d_taps_lower[i].real(), d_taps_lower[i].imag()); } printf("]\n\n"); } @@ -163,11 +296,11 @@ gr_fll_band_edge_cc::work (int noutput_items, float *frq = NULL; float *phs = NULL; - gr_complex *err = NULL; - if(output_items.size() > 2) { + float *err = NULL; + if(output_items.size() == 4) { frq = (float *) output_items[1]; phs = (float *) output_items[2]; - err = (gr_complex *) output_items[3]; + err = (float *) output_items[3]; } if (d_updated) { @@ -176,21 +309,24 @@ gr_fll_band_edge_cc::work (int noutput_items, } int i; + float error; gr_complex nco_out; gr_complex out_upper, out_lower; - float error; - float avg_k = 0.1; for(i = 0; i < noutput_items; i++) { nco_out = gr_expj(d_phase); - out[i] = in[i] * nco_out; - - out_upper = (d_filter_upper->filter(&out[i])); - out_lower = (d_filter_lower->filter(&out[i])); - error = -real((out_upper + out_lower) * conj(out_upper - out_lower)); - d_error = avg_k*error + avg_k*d_error; // average error + out[i+d_filter_size-1] = in[i] * nco_out; + + // Perform the dot product of the output with the filters + out_upper = 0; + out_lower = 0; + for(int k = 0; k < d_filter_size; k++) { + out_upper += d_taps_upper[k] * out[i+k]; + out_lower += d_taps_lower[k] * out[i+k]; + } + error = norm(out_lower) - norm(out_upper); - d_freq = d_freq + d_beta * d_error; - d_phase = d_phase + d_freq + d_alpha * d_error; + d_freq = d_freq + d_beta * error; + d_phase = d_phase + d_freq + d_alpha * error; if(d_phase > M_PI) d_phase -= M_TWOPI; @@ -202,13 +338,12 @@ gr_fll_band_edge_cc::work (int noutput_items, else if (d_freq < d_min_freq) d_freq = d_min_freq; - if(output_items.size() > 2) { + if(output_items.size() == 4) { frq[i] = d_freq; phs[i] = d_phase; - err[i] = d_error; + err[i] = error; } } - return noutput_items; } diff --git a/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.h b/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.h index db060793e..dba5e6e26 100644 --- a/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.h +++ b/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2009 Free Software Foundation, Inc. + * Copyright 2009,2011 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -29,10 +29,7 @@ class gr_fll_band_edge_cc; typedef boost::shared_ptr<gr_fll_band_edge_cc> gr_fll_band_edge_cc_sptr; gr_fll_band_edge_cc_sptr gr_make_fll_band_edge_cc (float samps_per_sym, float rolloff, - int filter_size, float alpha, float beta); - -class gr_fir_ccc; -class gri_fft_complex; + int filter_size, float bandwidth); /*! * \class gr_fll_band_edge_cc @@ -52,6 +49,9 @@ class gri_fft_complex; * provides an error signal at the DC term that is directly proportional to the carrier frequency. * We then make a second-order loop using the error signal that is the running average of e(t). * + * In practice, the above equation can be simplified by just comparing the absolute value squared + * of the output of both filters: abs(x_l(t))^2 - abs(x_u(t))^2 = norm(x_l(t)) - norm(x_u(t)). + * * In theory, the band-edge filter is the derivative of the matched filter in frequency, * (H_be(f) = \\frac{H(f)}{df}. In practice, this comes down to a quarter sine wave at the point * of the matched filter's rolloff (if it's a raised-cosine, the derivative of a cosine is a sine). @@ -63,6 +63,10 @@ class gri_fft_complex; * * Note: We use FIR filters here because the filters have to have a flat phase response over the * entire frequency range to allow their comparisons to be valid. + * + * It is very important that the band edge filters be the derivatives of the pulse shaping filter, + * and that they be linear phase. Otherwise, the variance of the error will be very large. + * */ class gr_fll_band_edge_cc : public gr_sync_block @@ -73,58 +77,199 @@ class gr_fll_band_edge_cc : public gr_sync_block * \param samps_per_sym (float) Number of samples per symbol of signal * \param rolloff (float) Rolloff factor of signal * \param filter_size (int) Size (in taps) of the filter - * \param alpha (float) Loop gain 1 - * \param beta (float) Loop gain 2 + * \param bandwdith (float) Loop bandwidth */ friend gr_fll_band_edge_cc_sptr gr_make_fll_band_edge_cc (float samps_per_sym, float rolloff, - int filter_size, float alpha, float beta); + int filter_size, float bandwidth); - float d_alpha; - float d_beta; + float d_sps; + float d_rolloff; + int d_filter_size; float d_max_freq; float d_min_freq; - gr_fir_ccc* d_filter_upper; - gr_fir_ccc* d_filter_lower; + float d_loop_bw; + float d_damping; + float d_alpha; + float d_beta; + + std::vector<gr_complex> d_taps_lower; + std::vector<gr_complex> d_taps_upper; bool d_updated; - float d_error; + float d_freq; - float d_phase; + float d_phase; /*! * Build the FLL * \param samps_per_sym (float) number of samples per symbol * \param rolloff (float) Rolloff (excess bandwidth) of signal filter * \param filter_size (int) number of filter taps to generate - * \param alpha (float) Alpha gain in the control loop - * \param beta (float) Beta gain in the control loop + * \param bandwidth (float) Loop bandwidth */ gr_fll_band_edge_cc(float samps_per_sym, float rolloff, - int filter_size, float alpha, float beta); - -public: - ~gr_fll_band_edge_cc (); + int filter_size, float bandwidth); /*! + * \brief Update the gains, alpha and beta, of the loop filter. + */ + void update_gains(); + + /*! * Design the band-edge filter based on the number of samples per symbol, * filter rolloff factor, and the filter size + * * \param samps_per_sym (float) Number of samples per symbol of signal * \param rolloff (float) Rolloff factor of signal * \param filter_size (int) Size (in taps) of the filter */ void design_filter(float samps_per_sym, float rolloff, int filter_size); +public: + ~gr_fll_band_edge_cc (); + + /******************************************************************* + SET FUNCTIONS + *******************************************************************/ + + /*! + * \brief Set the loop bandwidth + * + * Set the loop filter's bandwidth to \p bw. This should be between + * 2*pi/200 and 2*pi/100 (in rads/samp). It must also be a positive + * number. + * + * When a new damping factor is set, the gains, alpha and beta, of the loop + * are recalculated by a call to update_gains(). + * + * \param bw (float) new bandwidth + * + */ + void set_loop_bandwidth(float bw); + + /*! + * \brief Set the loop damping factor + * + * Set the loop filter's damping factor to \p df. The damping factor + * should be sqrt(2)/2.0 for critically damped systems. + * Set it to anything else only if you know what you are doing. It must + * be a number between 0 and 1. + * + * When a new damping factor is set, the gains, alpha and beta, of the loop + * are recalculated by a call to update_gains(). + * + * \param df (float) new damping factor + * + */ + void set_damping_factor(float df); + /*! - * Set the alpha gainvalue - * \param alpha (float) new gain value + * \brief Set the loop gain alpha + * + * Set's the loop filter's alpha gain parameter. + * + * This value should really only be set by adjusting the loop bandwidth + * and damping factor. + * + * \param alpha (float) new alpha gain + * */ void set_alpha(float alpha); /*! - * Set the beta gain value - * \param beta (float) new gain value + * \brief Set the loop gain beta + * + * Set's the loop filter's beta gain parameter. + * + * This value should really only be set by adjusting the loop bandwidth + * and damping factor. + * + * \param beta (float) new beta gain + * + */ + void set_beta(float beta); + + /*! + * \brief Set the number of samples per symbol + * + * Set's the number of samples per symbol the system should use. This value + * is uesd to calculate the filter taps and will force a recalculation. + * + * \param sps (float) new samples per symbol + * + */ + void set_samples_per_symbol(float sps); + + /*! + * \brief Set the rolloff factor of the shaping filter + * + * This sets the rolloff factor that is used in the pulse shaping filter + * and is used to calculate the filter taps. Changing this will force a + * recalculation of the filter taps. + * + * This should be the same value that is used in the transmitter's pulse + * shaping filter. It must be between 0 and 1 and is usually between + * 0.2 and 0.5 (where 0.22 and 0.35 are commonly used values). + * + * \param rolloff (float) new shaping filter rolloff factor [0,1] + * + */ + void set_rolloff(float rolloff); + + /*! + * \brief Set the number of taps in the filter + * + * This sets the number of taps in the band-edge filters. Setting this will + * force a recalculation of the filter taps. + * + * This should be about the same number of taps used in the transmitter's + * shaping filter and also not very large. A large number of taps will + * result in a large delay between input and frequency estimation, and + * so will not be as accurate. Between 30 and 70 taps is usual. + * + * \param filter_size (float) number of taps in the filters + * + */ + void set_filter_size(int filter_size); + + /******************************************************************* + GET FUNCTIONS + *******************************************************************/ + + /*! + * \brief Returns the loop bandwidth + */ + float get_loop_bandwidth(); + + /*! + * \brief Returns the loop damping factor + */ + float get_damping_factor(); + + /*! + * \brief Returns the loop gain alpha + */ + float get_alpha(); + + /*! + * \brief Returns the loop gain beta + */ + float get_beta(); + + /*! + * \brief Returns the number of sampler per symbol used for the filter + */ + float get_samples_per_symbol(); + + /*! + * \brief Returns the rolloff factor used for the filter + */ + float get_rolloff(); + + /*! + * \brief Returns the number of taps of the filter */ - void set_beta(float beta) { d_beta = beta; } + int get_filter_size(); /*! * Print the taps to screen. diff --git a/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.i b/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.i index c9c792c8a..3617dc395 100644 --- a/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.i +++ b/gnuradio-core/src/lib/general/gr_fll_band_edge_cc.i @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2009 Free Software Foundation, Inc. + * Copyright 2009,2011 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -23,19 +23,30 @@ GR_SWIG_BLOCK_MAGIC(gr,fll_band_edge_cc); gr_fll_band_edge_cc_sptr gr_make_fll_band_edge_cc (float samps_per_sym, float rolloff, - int filter_size, float alpha, float beta); + int filter_size, float bandwidth); class gr_fll_band_edge_cc : public gr_sync_block { private: gr_fll_band_edge_cc (float samps_per_sym, float rolloff, - int filter_size, float alpha, float beta); + int filter_size, float bandwidth); public: ~gr_fll_band_edge_cc (); - void set_alpha (float alpha); - void set_beta (float beta); - void design_filter(float samps_per_sym, float rolloff, int filter_size); + void set_loop_bandwidth(float bw); + void set_damping_factor(float df); + void set_alpha(float alpha); + void set_beta(float beta); + void set_samples_per_symbol(float sps); + void set_rolloff(float rolloff); + void set_filter_size(int filter_size); + float get_loop_bandwidth(); + float get_damping_factor(); + float get_alpha(); + float get_beta(); + float get_samples_per_symbol(); + float get_rolloff(); + int get_filter_size(); void print_taps(); }; |