diff options
author | Tom Rondeau <trondeau@vt.edu> | 2011-04-08 21:43:25 -0400 |
---|---|---|
committer | Tom Rondeau <trondeau@vt.edu> | 2011-04-08 21:43:25 -0400 |
commit | 78078fe0015fe87b15777b94858f5110e65244c5 (patch) | |
tree | d696a766c62e15507b4d5b2390f8c06779ced5ef /gr-digital/lib | |
parent | 2ce3ebb921bba17a11eda2253b5670b8a50684e9 (diff) |
gr-digital: moved consetellation object into gr-digital space.
Diffstat (limited to 'gr-digital/lib')
-rw-r--r-- | gr-digital/lib/digital_constellation.cc | 368 | ||||
-rw-r--r-- | gr-digital/lib/digital_constellation.h | 322 |
2 files changed, 690 insertions, 0 deletions
diff --git a/gr-digital/lib/digital_constellation.cc b/gr-digital/lib/digital_constellation.cc new file mode 100644 index 0000000000..eede99ffd8 --- /dev/null +++ b/gr-digital/lib/digital_constellation.cc @@ -0,0 +1,368 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010, 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. + */ + +#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/gr-digital/lib/digital_constellation.h b/gr-digital/lib/digital_constellation.h new file mode 100644 index 0000000000..f6726a9b52 --- /dev/null +++ b/gr-digital/lib/digital_constellation.h @@ -0,0 +1,322 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010, 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_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 |