diff options
author | Johnathan Corgan <jcorgan@corganenterprises.com> | 2009-10-12 18:46:56 -0700 |
---|---|---|
committer | Johnathan Corgan <jcorgan@corganenterprises.com> | 2009-10-12 18:46:56 -0700 |
commit | 4df83569c130c1f4fe2aba3e1b5dd1419272f22f (patch) | |
tree | f64ccf30880228ab639ff06c971603e214b6303f /gnuradio-core/src/lib | |
parent | 83ddffdaa968c5131228346b5f3f2ca123b759c7 (diff) | |
parent | 3eeb2720664ec7cb67e60d4f8a01b933ddba0143 (diff) |
Merge branch 'sync' of git@gnuradio.org:trondeau into master
This merge adds polyphase clock recovery, implements new PAM demodulators
that use it, and also moves GRC examples to gnuradio-examples component.
See merge commit diff for updated files post merge.
* 'sync' of git@gnuradio.org:trondeau: (54 commits)
Reverting dqpsk to be mpsk_receiver based and not change its behavior.
Fixing initialization of timing gains. Alpha should be < 1, and beta should be << 1.
This splits the rate into a fractional an integer value, which allows the loop to adjust the fractional rate while the integer rate keeps the increments moving properly. Allows the max rate deviation to be independent of the integer rate. Scaling of the differential taps also allows alpha and beta to operate independent of the rate when fractional samples per symbol are used. Slightly more tolerant to large signal values, but they still should be close to +/-1.
Fixing compiler warnings.
Adding dqpsk2 block to makefile for installation.
Adding DQPSK version that uses the PFB timing sync block (dqpsk2).
Minor fixes for logging.
Fixing import of UI file.
Temporary working dbpsk2 example until we match everything.
Working DBPSK implementation with new PFB clock recovery block. The feedforward AGC wasn't playing nicely, the frequency aquistion range was increased to swing half the sample rate in either direction, and the number of filter phases to use was increased to 32.
A bit of code cleanup.
Starting to rework QT app to control new PFB clock recovery alg.
Making old dbpsk work again to compare against new version.
Adding new DBPSK block with new PFB clock recovery alg.
Revert "More additions to PAM timing simulation."
More additions to PAM timing simulation.
Using 2-PAM by default.
Cleaning up GRC PAM timing example and adding ability to do M-ary PAM.
Cleaning up constructor.
Moving filter number decision to start work function.
...
Diffstat (limited to 'gnuradio-core/src/lib')
4 files changed, 139 insertions, 89 deletions
diff --git a/gnuradio-core/src/lib/filter/gr_pfb_clock_sync_ccf.cc b/gnuradio-core/src/lib/filter/gr_pfb_clock_sync_ccf.cc index 91cbf74c6b..433b7d6133 100644 --- a/gnuradio-core/src/lib/filter/gr_pfb_clock_sync_ccf.cc +++ b/gnuradio-core/src/lib/filter/gr_pfb_clock_sync_ccf.cc @@ -33,45 +33,51 @@ #include <gr_io_signature.h> #include <gr_math.h> -gr_pfb_clock_sync_ccf_sptr gr_make_pfb_clock_sync_ccf (float sps, float gain, +gr_pfb_clock_sync_ccf_sptr gr_make_pfb_clock_sync_ccf (double sps, float gain, const std::vector<float> &taps, unsigned int filter_size, - float init_phase) + float init_phase, + float max_rate_deviation) { return gr_pfb_clock_sync_ccf_sptr (new gr_pfb_clock_sync_ccf (sps, gain, taps, filter_size, - init_phase)); + init_phase, + max_rate_deviation)); } - -gr_pfb_clock_sync_ccf::gr_pfb_clock_sync_ccf (float sps, float gain, +int ios[] = {sizeof(gr_complex), sizeof(float), sizeof(float), sizeof(float)}; +std::vector<int> iosig(ios, ios+sizeof(ios)/sizeof(int)); +gr_pfb_clock_sync_ccf::gr_pfb_clock_sync_ccf (double sps, float gain, const std::vector<float> &taps, unsigned int filter_size, - float init_phase) + float init_phase, + float max_rate_deviation) : gr_block ("pfb_clock_sync_ccf", gr_make_io_signature (1, 1, sizeof(gr_complex)), - gr_make_io_signature2 (2, 2, sizeof(gr_complex), sizeof(float))), - d_updated (false), d_sps(sps), d_alpha(gain) + gr_make_io_signaturev (1, 4, iosig)), + d_updated (false), d_nfilters(filter_size), + d_max_dev(max_rate_deviation) { d_nfilters = filter_size; + d_sps = floor(sps); // Store the last filter between calls to work // The accumulator keeps track of overflow to increment the stride correctly. // set it here to the fractional difference based on the initial phaes - // assert(init_phase <= 2*M_PI); - float x = init_phase / (2*M_PI); //normalize initial phase - d_acc = x*(d_nfilters-1); - d_last_filter = (int)floor(d_acc); - d_acc = fmodf(d_acc, 1); - d_start_count = 0; - + set_alpha(gain); + set_beta(0.25*gain*gain); + d_k = init_phase; + d_rate = (sps-floor(sps))*(double)d_nfilters; + d_rate_i = (int)floor(d_rate); + d_rate_f = d_rate - (float)d_rate_i; + d_filtnum = (int)floor(d_k); d_filters = std::vector<gr_fir_ccf*>(d_nfilters); d_diff_filters = std::vector<gr_fir_ccf*>(d_nfilters); // Create an FIR filter for each channel and zero out the taps std::vector<float> vtaps(0, d_nfilters); - for(unsigned int i = 0; i < d_nfilters; i++) { + for(int i = 0; i < d_nfilters; i++) { d_filters[i] = gr_fir_util::create_gr_fir_ccf(vtaps); d_diff_filters[i] = gr_fir_util::create_gr_fir_ccf(vtaps); } @@ -85,7 +91,7 @@ gr_pfb_clock_sync_ccf::gr_pfb_clock_sync_ccf (float sps, float gain, gr_pfb_clock_sync_ccf::~gr_pfb_clock_sync_ccf () { - for(unsigned int i = 0; i < d_nfilters; i++) { + for(int i = 0; i < d_nfilters; i++) { delete d_filters[i]; } } @@ -95,7 +101,7 @@ gr_pfb_clock_sync_ccf::set_taps (const std::vector<float> &newtaps, std::vector< std::vector<float> > &ourtaps, std::vector<gr_fir_ccf*> &ourfilter) { - unsigned int i,j; + int i,j; unsigned int ntaps = newtaps.size(); d_taps_per_filter = (unsigned int)ceil((double)ntaps/(double)d_nfilters); @@ -114,13 +120,13 @@ gr_pfb_clock_sync_ccf::set_taps (const std::vector<float> &newtaps, // Partition the filter for(i = 0; i < d_nfilters; i++) { // Each channel uses all d_taps_per_filter with 0's if not enough taps to fill out - ourtaps[i] = std::vector<float>(d_taps_per_filter, 0); + ourtaps[d_nfilters-1-i] = std::vector<float>(d_taps_per_filter, 0); for(j = 0; j < d_taps_per_filter; j++) { - ourtaps[i][j] = tmp_taps[i + j*d_nfilters]; // add taps to channels in reverse order + ourtaps[d_nfilters - 1 - i][j] = tmp_taps[i + j*d_nfilters]; } // Build a filter for each channel and add it's taps to it - ourfilter[i]->set_taps(ourtaps[i]); + ourfilter[i]->set_taps(ourtaps[d_nfilters-1-i]); } // Set the history to ensure enough input items for each filter @@ -133,38 +139,53 @@ void gr_pfb_clock_sync_ccf::create_diff_taps(const std::vector<float> &newtaps, std::vector<float> &difftaps) { + float maxtap = 1e-20; difftaps.clear(); difftaps.push_back(0); //newtaps[0]); for(unsigned int i = 1; i < newtaps.size()-1; i++) { - difftaps.push_back(newtaps[i+1] - newtaps[i-1]); + float tap = newtaps[i+1] - newtaps[i-1]; + difftaps.push_back(tap); + if(tap > maxtap) { + maxtap = tap; + } } difftaps.push_back(0);//-newtaps[newtaps.size()-1]); + + // Scale the differential taps; helps scale error term to better update state + // FIXME: should this be scaled this way or use the same gain as the taps? + for(unsigned int i = 0; i < difftaps.size(); i++) { + difftaps[i] /= maxtap; + } } void gr_pfb_clock_sync_ccf::print_taps() { - unsigned int i, j; + int i, j; + printf("[ "); for(i = 0; i < d_nfilters; i++) { - printf("filter[%d]: [%.4e, ", i, d_taps[i][0]); + printf("[%.4e, ", d_taps[i][0]); for(j = 1; j < d_taps_per_filter-1; j++) { printf("%.4e,", d_taps[i][j]); } - printf("%.4e]\n", d_taps[i][j]); + printf("%.4e],", d_taps[i][j]); } + printf(" ]\n"); } void gr_pfb_clock_sync_ccf::print_diff_taps() { - unsigned int i, j; + int i, j; + printf("[ "); for(i = 0; i < d_nfilters; i++) { - printf("filter[%d]: [%.4e, ", i, d_dtaps[i][0]); + printf("[%.4e, ", d_dtaps[i][0]); for(j = 1; j < d_taps_per_filter-1; j++) { printf("%.4e,", d_dtaps[i][j]); } - printf("%.4e]\n", d_dtaps[i][j]); + printf("%.4e],", d_dtaps[i][j]); } + printf(" ]\n"); } @@ -172,8 +193,7 @@ std::vector<float> gr_pfb_clock_sync_ccf::channel_taps(int channel) { std::vector<float> taps; - unsigned int i; - for(i = 0; i < d_taps_per_filter; i++) { + for(int i = 0; i < d_taps_per_filter; i++) { taps.push_back(d_taps[channel][i]); } return taps; @@ -183,8 +203,7 @@ std::vector<float> gr_pfb_clock_sync_ccf::diff_channel_taps(int channel) { std::vector<float> taps; - unsigned int i; - for(i = 0; i < d_taps_per_filter; i++) { + for(int i = 0; i < d_taps_per_filter; i++) { taps.push_back(d_dtaps[channel][i]); } return taps; @@ -199,7 +218,13 @@ gr_pfb_clock_sync_ccf::general_work (int noutput_items, { gr_complex *in = (gr_complex *) input_items[0]; gr_complex *out = (gr_complex *) output_items[0]; - float *err = (float *) output_items[1]; + + float *err, *outrate, *outk; + if(output_items.size() > 2) { + err = (float *) output_items[1]; + outrate = (float*)output_items[2]; + outk = (float*)output_items[3]; + } if (d_updated) { d_updated = false; @@ -209,50 +234,50 @@ gr_pfb_clock_sync_ccf::general_work (int noutput_items, // We need this many to process one output int nrequired = ninput_items[0] - d_taps_per_filter; - int i = 0, count = d_start_count; - float error = 0; + int i = 0, count = 0; + float error, error_r, error_i; // produce output as long as we can and there are enough input samples while((i < noutput_items) && (count < nrequired)) { - out[i] = d_filters[d_last_filter]->filter(&in[count]); - error = (out[i] * d_diff_filters[d_last_filter]->filter(&in[count])).real(); - err[i] = error; - - d_acc += d_alpha*error; - gr_branchless_clip(d_acc, 1); - - int newfilter; - newfilter = (int)((float)d_last_filter + d_acc); - if(newfilter != (int)d_last_filter) - d_acc = 0.5; - - if(newfilter >= (int)d_nfilters) { - d_last_filter = newfilter - d_nfilters; - count++; + d_filtnum = (int)floor(d_k); + + // Keep the current filter number in [0, d_nfilters] + // If we've run beyond the last filter, wrap around and go to next sample + // If we've go below 0, wrap around and go to previous sample + while(d_filtnum >= d_nfilters) { + d_k -= d_nfilters; + d_filtnum -= d_nfilters; + count += 1; } - else if(newfilter < 0) { - d_last_filter = d_nfilters + newfilter; - count--; - } - else { - d_last_filter = newfilter; + while(d_filtnum < 0) { + d_k += d_nfilters; + d_filtnum += d_nfilters; + count -= 1; } + out[i] = d_filters[d_filtnum]->filter(&in[count]); + gr_complex diff = d_diff_filters[d_filtnum]->filter(&in[count]); + error_r = out[i].real() * diff.real(); + error_i = out[i].imag() * diff.imag(); + error = (error_i + error_r) / 2.0; // average error from I&Q channel + + // Run the control loop to update the current phase (k) and tracking rate + d_k = d_k + d_alpha*error + d_rate_i + d_rate_f; + d_rate_f = d_rate_f + d_beta*error; + + // Keep our rate within a good range + d_rate_f = gr_branchless_clip(d_rate_f, d_max_dev); + i++; - count += d_sps; - } + count += (int)floor(d_sps); - // Set the start index at the next entrance to the work function - // if we stop because we run out of input items, jump ahead in the - // next call to work. Otherwise, we can start at zero. - if(count > nrequired) { - d_start_count = count - (nrequired); - consume_each(ninput_items[0]-d_taps_per_filter); - } - else { - d_start_count = 0; - consume_each(count); + if(output_items.size() > 2) { + err[i] = error; + outrate[i] = d_rate_f; + outk[i] = d_k; + } } - + consume_each(count); + return i; } diff --git a/gnuradio-core/src/lib/filter/gr_pfb_clock_sync_ccf.h b/gnuradio-core/src/lib/filter/gr_pfb_clock_sync_ccf.h index 1a04e55c75..a07192a7f3 100644 --- a/gnuradio-core/src/lib/filter/gr_pfb_clock_sync_ccf.h +++ b/gnuradio-core/src/lib/filter/gr_pfb_clock_sync_ccf.h @@ -28,10 +28,11 @@ class gr_pfb_clock_sync_ccf; typedef boost::shared_ptr<gr_pfb_clock_sync_ccf> gr_pfb_clock_sync_ccf_sptr; -gr_pfb_clock_sync_ccf_sptr gr_make_pfb_clock_sync_ccf (float sps, float gain, - const std::vector<float> &taps, - unsigned int filter_size=32, - float init_phase=0); +gr_pfb_clock_sync_ccf_sptr gr_make_pfb_clock_sync_ccf (double sps, float gain, + const std::vector<float> &taps, + unsigned int filter_size=32, + float init_phase=0, + float max_rate_deviation=1.5); class gr_fir_ccf; @@ -50,31 +51,38 @@ class gr_pfb_clock_sync_ccf : public gr_block /*! * Build the polyphase filterbank timing synchronizer. */ - friend gr_pfb_clock_sync_ccf_sptr gr_make_pfb_clock_sync_ccf (float sps, float gain, + friend gr_pfb_clock_sync_ccf_sptr gr_make_pfb_clock_sync_ccf (double sps, float gain, const std::vector<float> &taps, unsigned int filter_size, - float init_phase); + float init_phase, + float max_rate_deviation); bool d_updated; - unsigned int d_sps; + double d_sps; + double d_sample_num; float d_alpha; - unsigned int d_nfilters; + float d_beta; + int d_nfilters; std::vector<gr_fir_ccf*> d_filters; std::vector<gr_fir_ccf*> d_diff_filters; std::vector< std::vector<float> > d_taps; std::vector< std::vector<float> > d_dtaps; - float d_acc; - unsigned int d_last_filter; - unsigned int d_start_count; - unsigned int d_taps_per_filter; + float d_k; + float d_rate; + float d_rate_i; + float d_rate_f; + float d_max_dev; + int d_filtnum; + int d_taps_per_filter; /*! * Build the polyphase filterbank timing synchronizer. */ - gr_pfb_clock_sync_ccf (float sps, float gain, + gr_pfb_clock_sync_ccf (double sps, float gain, const std::vector<float> &taps, unsigned int filter_size, - float init_phase); + float init_phase, + float max_rate_deviation); void create_diff_taps(const std::vector<float> &newtaps, std::vector<float> &difftaps); @@ -96,6 +104,20 @@ public: */ void print_taps(); void print_diff_taps(); + + void set_alpha(float alpha) + { + d_alpha = alpha; + } + void set_beta(float beta) + { + d_beta = beta; + } + + void set_max_rate_deviation(float m) + { + d_max_dev = m; + } int general_work (int noutput_items, gr_vector_int &ninput_items, diff --git a/gnuradio-core/src/lib/filter/gr_pfb_clock_sync_ccf.i b/gnuradio-core/src/lib/filter/gr_pfb_clock_sync_ccf.i index 729d4a1aa9..1979842871 100644 --- a/gnuradio-core/src/lib/filter/gr_pfb_clock_sync_ccf.i +++ b/gnuradio-core/src/lib/filter/gr_pfb_clock_sync_ccf.i @@ -22,18 +22,20 @@ GR_SWIG_BLOCK_MAGIC(gr,pfb_clock_sync_ccf); -gr_pfb_clock_sync_ccf_sptr gr_make_pfb_clock_sync_ccf (float sps, float gain, +gr_pfb_clock_sync_ccf_sptr gr_make_pfb_clock_sync_ccf (double sps, float gain, const std::vector<float> &taps, unsigned int filter_size=32, - float init_phase=0); + float init_phase=0, + float max_rate_deviation=1.5); class gr_pfb_clock_sync_ccf : public gr_block { private: - gr_pfb_clock_sync_ccf (float sps, float gain, + gr_pfb_clock_sync_ccf (double sps, float gain, const std::vector<float> &taps, unsigned int filter_size, - float init_phase); + float init_phase, + float max_rate_deviation); public: ~gr_pfb_clock_sync_ccf (); @@ -46,4 +48,7 @@ class gr_pfb_clock_sync_ccf : public gr_block std::vector<float> diff_channel_taps(int channel); void print_taps(); void print_diff_taps(); + void set_alpha(float alpha); + void set_beta(float beta); + void set_max_rate_deviation(float m); }; diff --git a/gnuradio-core/src/lib/general/gr_mpsk_receiver_cc.cc b/gnuradio-core/src/lib/general/gr_mpsk_receiver_cc.cc index 49bbb8d360..1efa827035 100644 --- a/gnuradio-core/src/lib/general/gr_mpsk_receiver_cc.cc +++ b/gnuradio-core/src/lib/general/gr_mpsk_receiver_cc.cc @@ -265,8 +265,6 @@ gr_mpsk_receiver_cc::phase_error_tracking(gr_complex sample) // Make phase and frequency corrections based on sampled value phase_error = (*this.*d_phase_error_detector)(sample); - - phase_error = gr_branchless_clip(phase_error, 1.0); d_freq += d_beta*phase_error; // adjust frequency based on error d_phase += d_freq + d_alpha*phase_error; // adjust phase based on error |