diff options
44 files changed, 2840 insertions, 948 deletions
@@ -11,3 +11,4 @@ Andrew Thomas Beck <Andrew.Beck@postgrads.unisa.edu.au> bugfixes Joshua Lackey <jl@noether.uoregon.edu> Original GMSK implementation. Johnathan Corgan <jcorgan@corganenterprises.com> Build system, ongoing stuff, release manager Bdale Garbee <bdale@gag.com> Debian release packages +Tom Rondeau <trondeau@vt.edu> Mostly digital waveforms and a little bit of trouble diff --git a/gnuradio-core/src/lib/general/Makefile.am b/gnuradio-core/src/lib/general/Makefile.am index b466ad44b..cde6a7549 100644 --- a/gnuradio-core/src/lib/general/Makefile.am +++ b/gnuradio-core/src/lib/general/Makefile.am @@ -96,7 +96,7 @@ libgeneral_la_SOURCES = \ gr_nop.cc \ gr_null_sink.cc \ gr_null_source.cc \ - gr_ofdm_correlator.cc \ + gr_ofdm_frame_acquisition.cc \ gr_ofdm_cyclic_prefixer.cc \ gr_ofdm_demapper_vcb.cc \ gr_ofdm_mapper_bcv.cc \ @@ -109,6 +109,7 @@ libgeneral_la_SOURCES = \ gr_ofdm_sampler.cc \ gr_pa_2x2_phase_combiner.cc \ gr_packet_sink.cc \ + gr_peak_detector2_fb.cc \ gr_phase_modulator_fc.cc \ gr_pll_carriertracking_cc.cc \ gr_pll_freqdet_cf.cc \ @@ -239,7 +240,7 @@ grinclude_HEADERS = \ gr_nop.h \ gr_null_sink.h \ gr_null_source.h \ - gr_ofdm_correlator.h \ + gr_ofdm_frame_acquisition.h \ gr_ofdm_cyclic_prefixer.h \ gr_ofdm_demapper_vcb.h \ gr_ofdm_mapper_bcv.h \ @@ -252,6 +253,7 @@ grinclude_HEADERS = \ gr_ofdm_sampler.h \ gr_pa_2x2_phase_combiner.h \ gr_packet_sink.h \ + gr_peak_detector2_fb.h \ gr_phase_modulator_fc.h \ gr_pll_carriertracking_cc.h \ gr_pll_freqdet_cf.h \ @@ -383,7 +385,7 @@ swiginclude_HEADERS = \ gr_nop.i \ gr_null_sink.i \ gr_null_source.i \ - gr_ofdm_correlator.i \ + gr_ofdm_frame_acquisition.i \ gr_ofdm_cyclic_prefixer.i \ gr_ofdm_demapper_vcb.i \ gr_ofdm_mapper_bcv.i \ @@ -396,6 +398,7 @@ swiginclude_HEADERS = \ gr_ofdm_sampler.i \ gr_pa_2x2_phase_combiner.i \ gr_packet_sink.i \ + gr_peak_detector2_fb.i \ gr_phase_modulator_fc.i \ gr_pll_carriertracking_cc.i \ gr_pll_freqdet_cf.i \ diff --git a/gnuradio-core/src/lib/general/general.i b/gnuradio-core/src/lib/general/general.i index e9780ea13..0eab24f1a 100644 --- a/gnuradio-core/src/lib/general/general.i +++ b/gnuradio-core/src/lib/general/general.i @@ -93,7 +93,7 @@ #include <gr_probe_avg_mag_sqrd_cf.h> #include <gr_probe_avg_mag_sqrd_f.h> #include <gr_probe_signal_f.h> -#include <gr_ofdm_correlator.h> +#include <gr_ofdm_frame_acquisition.h> #include <gr_ofdm_cyclic_prefixer.h> #include <gr_ofdm_bpsk_demapper.h> #include <gr_ofdm_mapper_bcv.h> @@ -128,6 +128,7 @@ #include <gr_bin_statistics_f.h> #include <gr_glfsr_source_b.h> #include <gr_glfsr_source_f.h> +#include <gr_peak_detector2_fb.h> %} %include "gr_nop.i" @@ -201,7 +202,7 @@ %include "gr_probe_avg_mag_sqrd_cf.i" %include "gr_probe_avg_mag_sqrd_f.i" %include "gr_probe_signal_f.i" -%include "gr_ofdm_correlator.i" +%include "gr_ofdm_frame_acquisition.i" %include "gr_ofdm_cyclic_prefixer.i" %include "gr_ofdm_bpsk_demapper.i" %include "gr_ofdm_mapper_bcv.i" @@ -236,3 +237,4 @@ %include "gr_bin_statistics_f.i" %include "gr_glfsr_source_b.i" %include "gr_glfsr_source_f.i" +%include "gr_peak_detector2_fb.i" diff --git a/gnuradio-core/src/lib/general/gr_math.h b/gnuradio-core/src/lib/general/gr_math.h index 439e5f4b9..2cd5b8eb7 100644 --- a/gnuradio-core/src/lib/general/gr_math.h +++ b/gnuradio-core/src/lib/general/gr_math.h @@ -64,4 +64,17 @@ static inline float gr_fast_atan2f(gr_complex z) return gr_fast_atan2f(z.imag(), z.real()); } + +/* This bounds x by +/- clip without a branch */ + +static inline float gr_branchless_clip(float x, float clip) +{ + float x1 = fabsf(x+clip); + float x2 = fabsf(x-clip); + x1 -= x2; + return 0.5*x1; +} + + + #endif /* _GR_MATH_H_ */ diff --git a/gnuradio-core/src/lib/general/gr_ofdm_correlator.cc b/gnuradio-core/src/lib/general/gr_ofdm_correlator.cc deleted file mode 100644 index e396eeb8e..000000000 --- a/gnuradio-core/src/lib/general/gr_ofdm_correlator.cc +++ /dev/null @@ -1,231 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2006, 2007 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_ofdm_correlator.h> -#include <gr_io_signature.h> -#include <gr_expj.h> - -#define VERBOSE 0 -#define M_TWOPI (2*M_PI) -#define MAX_NUM_SYMBOLS 1000 - -gr_ofdm_correlator_sptr -gr_make_ofdm_correlator (unsigned int occupied_carriers, unsigned int fft_length, - unsigned int cplen, - const std::vector<gr_complex> &known_symbol1, - const std::vector<gr_complex> &known_symbol2, - unsigned int max_fft_shift_len) -{ - return gr_ofdm_correlator_sptr (new gr_ofdm_correlator (occupied_carriers, fft_length, cplen, - known_symbol1, known_symbol2, - max_fft_shift_len)); -} - -gr_ofdm_correlator::gr_ofdm_correlator (unsigned occupied_carriers, unsigned int fft_length, - unsigned int cplen, - const std::vector<gr_complex> &known_symbol1, - const std::vector<gr_complex> &known_symbol2, - unsigned int max_fft_shift_len) - : gr_block ("ofdm_correlator", - gr_make_io_signature (1, 1, sizeof(gr_complex)*fft_length), - gr_make_io_signature2 (2, 2, sizeof(gr_complex)*occupied_carriers, sizeof(char))), - d_occupied_carriers(occupied_carriers), - d_fft_length(fft_length), - d_cplen(cplen), - d_freq_shift_len(max_fft_shift_len), - d_known_symbol1(known_symbol1), - d_known_symbol2(known_symbol2), - d_coarse_freq(0), - d_phase_count(0) -{ - d_diff_corr_factor.resize(d_occupied_carriers); - d_hestimate.resize(d_occupied_carriers); - - std::vector<gr_complex>::iterator i1, i2; - - unsigned int i = 0, j = 0; - gr_complex one(1.0, 0.0); - for(i1 = d_known_symbol1.begin(), i2 = d_known_symbol2.begin(); i1 != d_known_symbol1.end(); i1++, i2++) { - d_diff_corr_factor[i] = one / ((*i1) * conj(*i2)); - i++; - } - - d_phase_lut = new gr_complex[(2*d_freq_shift_len+1) * MAX_NUM_SYMBOLS]; - for(i = 0; i <= 2*d_freq_shift_len; i++) { - for(j = 0; j < MAX_NUM_SYMBOLS; j++) { - d_phase_lut[j + i*MAX_NUM_SYMBOLS] = gr_expj(-M_TWOPI*d_cplen/d_fft_length*(i-d_freq_shift_len)*j); - } - } -} - -gr_ofdm_correlator::~gr_ofdm_correlator(void) -{ - delete [] d_phase_lut; -} - -void -gr_ofdm_correlator::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] = 2; -} - -gr_complex -gr_ofdm_correlator::coarse_freq_comp(int freq_delta, int symbol_count) -{ - // return gr_complex(cos(-M_TWOPI*freq_delta*d_cplen/d_fft_length*symbol_count), - // sin(-M_TWOPI*freq_delta*d_cplen/d_fft_length*symbol_count)); - - return gr_expj(-M_TWOPI*freq_delta*d_cplen/d_fft_length*symbol_count); - - //assert(d_freq_shift_len + freq_delta >= 0); - //assert(symbol_count <= MAX_NUM_SYMBOLS); - - //return d_phase_lut[MAX_NUM_SYMBOLS * (d_freq_shift_len + freq_delta) + symbol_count]; -} - -bool -gr_ofdm_correlator::correlate(const gr_complex *previous, const gr_complex *current, - int zeros_on_left) -{ - unsigned int i = 0; - int search_delta = 0; - bool found = false; - - gr_complex h_sqrd = gr_complex(0.0,0.0); - float power = 0.0F; - - while(!found && ((unsigned)abs(search_delta) <= d_freq_shift_len)) { - h_sqrd = gr_complex(0.0,0.0); - power = 0.0F; - - for(i = 0; i < d_occupied_carriers; i++) { - h_sqrd = h_sqrd + previous[i+zeros_on_left+search_delta] * - conj(coarse_freq_comp(search_delta,1)*current[i+zeros_on_left+search_delta]) * - d_diff_corr_factor[i]; - - power = power + norm(current[i+zeros_on_left+search_delta]); // No need to do coarse freq here - } - -#if VERBOSE - printf("bin %d\th_sqrd = ( %f, %f )\t power = %f\t real(h)/p = %f\t angle = %f\n", - search_delta, h_sqrd.real(), h_sqrd.imag(), power, h_sqrd.real()/power, arg(h_sqrd)); -#endif - // FIXME: Look at h_sqrd.read() > power - if((h_sqrd.real() > 0.82*power) && (h_sqrd.real() < 1.1 * power)) { - found = true; - //printf("search delta: %d\n", search_delta); - d_coarse_freq = search_delta; - d_phase_count = 1; - //d_snr_est = 10*log10(power/(power-h_sqrd.real())); - - // check for low noise power; sets maximum SNR at 100 dB - if(fabs(h_sqrd.imag()) <= 1e-12) { - d_snr_est = 100.0; - } - else { - d_snr_est = 10*log10(fabs(h_sqrd.real()/h_sqrd.imag())); - } - -#if VERBOSE - printf("CORR: Found, bin %d\tSNR Est %f dB\tcorr power fraction %f\n", - search_delta, d_snr_est, h_sqrd.real()/power); -#endif - - // search_delta,10*log10(h_sqrd.real()/fabs(h_sqrd.imag())),h_sqrd.real()/power); - break; - } - else { - if(search_delta <= 0) - search_delta = (-search_delta) + 2; - else - search_delta = -search_delta; - } - } - return found; -} - -void -gr_ofdm_correlator::calculate_equalizer(const gr_complex *previous, const gr_complex *current, - int zeros_on_left) -{ - unsigned int i=0; - - for(i = 0; i < d_occupied_carriers; i++) { - // FIXME possibly add small epsilon in divisor to protect from div 0 - //d_hestimate[i] = 0.5F * (d_known_symbol1[i] / previous[i+zeros_on_left] + - // d_known_symbol2[i] / (coarse_freq_comp(d_coarse_freq,1)* - // current[i+zeros_on_left+d_coarse_freq])); - d_hestimate[i] = 0.5F * (d_known_symbol1[i] / previous[i+zeros_on_left+d_coarse_freq] + - d_known_symbol2[i] / (coarse_freq_comp(d_coarse_freq,1)* - current[i+zeros_on_left+d_coarse_freq])); - } -#if VERBOSE - fprintf(stderr, "\n"); -#endif -} - -int -gr_ofdm_correlator::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]; - const gr_complex *previous = &in[0]; - const gr_complex *current = &in[d_fft_length]; - - gr_complex *out = (gr_complex *) output_items[0]; - char *sig = (char *) output_items[1]; - - unsigned int i=0; - - int unoccupied_carriers = d_fft_length - d_occupied_carriers; - int zeros_on_left = (int)ceil(unoccupied_carriers/2.0); - - bool corr = correlate(previous, current, zeros_on_left); - if(corr) { - calculate_equalizer(previous, current, zeros_on_left); - sig[0] = 1; - } - else { - sig[0] = 0; - } - - for(i = 0; i < d_occupied_carriers; i++) { - out[i] = d_hestimate[i]*coarse_freq_comp(d_coarse_freq,d_phase_count)*current[i+zeros_on_left+d_coarse_freq]; - } - - - d_phase_count++; - if(d_phase_count == MAX_NUM_SYMBOLS) { - d_phase_count = 1; - } - - consume_each(1); - return 1; -} diff --git a/gnuradio-core/src/lib/general/gr_ofdm_frame_acquisition.cc b/gnuradio-core/src/lib/general/gr_ofdm_frame_acquisition.cc new file mode 100644 index 000000000..0fa6a3d3e --- /dev/null +++ b/gnuradio-core/src/lib/general/gr_ofdm_frame_acquisition.cc @@ -0,0 +1,223 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006, 2007 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_ofdm_frame_acquisition.h> +#include <gr_io_signature.h> +#include <gr_expj.h> +#include <gr_math.h> + +#define VERBOSE 0 +#define M_TWOPI (2*M_PI) +#define MAX_NUM_SYMBOLS 1000 + +gr_ofdm_frame_acquisition_sptr +gr_make_ofdm_frame_acquisition (unsigned int occupied_carriers, unsigned int fft_length, + unsigned int cplen, + const std::vector<gr_complex> &known_symbol, + unsigned int max_fft_shift_len) +{ + return gr_ofdm_frame_acquisition_sptr (new gr_ofdm_frame_acquisition (occupied_carriers, fft_length, cplen, + known_symbol, max_fft_shift_len)); +} + +gr_ofdm_frame_acquisition::gr_ofdm_frame_acquisition (unsigned occupied_carriers, unsigned int fft_length, + unsigned int cplen, + const std::vector<gr_complex> &known_symbol, + unsigned int max_fft_shift_len) + : gr_block ("ofdm_frame_acquisition", + gr_make_io_signature2 (2, 2, sizeof(gr_complex)*fft_length, sizeof(char)), + gr_make_io_signature2 (2, 2, sizeof(gr_complex)*occupied_carriers, sizeof(char))), + d_occupied_carriers(occupied_carriers), + d_fft_length(fft_length), + d_cplen(cplen), + d_freq_shift_len(max_fft_shift_len), + d_known_symbol(known_symbol), + d_coarse_freq(0), + d_phase_count(0) +{ + d_symbol_phase_diff.resize(d_fft_length); + d_known_phase_diff.resize(d_occupied_carriers); + d_hestimate.resize(d_occupied_carriers); + + unsigned int i = 0, j = 0; + + std::fill(d_known_phase_diff.begin(), d_known_phase_diff.end(), 0); + for(i = 0; i < d_known_symbol.size()-2; i+=2) { + d_known_phase_diff[i] = fabs(gr_fast_atan2f(d_known_symbol[i]) - gr_fast_atan2f(d_known_symbol[i+2])); + } + + d_phase_lut = new gr_complex[(2*d_freq_shift_len+1) * MAX_NUM_SYMBOLS]; + for(i = 0; i <= 2*d_freq_shift_len; i++) { + for(j = 0; j < MAX_NUM_SYMBOLS; j++) { + d_phase_lut[j + i*MAX_NUM_SYMBOLS] = gr_expj(-M_TWOPI*d_cplen/d_fft_length*(i-d_freq_shift_len)*j); + } + } +} + +gr_ofdm_frame_acquisition::~gr_ofdm_frame_acquisition(void) +{ + delete [] d_phase_lut; +} + +void +gr_ofdm_frame_acquisition::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] = 1; +} + +gr_complex +gr_ofdm_frame_acquisition::coarse_freq_comp(int freq_delta, int symbol_count) +{ + // return gr_complex(cos(-M_TWOPI*freq_delta*d_cplen/d_fft_length*symbol_count), + // sin(-M_TWOPI*freq_delta*d_cplen/d_fft_length*symbol_count)); + + return gr_expj(-M_TWOPI*freq_delta*d_cplen/d_fft_length*symbol_count); + + //return d_phase_lut[MAX_NUM_SYMBOLS * (d_freq_shift_len + freq_delta) + symbol_count]; +} + + + +bool +gr_ofdm_frame_acquisition::correlate(const gr_complex *symbol, int zeros_on_left) +{ + unsigned int i = 0, j = 0; + + std::fill(d_symbol_phase_diff.begin(), d_symbol_phase_diff.end(), 0); + for(i = 0; i < d_fft_length-2; i++) { + d_symbol_phase_diff[i] = fabs(gr_fast_atan2f(symbol[i]) - gr_fast_atan2f(symbol[i+2])); + } + + int index = 0; + float max = 0, sum=0; + for(i = zeros_on_left - d_freq_shift_len; i < zeros_on_left + d_freq_shift_len; i+=2) { + sum = 0; + for(j = 0; j < d_occupied_carriers; j++) { + sum += (d_known_phase_diff[j] * d_symbol_phase_diff[i+j]); + } + if(fabs(sum) > max) { + max = sum; + index = i; + } + } + + d_coarse_freq = index - zeros_on_left; + + if(VERBOSE) { + fprintf(stderr, "Coarse Freq Offset: %d\n", d_coarse_freq); + for(i = 0; i < 40; i++) { + fprintf(stderr, "%+.4f %+.4f\n", d_known_phase_diff[i], + d_symbol_phase_diff[zeros_on_left+d_coarse_freq+i]); + } + } + + return true; //FIXME: don't need ot return anything now +} + +void +gr_ofdm_frame_acquisition::calculate_equalizer(const gr_complex *symbol, int zeros_on_left) +{ + unsigned int i=0; + + // Set first tap of equalizer + d_hestimate[0] = d_known_symbol[0] / + (coarse_freq_comp(d_coarse_freq,1)*symbol[zeros_on_left+d_coarse_freq]); + + // set every even tap based on known symbol + // linearly interpolate between set carriers to set zero-filled carriers + // FIXME: is this the best way to set this? + for(i = 2; i < d_occupied_carriers; i+=2) { + d_hestimate[i] = d_known_symbol[i] / + (coarse_freq_comp(d_coarse_freq,1)*(symbol[i+zeros_on_left+d_coarse_freq])); + d_hestimate[i-1] = (d_hestimate[i] + d_hestimate[i-2]) / gr_complex(2.0, 0.0); + } + + // with even number of carriers; last equalizer tap is wrong + if(!(d_occupied_carriers & 1)) { + d_hestimate[d_occupied_carriers-1] = d_hestimate[d_occupied_carriers-2]; + } + + if(VERBOSE) { + fprintf(stderr, "Equalizer setting:\n"); + for(i = 0; i < d_occupied_carriers; i++) { + gr_complex sym = coarse_freq_comp(d_coarse_freq,1)*symbol[i+zeros_on_left+d_coarse_freq]; + gr_complex output = sym * d_hestimate[i]; + fprintf(stderr, "sym: %+.4f + j%+.4f ks: %+.4f + j%+.4f eq: %+.4f + j%+.4f ==> %+.4f + j%+.4f\n", + sym .real(), sym.imag(), + d_known_symbol[i].real(), d_known_symbol[i].imag(), + d_hestimate[i].real(), d_hestimate[i].imag(), + output.real(), output.imag()); + } + fprintf(stderr, "\n"); + } +} + +int +gr_ofdm_frame_acquisition::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 *symbol = (const gr_complex *)input_items[0]; + const char *trigger = (const char *)input_items[1]; + + gr_complex *out = (gr_complex *) output_items[0]; + char *sig = (char *) output_items[1]; + + int unoccupied_carriers = d_fft_length - d_occupied_carriers; + int zeros_on_left = (int)ceil(unoccupied_carriers/2.0); + + int found = 0; + for(int i = 0; i < ninput_items[1]; i++) { + found += trigger[i]; + } + + if(found) { + d_phase_count = 1; + correlate(symbol, zeros_on_left); + calculate_equalizer(symbol, zeros_on_left); + sig[0] = 1; + } + else { + sig[0] = 0; + } + + for(unsigned int i = 0; i < d_occupied_carriers; i++) { + out[i] = d_hestimate[i]*coarse_freq_comp(d_coarse_freq,d_phase_count) + *symbol[i+zeros_on_left+d_coarse_freq]; + } + + d_phase_count++; + if(d_phase_count == MAX_NUM_SYMBOLS) { + d_phase_count = 1; + } + + consume(0,1); + consume(1,ninput_items[1]); + return 1; +} diff --git a/gnuradio-core/src/lib/general/gr_ofdm_correlator.h b/gnuradio-core/src/lib/general/gr_ofdm_frame_acquisition.h index 55ee4e190..af63f3e4c 100644 --- a/gnuradio-core/src/lib/general/gr_ofdm_correlator.h +++ b/gnuradio-core/src/lib/general/gr_ofdm_frame_acquisition.h @@ -20,22 +20,21 @@ * Boston, MA 02110-1301, USA. */ -#ifndef INCLUDED_GR_OFDM_CORRELATOR_H -#define INCLUDED_GR_OFDM_CORRELATOR_H +#ifndef INCLUDED_GR_OFDM_FRAME_ACQUISITION_H +#define INCLUDED_GR_OFDM_FRAME_ACQUISITION_H #include <gr_block.h> #include <vector> -class gr_ofdm_correlator; -typedef boost::shared_ptr<gr_ofdm_correlator> gr_ofdm_correlator_sptr; +class gr_ofdm_frame_acquisition; +typedef boost::shared_ptr<gr_ofdm_frame_acquisition> gr_ofdm_frame_acquisition_sptr; -gr_ofdm_correlator_sptr -gr_make_ofdm_correlator (unsigned int occupied_carriers, unsigned int fft_length, - unsigned int cplen, - const std::vector<gr_complex> &known_symbol1, - const std::vector<gr_complex> &known_symbol2, - unsigned int max_fft_shift_len=10); +gr_ofdm_frame_acquisition_sptr +gr_make_ofdm_frame_acquisition (unsigned int occupied_carriers, unsigned int fft_length, + unsigned int cplen, + const std::vector<gr_complex> &known_symbol, + unsigned int max_fft_shift_len=10); /*! * \brief take a vector of complex constellation points in from an FFT @@ -54,7 +53,7 @@ gr_make_ofdm_correlator (unsigned int occupied_carriers, unsigned int fft_length * distortion caused by the channel. */ -class gr_ofdm_correlator : public gr_block +class gr_ofdm_frame_acquisition : public gr_block { /*! * \brief Build an OFDM correlator and equalizer. @@ -68,35 +67,33 @@ class gr_ofdm_correlator : public gr_block * for phase changes between symbols. * \param max_fft_shift_len Set's the maximum distance you can look between bins for correlation */ - friend gr_ofdm_correlator_sptr - gr_make_ofdm_correlator (unsigned int occupied_carriers, unsigned int fft_length, - unsigned int cplen, - const std::vector<gr_complex> &known_symbol1, - const std::vector<gr_complex> &known_symbol2, - unsigned int max_fft_shift_len); + friend gr_ofdm_frame_acquisition_sptr + gr_make_ofdm_frame_acquisition (unsigned int occupied_carriers, unsigned int fft_length, + unsigned int cplen, + const std::vector<gr_complex> &known_symbol, + unsigned int max_fft_shift_len); - protected: - gr_ofdm_correlator (unsigned int occupied_carriers, unsigned int fft_length, - unsigned int cplen, - const std::vector<gr_complex> &known_symbol1, - const std::vector<gr_complex> &known_symbol2, - unsigned int max_fft_shift_len); +protected: + gr_ofdm_frame_acquisition (unsigned int occupied_carriers, unsigned int fft_length, + unsigned int cplen, + const std::vector<gr_complex> &known_symbol, + unsigned int max_fft_shift_len); private: unsigned char slicer(gr_complex x); - bool correlate(const gr_complex *previous, const gr_complex *current, int zeros_on_left); - void calculate_equalizer(const gr_complex *previous, - const gr_complex *current, int zeros_on_left); + bool correlate(const gr_complex *symbol, int zeros_on_left); + void calculate_equalizer(const gr_complex *symbol, int zeros_on_left); gr_complex coarse_freq_comp(int freq_delta, int count); unsigned int d_occupied_carriers; // !< \brief number of subcarriers with data unsigned int d_fft_length; // !< \brief length of FFT vector unsigned int d_cplen; // !< \brief length of cyclic prefix in samples unsigned int d_freq_shift_len; // !< \brief number of surrounding bins to look at for correlation - std::vector<gr_complex> d_known_symbol1, d_known_symbol2; // !< \brief known symbols at start of frame - std::vector<gr_complex> d_diff_corr_factor; // !< \brief factor used in correlation + std::vector<gr_complex> d_known_symbol; // !< \brief known symbols at start of frame + std::vector<float> d_known_phase_diff; // !< \brief factor used in correlation from known symbol + std::vector<float> d_symbol_phase_diff; // !< \brief factor used in correlation from received symbol std::vector<gr_complex> d_hestimate; // !< channel estimate - signed int d_coarse_freq; // !< \brief search distance in number of bins + int d_coarse_freq; // !< \brief search distance in number of bins unsigned int d_phase_count; // !< \brief accumulator for coarse freq correction float d_snr_est; // !< an estimation of the signal to noise ratio @@ -110,7 +107,7 @@ class gr_ofdm_correlator : public gr_block */ float snr() { return d_snr_est; } - ~gr_ofdm_correlator(void); + ~gr_ofdm_frame_acquisition(void); int general_work(int noutput_items, gr_vector_int &ninput_items, gr_vector_const_void_star &input_items, diff --git a/gnuradio-core/src/lib/general/gr_ofdm_correlator.i b/gnuradio-core/src/lib/general/gr_ofdm_frame_acquisition.i index 2e39e2150..0fd0bc58b 100644 --- a/gnuradio-core/src/lib/general/gr_ofdm_correlator.i +++ b/gnuradio-core/src/lib/general/gr_ofdm_frame_acquisition.i @@ -22,25 +22,23 @@ #include <vector> -GR_SWIG_BLOCK_MAGIC(gr,ofdm_correlator); +GR_SWIG_BLOCK_MAGIC(gr,ofdm_frame_acquisition); -gr_ofdm_correlator_sptr -gr_make_ofdm_correlator (unsigned int occupied_carriers, - unsigned int fft_length, - unsigned int cplen, - const std::vector<gr_complex> &known_symbol1, - const std::vector<gr_complex> &known_symbol2, - unsigned int max_fft_shift_len=4); +gr_ofdm_frame_acquisition_sptr +gr_make_ofdm_frame_acquisition (unsigned int occupied_carriers, + unsigned int fft_length, + unsigned int cplen, + const std::vector<gr_complex> &known_symbol, + unsigned int max_fft_shift_len=4); -class gr_ofdm_correlator : public gr_sync_decimator +class gr_ofdm_frame_acquisition : public gr_sync_decimator { protected: - gr_ofdm_correlator (unsigned int occupied_carriers, - unsigned int fft_length, - unsigned int cplen, - const std::vector<gr_complex> &known_symbol1, - const std::vector<gr_complex> &known_symbol2, - unsigned int max_fft_shift_len); + gr_ofdm_frame_acquisition (unsigned int occupied_carriers, + unsigned int fft_length, + unsigned int cplen, + const std::vector<gr_complex> &known_symbol, + unsigned int max_fft_shift_len); public: float snr() { return d_snr_est; } diff --git a/gnuradio-core/src/lib/general/gr_ofdm_frame_sink.cc b/gnuradio-core/src/lib/general/gr_ofdm_frame_sink.cc index d75b693a2..a1ac66ff7 100644 --- a/gnuradio-core/src/lib/general/gr_ofdm_frame_sink.cc +++ b/gnuradio-core/src/lib/general/gr_ofdm_frame_sink.cc @@ -26,8 +26,12 @@ #include <gr_ofdm_frame_sink.h> #include <gr_io_signature.h> +#include <gr_expj.h> +#include <gr_math.h> +#include <math.h> #include <cstdio> #include <stdexcept> +#include <iostream> #define VERBOSE 0 @@ -55,6 +59,11 @@ gr_ofdm_frame_sink::enter_have_sync() d_header = 0; d_headerbytelen_cnt = 0; + + // Resetting PLL + d_freq = 0.0; + d_phase = 0.0; + fill(d_dfe.begin(), d_dfe.end(), gr_complex(1.0,0.0)); } inline void @@ -96,7 +105,11 @@ unsigned int gr_ofdm_frame_sink::demapper(const gr_complex *in, unsigned char *out) { unsigned int i=0, bytes_produced=0; + gr_complex carrier; + + carrier=gr_expj(d_phase); + gr_complex accum_error = 0.0; while(i < d_occupied_carriers) { if(d_nresid > 0) { d_partial_byte |= d_resid; @@ -106,8 +119,23 @@ unsigned int gr_ofdm_frame_sink::demapper(const gr_complex *in, } while((d_byte_offset < 8) && (i < d_occupied_carriers)) { - //fprintf(stderr, "%f+j%f = %d\n", in[i].real(), in[i].imag(), slicer(in[i])); - unsigned char bits = slicer(in[i++]); + gr_complex sigrot = in[i]*carrier*d_dfe[i]; + + if(d_derotated_output != NULL){ + d_derotated_output[i] = sigrot; + } + + unsigned char bits = slicer(sigrot); + + gr_complex closest_sym = d_sym_position[bits]; + + accum_error += sigrot * conj(closest_sym); + + // FIX THE FOLLOWING STATEMENT + if (norm(sigrot)> 0.001) d_dfe[i] += d_eq_gain*(closest_sym/sigrot-d_dfe[i]); + + i++; + if((8 - d_byte_offset) >= d_nbits) { d_partial_byte |= bits << (d_byte_offset); d_byte_offset += d_nbits; @@ -130,7 +158,18 @@ unsigned int gr_ofdm_frame_sink::demapper(const gr_complex *in, d_partial_byte = 0; } } + //std::cerr << "accum_error " << accum_error << std::endl; + + float angle = arg(accum_error); + d_freq = d_freq - d_freq_gain*angle; + d_phase = d_phase + d_freq - d_phase_gain*angle; + if (d_phase >= 2*M_PI) d_phase -= 2*M_PI; + if (d_phase <0) d_phase += 2*M_PI; + + //if(VERBOSE) + // std::cerr << angle << "\t" << d_freq << "\t" << d_phase << "\t" << std::endl; + return bytes_produced; } @@ -138,24 +177,30 @@ unsigned int gr_ofdm_frame_sink::demapper(const gr_complex *in, gr_ofdm_frame_sink_sptr gr_make_ofdm_frame_sink(const std::vector<gr_complex> &sym_position, const std::vector<unsigned char> &sym_value_out, - gr_msg_queue_sptr target_queue, unsigned int occupied_carriers) + gr_msg_queue_sptr target_queue, unsigned int occupied_carriers, + float phase_gain, float freq_gain) { return gr_ofdm_frame_sink_sptr(new gr_ofdm_frame_sink(sym_position, sym_value_out, - target_queue, occupied_carriers)); + target_queue, occupied_carriers, + phase_gain, freq_gain)); } gr_ofdm_frame_sink::gr_ofdm_frame_sink(const std::vector<gr_complex> &sym_position, const std::vector<unsigned char> &sym_value_out, - gr_msg_queue_sptr target_queue, unsigned int occupied_carriers) + gr_msg_queue_sptr target_queue, unsigned int occupied_carriers, + float phase_gain, float freq_gain) : gr_sync_block ("ofdm_frame_sink", gr_make_io_signature2 (2, 2, sizeof(gr_complex)*occupied_carriers, sizeof(char)), - gr_make_io_signature (0, 0, 0)), + gr_make_io_signature (1, 1, sizeof(gr_complex)*occupied_carriers)), d_target_queue(target_queue), d_occupied_carriers(occupied_carriers), d_byte_offset(0), d_partial_byte(0), - d_resid(0), d_nresid(0) + d_resid(0), d_nresid(0),d_phase(0),d_freq(0),d_phase_gain(phase_gain),d_freq_gain(freq_gain), + d_eq_gain(0.05) { d_bytes_out = new unsigned char[d_occupied_carriers]; + d_dfe.resize(occupied_carriers); + fill(d_dfe.begin(), d_dfe.end(), gr_complex(1.0,0.0)); set_sym_value_out(sym_position, sym_value_out); @@ -179,7 +224,7 @@ gr_ofdm_frame_sink::set_sym_value_out(const std::vector<gr_complex> &sym_positio d_sym_position = sym_position; d_sym_value_out = sym_value_out; - d_nbits = (unsigned long)(log10(d_sym_value_out.size()) / log10(2)); + d_nbits = (unsigned long)ceil(log10(d_sym_value_out.size()) / log10(2.0)); return true; } @@ -194,12 +239,16 @@ gr_ofdm_frame_sink::work (int noutput_items, const char *sig = (const char *) input_items[1]; unsigned int j = 0; unsigned int bytes=0; + + // If the output is connected, send it the derotated symbols + if(output_items.size() >= 1) + d_derotated_output = (gr_complex *)output_items[0]; + else + d_derotated_output = NULL; if (VERBOSE) fprintf(stderr,">>> Entering state machine\n"); - - bytes = demapper(&in[0], d_bytes_out); - + switch(d_state) { case STATE_SYNC_SEARCH: // Look for flag indicating beginning of pkt @@ -212,6 +261,10 @@ gr_ofdm_frame_sink::work (int noutput_items, break; case STATE_HAVE_SYNC: + // only demod after getting the preamble signal; otherwise, the + // equalizer taps will screw with the PLL performance + bytes = demapper(&in[0], d_bytes_out); + if (VERBOSE) { if(sig[0]) printf("ERROR -- Found SYNC in HAVE_SYNC\n"); @@ -258,6 +311,8 @@ gr_ofdm_frame_sink::work (int noutput_items, break; case STATE_HAVE_HEADER: + bytes = demapper(&in[0], d_bytes_out); + if (VERBOSE) { if(sig[0]) printf("ERROR -- Found SYNC in HAVE_HEADER at %d, length of %d\n", d_packetlen_cnt, d_packetlen); @@ -288,6 +343,6 @@ gr_ofdm_frame_sink::work (int noutput_items, assert(0); } // switch - + return 1; } diff --git a/gnuradio-core/src/lib/general/gr_ofdm_frame_sink.h b/gnuradio-core/src/lib/general/gr_ofdm_frame_sink.h index f1c9b76fe..904373bba 100644 --- a/gnuradio-core/src/lib/general/gr_ofdm_frame_sink.h +++ b/gnuradio-core/src/lib/general/gr_ofdm_frame_sink.h @@ -32,7 +32,8 @@ typedef boost::shared_ptr<gr_ofdm_frame_sink> gr_ofdm_frame_sink_sptr; gr_ofdm_frame_sink_sptr gr_make_ofdm_frame_sink (const std::vector<gr_complex> &sym_position, const std::vector<unsigned char> &sym_value_out, - gr_msg_queue_sptr target_queue, unsigned int occupied_tones); + gr_msg_queue_sptr target_queue, unsigned int occupied_tones, + float phase_gain=0.25, float freq_gain=0.25*0.25/4.0); /*! * \brief Takes an OFDM symbol in, demaps it into bits of 0's and 1's, packs @@ -47,7 +48,8 @@ class gr_ofdm_frame_sink : public gr_sync_block friend gr_ofdm_frame_sink_sptr gr_make_ofdm_frame_sink (const std::vector<gr_complex> &sym_position, const std::vector<unsigned char> &sym_value_out, - gr_msg_queue_sptr target_queue, unsigned int occupied_tones); + gr_msg_queue_sptr target_queue, unsigned int occupied_tones, + float phase_gain, float freq_gain); private: enum state_t {STATE_SYNC_SEARCH, STATE_HAVE_SYNC, STATE_HAVE_HEADER}; @@ -71,17 +73,26 @@ class gr_ofdm_frame_sink : public gr_sync_block int d_packet_whitener_offset; // offset into whitener string to use int d_packetlen_cnt; // how many so far + gr_complex * d_derotated_output; // Pointer to output stream to send deroated symbols out + std::vector<gr_complex> d_sym_position; std::vector<unsigned char> d_sym_value_out; + std::vector<gr_complex> d_dfe; unsigned int d_nbits; unsigned char d_resid; unsigned int d_nresid; + float d_phase; + float d_freq; + float d_phase_gain; + float d_freq_gain; + float d_eq_gain; protected: gr_ofdm_frame_sink(const std::vector<gr_complex> &sym_position, const std::vector<unsigned char> &sym_value_out, - gr_msg_queue_sptr target_queue, unsigned int occupied_tones); + gr_msg_queue_sptr target_queue, unsigned int occupied_tones, + float phase_gain, float freq_gain); void enter_search(); void enter_have_sync(); diff --git a/gnuradio-core/src/lib/general/gr_ofdm_frame_sink.i b/gnuradio-core/src/lib/general/gr_ofdm_frame_sink.i index 296eb6591..38ab50e97 100644 --- a/gnuradio-core/src/lib/general/gr_ofdm_frame_sink.i +++ b/gnuradio-core/src/lib/general/gr_ofdm_frame_sink.i @@ -25,14 +25,16 @@ GR_SWIG_BLOCK_MAGIC(gr,ofdm_frame_sink); gr_ofdm_frame_sink_sptr gr_make_ofdm_frame_sink(const std::vector<gr_complex> &sym_position, const std::vector<unsigned char> &sym_value_out, - gr_msg_queue_sptr target_queue, unsigned int occupied_tones); + gr_msg_queue_sptr target_queue, unsigned int occupied_tones, + float phase_gain=0.25, float freq_gain=0.25*0.25/4); class gr_ofdm_frame_sink : public gr_sync_block { protected: gr_ofdm_frame_sink(const std::vector<gr_complex> &sym_position, const std::vector<unsigned char> &sym_value_out, - gr_msg_queue_sptr target_queue, unsigned int occupied_tones); + gr_msg_queue_sptr target_queue, unsigned int occupied_tones, + float phase_gain, float freq_gain); public: ~gr_ofdm_frame_sink(); diff --git a/gnuradio-core/src/lib/general/gr_ofdm_mapper_bcv.cc b/gnuradio-core/src/lib/general/gr_ofdm_mapper_bcv.cc index 4ff55b5d8..f916977df 100644 --- a/gnuradio-core/src/lib/general/gr_ofdm_mapper_bcv.cc +++ b/gnuradio-core/src/lib/general/gr_ofdm_mapper_bcv.cc @@ -54,8 +54,8 @@ gr_ofdm_mapper_bcv::gr_ofdm_mapper_bcv (const std::vector<gr_complex> &constella { if (!(d_occupied_carriers <= d_fft_length)) throw std::invalid_argument("gr_ofdm_mapper_bcv: occupied carriers must be <= fft_length"); - - d_nbits = (unsigned long)(log10(d_constellation.size()) / log10(2)); + + d_nbits = (unsigned long)ceil(log10(d_constellation.size()) / log10(2.0)); } gr_ofdm_mapper_bcv::~gr_ofdm_mapper_bcv(void) diff --git a/gnuradio-core/src/lib/general/gr_ofdm_sampler.cc b/gnuradio-core/src/lib/general/gr_ofdm_sampler.cc index 56b5d50a4..6216df791 100644 --- a/gnuradio-core/src/lib/general/gr_ofdm_sampler.cc +++ b/gnuradio-core/src/lib/general/gr_ofdm_sampler.cc @@ -65,22 +65,26 @@ gr_ofdm_sampler::general_work (int noutput_items, gr_complex *optr = (gr_complex *) output_items[0]; - int found=0; + int found=0, index=0; int i=d_fft_length-1; - while(!found && i<std::min(ninput_items[0],ninput_items[1]) ) { - if(trigger[i]) + // FIXME: This is where we miss if the regeneration happens too soon. + //while(!found && i<std::min(ninput_items[0],ninput_items[1]) ) { + while(i<std::min(ninput_items[0],ninput_items[1]) ) { + if(trigger[i]) { found = 1; + index = i++; + } else i++; } - + if(found) { - assert(i-d_fft_length+1 >= 0); - for(int j=i-d_fft_length+1;j<=i;j++) + assert(index-d_fft_length+1 >= 0); + for(int j=index - d_fft_length + 1; j <= index; j++) *optr++ = iptr[j]; - consume_each(i-d_fft_length+2); + consume_each(index - d_fft_length + 2); //printf("OFDM Sampler found: ninput_items: %d/%d noutput_items: %d consumed: %d found: %d\n", // ninput_items[0], ninput_items[1], noutput_items, (i-d_fft_length+2), found); } diff --git a/gnuradio-core/src/lib/general/gr_peak_detector2_fb.cc b/gnuradio-core/src/lib/general/gr_peak_detector2_fb.cc new file mode 100644 index 000000000..a84cf189f --- /dev/null +++ b/gnuradio-core/src/lib/general/gr_peak_detector2_fb.cc @@ -0,0 +1,106 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 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_peak_detector2_fb.h> +#include <gr_io_signature.h> + +gr_peak_detector2_fb_sptr +gr_make_peak_detector2_fb (float threshold_factor_rise, + int look_ahead, float alpha) +{ + return gr_peak_detector2_fb_sptr (new gr_peak_detector2_fb (threshold_factor_rise, + look_ahead, alpha)); +} + +gr_peak_detector2_fb::gr_peak_detector2_fb (float threshold_factor_rise, + int look_ahead, float alpha) + : gr_sync_block ("peak_detector2_fb", + gr_make_io_signature (1, 1, sizeof(float)), + gr_make_io_signature2 (1, 2, sizeof(char), sizeof(float))), + d_threshold_factor_rise(threshold_factor_rise), + d_look_ahead(look_ahead), d_alpha(alpha), d_avg(0.0f), d_found(false) +{ +} + +int +gr_peak_detector2_fb::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) { + float *iptr = (float *) input_items[0]; + char *optr = (char *) output_items[0]; + + assert(noutput_items >= 2); + + memset(optr, 0, noutput_items*sizeof(char)); + + for (int i = 0; i < noutput_items; i++) { + + if (!d_found) { + // Have not yet detected presence of peak + if (iptr[i] > d_avg * (1.0f + d_threshold_factor_rise)) { + d_found = true; + d_look_ahead_remaining = d_look_ahead; + d_peak_val = -(float)INFINITY; + } + else { + d_avg = d_alpha*iptr[i] + (1.0f - d_alpha)*d_avg; + } + } + else { + // Detected presence of peak + if (iptr[i] > d_peak_val) { + d_peak_val = iptr[i]; + d_peak_ind = i; + } + else if (d_look_ahead_remaining <= 0) { + optr[d_peak_ind] = 1; + d_found = false; + d_avg = iptr[i]; + } + + // Have not yet located peak, loop and keep searching. + d_look_ahead_remaining--; + } + + // Every iteration of the loop, write debugging signal out if + // connected: + if (output_items.size() == 2) { + float *sigout = (float *) output_items[1]; + sigout[i] = d_avg; + } + } // loop + + if (!d_found) + return noutput_items; + + // else if detected presence, keep searching during the next call to work. + int tmp = d_peak_ind; + d_peak_ind = 1; + + return tmp - 1; +} + + diff --git a/gnuradio-core/src/lib/general/gr_peak_detector2_fb.h b/gnuradio-core/src/lib/general/gr_peak_detector2_fb.h new file mode 100644 index 000000000..e20c6c022 --- /dev/null +++ b/gnuradio-core/src/lib/general/gr_peak_detector2_fb.h @@ -0,0 +1,108 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 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_gr_peak_detector2_FB_H +#define INCLUDED_gr_peak_detector2_FB_H + +#include <gr_sync_block.h> + +class gr_peak_detector2_fb; +typedef boost::shared_ptr<gr_peak_detector2_fb> gr_peak_detector2_fb_sptr; + +gr_peak_detector2_fb_sptr gr_make_peak_detector2_fb (float threshold_factor_rise = 7, + int look_ahead = 1000, + float alpha = 0.001); + +/*! + * \brief Detect the peak of a signal + * \ingroup block + * + * If a peak is detected, this block outputs a 1, + * or it outputs 0's. A separate debug output may be connected, to + * view the internal EWMA described below. + * + * \param threshold_factor_rise The threshold factor determins when a peak + * is present. An EWMA average of the signal is calculated and when the + * value of the signal goes over threshold_factor_rise*average, we + * call the peak. + * \param look_ahead The look-ahead value is used when the threshold is + * found to locate the peak within this range. + * \param alpha The gain value of a single-pole moving average filter + */ + +class gr_peak_detector2_fb : public gr_sync_block +{ + friend gr_peak_detector2_fb_sptr + gr_make_peak_detector2_fb (float threshold_factor_rise, int look_ahead, float alpha); + + gr_peak_detector2_fb (float threshold_factor_rise, int look_ahead, float alpha); + +private: + float d_threshold_factor_rise; + int d_look_ahead; + int d_look_ahead_remaining; + int d_peak_ind; + float d_peak_val; + float d_alpha; + float d_avg; + bool d_found; + +public: + + /*! \brief Set the threshold factor value for the rise time + * \param thr new threshold factor + */ + void set_threshold_factor_rise(float thr) { d_threshold_factor_rise = thr; } + + /*! \brief Set the look-ahead factor + * \param look new look-ahead factor + */ + void set_look_ahead(int look) { d_look_ahead = look; } + + /*! \brief Set the running average alpha + * \param alpha new alpha for running average + */ + void set_alpha(int alpha) { d_alpha = alpha; } + + /*! \brief Get the threshold factor value for the rise time + * \return threshold factor + */ + float threshold_factor_rise() { return d_threshold_factor_rise; } + + /*! \brief Get the look-ahead factor value + * \return look-ahead factor + */ + int look_ahead() { return d_look_ahead; } + + /*! \brief Get the alpha value of the running average + * \return alpha + */ + float alpha() { return d_alpha; } + + int work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; + +#endif + + diff --git a/gnuradio-core/src/lib/general/gr_peak_detector2_fb.i b/gnuradio-core/src/lib/general/gr_peak_detector2_fb.i new file mode 100644 index 000000000..4b01435c8 --- /dev/null +++ b/gnuradio-core/src/lib/general/gr_peak_detector2_fb.i @@ -0,0 +1,45 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 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(gr,peak_detector2_fb) + + gr_peak_detector2_fb_sptr gr_make_peak_detector2_fb (float threshold_factor_rise = 7, + int look_ahead = 1000, + float alpha=0.001); + +class gr_peak_detector2_fb : public gr_sync_block +{ +private: + gr_peak_detector2_fb (float threshold_factor_rise, int look_ahead, float alpha); + +public: + void set_threshold_factor_rise(float thr) { d_threshold_factor_rise = thr; } + void set_look_ahead(int look) { d_look_ahead = look; } + void set_alpha(int alpha) { d_avg_alpha = alpha; } + + float threshold_factor_rise() { return d_threshold_factor_rise; } + int look_ahead() { return d_look_ahead; } + float alpha() { return d_avg_alpha; } +}; + + diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/Makefile.am b/gnuradio-core/src/python/gnuradio/blks2impl/Makefile.am index e3d83aeee..934326d8f 100644 --- a/gnuradio-core/src/python/gnuradio/blks2impl/Makefile.am +++ b/gnuradio-core/src/python/gnuradio/blks2impl/Makefile.am @@ -40,6 +40,12 @@ grblkspython_PYTHON = \ cpm.py \ nbfm_rx.py \ nbfm_tx.py \ + ofdm.py \ + ofdm_receiver.py \ + ofdm_sync_fixed.py \ + ofdm_sync_pn.py \ + ofdm_sync_pnac.py \ + ofdm_sync_ml.py \ pkt.py \ psk.py \ qam.py \ diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/ofdm.py b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm.py index b040d8c7f..491ef1a19 100644 --- a/gnuradio-core/src/python/gnuradio/blksimpl/ofdm.py +++ b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm.py @@ -1,4 +1,4 @@ - +#!/usr/bin/env python # # Copyright 2006,2007 Free Software Foundation, Inc. # @@ -21,40 +21,41 @@ # import math -from numpy import fft from gnuradio import gr, ofdm_packet_utils import gnuradio.gr.gr_threading as _threading import psk, qam -from gnuradio.blksimpl.ofdm_receiver import ofdm_receiver +from gnuradio.blks2impl.ofdm_receiver import ofdm_receiver # ///////////////////////////////////////////////////////////////////////////// # mod/demod with packets as i/o # ///////////////////////////////////////////////////////////////////////////// -class ofdm_mod(gr.hier_block): +class ofdm_mod(gr.hier_block2): """ Modulates an OFDM stream. Based on the options fft_length, occupied_tones, and cp_length, this block creates OFDM symbols using a specified modulation option. Send packets by calling send_pkt """ - def __init__(self, fg, options, msgq_limit=2, pad_for_usrp=True): + def __init__(self, options, msgq_limit=2, pad_for_usrp=True): """ Hierarchical block for sending packets Packets to be sent are enqueued by calling send_pkt. The output is the complex modulated signal at baseband. - @param fg: flow graph - @type fg: flow graph @param options: pass modulation options from higher layers (fft length, occupied tones, etc.) @param msgq_limit: maximum number of messages in message queue @type msgq_limit: int @param pad_for_usrp: If true, packets are padded such that they end up a multiple of 128 samples """ + gr.hier_block2.__init__(self, "ofdm_mod", + gr.io_signature(0, 0, 0), # Input signature + gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature + self._pad_for_usrp = pad_for_usrp self._modulation = options.modulation self._fft_length = options.fft_length @@ -71,10 +72,10 @@ class ofdm_mod(gr.hier_block): ksfreq[i] = 0 # hard-coded known symbols - preambles = (ksfreq, - known_symbols_4512_1[0:self._occupied_tones], - known_symbols_4512_2[0:self._occupied_tones]) - + preambles = (ksfreq,) +# known_symbols_4512_1[0:self._occupied_tones], +# known_symbols_4512_2[0:self._occupied_tones]) + padded_preambles = list() for pre in preambles: padded = self._fft_length*[0,] @@ -96,25 +97,29 @@ class ofdm_mod(gr.hier_block): rotated_const = map(lambda pt: pt * rot, qam.constellation[arity]) #print rotated_const self._pkt_input = gr.ofdm_mapper_bcv(rotated_const, msgq_limit, - options.occupied_tones, options.fft_length) + options.occupied_tones, options.fft_length) self.preambles = gr.ofdm_insert_preamble(self._fft_length, padded_preambles) self.ifft = gr.fft_vcc(self._fft_length, False, win, True) self.cp_adder = gr.ofdm_cyclic_prefixer(self._fft_length, symbol_length) self.scale = gr.multiply_const_cc(1.0 / math.sqrt(self._fft_length)) - fg.connect((self._pkt_input, 0), (self.preambles, 0)) - fg.connect((self._pkt_input, 1), (self.preambles, 1)) - fg.connect(self.preambles, self.ifft, self.cp_adder, self.scale) + self.connect((self._pkt_input, 0), (self.preambles, 0)) + self.connect((self._pkt_input, 1), (self.preambles, 1)) + self.connect(self.preambles, self.ifft, self.cp_adder, self.scale, self) if options.verbose: self._print_verbage() if options.log: - fg.connect(self._pkt_input, gr.file_sink(gr.sizeof_gr_complex*options.fft_length, - "ofdm_mapper_c.dat")) - - gr.hier_block.__init__(self, fg, None, self.scale) + self.connect(self._pkt_input, gr.file_sink(gr.sizeof_gr_complex*options.fft_length, + "ofdm_mapper_c.dat")) + self.connect(self.preambles, gr.file_sink(gr.sizeof_gr_complex*options.fft_length, + "ofdm_preambles.dat")) + self.connect(self.ifft, gr.file_sink(gr.sizeof_gr_complex*options.fft_length, + "ofdm_ifft_c.dat")) + self.connect(self.cp_adder, gr.file_sink(gr.sizeof_gr_complex, + "ofdm_cp_adder_c.dat")) def send_pkt(self, payload='', eof=False): """ @@ -138,7 +143,7 @@ class ofdm_mod(gr.hier_block): Adds OFDM-specific options to the Options Parser """ normal.add_option("-m", "--modulation", type="string", default="bpsk", - help="set modulation type (bpsk or qpsk) [default=%default]") + help="set modulation type (bpsk, qpsk, 8psk, qam{16,64}) [default=%default]") expert.add_option("", "--fft-length", type="intx", default=512, help="set the number of FFT bins [default=%default]") expert.add_option("", "--occupied-tones", type="intx", default=200, @@ -159,7 +164,7 @@ class ofdm_mod(gr.hier_block): print "CP length: %3d" % (self._cp_length) -class ofdm_demod(gr.hier_block): +class ofdm_demod(gr.hier_block2): """ Demodulates a received OFDM stream. Based on the options fft_length, occupied_tones, and cp_length, this block performs synchronization, FFT, and demodulation of incoming OFDM @@ -169,19 +174,22 @@ class ofdm_demod(gr.hier_block): app via the callback. """ - def __init__(self, fg, options, callback=None): + def __init__(self, options, callback=None): """ Hierarchical block for demodulating and deframing packets. The input is the complex modulated signal at baseband. Demodulated packets are sent to the handler. - @param fg: flow graph - @type fg: flow graph @param options: pass modulation options from higher layers (fft length, occupied tones, etc.) @param callback: function of two args: ok, payload @type callback: ok: bool; payload: string """ + gr.hier_block2.__init__(self, "ofdm_demod", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature + + self._rcvd_pktq = gr.msg_queue() # holds packets from the PHY self._modulation = options.modulation @@ -190,30 +198,19 @@ class ofdm_demod(gr.hier_block): self._cp_length = options.cp_length self._snr = options.snr - # Use freq domain to get doubled-up known symbol for correlation in time domain ksfreq = known_symbols_4512_3[0:self._occupied_tones] for i in range(len(ksfreq)): if(i&1): ksfreq[i] = 0 - zeros_on_left = int(math.ceil((self._fft_length - self._occupied_tones)/2.0)) - zeros_on_right = self._fft_length - self._occupied_tones - zeros_on_left - ks0 = zeros_on_left*[0.0,] - ks0.extend(ksfreq) - ks0.extend(zeros_on_right*[0.0,]) - - ks0time = fft.ifft(ks0) - # ADD SCALING FACTOR - ks0time = ks0time.tolist() - # hard-coded known symbols - preambles = (ks0time, - known_symbols_4512_1[0:self._occupied_tones], - known_symbols_4512_2[0:self._occupied_tones]) + preambles = (ksfreq,) + #known_symbols_4512_1[0:self._occupied_tones], + #known_symbols_4512_2[0:self._occupied_tones]) symbol_length = self._fft_length + self._cp_length - self.ofdm_recv = ofdm_receiver(fg, self._fft_length, self._cp_length, + self.ofdm_recv = ofdm_receiver(self._fft_length, self._cp_length, self._occupied_tones, self._snr, preambles, options.log) @@ -231,18 +228,27 @@ class ofdm_demod(gr.hier_block): #print rotated_const self.ofdm_demod = gr.ofdm_frame_sink(rotated_const, range(arity), self._rcvd_pktq, - self._occupied_tones) - - fg.connect((self.ofdm_recv, 0), (self.ofdm_demod, 0)) - fg.connect((self.ofdm_recv, 1), (self.ofdm_demod, 1)) + self._occupied_tones, + 0.25, 0.25*.25/4.0) + + self.connect(self, self.ofdm_recv) + self.connect((self.ofdm_recv, 0), (self.ofdm_demod, 0)) + self.connect((self.ofdm_recv, 1), (self.ofdm_demod, 1)) + + # added output signature to work around bug, though it might not be a bad + # thing to export, anyway + self.connect(self.ofdm_recv.chan_filt, self) + + if options.log: + self.connect(self.ofdm_demod, gr.file_sink(gr.sizeof_gr_complex*self._occupied_tones, "ofdm_frame_sink_c.dat")) + else: + self.connect(self.ofdm_demod, gr.null_sink(gr.sizeof_gr_complex*self._occupied_tones)) if options.verbose: self._print_verbage() - gr.hier_block.__init__(self, fg, self.ofdm_recv, None) self._watcher = _queue_watcher_thread(self._rcvd_pktq, callback) - def add_options(normal, expert): """ Adds OFDM-specific options to the Options Parser diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_receiver.py b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_receiver.py new file mode 100644 index 000000000..9592755b1 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_receiver.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python +# +# Copyright 2006, 2007 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. +# + +import math +from numpy import fft +from gnuradio import gr +from gnuradio.blks2impl.ofdm_sync_ml import ofdm_sync_ml +from gnuradio.blks2impl.ofdm_sync_pn import ofdm_sync_pn +from gnuradio.blks2impl.ofdm_sync_pnac import ofdm_sync_pnac +from gnuradio.blks2impl.ofdm_sync_fixed import ofdm_sync_fixed + +class ofdm_receiver(gr.hier_block2): + """ + Performs receiver synchronization on OFDM symbols. + + The receiver performs channel filtering as well as symbol, frequency, and phase synchronization. + The synchronization routines are available in three flavors: preamble correlator (Schmidl and Cox), + modifid preamble correlator with autocorrelation (not yet working), and cyclic prefix correlator + (Van de Beeks). + """ + + def __init__(self, fft_length, cp_length, occupied_tones, snr, ks, logging=False): + """ + Hierarchical block for receiving OFDM symbols. + + The input is the complex modulated signal at baseband. + Synchronized packets are sent back to the demodulator. + + @param fft_length: total number of subcarriers + @type fft_length: int + @param cp_length: length of cyclic prefix as specified in subcarriers (<= fft_length) + @type cp_length: int + @param occupied_tones: number of subcarriers used for data + @type occupied_tones: int + @param snr: estimated signal to noise ratio used to guide cyclic prefix synchronizer + @type snr: float + @param ks: known symbols used as preambles to each packet + @type ks: list of lists + @param logging: turn file logging on or off + @type logging: bool + """ + + gr.hier_block2.__init__(self, "ofdm_receiver", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + gr.io_signature2(2, 2, gr.sizeof_gr_complex*occupied_tones, gr.sizeof_char)) # Output signature + + bw = (float(occupied_tones) / float(fft_length)) / 2.0 + tb = bw*0.08 + chan_coeffs = gr.firdes.low_pass (1.0, # gain + 1.0, # sampling rate + bw+tb, # midpoint of trans. band + tb, # width of trans. band + gr.firdes.WIN_HAMMING) # filter type + self.chan_filt = gr.fft_filter_ccc(1, chan_coeffs) + + win = [1 for i in range(fft_length)] + + zeros_on_left = int(math.ceil((fft_length - occupied_tones)/2.0)) + zeros_on_right = fft_length - occupied_tones - zeros_on_left + ks0 = zeros_on_left*[0.0,] + ks0.extend(ks[0]) + ks0.extend(zeros_on_right*[0.0,]) + + ks0time = fft.ifft(ks0) + # ADD SCALING FACTOR + ks0time = ks0time.tolist() + + SYNC = "pn" + if SYNC == "ml": + self.ofdm_sync = ofdm_sync_ml(fft_length, cp_length, snr, logging) + elif SYNC == "pn": + self.ofdm_sync = ofdm_sync_pn(fft_length, cp_length, logging) + elif SYNC == "pnac": + self.ofdm_sync = ofdm_sync_pnac(fft_length, cp_length, ks0time) + elif SYNC == "fixed": + self.ofdm_sync = ofdm_sync_fixed(fft_length, cp_length, logging) + + self.fft_demod = gr.fft_vcc(fft_length, True, win, True) + self.ofdm_frame_acq = gr.ofdm_frame_acquisition(occupied_tones, fft_length, + cp_length, ks[0]) + + self.connect(self, self.chan_filt) + self.connect(self.chan_filt, self.ofdm_sync, self.fft_demod, (self.ofdm_frame_acq,0)) + self.connect((self.ofdm_sync,1), (self.ofdm_frame_acq,1)) + self.connect((self.ofdm_frame_acq,0), (self,0)) + self.connect((self.ofdm_frame_acq,1), (self,1)) + + if logging: + self.connect(self.chan_filt, gr.file_sink(gr.sizeof_gr_complex, "chan_filt_c.dat")) + self.connect(self.fft_demod, gr.file_sink(gr.sizeof_gr_complex*fft_length, "fft_out_c.dat")) + self.connect(self.ofdm_frame_acq, + gr.file_sink(gr.sizeof_gr_complex*occupied_tones, "ofdm_frame_acq_c.dat")) + self.connect((self.ofdm_frame_acq,1), gr.file_sink(1, "found_corr_b.dat")) diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_fixed.py b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_fixed.py new file mode 100644 index 000000000..74acc8fde --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_fixed.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# +# Copyright 2007 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. +# + +import math +from gnuradio import gr + +class ofdm_sync_fixed(gr.hier_block2): + def __init__(self, fft_length, cp_length, logging=False): + + gr.hier_block2.__init__(self, "ofdm_sync_fixed", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + gr.io_signature2(2, 2, gr.sizeof_gr_complex*fft_length, gr.sizeof_char)) # Output signature + + # Use a fixed trigger point instead of sync block + symbol_length = fft_length + cp_length + data = (symbol_length)*[0,] + data[(symbol_length)-1] = 1 + self.peak_trigger = gr.vector_source_b(data, True) + self.sampler = gr.ofdm_sampler(fft_length, symbol_length) + + self.connect(self, (self.sampler,0)) + self.connect(self.peak_trigger, (self.sampler,1)) + self.connect(self.sampler, (self,0)) + self.connect(self.peak_trigger, (self,1)) + + if logging: + self.connect(self.peak_trigger, gr.file_sink(gr.sizeof_char, "ofdm_sync_fixed-peaks_b.dat")) + self.connect(self.sampler, gr.file_sink(gr.sizeof_gr_complex*fft_length, + "ofdm_sync_fixed-sampler_c.dat")) + diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/ofdm_sync_ml.py b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_ml.py index d58f56cff..a93852623 100644 --- a/gnuradio-core/src/python/gnuradio/blksimpl/ofdm_sync_ml.py +++ b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_ml.py @@ -23,19 +23,22 @@ import math from gnuradio import gr -class ofdm_sync_ml(gr.hier_block): - def __init__(self, fg, fft_length, cp_length, snr, logging): +class ofdm_sync_ml(gr.hier_block2): + def __init__(self, fft_length, cp_length, snr, logging): ''' Maximum Likelihood OFDM synchronizer: J. van de Beek, M. Sandell, and P. O. Borjesson, "ML Estimation of Time and Frequency Offset in OFDM Systems," IEEE Trans. Signal Processing, vol. 45, no. 7, pp. 1800-1805, 1997. ''' - self.fg = fg - - # FIXME: when converting to hier_block2's, the output signature + # FIXME: change the output signature # should be the output of the divider (the normalized peaks) and # the angle value out of the sample and hold block + # move sampler out of this block + + gr.hier_block2.__init__(self, "ofdm_sync_ml", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + gr.io_signature(1, 1, gr.sizeof_gr_complex*fft_length)) # Output signature self.input = gr.add_const_cc(0) @@ -47,9 +50,11 @@ class ofdm_sync_ml(gr.hier_block): # Energy Detection from ML Sync + self.connect(self, self.input) + # Create a delay line self.delay = gr.delay(gr.sizeof_gr_complex, fft_length) - self.fg.connect(self.input, self.delay) + self.connect(self.input, self.delay) # magnitude squared blocks self.magsqrd1 = gr.complex_to_mag_squared() @@ -59,11 +64,11 @@ class ofdm_sync_ml(gr.hier_block): moving_sum_taps = [rho/2 for i in range(cp_length)] self.moving_sum_filter = gr.fir_filter_fff(1,moving_sum_taps) - self.fg.connect(self.input,self.magsqrd1) - self.fg.connect(self.delay,self.magsqrd2) - self.fg.connect(self.magsqrd1,(self.adder,0)) - self.fg.connect(self.magsqrd2,(self.adder,1)) - self.fg.connect(self.adder,self.moving_sum_filter) + self.connect(self.input,self.magsqrd1) + self.connect(self.delay,self.magsqrd2) + self.connect(self.magsqrd1,(self.adder,0)) + self.connect(self.magsqrd2,(self.adder,1)) + self.connect(self.adder,self.moving_sum_filter) # Correlation from ML Sync @@ -76,60 +81,62 @@ class ofdm_sync_ml(gr.hier_block): # Correlator data handler self.c2mag = gr.complex_to_mag() self.angle = gr.complex_to_arg() - self.fg.connect(self.input,(self.mixer,1)) - self.fg.connect(self.delay,self.conjg,(self.mixer,0)) - self.fg.connect(self.mixer,self.movingsum2,self.c2mag) - self.fg.connect(self.movingsum2,self.angle) + self.connect(self.input,(self.mixer,1)) + self.connect(self.delay,self.conjg,(self.mixer,0)) + self.connect(self.mixer,self.movingsum2,self.c2mag) + self.connect(self.movingsum2,self.angle) # ML Sync output arg, need to find maximum point of this self.diff = gr.sub_ff() - self.fg.connect(self.c2mag,(self.diff,0)) - self.fg.connect(self.moving_sum_filter,(self.diff,1)) + self.connect(self.c2mag,(self.diff,0)) + self.connect(self.moving_sum_filter,(self.diff,1)) #ML measurements input to sampler block and detect nco_sensitivity = -1.0/fft_length self.f2c = gr.float_to_complex() self.sampler = gr.ofdm_sampler(fft_length,symbol_length) self.pk_detect = gr.peak_detector_fb(0.2, 0.25, 30, 0.0005) + #self.pk_detect = gr.peak_detector2_fb() self.sample_and_hold = gr.sample_and_hold_ff() self.nco = gr.frequency_modulator_fc(nco_sensitivity) self.sigmix = gr.multiply_cc() # Mix the signal with an NCO controlled by the sync loop - self.fg.connect(self.input, (self.sigmix,0)) - self.fg.connect(self.nco, (self.sigmix,1)) - self.fg.connect(self.sigmix, (self.sampler,0)) + self.connect(self.input, (self.sigmix,0)) + self.connect(self.nco, (self.sigmix,1)) + self.connect(self.sigmix, (self.sampler,0)) # use the sync loop values to set the sampler and the NCO # self.diff = theta # self.angle = epsilon - self.fg.connect(self.diff, self.pk_detect) + self.connect(self.diff, self.pk_detect) use_dpll = 1 if use_dpll: self.dpll = gr.dpll_bb(float(symbol_length),0.01) - self.fg.connect(self.pk_detect, self.dpll) - self.fg.connect(self.dpll, (self.sampler,1)) - self.fg.connect(self.dpll, (self.sample_and_hold,1)) + self.connect(self.pk_detect, self.dpll) + self.connect(self.dpll, (self.sampler,1)) + self.connect(self.dpll, (self.sample_and_hold,1)) else: - self.fg.connect(self.pk_detect, (self.sampler,1)) - self.fg.connect(self.pk_detect, (self.sample_and_hold,1)) + self.connect(self.pk_detect, (self.sampler,1)) + self.connect(self.pk_detect, (self.sample_and_hold,1)) - self.fg.connect(self.angle, (self.sample_and_hold,0)) - self.fg.connect(self.sample_and_hold, self.nco) + self.connect(self.angle, (self.sample_and_hold,0)) + self.connect(self.sample_and_hold, self.nco) + + self.connect(self.sampler, self) if logging: - self.fg.connect(self.diff, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-theta_f.dat")) - self.fg.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-epsilon_f.dat")) - self.fg.connect(self.pk_detect, gr.file_sink(gr.sizeof_char, "ofdm_sync_ml-peaks_b.dat")) + self.connect(self.diff, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-theta_f.dat")) + self.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-epsilon_f.dat")) + self.connect(self.pk_detect, gr.file_sink(gr.sizeof_char, "ofdm_sync_ml-peaks_b.dat")) if use_dpll: - self.fg.connect(self.dpll, gr.file_sink(gr.sizeof_char, "ofdm_sync_ml-dpll_b.dat")) + self.connect(self.dpll, gr.file_sink(gr.sizeof_char, "ofdm_sync_ml-dpll_b.dat")) - self.fg.connect(self.sigmix, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_ml-sigmix_c.dat")) - self.fg.connect(self.sampler, gr.file_sink(gr.sizeof_gr_complex*fft_length, "ofdm_sync_ml-sampler_c.dat")) - self.fg.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-sample_and_hold_f.dat")) - self.fg.connect(self.nco, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_ml-nco_c.dat")) - self.fg.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_ml-input_c.dat")) + self.connect(self.sigmix, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_ml-sigmix_c.dat")) + self.connect(self.sampler, gr.file_sink(gr.sizeof_gr_complex*fft_length, "ofdm_sync_ml-sampler_c.dat")) + self.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-sample_and_hold_f.dat")) + self.connect(self.nco, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_ml-nco_c.dat")) + self.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_ml-input_c.dat")) - gr.hier_block.__init__(self, fg, self.input, self.sampler) diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/ofdm_sync_pn.py b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_pn.py index 56425868f..e3e0ad9d2 100644 --- a/gnuradio-core/src/python/gnuradio/blksimpl/ofdm_sync_pn.py +++ b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_pn.py @@ -24,15 +24,18 @@ import math from numpy import fft from gnuradio import gr -class ofdm_sync_pn(gr.hier_block): - def __init__(self, fg, fft_length, cp_length, logging=False): - ''' OFDM synchronization using PN Correlation: +class ofdm_sync_pn(gr.hier_block2): + def __init__(self, fft_length, cp_length, logging=False): + """ + OFDM synchronization using PN Correlation: T. M. Schmidl and D. C. Cox, "Robust Frequency and Timing Synchonization for OFDM," IEEE Trans. Communications, vol. 45, no. 12, 1997. - ''' + """ - self.fg = fg + gr.hier_block2.__init__(self, "ofdm_sync_pn", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + gr.io_signature2(2, 2, gr.sizeof_gr_complex*fft_length, gr.sizeof_char)) # Output signature # FIXME: when converting to hier_block2's, the output signature # should be the output of the divider (the normalized peaks) and @@ -84,52 +87,60 @@ class ofdm_sync_pn(gr.hier_block): #ML measurements input to sampler block and detect self.sub1 = gr.add_const_ff(-1) - self.pk_detect = gr.peak_detector_fb(0.2, 0.25, 30, 0.0005) + self.pk_detect = gr.peak_detector_fb(0.20, 0.20, 30, 0.001) + #self.pk_detect = gr.peak_detector2_fb() + #self.pk_detect = gr.threshold_detector_fb(0.5) self.regen = gr.regenerate_bb(symbol_length) + # FIXME: If sampler doesn't get proper input, it can completely + # stall the flowgraph. self.sampler = gr.ofdm_sampler(fft_length,symbol_length) + + self.connect(self, self.input) - self.fg.connect(self.input, self.delay) - self.fg.connect(self.input, (self.corr,0)) - self.fg.connect(self.delay, self.conjg) - self.fg.connect(self.conjg, (self.corr,1)) - self.fg.connect(self.corr, self.moving_sum_filter) - self.fg.connect(self.moving_sum_filter, self.c2mag) - self.fg.connect(self.moving_sum_filter, self.angle) - self.fg.connect(self.angle, (self.sample_and_hold,0)) - self.fg.connect(self.sample_and_hold, self.nco) - - self.fg.connect(self.input, (self.sigmix,0)) - self.fg.connect(self.nco, (self.sigmix,1)) - self.fg.connect(self.sigmix, (self.sampler,0)) - - self.fg.connect(self.input, self.inputmag2, self.inputmovingsum) - self.fg.connect(self.inputmovingsum, (self.square,0)) - self.fg.connect(self.inputmovingsum, (self.square,1)) - self.fg.connect(self.square, (self.normalize,1)) - self.fg.connect(self.c2mag, (self.normalize,0)) + self.connect(self.input, self.delay) + self.connect(self.input, (self.corr,0)) + self.connect(self.delay, self.conjg) + self.connect(self.conjg, (self.corr,1)) + self.connect(self.corr, self.moving_sum_filter) + self.connect(self.moving_sum_filter, self.c2mag) + self.connect(self.moving_sum_filter, self.angle) + self.connect(self.angle, (self.sample_and_hold,0)) + self.connect(self.sample_and_hold, self.nco) + + self.connect(self.input, (self.sigmix,0)) + self.connect(self.nco, (self.sigmix,1)) + self.connect(self.sigmix, (self.sampler,0)) + + self.connect(self.input, self.inputmag2, self.inputmovingsum) + self.connect(self.inputmovingsum, (self.square,0)) + self.connect(self.inputmovingsum, (self.square,1)) + self.connect(self.square, (self.normalize,1)) + self.connect(self.c2mag, (self.normalize,0)) # Create a moving sum filter for the corr output matched_filter_taps = [1.0/cp_length for i in range(cp_length)] self.matched_filter = gr.fir_filter_fff(1,matched_filter_taps) - self.fg.connect(self.normalize, self.matched_filter) + self.connect(self.normalize, self.matched_filter) - self.fg.connect(self.matched_filter, self.sub1, self.pk_detect) - self.fg.connect(self.pk_detect, self.regen) - self.fg.connect(self.regen, (self.sampler,1)) - self.fg.connect(self.pk_detect, (self.sample_and_hold,1)) + self.connect(self.matched_filter, self.sub1, self.pk_detect) + self.connect(self.pk_detect, self.regen) + self.connect(self.regen, (self.sampler,1)) + self.connect(self.pk_detect, (self.sample_and_hold,1)) + # Set output from sampler + self.connect(self.sampler, (self,0)) + self.connect(self.pk_detect, (self,1)) if logging: - self.fg.connect(self.matched_filter, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-mf_f.dat")) - self.fg.connect(self.normalize, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-theta_f.dat")) - self.fg.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-epsilon_f.dat")) - self.fg.connect(self.pk_detect, gr.file_sink(gr.sizeof_char, "ofdm_sync_pn-peaks_b.dat")) - self.fg.connect(self.regen, gr.file_sink(gr.sizeof_char, "ofdm_sync_pn-regen_b.dat")) - self.fg.connect(self.sigmix, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pn-sigmix_c.dat")) - self.fg.connect(self.sampler, gr.file_sink(gr.sizeof_gr_complex*fft_length, "ofdm_sync_pn-sampler_c.dat")) - self.fg.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-sample_and_hold_f.dat")) - self.fg.connect(self.nco, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pn-nco_c.dat")) - self.fg.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pn-input_c.dat")) - - gr.hier_block.__init__(self, fg, self.input, self.sampler) + self.connect(self.matched_filter, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-mf_f.dat")) + self.connect(self.normalize, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-theta_f.dat")) + self.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-epsilon_f.dat")) + self.connect(self.pk_detect, gr.file_sink(gr.sizeof_char, "ofdm_sync_pn-peaks_b.dat")) + self.connect(self.regen, gr.file_sink(gr.sizeof_char, "ofdm_sync_pn-regen_b.dat")) + self.connect(self.sigmix, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pn-sigmix_c.dat")) + self.connect(self.sampler, gr.file_sink(gr.sizeof_gr_complex*fft_length, "ofdm_sync_pn-sampler_c.dat")) + self.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-sample_and_hold_f.dat")) + self.connect(self.nco, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pn-nco_c.dat")) + self.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pn-input_c.dat")) + diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_pnac.py b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_pnac.py new file mode 100644 index 000000000..5c16a5703 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm_sync_pnac.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python +# +# Copyright 2007 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. +# + +import math +from numpy import fft +from gnuradio import gr + +class ofdm_sync_pnac(gr.hier_block2): + def __init__(self, fft_length, cp_length, ks): + + # FIXME: change the output signature + # should be the output of the divider (the normalized peaks) and + # the angle value out of the sample and hold block + # move sampler out of this block + + gr.hier_block2.__init__(self, "ofdm_sync_pnac", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + gr.io_signature(1, 1, gr.sizeof_gr_complex*fft_length)) # Output signature + + + self.input = gr.add_const_cc(0) + + symbol_length = fft_length + cp_length + + # PN Sync + + # autocorrelate with the known symbol + ks = ks[0:fft_length//2] + ks.reverse() + self.crosscorr_filter = gr.fir_filter_ccc(1, ks) + self.connect(self.crosscorr_filter, gr.file_sink(gr.sizeof_gr_complex, "crosscorr.dat")) + + # Create a delay line + self.delay = gr.delay(gr.sizeof_gr_complex, fft_length/2) + + # Correlation from ML Sync + self.conjg = gr.conjugate_cc(); + self.corr = gr.multiply_cc(); + + # Create a moving sum filter for the corr output + moving_sum_taps = [1.0 for i in range(fft_length//2)] + self.moving_sum_filter = gr.fir_filter_ccf(1,moving_sum_taps) + + # Create a moving sum filter for the input + self.inputmag2 = gr.complex_to_mag_squared() + movingsum2_taps = [1.0 for i in range(fft_length/2)] + self.inputmovingsum = gr.fir_filter_fff(1,movingsum2_taps) + self.square = gr.multiply_ff() + self.normalize = gr.divide_ff() + + # Get magnitude (peaks) and angle (phase/freq error) + self.c2mag = gr.complex_to_mag_squared() + self.angle = gr.complex_to_arg() + + self.sample_and_hold = gr.sample_and_hold_ff() + + # Mix the signal with an NCO controlled by the sync loop + nco_sensitivity = -1.0/fft_length + self.nco = gr.frequency_modulator_fc(nco_sensitivity) + self.sigmix = gr.multiply_cc() + + #ML measurements input to sampler block and detect + self.sub1 = gr.add_const_ff(-1) + self.pk_detect = gr.peak_detector_fb(0.2, 0.25, 30, 0.0005) + + self.sampler = gr.ofdm_sampler(fft_length,symbol_length) + + self.connect(self, self.input) + self.connect(self.input, self.crosscorr_filter) + self.connect(self.crosscorr_filter, self.delay) + self.connect(self.crosscorr_filter, (self.corr,0)) + self.connect(self.delay, self.conjg) + self.connect(self.conjg, (self.corr,1)) + self.connect(self.corr, self.moving_sum_filter) + self.connect(self.moving_sum_filter, self.c2mag) + self.connect(self.moving_sum_filter, self.angle) + self.connect(self.angle, (self.sample_and_hold,0)) + self.connect(self.sample_and_hold, self.nco) + + self.connect(self.input, (self.sigmix,0)) + self.connect(self.nco, (self.sigmix,1)) + self.connect(self.sigmix, (self.sampler,0)) + + self.connect(self.input, self.inputmag2, self.inputmovingsum) + self.connect(self.inputmovingsum, (self.square,0)) + self.connect(self.inputmovingsum, (self.square,1)) + self.connect(self.square, (self.normalize,1)) + self.connect(self.c2mag, (self.normalize,0)) + self.connect(self.normalize, self.sub1, self.pk_detect) + + self.connect(self.pk_detect, (self.sampler,1)) + self.connect(self.pk_detect, (self.sample_and_hold,1)) + + self.connect(self.sampler, self) + + if 1: + self.connect(self.normalize, gr.file_sink(gr.sizeof_float, + "ofdm_sync_pnac-theta_f.dat")) + self.connect(self.angle, gr.file_sink(gr.sizeof_float, + "ofdm_sync_pnac-epsilon_f.dat")) + self.connect(self.pk_detect, gr.file_sink(gr.sizeof_char, + "ofdm_sync_pnac-peaks_b.dat")) + self.connect(self.sigmix, gr.file_sink(gr.sizeof_gr_complex, + "ofdm_sync_pnac-sigmix_c.dat")) + self.connect(self.sampler, gr.file_sink(gr.sizeof_gr_complex*fft_length, + "ofdm_sync_pnac-sampler_c.dat")) + self.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, + "ofdm_sync_pnac-sample_and_hold_f.dat")) + self.connect(self.nco, gr.file_sink(gr.sizeof_gr_complex, + "ofdm_sync_pnac-nco_c.dat")) + self.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, + "ofdm_sync_pnac-input_c.dat")) diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/Makefile.am b/gnuradio-core/src/python/gnuradio/blksimpl/Makefile.am index b75fade58..74fa098d4 100644 --- a/gnuradio-core/src/python/gnuradio/blksimpl/Makefile.am +++ b/gnuradio-core/src/python/gnuradio/blksimpl/Makefile.am @@ -40,12 +40,6 @@ grblkspython_PYTHON = \ cpm.py \ nbfm_rx.py \ nbfm_tx.py \ - ofdm.py \ - ofdm_receiver.py \ - ofdm_sync_fixed.py \ - ofdm_sync_ml.py \ - ofdm_sync_pnac.py \ - ofdm_sync_pn.py \ pkt.py \ psk.py \ qam.py \ diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/ofdm_receiver.py b/gnuradio-core/src/python/gnuradio/blksimpl/ofdm_receiver.py deleted file mode 100644 index d16d2e294..000000000 --- a/gnuradio-core/src/python/gnuradio/blksimpl/ofdm_receiver.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2006, 2007 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. -# - -import math -from gnuradio import gr -from gnuradio.blksimpl.ofdm_sync_ml import ofdm_sync_ml -from gnuradio.blksimpl.ofdm_sync_pn import ofdm_sync_pn -from gnuradio.blksimpl.ofdm_sync_pnac import ofdm_sync_pnac - -class ofdm_receiver(gr.hier_block): - def __init__(self, fg, fft_length, cp_length, occupied_tones, snr, ks, logging=False): - self.fg = fg - - bw = (float(occupied_tones) / float(fft_length)) / 2.0 - tb = bw*0.08 - chan_coeffs = gr.firdes.low_pass (1.0, # gain - 1.0, # sampling rate - bw+tb, # midpoint of trans. band - tb, # width of trans. band - gr.firdes.WIN_HAMMING) # filter type - self.chan_filt = gr.fft_filter_ccc(1, chan_coeffs) - - win = [1 for i in range(fft_length)] - - SYNC = "pn" - if SYNC == "ml": - self.ofdm_sync = ofdm_sync_ml(fg, fft_length, cp_length, snr, logging) - elif SYNC == "pn": - self.ofdm_sync = ofdm_sync_pn(fg, fft_length, cp_length, logging) - elif SYNC == "pnac": - self.ofdm_sync = ofdm_sync_pnac(fg, fft_length, cp_length, ks[0]) - - self.fft_demod = gr.fft_vcc(fft_length, True, win, True) - self.ofdm_corr = gr.ofdm_correlator(occupied_tones, fft_length, - cp_length, ks[1], ks[2]) - - self.fg.connect(self.chan_filt, self.ofdm_sync, self.fft_demod, self.ofdm_corr) - - if logging: - self.fg.connect(self.chan_filt, gr.file_sink(gr.sizeof_gr_complex, "chan_filt_c.dat")) - self.fg.connect(self.fft_demod, gr.file_sink(gr.sizeof_gr_complex*fft_length, "fft_out_c.dat")) - self.fg.connect(self.ofdm_corr, gr.file_sink(gr.sizeof_gr_complex*occupied_tones, "ofdm_corr_out_c.dat")) - self.fg.connect((self.ofdm_corr,1), gr.file_sink(1, "found_corr_b.dat")) - - gr.hier_block.__init__(self, fg, self.chan_filt, self.ofdm_corr) diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/ofdm_sync_fixed.py b/gnuradio-core/src/python/gnuradio/blksimpl/ofdm_sync_fixed.py deleted file mode 100644 index b56f65660..000000000 --- a/gnuradio-core/src/python/gnuradio/blksimpl/ofdm_sync_fixed.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2007 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. -# - -import math -from gnuradio import gr - -class ofdm_sync_fixed(gr.hier_block): - def __init__(self, fg, fft_length, cp_length, snr): - self.fg = fg - - # Use a fixed trigger point instead of sync block - data = (fft_length+cp_len)*[0,] - data[(fft_length+cp_len)-1] = 1 - peak_trigger = gr.vector_source_b(data, True) - - self.fg.connect(peak_trigger, (self.sampler,1)) - - if 1: - self.fg.connect(self.sampler, gr.file_sink(gr.sizeof_gr_complex*fft_length, - "ofdm_sync_fixed-sampler_c.dat")) - - gr.hier_block.__init__(self, fg, (self.sampler,0), self.sampler) diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/ofdm_sync_pnac.py b/gnuradio-core/src/python/gnuradio/blksimpl/ofdm_sync_pnac.py deleted file mode 100644 index e3774e341..000000000 --- a/gnuradio-core/src/python/gnuradio/blksimpl/ofdm_sync_pnac.py +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2007 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. -# - -import math -from numpy import fft -from gnuradio import gr - -class ofdm_sync_pnac(gr.hier_block): - def __init__(self, fg, fft_length, cp_length, ks): - self.fg = fg - - # FIXME: when converting to hier_block2's, the output signature - # should be the output of the divider (the normalized peaks) and - # the angle value out of the sample and hold block - - self.input = gr.add_const_cc(0) - - symbol_length = fft_length + cp_length - - # PN Sync - - # autocorrelate with the known symbol - ks = ks[0:fft_length//2] - ks.reverse() - self.crosscorr_filter = gr.fir_filter_ccc(1, ks) - self.fg.connect(self.crosscorr_filter, gr.file_sink(gr.sizeof_gr_complex, "crosscorr.dat")) - - # Create a delay line - self.delay = gr.delay(gr.sizeof_gr_complex, fft_length/2) - - # Correlation from ML Sync - self.conjg = gr.conjugate_cc(); - self.corr = gr.multiply_cc(); - - # Create a moving sum filter for the corr output - moving_sum_taps = [1.0 for i in range(fft_length//2)] - self.moving_sum_filter = gr.fir_filter_ccf(1,moving_sum_taps) - - # Create a moving sum filter for the input - self.inputmag2 = gr.complex_to_mag_squared() - movingsum2_taps = [1.0 for i in range(fft_length/2)] - self.inputmovingsum = gr.fir_filter_fff(1,movingsum2_taps) - self.square = gr.multiply_ff() - self.normalize = gr.divide_ff() - - # Get magnitude (peaks) and angle (phase/freq error) - self.c2mag = gr.complex_to_mag_squared() - self.angle = gr.complex_to_arg() - - self.sample_and_hold = gr.sample_and_hold_ff() - - # Mix the signal with an NCO controlled by the sync loop - nco_sensitivity = -1.0/fft_length - self.nco = gr.frequency_modulator_fc(nco_sensitivity) - self.sigmix = gr.multiply_cc() - - #ML measurements input to sampler block and detect - self.sub1 = gr.add_const_ff(-1) - self.pk_detect = gr.peak_detector_fb(0.2, 0.25, 30, 0.0005) - - self.sampler = gr.ofdm_sampler(fft_length,symbol_length) - - self.fg.connect(self.input, self.crosscorr_filter) - self.fg.connect(self.crosscorr_filter, self.delay) - self.fg.connect(self.crosscorr_filter, (self.corr,0)) - self.fg.connect(self.delay, self.conjg) - self.fg.connect(self.conjg, (self.corr,1)) - self.fg.connect(self.corr, self.moving_sum_filter) - self.fg.connect(self.moving_sum_filter, self.c2mag) - self.fg.connect(self.moving_sum_filter, self.angle) - self.fg.connect(self.angle, (self.sample_and_hold,0)) - self.fg.connect(self.sample_and_hold, self.nco) - - self.fg.connect(self.input, (self.sigmix,0)) - self.fg.connect(self.nco, (self.sigmix,1)) - self.fg.connect(self.sigmix, (self.sampler,0)) - - self.fg.connect(self.input, self.inputmag2, self.inputmovingsum) - self.fg.connect(self.inputmovingsum, (self.square,0)) - self.fg.connect(self.inputmovingsum, (self.square,1)) - self.fg.connect(self.square, (self.normalize,1)) - self.fg.connect(self.c2mag, (self.normalize,0)) - self.fg.connect(self.normalize, self.sub1, self.pk_detect) - - self.fg.connect(self.pk_detect, (self.sampler,1)) - self.fg.connect(self.pk_detect, (self.sample_and_hold,1)) - - - if 1: - self.fg.connect(self.normalize, gr.file_sink(gr.sizeof_float, - "ofdm_sync_pnac-theta_f.dat")) - self.fg.connect(self.angle, gr.file_sink(gr.sizeof_float, - "ofdm_sync_pnac-epsilon_f.dat")) - self.fg.connect(self.pk_detect, gr.file_sink(gr.sizeof_char, - "ofdm_sync_pnac-peaks_b.dat")) - self.fg.connect(self.sigmix, gr.file_sink(gr.sizeof_gr_complex, - "ofdm_sync_pnac-sigmix_c.dat")) - self.fg.connect(self.sampler, gr.file_sink(gr.sizeof_gr_complex*fft_length, - "ofdm_sync_pnac-sampler_c.dat")) - self.fg.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, - "ofdm_sync_pnac-sample_and_hold_f.dat")) - self.fg.connect(self.nco, gr.file_sink(gr.sizeof_gr_complex, - "ofdm_sync_pnac-nco_c.dat")) - self.fg.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, - "ofdm_sync_pnac-input_c.dat")) - - gr.hier_block.__init__(self, fg, self.input, self.sampler) diff --git a/gnuradio-core/src/utils/README b/gnuradio-core/src/utils/README new file mode 100644 index 000000000..0c4657ba9 --- /dev/null +++ b/gnuradio-core/src/utils/README @@ -0,0 +1,40 @@ +* gr_plot_*.py: +These are a collection of Python scripts to enable viewing and analysis of files produced by GNU Radio flow graphs. Most of them work off complex data produced by digital waveforms. + + +** gr_plot_float.py: +Takes a GNU Radio floating point binary file and displays the samples versus time. You can set the block size to specify how many points to read in at a time and the start position in the file. + +By default, the system assumes a sample rate of 1, so in time, each sample is plotted versus the sample number. To set a true time axis, set the sample rate (-R or --sample-rate) to the sample rate used when capturing the samples. + + + +** gr_plot_iq.py: +Takes a GNU Radio complex binary file and displays the I&Q data versus time. You can set the block size to specify how many points to read in at a time and the start position in the file. + +By default, the system assumes a sample rate of 1, so in time, each sample is plotted versus the sample number. To set a true time axis, set the sample rate (-R or --sample-rate) to the sample rate used when capturing the samples. + + + +** gr_plot_const.py: +Takes a GNU Radio complex binary file and displays the I&Q data versus time and the constellation plot (I vs. Q). You can set the block size to specify how many points to read in at a time and the start position in the file. + +By default, the system assumes a sample rate of 1, so in time, each sample is plotted versus the sample number. To set a true time axis, set the sample rate (-R or --sample-rate) to the sample rate used when capturing the samples. + + + +** gr_plot_fft_c.py: +Takes a GNU Radio complex binary file and displays the I&Q data versus time as well as the frequency domain (FFT) plot. The y-axis values are plotted assuming volts as the amplitude of the I&Q streams and converted into dBm in the frequency domain (the 1/N power adjustment out of the FFT is performed internally). + +The script plots a certain block of data at a time, specified on the command line as -B or --block. This value defaults to 1000. The start position in the file can be set by specifying -s or --start and defaults to 0 (the start of the file). + +By default, the system assumes a sample rate of 1, so in time, each sample is plotted versus the sample number. To set a true time and frequency axis, set the sample rate (-R or --sample-rate) to the sample rate used when capturing the samples. + + + +** gr_plot_fft_f.py: +Takes a GNU Radio floating point binary file and displays the samples versus time as well as the frequency domain (FFT) plot. The y-axis values are plotted assuming volts as the amplitude of the I&Q streams and converted into dBm in the frequency domain (the 1/N power adjustment out of the FFT is performed internally). + +The script plots a certain block of data at a time, specified on the command line as -B or --block. This value defaults to 1000. The start position in the file can be set by specifying -s or --start and defaults to 0 (the start of the file). + +By default, the system assumes a sample rate of 1, so in time, each sample is plotted versus the sample number. To set a true time and frequency axis, set the sample rate (-R or --sample-rate) to the sample rate used when capturing the samples. diff --git a/gnuradio-core/src/utils/gr_plot_char.py b/gnuradio-core/src/utils/gr_plot_char.py new file mode 100755 index 000000000..6f799cdd2 --- /dev/null +++ b/gnuradio-core/src/utils/gr_plot_char.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python +# +# Copyright 2007 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. +# + +import scipy +from pylab import * +from optparse import OptionParser + +matplotlib.interactive(True) +matplotlib.use('TkAgg') + +class draw_fft_c: + def __init__(self, filename, options): + self.hfile = open(filename, "r") + self.block_length = options.block + self.start = options.start + self.sample_rate = options.sample_rate + + self.axis_font_size = 16 + self.label_font_size = 18 + self.title_font_size = 20 + self.text_size = 22 + + # Setup PLOT + self.fig = figure(1, figsize=(16, 9), facecolor='w') + rcParams['xtick.labelsize'] = self.axis_font_size + rcParams['ytick.labelsize'] = self.axis_font_size + + self.text_file = figtext(0.10, 0.94, ("File: %s" % filename), weight="heavy", size=self.text_size) + self.text_file_pos = figtext(0.10, 0.88, "File Position: ", weight="heavy", size=self.text_size) + self.text_block = figtext(0.40, 0.88, ("Block Size: %d" % self.block_length), + weight="heavy", size=self.text_size) + self.text_sr = figtext(0.60, 0.88, ("Sample Rate: %.2f" % self.sample_rate), + weight="heavy", size=self.text_size) + self.make_plots() + + self.button_left_axes = self.fig.add_axes([0.45, 0.01, 0.05, 0.05], frameon=True) + self.button_left = Button(self.button_left_axes, "<") + self.button_left_callback = self.button_left.on_clicked(self.button_left_click) + + self.button_right_axes = self.fig.add_axes([0.50, 0.01, 0.05, 0.05], frameon=True) + self.button_right = Button(self.button_right_axes, ">") + self.button_right_callback = self.button_right.on_clicked(self.button_right_click) + + self.xlim = self.sp_f.get_xlim() + + self.manager = get_current_fig_manager() + connect('key_press_event', self.click) + show() + + def get_data(self): + self.text_file_pos.set_text("File Position: %d" % (self.hfile.tell())) + f = scipy.fromfile(self.hfile, dtype=scipy.int8, count=self.block_length) + #print "Read in %d items" % len(self.f) + if(len(f) == 0): + print "End of File" + else: + self.f = f + self.time = [i*(1/self.sample_rate) for i in range(len(self.f))] + + def make_plots(self): + # if specified on the command-line, set file pointer + self.hfile.seek(8*self.start, 1) + + self.get_data() + + # Subplot for real and imaginary parts of signal + self.sp_f = self.fig.add_subplot(2,1,1, position=[0.075, 0.2, 0.875, 0.6]) + self.sp_f.set_title(("Amplitude"), fontsize=self.title_font_size, fontweight="bold") + self.sp_f.set_xlabel("Time (s)", fontsize=self.label_font_size, fontweight="bold") + self.sp_f.set_ylabel("Amplitude (V)", fontsize=self.label_font_size, fontweight="bold") + self.plot_f = plot(self.time, self.f, 'bo-') + self.sp_f.set_ylim([1.5*min(self.f), + 1.5*max(self.f)]) + + draw() + + def update_plots(self): + self.plot_f[0].set_data([self.time, self.f]) + self.sp_f.set_ylim([1.5*min(self.f), + 1.5*max(self.f)]) + draw() + + def click(self, event): + forward_valid_keys = [" ", "down", "right"] + backward_valid_keys = ["up", "left"] + + if(find(event.key, forward_valid_keys)): + self.step_forward() + + elif(find(event.key, backward_valid_keys)): + self.step_backward() + + def button_left_click(self, event): + self.step_backward() + + def button_right_click(self, event): + self.step_forward() + + def step_forward(self): + self.get_data() + self.update_plots() + + def step_backward(self): + # Step back in file position + if(self.hfile.tell() >= 2*self.block_length ): + self.hfile.seek(-2*self.block_length, 1) + else: + self.hfile.seek(-self.hfile.tell(),1) + self.get_data() + self.update_plots() + + + +#FIXME: there must be a way to do this with a Python builtin +def find(item_in, list_search): + for l in list_search: + if item_in == l: + return True + return False + +def main(): + usage="%prog: [options] input_filename" + description = "Takes a GNU Radio floating point binary file and displays the samples versus time. You can set the block size to specify how many points to read in at a time and the start position in the file. By default, the system assumes a sample rate of 1, so in time, each sample is plotted versus the sample number. To set a true time axis, set the sample rate (-R or --sample-rate) to the sample rate used when capturing the samples." + + parser = OptionParser(conflict_handler="resolve", usage=usage, description=description) + parser.add_option("-B", "--block", type="int", default=1000, + help="Specify the block size [default=%default]") + parser.add_option("-s", "--start", type="int", default=0, + help="Specify where to start in the file [default=%default]") + parser.add_option("-R", "--sample-rate", type="float", default=1.0, + help="Set the sampler rate of the data [default=%default]") + + (options, args) = parser.parse_args () + if len(args) != 1: + parser.print_help() + raise SystemExit, 1 + filename = args[0] + + dc = draw_fft_c(filename, options) + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gnuradio-core/src/utils/gr_plot_const.py b/gnuradio-core/src/utils/gr_plot_const.py index effda3e0c..bbcbb85cc 100755 --- a/gnuradio-core/src/utils/gr_plot_const.py +++ b/gnuradio-core/src/utils/gr_plot_const.py @@ -20,88 +20,171 @@ # Boston, MA 02110-1301, USA. # -import pylab, math +import scipy from pylab import * -import struct, sys +from matplotlib.font_manager import fontManager, FontProperties from optparse import OptionParser -import gr_read_binary +matplotlib.interactive(True) +matplotlib.use('TkAgg') -class zoom: - def __init__(self, xdata, reals, imags, sp_iq, sp_const, plot_const, manager): - self.sp_iq = sp_iq - self.sp_const = sp_const - self.xaxis = xdata - self.reals = reals - self.imags = imags - self.plot_const = plot_const - self.manager = manager +class draw_constellation: + def __init__(self, filename, options): + self.hfile = open(filename, "r") + self.block_length = options.block + self.start = options.start + self.sample_rate = options.sample_rate + + self.axis_font_size = 16 + self.label_font_size = 18 + self.title_font_size = 20 + + # Setup PLOT + self.fig = figure(1, figsize=(16, 9), facecolor='w') + rcParams['xtick.labelsize'] = self.axis_font_size + rcParams['ytick.labelsize'] = self.axis_font_size + + self.text_file = figtext(0.10, 0.95, ("File: %s" % filename), weight="heavy", size=16) + self.text_file_pos = figtext(0.10, 0.90, "File Position: ", weight="heavy", size=16) + self.text_block = figtext(0.40, 0.90, ("Block Size: %d" % self.block_length), + weight="heavy", size=16) + self.text_sr = figtext(0.60, 0.90, ("Sample Rate: %.2f" % self.sample_rate), + weight="heavy", size=16) + self.make_plots() + + self.button_left_axes = self.fig.add_axes([0.45, 0.01, 0.05, 0.05], frameon=True) + self.button_left = Button(self.button_left_axes, "<") + self.button_left_callback = self.button_left.on_clicked(self.button_left_click) + + self.button_right_axes = self.fig.add_axes([0.50, 0.01, 0.05, 0.05], frameon=True) + self.button_right = Button(self.button_right_axes, ">") + self.button_right_callback = self.button_right.on_clicked(self.button_right_click) self.xlim = self.sp_iq.get_xlim() - - def __call__(self, event): - newxlim = self.sp_iq.get_xlim() + self.manager = get_current_fig_manager() + connect('draw_event', self.zoom) + connect('key_press_event', self.click) + show() + + def get_data(self): + self.text_file_pos.set_text("File Position: %d" % (self.hfile.tell()//8)) + iq = scipy.fromfile(self.hfile, dtype=scipy.complex64, count=self.block_length) + #print "Read in %d items" % len(iq) + if(len(iq) == 0): + print "End of File" + else: + self.reals = [r.real for r in iq] + self.imags = [i.imag for i in iq] + + self.time = [i*(1/self.sample_rate) for i in range(len(self.reals))] + + def make_plots(self): + # if specified on the command-line, set file pointer + self.hfile.seek(16*self.start, 1) + + self.get_data() + + # Subplot for real and imaginary parts of signal + self.sp_iq = self.fig.add_subplot(2,1,1, position=[0.075, 0.2, 0.4, 0.6]) + self.sp_iq.set_title(("I&Q"), fontsize=self.title_font_size, fontweight="bold") + self.sp_iq.set_xlabel("Time (s)", fontsize=self.label_font_size, fontweight="bold") + self.sp_iq.set_ylabel("Amplitude (V)", fontsize=self.label_font_size, fontweight="bold") + self.plot_iq = plot(self.time, self.reals, 'bo-', self.time, self.imags, 'ro-') + self.sp_iq.axis([min(self.time), max(self.time), + 1.5*min([min(self.reals), min(self.imags)]), + 1.5*max([max(self.reals), max(self.imags)])]) + + # Subplot for constellation plot + self.sp_const = self.fig.add_subplot(2,2,1, position=[0.575, 0.2, 0.4, 0.6]) + self.sp_const.set_title(("Constellation"), fontsize=self.title_font_size, fontweight="bold") + self.sp_const.set_xlabel("Inphase", fontsize=self.label_font_size, fontweight="bold") + self.sp_const.set_ylabel("Qaudrature", fontsize=self.label_font_size, fontweight="bold") + self.plot_const = plot(self.reals, self.imags, 'bo') + self.sp_const.axis([-2, 2, -2, 2]) + + draw() + + def update_plots(self): + self.plot_iq[0].set_data([self.time, self.reals]) + self.plot_iq[1].set_data([self.time, self.imags]) + self.sp_iq.axis([min(self.time), max(self.time), + 1.5*min([min(self.reals), min(self.imags)]), + 1.5*max([max(self.reals), max(self.imags)])]) + + self.plot_const[0].set_data([self.reals, self.imags]) + self.sp_const.axis([-2, 2, -2, 2]) + draw() + + def zoom(self, event): + newxlim = self.sp_iq.get_xlim() if(newxlim != self.xlim): self.xlim = newxlim - r = self.reals[int(self.xlim[0]) : int(self.xlim[1])] - i = self.imags[int(self.xlim[0]) : int(self.xlim[1])] + r = self.reals[int(ceil(self.xlim[0])) : int(ceil(self.xlim[1]))] + i = self.imags[int(ceil(self.xlim[0])) : int(ceil(self.xlim[1]))] self.plot_const[0].set_data(r, i) self.sp_const.axis([-2, 2, -2, 2]) self.manager.canvas.draw() + draw() + + def click(self, event): + forward_valid_keys = [" ", "down", "right"] + backward_valid_keys = ["up", "left"] + + if(find(event.key, forward_valid_keys)): + self.step_forward() + + elif(find(event.key, backward_valid_keys)): + self.step_backward() + + def button_left_click(self, event): + self.step_backward() + + def button_right_click(self, event): + self.step_forward() + + def step_forward(self): + self.get_data() + self.update_plots() + + def step_backward(self): + # Step back in file position + if(self.hfile.tell() >= 16*self.block_length ): + self.hfile.seek(-16*self.block_length, 1) + else: + self.hfile.seek(-self.hfile.tell(),1) + self.get_data() + self.update_plots() + + +#FIXME: there must be a way to do this with a Python builtin +def find(item_in, list_search): + for l in list_search: + if item_in == l: + return True + return False + def main(): - usage="%prog: [options] output_filename" - parser = OptionParser(conflict_handler="resolve", usage=usage) - parser.add_option("-s", "--size", type="int", default=None, - help="Specify the number of points to plot [default=%default]") - parser.add_option("", "--skip", type="int", default=None, - help="Specify the number of points to skip [default=%default]") + usage="%prog: [options] input_filename" + description = "Takes a GNU Radio complex binary file and displays the I&Q data versus time and the constellation plot (I vs. Q). You can set the block size to specify how many points to read in at a time and the start position in the file. By default, the system assumes a sample rate of 1, so in time, each sample is plotted versus the sample number. To set a true time axis, set the sample rate (-R or --sample-rate) to the sample rate used when capturing the samples." + parser = OptionParser(conflict_handler="resolve", usage=usage, description=description) + parser.add_option("-B", "--block", type="int", default=1000, + help="Specify the block size [default=%default]") + parser.add_option("-s", "--start", type="int", default=0, + help="Specify where to start in the file [default=%default]") + parser.add_option("-R", "--sample-rate", type="float", default=1.0, + help="Set the sampler rate of the data [default=%default]") + (options, args) = parser.parse_args () if len(args) != 1: parser.print_help() raise SystemExit, 1 filename = args[0] - iq = gr_read_binary.read_complex_binary(filename) - - if(options.skip is None): - options.skip = 0 - - if((options.size is None) or ((options.skip+options.size) > len(iq[0]))): - options.size = len(iq[0]) - options.skip - - reals = iq[0][options.skip : options.skip + options.size] - imags = iq[1][options.skip : options.skip + options.size] - x = range(options.skip, options.skip + options.size) - - # PLOT - f = figure(1, figsize=(16, 12), facecolor='w') - rcParams['xtick.labelsize'] = 16 - rcParams['ytick.labelsize'] = 16 - - # Subplot for real and imaginary parts of signal - sp1 = f.add_subplot(2,1,1) - sp1.set_title(("I&Q"), fontsize=26, fontweight="bold") - sp1.set_xlabel("Time (s)", fontsize=20, fontweight="bold") - sp1.set_ylabel("Amplitude (V)", fontsize=20, fontweight="bold") - plot(x, reals, 'bo-', x, imags, 'ro-') - - # Subplot for constellation plot - sp2 = f.add_subplot(2,1,2) - sp2.set_title(("Constellation"), fontsize=26, fontweight="bold") - sp2.set_xlabel("Inphase", fontsize=20, fontweight="bold") - sp2.set_ylabel("Qaudrature", fontsize=20, fontweight="bold") - p2 = plot(reals, imags, 'bo') - sp2.axis([-2, 2, -2, 2]) - - manager = get_current_fig_manager() - zm = zoom(x, reals, imags, sp1, sp2, p2, manager) - connect('draw_event', zm) - - show() + dc = draw_constellation(filename, options) if __name__ == "__main__": try: diff --git a/gnuradio-core/src/utils/gr_plot_fft_c.py b/gnuradio-core/src/utils/gr_plot_fft_c.py new file mode 100755 index 000000000..924566968 --- /dev/null +++ b/gnuradio-core/src/utils/gr_plot_fft_c.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python +# +# Copyright 2007 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. +# + +import scipy +from pylab import * +from optparse import OptionParser +from scipy import fftpack +from math import log10 + +matplotlib.interactive(True) +matplotlib.use('TkAgg') + +class draw_fft_c: + def __init__(self, filename, options): + self.hfile = open(filename, "r") + self.block_length = options.block + self.start = options.start + self.sample_rate = options.sample_rate + + self.axis_font_size = 16 + self.label_font_size = 18 + self.title_font_size = 20 + self.text_size = 22 + + # Setup PLOT + self.fig = figure(1, figsize=(16, 9), facecolor='w') + rcParams['xtick.labelsize'] = self.axis_font_size + rcParams['ytick.labelsize'] = self.axis_font_size + + self.text_file = figtext(0.10, 0.94, ("File: %s" % filename), weight="heavy", size=self.text_size) + self.text_file_pos = figtext(0.10, 0.88, "File Position: ", weight="heavy", size=self.text_size) + self.text_block = figtext(0.40, 0.88, ("Block Size: %d" % self.block_length), + weight="heavy", size=self.text_size) + self.text_sr = figtext(0.60, 0.88, ("Sample Rate: %.2f" % self.sample_rate), + weight="heavy", size=self.text_size) + self.make_plots() + + self.button_left_axes = self.fig.add_axes([0.45, 0.01, 0.05, 0.05], frameon=True) + self.button_left = Button(self.button_left_axes, "<") + self.button_left_callback = self.button_left.on_clicked(self.button_left_click) + + self.button_right_axes = self.fig.add_axes([0.50, 0.01, 0.05, 0.05], frameon=True) + self.button_right = Button(self.button_right_axes, ">") + self.button_right_callback = self.button_right.on_clicked(self.button_right_click) + + self.xlim = self.sp_iq.get_xlim() + + self.manager = get_current_fig_manager() + connect('draw_event', self.zoom) + connect('key_press_event', self.click) + show() + + def get_data(self): + self.text_file_pos.set_text("File Position: %d" % (self.hfile.tell()//8)) + self.iq = scipy.fromfile(self.hfile, dtype=scipy.complex64, count=self.block_length) + #print "Read in %d items" % len(self.iq) + if(len(self.iq) == 0): + print "End of File" + else: + self.reals = [r.real for r in self.iq] + self.imags = [i.imag for i in self.iq] + + self.iq_fft = self.dofft(self.iq) + + self.time = [i*(1/self.sample_rate) for i in range(len(self.reals))] + self.freq = self.calc_freq(self.time, self.sample_rate) + + + def dofft(self, iq): + N = len(iq) + iq_fft = fftpack.fftshift(scipy.fft(iq)) # fft and shift axis + iq_fft = [20*log10(abs(i/N)) for i in iq_fft] # convert to decibels, adjust power + return iq_fft + + def calc_freq(self, time, sample_rate): + N = len(time) + Fs = 1.0 / (max(time) - min(time)) + Fn = 0.5 * sample_rate + freq = [-Fn + i*Fs for i in range(N)] + return freq + + def make_plots(self): + # if specified on the command-line, set file pointer + self.hfile.seek(16*self.start, 1) + + self.get_data() + + # Subplot for real and imaginary parts of signal + self.sp_iq = self.fig.add_subplot(2,1,1, position=[0.075, 0.2, 0.4, 0.6]) + self.sp_iq.set_title(("I&Q"), fontsize=self.title_font_size, fontweight="bold") + self.sp_iq.set_xlabel("Time (s)", fontsize=self.label_font_size, fontweight="bold") + self.sp_iq.set_ylabel("Amplitude (V)", fontsize=self.label_font_size, fontweight="bold") + self.plot_iq = plot(self.time, self.reals, 'bo-', self.time, self.imags, 'ro-') + self.sp_iq.set_ylim([1.5*min([min(self.reals), min(self.imags)]), + 1.5*max([max(self.reals), max(self.imags)])]) + + # Subplot for constellation plot + self.sp_fft = self.fig.add_subplot(2,2,1, position=[0.575, 0.2, 0.4, 0.6]) + self.sp_fft.set_title(("FFT"), fontsize=self.title_font_size, fontweight="bold") + self.sp_fft.set_xlabel("Frequency (Hz)", fontsize=self.label_font_size, fontweight="bold") + self.sp_fft.set_ylabel("Power (dBm)", fontsize=self.label_font_size, fontweight="bold") + self.plot_fft = plot(self.freq, self.iq_fft, '-bo') + self.sp_fft.set_ylim([min(self.iq_fft)-10, max(self.iq_fft)+10]) + + draw() + + def update_plots(self): + self.plot_iq[0].set_data([self.time, self.reals]) + self.plot_iq[1].set_data([self.time, self.imags]) + self.sp_iq.set_ylim([1.5*min([min(self.reals), min(self.imags)]), + 1.5*max([max(self.reals), max(self.imags)])]) + + self.plot_fft[0].set_data([self.freq, self.iq_fft]) + self.sp_fft.set_ylim([min(self.iq_fft)-10, max(self.iq_fft)+10]) + + draw() + + def zoom(self, event): + newxlim = self.sp_iq.get_xlim() + if(newxlim != self.xlim): + self.xlim = newxlim + xmin = max(0, int(ceil(self.sample_rate*self.xlim[0]))) + xmax = min(int(ceil(self.sample_rate*self.xlim[1])), len(self.iq)) + + iq = self.iq[xmin : xmax] + time = self.time[xmin : xmax] + + iq_fft = self.dofft(iq) + freq = self.calc_freq(time, self.sample_rate) + + self.plot_fft[0].set_data(freq, iq_fft) + self.sp_fft.axis([min(freq), max(freq), + min(iq_fft)-10, max(iq_fft)+10]) + + draw() + + def click(self, event): + forward_valid_keys = [" ", "down", "right"] + backward_valid_keys = ["up", "left"] + + if(find(event.key, forward_valid_keys)): + self.step_forward() + + elif(find(event.key, backward_valid_keys)): + self.step_backward() + + def button_left_click(self, event): + self.step_backward() + + def button_right_click(self, event): + self.step_forward() + + def step_forward(self): + self.get_data() + self.update_plots() + + def step_backward(self): + # Step back in file position + if(self.hfile.tell() >= 16*self.block_length ): + self.hfile.seek(-16*self.block_length, 1) + else: + self.hfile.seek(-self.hfile.tell(),1) + self.get_data() + self.update_plots() + + + +#FIXME: there must be a way to do this with a Python builtin +def find(item_in, list_search): + for l in list_search: + if item_in == l: + return True + return False + +def main(): + usage="%prog: [options] input_filename" + description = "Takes a GNU Radio complex binary file and displays the I&Q data versus time as well as the frequency domain (FFT) plot. The y-axis values are plotted assuming volts as the amplitude of the I&Q streams and converted into dBm in the frequency domain (the 1/N power adjustment out of the FFT is performed internally). The script plots a certain block of data at a time, specified on the command line as -B or --block. This value defaults to 1000. The start position in the file can be set by specifying -s or --start and defaults to 0 (the start of the file). By default, the system assumes a sample rate of 1, so in time, each sample is plotted versus the sample number. To set a true time and frequency axis, set the sample rate (-R or --sample-rate) to the sample rate used when capturing the samples." + + parser = OptionParser(conflict_handler="resolve", usage=usage, description=description) + parser.add_option("-B", "--block", type="int", default=1000, + help="Specify the block size [default=%default]") + parser.add_option("-s", "--start", type="int", default=0, + help="Specify where to start in the file [default=%default]") + parser.add_option("-R", "--sample-rate", type="float", default=1.0, + help="Set the sampler rate of the data [default=%default]") + + (options, args) = parser.parse_args () + if len(args) != 1: + parser.print_help() + raise SystemExit, 1 + filename = args[0] + + dc = draw_fft_c(filename, options) + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + + + diff --git a/gnuradio-core/src/utils/gr_plot_fft_f.py b/gnuradio-core/src/utils/gr_plot_fft_f.py new file mode 100755 index 000000000..717efdfb5 --- /dev/null +++ b/gnuradio-core/src/utils/gr_plot_fft_f.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python +# +# Copyright 2007 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. +# + +import scipy +from pylab import * +from optparse import OptionParser +from scipy import fftpack +from math import log10 + +matplotlib.interactive(True) +matplotlib.use('TkAgg') + +class draw_fft_f: + def __init__(self, filename, options): + self.hfile = open(filename, "r") + self.block_length = options.block + self.start = options.start + self.sample_rate = options.sample_rate + + self.axis_font_size = 16 + self.label_font_size = 18 + self.title_font_size = 20 + self.text_size = 22 + + # Setup PLOT + self.fig = figure(1, figsize=(16, 9), facecolor='w') + rcParams['xtick.labelsize'] = self.axis_font_size + rcParams['ytick.labelsize'] = self.axis_font_size + + self.text_file = figtext(0.10, 0.94, ("File: %s" % filename), weight="heavy", size=self.text_size) + self.text_file_pos = figtext(0.10, 0.88, "File Position: ", weight="heavy", size=self.text_size) + self.text_block = figtext(0.40, 0.88, ("Block Size: %d" % self.block_length), + weight="heavy", size=self.text_size) + self.text_sr = figtext(0.60, 0.88, ("Sample Rate: %.2f" % self.sample_rate), + weight="heavy", size=self.text_size) + self.make_plots() + + self.button_left_axes = self.fig.add_axes([0.45, 0.01, 0.05, 0.05], frameon=True) + self.button_left = Button(self.button_left_axes, "<") + self.button_left_callback = self.button_left.on_clicked(self.button_left_click) + + self.button_right_axes = self.fig.add_axes([0.50, 0.01, 0.05, 0.05], frameon=True) + self.button_right = Button(self.button_right_axes, ">") + self.button_right_callback = self.button_right.on_clicked(self.button_right_click) + + self.xlim = self.sp_f.get_xlim() + + self.manager = get_current_fig_manager() + connect('draw_event', self.zoom) + connect('key_press_event', self.click) + show() + + def get_data(self): + self.text_file_pos.set_text("File Position: %d" % (self.hfile.tell()//4)) + self.floats = scipy.fromfile(self.hfile, dtype=scipy.float32, count=self.block_length) + #print "Read in %d items" % len(self.floats) + if(len(self.floats) == 0): + print "End of File" + else: + self.f_fft = self.dofft(self.floats) + + self.time = [i*(1/self.sample_rate) for i in range(len(self.floats))] + self.freq = self.calc_freq(self.time, self.sample_rate) + + def dofft(self, f): + N = len(f) + f_fft = fftpack.fftshift(scipy.fft(f)) # fft and shift axis + f_dB = list() + for f in f_fft: + try: + f_dB.append(20*log10(abs(f/N))) # convert to decibels, adjust power + except OverflowError: # protect against taking log(0) + f = 1e-14 # not sure if this is the best way to do this + f_dB.append(20*log10(abs(f/N))) + + return f_dB + + def calc_freq(self, time, sample_rate): + N = len(time) + Fs = 1.0 / (max(time) - min(time)) + Fn = 0.5 * sample_rate + freq = [-Fn + i*Fs for i in range(N)] + return freq + + def make_plots(self): + # if specified on the command-line, set file pointer + self.hfile.seek(16*self.start, 1) + + self.get_data() + + # Subplot for real and imaginary parts of signal + self.sp_f = self.fig.add_subplot(2,1,1, position=[0.075, 0.2, 0.4, 0.6]) + self.sp_f.set_title(("Amplitude"), fontsize=self.title_font_size, fontweight="bold") + self.sp_f.set_xlabel("Time (s)", fontsize=self.label_font_size, fontweight="bold") + self.sp_f.set_ylabel("Amplitude (V)", fontsize=self.label_font_size, fontweight="bold") + self.plot_f = plot(self.time, self.floats, 'bo-') + self.sp_f.set_ylim([1.5*min(self.floats), + 1.5*max(self.floats)]) + + # Subplot for constellation plot + self.sp_fft = self.fig.add_subplot(2,2,1, position=[0.575, 0.2, 0.4, 0.6]) + self.sp_fft.set_title(("FFT"), fontsize=self.title_font_size, fontweight="bold") + self.sp_fft.set_xlabel("Frequency (Hz)", fontsize=self.label_font_size, fontweight="bold") + self.sp_fft.set_ylabel("Power (dBm)", fontsize=self.label_font_size, fontweight="bold") + self.plot_fft = plot(self.freq, self.f_fft, '-bo') + self.sp_fft.set_ylim([min(self.f_fft)-10, max(self.f_fft)+10]) + + draw() + + def update_plots(self): + self.plot_f[0].set_data([self.time, self.floats]) + self.sp_f.set_ylim([1.5*min(self.floats), + 1.5*max(self.floats)]) + + self.plot_fft[0].set_data([self.freq, self.f_fft]) + self.sp_fft.set_ylim([min(self.f_fft)-10, max(self.f_fft)+10]) + + draw() + + def zoom(self, event): + newxlim = self.sp_f.get_xlim() + if(newxlim != self.xlim): + self.xlim = newxlim + xmin = max(0, int(ceil(self.sample_rate*self.xlim[0]))) + xmax = min(int(ceil(self.sample_rate*self.xlim[1])), len(self.floats)) + + f = self.floats[xmin : xmax] + time = self.time[xmin : xmax] + + f_fft = self.dofft(f) + freq = self.calc_freq(time, self.sample_rate) + + self.plot_fft[0].set_data(freq, f_fft) + self.sp_fft.axis([min(freq), max(freq), + min(f_fft)-10, max(f_fft)+10]) + + draw() + + def click(self, event): + forward_valid_keys = [" ", "down", "right"] + backward_valid_keys = ["up", "left"] + + if(find(event.key, forward_valid_keys)): + self.step_forward() + + elif(find(event.key, backward_valid_keys)): + self.step_backward() + + def button_left_click(self, event): + self.step_backward() + + def button_right_click(self, event): + self.step_forward() + + def step_forward(self): + self.get_data() + self.update_plots() + + def step_backward(self): + # Step back in file position + if(self.hfile.tell() >= 8*self.block_length ): + self.hfile.seek(-8*self.block_length, 1) + else: + self.hfile.seek(-self.hfile.tell(),1) + self.get_data() + self.update_plots() + + + +#FIXME: there must be a way to do this with a Python builtin +def find(item_in, list_search): + for l in list_search: + if item_in == l: + return True + return False + +def main(): + usage="%prog: [options] input_filename" + description = "Takes a GNU Radio floating point binary file and displays the sample data versus time as well as the frequency domain (FFT) plot. The y-axis values are plotted assuming volts as the amplitude of the I&Q streams and converted into dBm in the frequency domain (the 1/N power adjustment out of the FFT is performed internally). The script plots a certain block of data at a time, specified on the command line as -B or --block. This value defaults to 1000. The start position in the file can be set by specifying -s or --start and defaults to 0 (the start of the file). By default, the system assumes a sample rate of 1, so in time, each sample is plotted versus the sample number. To set a true time and frequency axis, set the sample rate (-R or --sample-rate) to the sample rate used when capturing the samples." + + parser = OptionParser(conflict_handler="resolve", usage=usage, description=description) + parser.add_option("-B", "--block", type="int", default=1000, + help="Specify the block size [default=%default]") + parser.add_option("-s", "--start", type="int", default=0, + help="Specify where to start in the file [default=%default]") + parser.add_option("-R", "--sample-rate", type="float", default=1.0, + help="Set the sampler rate of the data [default=%default]") + + (options, args) = parser.parse_args () + if len(args) != 1: + parser.print_help() + raise SystemExit, 1 + filename = args[0] + + dc = draw_fft_f(filename, options) + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + + + diff --git a/gnuradio-core/src/utils/gr_plot_float.py b/gnuradio-core/src/utils/gr_plot_float.py index e4043a640..473326012 100755 --- a/gnuradio-core/src/utils/gr_plot_float.py +++ b/gnuradio-core/src/utils/gr_plot_float.py @@ -20,50 +20,142 @@ # Boston, MA 02110-1301, USA. # -import pylab, math +import scipy from pylab import * -import struct, sys from optparse import OptionParser -import gr_read_binary +matplotlib.interactive(True) +matplotlib.use('TkAgg') + +class draw_fft_c: + def __init__(self, filename, options): + self.hfile = open(filename, "r") + self.block_length = options.block + self.start = options.start + self.sample_rate = options.sample_rate + + self.axis_font_size = 16 + self.label_font_size = 18 + self.title_font_size = 20 + self.text_size = 22 + + # Setup PLOT + self.fig = figure(1, figsize=(16, 9), facecolor='w') + rcParams['xtick.labelsize'] = self.axis_font_size + rcParams['ytick.labelsize'] = self.axis_font_size + + self.text_file = figtext(0.10, 0.94, ("File: %s" % filename), weight="heavy", size=self.text_size) + self.text_file_pos = figtext(0.10, 0.88, "File Position: ", weight="heavy", size=self.text_size) + self.text_block = figtext(0.40, 0.88, ("Block Size: %d" % self.block_length), + weight="heavy", size=self.text_size) + self.text_sr = figtext(0.60, 0.88, ("Sample Rate: %.2f" % self.sample_rate), + weight="heavy", size=self.text_size) + self.make_plots() + + self.button_left_axes = self.fig.add_axes([0.45, 0.01, 0.05, 0.05], frameon=True) + self.button_left = Button(self.button_left_axes, "<") + self.button_left_callback = self.button_left.on_clicked(self.button_left_click) + + self.button_right_axes = self.fig.add_axes([0.50, 0.01, 0.05, 0.05], frameon=True) + self.button_right = Button(self.button_right_axes, ">") + self.button_right_callback = self.button_right.on_clicked(self.button_right_click) + + self.xlim = self.sp_f.get_xlim() + + self.manager = get_current_fig_manager() + connect('key_press_event', self.click) + show() + + def get_data(self): + self.text_file_pos.set_text("File Position: %d" % (self.hfile.tell()//4)) + f = scipy.fromfile(self.hfile, dtype=scipy.float32, count=self.block_length) + #print "Read in %d items" % len(self.f) + if(len(f) == 0): + print "End of File" + else: + self.f = f + self.time = [i*(1/self.sample_rate) for i in range(len(self.f))] + + def make_plots(self): + # if specified on the command-line, set file pointer + self.hfile.seek(8*self.start, 1) + + self.get_data() + + # Subplot for real and imaginary parts of signal + self.sp_f = self.fig.add_subplot(2,1,1, position=[0.075, 0.2, 0.875, 0.6]) + self.sp_f.set_title(("Amplitude"), fontsize=self.title_font_size, fontweight="bold") + self.sp_f.set_xlabel("Time (s)", fontsize=self.label_font_size, fontweight="bold") + self.sp_f.set_ylabel("Amplitude (V)", fontsize=self.label_font_size, fontweight="bold") + self.plot_f = plot(self.time, self.f, 'bo-') + self.sp_f.set_ylim([1.5*min(self.f), + 1.5*max(self.f)]) + + draw() + + def update_plots(self): + self.plot_f[0].set_data([self.time, self.f]) + self.sp_f.set_ylim([1.5*min(self.f), + 1.5*max(self.f)]) + draw() + + def click(self, event): + forward_valid_keys = [" ", "down", "right"] + backward_valid_keys = ["up", "left"] + + if(find(event.key, forward_valid_keys)): + self.step_forward() + + elif(find(event.key, backward_valid_keys)): + self.step_backward() + + def button_left_click(self, event): + self.step_backward() + + def button_right_click(self, event): + self.step_forward() + + def step_forward(self): + self.get_data() + self.update_plots() + + def step_backward(self): + # Step back in file position + if(self.hfile.tell() >= 8*self.block_length ): + self.hfile.seek(-8*self.block_length, 1) + else: + self.hfile.seek(-self.hfile.tell(),1) + self.get_data() + self.update_plots() + + + +#FIXME: there must be a way to do this with a Python builtin +def find(item_in, list_search): + for l in list_search: + if item_in == l: + return True + return False def main(): - usage="%prog: [options] output_filename" - parser = OptionParser(conflict_handler="resolve", usage=usage) - parser.add_option("-s", "--size", type="int", default=None, - help="Specify the number of points to plot [default=%default]") - parser.add_option("", "--skip", type="int", default=None, - help="Specify the number of points to skip [default=%default]") + usage="%prog: [options] input_filename" + description = "Takes a GNU Radio floating point binary file and displays the samples versus time. You can set the block size to specify how many points to read in at a time and the start position in the file. By default, the system assumes a sample rate of 1, so in time, each sample is plotted versus the sample number. To set a true time axis, set the sample rate (-R or --sample-rate) to the sample rate used when capturing the samples." + parser = OptionParser(conflict_handler="resolve", usage=usage, description=description) + parser.add_option("-B", "--block", type="int", default=1000, + help="Specify the block size [default=%default]") + parser.add_option("-s", "--start", type="int", default=0, + help="Specify where to start in the file [default=%default]") + parser.add_option("-R", "--sample-rate", type="float", default=1.0, + help="Set the sampler rate of the data [default=%default]") + (options, args) = parser.parse_args () if len(args) != 1: parser.print_help() raise SystemExit, 1 filename = args[0] - fl = gr_read_binary.read_float_binary(filename) - - if(options.skip is None): - options.skip = 0 - - if((options.size is None) or ((options.skip+options.size) > len(iq[0]))): - options.size = len(fl) - options.skip - - x = range(options.skip, options.skip + options.size) - - # PLOT REAL AND IMAGINARY PARTS - - f = figure(1, figsize=(16, 12), facecolor='w') - rcParams['xtick.labelsize'] = 16 - rcParams['ytick.labelsize'] = 16 - - sp1 = f.add_subplot(1,1,1) - sp1.set_title(("GNU Radio Float"), fontsize=26, fontweight="bold") - sp1.set_xlabel("Time (s)", fontsize=20, fontweight="bold") - sp1.set_ylabel("Amplitude (V)", fontsize=20, fontweight="bold") - plot(x, fl, 'bo-') - - show() + dc = draw_fft_c(filename, options) if __name__ == "__main__": try: @@ -71,5 +163,3 @@ if __name__ == "__main__": except KeyboardInterrupt: pass - - diff --git a/gnuradio-core/src/utils/gr_plot_int.py b/gnuradio-core/src/utils/gr_plot_int.py new file mode 100755 index 000000000..ef2c7170a --- /dev/null +++ b/gnuradio-core/src/utils/gr_plot_int.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python +# +# Copyright 2007 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. +# + +import scipy +from pylab import * +from optparse import OptionParser + +matplotlib.interactive(True) +matplotlib.use('TkAgg') + +class draw_fft_c: + def __init__(self, filename, options): + self.hfile = open(filename, "r") + self.block_length = options.block + self.start = options.start + self.sample_rate = options.sample_rate + + self.axis_font_size = 16 + self.label_font_size = 18 + self.title_font_size = 20 + self.text_size = 22 + + # Setup PLOT + self.fig = figure(1, figsize=(16, 9), facecolor='w') + rcParams['xtick.labelsize'] = self.axis_font_size + rcParams['ytick.labelsize'] = self.axis_font_size + + self.text_file = figtext(0.10, 0.94, ("File: %s" % filename), weight="heavy", size=self.text_size) + self.text_file_pos = figtext(0.10, 0.88, "File Position: ", weight="heavy", size=self.text_size) + self.text_block = figtext(0.40, 0.88, ("Block Size: %d" % self.block_length), + weight="heavy", size=self.text_size) + self.text_sr = figtext(0.60, 0.88, ("Sample Rate: %.2f" % self.sample_rate), + weight="heavy", size=self.text_size) + self.make_plots() + + self.button_left_axes = self.fig.add_axes([0.45, 0.01, 0.05, 0.05], frameon=True) + self.button_left = Button(self.button_left_axes, "<") + self.button_left_callback = self.button_left.on_clicked(self.button_left_click) + + self.button_right_axes = self.fig.add_axes([0.50, 0.01, 0.05, 0.05], frameon=True) + self.button_right = Button(self.button_right_axes, ">") + self.button_right_callback = self.button_right.on_clicked(self.button_right_click) + + self.xlim = self.sp_f.get_xlim() + + self.manager = get_current_fig_manager() + connect('key_press_event', self.click) + show() + + def get_data(self): + self.text_file_pos.set_text("File Position: %d" % (self.hfile.tell()//4)) + f = scipy.fromfile(self.hfile, dtype=scipy.int32, count=self.block_length) + #print "Read in %d items" % len(self.f) + if(len(f) == 0): + print "End of File" + else: + self.f = f + self.time = [i*(1/self.sample_rate) for i in range(len(self.f))] + + def make_plots(self): + # if specified on the command-line, set file pointer + self.hfile.seek(8*self.start, 1) + + self.get_data() + + # Subplot for real and imaginary parts of signal + self.sp_f = self.fig.add_subplot(2,1,1, position=[0.075, 0.2, 0.875, 0.6]) + self.sp_f.set_title(("Amplitude"), fontsize=self.title_font_size, fontweight="bold") + self.sp_f.set_xlabel("Time (s)", fontsize=self.label_font_size, fontweight="bold") + self.sp_f.set_ylabel("Amplitude (V)", fontsize=self.label_font_size, fontweight="bold") + self.plot_f = plot(self.time, self.f, 'bo-') + self.sp_f.set_ylim([1.5*min(self.f), + 1.5*max(self.f)]) + + draw() + + def update_plots(self): + self.plot_f[0].set_data([self.time, self.f]) + self.sp_f.set_ylim([1.5*min(self.f), + 1.5*max(self.f)]) + draw() + + def click(self, event): + forward_valid_keys = [" ", "down", "right"] + backward_valid_keys = ["up", "left"] + + if(find(event.key, forward_valid_keys)): + self.step_forward() + + elif(find(event.key, backward_valid_keys)): + self.step_backward() + + def button_left_click(self, event): + self.step_backward() + + def button_right_click(self, event): + self.step_forward() + + def step_forward(self): + self.get_data() + self.update_plots() + + def step_backward(self): + # Step back in file position + if(self.hfile.tell() >= 8*self.block_length ): + self.hfile.seek(-8*self.block_length, 1) + else: + self.hfile.seek(-self.hfile.tell(),1) + self.get_data() + self.update_plots() + + + +#FIXME: there must be a way to do this with a Python builtin +def find(item_in, list_search): + for l in list_search: + if item_in == l: + return True + return False + +def main(): + usage="%prog: [options] input_filename" + description = "Takes a GNU Radio floating point binary file and displays the samples versus time. You can set the block size to specify how many points to read in at a time and the start position in the file. By default, the system assumes a sample rate of 1, so in time, each sample is plotted versus the sample number. To set a true time axis, set the sample rate (-R or --sample-rate) to the sample rate used when capturing the samples." + + parser = OptionParser(conflict_handler="resolve", usage=usage, description=description) + parser.add_option("-B", "--block", type="int", default=1000, + help="Specify the block size [default=%default]") + parser.add_option("-s", "--start", type="int", default=0, + help="Specify where to start in the file [default=%default]") + parser.add_option("-R", "--sample-rate", type="float", default=1.0, + help="Set the sampler rate of the data [default=%default]") + + (options, args) = parser.parse_args () + if len(args) != 1: + parser.print_help() + raise SystemExit, 1 + filename = args[0] + + dc = draw_fft_c(filename, options) + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gnuradio-core/src/utils/gr_plot_iq.py b/gnuradio-core/src/utils/gr_plot_iq.py index db5d99a11..1bb24517e 100755 --- a/gnuradio-core/src/utils/gr_plot_iq.py +++ b/gnuradio-core/src/utils/gr_plot_iq.py @@ -20,52 +20,144 @@ # Boston, MA 02110-1301, USA. # -import pylab, math +import scipy from pylab import * -import struct, sys from optparse import OptionParser -import gr_read_binary +matplotlib.interactive(True) +matplotlib.use('TkAgg') + +class draw_fft: + def __init__(self, filename, options): + self.hfile = open(filename, "r") + self.block_length = options.block + self.start = options.start + self.sample_rate = options.sample_rate + + self.axis_font_size = 16 + self.label_font_size = 18 + self.title_font_size = 20 + self.text_size = 22 + + # Setup PLOT + self.fig = figure(1, figsize=(16, 9), facecolor='w') + rcParams['xtick.labelsize'] = self.axis_font_size + rcParams['ytick.labelsize'] = self.axis_font_size + + self.text_file = figtext(0.10, 0.94, ("File: %s" % filename), weight="heavy", size=self.text_size) + self.text_file_pos = figtext(0.10, 0.88, "File Position: ", weight="heavy", size=self.text_size) + self.text_block = figtext(0.40, 0.88, ("Block Size: %d" % self.block_length), + weight="heavy", size=self.text_size) + self.text_sr = figtext(0.60, 0.88, ("Sample Rate: %.2f" % self.sample_rate), + weight="heavy", size=self.text_size) + self.make_plots() + + self.button_left_axes = self.fig.add_axes([0.45, 0.01, 0.05, 0.05], frameon=True) + self.button_left = Button(self.button_left_axes, "<") + self.button_left_callback = self.button_left.on_clicked(self.button_left_click) + + self.button_right_axes = self.fig.add_axes([0.50, 0.01, 0.05, 0.05], frameon=True) + self.button_right = Button(self.button_right_axes, ">") + self.button_right_callback = self.button_right.on_clicked(self.button_right_click) + + self.xlim = self.sp_iq.get_xlim() + + self.manager = get_current_fig_manager() + connect('key_press_event', self.click) + show() + + def get_data(self): + self.text_file_pos.set_text("File Position: %d" % (self.hfile.tell()//8)) + self.iq = scipy.fromfile(self.hfile, dtype=scipy.complex64, count=self.block_length) + #print "Read in %d items" % len(self.iq) + if(len(self.iq) == 0): + print "End of File" + else: + self.reals = [r.real for r in self.iq] + self.imags = [i.imag for i in self.iq] + self.time = [i*(1/self.sample_rate) for i in range(len(self.reals))] + + def make_plots(self): + # if specified on the command-line, set file pointer + self.hfile.seek(16*self.start, 1) + + self.get_data() + + # Subplot for real and imaginary parts of signal + self.sp_iq = self.fig.add_subplot(2,1,1, position=[0.075, 0.14, 0.85, 0.67]) + self.sp_iq.set_title(("I&Q"), fontsize=self.title_font_size, fontweight="bold") + self.sp_iq.set_xlabel("Time (s)", fontsize=self.label_font_size, fontweight="bold") + self.sp_iq.set_ylabel("Amplitude (V)", fontsize=self.label_font_size, fontweight="bold") + self.plot_iq = plot(self.time, self.reals, 'bo-', self.time, self.imags, 'ro-') + self.sp_iq.set_ylim([1.5*min([min(self.reals), min(self.imags)]), + 1.5*max([max(self.reals), max(self.imags)])]) + + draw() + + def update_plots(self): + self.plot_iq[0].set_data([self.time, self.reals]) + self.plot_iq[1].set_data([self.time, self.imags]) + self.sp_iq.set_ylim([1.5*min([min(self.reals), min(self.imags)]), + 1.5*max([max(self.reals), max(self.imags)])]) + draw() + + def click(self, event): + forward_valid_keys = [" ", "down", "right"] + backward_valid_keys = ["up", "left"] + + if(find(event.key, forward_valid_keys)): + self.step_forward() + + elif(find(event.key, backward_valid_keys)): + self.step_backward() + + def button_left_click(self, event): + self.step_backward() + + def button_right_click(self, event): + self.step_forward() + + def step_forward(self): + self.get_data() + self.update_plots() + + def step_backward(self): + # Step back in file position + if(self.hfile.tell() >= 16*self.block_length ): + self.hfile.seek(-16*self.block_length, 1) + else: + self.hfile.seek(-self.hfile.tell(),1) + self.get_data() + self.update_plots() + + + +#FIXME: there must be a way to do this with a Python builtin +def find(item_in, list_search): + for l in list_search: + if item_in == l: + return True + return False def main(): - usage="%prog: [options] output_filename" - parser = OptionParser(conflict_handler="resolve", usage=usage) - parser.add_option("-s", "--size", type="int", default=None, - help="Specify the number of points to plot [default=%default]") - parser.add_option("", "--skip", type="int", default=None, - help="Specify the number of points to skip [default=%default]") + usage="%prog: [options] input_filename" + description = "Takes a GNU Radio complex binary file and displays the I&Q data versus time. You can set the block size to specify how many points to read in at a time and the start position in the file. By default, the system assumes a sample rate of 1, so in time, each sample is plotted versus the sample number. To set a true time axis, set the sample rate (-R or --sample-rate) to the sample rate used when capturing the samples." + parser = OptionParser(conflict_handler="resolve", usage=usage, description=description) + parser.add_option("-B", "--block", type="int", default=1000, + help="Specify the block size [default=%default]") + parser.add_option("-s", "--start", type="int", default=0, + help="Specify where to start in the file [default=%default]") + parser.add_option("-R", "--sample-rate", type="float", default=1.0, + help="Set the sampler rate of the data [default=%default]") + (options, args) = parser.parse_args () if len(args) != 1: parser.print_help() raise SystemExit, 1 filename = args[0] - iq = gr_read_binary.read_complex_binary(filename) - - if(options.skip is None): - options.skip = 0 - - if((options.size is None) or ((options.skip+options.size) > len(iq[0]))): - options.size = len(iq[0]) - options.skip - - reals = iq[0][options.skip : options.skip + options.size] - imags = iq[1][options.skip : options.skip + options.size] - x = range(options.skip, options.skip + options.size) - - # PLOT REAL AND IMAGINARY PARTS - - f = figure(1, figsize=(16, 12), facecolor='w') - rcParams['xtick.labelsize'] = 16 - rcParams['ytick.labelsize'] = 16 - - sp1 = f.add_subplot(1,1,1) - sp1.set_title(("I&Q"), fontsize=26, fontweight="bold") - sp1.set_xlabel("Time (s)", fontsize=20, fontweight="bold") - sp1.set_ylabel("Amplitude (V)", fontsize=20, fontweight="bold") - plot(x, reals, 'bo-', x, imags, 'ro-') - - show() + dc = draw_fft(filename, options) if __name__ == "__main__": try: diff --git a/gnuradio-core/src/utils/gr_plot_short.py b/gnuradio-core/src/utils/gr_plot_short.py new file mode 100755 index 000000000..0bb091def --- /dev/null +++ b/gnuradio-core/src/utils/gr_plot_short.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python +# +# Copyright 2007 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. +# + +import scipy +from pylab import * +from optparse import OptionParser + +matplotlib.interactive(True) +matplotlib.use('TkAgg') + +class draw_fft_c: + def __init__(self, filename, options): + self.hfile = open(filename, "r") + self.block_length = options.block + self.start = options.start + self.sample_rate = options.sample_rate + + self.axis_font_size = 16 + self.label_font_size = 18 + self.title_font_size = 20 + self.text_size = 22 + + # Setup PLOT + self.fig = figure(1, figsize=(16, 9), facecolor='w') + rcParams['xtick.labelsize'] = self.axis_font_size + rcParams['ytick.labelsize'] = self.axis_font_size + + self.text_file = figtext(0.10, 0.94, ("File: %s" % filename), weight="heavy", size=self.text_size) + self.text_file_pos = figtext(0.10, 0.88, "File Position: ", weight="heavy", size=self.text_size) + self.text_block = figtext(0.40, 0.88, ("Block Size: %d" % self.block_length), + weight="heavy", size=self.text_size) + self.text_sr = figtext(0.60, 0.88, ("Sample Rate: %.2f" % self.sample_rate), + weight="heavy", size=self.text_size) + self.make_plots() + + self.button_left_axes = self.fig.add_axes([0.45, 0.01, 0.05, 0.05], frameon=True) + self.button_left = Button(self.button_left_axes, "<") + self.button_left_callback = self.button_left.on_clicked(self.button_left_click) + + self.button_right_axes = self.fig.add_axes([0.50, 0.01, 0.05, 0.05], frameon=True) + self.button_right = Button(self.button_right_axes, ">") + self.button_right_callback = self.button_right.on_clicked(self.button_right_click) + + self.xlim = self.sp_f.get_xlim() + + self.manager = get_current_fig_manager() + connect('key_press_event', self.click) + show() + + def get_data(self): + self.text_file_pos.set_text("File Position: %d" % (self.hfile.tell()//2)) + f = scipy.fromfile(self.hfile, dtype=scipy.int16, count=self.block_length) + #print "Read in %d items" % len(self.f) + if(len(f) == 0): + print "End of File" + else: + self.f = f + self.time = [i*(1/self.sample_rate) for i in range(len(self.f))] + + def make_plots(self): + # if specified on the command-line, set file pointer + self.hfile.seek(8*self.start, 1) + + self.get_data() + + # Subplot for real and imaginary parts of signal + self.sp_f = self.fig.add_subplot(2,1,1, position=[0.075, 0.2, 0.875, 0.6]) + self.sp_f.set_title(("Amplitude"), fontsize=self.title_font_size, fontweight="bold") + self.sp_f.set_xlabel("Time (s)", fontsize=self.label_font_size, fontweight="bold") + self.sp_f.set_ylabel("Amplitude (V)", fontsize=self.label_font_size, fontweight="bold") + self.plot_f = plot(self.time, self.f, 'bo-') + self.sp_f.set_ylim([1.5*min(self.f), + 1.5*max(self.f)]) + + draw() + + def update_plots(self): + self.plot_f[0].set_data([self.time, self.f]) + self.sp_f.set_ylim([1.5*min(self.f), + 1.5*max(self.f)]) + draw() + + def click(self, event): + forward_valid_keys = [" ", "down", "right"] + backward_valid_keys = ["up", "left"] + + if(find(event.key, forward_valid_keys)): + self.step_forward() + + elif(find(event.key, backward_valid_keys)): + self.step_backward() + + def button_left_click(self, event): + self.step_backward() + + def button_right_click(self, event): + self.step_forward() + + def step_forward(self): + self.get_data() + self.update_plots() + + def step_backward(self): + # Step back in file position + if(self.hfile.tell() >= 4*self.block_length ): + self.hfile.seek(-4*self.block_length, 1) + else: + self.hfile.seek(-self.hfile.tell(),1) + self.get_data() + self.update_plots() + + + +#FIXME: there must be a way to do this with a Python builtin +def find(item_in, list_search): + for l in list_search: + if item_in == l: + return True + return False + +def main(): + usage="%prog: [options] input_filename" + description = "Takes a GNU Radio floating point binary file and displays the samples versus time. You can set the block size to specify how many points to read in at a time and the start position in the file. By default, the system assumes a sample rate of 1, so in time, each sample is plotted versus the sample number. To set a true time axis, set the sample rate (-R or --sample-rate) to the sample rate used when capturing the samples." + + parser = OptionParser(conflict_handler="resolve", usage=usage, description=description) + parser.add_option("-B", "--block", type="int", default=1000, + help="Specify the block size [default=%default]") + parser.add_option("-s", "--start", type="int", default=0, + help="Specify where to start in the file [default=%default]") + parser.add_option("-R", "--sample-rate", type="float", default=1.0, + help="Set the sampler rate of the data [default=%default]") + + (options, args) = parser.parse_args () + if len(args) != 1: + parser.print_help() + raise SystemExit, 1 + filename = args[0] + + dc = draw_fft_c(filename, options) + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gnuradio-core/src/utils/gr_read_binary.py b/gnuradio-core/src/utils/gr_read_binary.py deleted file mode 100644 index ae91b4480..000000000 --- a/gnuradio-core/src/utils/gr_read_binary.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2007 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. -# - -import struct - -def read_binary(filename, type): - n = struct.calcsize(type) - f = open(filename, 'rb') - out = list() - lin = f.read(n) - while(len(lin) == n): - cp = struct.unpack(type, lin) - out.append(cp) - lin = f.read(n) - return out - -def read_char_binary(filename): - return read_binary(filename, 'c') - -def read_float_binary(filename): - return read_binary(filename, 'f') - -def read_int_binary(filename): - return read_binary(filename, 'i') - -def read_short_binary(filename): - return read_binary(filename, 'h') - -def read_complex_binary(filename): - n = struct.calcsize('ff') - f = open(filename, 'rb') - re = list() - im = list() - lin = f.read(n) - while(len(lin) == n): - cp = struct.unpack('ff', lin) - re.append(cp[0]) - im.append(cp[1]) - lin = f.read(n) - return (re, im) - - diff --git a/gnuradio-examples/python/ofdm/benchmark_ofdm.py b/gnuradio-examples/python/ofdm/benchmark_ofdm.py index 690c57d60..5861da16c 100755 --- a/gnuradio-examples/python/ofdm/benchmark_ofdm.py +++ b/gnuradio-examples/python/ofdm/benchmark_ofdm.py @@ -20,7 +20,7 @@ # Boston, MA 02110-1301, USA. # -from gnuradio import gr, blks +from gnuradio import gr, blks2 from gnuradio import eng_notation from gnuradio.eng_option import eng_option from optparse import OptionParser @@ -32,9 +32,9 @@ from transmit_path import transmit_path from receive_path import receive_path -class my_graph(gr.flow_graph): +class my_top_block(gr.top_block): def __init__(self, callback, options): - gr.flow_graph.__init__(self) + gr.top_block.__init__(self) if not options.channel_off: SNR = 10.0**(options.snr/10.0) @@ -67,22 +67,24 @@ class my_graph(gr.flow_graph): z = [0,] self.zeros = gr.vector_source_c(z, True) - self.txpath = transmit_path(self, options) + self.txpath = transmit_path(options) - self.mux = gr.stream_mux(gr.sizeof_gr_complex, stream_size) + #self.mux = gr.stream_mux(gr.sizeof_gr_complex, stream_size) self.throttle = gr.throttle(gr.sizeof_gr_complex, options.sample_rate) - self.channel = blks.channel_model(self, noise_voltage, frequency_offset, - options.clockrate_ratio, taps) - self.rxpath = receive_path(self, callback, options) + self.channel = blks2.channel_model(noise_voltage, frequency_offset, + options.clockrate_ratio, taps) + self.rxpath = receive_path(callback, options) - self.connect(self.zeros, (self.mux,0)) - self.connect(self.txpath, (self.mux,1)) - self.connect(self.mux, self.throttle, self.channel, self.rxpath) - + #self.connect(self.zeros, (self.mux,0)) + #self.connect(self.txpath, (self.mux,1)) + #self.connect(self.mux, self.throttle, self.channel, self.rxpath) + #self.connect(self.mux, self.throttle, self.rxpath) + self.connect(self.txpath, self.throttle, self.channel, self.rxpath) + if options.log: self.connect(self.txpath, gr.file_sink(gr.sizeof_gr_complex, "txpath.dat")) - self.connect(self.mux, gr.file_sink(gr.sizeof_gr_complex, "mux.dat")) - self.connect(self.channel, gr.file_sink(gr.sizeof_gr_complex, "channel.dat")) + #self.connect(self.mux, gr.file_sink(gr.sizeof_gr_complex, "mux.dat")) + #self.connect(self.channel, gr.file_sink(gr.sizeof_gr_complex, "channel.dat")) # ///////////////////////////////////////////////////////////////////////////// # main @@ -95,7 +97,7 @@ def main(): n_right = 0 def send_pkt(payload='', eof=False): - return fg.txpath.send_pkt(payload, eof) + return tb.txpath.send_pkt(payload, eof) def rx_callback(ok, payload): global n_rcvd, n_right @@ -139,19 +141,19 @@ def main(): transmit_path.add_options(parser, expert_grp) receive_path.add_options(parser, expert_grp) - blks.ofdm_mod.add_options(parser, expert_grp) - blks.ofdm_demod.add_options(parser, expert_grp) + blks2.ofdm_mod.add_options(parser, expert_grp) + blks2.ofdm_demod.add_options(parser, expert_grp) (options, args) = parser.parse_args () # build the graph - fg = my_graph(rx_callback, options) + tb = my_top_block(rx_callback, options) r = gr.enable_realtime_scheduling() # if r != gr.RT_OK: # print "Warning: failed to enable realtime scheduling" - - fg.start() # start flow graph + + tb.start() # start flow graph # generate and send packets nbytes = int(1e6 * options.megabytes) @@ -173,7 +175,7 @@ def main(): pktno += 1 send_pkt(eof=True) - fg.wait() # wait for it to finish + tb.wait() # wait for it to finish if __name__ == '__main__': diff --git a/gnuradio-examples/python/ofdm/benchmark_ofdm_rx.py b/gnuradio-examples/python/ofdm/benchmark_ofdm_rx.py index a8c20d390..cb9649a6c 100755 --- a/gnuradio-examples/python/ofdm/benchmark_ofdm_rx.py +++ b/gnuradio-examples/python/ofdm/benchmark_ofdm_rx.py @@ -20,21 +20,21 @@ # Boston, MA 02110-1301, USA. # -from gnuradio import gr, blks +from gnuradio import gr, blks2 from gnuradio import usrp from gnuradio import eng_notation from gnuradio.eng_option import eng_option from optparse import OptionParser -import random, time, struct, sys +import struct, sys # from current dir from receive_path import receive_path import fusb_options -class usrp_graph(gr.flow_graph): +class my_top_block(gr.top_block): def __init__(self, callback, options): - gr.flow_graph.__init__(self) + gr.top_block.__init__(self) self._rx_freq = options.rx_freq # receiver's center frequency self._rx_gain = options.rx_gain # receiver's gain @@ -61,10 +61,10 @@ class usrp_graph(gr.flow_graph): self.set_auto_tr(True) # enable Auto Transmit/Receive switching # Set up receive path - self.rxpath = receive_path(self, callback, options) + self.rxpath = receive_path(callback, options) self.connect(self.u, self.rxpath) - + def _setup_usrp_source(self): self.u = usrp.source_c (fusb_block_size=self._fusb_block_size, fusb_nblocks=self._fusb_nblocks) @@ -187,23 +187,23 @@ def main(): parser.add_option("","--discontinuous", action="store_true", default=False, help="enable discontinuous") - usrp_graph.add_options(parser, expert_grp) + my_top_block.add_options(parser, expert_grp) receive_path.add_options(parser, expert_grp) - blks.ofdm_mod.add_options(parser, expert_grp) - blks.ofdm_demod.add_options(parser, expert_grp) + blks2.ofdm_mod.add_options(parser, expert_grp) + blks2.ofdm_demod.add_options(parser, expert_grp) fusb_options.add_options(expert_grp) (options, args) = parser.parse_args () # build the graph - fg = usrp_graph(rx_callback, options) + tb = my_top_block(rx_callback, options) r = gr.enable_realtime_scheduling() if r != gr.RT_OK: print "Warning: failed to enable realtime scheduling" - fg.start() # start flow graph - fg.wait() # wait for it to finish + tb.start() # start flow graph + tb.wait() # wait for it to finish if __name__ == '__main__': try: diff --git a/gnuradio-examples/python/ofdm/benchmark_ofdm_tx.py b/gnuradio-examples/python/ofdm/benchmark_ofdm_tx.py index a9d1e00e6..fb41ab129 100755 --- a/gnuradio-examples/python/ofdm/benchmark_ofdm_tx.py +++ b/gnuradio-examples/python/ofdm/benchmark_ofdm_tx.py @@ -20,7 +20,7 @@ # Boston, MA 02110-1301, USA. # -from gnuradio import gr, blks +from gnuradio import gr, blks2 from gnuradio import usrp from gnuradio import eng_notation from gnuradio.eng_option import eng_option @@ -33,9 +33,9 @@ from transmit_path import transmit_path from pick_bitrate import pick_tx_bitrate import fusb_options -class usrp_graph(gr.flow_graph): +class my_top_block(gr.top_block): def __init__(self, options): - gr.flow_graph.__init__(self) + gr.top_block.__init__(self) self._tx_freq = options.tx_freq # tranmitter's center frequency self._tx_subdev_spec = options.tx_subdev_spec # daughterboard to use @@ -53,10 +53,10 @@ class usrp_graph(gr.flow_graph): # copy the final answers back into options for use by modulator #options.bitrate = self._bitrate - self.txpath = transmit_path(self, options) + self.txpath = transmit_path(options) self.connect(self.txpath, self.u) - + def _setup_usrp_sink(self): """ Creates a USRP sink, determines the settings for best bitrate, @@ -167,7 +167,7 @@ def add_freq_option(parser): def main(): def send_pkt(payload='', eof=False): - return fg.txpath.send_pkt(payload, eof) + return tb.txpath.send_pkt(payload, eof) parser = OptionParser(option_class=eng_option, conflict_handler="resolve") expert_grp = parser.add_option_group("Expert") @@ -178,23 +178,23 @@ def main(): parser.add_option("","--discontinuous", action="store_true", default=False, help="enable discontinuous mode") - usrp_graph.add_options(parser, expert_grp) + my_top_block.add_options(parser, expert_grp) transmit_path.add_options(parser, expert_grp) - blks.ofdm_mod.add_options(parser, expert_grp) - blks.ofdm_demod.add_options(parser, expert_grp) + blks2.ofdm_mod.add_options(parser, expert_grp) + blks2.ofdm_demod.add_options(parser, expert_grp) fusb_options.add_options(expert_grp) (options, args) = parser.parse_args () # build the graph - fg = usrp_graph(options) - + tb = my_top_block(options) + r = gr.enable_realtime_scheduling() if r != gr.RT_OK: print "Warning: failed to enable realtime scheduling" - fg.start() # start flow graph - + tb.start() # start flow graph + # generate and send packets nbytes = int(1e6 * options.megabytes) n = 0 @@ -210,7 +210,7 @@ def main(): pktno += 1 send_pkt(eof=True) - fg.wait() # wait for it to finish + tb.wait() # wait for it to finish if __name__ == '__main__': try: diff --git a/gnuradio-examples/python/ofdm/gr_plot_ofdm.py b/gnuradio-examples/python/ofdm/gr_plot_ofdm.py new file mode 100755 index 000000000..0bca41037 --- /dev/null +++ b/gnuradio-examples/python/ofdm/gr_plot_ofdm.py @@ -0,0 +1,268 @@ +#!/usr/bin/env python +# +# Copyright 2007 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. +# + +import scipy, pylab, math +import struct, sys +from pylab import * +from matplotlib.font_manager import fontManager, FontProperties +from optparse import OptionParser +from scipy import fftpack +from math import log10 + +matplotlib.interactive(True) +matplotlib.use('TkAgg') + +class draw_constellation: + def __init__(self, options): + derot_file = "ofdm_frame_sink_c.dat" + acq_file = "ofdm_frame_acq_c.dat" + fft_file = "fft_out_c.dat" + + self.h_derot_file = open(derot_file, "r") + self.h_acq_file = open(acq_file, "r") + self.h_fft_file = open(fft_file, "r") + + self.occ_tones = options.occ_tones + self.fft_size = options.fft_size + self.symbol = options.start + self.sample_rate = options.sample_rate + + self.axis_font_size = 16 + self.label_font_size = 18 + self.title_font_size = 20 + self.text_size = 22 + + # Setup PLOT + self.fig = figure(1, figsize=(14, 9), facecolor='w') + rcParams['xtick.labelsize'] = self.axis_font_size + rcParams['ytick.labelsize'] = self.axis_font_size + + self.text_sym = figtext(0.05, 0.95, ("Symbol: %s" % self.symbol), weight="heavy", size=self.text_size) + + self.make_plots() + + self.button_left_axes = self.fig.add_axes([0.45, 0.01, 0.05, 0.05], frameon=True) + self.button_left = Button(self.button_left_axes, "<") + self.button_left_callback = self.button_left.on_clicked(self.button_left_click) + + self.button_right_axes = self.fig.add_axes([0.50, 0.01, 0.05, 0.05], frameon=True) + self.button_right = Button(self.button_right_axes, ">") + self.button_right_callback = self.button_right.on_clicked(self.button_right_click) + + self.xlim = self.sp_eq.get_xlim() + + self.manager = get_current_fig_manager() + #connect('draw_event', self.zoom) + connect('key_press_event', self.click) + show() + + def get_data(self): + self.text_sym.set_text("Symbol: %d" % (self.symbol)) + + derot_data = scipy.fromfile(self.h_derot_file, dtype=scipy.complex64, count=self.occ_tones) + acq_data = scipy.fromfile(self.h_acq_file, dtype=scipy.complex64, count=self.occ_tones) + fft_data = scipy.fromfile(self.h_fft_file, dtype=scipy.complex64, count=self.fft_size) + if(len(acq_data) == 0): + print "End of File" + else: + self.acq_data_reals = [r.real for r in acq_data] + self.acq_data_imags = [i.imag for i in acq_data] + self.derot_data_reals = [r.real for r in derot_data] + self.derot_data_imags = [i.imag for i in derot_data] + + self.unequalized_angle = [math.atan2(x.imag, x.real) for x in fft_data] + self.equalized_angle = [math.atan2(x.imag, x.real) for x in acq_data] + self.derot_equalized_angle = [math.atan2(x.imag, x.real) for x in derot_data] + + self.time = [i*(1/self.sample_rate) for i in range(len(acq_data))] + ffttime = [i*(1/self.sample_rate) for i in range(len(fft_data))] + + self.freq = self.get_freq(ffttime, self.sample_rate) + + for i in range(len(fft_data)): + if(abs(fft_data[i]) == 0.0): + fft_data[i] = complex(1e-6,1e-6) + self.fft_data = [20*log10(abs(f)) for f in fft_data] + + def get_freq(self, time, sample_rate, T=1): + N = len(time) + Fs = 1.0 / (max(time) - min(time)) + Fn = 0.5 * sample_rate + freq = [-Fn + i*Fs for i in range(N)] + return freq + + def make_plots(self): + self.h_acq_file.seek(8*self.symbol*self.occ_tones, 0) + self.h_fft_file.seek(8*self.symbol*self.fft_size, 0) + self.h_derot_file.seek(8*self.symbol*self.occ_tones, 0) + + self.get_data() + + # Subplot: constellation of rotated symbols + self.sp_const = self.fig.add_subplot(4,1,1, position=[0.15, 0.55, 0.3, 0.35]) + self.sp_const.set_title(("Constellation"), fontsize=self.title_font_size, fontweight="bold") + self.sp_const.set_xlabel("Inphase", fontsize=self.label_font_size, fontweight="bold") + self.sp_const.set_ylabel("Qaudrature", fontsize=self.label_font_size, fontweight="bold") + self.plot_const = plot(self.acq_data_reals, self.acq_data_imags, 'bo') + self.plot_const += plot(self.derot_data_reals, self.derot_data_imags, 'ro') + self.sp_const.axis([-2, 2, -2, 2]) + + # Subplot: unequalized angle + self.sp_uneq = self.fig.add_subplot(4,2,1, position=[0.575, 0.55, 0.3, 0.35]) + self.sp_uneq.set_title(("Unequalized Angle"), fontsize=self.title_font_size, fontweight="bold") + self.sp_uneq.set_xlabel("Time (s)", fontsize=self.label_font_size, fontweight="bold") + self.sp_uneq.set_ylabel("Angle", fontsize=self.label_font_size, fontweight="bold") + uneqscale = range(len(self.unequalized_angle)) + self.plot_uneq = plot(uneqscale, self.unequalized_angle, 'bo') + + # Subplot: equalized angle + self.sp_eq = self.fig.add_subplot(4,1,2, position=[0.15, 0.1, 0.3, 0.35]) + self.sp_eq.set_title(("Equalized Angle"), fontsize=self.title_font_size, fontweight="bold") + self.sp_eq.set_xlabel("Time (s)", fontsize=self.label_font_size, fontweight="bold") + self.sp_eq.set_ylabel("Angle", fontsize=self.label_font_size, fontweight="bold") + eqscale = range(len(self.equalized_angle)) + self.plot_eq = plot(eqscale, self.equalized_angle, 'bo') + self.plot_eq += plot(eqscale, self.derot_equalized_angle, 'ro', markersize=4) + + # Subplot: FFT + self.sp_fft = self.fig.add_subplot(4,2,2, position=[0.575, 0.1, 0.3, 0.35]) + self.sp_fft.set_title(("FFT"), fontsize=self.title_font_size, fontweight="bold") + self.sp_fft.set_xlabel("Frequency (MHz)", fontsize=self.label_font_size, fontweight="bold") + self.sp_fft.set_ylabel("Power (dBm)", fontsize=self.label_font_size, fontweight="bold") + self.plot_fft = plot(self.freq, self.fft_data, '-bo') + + draw() + + def update_plots(self): + eqscale = range(len(self.equalized_angle)) + uneqscale = range(len(self.unequalized_angle)) + self.plot_eq[0].set_data([eqscale, self.equalized_angle]) + self.plot_eq[1].set_data([eqscale, self.derot_equalized_angle]) + self.plot_uneq[0].set_data([uneqscale, self.unequalized_angle]) + self.sp_eq.set_ylim([-4, 4]) + self.sp_uneq.set_ylim([-4, 4]) + + #self.sp_iq.axis([min(self.time), max(self.time), + # 1.5*min([min(self.acq_data_reals), min(self.acq_data_imags)]), + # 1.5*max([max(self.acq_data_reals), max(self.acq_data_imags)])]) + + self.plot_const[0].set_data([self.acq_data_reals, self.acq_data_imags]) + self.plot_const[1].set_data([self.derot_data_reals, self.derot_data_imags]) + self.sp_const.axis([-2, 2, -2, 2]) + + self.plot_fft[0].set_data([self.freq, self.fft_data]) + + draw() + + def zoom(self, event): + newxlim = self.sp_eq.get_xlim() + if(newxlim != self.xlim): + self.xlim = newxlim + r = self.reals[int(ceil(self.xlim[0])) : int(ceil(self.xlim[1]))] + i = self.imags[int(ceil(self.xlim[0])) : int(ceil(self.xlim[1]))] + + self.plot_const[0].set_data(r, i) + self.sp_const.axis([-2, 2, -2, 2]) + self.manager.canvas.draw() + draw() + + def click(self, event): + forward_valid_keys = [" ", "down", "right"] + backward_valid_keys = ["up", "left"] + + if(find(event.key, forward_valid_keys)): + self.step_forward() + + elif(find(event.key, backward_valid_keys)): + self.step_backward() + + def button_left_click(self, event): + self.step_backward() + + def button_right_click(self, event): + self.step_forward() + + def step_forward(self): + self.symbol += 1 + self.get_data() + self.update_plots() + + def step_backward(self): + # Step back in file position + self.symbol -= 1 + if(self.h_acq_file.tell() >= 16*self.occ_tones): + self.h_acq_file.seek(-16*self.occ_tones, 1) + else: + self.symbol = 0 + self.h_acq_file.seek(-self.h_acq_file.tell(),1) + + + if(self.h_derot_file.tell() >= 16*self.occ_tones): + self.h_derot_file.seek(-16*self.occ_tones, 1) + else: + self.symbol = 0 + self.h_derot_file.seek(-self.h_derot_file.tell(),1) + + + if(self.h_fft_file.tell() >= 16*self.fft_size): + self.h_fft_file.seek(-16*self.fft_size, 1) + else: + self.symbol = 0 + self.h_fft_file.seek(-self.h_fft_file.tell(),1) + + self.get_data() + self.update_plots() + + + +#FIXME: there must be a way to do this with a Python builtin +def find(item_in, list_search): + for l in list_search: + if item_in == l: + return True + return False + +def main(): + usage="%prog: [options]" + + parser = OptionParser(conflict_handler="resolve", usage=usage) + parser.add_option("", "--fft-size", type="int", default=512, + help="Specify the size of the FFT [default=%default]") + parser.add_option("", "--occ-tones", type="int", default=200, + help="Specify the number of occupied tones [default=%default]") + parser.add_option("-s", "--start", type="int", default=0, + help="Specify the starting symbol to plot [default=%default]") + parser.add_option("-R", "--sample-rate", type="float", default=1.0, + help="Set the sampler rate of the data [default=%default]") + + (options, args) = parser.parse_args () + + dc = draw_constellation(options) + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + + + diff --git a/gnuradio-examples/python/ofdm/plot_ofdm.m b/gnuradio-examples/python/ofdm/plot_ofdm.m index 87eae3a11..2a649b5f5 100755 --- a/gnuradio-examples/python/ofdm/plot_ofdm.m +++ b/gnuradio-examples/python/ofdm/plot_ofdm.m @@ -1,13 +1,22 @@ function plot_ofdm(fft_size, occ_tones) -ofdm = read_complex_binary('ofdm_corr_out_c.dat'); +ofdm = read_complex_binary('ofdm_frame_acq_c.dat'); ofdm_split = split_vect(ofdm, occ_tones); +ofdm_derot = read_complex_binary('ofdm_frame_sink_c.dat'); +ofdm_derot_split = split_vect(ofdm_derot, occ_tones); + fftc = read_complex_binary('fft_out_c.dat'); fftc_split = split_vect(fftc, fft_size); +size(ofdm_split) +size(ofdm_derot_split) +disp "DEROTATED SPLIT" +ofdm_derot(1:100) + + figure(1) -set(gcf, 'Position', [50 50 1000 600]); +#set(gcf, 'Position', [50 50 1000 600]); a = size(ofdm_split); if nargin == 3 @@ -19,47 +28,47 @@ else maxcount = a(1); end -for i = 1:20000 +for i = 1:size(ofdm_split)[0] x = ofdm_split(i,:); y = fftc_split(i+1,:); - subplot(2,2,1) - plot(real(x), imag(x), 'bo') - set(gca, 'FontSize', 30, 'FontWeight', 'Bold'); + subplot(2,2,1); + plot(real(x), imag(x), 'bo'); + #set(gca, 'FontSize', 30, 'FontWeight', 'Bold'); axis([-1.5, 1.5, -1.5, 1.5]) - title('I&Q Constellation', 'FontSize', 36); - xlabel('Inphase', 'FontSize', 32); - ylabel('Quadrature', 'FontSize', 32); + #title('I&Q Constellation', 'FontSize', 36); + #xlabel('Inphase', 'FontSize', 32); + #ylabel('Quadrature', 'FontSize', 32); - subplot(2,2,3) - plot(angle(x*j), 'bo') - set(gca, 'FontSize', 30, 'FontWeight', 'Bold'); + subplot(2,2,3); + plot(angle(x*j), 'bo'); + #set(gca, 'FontSize', 30, 'FontWeight', 'Bold'); axis([0, occ_tones, -3.5, 3.5]) - title('Equalized Symbol Angle', 'FontSize', 36); - xlabel('Bin Number (Occ. Tones)', 'FontSize', 32); - ylabel('Symbol Angle', 'FontSize', 32); + #title('Equalized Symbol Angle', 'FontSize', 36); + #xlabel('Bin Number (Occ. Tones)', 'FontSize', 32); + #ylabel('Symbol Angle', 'FontSize', 32); - subplot(2,2,2) - plot(angle(y*j), 'bo') - set(gca, 'FontSize', 30, 'FontWeight', 'Bold'); + subplot(2,2,2); + plot(angle(y*j), 'bo'); + #set(gca, 'FontSize', 30, 'FontWeight', 'Bold'); axis([0, fft_size, -3.5, 3.5]) - title('Unequalized Symbol Angle', 'FontSize', 36); - xlabel('Bin Number (FFT Size)', 'FontSize', 32); - ylabel('Symbol Angle', 'FontSize', 32); + #title('Unequalized Symbol Angle', 'FontSize', 36); + #xlabel('Bin Number (FFT Size)', 'FontSize', 32); + #ylabel('Symbol Angle', 'FontSize', 32); Y = 20*log10(abs(y) ./ max(abs(y))); - subplot(2,2,4) - plot(Y, 'b-') - set(gca, 'FontSize', 30, 'FontWeight', 'Bold'); + subplot(2,2,4); + plot(Y, 'b-'); + #set(gca, 'FontSize', 30, 'FontWeight', 'Bold'); axis([0, fft_size, -50, 1]); - title('Frequency Domain of Unequalized Rx', 'FontSize', 36); - xlabel('Bin Number (FFT Size)', 'FontSize', 32); - ylabel('Power (dB)', 'FontSize', 32); + #title('Frequency Domain of Unequalized Rx', 'FontSize', 36); + #xlabel('Bin Number (FFT Size)', 'FontSize', 32); + #ylabel('Power (dB)', 'FontSize', 32); - % N = 20*log10(var(abs(x)-1)) + #N = 20*log10(var(abs(x)-1)); disp(sprintf('Symbol Number: %d\n', i)) -% disp(sprintf('\tFreq Error: %f\n', anglesh_pn(1+(i-1)*fft_size))) + #disp(sprintf('\tFreq Error: %f\n', anglesh_pn(1+(i-1)*fft_size))) pause end diff --git a/gnuradio-examples/python/ofdm/receive_path.py b/gnuradio-examples/python/ofdm/receive_path.py index 2ebaad883..b758bc4d0 100644 --- a/gnuradio-examples/python/ofdm/receive_path.py +++ b/gnuradio-examples/python/ofdm/receive_path.py @@ -20,7 +20,7 @@ # Boston, MA 02110-1301, USA. # -from gnuradio import gr, gru, blks +from gnuradio import gr, gru, blks2 from gnuradio import usrp from gnuradio import eng_notation import copy @@ -33,8 +33,13 @@ from pick_bitrate import pick_rx_bitrate # receive path # ///////////////////////////////////////////////////////////////////////////// -class receive_path(gr.hier_block): - def __init__(self, fg, rx_callback, options): +class receive_path(gr.hier_block2): + def __init__(self, rx_callback, options): + + gr.hier_block2.__init__(self, "receive_path", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + gr.io_signature(0, 0, 0)) # Output signature + options = copy.copy(options) # make a copy so we can destructively modify @@ -44,20 +49,20 @@ class receive_path(gr.hier_block): # receiver self.ofdm_rx = \ - blks.ofdm_demod(fg, options, callback=self._rx_callback) + blks2.ofdm_demod(options, callback=self._rx_callback) # Carrier Sensing Blocks alpha = 0.001 thresh = 30 # in dB, will have to adjust self.probe = gr.probe_avg_mag_sqrd_c(thresh,alpha) - fg.connect(self.ofdm_rx.ofdm_recv.chan_filt, self.probe) + + self.connect(self, self.ofdm_rx) + self.connect(self.ofdm_rx, self.probe) # Display some information about the setup if self._verbose: self._print_verbage() - gr.hier_block.__init__(self, fg, self.ofdm_rx, None) - def carrier_sensed(self): """ Return True if we think carrier is present. diff --git a/gnuradio-examples/python/ofdm/transmit_path.py b/gnuradio-examples/python/ofdm/transmit_path.py index f15845b00..44c7331b0 100644 --- a/gnuradio-examples/python/ofdm/transmit_path.py +++ b/gnuradio-examples/python/ofdm/transmit_path.py @@ -19,7 +19,7 @@ # Boston, MA 02110-1301, USA. # -from gnuradio import gr, gru, blks +from gnuradio import gr, gru, blks2 from gnuradio import usrp from gnuradio import eng_notation @@ -30,19 +30,23 @@ import sys # transmit path # ///////////////////////////////////////////////////////////////////////////// -class transmit_path(gr.hier_block): - def __init__(self, fg, options): +class transmit_path(gr.hier_block2): + def __init__(self, options): ''' See below for what options should hold ''' + gr.hier_block2.__init__(self, "transmit_path", + gr.io_signature(0, 0, 0), # Input signature + gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature + options = copy.copy(options) # make a copy so we can destructively modify - self._verbose = options.verbose + self._verbose = options.verbose # turn verbose mode on/off self._tx_amplitude = options.tx_amplitude # digital amplitude sent to USRP self.ofdm_tx = \ - blks.ofdm_mod(fg, options, msgq_limit=4, pad_for_usrp=False) + blks2.ofdm_mod(options, msgq_limit=4, pad_for_usrp=False) self.amp = gr.multiply_const_cc(1) self.set_tx_amplitude(self._tx_amplitude) @@ -52,8 +56,7 @@ class transmit_path(gr.hier_block): self._print_verbage() # Create and setup transmit path flow graph - fg.connect(self.ofdm_tx, self.amp) - gr.hier_block.__init__(self, fg, None, self.amp) + self.connect(self.ofdm_tx, self.amp, self) def set_tx_amplitude(self, ampl): """ |