diff options
Diffstat (limited to 'gnuradio-core')
42 files changed, 3507 insertions, 991 deletions
diff --git a/gnuradio-core/src/lib/filter/gr_pfb_clock_sync_ccf.h b/gnuradio-core/src/lib/filter/gr_pfb_clock_sync_ccf.h index 4e6ef5fc4..fc80d2961 100644 --- a/gnuradio-core/src/lib/filter/gr_pfb_clock_sync_ccf.h +++ b/gnuradio-core/src/lib/filter/gr_pfb_clock_sync_ccf.h @@ -47,7 +47,7 @@ class gr_fir_ccf; * derivative of the filtered signal, which in turn maximizes the SNR and * minimizes ISI. * - * This approach works by setting up two filterbanks; one filterbanke contains the + * This approach works by setting up two filterbanks; one filterbank contains the * signal's pulse shaping matched filter (such as a root raised cosine filter), * where each branch of the filterbank contains a different phase of the filter. * The second filterbank contains the derivatives of the filters in the first @@ -84,8 +84,8 @@ class gr_fir_ccf; * the block constructor, we just ask for "gain," which is d_alpha while d_beta is * equal to (gain^2)/4. * - * The clock sync block needs to know the number of samples per second (sps), because it - * only returns a single point representing the sample. The sps can be any positive real + * The clock sync block needs to know the number of samples per symbol (sps), because it + * only returns a single point representing the symbol. The sps can be any positive real * number and does not need to be an integer. The filter taps must also be specified. The * taps are generated by first conceiving of the prototype filter that would be the signal's * matched filter. Then interpolate this by the number of filters in the filterbank. These @@ -115,7 +115,7 @@ class gr_pfb_clock_sync_ccf : public gr_block private: /*! * Build the polyphase filterbank timing synchronizer. - * \param sps (double) The number of samples per second in the incoming signal + * \param sps (double) The number of samples per symbol in the incoming signal * \param gain (float) The alpha gain of the control loop; beta = (gain^2)/4 by default. * \param taps (vector<int>) The filter taps. * \param filter_size (uint) The number of filters in the filterbank (default = 32). diff --git a/gnuradio-core/src/lib/general/Makefile.am b/gnuradio-core/src/lib/general/Makefile.am index 3ceea7a6d..8f895f364 100644 --- a/gnuradio-core/src/lib/general/Makefile.am +++ b/gnuradio-core/src/lib/general/Makefile.am @@ -56,7 +56,10 @@ libgeneral_la_SOURCES = \ gr_complex_to_xxx.cc \ gr_conjugate_cc.cc \ gr_copy.cc \ + gr_constellation.cc \ gr_constellation_decoder_cb.cc \ + gr_constellation_decoder2_cb.cc \ + gr_constellation_receiver_cb.cc \ gr_correlate_access_code_bb.cc \ gr_costas_loop_cc.cc \ gr_count_bits.cc \ @@ -111,6 +114,7 @@ libgeneral_la_SOURCES = \ gr_ofdm_demapper_vcb.cc \ gr_ofdm_mapper_bcv.cc \ gr_ofdm_frame_sink.cc \ + gr_ofdm_frame_sink2.cc \ gr_ofdm_insert_preamble.cc \ gr_ofdm_sampler.cc \ gr_pa_2x2_phase_combiner.cc \ @@ -212,7 +216,10 @@ grinclude_HEADERS = \ gr_complex_to_interleaved_short.h \ gr_complex_to_xxx.h \ gr_conjugate_cc.h \ + gr_constellation.h \ gr_constellation_decoder_cb.h \ + gr_constellation_decoder2_cb.h \ + gr_constellation_receiver_cb.h \ gr_copy.h \ gr_correlate_access_code_bb.h \ gr_costas_loop_cc.h \ @@ -261,8 +268,8 @@ grinclude_HEADERS = \ gr_log2_const.h \ gr_map_bb.h \ gr_math.h \ + gr_metric_type.h \ gr_misc.h \ - gr_mpsk_receiver_cc.h \ gr_nco.h \ gr_nlog10_ff.h \ gr_nop.h \ @@ -273,6 +280,7 @@ grinclude_HEADERS = \ gr_ofdm_demapper_vcb.h \ gr_ofdm_mapper_bcv.h \ gr_ofdm_frame_sink.h \ + gr_ofdm_frame_sink2.h \ gr_ofdm_insert_preamble.h \ gr_ofdm_sampler.h \ gr_pa_2x2_phase_combiner.h \ @@ -386,7 +394,10 @@ swiginclude_HEADERS = \ gr_complex_to_interleaved_short.i \ gr_complex_to_xxx.i \ gr_conjugate_cc.i \ + gr_constellation.i \ gr_constellation_decoder_cb.i \ + gr_constellation_decoder2_cb.i \ + gr_constellation_receiver_cb.i \ gr_copy.i \ gr_correlate_access_code_bb.i \ gr_costas_loop_cc.i \ @@ -427,7 +438,6 @@ swiginclude_HEADERS = \ gr_lms_dfe_cc.i \ gr_lms_dfe_ff.i \ gr_map_bb.i \ - gr_mpsk_receiver_cc.i \ gr_nlog10_ff.i \ gr_nop.i \ gr_null_sink.i \ @@ -437,6 +447,7 @@ swiginclude_HEADERS = \ gr_ofdm_demapper_vcb.i \ gr_ofdm_mapper_bcv.i \ gr_ofdm_frame_sink.i \ + gr_ofdm_frame_sink2.i \ gr_ofdm_insert_preamble.i \ gr_ofdm_sampler.i \ gr_pa_2x2_phase_combiner.i \ diff --git a/gnuradio-core/src/lib/general/general.i b/gnuradio-core/src/lib/general/general.i index fb9c4c0f2..dddda60af 100644 --- a/gnuradio-core/src/lib/general/general.i +++ b/gnuradio-core/src/lib/general/general.i @@ -69,6 +69,8 @@ #include <gr_fake_channel_coder_pp.h> #include <gr_throttle.h> #include <gr_mpsk_receiver_cc.h> +#include <gr_constellation_receiver_cb.h> +#include <gr_constellation.h> #include <gr_stream_mux.h> #include <gr_stream_to_streams.h> #include <gr_streams_to_stream.h> @@ -97,6 +99,7 @@ #include <gr_ofdm_cyclic_prefixer.h> #include <gr_ofdm_mapper_bcv.h> #include <gr_ofdm_frame_sink.h> +#include <gr_ofdm_frame_sink2.h> #include <gr_ofdm_insert_preamble.h> #include <gr_ofdm_sampler.h> #include <gr_regenerate_bb.h> @@ -111,6 +114,7 @@ #include <gr_correlate_access_code_bb.h> #include <gr_diff_phasor_cc.h> #include <gr_constellation_decoder_cb.h> +#include <gr_constellation_decoder2_cb.h> #include <gr_binary_slicer_fb.h> #include <gr_diff_encoder_bb.h> #include <gr_diff_decoder_bb.h> @@ -194,6 +198,8 @@ %include "gr_fake_channel_coder_pp.i" %include "gr_throttle.i" %include "gr_mpsk_receiver_cc.i" +%include "gr_constellation_receiver_cb.i" +%include "gr_constellation.i" %include "gr_stream_mux.i" %include "gr_stream_to_streams.i" %include "gr_streams_to_stream.i" @@ -222,6 +228,7 @@ %include "gr_ofdm_cyclic_prefixer.i" %include "gr_ofdm_mapper_bcv.i" %include "gr_ofdm_frame_sink.i" +%include "gr_ofdm_frame_sink2.i" %include "gr_ofdm_insert_preamble.i" %include "gr_ofdm_sampler.i" %include "gr_regenerate_bb.i" @@ -236,6 +243,7 @@ %include "gr_correlate_access_code_bb.i" %include "gr_diff_phasor_cc.i" %include "gr_constellation_decoder_cb.i" +%include "gr_constellation_decoder2_cb.i" %include "gr_binary_slicer_fb.i" %include "gr_diff_encoder_bb.i" %include "gr_diff_decoder_bb.i" diff --git a/gnuradio-core/src/lib/general/gr_constellation.cc b/gnuradio-core/src/lib/general/gr_constellation.cc new file mode 100644 index 000000000..69b6e1bdc --- /dev/null +++ b/gnuradio-core/src/lib/general/gr_constellation.cc @@ -0,0 +1,368 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 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. + */ + +#include <gr_io_signature.h> +#include <gr_constellation.h> +#include <gr_metric_type.h> +#include <gr_math.h> +#include <gr_complex.h> +#include <math.h> +#include <iostream> +#include <stdlib.h> +#include <float.h> +#include <stdexcept> + +#define M_TWOPI (2*M_PI) +#define SQRT_TWO 0.707107 + +// Base Constellation Class + +gr_constellation::gr_constellation (std::vector<gr_complex> constellation, std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, unsigned int dimensionality) : + d_constellation(constellation), + d_pre_diff_code(pre_diff_code), + d_rotational_symmetry(rotational_symmetry), + d_dimensionality(dimensionality) +{ + if (pre_diff_code.size() == 0) + d_apply_pre_diff_code = false; + else if (pre_diff_code.size() != constellation.size()) + throw std::runtime_error ("The constellation and pre-diff code must be of the same length."); + else + d_apply_pre_diff_code = true; + calc_arity(); +} + +gr_constellation::gr_constellation () : + d_apply_pre_diff_code(false), + d_rotational_symmetry(0), + d_dimensionality(1) +{ + calc_arity(); +} + +//! Returns the constellation points for a symbol value +void gr_constellation::map_to_points(unsigned int value, gr_complex *points) { + for (unsigned int i=0; i<d_dimensionality; i++) + points[i] = d_constellation[value*d_dimensionality + i]; +} + +std::vector<gr_complex> gr_constellation::map_to_points_v(unsigned int value) { + std::vector<gr_complex> points_v; + points_v.resize(d_dimensionality); + map_to_points(value, &(points_v[0])); + return points_v; +} + +float gr_constellation::get_distance(unsigned int index, const gr_complex *sample) { + float dist = 0; + for (unsigned int i=0; i<d_dimensionality; i++) { + dist += norm(sample[i] - d_constellation[index*d_dimensionality + i]); + } + return dist; +} + +unsigned int gr_constellation::get_closest_point(const gr_complex *sample) { + + unsigned int min_index = 0; + float min_euclid_dist; + float euclid_dist; + + min_euclid_dist = get_distance(0, sample); + min_index = 0; + for (unsigned int j = 1; j < d_arity; j++){ + euclid_dist = get_distance(j, sample); + if (euclid_dist < min_euclid_dist){ + min_euclid_dist = euclid_dist; + min_index = j; + } + } + return min_index; +} + +unsigned int gr_constellation::decision_maker_pe(const gr_complex *sample, float *phase_error) +{ + unsigned int index = decision_maker(sample); + *phase_error = 0; + for (unsigned int d=0; d<d_dimensionality; d++) + *phase_error += -arg(sample[d]*conj(d_constellation[index+d])); + return index; +} + +/* +unsigned int gr_constellation::decision_maker_e(const gr_complex *sample, float *error) +{ + unsigned int index = decision_maker(sample); + *error = 0; + for (unsigned int d=0; d<d_dimensionality; d++) + *error += sample[d]*conj(d_constellation[index+d]); + return index; +} +*/ + +std::vector<gr_complex> gr_constellation::s_points () { + if (d_dimensionality != 1) + throw std::runtime_error ("s_points only works for dimensionality 1 constellations."); + else + return d_constellation; +} + +std::vector<std::vector<gr_complex> > gr_constellation::v_points () { + std::vector<std::vector<gr_complex> > vv_const; + vv_const.resize(d_arity); + for (unsigned int p=0; p<d_arity; p++) { + std::vector<gr_complex> v_const; + v_const.resize(d_dimensionality); + for (unsigned int d=0; d<d_dimensionality; d++) { + v_const[d] = d_constellation[p*d_dimensionality+d]; + } + vv_const[p] = v_const; + } + return vv_const; +} + +void gr_constellation::calc_metric(const gr_complex *sample, float *metric, trellis_metric_type_t type) { + switch (type){ + case TRELLIS_EUCLIDEAN: + calc_euclidean_metric(sample, metric); + break; + case TRELLIS_HARD_SYMBOL: + calc_hard_symbol_metric(sample, metric); + break; + case TRELLIS_HARD_BIT: + throw std::runtime_error ("Invalid metric type (not yet implemented)."); + break; + default: + throw std::runtime_error ("Invalid metric type."); + } +} + +void gr_constellation::calc_euclidean_metric(const gr_complex *sample, float *metric) { + for (unsigned int o=0; o<d_arity; o++) { + metric[o] = get_distance(o, sample); + } +} + +void gr_constellation::calc_hard_symbol_metric(const gr_complex *sample, float *metric){ + float minm = FLT_MAX; + unsigned int minmi = 0; + for (unsigned int o=0; o<d_arity; o++) { + float dist = get_distance(o, sample); + if (dist < minm) { + minm = dist; + minmi = o; + } + } + for(unsigned int o=0; o<d_arity; o++) { + metric[o] = (o==minmi?0.0:1.0); + } +} + +void gr_constellation::calc_arity () { + if (d_constellation.size() % d_dimensionality != 0) + throw std::runtime_error ("Constellation vector size must be a multiple of the dimensionality."); + d_arity = d_constellation.size()/d_dimensionality; +} + +unsigned int gr_constellation::decision_maker_v (std::vector<gr_complex> sample) { + assert(sample.size() == d_dimensionality); + return decision_maker (&(sample[0])); +} + +gr_constellation_calcdist_sptr +gr_make_constellation_calcdist(std::vector<gr_complex> constellation, std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, unsigned int dimensionality) +{ + return gr_constellation_calcdist_sptr(new gr_constellation_calcdist (constellation, pre_diff_code, rotational_symmetry, + dimensionality)); +} + +gr_constellation_calcdist::gr_constellation_calcdist(std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int dimensionality) : + gr_constellation(constellation, pre_diff_code, rotational_symmetry, dimensionality) +{} + +// Chooses points base on shortest distance. +// Inefficient. +unsigned int gr_constellation_calcdist::decision_maker(const gr_complex *sample) +{ + return get_closest_point(sample); +} + +gr_constellation_sector::gr_constellation_sector (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int dimensionality, + unsigned int n_sectors) : + gr_constellation(constellation, pre_diff_code, rotational_symmetry, dimensionality), + n_sectors(n_sectors) +{ +} + +unsigned int gr_constellation_sector::decision_maker (const gr_complex *sample) { + unsigned int sector; + sector = get_sector(sample); + return sector_values[sector]; +} + +void gr_constellation_sector::find_sector_values () { + unsigned int i; + sector_values.clear(); + for (i=0; i<n_sectors; i++) { + sector_values.push_back(calc_sector_value(i)); + } +} + +gr_constellation_rect_sptr +gr_make_constellation_rect(std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int real_sectors, unsigned int imag_sectors, + float width_real_sectors, float width_imag_sectors) +{ + return gr_constellation_rect_sptr(new gr_constellation_rect (constellation, pre_diff_code, rotational_symmetry, + real_sectors, imag_sectors, width_real_sectors, + width_imag_sectors)); + } + +gr_constellation_rect::gr_constellation_rect (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int real_sectors, unsigned int imag_sectors, + float width_real_sectors, float width_imag_sectors) : + gr_constellation_sector(constellation, pre_diff_code, rotational_symmetry, 1, real_sectors * imag_sectors), + n_real_sectors(real_sectors), n_imag_sectors(imag_sectors), + d_width_real_sectors(width_real_sectors), d_width_imag_sectors(width_imag_sectors) +{ + find_sector_values(); +} + +unsigned int gr_constellation_rect::get_sector (const gr_complex *sample) { + int real_sector, imag_sector; + unsigned int sector; + real_sector = int(real(*sample)/d_width_real_sectors + n_real_sectors/2.0); + if (real_sector < 0) real_sector = 0; + if (real_sector >= n_real_sectors) real_sector = n_real_sectors-1; + imag_sector = int(imag(*sample)/d_width_imag_sectors + n_imag_sectors/2.0); + if (imag_sector < 0) imag_sector = 0; + if (imag_sector >= n_imag_sectors) imag_sector = n_imag_sectors-1; + sector = real_sector * n_imag_sectors + imag_sector; + return sector; +} + +unsigned int gr_constellation_rect::calc_sector_value (unsigned int sector) { + unsigned int real_sector, imag_sector; + gr_complex sector_center; + unsigned int closest_point; + real_sector = float(sector)/n_imag_sectors; + imag_sector = sector - real_sector * n_imag_sectors; + sector_center = gr_complex((real_sector + 0.5 - n_real_sectors/2.0) * d_width_real_sectors, + (imag_sector + 0.5 - n_imag_sectors/2.0) * d_width_imag_sectors); + closest_point = get_closest_point(§or_center); + return closest_point; +} + + +gr_constellation_psk_sptr +gr_make_constellation_psk(std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int n_sectors) +{ + return gr_constellation_psk_sptr(new gr_constellation_psk (constellation, pre_diff_code, + n_sectors)); +} + +gr_constellation_psk::gr_constellation_psk (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int n_sectors) : + gr_constellation_sector(constellation, pre_diff_code, constellation.size(), 1, n_sectors) +{ + find_sector_values(); +} + +unsigned int gr_constellation_psk::get_sector (const gr_complex *sample) { + float phase = arg(*sample); + float width = M_TWOPI / n_sectors; + int sector = floor(phase/width + 0.5); + unsigned int u_sector; + if (sector < 0) sector += n_sectors; + u_sector = sector; + return sector; +} + +unsigned int gr_constellation_psk::calc_sector_value (unsigned int sector) { + float phase = sector * M_TWOPI / n_sectors; + gr_complex sector_center = gr_complex(cos(phase), sin(phase)); + unsigned int closest_point = get_closest_point(§or_center); + return closest_point; +} + + +gr_constellation_bpsk_sptr +gr_make_constellation_bpsk() +{ + return gr_constellation_bpsk_sptr(new gr_constellation_bpsk ()); +} + +gr_constellation_bpsk::gr_constellation_bpsk () +{ + d_constellation.resize(2); + d_constellation[0] = gr_complex(-1, 0); + d_constellation[1] = gr_complex(1, 0); + d_rotational_symmetry = 2; + d_dimensionality = 1; + calc_arity(); +} + +unsigned int gr_constellation_bpsk::decision_maker(const gr_complex *sample) +{ + return (real(*sample) > 0); +} + + +gr_constellation_qpsk_sptr +gr_make_constellation_qpsk() +{ + return gr_constellation_qpsk_sptr(new gr_constellation_qpsk ()); +} + +gr_constellation_qpsk::gr_constellation_qpsk () +{ + d_constellation.resize(4); + // Gray-coded + d_constellation[0] = gr_complex(-SQRT_TWO, -SQRT_TWO); + d_constellation[1] = gr_complex(SQRT_TWO, -SQRT_TWO); + d_constellation[2] = gr_complex(-SQRT_TWO, SQRT_TWO); + d_constellation[3] = gr_complex(SQRT_TWO, SQRT_TWO); + d_rotational_symmetry = 4; + d_dimensionality = 1; + calc_arity(); +} + +unsigned int gr_constellation_qpsk::decision_maker(const gr_complex *sample) +{ + // Real component determines small bit. + // Imag component determines big bit. + return 2*(imag(*sample)>0) + (real(*sample)>0); +} diff --git a/gnuradio-core/src/lib/general/gr_constellation.h b/gnuradio-core/src/lib/general/gr_constellation.h new file mode 100644 index 000000000..4ae539572 --- /dev/null +++ b/gnuradio-core/src/lib/general/gr_constellation.h @@ -0,0 +1,322 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 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_CONSTELLATION_H +#define INCLUDED_GR_CONSTELLATION_H + +#include <vector> +#include <math.h> +#include <gr_complex.h> +#include <boost/enable_shared_from_this.hpp> +#include <gr_metric_type.h> + +/************************************************************/ +/* gr_constellation */ +/* */ +/* Base class defining interface. */ +/************************************************************/ + +class gr_constellation; +typedef boost::shared_ptr<gr_constellation> gr_constellation_sptr; + +class gr_constellation : public boost::enable_shared_from_this<gr_constellation> +{ +public: + gr_constellation (std::vector<gr_complex> constellation, std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, unsigned int dimensionality); + gr_constellation (); + + //! Returns the constellation points for a symbol value + void map_to_points(unsigned int value, gr_complex *points); + std::vector<gr_complex> map_to_points_v(unsigned int value); + + //! Returns the constellation point that matches best. + virtual unsigned int decision_maker (const gr_complex *sample) = 0; + //! Takes a vector rather than a pointer. Better for SWIG wrapping. + unsigned int decision_maker_v (std::vector<gr_complex> sample); + //! Also calculates the phase error. + unsigned int decision_maker_pe (const gr_complex *sample, float *phase_error); + //! Calculates distance. + unsigned int decision_maker_e (const gr_complex *sample, float *error); + + //! Calculates metrics for all points in the constellation. + //! For use with the viterbi algorithm. + virtual void calc_metric(const gr_complex *sample, float *metric, trellis_metric_type_t type); + virtual void calc_euclidean_metric(const gr_complex *sample, float *metric); + virtual void calc_hard_symbol_metric(const gr_complex *sample, float *metric); + + //! Returns the set of points in this constellation. + std::vector<gr_complex> points() { return d_constellation;} + //! Returns the vector of points in this constellation. + //! Raise error if dimensionality is not one. + std::vector<gr_complex> s_points(); + //! Returns a vector of vectors of points. + std::vector<std::vector<gr_complex> > v_points(); + //! Whether to apply an encoding before doing differential encoding. (e.g. gray coding) + bool apply_pre_diff_code() { return d_apply_pre_diff_code;} + //! Returns the encoding to apply before differential encoding. + std::vector<unsigned int> pre_diff_code() { return d_pre_diff_code;} + //! Returns the order of rotational symmetry. + unsigned int rotational_symmetry() { return d_rotational_symmetry;} + //! Returns the number of complex numbers in a single symbol. + unsigned int dimensionality() {return d_dimensionality;} + + unsigned int bits_per_symbol () { + return floor(log(d_constellation.size())/d_dimensionality/log(2)); + } + + unsigned int arity () { + return d_arity; + } + + gr_constellation_sptr base() { + return shared_from_this(); + } + + protected: + + std::vector<gr_complex> d_constellation; + std::vector<unsigned int> d_pre_diff_code; + bool d_apply_pre_diff_code; + unsigned int d_rotational_symmetry; + unsigned int d_dimensionality; + unsigned int d_arity; + + float get_distance(unsigned int index, const gr_complex *sample); + unsigned int get_closest_point(const gr_complex *sample); + void calc_arity (); +}; + +/************************************************************/ +/* gr_constellation_calcdist */ +/* */ +/* Constellation which calculates the distance to each */ +/* point in the constellation for decision making. */ +/* Inefficient for large constellations. */ +/************************************************************/ + +class gr_constellation_calcdist; +typedef boost::shared_ptr<gr_constellation_calcdist> gr_constellation_calcdist_sptr; + +// public constructor +gr_constellation_calcdist_sptr +gr_make_constellation_calcdist (std::vector<gr_complex> constellation, std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, unsigned int dimensionality); + + +class gr_constellation_calcdist : public gr_constellation +{ + public: + gr_constellation_calcdist (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int dimensionality); + unsigned int decision_maker (const gr_complex *sample); + // void calc_metric(gr_complex *sample, float *metric, trellis_metric_type_t type); + // void calc_euclidean_metric(gr_complex *sample, float *metric); + // void calc_hard_symbol_metric(gr_complex *sample, float *metric); + + private: + friend gr_constellation_calcdist_sptr + gr_make_constellation_calcdist (std::vector<gr_complex> constellation); +}; + +/************************************************************/ +/* gr_constellation_sector */ +/* */ +/* An abstract class. */ +/* Constellation space is divided into sectors. */ +/* Each sector is associated with the nearest constellation */ +/* point. */ +/************************************************************/ + +class gr_constellation_sector : public gr_constellation +{ + public: + + gr_constellation_sector (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int dimensionality, + unsigned int n_sectors); + + unsigned int decision_maker (const gr_complex *sample); + + protected: + + virtual unsigned int get_sector (const gr_complex *sample) = 0; + virtual unsigned int calc_sector_value (unsigned int sector) = 0; + void find_sector_values (); + + unsigned int n_sectors; + + private: + + std::vector<unsigned int> sector_values; + +}; + +/************************************************************/ +/* gr_constellation_rect */ +/* */ +/* Only implemented for 1-(complex)dimensional */ +/* constellation. */ +/* Constellation space is divided into rectangular sectors. */ +/* Each sector is associated with the nearest constellation */ +/* point. */ +/* Works well for square QAM. */ +/* Works for any generic constellation provided sectors are */ +/* not too large. */ +/************************************************************/ + +class gr_constellation_rect; +typedef boost::shared_ptr<gr_constellation_rect> gr_constellation_rect_sptr; + +// public constructor +gr_constellation_rect_sptr +gr_make_constellation_rect (std::vector<gr_complex> constellation, std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int real_sectors, unsigned int imag_sectors, + float width_real_sectors, float width_imag_sectors); + +class gr_constellation_rect : public gr_constellation_sector +{ + public: + + gr_constellation_rect (std::vector<gr_complex> constellation, std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int real_sectors, unsigned int imag_sectors, + float width_real_sectors, float width_imag_sectors); + + protected: + + unsigned int get_sector (const gr_complex *sample); + + unsigned int calc_sector_value (unsigned int sector); + + private: + + unsigned int n_real_sectors; + unsigned int n_imag_sectors; + float d_width_real_sectors; + float d_width_imag_sectors; + + friend gr_constellation_rect_sptr + gr_make_constellation_rect (std::vector<gr_complex> constellation, std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int real_sectors, unsigned int imag_sectors, + float width_real_sectors, float width_imag_sectors); + +}; + +/************************************************************/ +/* gr_constellation_psk */ +/* */ +/* Constellation space is divided into pie slices sectors. */ +/* Each slice is associated with the nearest constellation */ +/* point. */ +/* Works well for PSK but nothing else. */ +/* Assumes that there is a constellation point at 1. */ +/************************************************************/ + +class gr_constellation_psk; +typedef boost::shared_ptr<gr_constellation_psk> gr_constellation_psk_sptr; + +// public constructor +gr_constellation_psk_sptr +gr_make_constellation_psk (std::vector<gr_complex> constellation, std::vector<unsigned int> pre_diff_code, + unsigned int n_sectors); + +class gr_constellation_psk : public gr_constellation_sector +{ + public: + + gr_constellation_psk (std::vector<gr_complex> constellation, std::vector<unsigned int> pre_diff_code, + unsigned int n_sectors); + + protected: + + unsigned int get_sector (const gr_complex *sample); + + unsigned int calc_sector_value (unsigned int sector); + + private: + + friend gr_constellation_psk_sptr + gr_make_constellation_psk (std::vector<gr_complex> constellation, std::vector<unsigned int> pre_diff_code, + unsigned int n_sectors); + +}; + +/************************************************************/ +/* gr_constellation_bpsk */ +/* */ +/* Only works for BPSK. */ +/* */ +/************************************************************/ + +class gr_constellation_bpsk; +typedef boost::shared_ptr<gr_constellation_bpsk> gr_constellation_bpsk_sptr; + +// public constructor +gr_constellation_bpsk_sptr +gr_make_constellation_bpsk (); + +class gr_constellation_bpsk : public gr_constellation +{ + public: + + gr_constellation_bpsk (); + unsigned int decision_maker (const gr_complex *sample); + + friend gr_constellation_bpsk_sptr + gr_make_constellation_bpsk (); + +}; + +/************************************************************/ +/* gr_constellation_qpsk */ +/* */ +/* Only works for QPSK. */ +/* */ +/************************************************************/ + +class gr_constellation_qpsk; +typedef boost::shared_ptr<gr_constellation_qpsk> gr_constellation_qpsk_sptr; + +// public constructor +gr_constellation_qpsk_sptr +gr_make_constellation_qpsk (); + +class gr_constellation_qpsk : public gr_constellation +{ + public: + + gr_constellation_qpsk (); + unsigned int decision_maker (const gr_complex *sample); + + friend gr_constellation_qpsk_sptr + gr_make_constellation_qpsk (); + +}; + +#endif diff --git a/gnuradio-core/src/lib/general/gr_constellation.i b/gnuradio-core/src/lib/general/gr_constellation.i new file mode 100644 index 000000000..bc1724c3a --- /dev/null +++ b/gnuradio-core/src/lib/general/gr_constellation.i @@ -0,0 +1,146 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 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. + */ + +%template(gr_complex_vector) std::vector<gr_complex>; +%template(unsigned_int_vector) std::vector<unsigned int>; + +// Make sure metric types get SWIGed. +%include gr_metric_type.h + +class gr_constellation; +typedef boost::shared_ptr<gr_constellation> gr_constellation_sptr; +%template(gr_constellation_sptr) boost::shared_ptr<gr_constellation>; + +class gr_constellation +{ +public: + gr_constellation (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int dimensionality); + std::vector<gr_complex> points(); + std::vector<gr_complex> s_points(); + std::vector<std::vector<gr_complex> > v_points(); + virtual unsigned int decision_maker (gr_complex *sample) = 0; + unsigned int decision_maker_v (std::vector<gr_complex> sample); + // void calc_metric(gr_complex *sample, float *metric, trellis_metric_type_t type); + // void calc_euclidean_metric(gr_complex *sample, float *metric); + // void calc_hard_symbol_metric(gr_complex *sample, float *metric); + std::vector<gr_complex> map_to_points_v(unsigned int value); + unsigned int bits_per_symbol (); + unsigned int arity (); + gr_constellation_sptr base (); + bool apply_pre_diff_code(); + std::vector<unsigned int> pre_diff_code(); + unsigned int rotational_symmetry(); + unsigned int dimensionality(); +}; + +class gr_constellation_calcdist; +typedef boost::shared_ptr<gr_constellation_calcdist> gr_constellation_calcdist_sptr; +%template(gr_constellation_calcdist_sptr) boost::shared_ptr<gr_constellation_calcdist>; +%rename(constellation_calcdist) gr_make_constellation_calcdist; +gr_constellation_calcdist_sptr +gr_make_constellation_calcdist(std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int dimensionality); +%ignore gr_constellation_calcdist; + +class gr_constellation_calcdist: public gr_constellation +{ + public: + gr_constellation_calcdist (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int dimensionality); + unsigned int decision_maker (const gr_complex *sample); +}; + +class gr_constellation_sector: public gr_constellation +{ +}; + +class gr_constellation_rect; +typedef boost::shared_ptr<gr_constellation_rect> gr_constellation_rect_sptr; +%template(gr_constellation_rect_sptr) boost::shared_ptr<gr_constellation_rect>; +%rename(constellation_rect) gr_make_constellation_rect; +gr_constellation_rect_sptr gr_make_constellation_rect(std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int real_sectors, unsigned int imag_sectors, + float width_real_sectors, float width_imag_sectors); +%ignore gr_constellation_rect; + +class gr_constellation_rect : public gr_constellation_sector +{ +public: + gr_constellation_rect (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int real_sectors, unsigned int imag_sectors, + float width_real_sectors, float width_imag_sectors); +}; + +class gr_constellation_psk; +typedef boost::shared_ptr<gr_constellation_psk> gr_constellation_psk_sptr; +%template(gr_constellation_psk_sptr) boost::shared_ptr<gr_constellation_psk>; +%rename(constellation_psk) gr_make_constellation_psk; +gr_constellation_psk_sptr gr_make_constellation_psk(std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int n_sectors); +%ignore gr_constellation_psk; + +class gr_constellation_psk : public gr_constellation_sector +{ +public: + gr_constellation_psk (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int n_sectors); +}; + +class gr_constellation_bpsk; +typedef boost::shared_ptr<gr_constellation_bpsk> gr_constellation_bpsk_sptr; +%template(gr_constellation_bpsk_sptr) boost::shared_ptr<gr_constellation_bpsk>; +%rename(constellation_bpsk) gr_make_constellation_bpsk; +gr_constellation_bpsk_sptr gr_make_constellation_bpsk(); +%ignore gr_constellation_bpsk; + +class gr_constellation_bpsk : public gr_constellation +{ +public: + gr_constellation_bpsk (); +}; + +class gr_constellation_qpsk; +typedef boost::shared_ptr<gr_constellation_qpsk> gr_constellation_qpsk_sptr; +%template(gr_constellation_qpsk_sptr) boost::shared_ptr<gr_constellation_qpsk>; +%rename(constellation_qpsk) gr_make_constellation_qpsk; +gr_constellation_qpsk_sptr gr_make_constellation_qpsk(); +%ignore gr_constellation_qpsk; + +class gr_constellation_qpsk : public gr_constellation +{ +public: + gr_constellation_qpsk (); +}; + diff --git a/gnuradio-core/src/lib/general/gr_constellation_decoder2_cb.cc b/gnuradio-core/src/lib/general/gr_constellation_decoder2_cb.cc new file mode 100644 index 000000000..a63c1d38a --- /dev/null +++ b/gnuradio-core/src/lib/general/gr_constellation_decoder2_cb.cc @@ -0,0 +1,78 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006, 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_constellation_decoder2_cb.h> +#include <gr_constellation.h> +#include <gr_io_signature.h> +#include <iostream> + +gr_constellation_decoder2_cb_sptr +gr_make_constellation_decoder2_cb (gr_constellation_sptr constellation) +{ + return gr_constellation_decoder2_cb_sptr + (new gr_constellation_decoder2_cb(constellation)); +} + +gr_constellation_decoder2_cb:: +gr_constellation_decoder2_cb (gr_constellation_sptr constellation) + : gr_block ("constellation_decoder2_cb", + gr_make_io_signature (1, 1, sizeof (gr_complex)), + gr_make_io_signature (1, 1, sizeof (unsigned char))), + d_constellation(constellation), + d_dim(constellation->dimensionality()) +{ + set_relative_rate (1.0 / ((double) d_dim)); +} + +void +gr_constellation_decoder2_cb::forecast (int noutput_items, + gr_vector_int &ninput_items_required) +{ + unsigned int input_required = noutput_items * d_dim; + + unsigned ninputs = ninput_items_required.size(); + for (unsigned int i = 0; i < ninputs; i++) + ninput_items_required[i] = input_required; +} + + +int +gr_constellation_decoder2_cb:: +general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + gr_complex const *in = (const gr_complex *) input_items[0]; + unsigned char *out = (unsigned char *) output_items[0]; + + for(int i = 0; i < noutput_items; i++){ + out[i] = d_constellation->decision_maker(&(in[i*d_dim])); + } + + consume_each (noutput_items * d_dim); + return noutput_items; +} diff --git a/gnuradio-core/src/lib/general/gr_constellation_decoder2_cb.h b/gnuradio-core/src/lib/general/gr_constellation_decoder2_cb.h new file mode 100644 index 000000000..51891b636 --- /dev/null +++ b/gnuradio-core/src/lib/general/gr_constellation_decoder2_cb.h @@ -0,0 +1,64 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006, 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GR_CONSTELLATION_DECODER2_CB_H +#define INCLUDED_GR_CONSTELLATION_DECODER2_CB_H + +#include <gr_block.h> +#include <gr_constellation.h> +#include <vector> + +class gr_constellation_decoder2_cb; +typedef boost::shared_ptr<gr_constellation_decoder2_cb> gr_constellation_decoder2_cb_sptr; + +gr_constellation_decoder2_cb_sptr +gr_make_constellation_decoder2_cb (gr_constellation_sptr constellation); + +/*! + * \brief Constellation Decoder + * \ingroup coding_blk + * + */ +class gr_constellation_decoder2_cb : public gr_block +{ + + private: + gr_constellation_sptr d_constellation; + unsigned int d_dim; + + friend gr_constellation_decoder2_cb_sptr + gr_make_constellation_decoder2_cb (gr_constellation_sptr constellation); + + gr_constellation_decoder2_cb (gr_constellation_sptr constellation); + + public: + + void forecast (int noutput_items, + gr_vector_int &ninput_items_required); + + int general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; + +#endif diff --git a/gnuradio-core/src/lib/general/gr_constellation_decoder2_cb.i b/gnuradio-core/src/lib/general/gr_constellation_decoder2_cb.i new file mode 100644 index 000000000..2865363d8 --- /dev/null +++ b/gnuradio-core/src/lib/general/gr_constellation_decoder2_cb.i @@ -0,0 +1,38 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006, 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +GR_SWIG_BLOCK_MAGIC(gr,constellation_decoder2_cb) + +gr_constellation_decoder2_cb_sptr +gr_make_constellation_decoder2_cb (gr_constellation_sptr constellation); + +class gr_constellation_decoder2_cb : public gr_sync_block +{ + private: + gr_constellation_decoder2_cb (gr_constellation_sptr constellation); + + friend gr_constellation_decoder2_cb_sptr + gr_make_constellation_decoder2_cb (gr_constellation_sptr constellation); + + public: + ~gr_constellation_decoder2_cb(); +}; diff --git a/gnuradio-core/src/lib/general/gr_constellation_receiver_cb.cc b/gnuradio-core/src/lib/general/gr_constellation_receiver_cb.cc new file mode 100644 index 000000000..dae70d931 --- /dev/null +++ b/gnuradio-core/src/lib/general/gr_constellation_receiver_cb.cc @@ -0,0 +1,131 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2006,2007,2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_io_signature.h> +#include <gr_prefs.h> +#include <gr_constellation.h> +#include <gr_constellation_receiver_cb.h> +#include <stdexcept> +#include <gr_math.h> +#include <gr_expj.h> + + +#define M_TWOPI (2*M_PI) +#define VERBOSE_MM 0 // Used for debugging symbol timing loop +#define VERBOSE_COSTAS 0 // Used for debugging phase and frequency tracking + +// Public constructor + +gr_constellation_receiver_cb_sptr +gr_make_constellation_receiver_cb(gr_constellation_sptr constell, + float alpha, float beta, + float fmin, float fmax) +{ + return gnuradio::get_initial_sptr(new gr_constellation_receiver_cb (constell, + alpha, beta, + fmin, fmax)); +} + +static int ios[] = {sizeof(char), sizeof(float), sizeof(float), sizeof(float)}; +static std::vector<int> iosig(ios, ios+sizeof(ios)/sizeof(int)); +gr_constellation_receiver_cb::gr_constellation_receiver_cb (gr_constellation_sptr constellation, + float alpha, float beta, + float fmin, float fmax) + : gr_block ("constellation_receiver_cb", + gr_make_io_signature (1, 1, sizeof (gr_complex)), + gr_make_io_signaturev (1, 4, iosig)), + d_constellation(constellation), + d_alpha(alpha), d_beta(beta), d_freq(0), d_max_freq(fmax), d_min_freq(fmin), d_phase(0), + d_current_const_point(0) +{ + if (d_constellation->dimensionality() != 1) + throw std::runtime_error ("This receiver only works with constellations of dimension 1."); +} + +void +gr_constellation_receiver_cb::phase_error_tracking(float phase_error) +{ + d_freq += d_beta*phase_error; // adjust frequency based on error + d_phase += d_freq + d_alpha*phase_error; // adjust phase based on error + + // Make sure we stay within +-2pi + while(d_phase > M_TWOPI) + d_phase -= M_TWOPI; + while(d_phase < -M_TWOPI) + d_phase += M_TWOPI; + + // Limit the frequency range + d_freq = gr_branchless_clip(d_freq, d_max_freq); + +#if VERBOSE_COSTAS + printf("cl: phase_error: %f phase: %f freq: %f sample: %f+j%f constellation: %f+j%f\n", + phase_error, d_phase, d_freq, sample.real(), sample.imag(), + d_constellation->points()[d_current_const_point].real(), d_constellation->points()[d_current_const_point].imag()); +#endif +} + +int +gr_constellation_receiver_cb::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]; + unsigned char *out = (unsigned char *) output_items[0]; + + int i=0; + + float phase_error; + unsigned int sym_value; + gr_complex sample, nco; + + float *out_err = 0, *out_phase = 0, *out_freq = 0; + if(output_items.size() == 4) { + out_err = (float *) output_items[1]; + out_phase = (float *) output_items[2]; + out_freq = (float *) output_items[3]; + } + + while((i < noutput_items) && (i < ninput_items[0])) { + sample = in[i]; + nco = gr_expj(d_phase); // get the NCO value for derotating the current sample + sample = nco*sample; // get the downconverted symbol + sym_value = d_constellation->decision_maker_pe(&sample, &phase_error); + // phase_error = -arg(sample*conj(d_constellation->points()[sym_value])); + phase_error_tracking(phase_error); // corrects phase and frequency offsets + out[i] = sym_value; + if(output_items.size() == 4) { + out_err[i] = phase_error; + out_phase[i] = d_phase; + out_freq[i] = d_freq; + } + i++; + } + + consume_each(i); + return i; +} + diff --git a/gnuradio-core/src/lib/general/gr_constellation_receiver_cb.h b/gnuradio-core/src/lib/general/gr_constellation_receiver_cb.h new file mode 100644 index 000000000..d40d77fff --- /dev/null +++ b/gnuradio-core/src/lib/general/gr_constellation_receiver_cb.h @@ -0,0 +1,149 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2007,2010 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_CONSTELLATION_RECEIVER_CB_H +#define INCLUDED_GR_CONSTELLATION_RECEIVER_CB_H + +#include <gr_block.h> +#include <gr_constellation.h> +#include <gr_complex.h> +#include <math.h> +#include <fstream> + +class gr_constellation_receiver_cb; +typedef boost::shared_ptr<gr_constellation_receiver_cb> gr_constellation_receiver_cb_sptr; + +// public constructor +gr_constellation_receiver_cb_sptr +gr_make_constellation_receiver_cb (gr_constellation_sptr constellation, + float alpha, float beta, + float fmin, float fmax); + +/*! + * \brief This block takes care of receiving generic modulated signals through phase, frequency, and symbol + * synchronization. + * \ingroup sync_blk + * \ingroup demod_blk + * + * This block takes care of receiving generic modulated signals through phase, frequency, and symbol + * synchronization. It performs carrier frequency and phase locking as well as symbol timing recovery. + * + * The phase and frequency synchronization are based on a Costas loop that finds the error of the incoming + * signal point compared to its nearest constellation point. The frequency and phase of the NCO are + * updated according to this error. + * + * The symbol synchronization is done using a modified Mueller and Muller circuit from the paper: + * + * G. R. Danesfahani, T.G. Jeans, "Optimisation of modified Mueller and Muller + * algorithm," Electronics Letters, Vol. 31, no. 13, 22 June 1995, pp. 1032 - 1033. + * + * This circuit interpolates the downconverted sample (using the NCO developed by the Costas loop) + * every mu samples, then it finds the sampling error based on this and the past symbols and the decision + * made on the samples. Like the phase error detector, there are optimized decision algorithms for BPSK + * and QPKS, but 8PSK uses another brute force computation against all possible symbols. The modifications + * to the M&M used here reduce self-noise. + * + */ + +class gr_constellation_receiver_cb : public gr_block +{ + public: + int general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + + // Member function related to the phase/frequency tracking portion of the receiver + //! (CL) Returns the value for alpha (the phase gain term) + float alpha() const { return d_alpha; } + + //! (CL) Returns the value of beta (the frequency gain term) + float beta() const { return d_beta; } + + //! (CL) Returns the current value of the frequency of the NCO in the Costas loop + float freq() const { return d_freq; } + + //! (CL) Returns the current value of the phase of the NCO in the Costal loop + float phase() const { return d_phase; } + + //! (CL) Sets the value for alpha (the phase gain term) + void set_alpha(float alpha) { d_alpha = alpha; } + + //! (CL) Setss the value of beta (the frequency gain term) + void set_beta(float beta) { d_beta = beta; } + + //! (CL) Sets the current value of the frequency of the NCO in the Costas loop + void set_freq(float freq) { d_freq = freq; } + + //! (CL) Setss the current value of the phase of the NCO in the Costal loop + void set_phase(float phase) { d_phase = phase; } + + +protected: + + /*! + * \brief Constructor to synchronize incoming M-PSK symbols + * + * \param constellation constellation of points for generic modulation + * \param alpha gain parameter to adjust the phase in the Costas loop (~0.01) + * \param beta gain parameter to adjust the frequency in the Costas loop (~alpha^2/4) + * \param fmin minimum normalized frequency value the loop can achieve + * \param fmax maximum normalized frequency value the loop can achieve + * + * The constructor also chooses which phase detector and decision maker to use in the work loop based on the + * value of M. + */ + gr_constellation_receiver_cb (gr_constellation_sptr constellation, + float alpha, float beta, + float fmin, float fmax); + + void phase_error_tracking(float phase_error); + + private: + unsigned int d_M; + + // Members related to carrier and phase tracking + float d_alpha; + float d_beta; + float d_freq, d_max_freq, d_min_freq; + float d_phase; + + gr_constellation_sptr d_constellation; + unsigned int d_current_const_point; + + //! delay line length. + static const unsigned int DLLEN = 8; + + //! delay line plus some length for overflow protection + gr_complex d_dl[2*DLLEN] __attribute__ ((aligned(8))); + + //! index to delay line + unsigned int d_dl_idx; + + friend gr_constellation_receiver_cb_sptr + gr_make_constellation_receiver_cb (gr_constellation_sptr constell, + float alpha, float beta, + float fmin, float fmax); +}; + +#endif diff --git a/gnuradio-core/src/lib/general/gr_constellation_receiver_cb.i b/gnuradio-core/src/lib/general/gr_constellation_receiver_cb.i new file mode 100644 index 000000000..2298d5099 --- /dev/null +++ b/gnuradio-core/src/lib/general/gr_constellation_receiver_cb.i @@ -0,0 +1,43 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 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,constellation_receiver_cb); + +gr_constellation_receiver_cb_sptr gr_make_constellation_receiver_cb (gr_constellation_sptr constellation, + float alpha, float beta, + float fmin, float fmax); +class gr_constellation_receiver_cb : public gr_block +{ + private: + gr_constellation_receiver_cb (gr_contellation_sptr constellation, + float alpha, float beta, + float fmin, float fmax); +public: + float alpha() const { return d_alpha; } + float beta() const { return d_beta; } + float freq() const { return d_freq; } + float phase() const { return d_phase; } + void set_alpha(float alpha) { d_alpha = alpha; } + void set_beta(float beta) { d_beta = beta; } + void set_freq(float freq) { d_freq = freq; } + void set_phase(float phase) { d_phase = phase; } +}; diff --git a/gnuradio-core/src/lib/general/gr_metric_type.h b/gnuradio-core/src/lib/general/gr_metric_type.h new file mode 100644 index 000000000..74c93b55e --- /dev/null +++ b/gnuradio-core/src/lib/general/gr_metric_type.h @@ -0,0 +1,31 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 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_METRIC_TYPE_H +#define INCLUDED_GR_METRIC_TYPE_H + +typedef enum { + TRELLIS_EUCLIDEAN = 200, TRELLIS_HARD_SYMBOL, TRELLIS_HARD_BIT +} trellis_metric_type_t; + +#endif + diff --git a/gnuradio-core/src/lib/general/gr_ofdm_frame_sink2.cc b/gnuradio-core/src/lib/general/gr_ofdm_frame_sink2.cc new file mode 100644 index 000000000..f0cb0fb0a --- /dev/null +++ b/gnuradio-core/src/lib/general/gr_ofdm_frame_sink2.cc @@ -0,0 +1,374 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2008,2010 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_sink2.h> +#include <gr_io_signature.h> +#include <gr_expj.h> +#include <gr_math.h> +#include <math.h> +#include <cstdio> +#include <stdexcept> +#include <iostream> +#include <string.h> +#include <gr_constellation.h> + +#define VERBOSE 0 + +inline void +gr_ofdm_frame_sink2::enter_search() +{ + if (VERBOSE) + fprintf(stderr, "@ enter_search\n"); + + d_state = STATE_SYNC_SEARCH; + +} + +inline void +gr_ofdm_frame_sink2::enter_have_sync() +{ + if (VERBOSE) + fprintf(stderr, "@ enter_have_sync\n"); + + d_state = STATE_HAVE_SYNC; + + // clear state of demapper + d_byte_offset = 0; + d_partial_byte = 0; + + 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 +gr_ofdm_frame_sink2::enter_have_header() +{ + d_state = STATE_HAVE_HEADER; + + // header consists of two 16-bit shorts in network byte order + // payload length is lower 12 bits + // whitener offset is upper 4 bits + d_packetlen = (d_header >> 16) & 0x0fff; + d_packet_whitener_offset = (d_header >> 28) & 0x000f; + d_packetlen_cnt = 0; + + if (VERBOSE) + fprintf(stderr, "@ enter_have_header (payload_len = %d) (offset = %d)\n", + d_packetlen, d_packet_whitener_offset); +} + + +unsigned int gr_ofdm_frame_sink2::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) { + while(i < d_subcarrier_map.size()) { + if(d_nresid > 0) { + d_partial_byte |= d_resid; + d_byte_offset += d_nresid; + d_nresid = 0; + d_resid = 0; + } + + //while((d_byte_offset < 8) && (i < d_occupied_carriers)) { + while((d_byte_offset < 8) && (i < d_subcarrier_map.size())) { + //gr_complex sigrot = in[i]*carrier*d_dfe[i]; + gr_complex sigrot = in[d_subcarrier_map[i]]*carrier*d_dfe[i]; + + if(d_derotated_output != NULL){ + d_derotated_output[i] = sigrot; + } + + unsigned char bits = d_constell->decision_maker(&sigrot); + + gr_complex closest_sym = d_constell->points()[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; + } + else { + d_nresid = d_nbits-(8-d_byte_offset); + int mask = ((1<<(8-d_byte_offset))-1); + d_partial_byte |= (bits & mask) << d_byte_offset; + d_resid = bits >> (8-d_byte_offset); + d_byte_offset += (d_nbits - d_nresid); + } + //printf("demod symbol: %.4f + j%.4f bits: %x partial_byte: %x byte_offset: %d resid: %x nresid: %d\n", + // in[i-1].real(), in[i-1].imag(), bits, d_partial_byte, d_byte_offset, d_resid, d_nresid); + } + + if(d_byte_offset == 8) { + //printf("demod byte: %x \n\n", d_partial_byte); + out[bytes_produced++] = d_partial_byte; + d_byte_offset = 0; + 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; +} + + +gr_ofdm_frame_sink2_sptr +gr_make_ofdm_frame_sink2(gr_constellation_sptr constell, + gr_msg_queue_sptr target_queue, unsigned int occupied_carriers, + float phase_gain, float freq_gain) +{ + return gnuradio::get_initial_sptr(new gr_ofdm_frame_sink2(constell, + target_queue, occupied_carriers, + phase_gain, freq_gain)); +} + + +gr_ofdm_frame_sink2::gr_ofdm_frame_sink2(gr_constellation_sptr constell, + gr_msg_queue_sptr target_queue, unsigned int occupied_carriers, + float phase_gain, float freq_gain) + : gr_sync_block ("ofdm_frame_sink2", + gr_make_io_signature2 (2, 2, sizeof(gr_complex)*occupied_carriers, sizeof(char)), + gr_make_io_signature (1, 1, sizeof(gr_complex)*occupied_carriers)), + d_constell(constell), + 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_phase(0),d_freq(0),d_phase_gain(phase_gain),d_freq_gain(freq_gain), + d_eq_gain(0.05) +{ + if (d_constell->dimensionality() != 1) + throw std::runtime_error ("This receiver only works with constellations of dimension 1."); + + std::string carriers = "FE7F"; + + // A bit hacky to fill out carriers to occupied_carriers length + int diff = (d_occupied_carriers - 4*carriers.length()); + while(diff > 7) { + carriers.insert(0, "f"); + carriers.insert(carriers.length(), "f"); + diff -= 8; + } + + // if there's extras left to be processed + // divide remaining to put on either side of current map + // all of this is done to stick with the concept of a carrier map string that + // can be later passed by the user, even though it'd be cleaner to just do this + // on the carrier map itself + int diff_left=0; + int diff_right=0; + + // dictionary to convert from integers to ascii hex representation + char abc[16] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + if(diff > 0) { + char c[2] = {0,0}; + + diff_left = (int)ceil((float)diff/2.0f); // number of carriers to put on the left side + c[0] = abc[(1 << diff_left) - 1]; // convert to bits and move to ASCI integer + carriers.insert(0, c); + + diff_right = diff - diff_left; // number of carriers to put on the right side + c[0] = abc[0xF^((1 << diff_right) - 1)]; // convert to bits and move to ASCI integer + carriers.insert(carriers.length(), c); + } + + // It seemed like such a good idea at the time... + // because we are only dealing with the occupied_carriers + // at this point, the diff_left in the following compensates + // for any offset from the 0th carrier introduced + unsigned int i,j,k; + for(i = 0; i < (d_occupied_carriers/4)+diff_left; i++) { + char c = carriers[i]; + for(j = 0; j < 4; j++) { + k = (strtol(&c, NULL, 16) >> (3-j)) & 0x1; + if(k) { + d_subcarrier_map.push_back(4*i + j - diff_left); + } + } + } + + // make sure we stay in the limit currently imposed by the occupied_carriers + if(d_subcarrier_map.size() > d_occupied_carriers) { + throw std::invalid_argument("gr_ofdm_mapper_bcv: subcarriers allocated exceeds size of occupied carriers"); + } + + 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)); + + d_nbits = d_constell->bits_per_symbol(); + + enter_search(); +} + +gr_ofdm_frame_sink2::~gr_ofdm_frame_sink2 () +{ + delete [] d_bytes_out; +} + + +int +gr_ofdm_frame_sink2::work (int noutput_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 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"); + + switch(d_state) { + + case STATE_SYNC_SEARCH: // Look for flag indicating beginning of pkt + if (VERBOSE) + fprintf(stderr,"SYNC Search, noutput=%d\n", noutput_items); + + if (sig[0]) { // Found it, set up for header decode + enter_have_sync(); + } + 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"); + fprintf(stderr,"Header Search bitcnt=%d, header=0x%08x\n", + d_headerbytelen_cnt, d_header); + } + + j = 0; + while(j < bytes) { + d_header = (d_header << 8) | (d_bytes_out[j] & 0xFF); + j++; + + if (++d_headerbytelen_cnt == HEADERBYTELEN) { + + if (VERBOSE) + fprintf(stderr, "got header: 0x%08x\n", d_header); + + // we have a full header, check to see if it has been received properly + if (header_ok()){ + enter_have_header(); + + if (VERBOSE) + printf("\nPacket Length: %d\n", d_packetlen); + + while((j < bytes) && (d_packetlen_cnt < d_packetlen)) { + d_packet[d_packetlen_cnt++] = d_bytes_out[j++]; + } + + if(d_packetlen_cnt == d_packetlen) { + gr_message_sptr msg = + gr_make_message(0, d_packet_whitener_offset, 0, d_packetlen); + memcpy(msg->msg(), d_packet, d_packetlen_cnt); + d_target_queue->insert_tail(msg); // send it + msg.reset(); // free it up + + enter_search(); + } + } + else { + enter_search(); // bad header + } + } + } + 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); + fprintf(stderr,"Packet Build\n"); + } + + j = 0; + while(j < bytes) { + d_packet[d_packetlen_cnt++] = d_bytes_out[j++]; + + if (d_packetlen_cnt == d_packetlen){ // packet is filled + // build a message + // NOTE: passing header field as arg1 is not scalable + gr_message_sptr msg = + gr_make_message(0, d_packet_whitener_offset, 0, d_packetlen_cnt); + memcpy(msg->msg(), d_packet, d_packetlen_cnt); + + d_target_queue->insert_tail(msg); // send it + msg.reset(); // free it up + + enter_search(); + break; + } + } + break; + + default: + assert(0); + + } // switch + + return 1; +} diff --git a/gnuradio-core/src/lib/general/gr_ofdm_frame_sink2.h b/gnuradio-core/src/lib/general/gr_ofdm_frame_sink2.h new file mode 100644 index 000000000..647ce7958 --- /dev/null +++ b/gnuradio-core/src/lib/general/gr_ofdm_frame_sink2.h @@ -0,0 +1,120 @@ +/* -*- 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_OFDM_FRAME_SINK2_H +#define INCLUDED_GR_OFDM_FRAME_SINK2_H + +#include <gr_sync_block.h> +#include <gr_msg_queue.h> +#include <gr_constellation.h> + +class gr_ofdm_frame_sink2; +typedef boost::shared_ptr<gr_ofdm_frame_sink2> gr_ofdm_frame_sink2_sptr; + +gr_ofdm_frame_sink2_sptr +gr_make_ofdm_frame_sink2 (gr_constellation_sptr constell, + 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 + * them into packets, and sends to to a message queue sink. + * \ingroup sink_blk + * \ingroup ofdm_blk + * + * NOTE: The mod input parameter simply chooses a pre-defined demapper/slicer. Eventually, + * we want to be able to pass in a reference to an object to do the demapping and slicing + * for a given modulation type. + */ +class gr_ofdm_frame_sink2 : public gr_sync_block +{ + friend gr_ofdm_frame_sink2_sptr + gr_make_ofdm_frame_sink2 (gr_constellation_sptr constell, + 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}; + + static const int MAX_PKT_LEN = 4096; + static const int HEADERBYTELEN = 4; + + gr_msg_queue_sptr d_target_queue; // where to send the packet when received + state_t d_state; + unsigned int d_header; // header bits + int d_headerbytelen_cnt; // how many so far + + unsigned char *d_bytes_out; // hold the current bytes produced by the demapper + + unsigned int d_occupied_carriers; + unsigned int d_byte_offset; + unsigned int d_partial_byte; + + unsigned char d_packet[MAX_PKT_LEN]; // assembled payload + int d_packetlen; // length of packet + 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 + + gr_constellation_sptr d_constell; + 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; + + std::vector<int> d_subcarrier_map; + + protected: + gr_ofdm_frame_sink2(gr_constellation_sptr constell, + gr_msg_queue_sptr target_queue, unsigned int occupied_tones, + float phase_gain, float freq_gain); + + void enter_search(); + void enter_have_sync(); + void enter_have_header(); + + bool header_ok() + { + // confirm that two copies of header info are identical + return ((d_header >> 16) ^ (d_header & 0xffff)) == 0; + } + + unsigned char slicer(const gr_complex x); + unsigned int demapper(const gr_complex *in, + unsigned char *out); + + public: + ~gr_ofdm_frame_sink2(); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; + +#endif /* INCLUDED_GR_OFDM_FRAME_SINK2_H */ diff --git a/gnuradio-core/src/lib/general/gr_ofdm_frame_sink2.i b/gnuradio-core/src/lib/general/gr_ofdm_frame_sink2.i new file mode 100644 index 000000000..24d4b32bd --- /dev/null +++ b/gnuradio-core/src/lib/general/gr_ofdm_frame_sink2.i @@ -0,0 +1,39 @@ +/* -*- 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,ofdm_frame_sink2); + +gr_ofdm_frame_sink2_sptr +gr_make_ofdm_frame_sink2(gr_constellation_sptr constell, + 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_sink2 : public gr_sync_block +{ + protected: + gr_ofdm_frame_sink2(gr_constellation_sptr constell, + gr_msg_queue_sptr target_queue, unsigned int occupied_tones, + float phase_gain, float freq_gain); + + public: + ~gr_ofdm_frame_sink2(); +}; diff --git a/gnuradio-core/src/lib/swig/Makefile.am b/gnuradio-core/src/lib/swig/Makefile.am index f8e7640ae..d304a2123 100644 --- a/gnuradio-core/src/lib/swig/Makefile.am +++ b/gnuradio-core/src/lib/swig/Makefile.am @@ -45,7 +45,7 @@ TOP_SWIG_IFILES = \ swiginclude_HEADERS = \ gnuradio.i \ gr_swig_block_magic.i \ - gr_shared_ptr.i + gr_shared_ptr.i # SWIG headers that get installed in ${prefix}/include/gnuradio/swig/... nobase_swiginclude_HEADERS = \ diff --git a/gnuradio-core/src/lib/swig/Makefile.swig.gen b/gnuradio-core/src/lib/swig/Makefile.swig.gen index 0c3247565..cede68817 100644 --- a/gnuradio-core/src/lib/swig/Makefile.swig.gen +++ b/gnuradio-core/src/lib/swig/Makefile.swig.gen @@ -105,7 +105,7 @@ _gnuradio_core_runtime_la_CXXFLAGS = \ $(gnuradio_core_runtime_la_swig_cxxflags) python/gnuradio_core_runtime.cc: gnuradio_core_runtime.py -gnuradio_core_runtime.py: gnuradio_core_runtime.i +gnuradio_core_runtime.py: gnuradio_core_runtime.i # Include the python dependencies for this file -include python/gnuradio_core_runtime.d @@ -250,7 +250,7 @@ _gnuradio_core_general_la_CXXFLAGS = \ $(gnuradio_core_general_la_swig_cxxflags) python/gnuradio_core_general.cc: gnuradio_core_general.py -gnuradio_core_general.py: gnuradio_core_general.i +gnuradio_core_general.py: gnuradio_core_general.i # Include the python dependencies for this file -include python/gnuradio_core_general.d @@ -395,7 +395,7 @@ _gnuradio_core_gengen_la_CXXFLAGS = \ $(gnuradio_core_gengen_la_swig_cxxflags) python/gnuradio_core_gengen.cc: gnuradio_core_gengen.py -gnuradio_core_gengen.py: gnuradio_core_gengen.i +gnuradio_core_gengen.py: gnuradio_core_gengen.i # Include the python dependencies for this file -include python/gnuradio_core_gengen.d @@ -540,7 +540,7 @@ _gnuradio_core_filter_la_CXXFLAGS = \ $(gnuradio_core_filter_la_swig_cxxflags) python/gnuradio_core_filter.cc: gnuradio_core_filter.py -gnuradio_core_filter.py: gnuradio_core_filter.i +gnuradio_core_filter.py: gnuradio_core_filter.i # Include the python dependencies for this file -include python/gnuradio_core_filter.d @@ -685,7 +685,7 @@ _gnuradio_core_io_la_CXXFLAGS = \ $(gnuradio_core_io_la_swig_cxxflags) python/gnuradio_core_io.cc: gnuradio_core_io.py -gnuradio_core_io.py: gnuradio_core_io.i +gnuradio_core_io.py: gnuradio_core_io.i # Include the python dependencies for this file -include python/gnuradio_core_io.d @@ -830,7 +830,7 @@ _gnuradio_core_hier_la_CXXFLAGS = \ $(gnuradio_core_hier_la_swig_cxxflags) python/gnuradio_core_hier.cc: gnuradio_core_hier.py -gnuradio_core_hier.py: gnuradio_core_hier.i +gnuradio_core_hier.py: gnuradio_core_hier.i # Include the python dependencies for this file -include python/gnuradio_core_hier.d diff --git a/gnuradio-core/src/lib/swig/gnuradio.i b/gnuradio-core/src/lib/swig/gnuradio.i index 1856d5007..ba8357c40 100644 --- a/gnuradio-core/src/lib/swig/gnuradio.i +++ b/gnuradio-core/src/lib/swig/gnuradio.i @@ -35,7 +35,7 @@ #include <complex> %} -%feature("autodoc","1"); +// %feature("autodoc","1"); #ifdef SWIGGUILE // Export constants and enums as scheme variables, not functions. diff --git a/gnuradio-core/src/python/gnuradio/Makefile.am b/gnuradio-core/src/python/gnuradio/Makefile.am index a3f3518de..55a2ba1df 100644 --- a/gnuradio-core/src/python/gnuradio/Makefile.am +++ b/gnuradio-core/src/python/gnuradio/Makefile.am @@ -22,7 +22,7 @@ include $(top_srcdir)/Makefile.common if PYTHON -SUBDIRS = gr gru gruimpl blks2 blks2impl vocoder +SUBDIRS = gr gru gruimpl blks2 blks2impl vocoder utils grpython_PYTHON = \ __init__.py \ diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/Makefile.am b/gnuradio-core/src/python/gnuradio/blks2impl/Makefile.am index 7b24fb69d..6a2e7d5f7 100644 --- a/gnuradio-core/src/python/gnuradio/blks2impl/Makefile.am +++ b/gnuradio-core/src/python/gnuradio/blks2impl/Makefile.am @@ -29,6 +29,7 @@ grblkspythondir = $(grpythondir)/blks2impl grblkspython_PYTHON = \ __init__.py \ am_demod.py \ + bpsk.py \ channel_model.py \ dbpsk.py \ dbpsk2.py \ @@ -38,6 +39,7 @@ grblkspython_PYTHON = \ filterbank.py \ fm_demod.py \ fm_emph.py \ + generic_mod_demod.py \ generic_usrp.py \ gmsk.py \ cpm.py \ @@ -56,11 +58,9 @@ grblkspython_PYTHON = \ pfb_interpolator.py \ pkt.py \ psk.py \ + psk2.py \ qam.py \ - qam8.py \ - qam16.py \ - qam64.py \ - qam256.py \ + qpsk.py \ rational_resampler.py \ standard_squelch.py \ stream_to_vector_decimator.py \ diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/bpsk.py b/gnuradio-core/src/python/gnuradio/blks2impl/bpsk.py new file mode 100644 index 000000000..222178abb --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blks2impl/bpsk.py @@ -0,0 +1,98 @@ +# +# Copyright 2005,2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +""" +BPSK modulation and demodulation. +""" + +from math import pi, log +from cmath import exp + +from gnuradio import gr, modulation_utils2 +from gnuradio.blks2impl.generic_mod_demod import generic_mod, generic_demod + +# Default number of points in constellation. +_def_constellation_points = 2 +# Whether differential coding is used. +_def_differential = True + +# ///////////////////////////////////////////////////////////////////////////// +# BPSK constellation +# ///////////////////////////////////////////////////////////////////////////// + +def bpsk_constellation(m=_def_constellation_points): + if m != _def_constellation_points: + raise ValueError("BPSK can only have 2 constellation points.") + return gr.constellation_bpsk() + +# ///////////////////////////////////////////////////////////////////////////// +# BPSK modulator +# ///////////////////////////////////////////////////////////////////////////// + +class bpsk_mod(generic_mod): + + def __init__(self, constellation_points=_def_constellation_points, + *args, **kwargs): + + """ + Hierarchical block for RRC-filtered BPSK modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + See generic_mod block for list of parameters. + """ + + constellation = gr.constellation_bpsk() + if constellation_points != 2: + raise ValueError('Number of constellation points must be 2 for BPSK.') + super(bpsk_mod, self).__init__(constellation, *args, **kwargs) + +# ///////////////////////////////////////////////////////////////////////////// +# BPSK demodulator +# +# ///////////////////////////////////////////////////////////////////////////// + +class bpsk_demod(generic_demod): + + def __init__(self, constellation_points=_def_constellation_points, + *args, **kwargs): + + """ + Hierarchical block for RRC-filtered BPSK modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + See generic_demod block for list of parameters. + """ + + constellation = gr.constellation_bpsk() + if constellation_points != 2: + raise ValueError('Number of constellation points must be 2 for BPSK.') + super(bpsk_demod, self).__init__(constellation, *args, **kwargs) + +# +# Add these to the mod/demod registry +# +modulation_utils2.add_type_1_mod('bpsk', bpsk_mod) +modulation_utils2.add_type_1_demod('bpsk', bpsk_demod) +modulation_utils2.add_type_1_constellation('bpsk', bpsk_constellation) diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/generic_mod_demod.py b/gnuradio-core/src/python/gnuradio/blks2impl/generic_mod_demod.py new file mode 100644 index 000000000..b88ff72a0 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blks2impl/generic_mod_demod.py @@ -0,0 +1,395 @@ +# +# Copyright 2005,2006,2007,2009 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. +# + +# See gnuradio-examples/python/digital for examples + +""" +Generic modulation and demodulation. +""" + +from gnuradio import gr +from gnuradio.modulation_utils2 import extract_kwargs_from_options_for_class +from gnuradio.utils import mod_codes + +# default values (used in __init__ and add_options) +_def_samples_per_symbol = 2 +_def_excess_bw = 0.35 +_def_verbose = False +_def_log = False + +# Frequency correction +_def_freq_alpha = 0.010 +# Symbol timing recovery +_def_timing_alpha = 0.100 +_def_timing_beta = 0.010 +_def_timing_max_dev = 1.5 +# Fine frequency / Phase correction +_def_phase_alpha = 0.1 +# Number of points in constellation +_def_constellation_points = 16 +# Whether differential coding is used. +_def_differential = True + +def add_common_options(parser): + """ + Sets options common to both modulator and demodulator. + """ + parser.add_option("-p", "--constellation-points", type="int", default=_def_constellation_points, + help="set the number of constellation points (must be a power of 2 (power of 4 for QAM) [default=%default]") + parser.add_option("", "--differential", action="store_true", dest="differential", default=True, + help="use differential encoding [default=%default]") + parser.add_option("", "--not-differential", action="store_false", dest="differential", + help="do not use differential encoding [default=%default]") + parser.add_option("", "--mod-code", type="choice", choices=mod_codes.codes, + default=mod_codes.NO_CODE, + help="Select modulation code from: %s [default=%%default]" + % (', '.join(mod_codes.codes),)) + parser.add_option("", "--excess-bw", type="float", default=_def_excess_bw, + help="set RRC excess bandwith factor [default=%default]") + + +# ///////////////////////////////////////////////////////////////////////////// +# Generic modulator +# ///////////////////////////////////////////////////////////////////////////// + +class generic_mod(gr.hier_block2): + + def __init__(self, constellation, + differential=_def_differential, + samples_per_symbol=_def_samples_per_symbol, + excess_bw=_def_excess_bw, + verbose=_def_verbose, + log=_def_log): + """ + Hierarchical block for RRC-filtered differential generic modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + @param constellation: determines the modulation type + @type constellation: gnuradio.gr.gr_constellation + @param samples_per_symbol: samples per baud >= 2 + @type samples_per_symbol: integer + @param excess_bw: Root-raised cosine filter excess bandwidth + @type excess_bw: float + @param verbose: Print information about modulator? + @type verbose: bool + @param log: Log modulation data to files? + @type log: bool + """ + + gr.hier_block2.__init__(self, "generic_mod", + gr.io_signature(1, 1, gr.sizeof_char), # Input signature + gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature + + self._constellation = constellation.base() + self._samples_per_symbol = samples_per_symbol + self._excess_bw = excess_bw + self._differential = differential + + if not isinstance(self._samples_per_symbol, int) or self._samples_per_symbol < 2: + raise TypeError, ("sbp must be an integer >= 2, is %d" % self._samples_per_symbol) + + ntaps = 11 * self._samples_per_symbol + + arity = pow(2,self.bits_per_symbol()) + + # turn bytes into k-bit vectors + self.bytes2chunks = \ + gr.packed_to_unpacked_bb(self.bits_per_symbol(), gr.GR_MSB_FIRST) + + if self._constellation.apply_pre_diff_code(): + self.symbol_mapper = gr.map_bb(self._constellation.pre_diff_code()) + + if differential: + self.diffenc = gr.diff_encoder_bb(arity) + + self.chunks2symbols = gr.chunks_to_symbols_bc(self._constellation.points()) + + # pulse shaping filter + self.rrc_taps = gr.firdes.root_raised_cosine( + self._samples_per_symbol, # gain (samples_per_symbol since we're + # interpolating by samples_per_symbol) + self._samples_per_symbol, # sampling rate + 1.0, # symbol rate + self._excess_bw, # excess bandwidth (roll-off factor) + ntaps) + self.rrc_filter = gr.interp_fir_filter_ccf(self._samples_per_symbol, + self.rrc_taps) + + # Connect + blocks = [self, self.bytes2chunks] + if self._constellation.apply_pre_diff_code(): + blocks.append(self.symbol_mapper) + if differential: + blocks.append(self.diffenc) + blocks += [self.chunks2symbols, self.rrc_filter, self] + self.connect(*blocks) + + if verbose: + self._print_verbage() + + if log: + self._setup_logging() + + + def samples_per_symbol(self): + return self._samples_per_symbol + + def bits_per_symbol(self): # static method that's also callable on an instance + return self._constellation.bits_per_symbol() + + def add_options(parser): + """ + Adds generic modulation options to the standard parser + """ + add_common_options(parser) + add_options=staticmethod(add_options) + + def extract_kwargs_from_options(cls, options): + """ + Given command line options, create dictionary suitable for passing to __init__ + """ + return extract_kwargs_from_options_for_class(cls, options) + extract_kwargs_from_options=classmethod(extract_kwargs_from_options) + + + def _print_verbage(self): + print "\nModulator:" + print "bits per symbol: %d" % self.bits_per_symbol() + print "RRC roll-off factor: %.2f" % self._excess_bw + + def _setup_logging(self): + print "Modulation logging turned on." + self.connect(self.bytes2chunks, + gr.file_sink(gr.sizeof_char, "tx_bytes2chunks.dat")) + if self._constellation.apply_pre_diff_code(): + self.connect(self.symbol_mapper, + gr.file_sink(gr.sizeof_char, "tx_symbol_mapper.dat")) + if self._differential: + self.connect(self.diffenc, + gr.file_sink(gr.sizeof_char, "tx_diffenc.dat")) + self.connect(self.chunks2symbols, + gr.file_sink(gr.sizeof_gr_complex, "tx_chunks2symbols.dat")) + self.connect(self.rrc_filter, + gr.file_sink(gr.sizeof_gr_complex, "tx_rrc_filter.dat")) + + +# ///////////////////////////////////////////////////////////////////////////// +# Generic demodulator +# +# Differentially coherent detection of differentially encoded generically +# modulated signal. +# ///////////////////////////////////////////////////////////////////////////// + +class generic_demod(gr.hier_block2): + + def __init__(self, constellation, + samples_per_symbol=_def_samples_per_symbol, + differential=_def_differential, + excess_bw=_def_excess_bw, + freq_alpha=_def_freq_alpha, + timing_alpha=_def_timing_alpha, + timing_max_dev=_def_timing_max_dev, + phase_alpha=_def_phase_alpha, + verbose=_def_verbose, + log=_def_log): + """ + Hierarchical block for RRC-filtered differential generic demodulation. + + The input is the complex modulated signal at baseband. + The output is a stream of bits packed 1 bit per byte (LSB) + + @param constellation: determines the modulation type + @type constellation: gnuradio.gr.gr_constellation + @param samples_per_symbol: samples per symbol >= 2 + @type samples_per_symbol: float + @param excess_bw: Root-raised cosine filter excess bandwidth + @type excess_bw: float + @param freq_alpha: loop filter gain for frequency recovery + @type freq_alpha: float + @param timing_alpha: loop alpha gain for timing recovery + @type timing_alpha: float + @param timing_max_dev: timing loop maximum rate deviations + @type timing_max_dev: float + @param phase_alpha: loop filter gain in phase loop + @type phase_alphas: float + @param verbose: Print information about modulator? + @type verbose: bool + @param debug: Print modualtion data to files? + @type debug: bool + """ + + gr.hier_block2.__init__(self, "generic_demod", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + gr.io_signature(1, 1, gr.sizeof_char)) # Output signature + + self._constellation = constellation.base() + self._samples_per_symbol = samples_per_symbol + self._excess_bw = excess_bw + self._phase_alpha = phase_alpha + self._freq_alpha = freq_alpha + self._freq_beta = 0.10*self._freq_alpha + self._timing_alpha = timing_alpha + self._timing_beta = _def_timing_beta + self._timing_max_dev=timing_max_dev + self._differential = differential + + if not isinstance(self._samples_per_symbol, int) or self._samples_per_symbol < 2: + raise TypeError, ("sbp must be an integer >= 2, is %d" % self._samples_per_symbol) + + arity = pow(2,self.bits_per_symbol()) + + # Automatic gain control + self.agc = gr.agc2_cc(0.6e-1, 1e-3, 1, 1, 100) + + # Frequency correction + self.freq_recov = gr.fll_band_edge_cc(self._samples_per_symbol, self._excess_bw, + 11*int(self._samples_per_symbol), + self._freq_alpha, self._freq_beta) + + # symbol timing recovery with RRC data filter + nfilts = 32 + ntaps = 11 * int(self._samples_per_symbol*nfilts) + taps = gr.firdes.root_raised_cosine(nfilts, nfilts, + 1.0/float(self._samples_per_symbol), + self._excess_bw, ntaps) + self.time_recov = gr.pfb_clock_sync_ccf(self._samples_per_symbol, + self._timing_alpha, + taps, nfilts, nfilts/2, self._timing_max_dev) + self.time_recov.set_beta(self._timing_beta) + + #self._phase_beta = 0.25 * self._phase_alpha * self._phase_alpha + self._phase_beta = 0.25 * self._phase_alpha * self._phase_alpha + fmin = -0.25 + fmax = 0.25 + + self.receiver = gr.constellation_receiver_cb( + self._constellation, + self._phase_alpha, self._phase_beta, + fmin, fmax) + + # Do differential decoding based on phase change of symbols + if differential: + self.diffdec = gr.diff_decoder_bb(arity) + + if self._constellation.apply_pre_diff_code(): + self.symbol_mapper = gr.map_bb( + mod_codes.invert_code(self._constellation.pre_diff_code())) + + # unpack the k bit vector into a stream of bits + self.unpack = gr.unpack_k_bits_bb(self.bits_per_symbol()) + + if verbose: + self._print_verbage() + + if log: + self._setup_logging() + + # Connect and Initialize base class + blocks = [self, self.agc, self.freq_recov, self.time_recov, self.receiver] + if differential: + blocks.append(self.diffdec) + if self._constellation.apply_pre_diff_code(): + blocks.append(self.symbol_mapper) + blocks += [self.unpack, self] + self.connect(*blocks) + + def samples_per_symbol(self): + return self._samples_per_symbol + + def bits_per_symbol(self): # staticmethod that's also callable on an instance + return self._constellation.bits_per_symbol() + + def _print_verbage(self): + print "\nDemodulator:" + print "bits per symbol: %d" % self.bits_per_symbol() + print "RRC roll-off factor: %.2f" % self._excess_bw + print "FLL gain: %.2e" % self._freq_alpha + print "Timing alpha gain: %.2e" % self._timing_alpha + print "Timing beta gain: %.2e" % self._timing_beta + print "Timing max dev: %.2f" % self._timing_max_dev + print "Phase track alpha: %.2e" % self._phase_alpha + print "Phase track beta: %.2e" % self._phase_beta + + def _setup_logging(self): + print "Modulation logging turned on." + self.connect(self.agc, + gr.file_sink(gr.sizeof_gr_complex, "rx_agc.dat")) + self.connect((self.freq_recov, 0), + gr.file_sink(gr.sizeof_gr_complex, "rx_freq_recov.dat")) + self.connect((self.freq_recov, 1), + gr.file_sink(gr.sizeof_float, "rx_freq_recov_freq.dat")) + self.connect((self.freq_recov, 2), + gr.file_sink(gr.sizeof_float, "rx_freq_recov_phase.dat")) + self.connect((self.freq_recov, 3), + gr.file_sink(gr.sizeof_gr_complex, "rx_freq_recov_error.dat")) + self.connect((self.time_recov, 0), + gr.file_sink(gr.sizeof_gr_complex, "rx_time_recov.dat")) + self.connect((self.time_recov, 1), + gr.file_sink(gr.sizeof_float, "rx_time_recov_error.dat")) + self.connect((self.time_recov, 2), + gr.file_sink(gr.sizeof_float, "rx_time_recov_rate.dat")) + self.connect((self.time_recov, 3), + gr.file_sink(gr.sizeof_float, "rx_time_recov_phase.dat")) + self.connect((self.receiver, 0), + gr.file_sink(gr.sizeof_char, "rx_receiver.dat")) + self.connect((self.receiver, 1), + gr.file_sink(gr.sizeof_float, "rx_receiver_error.dat")) + self.connect((self.receiver, 2), + gr.file_sink(gr.sizeof_float, "rx_receiver_phase.dat")) + self.connect((self.receiver, 3), + gr.file_sink(gr.sizeof_float, "rx_receiver_freq.dat")) + if self._differential: + self.connect(self.diffdec, + gr.file_sink(gr.sizeof_char, "rx_diffdec.dat")) + if self._constellation.apply_pre_diff_code(): + self.connect(self.symbol_mapper, + gr.file_sink(gr.sizeof_char, "rx_symbol_mapper.dat")) + self.connect(self.unpack, + gr.file_sink(gr.sizeof_char, "rx_unpack.dat")) + + def add_options(parser): + """ + Adds generic demodulation options to the standard parser + """ + # Add options shared with modulator. + add_common_options(parser) + # Add options specific to demodulator. + parser.add_option("", "--freq-alpha", type="float", default=_def_freq_alpha, + help="set frequency lock loop alpha gain value [default=%default]") + parser.add_option("", "--phase-alpha", type="float", default=_def_phase_alpha, + help="set phase tracking loop alpha value [default=%default]") + parser.add_option("", "--timing-alpha", type="float", default=_def_timing_alpha, + help="set timing symbol sync loop gain alpha value [default=%default]") + parser.add_option("", "--timing-beta", type="float", default=_def_timing_beta, + help="set timing symbol sync loop gain beta value [default=%default]") + parser.add_option("", "--timing-max-dev", type="float", default=_def_timing_max_dev, + help="set timing symbol sync loop maximum deviation [default=%default]") + add_options=staticmethod(add_options) + + def extract_kwargs_from_options(cls, options): + """ + Given command line options, create dictionary suitable for passing to __init__ + """ + return extract_kwargs_from_options_for_class(cls, options) + extract_kwargs_from_options=classmethod(extract_kwargs_from_options) + diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/ofdm.py b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm.py index 2663f7cf8..3b1cd12ac 100644 --- a/gnuradio-core/src/python/gnuradio/blks2impl/ofdm.py +++ b/gnuradio-core/src/python/gnuradio/blks2impl/ofdm.py @@ -21,12 +21,26 @@ # import math -from gnuradio import gr, ofdm_packet_utils +from gnuradio import gr, ofdm_packet_utils, modulation_utils2 import gnuradio.gr.gr_threading as _threading import psk, qam from gnuradio.blks2impl.ofdm_receiver import ofdm_receiver +def _add_common_options(normal, expert): + """ + Adds OFDM-specific options to the Options Parser that are common + both to the modulator and demodulator. + """ + mods_list = ", ".join(modulation_utils2.type_1_constellations().keys()) + normal.add_option("-m", "--modulation", type="string", default="psk", + help="set modulation type (" + mods_list + ") [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, + help="set the number of occupied FFT bins [default=%default]") + expert.add_option("", "--cp-length", type="intx", default=128, + help="set the number of bits in the cyclic prefix [default=%default]") # ///////////////////////////////////////////////////////////////////////////// # mod/demod with packets as i/o @@ -61,6 +75,8 @@ class ofdm_mod(gr.hier_block2): self._fft_length = options.fft_length self._occupied_tones = options.occupied_tones self._cp_length = options.cp_length + + arity = options.constellation_points win = [] #[1 for i in range(self._fft_length)] @@ -82,19 +98,9 @@ class ofdm_mod(gr.hier_block2): symbol_length = options.fft_length + options.cp_length - mods = {"bpsk": 2, "qpsk": 4, "8psk": 8, "qam8": 8, "qam16": 16, "qam64": 64, "qam256": 256} - arity = mods[self._modulation] - - rot = 1 - if self._modulation == "qpsk": - rot = (0.707+0.707j) - - if(self._modulation.find("psk") >= 0): - rotated_const = map(lambda pt: pt * rot, psk.gray_constellation[arity]) - elif(self._modulation.find("qam") >= 0): - rotated_const = map(lambda pt: pt * rot, qam.constellation[arity]) - #print rotated_const - self._pkt_input = gr.ofdm_mapper_bcv(rotated_const, msgq_limit, + const = modulation_utils2.type_1_constellations()[self._modulation](arity).points() + + self._pkt_input = gr.ofdm_mapper_bcv(const, msgq_limit, options.occupied_tones, options.fft_length) self.preambles = gr.ofdm_insert_preamble(self._fft_length, padded_preambles) @@ -140,14 +146,10 @@ class ofdm_mod(gr.hier_block2): """ Adds OFDM-specific options to the Options Parser """ - normal.add_option("-m", "--modulation", type="string", default="bpsk", - 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, - help="set the number of occupied FFT bins [default=%default]") - expert.add_option("", "--cp-length", type="intx", default=128, - help="set the number of bits in the cyclic prefix [default=%default]") + _add_common_options(normal, expert) + for mod in modulation_utils2.type_1_mods().values(): + mod.add_options(expert) + # Make a static method to call before instantiation add_options = staticmethod(add_options) @@ -196,6 +198,9 @@ class ofdm_demod(gr.hier_block2): self._cp_length = options.cp_length self._snr = options.snr + arity = options.constellation_points + print("con points is %s" % options.constellation_points) + # Use freq domain to get doubled-up known symbol for correlation in time domain zeros_on_left = int(math.ceil((self._fft_length - self._occupied_tones)/2.0)) ksfreq = known_symbols_4512_3[0:self._occupied_tones] @@ -211,22 +216,11 @@ class ofdm_demod(gr.hier_block2): self._occupied_tones, self._snr, preambles, options.log) - mods = {"bpsk": 2, "qpsk": 4, "8psk": 8, "qam8": 8, "qam16": 16, "qam64": 64, "qam256": 256} - arity = mods[self._modulation] - - rot = 1 - if self._modulation == "qpsk": - rot = (0.707+0.707j) - - if(self._modulation.find("psk") >= 0): - rotated_const = map(lambda pt: pt * rot, psk.gray_constellation[arity]) - elif(self._modulation.find("qam") >= 0): - rotated_const = map(lambda pt: pt * rot, qam.constellation[arity]) - #print rotated_const + constell = modulation_utils2.type_1_constellations()[self._modulation](arity) phgain = 0.25 frgain = phgain*phgain / 4.0 - self.ofdm_demod = gr.ofdm_frame_sink(rotated_const, range(arity), + self.ofdm_demod = gr.ofdm_frame_sink2(constell.base(), self._rcvd_pktq, self._occupied_tones, phgain, frgain) @@ -253,14 +247,9 @@ class ofdm_demod(gr.hier_block2): """ 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]") - 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, - help="set the number of occupied FFT bins [default=%default]") - expert.add_option("", "--cp-length", type="intx", default=128, - help="set the number of bits in the cyclic prefix [default=%default]") + _add_common_options(normal, expert) + for mod in modulation_utils2.type_1_mods().values(): + mod.add_options(expert) # Make a static method to call before instantiation add_options = staticmethod(add_options) diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/pkt.py b/gnuradio-core/src/python/gnuradio/blks2impl/pkt.py index 908437ef2..aa720d1a5 100644 --- a/gnuradio-core/src/python/gnuradio/blks2impl/pkt.py +++ b/gnuradio-core/src/python/gnuradio/blks2impl/pkt.py @@ -34,7 +34,8 @@ class mod_pkts(gr.hier_block2): Send packets by calling send_pkt """ - def __init__(self, modulator, access_code=None, msgq_limit=2, pad_for_usrp=True, use_whitener_offset=False): + def __init__(self, modulator, access_code=None, msgq_limit=2, pad_for_usrp=True, use_whitener_offset=False, + modulate=True): """ Hierarchical block for sending packets @@ -49,13 +50,18 @@ class mod_pkts(gr.hier_block2): @type msgq_limit: int @param pad_for_usrp: If true, packets are padded such that they end up a multiple of 128 samples @param use_whitener_offset: If true, start of whitener XOR string is incremented each packet + @param modulate: If false, no modulation will be performed. See gmsk_mod for remaining parameters """ + if modulate: + output_size = gr.sizeof_gr_complex + else: + output_size = gr.sizeof_char gr.hier_block2.__init__(self, "mod_pkts", gr.io_signature(0, 0, 0), # Input signature - gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature + gr.io_signature(1, 1, output_size)) # Output signature self._modulator = modulator self._pad_for_usrp = pad_for_usrp @@ -70,7 +76,10 @@ class mod_pkts(gr.hier_block2): # accepts messages from the outside world self._pkt_input = gr.message_source(gr.sizeof_char, msgq_limit) - self.connect(self._pkt_input, self._modulator, self) + if modulate: + self.connect(self._pkt_input, self._modulator, self) + else: + self.connect(self._pkt_input, self) def send_pkt(self, payload='', eof=False): """ @@ -106,13 +115,15 @@ class demod_pkts(gr.hier_block2): app via the callback. """ - def __init__(self, demodulator, access_code=None, callback=None, threshold=-1): + def __init__(self, demodulator, access_code=None, callback=None, threshold=-1, demodulate=True): """ Hierarchical block for demodulating and deframing packets. The input is the complex modulated signal at baseband. Demodulated packets are sent to the handler. + If demodulator is None it is assumed the input is already demodulated. + @param demodulator: instance of demodulator class (gr_block or hier_block2) @type demodulator: complex baseband in @param access_code: AKA sync vector @@ -123,9 +134,14 @@ class demod_pkts(gr.hier_block2): @type threshold: int """ + if demodulator is not None: + input_size = gr.sizeof_gr_complex + else: + input_size = gr.sizeof_char + gr.hier_block2.__init__(self, "demod_pkts", - gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature - gr.io_signature(0, 0, 0)) # Output signature + gr.io_signature(1, 1, input_size), # Input signature + gr.io_signature(0, 0, 0)) # Output signature self._demodulator = demodulator if access_code is None: @@ -141,9 +157,13 @@ class demod_pkts(gr.hier_block2): self.correlator = gr.correlate_access_code_bb(access_code, threshold) self.framer_sink = gr.framer_sink_1(self._rcvd_pktq) - self.connect(self, self._demodulator, self.correlator, self.framer_sink) + if self._demodulator is not None: + self.connect(self, self._demodulator, self.correlator, self.framer_sink) + else: + self.connect(self, self.correlator, self.framer_sink) - self._watcher = _queue_watcher_thread(self._rcvd_pktq, callback) + if callback is not None: + self._watcher = _queue_watcher_thread(self._rcvd_pktq, callback) class _queue_watcher_thread(_threading.Thread): diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/psk2.py b/gnuradio-core/src/python/gnuradio/blks2impl/psk2.py new file mode 100644 index 000000000..95f25e75a --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blks2impl/psk2.py @@ -0,0 +1,121 @@ +# +# Copyright 2005,2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +""" +PSK modulation and demodulation. +""" + +from math import pi, log +from cmath import exp + +from gnuradio import gr, modulation_utils2 +from gnuradio.blks2impl.generic_mod_demod import generic_mod, generic_demod +from gnuradio.utils import mod_codes, gray_code + +# Default number of points in constellation. +_def_constellation_points = 4 +# The default encoding (e.g. gray-code, set-partition) +_def_mod_code = mod_codes.GRAY_CODE + +def create_encodings(mod_code, arity): + post_diff_code = None + if mod_code not in mod_codes.codes: + raise ValueError('That modulation code does not exist.') + if mod_code == mod_codes.GRAY_CODE: + pre_diff_code = gray_code.gray_code(arity) + elif mod_code == mod_codes.SET_PARTITION_CODE: + pre_diff_code = set_partition_code.set_partition_code(arity) + elif mod_code == mod_codes.NO_CODE: + pre_diff_code = [] + else: + raise ValueError('That modulation code is not implemented for this constellation.') + return (pre_diff_code, post_diff_code) + +# ///////////////////////////////////////////////////////////////////////////// +# PSK constellation +# ///////////////////////////////////////////////////////////////////////////// + +def psk_constellation(m=_def_constellation_points, mod_code=_def_mod_code): + """ + Creates a PSK constellation object. + """ + k = log(m) / log(2.0) + if (k != int(k)): + raise StandardError('Number of constellation points must be a power of two.') + points = [exp(2*pi*(0+1j)*i/m) for i in range(0,m)] + pre_diff_code, post_diff_code = create_encodings(mod_code, m) + if post_diff_code is not None: + inverse_post_diff_code = mod_codes.invert_code(post_diff_code) + points = [points[x] for x in inverse_post_diff_code] + constellation = gr.constellation_psk(points, pre_diff_code, m) + return constellation + +# ///////////////////////////////////////////////////////////////////////////// +# PSK modulator +# ///////////////////////////////////////////////////////////////////////////// + +class psk_mod(generic_mod): + + def __init__(self, constellation_points=_def_constellation_points, + mod_code=_def_mod_code, + *args, **kwargs): + + """ + Hierarchical block for RRC-filtered PSK modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + See generic_mod block for list of parameters. + """ + + constellation = psk_constellation(constellation_points, mod_code) + super(psk_mod, self).__init__(constellation, *args, **kwargs) + +# ///////////////////////////////////////////////////////////////////////////// +# PSK demodulator +# +# ///////////////////////////////////////////////////////////////////////////// + +class psk_demod(generic_demod): + + def __init__(self, constellation_points=_def_constellation_points, + mod_code=_def_mod_code, + *args, **kwargs): + + """ + Hierarchical block for RRC-filtered PSK modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + See generic_demod block for list of parameters. + """ + + constellation = psk_constellation(constellation_points, mod_code) + super(psk_demod, self).__init__(constellation, *args, **kwargs) + +# +# Add these to the mod/demod registry +# +modulation_utils2.add_type_1_mod('psk', psk_mod) +modulation_utils2.add_type_1_demod('psk', psk_demod) +modulation_utils2.add_type_1_constellation('psk', psk_constellation) diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/qam.py b/gnuradio-core/src/python/gnuradio/blks2impl/qam.py index 22b1e1dab..bdd27e9bb 100644 --- a/gnuradio-core/src/python/gnuradio/blks2impl/qam.py +++ b/gnuradio-core/src/python/gnuradio/blks2impl/qam.py @@ -19,95 +19,208 @@ # Boston, MA 02110-1301, USA. # -from math import pi, sqrt -import math +""" +QAM modulation and demodulation. +""" -# These constellations are generated for Gray coding when symbos [1, ..., m] are used -# Mapping to Gray coding is therefore unnecessary +from math import pi, sqrt, log -def make_constellation(m): - # number of bits/symbol (log2(M)) - k = int(math.log10(m) / math.log10(2.0)) +from gnuradio import gr, modulation_utils2 +from gnuradio.blks2impl.generic_mod_demod import generic_mod, generic_demod +from gnuradio.utils.gray_code import gray_code +from gnuradio.utils import mod_codes - coeff = 1 +# Default number of points in constellation. +_def_constellation_points = 16 +# Whether the quadrant bits are coded differentially. +_def_differential = True +# Whether gray coding is used. If differential is True then gray +# coding is used within but not between each quadrant. +_def_mod_code = mod_codes.NO_CODE + +def is_power_of_four(x): + v = log(x)/log(4) + return int(v) == v + +def get_bit(x, n): + """ Get the n'th bit of integer x (from little end).""" + return (x&(0x01 << n)) >> n + +def get_bits(x, n, k): + """ Get the k bits of integer x starting at bit n(from little end).""" + # Remove the n smallest bits + v = x >> n + # Remove all bits bigger than n+k-1 + return v % pow(2, k) + +def make_differential_constellation(m, gray_coded): + """ + Create a constellation with m possible symbols where m must be + a power of 4. + + Points are laid out in a square grid. + + Bits referring to the quadrant are differentilly encoded, + remaining bits are gray coded. + + """ + sqrtm = pow(m, 0.5) + if (not isinstance(m, int) or m < 4 or not is_power_of_four(m)): + raise ValueError("m must be a power of 4 integer.") + # Each symbol holds k bits. + k = int(log(m) / log(2.0)) + # First create a constellation for one quadrant containing m/4 points. + # The quadrant has 'side' points along each side of a quadrant. + side = int(sqrtm/2) + if gray_coded: + # Number rows and columns using gray codes. + gcs = gray_code(side) + # Get inverse gray codes. + i_gcs = dict([(v, key) for key, v in enumerate(gcs)]) + else: + i_gcs = dict([(i, i) for i in range(0, side)]) + # The distance between points is found. + step = 1/(side-0.5) + + gc_to_x = [(i_gcs[gc]+0.5)*step for gc in range(0, side)] + + # Takes the (x, y) location of the point with the quadrant along + # with the quadrant number. (x, y) are integers referring to which + # point within the quadrant it is. + # A complex number representing this location of this point is returned. + def get_c(gc_x, gc_y, quad): + if quad == 0: + return complex(gc_to_x[gc_x], gc_to_x[gc_y]) + if quad == 1: + return complex(-gc_to_x[gc_y], gc_to_x[gc_x]) + if quad == 2: + return complex(-gc_to_x[gc_x], -gc_to_x[gc_y]) + if quad == 3: + return complex(gc_to_x[gc_y], -gc_to_x[gc_x]) + raise StandardError("Impossible!") + + # First two bits determine quadrant. + # Next (k-2)/2 bits determine x position. + # Following (k-2)/2 bits determine y position. + # How x and y relate to real and imag depends on quadrant (see get_c function). + const_map = [] + for i in range(m): + y = get_bits(i, 0, (k-2)/2) + x = get_bits(i, (k-2)/2, (k-2)/2) + quad = get_bits(i, k-2, 2) + const_map.append(get_c(x, y, quad)) + + return const_map + +def make_not_differential_constellation(m, gray_coded): + side = int(pow(m, 0.5)) + if (not isinstance(m, int) or m < 4 or not is_power_of_four(m)): + raise ValueError("m must be a power of 4 integer.") + # Each symbol holds k bits. + k = int(log(m) / log(2.0)) + if gray_coded: + # Number rows and columns using gray codes. + gcs = gray_code(side) + # Get inverse gray codes. + i_gcs = mod_codes.invert_code(gcs) + else: + i_gcs = range(0, side) + # The distance between points is found. + step = 2.0/(side-1) + + gc_to_x = [-1 + i_gcs[gc]*step for gc in range(0, side)] + # First k/2 bits determine x position. + # Following k/2 bits determine y position. const_map = [] for i in range(m): - a = (i&(0x01 << k-1)) >> k-1 - b = (i&(0x01 << k-2)) >> k-2 - bits_i = [((i&(0x01 << k-j-1)) >> k-j-1) for j in range(2, k, 2)] - bits_q = [((i&(0x01 << k-j-1)) >> k-j-1) for j in range(3, k, 2)] - - ss = 0 - ll = len(bits_i) - for ii in range(ll): - rr = 0 - for jj in range(ll-ii): - rr = abs(bits_i[jj] - rr) - ss += rr*pow(2.0, ii+1) - re = (2*a-1)*(ss+1) - - ss = 0 - ll = len(bits_q) - for ii in range(ll): - rr = 0 - for jj in range(ll-ii): - rr = abs(bits_q[jj] - rr) - ss += rr*pow(2.0, ii+1) - im = (2*b-1)*(ss+1) - - a = max(re, im) - if a > coeff: - coeff = a - const_map.append(complex(re, im)) - - norm_map = [complex(i.real/coeff, i.imag/coeff) for i in const_map] - return norm_map - -# Common definition of constellations for Tx and Rx -constellation = { - 4 : make_constellation(4), # QAM4 (QPSK) - 8 : make_constellation(8), # QAM8 - 16: make_constellation(16), # QAM16 - 64: make_constellation(64), # QAM64 - 256: make_constellation(256) # QAM256 - } - -# ----------------------- -# Do Gray code -# ----------------------- -# binary to gray coding -binary_to_gray = { - 4 : range(4), - 8 : range(8), - 16: range(16), - 64: range(64), - 256: range(256) - } - -# gray to binary -gray_to_binary = { - 4 : range(4), - 8 : range(8), - 16: range(16), - 64: range(64), - 256: range(256) - } - -# ----------------------- -# Don't Gray code -# ----------------------- -# identity mapping -binary_to_ungray = { - 4 : range(4), - 8 : range(8), - 16: range(16), - 64: range(64) - } - -# identity mapping -ungray_to_binary = { - 4 : range(4), - 8 : range(8), - 16: range(16), - 64: range(64) - } + y = gc_to_x[get_bits(i, 0, k/2)] + x = gc_to_x[get_bits(i, k/2, k/2)] + const_map.append(complex(x,y)) + return const_map + +# ///////////////////////////////////////////////////////////////////////////// +# QAM constellation +# ///////////////////////////////////////////////////////////////////////////// + +def qam_constellation(constellation_points=_def_constellation_points, + differential=_def_differential, + mod_code=_def_mod_code): + """ + Creates a QAM constellation object. + """ + if mod_code == mod_codes.GRAY_CODE: + gray_coded = True + elif mod_code == mod_codes.NO_CODE: + gray_coded = False + else: + raise ValueError("Mod code is not implemented for QAM") + if differential: + points = make_differential_constellation(constellation_points, gray_coded) + else: + points = make_not_differential_constellation(constellation_points, gray_coded) + side = int(sqrt(constellation_points)) + width = 2.0/(side-1) + # No pre-diff code + # Should add one so that we can gray-code the quadrant bits too. + pre_diff_code = [] + constellation = gr.constellation_rect(points, pre_diff_code, 4, side, side, width, width) + return constellation + +# ///////////////////////////////////////////////////////////////////////////// +# QAM modulator +# ///////////////////////////////////////////////////////////////////////////// + +class qam_mod(generic_mod): + + def __init__(self, constellation_points=_def_constellation_points, + differential=_def_differential, + mod_code=_def_mod_code, + *args, **kwargs): + + """ + Hierarchical block for RRC-filtered QAM modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + See generic_mod block for list of parameters. + """ + + constellation = qam_constellation(constellation_points, differential, mod_code) + # We take care of the gray coding in the constellation generation so it doesn't + # need to be done in the block. + super(qam_mod, self).__init__(constellation, differential=differential, + *args, **kwargs) + +# ///////////////////////////////////////////////////////////////////////////// +# QAM demodulator +# +# ///////////////////////////////////////////////////////////////////////////// + +class qam_demod(generic_demod): + + def __init__(self, constellation_points=_def_constellation_points, + differential=_def_differential, + mod_code=_def_mod_code, + *args, **kwargs): + + """ + Hierarchical block for RRC-filtered QAM modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + See generic_demod block for list of parameters. + """ + constellation = qam_constellation(constellation_points, differential, mod_code) + # We take care of the gray coding in the constellation generation so it doesn't + # need to be done in the block. + super(qam_demod, self).__init__(constellation, differential=differential, + *args, **kwargs) + +# +# Add these to the mod/demod registry +# +modulation_utils2.add_type_1_mod('qam', qam_mod) +modulation_utils2.add_type_1_demod('qam', qam_demod) +modulation_utils2.add_type_1_constellation('qam', qam_constellation) diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/qam16.py b/gnuradio-core/src/python/gnuradio/blks2impl/qam16.py deleted file mode 100644 index 0bdb9c6fb..000000000 --- a/gnuradio-core/src/python/gnuradio/blks2impl/qam16.py +++ /dev/null @@ -1,208 +0,0 @@ -# -# Copyright 2005,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. -# - -# See gnuradio-examples/python/digital for examples - -""" -QAM16 modulation and demodulation. -""" - -from gnuradio import gr, gru, modulation_utils -from math import pi, sqrt -import qam -import cmath -from pprint import pprint - -# default values (used in __init__ and add_options) -_def_samples_per_symbol = 2 -_def_excess_bw = 0.35 -_def_gray_code = True -_def_verbose = False -_def_log = False - -_def_costas_alpha = None -_def_gain_mu = 0.03 -_def_mu = 0.05 -_def_omega_relative_limit = 0.005 - - -# ///////////////////////////////////////////////////////////////////////////// -# QAM16 modulator -# ///////////////////////////////////////////////////////////////////////////// - -class qam16_mod(gr.hier_block2): - - def __init__(self, - samples_per_symbol=_def_samples_per_symbol, - excess_bw=_def_excess_bw, - gray_code=_def_gray_code, - verbose=_def_verbose, - log=_def_log): - - """ - Hierarchical block for RRC-filtered QPSK modulation. - - The input is a byte stream (unsigned char) and the - output is the complex modulated signal at baseband. - - @param samples_per_symbol: samples per symbol >= 2 - @type samples_per_symbol: integer - @param excess_bw: Root-raised cosine filter excess bandwidth - @type excess_bw: float - @param gray_code: Tell modulator to Gray code the bits - @type gray_code: bool - @param verbose: Print information about modulator? - @type verbose: bool - @param debug: Print modualtion data to files? - @type debug: bool - """ - - gr.hier_block2.__init__(self, "qam16_mod", - gr.io_signature(1, 1, gr.sizeof_char), # Input signature - gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature - - self._samples_per_symbol = samples_per_symbol - self._excess_bw = excess_bw - self._gray_code = gray_code - - if not isinstance(samples_per_symbol, int) or samples_per_symbol < 2: - raise TypeError, ("sbp must be an integer >= 2, is %d" % samples_per_symbol) - - ntaps = 11 * samples_per_symbol - - arity = pow(2, self.bits_per_symbol()) - - # turn bytes into k-bit vectors - self.bytes2chunks = \ - gr.packed_to_unpacked_bb(self.bits_per_symbol(), gr.GR_MSB_FIRST) - - if self._gray_code: - self.symbol_mapper = gr.map_bb(qam.binary_to_gray[arity]) - else: - self.symbol_mapper = gr.map_bb(qam.binary_to_ungray[arity]) - - self.diffenc = gr.diff_encoder_bb(arity) - - rot = 1.0 - print "constellation with %d arity" % arity - rotated_const = map(lambda pt: pt * rot, qam.constellation[arity]) - self.chunks2symbols = gr.chunks_to_symbols_bc(rotated_const) - - # pulse shaping filter - self.rrc_taps = gr.firdes.root_raised_cosine( - self._samples_per_symbol, # gain (sps since we're interpolating by sps) - self._samples_per_symbol, # sampling rate - 1.0, # symbol rate - self._excess_bw, # excess bandwidth (roll-off factor) - ntaps) - - self.rrc_filter = gr.interp_fir_filter_ccf(self._samples_per_symbol, self.rrc_taps) - - if verbose: - self._print_verbage() - - if log: - self._setup_logging() - - # Connect - self.connect(self, self.bytes2chunks, self.symbol_mapper, self.diffenc, - self.chunks2symbols, self.rrc_filter, self) - - def samples_per_symbol(self): - return self._samples_per_symbol - - def bits_per_symbol(self=None): # staticmethod that's also callable on an instance - return 4 - bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM - - def _print_verbage(self): - print "bits per symbol = %d" % self.bits_per_symbol() - print "Gray code = %s" % self._gray_code - print "RRS roll-off factor = %f" % self._excess_bw - - def _setup_logging(self): - print "Modulation logging turned on." - self.connect(self.bytes2chunks, - gr.file_sink(gr.sizeof_char, "bytes2chunks.dat")) - self.connect(self.symbol_mapper, - gr.file_sink(gr.sizeof_char, "graycoder.dat")) - self.connect(self.diffenc, - gr.file_sink(gr.sizeof_char, "diffenc.dat")) - self.connect(self.chunks2symbols, - gr.file_sink(gr.sizeof_gr_complex, "chunks2symbols.dat")) - self.connect(self.rrc_filter, - gr.file_sink(gr.sizeof_gr_complex, "rrc_filter.dat")) - - def add_options(parser): - """ - Adds QAM modulation-specific options to the standard parser - """ - parser.add_option("", "--excess-bw", type="float", default=_def_excess_bw, - help="set RRC excess bandwith factor [default=%default] (PSK)") - parser.add_option("", "--no-gray-code", dest="gray_code", - action="store_false", default=_def_gray_code, - help="disable gray coding on modulated bits (PSK)") - add_options=staticmethod(add_options) - - - def extract_kwargs_from_options(options): - """ - Given command line options, create dictionary suitable for passing to __init__ - """ - return modulation_utils.extract_kwargs_from_options(qam16_mod.__init__, - ('self',), options) - extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) - - -# ///////////////////////////////////////////////////////////////////////////// -# QAM16 demodulator -# -# ///////////////////////////////////////////////////////////////////////////// - -class qam16_demod(gr.hier_block2): - - def __init__(self, - samples_per_symbol=_def_samples_per_symbol, - excess_bw=_def_excess_bw, - costas_alpha=_def_costas_alpha, - gain_mu=_def_gain_mu, - mu=_def_mu, - omega_relative_limit=_def_omega_relative_limit, - gray_code=_def_gray_code, - verbose=_def_verbose, - log=_def_log): - - gr.hier_block2.__init__(self, "qam16_demod", - gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature - gr.io_signature(1, 1, gr.sizeof_char)) # Output signature - # do this - pass - - def bits_per_symbol(self=None): # staticmethod that's also callable on an instance - return 4 - bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM - -# -# Add these to the mod/demod registry -# -# NOT READY TO BE USED YET -- ENABLE AT YOUR OWN RISK -#modulation_utils.add_type_1_mod('qam16', qam16_mod) -#modulation_utils.add_type_1_demod('qam16', qam16_demod) diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/qam256.py b/gnuradio-core/src/python/gnuradio/blks2impl/qam256.py deleted file mode 100644 index fc455f17c..000000000 --- a/gnuradio-core/src/python/gnuradio/blks2impl/qam256.py +++ /dev/null @@ -1,209 +0,0 @@ -# -# Copyright 2005,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. -# - -# See gnuradio-examples/python/digital for examples - -""" -QAM256 modulation and demodulation. -""" - -from gnuradio import gr, gru, modulation_utils -from math import pi, sqrt -import qam -import cmath -from pprint import pprint - -# default values (used in __init__ and add_options) -_def_samples_per_symbol = 2 -_def_excess_bw = 0.35 -_def_gray_code = True -_def_verbose = False -_def_log = False - -_def_costas_alpha = None -_def_gain_mu = 0.03 -_def_mu = 0.05 -_def_omega_relative_limit = 0.005 - - -# ///////////////////////////////////////////////////////////////////////////// -# QAM256 modulator -# ///////////////////////////////////////////////////////////////////////////// - -class qam256_mod(gr.hier_block2): - - def __init__(self, - samples_per_symbol=_def_samples_per_symbol, - excess_bw=_def_excess_bw, - gray_code=_def_gray_code, - verbose=_def_verbose, - log=_def_log): - - """ - Hierarchical block for RRC-filtered QPSK modulation. - - The input is a byte stream (unsigned char) and the - output is the complex modulated signal at baseband. - - @param samples_per_symbol: samples per symbol >= 2 - @type samples_per_symbol: integer - @param excess_bw: Root-raised cosine filter excess bandwidth - @type excess_bw: float - @param gray_code: Tell modulator to Gray code the bits - @type gray_code: bool - @param verbose: Print information about modulator? - @type verbose: bool - @param debug: Print modualtion data to files? - @type debug: bool - """ - - gr.hier_block2.__init__(self, "qam256_mod", - gr.io_signature(1, 1, gr.sizeof_char), # Input signature - gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature - - self._samples_per_symbol = samples_per_symbol - self._excess_bw = excess_bw - self._gray_code = gray_code - - if not isinstance(samples_per_symbol, int) or samples_per_symbol < 2: - raise TypeError, ("sbp must be an integer >= 2, is %d" % samples_per_symbol) - - ntaps = 11 * samples_per_symbol - - arity = pow(2, self.bits_per_symbol()) - - # turn bytes into k-bit vectors - self.bytes2chunks = \ - gr.packed_to_unpacked_bb(self.bits_per_symbol(), gr.GR_MSB_FIRST) - - if self._gray_code: - self.symbol_mapper = gr.map_bb(qam.binary_to_gray[arity]) - else: - self.symbol_mapper = gr.map_bb(qam.binary_to_ungray[arity]) - - self.diffenc = gr.diff_encoder_bb(arity) - - rot = 1.0 - print "constellation with %d arity" % arity - rotated_const = map(lambda pt: pt * rot, qam.constellation[arity]) - self.chunks2symbols = gr.chunks_to_symbols_bc(rotated_const) - - # pulse shaping filter - self.rrc_taps = gr.firdes.root_raised_cosine( - self._samples_per_symbol, # gain (sps since we're interpolating by sps) - self._samples_per_symbol, # sampling rate - 1.0, # symbol rate - self._excess_bw, # excess bandwidth (roll-off factor) - ntaps) - - self.rrc_filter = gr.interp_fir_filter_ccf(self._samples_per_symbol, self.rrc_taps) - - if verbose: - self._print_verbage() - - if log: - self._setup_logging() - - # Connect - self.connect(self, self.bytes2chunks, self.symbol_mapper, self.diffenc, - self.chunks2symbols, self.rrc_filter, self) - - def samples_per_symbol(self): - return self._samples_per_symbol - - def bits_per_symbol(self=None): # staticmethod that's also callable on an instance - return 8 - bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM - - def _print_verbage(self): - print "bits per symbol = %d" % self.bits_per_symbol() - print "Gray code = %s" % self._gray_code - print "RRS roll-off factor = %f" % self._excess_bw - - def _setup_logging(self): - print "Modulation logging turned on." - self.connect(self.bytes2chunks, - gr.file_sink(gr.sizeof_char, "bytes2chunks.dat")) - self.connect(self.symbol_mapper, - gr.file_sink(gr.sizeof_char, "graycoder.dat")) - self.connect(self.diffenc, - gr.file_sink(gr.sizeof_char, "diffenc.dat")) - self.connect(self.chunks2symbols, - gr.file_sink(gr.sizeof_gr_complex, "chunks2symbols.dat")) - self.connect(self.rrc_filter, - gr.file_sink(gr.sizeof_gr_complex, "rrc_filter.dat")) - - def add_options(parser): - """ - Adds QAM modulation-specific options to the standard parser - """ - parser.add_option("", "--excess-bw", type="float", default=_def_excess_bw, - help="set RRC excess bandwith factor [default=%default] (PSK)") - parser.add_option("", "--no-gray-code", dest="gray_code", - action="store_false", default=_def_gray_code, - help="disable gray coding on modulated bits (PSK)") - add_options=staticmethod(add_options) - - - def extract_kwargs_from_options(options): - """ - Given command line options, create dictionary suitable for passing to __init__ - """ - return modulation_utils.extract_kwargs_from_options(qam256_mod.__init__, - ('self',), options) - extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) - - -# ///////////////////////////////////////////////////////////////////////////// -# QAM256 demodulator -# -# ///////////////////////////////////////////////////////////////////////////// - -class qam256_demod(gr.hier_block2): - - def __init__(self, - samples_per_symbol=_def_samples_per_symbol, - excess_bw=_def_excess_bw, - costas_alpha=_def_costas_alpha, - gain_mu=_def_gain_mu, - mu=_def_mu, - omega_relative_limit=_def_omega_relative_limit, - gray_code=_def_gray_code, - verbose=_def_verbose, - log=_def_log): - - gr.hier_block2.__init__(self, "qam256_demod", - gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature - gr.io_signature(1, 1, gr.sizeof_char)) # Output signature - - # do this - pass - - def bits_per_symbol(self=None): # staticmethod that's also callable on an instance - return 8 - bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM - -# -# Add these to the mod/demod registry -# -# NOT READY TO BE USED YET -- ENABLE AT YOUR OWN RISK -#modulation_utils.add_type_1_mod('qam256', qam256_mod) -#modulation_utils.add_type_1_demod('qam256', qam256_demod) diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/qam64.py b/gnuradio-core/src/python/gnuradio/blks2impl/qam64.py deleted file mode 100644 index 5509f3745..000000000 --- a/gnuradio-core/src/python/gnuradio/blks2impl/qam64.py +++ /dev/null @@ -1,208 +0,0 @@ -# -# Copyright 2005,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. -# - -# See gnuradio-examples/python/digital for examples - -""" -differential QPSK modulation and demodulation. -""" - -from gnuradio import gr, gru, modulation_utils -from math import pi, sqrt -import qam -import cmath -from pprint import pprint - -# default values (used in __init__ and add_options) -_def_samples_per_symbol = 2 -_def_excess_bw = 0.35 -_def_gray_code = True -_def_verbose = False -_def_log = False - -_def_costas_alpha = None -_def_gain_mu = 0.03 -_def_mu = 0.05 -_def_omega_relative_limit = 0.005 - - -# ///////////////////////////////////////////////////////////////////////////// -# QAM64 modulator -# ///////////////////////////////////////////////////////////////////////////// - -class qam64_mod(gr.hier_block2): - - def __init__(self, - samples_per_symbol=_def_samples_per_symbol, - excess_bw=_def_excess_bw, - gray_code=_def_gray_code, - verbose=_def_verbose, - log=_def_log): - - """ - Hierarchical block for RRC-filtered QPSK modulation. - - The input is a byte stream (unsigned char) and the - output is the complex modulated signal at baseband. - - @param samples_per_symbol: samples per symbol >= 2 - @type samples_per_symbol: integer - @param excess_bw: Root-raised cosine filter excess bandwidth - @type excess_bw: float - @param gray_code: Tell modulator to Gray code the bits - @type gray_code: bool - @param verbose: Print information about modulator? - @type verbose: bool - @param debug: Print modualtion data to files? - @type debug: bool - """ - - gr.hier_block2.__init__(self, "qam64_mod", - gr.io_signature(1, 1, gr.sizeof_char), # Input signature - gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature - self._samples_per_symbol = samples_per_symbol - self._excess_bw = excess_bw - self._gray_code = gray_code - - if not isinstance(samples_per_symbol, int) or samples_per_symbol < 2: - raise TypeError, ("sbp must be an integer >= 2, is %d" % samples_per_symbol) - - ntaps = 11 * samples_per_symbol - - arity = pow(2, self.bits_per_symbol()) - - # turn bytes into k-bit vectors - self.bytes2chunks = \ - gr.packed_to_unpacked_bb(self.bits_per_symbol(), gr.GR_MSB_FIRST) - - if self._gray_code: - self.symbol_mapper = gr.map_bb(qam.binary_to_gray[arity]) - else: - self.symbol_mapper = gr.map_bb(qam.binary_to_ungray[arity]) - - self.diffenc = gr.diff_encoder_bb(arity) - - rot = 1.0 - print "constellation with %d arity" % arity - rotated_const = map(lambda pt: pt * rot, qam.constellation[arity]) - self.chunks2symbols = gr.chunks_to_symbols_bc(rotated_const) - - # pulse shaping filter - self.rrc_taps = gr.firdes.root_raised_cosine( - self._samples_per_symbol, # gain (sps since we're interpolating by sps) - self._samples_per_symbol, # sampling rate - 1.0, # symbol rate - self._excess_bw, # excess bandwidth (roll-off factor) - ntaps) - - self.rrc_filter = gr.interp_fir_filter_ccf(self._samples_per_symbol, self.rrc_taps) - - if verbose: - self._print_verbage() - - if log: - self._setup_logging() - - # Connect - self.connect(self, self.bytes2chunks, self.symbol_mapper, self.diffenc, - self.chunks2symbols, self.rrc_filter, self) - - def samples_per_symbol(self): - return self._samples_per_symbol - - def bits_per_symbol(self=None): # staticmethod that's also callable on an instance - return 6 - bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM - - def _print_verbage(self): - print "bits per symbol = %d" % self.bits_per_symbol() - print "Gray code = %s" % self._gray_code - print "RRS roll-off factor = %f" % self._excess_bw - - def _setup_logging(self): - print "Modulation logging turned on." - self.connect(self.bytes2chunks, - gr.file_sink(gr.sizeof_char, "bytes2chunks.dat")) - self.connect(self.symbol_mapper, - gr.file_sink(gr.sizeof_char, "graycoder.dat")) - self.connect(self.diffenc, - gr.file_sink(gr.sizeof_char, "diffenc.dat")) - self.connect(self.chunks2symbols, - gr.file_sink(gr.sizeof_gr_complex, "chunks2symbols.dat")) - self.connect(self.rrc_filter, - gr.file_sink(gr.sizeof_gr_complex, "rrc_filter.dat")) - - def add_options(parser): - """ - Adds QAM modulation-specific options to the standard parser - """ - parser.add_option("", "--excess-bw", type="float", default=_def_excess_bw, - help="set RRC excess bandwith factor [default=%default] (PSK)") - parser.add_option("", "--no-gray-code", dest="gray_code", - action="store_false", default=_def_gray_code, - help="disable gray coding on modulated bits (PSK)") - add_options=staticmethod(add_options) - - - def extract_kwargs_from_options(options): - """ - Given command line options, create dictionary suitable for passing to __init__ - """ - return modulation_utils.extract_kwargs_from_options(qam64_mod.__init__, - ('self',), options) - extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) - - -# ///////////////////////////////////////////////////////////////////////////// -# QAM16 demodulator -# -# ///////////////////////////////////////////////////////////////////////////// - -class qam64_demod(gr.hier_block2): - - def __init__(self, - samples_per_symbol=_def_samples_per_symbol, - excess_bw=_def_excess_bw, - costas_alpha=_def_costas_alpha, - gain_mu=_def_gain_mu, - mu=_def_mu, - omega_relative_limit=_def_omega_relative_limit, - gray_code=_def_gray_code, - verbose=_def_verbose, - log=_def_log): - - gr.hier_block2.__init__(self, "qam64_demod", - gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature - gr.io_signature(1, 1, gr.sizeof_char)) # Output signature - - # do this - pass - - def bits_per_symbol(self=None): # staticmethod that's also callable on an instance - return 6 - bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM - -# -# Add these to the mod/demod registry -# -# NOT READY TO BE USED YET -- ENABLE AT YOUR OWN RISK -#modulation_utils.add_type_1_mod('qam64', qam64_mod) -#modulation_utils.add_type_1_demod('qam16', qam16_demod) diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/qam8.py b/gnuradio-core/src/python/gnuradio/blks2impl/qam8.py deleted file mode 100644 index 6a7b35597..000000000 --- a/gnuradio-core/src/python/gnuradio/blks2impl/qam8.py +++ /dev/null @@ -1,209 +0,0 @@ -# -# Copyright 2005,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. -# - -# See gnuradio-examples/python/digital for examples - -""" -QAM8 modulation and demodulation. -""" - -from gnuradio import gr, gru, modulation_utils -from math import pi, sqrt -import qam -import cmath -from pprint import pprint - -# default values (used in __init__ and add_options) -_def_samples_per_symbol = 2 -_def_excess_bw = 0.35 -_def_gray_code = True -_def_verbose = False -_def_log = False - -_def_costas_alpha = None -_def_gain_mu = 0.03 -_def_mu = 0.05 -_def_omega_relative_limit = 0.005 - - -# ///////////////////////////////////////////////////////////////////////////// -# QAM8 modulator -# ///////////////////////////////////////////////////////////////////////////// - -class qam8_mod(gr.hier_block2): - - def __init__(self, - samples_per_symbol=_def_samples_per_symbol, - excess_bw=_def_excess_bw, - gray_code=_def_gray_code, - verbose=_def_verbose, - log=_def_log): - - """ - Hierarchical block for RRC-filtered QPSK modulation. - - The input is a byte stream (unsigned char) and the - output is the complex modulated signal at baseband. - - @param samples_per_symbol: samples per symbol >= 2 - @type samples_per_symbol: integer - @param excess_bw: Root-raised cosine filter excess bandwidth - @type excess_bw: float - @param gray_code: Tell modulator to Gray code the bits - @type gray_code: bool - @param verbose: Print information about modulator? - @type verbose: bool - @param debug: Print modualtion data to files? - @type debug: bool - """ - - gr.hier_block2.__init__(self, "qam8_mod", - gr.io_signature(1, 1, gr.sizeof_char), # Input signature - gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature - - self._samples_per_symbol = samples_per_symbol - self._excess_bw = excess_bw - self._gray_code = gray_code - - if not isinstance(samples_per_symbol, int) or samples_per_symbol < 2: - raise TypeError, ("sbp must be an integer >= 2, is %d" % samples_per_symbol) - - ntaps = 11 * samples_per_symbol - - arity = pow(2, self.bits_per_symbol()) - - # turn bytes into k-bit vectors - self.bytes2chunks = \ - gr.packed_to_unpacked_bb(self.bits_per_symbol(), gr.GR_MSB_FIRST) - - if self._gray_code: - self.symbol_mapper = gr.map_bb(qam.binary_to_gray[arity]) - else: - self.symbol_mapper = gr.map_bb(qam.binary_to_ungray[arity]) - - self.diffenc = gr.diff_encoder_bb(arity) - - rot = 1.0 - print "constellation with %d arity" % arity - rotated_const = map(lambda pt: pt * rot, qam.constellation[arity]) - self.chunks2symbols = gr.chunks_to_symbols_bc(rotated_const) - - # pulse shaping filter - self.rrc_taps = gr.firdes.root_raised_cosine( - self._samples_per_symbol, # gain (sps since we're interpolating by sps) - self._samples_per_symbol, # sampling rate - 1.0, # symbol rate - self._excess_bw, # excess bandwidth (roll-off factor) - ntaps) - - self.rrc_filter = gr.interp_fir_filter_ccf(self._samples_per_symbol, self.rrc_taps) - - if verbose: - self._print_verbage() - - if log: - self._setup_logging() - - # Connect - self.connect(self, self.bytes2chunks, self.symbol_mapper, self.diffenc, - self.chunks2symbols, self.rrc_filter, self) - - def samples_per_symbol(self): - return self._samples_per_symbol - - def bits_per_symbol(self=None): # staticmethod that's also callable on an instance - return 3 - bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM - - def _print_verbage(self): - print "bits per symbol = %d" % self.bits_per_symbol() - print "Gray code = %s" % self._gray_code - print "RRS roll-off factor = %f" % self._excess_bw - - def _setup_logging(self): - print "Modulation logging turned on." - self.connect(self.bytes2chunks, - gr.file_sink(gr.sizeof_char, "bytes2chunks.dat")) - self.connect(self.symbol_mapper, - gr.file_sink(gr.sizeof_char, "graycoder.dat")) - self.connect(self.diffenc, - gr.file_sink(gr.sizeof_char, "diffenc.dat")) - self.connect(self.chunks2symbols, - gr.file_sink(gr.sizeof_gr_complex, "chunks2symbols.dat")) - self.connect(self.rrc_filter, - gr.file_sink(gr.sizeof_gr_complex, "rrc_filter.dat")) - - def add_options(parser): - """ - Adds QAM modulation-specific options to the standard parser - """ - parser.add_option("", "--excess-bw", type="float", default=_def_excess_bw, - help="set RRC excess bandwith factor [default=%default] (PSK)") - parser.add_option("", "--no-gray-code", dest="gray_code", - action="store_false", default=_def_gray_code, - help="disable gray coding on modulated bits (PSK)") - add_options=staticmethod(add_options) - - - def extract_kwargs_from_options(options): - """ - Given command line options, create dictionary suitable for passing to __init__ - """ - return modulation_utils.extract_kwargs_from_options(qam8_mod.__init__, - ('self',), options) - extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) - - -# ///////////////////////////////////////////////////////////////////////////// -# QAM8 demodulator -# -# ///////////////////////////////////////////////////////////////////////////// - -class qam8_demod(gr.hier_block2): - - def __init__(self, - samples_per_symbol=_def_samples_per_symbol, - excess_bw=_def_excess_bw, - costas_alpha=_def_costas_alpha, - gain_mu=_def_gain_mu, - mu=_def_mu, - omega_relative_limit=_def_omega_relative_limit, - gray_code=_def_gray_code, - verbose=_def_verbose, - log=_def_log): - - gr.hier_block2.__init__(self, "qam8_demod", - gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature - gr.io_signature(1, 1, gr.sizeof_char)) # Output signature - - # do this - pass - - def bits_per_symbol(self=None): # staticmethod that's also callable on an instance - return 3 - bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM - -# -# Add these to the mod/demod registry -# -# NOT READY TO BE USED YET -- ENABLE AT YOUR OWN RISK -modulation_utils.add_type_1_mod('qam8', qam8_mod) -#modulation_utils.add_type_1_demod('qam8', qam8_demod) diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/qpsk.py b/gnuradio-core/src/python/gnuradio/blks2impl/qpsk.py new file mode 100644 index 000000000..f62ae232c --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blks2impl/qpsk.py @@ -0,0 +1,79 @@ +# +# Copyright 2005,2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +""" +QPSK modulation. + +Demodulation is not included since the generic_mod_demod +doesn't work for non-differential encodings. +""" + +from gnuradio import gr, modulation_utils2 +from gnuradio.blks2impl.generic_mod_demod import generic_mod + + +# Default number of points in constellation. +_def_constellation_points = 4 +# Whether differential coding is used. +_def_differential = False +_def_gray_coded = True + +# ///////////////////////////////////////////////////////////////////////////// +# QPSK constellation +# ///////////////////////////////////////////////////////////////////////////// + +def qpsk_constellation(m=_def_constellation_points): + if m != _def_constellation_points: + raise ValueError("QPSK can only have 4 constellation points.") + return gr.constellation_qpsk() + +# ///////////////////////////////////////////////////////////////////////////// +# QPSK modulator +# ///////////////////////////////////////////////////////////////////////////// + +class qpsk_mod(generic_mod): + + def __init__(self, constellation_points=_def_constellation_points, + differential=_def_differential, + gray_coded=_def_gray_coded, + *args, **kwargs): + + """ + Hierarchical block for RRC-filtered QPSK modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + See generic_mod block for list of parameters. + """ + + constellation = gr.constellation_qpsk() + if constellation_points != 4: + raise ValueError("QPSK can only have 4 constellation points.") + if differential or not gray_coded: + raise ValueError("This QPSK mod/demod works only for gray-coded, non-differential.") + super(qpsk_mod, self).__init__(constellation, differential, gray_coded, *args, **kwargs) + +# +# Add these to the mod/demod registry +# +modulation_utils2.add_type_1_mod('qpsk', qpsk_mod) +modulation_utils2.add_type_1_constellation('qpsk', qpsk_constellation) diff --git a/gnuradio-core/src/python/gnuradio/gr/Makefile.am b/gnuradio-core/src/python/gnuradio/gr/Makefile.am index b8da9cf48..db2cc6eb3 100644 --- a/gnuradio-core/src/python/gnuradio/gr/Makefile.am +++ b/gnuradio-core/src/python/gnuradio/gr/Makefile.am @@ -51,6 +51,7 @@ noinst_PYTHON = \ qa_classify.py \ qa_cma_equalizer.py \ qa_complex_to_xxx.py \ + qa_constellation.py \ qa_constellation_decoder_cb.py \ qa_copy.py \ qa_correlate_access_code.py \ diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_constellation.py b/gnuradio-core/src/python/gnuradio/gr/qa_constellation.py new file mode 100644 index 000000000..6ac3d95b6 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_constellation.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python +# +# Copyright 2004,2007,2010 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 random +from cmath import exp, pi, log +from itertools import product + +from gnuradio import gr, gr_unittest, blks2 +from gnuradio.utils import mod_codes + +tested_mod_codes = (mod_codes.NO_CODE, mod_codes.GRAY_CODE) + +# A list of the constellations to test. +# Each constellation is given by a 3-tuple. +# First item is a function to generate the constellation +# Second item is a dictionary of arguments for function with lists of +# possible values. +# Third item is whether differential encoding should be tested. +# Fourth item is the name of the argument to constructor that specifices +# whether differential encoding is used. + +def twod_constell(): + """ + + """ + points = ((1+0j), (0+1j), + (-1+0j), (0-1j)) + rot_sym = 2 + dim = 2 + return gr.constellation_calcdist(points, [], rot_sym, dim) + +def threed_constell(): + oned_points = ((1+0j), (0+1j), (-1+0j), (0-1j)) + points = [] + r4 = range(0, 4) + for ia, ib, ic in product(r4, r4, r4): + points += [oned_points[ia], oned_points[ib], oned_points[ic]] + rot_sym = 4 + dim = 3 + return gr.constellation_calcdist(points, [], rot_sym, dim) + +tested_constellation_info = ( + (blks2.psk_constellation, + {'m': (2, 4, 8, 16, 32, 64), + 'mod_code': tested_mod_codes, }, + True, None), + (blks2.qam_constellation, + {'constellation_points': (4, 16, 64), + 'mod_code': tested_mod_codes, }, + True, 'differential'), + (blks2.bpsk_constellation, {}, True, None), + # No differential testing for qpsk because it is gray-coded. + # This is because soft decision making is simpler if we can assume + # gray coding. + (blks2.qpsk_constellation, {}, False, None), + (twod_constell, {}, True, None), + (threed_constell, {}, True, None), + ) + +def tested_constellations(): + """ + Generator to produce (constellation, differential) tuples for testing purposes. + """ + for constructor, poss_args, differential, diff_argname in tested_constellation_info: + if differential: + diff_poss = (True, False) + else: + diff_poss = (False,) + poss_args = [[argname, argvalues, 0] for argname, argvalues in poss_args.items()] + for current_diff in diff_poss: + # Add an index into args to keep track of current position in argvalues + while True: + current_args = dict([(argname, argvalues[argindex]) + for argname, argvalues, argindex in poss_args]) + if diff_argname is not None: + current_args[diff_argname] = current_diff + constellation = constructor(**current_args) + yield (constellation, current_diff) + for this_poss_arg in poss_args: + argname, argvalues, argindex = this_poss_arg + if argindex < len(argvalues) - 1: + this_poss_arg[2] += 1 + break + else: + this_poss_arg[2] = 0 + if sum([argindex for argname, argvalues, argindex in poss_args]) == 0: + break + + +class test_constellation (gr_unittest.TestCase): + + src_length = 256 + + def setUp(self): + # Generate a list of random bits. + self.src_data = tuple([random.randint(0,1) for i in range(0, self.src_length)]) + + def tearDown(self): + pass + + def test_hard_decision(self): + for constellation, differential in tested_constellations(): + if differential: + rs = constellation.rotational_symmetry() + rotations = [exp(i*2*pi*(0+1j)/rs) for i in range(0, rs)] + else: + rotations = [None] + for rotation in rotations: + src = gr.vector_source_b(self.src_data) + content = mod_demod(constellation, differential, rotation) + dst = gr.vector_sink_b() + self.tb = gr.top_block() + self.tb.connect(src, content, dst) + self.tb.run() + data = dst.data() + # Don't worry about cut off data for now. + first = constellation.bits_per_symbol() + self.assertEqual (self.src_data[first:len(data)], data[first:]) + + +class mod_demod(gr.hier_block2): + def __init__(self, constellation, differential, rotation): + if constellation.arity() > 256: + # If this becomes limiting some of the blocks should be generalised so that they can work + # with shorts and ints as well as chars. + raise ValueError("Constellation cannot contain more than 256 points.") + + gr.hier_block2.__init__(self, "mod_demod", + gr.io_signature(1, 1, gr.sizeof_char), # Input signature + gr.io_signature(1, 1, gr.sizeof_char)) # Output signature + + arity = constellation.arity() + + # TX + self.constellation = constellation + self.differential = differential + self.blocks = [self] + # We expect a stream of unpacked bits. + # First step is to pack them. + self.blocks.append( + gr.unpacked_to_packed_bb(1, gr.GR_MSB_FIRST)) + # Second step we unpack them such that we have k bits in each byte where + # each constellation symbol hold k bits. + self.blocks.append( + gr.packed_to_unpacked_bb(self.constellation.bits_per_symbol(), + gr.GR_MSB_FIRST)) + # Apply any pre-differential coding + # Gray-coding is done here if we're also using differential coding. + if self.constellation.apply_pre_diff_code(): + self.blocks.append(gr.map_bb(self.constellation.pre_diff_code())) + # Differential encoding. + if self.differential: + self.blocks.append(gr.diff_encoder_bb(arity)) + # Convert to constellation symbols. + self.blocks.append(gr.chunks_to_symbols_bc(self.constellation.points(), self.constellation.dimensionality())) + # CHANNEL + # Channel just consists of a rotation to check differential coding. + if rotation is not None: + self.blocks.append(gr.multiply_const_cc(rotation)) + + # RX + # Convert the constellation symbols back to binary values. + self.blocks.append(gr.constellation_decoder2_cb(self.constellation.base())) + # Differential decoding. + if self.differential: + self.blocks.append(gr.diff_decoder_bb(arity)) + # Decode any pre-differential coding. + if self.constellation.apply_pre_diff_code(): + self.blocks.append(gr.map_bb( + mod_codes.invert_code(self.constellation.pre_diff_code()))) + # unpack the k bit vector into a stream of bits + self.blocks.append(gr.unpack_k_bits_bb( + self.constellation.bits_per_symbol())) + # connect to block output + check_index = len(self.blocks) + self.blocks = self.blocks[:check_index] + self.blocks.append(self) + + self.connect(*self.blocks) + + +if __name__ == '__main__': + gr_unittest.run(test_constellation, "test_constellation.xml") diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_constellation_receiver.py b/gnuradio-core/src/python/gnuradio/gr/qa_constellation_receiver.py new file mode 100644 index 000000000..8d7719739 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/gr/qa_constellation_receiver.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python +# +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import random + +from gnuradio import gr, blks2, packet_utils, gr_unittest +from gnuradio.utils import mod_codes, alignment + +from qa_constellation import tested_constellations, twod_constell + +# Set a seed so that if errors turn up they are reproducible. +# 1234 fails +random.seed(1239) + +# TESTING PARAMETERS +# The number of symbols to test with. +# We need this many to let the frequency recovery block converge. +DATA_LENGTH = 200000 +# Test fails if fraction of output that is correct is less than this. +REQ_CORRECT = 0.8 + +# CHANNEL PARAMETERS +NOISE_VOLTAGE = 0.01 +FREQUENCY_OFFSET = 0.01 +TIMING_OFFSET = 1.0 + +# RECEIVER PARAMETERS +# Increased from normal default of 0.01 to speed things up. +FREQ_ALPHA = 0.02 +# Decreased from normal default of 0.1 is required for the constellations +# with smaller point separations. +PHASE_ALPHA = 0.02 + + +class test_constellation_receiver (gr_unittest.TestCase): + + # We ignore the first half of the output data since often it takes + # a while for the receiver to lock on. + ignore_fraction = 0.8 + seed = 1234 + max_data_length = DATA_LENGTH * 6 + max_num_samples = 1000 + + def test_basic(self): + """ + Tests a bunch of different constellations by using generic + modulation, a channel, and generic demodulation. The generic + demodulation uses constellation_receiver which is what + we're really trying to test. + """ + # Assumes not more than 64 points in a constellation + # Generates some random input data to use. + self.src_data = tuple( + [random.randint(0,1) for i in range(0, self.max_data_length)]) + # Generates some random indices to use for comparing input and + # output data (a full comparison is too slow in python). + self.indices = alignment.random_sample( + self.max_data_length, self.max_num_samples, self.seed) + + for constellation, differential in tested_constellations(): + # The constellation_receiver doesn't work for constellations + # of multple dimensions (i.e. multiple complex numbers to a + # single symbol). + # That is not implemented since the receiver has no way of + # knowing where the beginning of a symbol is. + # It also doesn't work for non-differential modulation. + if constellation.dimensionality() != 1 or not differential: + continue + data_length = DATA_LENGTH * constellation.bits_per_symbol() + tb = rec_test_tb(constellation, differential, + src_data=self.src_data[:data_length]) + tb.run() + data = tb.dst.data() + d1 = tb.src_data[:int(len(tb.src_data)*self.ignore_fraction)] + d2 = data[:int(len(data)*self.ignore_fraction)] + correct, overlap, offset, indices = alignment.align_sequences( + d1, d2, indices=self.indices) + print(constellation, constellation.arity(), differential, correct, overlap, offset) + self.assertTrue(correct > REQ_CORRECT) + + def single(self): + constellation = blks2.psk_constellation(64, mod_codes.NO_CODE) + tb = rec_test_tb(constellation, True, DATA_LENGTH) + tb.run() + data = tb.dst.data() + d1 = tb.src_data[:int(len(tb.src_data)*self.ignore_fraction)] + d2 = data[:int(len(data)*self.ignore_fraction)] + seed = random.randint(0, 256) + correct, overlap, offset, indices = alignment.align_sequences(d1, d2, seed=seed) + print(constellation, constellation.arity(), correct, overlap, offset) + + +class rec_test_tb (gr.top_block): + """ + Takes a constellation an runs a generic modulation, channel, + and generic demodulation. + """ + def __init__(self, constellation, differential, + data_length=None, src_data=None): + """ + constellation -- a constellation object + differential -- whether differential encoding is used + data_length -- the number of bits of data to use + src_data -- a list of the bits to use + """ + super(rec_test_tb, self).__init__() + # Transmission Blocks + if src_data is None: + self.src_data = tuple([random.randint(0,1) for i in range(0, data_length)]) + else: + self.src_data = src_data + packer = gr.unpacked_to_packed_bb(1, gr.GR_MSB_FIRST) + src = gr.vector_source_b(self.src_data) + mod = blks2.generic_mod(constellation, differential=differential) + # Channel + channel = gr.channel_model(NOISE_VOLTAGE, FREQUENCY_OFFSET, TIMING_OFFSET) + # Receiver Blocks + demod = blks2.generic_demod(constellation, differential=differential, log=True, + freq_alpha=FREQ_ALPHA, + phase_alpha=PHASE_ALPHA) + self.dst = gr.vector_sink_b() + self.connect(src, packer, mod, channel, demod, self.dst) + +if __name__ == '__main__': + gr_unittest.run(test_constellation_receiver, "test_constellation_receiver.xml") diff --git a/gnuradio-core/src/python/gnuradio/modulation_utils2.py b/gnuradio-core/src/python/gnuradio/modulation_utils2.py index c5dba3e79..f30055f4a 100644 --- a/gnuradio-core/src/python/gnuradio/modulation_utils2.py +++ b/gnuradio-core/src/python/gnuradio/modulation_utils2.py @@ -47,6 +47,15 @@ def type_1_demods(): def add_type_1_demod(name, demod_class): _type_1_demodulators[name] = demod_class +# Also record the constellation making functions of the modulations +_type_1_constellations = {} + +def type_1_constellations(): + return _type_1_constellations + +def add_type_1_constellation(name, constellation): + _type_1_constellations[name] = constellation + def extract_kwargs_from_options(function, excluded_args, options): """ @@ -79,3 +88,14 @@ def extract_kwargs_from_options(function, excluded_args, options): if getattr(options, kw) is not None: d[kw] = getattr(options, kw) return d + +def extract_kwargs_from_options_for_class(cls, options): + """ + Given command line options, create dictionary suitable for passing to __init__ + """ + d = extract_kwargs_from_options( + cls.__init__, ('self',), options) + for base in cls.__bases__: + if hasattr(base, 'extract_kwargs_from_options'): + d.update(base.extract_kwargs_from_options(options)) + return d diff --git a/gnuradio-core/src/python/gnuradio/utils/Makefile.am b/gnuradio-core/src/python/gnuradio/utils/Makefile.am new file mode 100644 index 000000000..c35951b44 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/utils/Makefile.am @@ -0,0 +1,35 @@ +# +# Copyright 2007,2009 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. +# + +include $(top_srcdir)/Makefile.common + +if PYTHON +utilspythondir = $(grpythondir)/utils + +TESTS = \ + run_tests + +nobase_utilspython_PYTHON = \ + __init__.py \ + gray_code.py \ + mod_codes.py \ + alignment.py +endif
\ No newline at end of file diff --git a/gnuradio-core/src/python/gnuradio/utils/__init__.py b/gnuradio-core/src/python/gnuradio/utils/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/utils/__init__.py diff --git a/gnuradio-core/src/python/gnuradio/utils/alignment.py b/gnuradio-core/src/python/gnuradio/utils/alignment.py new file mode 100644 index 000000000..d32365866 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/utils/alignment.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python +# +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# +""" +This module contains functions for aligning sequences. + +>>> import random +>>> random.seed(1234) +>>> ran_seq = [random.randint(0,1) for i in range(0, 100)] +>>> offset_seq = [0] * 20 + ran_seq +>>> correct, overlap, offset = align_sequences(ran_seq, offset_seq) +>>> print(correct, overlap, offset) +(1.0, 100, -20) +>>> offset_err_seq = [] +>>> for bit in offset_seq: +... if random.randint(0,4) == 4: +... offset_err_seq.append(random.randint(0,1)) +... else: +... offset_err_seq.append(bit) +>>> correct, overlap, offset = align_sequences(ran_seq, offset_err_seq) +>>> print(overlap, offset) +(100, -20) + +""" + +import random + +# DEFAULT PARAMETERS +# If the fraction of matching bits between two sequences is greater than +# this the sequences are assumed to be aligned. +def_correct_cutoff = 0.9 +# The maximum offset to test during sequence alignment. +def_max_offset = 500 +# The maximum number of samples to take from two sequences to check alignment. +def_num_samples = 1000 + +def compare_sequences(d1, d2, offset, sample_indices=None): + """ + Takes two binary sequences and an offset and returns the number of + matching entries and the number of compared entries. + d1 & d2 -- sequences + offset -- offset of d2 relative to d1 + sample_indices -- a list of indices to use for the comparison + """ + max_index = min(len(d1), len(d2)+offset) + if sample_indices is None: + sample_indices = range(0, max_index) + correct = 0 + total = 0 + for i in sample_indices: + if i >= max_index: + break + if d1[i] == d2[i-offset]: + correct += 1 + total += 1 + return (correct, total) + +def random_sample(size, num_samples=def_num_samples, seed=None): + """ + Returns a set of random integers between 0 and (size-1). + The set contains no more than num_samples integers. + """ + random.seed(seed) + if num_samples > size: + indices = set(range(0, size)) + else: + if num_samples > size/2: + num_samples = num_samples/2 + indices = set([]) + while len(indices) < num_samples: + index = random.randint(0, size-1) + indices.add(index) + indices = list(indices) + indices.sort() + return indices + +def align_sequences(d1, d2, + num_samples=def_num_samples, + max_offset=def_max_offset, + correct_cutoff=def_correct_cutoff, + seed=None, + indices=None): + """ + Takes two sequences and finds the offset and which the two sequences best + match. It returns the fraction correct, the number of entries compared, + the offset. + d1 & d2 -- sequences to compare + num_samples -- the maximum number of entries to compare + max_offset -- the maximum offset between the sequences that is checked + correct_cutoff -- If the fraction of bits correct is greater than this then + the offset is assumed to optimum. + seed -- a random number seed + indices -- an explicit list of the indices used to compare the two sequences + """ + max_overlap = max(len(d1), len(d2)) + if indices is None: + indices = random_sample(max_overlap, num_samples, seed) + max_frac_correct = 0 + best_offset = None + best_compared = None + best_correct = None + pos_range = range(0, min(len(d1), max_offset)) + neg_range = range(-1, -min(len(d2), max_offset), -1) + # Interleave the positive and negative offsets. + int_range = [item for items in zip(pos_range, neg_range) for item in items] + for offset in int_range: + correct, compared = compare_sequences(d1, d2, offset, indices) + frac_correct = 1.0*correct/compared + if frac_correct > max_frac_correct: + max_frac_correct = frac_correct + best_offset = offset + best_compared = compared + best_correct = correct + if frac_correct > correct_cutoff: + break + return max_frac_correct, best_compared, best_offset, indices + +if __name__ == "__main__": + import doctest + doctest.testmod() + diff --git a/gnuradio-core/src/python/gnuradio/utils/gray_code.py b/gnuradio-core/src/python/gnuradio/utils/gray_code.py new file mode 100644 index 000000000..70cb9d7e2 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/utils/gray_code.py @@ -0,0 +1,45 @@ + +class GrayCodeGenerator(object): + """ + Generates and caches gray codes. + """ + + def __init__(self): + self.gcs = [0, 1] + # The last power of two passed through. + self.lp2 = 2 + # The next power of two that will be passed through. + self.np2 = 4 + # Curent index + self.i = 2 + + def get_gray_code(self, length): + """ + Returns a list of gray code of given length. + """ + if len(self.gcs) < length: + self.generate_new_gray_code(length) + return self.gcs[:length] + + def generate_new_gray_code(self, length): + """ + Generates new gray code and places into cache. + """ + while len(self.gcs) < length: + if self.i == self.lp2: + # if i is a power of two then gray number is of form 1100000... + result = self.i + self.i/2 + else: + # if not we take advantage of the symmetry of all but the last bit + # around a power of two. + result = self.gcs[2*self.lp2-1-self.i] + self.lp2 + self.gcs.append(result) + self.i += 1 + if self.i == self.np2: + self.lp2 = self.i + self.np2 = self.i*2 + +_gray_code_generator = GrayCodeGenerator() + +gray_code = _gray_code_generator.get_gray_code + diff --git a/gnuradio-core/src/python/gnuradio/utils/mod_codes.py b/gnuradio-core/src/python/gnuradio/utils/mod_codes.py new file mode 100644 index 000000000..db3dc252d --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/utils/mod_codes.py @@ -0,0 +1,11 @@ +GRAY_CODE = 'gray' +SET_PARTITION_CODE = 'set-partition' +NO_CODE = 'none' + +codes = (GRAY_CODE, SET_PARTITION_CODE, NO_CODE) + +def invert_code(code): + c = enumerate(code) + ic = [(b, a) for (a, b) in c] + ic.sort() + return [a for (b, a) in ic] diff --git a/gnuradio-core/src/python/gnuradio/utils/run_tests.in b/gnuradio-core/src/python/gnuradio/utils/run_tests.in new file mode 100644 index 000000000..504f33728 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/utils/run_tests.in @@ -0,0 +1,16 @@ +#!/bin/sh + +# 1st parameter is absolute path to component source directory +# 2nd parameter is absolute path to component build directory +# 3rd parameter is path to Python QA directory + +# Note: calling master run_tests.sh in gnuradio core is not strictly +# correct, as it will result in a partially bogus PYTHONPATH, but it +# does make the correct paths in the second half so all is well. + +@PYTHON@ @srcdir@/doxyxml/__init__.py + +# @top_builddir@/run_tests.sh \ +# @abs_top_srcdir@/gnuradio-core \ +# @abs_top_builddir@/gnuradio-core \ +# @srcdir@ |