diff options
Diffstat (limited to 'gr-digital')
45 files changed, 4130 insertions, 0 deletions
diff --git a/gr-digital/.gitignore b/gr-digital/.gitignore new file mode 100644 index 000000000..37c287f40 --- /dev/null +++ b/gr-digital/.gitignore @@ -0,0 +1,3 @@ +/Makefile +/Makefile.in +gnuradio-digital.pc diff --git a/gr-digital/Makefile.am b/gr-digital/Makefile.am new file mode 100644 index 000000000..62c40f2df --- /dev/null +++ b/gr-digital/Makefile.am @@ -0,0 +1,31 @@ +# +# 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. +# + +include $(top_srcdir)/Makefile.common + +SUBDIRS = lib + +if PYTHON +SUBDIRS += swig python apps grc +endif + +pkgconfigdir = $(libdir)/pkgconfig +dist_pkgconfig_DATA = gnuradio-digital.pc diff --git a/gr-digital/README b/gr-digital/README new file mode 100644 index 000000000..af2005c97 --- /dev/null +++ b/gr-digital/README @@ -0,0 +1,4 @@ +This GNU Radio component for implementing digitial modulators and demodulators. + + +FIXME: just fixme.
\ No newline at end of file diff --git a/gr-digital/apps/.gitignore b/gr-digital/apps/.gitignore new file mode 100644 index 000000000..282522db0 --- /dev/null +++ b/gr-digital/apps/.gitignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/gr-digital/apps/Makefile.am b/gr-digital/apps/Makefile.am new file mode 100644 index 000000000..7373a0719 --- /dev/null +++ b/gr-digital/apps/Makefile.am @@ -0,0 +1,33 @@ +# +# 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. +# + +include $(top_srcdir)/Makefile.common + +if PYTHON + +dist_bin_SCRIPTS = + +noinst_PYTHON = + +endif + +EXTRA_DIST += + diff --git a/gr-digital/gnuradio-digital.pc.in b/gr-digital/gnuradio-digital.pc.in new file mode 100644 index 000000000..6c0a7ccf8 --- /dev/null +++ b/gr-digital/gnuradio-digital.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: gnuradio-digital +Description: GNU Radio blocks for digital communications +Requires: gnuradio-core +Version: @LIBVER@ +Libs: -L${libdir} -lgnuradio-digital +Cflags: -I${includedir} diff --git a/gr-digital/grc/.gitignore b/gr-digital/grc/.gitignore new file mode 100644 index 000000000..3dda72986 --- /dev/null +++ b/gr-digital/grc/.gitignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/gr-digital/grc/Makefile.am b/gr-digital/grc/Makefile.am new file mode 100644 index 000000000..8d08c3f59 --- /dev/null +++ b/gr-digital/grc/Makefile.am @@ -0,0 +1,32 @@ +# +# 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. +# + +include $(top_srcdir)/Makefile.common + +grcblocksdir = $(grc_blocksdir) + +dist_grcblocks_DATA = \ + digital_block_tree.xml \ + digital_costas_loop_cc.xml \ + digital_cma_equalizer_cc.xml \ + digital_kurtotic_equalizer_cc.xml \ + digital_dxpsk_mod.xml \ + digital_dxpsk_demod.xml diff --git a/gr-digital/grc/digital_block_tree.xml b/gr-digital/grc/digital_block_tree.xml new file mode 100644 index 000000000..c132ff5b4 --- /dev/null +++ b/gr-digital/grc/digital_block_tree.xml @@ -0,0 +1,42 @@ +<?xml version="1.0"?> + +<!-- + 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. +--> + +<!-- +################################################### +##Block Tree for GR Digital blocks. +################################################### + --> +<cat> + <name></name> <!-- Blank for Root Name --> + <cat> + <name>Digital</name> + <block>digital_costas_loop_cc</block> + <block>digital_cma_equalizer_cc</block> + <block>digital_kurtotic_equalizer_cc</block> + </cat> + <cat> + <name>Digital Modulators</name> + <block>digital_dxpsk_mod</block> + <block>digital_dxpsk_demod</block> + </cat> +</cat> diff --git a/gr-digital/grc/digital_cma_equalizer_cc.xml b/gr-digital/grc/digital_cma_equalizer_cc.xml new file mode 100644 index 000000000..a21d700db --- /dev/null +++ b/gr-digital/grc/digital_cma_equalizer_cc.xml @@ -0,0 +1,37 @@ +<?xml version="1.0"?> +<!-- +################################################### +## CMA Equalizer +################################################### + --> +<block> + <name>CMA Equalizer</name> + <key>digital_cma_equalizer_cc</key> + <import>from gnuradio import digital</import> + <make>digital.cma_equalizer_cc($num_taps, $modulus, $mu)</make> + <callback>set_gain($mu)</callback> + <callback>set_modulus($modulus)</callback> + <param> + <name>Num. Taps</name> + <key>num_taps</key> + <type>int</type> + </param> + <param> + <name>Modulus</name> + <key>modulus</key> + <type>real</type> + </param> + <param> + <name>Mu</name> + <key>mu</key> + <type>real</type> + </param> + <sink> + <name>in</name> + <type>complex</type> + </sink> + <source> + <name>out</name> + <type>complex</type> + </source> +</block> diff --git a/gr-digital/grc/digital_costas_loop_cc.xml b/gr-digital/grc/digital_costas_loop_cc.xml new file mode 100644 index 000000000..087535b87 --- /dev/null +++ b/gr-digital/grc/digital_costas_loop_cc.xml @@ -0,0 +1,44 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Costas Loop +################################################### + --> +<block> + <name>Costas Loop</name> + <key>digital_costas_loop_cc</key> + <import>from gnuradio import digital</import> + <make>digital.costas_loop_cc($eta, $w, $order)</make> + <callback>set_damping_factor($eta)</callback> + <callback>set_natural_freq($w)</callback> + <param> + <name>Damping Factor</name> + <key>eta</key> + <type>real</type> + </param> + <param> + <name>Natural Frequency</name> + <key>w</key> + <type>real</type> + </param> + <param> + <name>Order</name> + <key>order</key> + <type>int</type> + </param> + <sink> + <name>in</name> + <type>complex</type> + </sink> + <source> + <name>out</name> + <type>complex</type> + </source> + + <!-- Optional Outputs --> + <source> + <name>frequency</name> + <type>float</type> + <optional>1</optional> + </source> +</block> diff --git a/gr-digital/grc/digital_dxpsk_demod.xml b/gr-digital/grc/digital_dxpsk_demod.xml new file mode 100644 index 000000000..5e6dced22 --- /dev/null +++ b/gr-digital/grc/digital_dxpsk_demod.xml @@ -0,0 +1,167 @@ +<?xml version="1.0"?> + +<!-- + Copyright 2009,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. +--> + +<!-- +################################################### +##DPSK2 Mod - 2, 4, 8 +################################################### + --> +<block> + <name>DPSK Demod</name> + <key>digital_dxpsk_demod</key> + <import>from gnuradio import digital</import> + <make>digital.$(type)_demod( + samples_per_symbol=$samples_per_symbol, + excess_bw=$excess_bw, + costas_alpha=$costas_alpha, + timing_alpha=$timing_alpha, + timing_max_dev=$timing_max_dev, + gray_code=$gray_code, + verbose=$verbose, + log=$log, + sync_out=$sync_out, +)</make> + <callback>clock_recov.set_alpha($costas_alpha)</callback> + <callback>clock_recov.set_beta(0.25*$costas_alpha**2)</callback> + <callback>time_recov.set_alpha($timing_alpha)</callback> + <param> + <name>Type</name> + <key>type</key> + <type>enum</type> + <option> + <name>DBPSK</name> + <key>dbpsk</key> + </option> + <option> + <name>DQPSK</name> + <key>dqpsk</key> + </option> + </param> + <param> + <name>Samples/Symbol</name> + <key>samples_per_symbol</key> + <value>2</value> + <type>int</type> + </param> + <param> + <name>Excess BW</name> + <key>excess_bw</key> + <value>0.35</value> + <type>real</type> + </param> + <param> + <name>Costas Alpha</name> + <key>costas_alpha</key> + <value>0.175</value> + <type>real</type> + </param> + <param> + <name>Timing Alpha</name> + <key>timing_alpha</key> + <value>0.100</value> + <type>real</type> + </param> + <param> + <name>Timing Max Dev</name> + <key>timing_max_dev</key> + <value>1.5</value> + <type>real</type> + </param> + <param> + <name>Omega Relative Limit</name> + <key>omega_relative_limit</key> + <value>0.005</value> + <type>real</type> + </param> + <param> + <name>Gray Code</name> + <key>gray_code</key> + <value>True</value> + <type>bool</type> + <option> + <name>Yes</name> + <key>True</key> + </option> + <option> + <name>No</name> + <key>False</key> + </option> + </param> + <param> + <name>Verbose</name> + <key>verbose</key> + <value>False</value> + <type>bool</type> + <hide>#if str($verbose) == 'False' then 'part' else 'none'#</hide> + <option> + <name>On</name> + <key>True</key> + </option> + <option> + <name>Off</name> + <key>False</key> + </option> + </param> + <param> + <name>Logging</name> + <key>log</key> + <value>False</value> + <type>bool</type> + <hide>#if str($log) == 'False' then 'part' else 'none'#</hide> + <option> + <name>On</name> + <key>True</key> + </option> + <option> + <name>Off</name> + <key>False</key> + </option> + </param> + <param> + <name>Sync Out</name> + <key>sync_out</key> + <value>False</value> + <type>bool</type> + <option> + <name>On</name> + <key>True</key> + </option> + <option> + <name>Off</name> + <key>False</key> + </option> + </param> + <sink> + <name>in</name> + <type>complex</type> + </sink> + <source> + <name>out</name> + <type>byte</type> + </source> + <source> + <name>sync</name> + <type>complex</type> + <optional>1</optional> + </source> +</block> diff --git a/gr-digital/grc/digital_dxpsk_mod.xml b/gr-digital/grc/digital_dxpsk_mod.xml new file mode 100644 index 000000000..5d59f36e0 --- /dev/null +++ b/gr-digital/grc/digital_dxpsk_mod.xml @@ -0,0 +1,121 @@ +<?xml version="1.0"?> + +<!-- + Copyright 2009,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. +--> + +<!-- +################################################### +##DPSK2 Mod - 2, 4, 8 +################################################### + --> +<block> + <name>DPSK Mod</name> + <key>digital_dxpsk_mod</key> + <import>from gnuradio import digital</import> + <make>digital.$(type)_mod( + samples_per_symbol=$samples_per_symbol, + excess_bw=$excess_bw, + gray_code=$gray_code, + verbose=$verbose, + log=$log) + </make> + <param> + <name>Type</name> + <key>type</key> + <type>enum</type> + <option> + <name>DBPSK</name> + <key>dbpsk</key> + </option> + <option> + <name>DQPSK</name> + <key>dqpsk</key> + </option> + <option> + <name>D8PSK</name> + <key>d8psk</key> + </option> + </param> + <param> + <name>Samples/Symbol</name> + <key>samples_per_symbol</key> + <value>2</value> + <type>int</type> + </param> + <param> + <name>Excess BW</name> + <key>excess_bw</key> + <value>0.35</value> + <type>real</type> + </param> + <param> + <name>Gray Code</name> + <key>gray_code</key> + <value>True</value> + <type>bool</type> + <option> + <name>Yes</name> + <key>True</key> + </option> + <option> + <name>No</name> + <key>False</key> + </option> + </param> + <param> + <name>Verbose</name> + <key>verbose</key> + <value>False</value> + <type>bool</type> + <hide>#if str($verbose) == 'False' then 'part' else 'none'#</hide> + <option> + <name>On</name> + <key>True</key> + </option> + <option> + <name>Off</name> + <key>False</key> + </option> + </param> + <param> + <name>Logging</name> + <key>log</key> + <value>False</value> + <type>bool</type> + <hide>#if str($log) == 'False' then 'part' else 'none'#</hide> + <option> + <name>On</name> + <key>True</key> + </option> + <option> + <name>Off</name> + <key>False</key> + </option> + </param> + <sink> + <name>in</name> + <type>byte</type> + </sink> + <source> + <name>out</name> + <type>complex</type> + </source> +</block> diff --git a/gr-digital/grc/digital_kurtotic_equalizer_cc.xml b/gr-digital/grc/digital_kurtotic_equalizer_cc.xml new file mode 100644 index 000000000..8c4a2012d --- /dev/null +++ b/gr-digital/grc/digital_kurtotic_equalizer_cc.xml @@ -0,0 +1,31 @@ +<?xml version="1.0"?> +<!-- +################################################### +## Kurtotic Equalizer +################################################### + --> +<block> + <name>Kurtotic Equalizer</name> + <key>digital_kurtotic_equalizer_cc</key> + <import>from gnuradio import digital</import> + <make>digital.kurtotic_equalizer_cc($num_taps, $mu)</make> + <callback>set_gain($mu)</callback> + <param> + <name>Num. Taps</name> + <key>num_taps</key> + <type>int</type> + </param> + <param> + <name>Mu</name> + <key>mu</key> + <type>real</type> + </param> + <sink> + <name>in</name> + <type>complex</type> + </sink> + <source> + <name>out</name> + <type>complex</type> + </source> +</block> diff --git a/gr-digital/lib/.gitignore b/gr-digital/lib/.gitignore new file mode 100644 index 000000000..1b6114c39 --- /dev/null +++ b/gr-digital/lib/.gitignore @@ -0,0 +1,4 @@ +/.libs +/.deps +/Makefile +/Makefile.in diff --git a/gr-digital/lib/Makefile.am b/gr-digital/lib/Makefile.am new file mode 100644 index 000000000..6f641b745 --- /dev/null +++ b/gr-digital/lib/Makefile.am @@ -0,0 +1,44 @@ +# +# 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. +# + +include $(top_srcdir)/Makefile.common + +AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(PYTHON_CPPFLAGS) $(WITH_INCLUDES) + +# These headers get installed in ${prefix}/include/gnuradio +grinclude_HEADERS = \ + digital_constellation.h \ + digital_costas_loop_cc.h \ + digital_cma_equalizer_cc.h \ + digital_kurtotic_equalizer_cc.h + +lib_LTLIBRARIES = libgnuradio-digital.la + +libgnuradio_digital_la_SOURCES = \ + digital_constellation.cc \ + digital_costas_loop_cc.cc \ + digital_cma_equalizer_cc.cc \ + digital_kurtotic_equalizer_cc.cc + +libgnuradio_digital_la_LIBADD = \ + $(GNURADIO_CORE_LA) + +libgnuradio_digital_la_LDFLAGS = $(NO_UNDEFINED) $(LTVERSIONFLAGS) diff --git a/gr-digital/lib/digital_cma_equalizer_cc.cc b/gr-digital/lib/digital_cma_equalizer_cc.cc new file mode 100644 index 000000000..89f56c16f --- /dev/null +++ b/gr-digital/lib/digital_cma_equalizer_cc.cc @@ -0,0 +1,43 @@ +/* -*- c++ -*- */ +/* + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_cma_equalizer_cc.h> + +digital_cma_equalizer_cc_sptr +digital_make_cma_equalizer_cc(int num_taps, float modulus, float mu) +{ + return gnuradio::get_initial_sptr(new digital_cma_equalizer_cc(num_taps, modulus, mu)); +} + +digital_cma_equalizer_cc::digital_cma_equalizer_cc(int num_taps, float modulus, float mu) + : gr_adaptive_fir_ccc("cma_equalizer_cc", 1, std::vector<gr_complex>(num_taps)) +{ + set_modulus(modulus); + set_gain(mu); + if (num_taps > 0) + d_taps[0] = 1.0; +} + diff --git a/gr-digital/lib/digital_cma_equalizer_cc.h b/gr-digital/lib/digital_cma_equalizer_cc.h new file mode 100644 index 000000000..54cba319c --- /dev/null +++ b/gr-digital/lib/digital_cma_equalizer_cc.h @@ -0,0 +1,89 @@ +/* -*- c++ -*- */ +/* + * 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. + */ + +#ifndef INCLUDED_DIGITAL_CMA_EQUALIZER_CC_H +#define INCLUDED_DIGITAL_CMA_EQUALIZER_CC_H + +#include <gr_adaptive_fir_ccc.h> +#include <gr_math.h> +#include <iostream> + +class digital_cma_equalizer_cc; +typedef boost::shared_ptr<digital_cma_equalizer_cc> digital_cma_equalizer_cc_sptr; + +digital_cma_equalizer_cc_sptr +digital_make_cma_equalizer_cc(int num_taps, float modulus, float mu); + +/*! + * \brief Implements constant modulus adaptive filter on complex stream + * \ingroup eq_blk + * + * The error value and tap update equations (for p=2) can be found in: + * + * D. Godard, "Self-Recovering Equalization and Carrier Tracking in + * Two-Dimensional Data Communication Systems," IEEE Transactions on + * Communications, Vol. 28, No. 11, pp. 1867 - 1875, 1980, + */ +class digital_cma_equalizer_cc : public gr_adaptive_fir_ccc +{ +private: + float d_modulus; + float d_mu; + + friend digital_cma_equalizer_cc_sptr digital_make_cma_equalizer_cc(int num_taps, + float modulus, + float mu); + digital_cma_equalizer_cc(int num_taps, float modulus, float mu); + +protected: + + virtual gr_complex error(const gr_complex &out) + { + gr_complex error = out*(norm(out) - d_modulus); + float re = gr_clip(error.real(), 1.0); + float im = gr_clip(error.imag(), 1.0); + return gr_complex(re, im); + } + + virtual void update_tap(gr_complex &tap, const gr_complex &in) + { + // Hn+1 = Hn - mu*conj(Xn)*zn*(|zn|^2 - 1) + tap -= d_mu*conj(in)*d_error; + } + +public: + void set_gain(float mu) + { + if(mu < 0) + throw std::out_of_range("digital_cma_equalizer::set_gain: Gain value must be >= 0"); + d_mu = mu; + } + + void set_modulus(float mod) + { + if(mod < 0) + throw std::out_of_range("digital_cma_equalizer::set_modulus: Modulus value must be >= 0"); + d_modulus = mod; + } +}; + +#endif diff --git a/gr-digital/lib/digital_constellation.cc b/gr-digital/lib/digital_constellation.cc new file mode 100644 index 000000000..bfed84320 --- /dev/null +++ b/gr-digital/lib/digital_constellation.cc @@ -0,0 +1,464 @@ +/* -*- 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 <digital_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 + +digital_constellation::digital_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(); +} + +digital_constellation::digital_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 +digital_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> +digital_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 +digital_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 +digital_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 +digital_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 digital_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> digital_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> > +digital_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 +digital_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 +digital_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 +digital_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 +digital_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 +digital_constellation::decision_maker_v (std::vector<gr_complex> sample) +{ + assert(sample.size() == d_dimensionality); + return decision_maker (&(sample[0])); +} + +digital_constellation_calcdist_sptr +digital_make_constellation_calcdist(std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int dimensionality) +{ + return digital_constellation_calcdist_sptr(new digital_constellation_calcdist + (constellation, pre_diff_code, + rotational_symmetry, dimensionality)); +} + +digital_constellation_calcdist::digital_constellation_calcdist(std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int dimensionality) : + digital_constellation(constellation, pre_diff_code, rotational_symmetry, dimensionality) +{} + +// Chooses points base on shortest distance. +// Inefficient. +unsigned int +digital_constellation_calcdist::decision_maker(const gr_complex *sample) +{ + return get_closest_point(sample); +} + +digital_constellation_sector::digital_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) : + digital_constellation(constellation, pre_diff_code, rotational_symmetry, dimensionality), + n_sectors(n_sectors) +{ +} + +unsigned int +digital_constellation_sector::decision_maker (const gr_complex *sample) +{ + unsigned int sector; + sector = get_sector(sample); + return sector_values[sector]; +} + +void +digital_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)); + } +} + +digital_constellation_rect_sptr +digital_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 digital_constellation_rect_sptr(new digital_constellation_rect + (constellation, pre_diff_code, + rotational_symmetry, + real_sectors, imag_sectors, + width_real_sectors, + width_imag_sectors)); + } + +digital_constellation_rect::digital_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) : + digital_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 +digital_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 >= (int)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 >= (int)n_imag_sectors) + imag_sector = n_imag_sectors-1; + + sector = real_sector * n_imag_sectors + imag_sector; + return sector; +} + +unsigned int +digital_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; +} + + +digital_constellation_psk_sptr +digital_make_constellation_psk(std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int n_sectors) +{ + return digital_constellation_psk_sptr(new digital_constellation_psk + (constellation, pre_diff_code, + n_sectors)); +} + +digital_constellation_psk::digital_constellation_psk (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int n_sectors) : + digital_constellation_sector(constellation, pre_diff_code, constellation.size(), 1, n_sectors) +{ + find_sector_values(); +} + +unsigned int +digital_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 +digital_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; +} + + +digital_constellation_bpsk_sptr +digital_make_constellation_bpsk() +{ + return digital_constellation_bpsk_sptr(new digital_constellation_bpsk ()); +} + +digital_constellation_bpsk::digital_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 +digital_constellation_bpsk::decision_maker(const gr_complex *sample) +{ + return (real(*sample) > 0); +} + + +digital_constellation_qpsk_sptr +digital_make_constellation_qpsk() +{ + return digital_constellation_qpsk_sptr(new digital_constellation_qpsk ()); +} + +digital_constellation_qpsk::digital_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 +digital_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); +} + + +digital_constellation_8psk_sptr +digital_make_constellation_8psk() +{ + return digital_constellation_8psk_sptr(new digital_constellation_8psk ()); +} + +digital_constellation_8psk::digital_constellation_8psk () +{ + float angle = M_PI/8.0; + d_constellation.resize(8); + // Gray-coded + d_constellation[0] = gr_complex(cos( 1*angle), sin( 1*angle)); + d_constellation[1] = gr_complex(cos( 7*angle), sin( 3*angle)); + d_constellation[2] = gr_complex(cos(15*angle), sin(15*angle)); + d_constellation[3] = gr_complex(cos( 9*angle), sin( 9*angle)); + d_constellation[4] = gr_complex(cos( 3*angle), sin( 3*angle)); + d_constellation[5] = gr_complex(cos( 5*angle), sin( 5*angle)); + d_constellation[6] = gr_complex(cos(13*angle), sin(13*angle)); + d_constellation[7] = gr_complex(cos(11*angle), sin(11*angle)); + d_rotational_symmetry = 8; + d_dimensionality = 1; + calc_arity(); +} + +unsigned int +digital_constellation_8psk::decision_maker(const gr_complex *sample) +{ + // Real component determines small bit. + // Imag component determines big bit. + return 2*(imag(*sample)>0) + (real(*sample)>0); + + unsigned int real, imag; + imag = 2*(sample->imag()<=0); + real = (sample->real()<=0); + if(abs(sample->real()) >= abs(sample->imag())) { + return 0 + imag + real; + } + else { + return 4 + imag + real; + } +} diff --git a/gr-digital/lib/digital_constellation.h b/gr-digital/lib/digital_constellation.h new file mode 100644 index 000000000..9da87a4de --- /dev/null +++ b/gr-digital/lib/digital_constellation.h @@ -0,0 +1,352 @@ +/* -*- 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_DIGITAL_CONSTELLATION_H +#define INCLUDED_DIGITAL_CONSTELLATION_H + +#include <vector> +#include <math.h> +#include <gr_complex.h> +#include <boost/enable_shared_from_this.hpp> +#include <gr_metric_type.h> + +/************************************************************/ +/* digital_constellation */ +/* */ +/* Base class defining interface. */ +/************************************************************/ + +class digital_constellation; +typedef boost::shared_ptr<digital_constellation> digital_constellation_sptr; + +class digital_constellation : public boost::enable_shared_from_this<digital_constellation> +{ +public: + digital_constellation (std::vector<gr_complex> constellation, std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, unsigned int dimensionality); + digital_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; + } + + digital_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 (); +}; + +/************************************************************/ +/* digital_constellation_calcdist */ +/* */ +/* Constellation which calculates the distance to each */ +/* point in the constellation for decision making. */ +/* Inefficient for large constellations. */ +/************************************************************/ + +class digital_constellation_calcdist; +typedef boost::shared_ptr<digital_constellation_calcdist> digital_constellation_calcdist_sptr; + +// public constructor +digital_constellation_calcdist_sptr +digital_make_constellation_calcdist (std::vector<gr_complex> constellation, std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, unsigned int dimensionality); + + +class digital_constellation_calcdist : public digital_constellation +{ + public: + digital_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 digital_constellation_calcdist_sptr + digital_make_constellation_calcdist (std::vector<gr_complex> constellation); +}; + +/************************************************************/ +/* digital_constellation_sector */ +/* */ +/* An abstract class. */ +/* Constellation space is divided into sectors. */ +/* Each sector is associated with the nearest constellation */ +/* point. */ +/************************************************************/ + +class digital_constellation_sector : public digital_constellation +{ + public: + + digital_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; + +}; + +/************************************************************/ +/* digital_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 digital_constellation_rect; +typedef boost::shared_ptr<digital_constellation_rect> digital_constellation_rect_sptr; + +// public constructor +digital_constellation_rect_sptr +digital_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 digital_constellation_rect : public digital_constellation_sector +{ + public: + + digital_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 digital_constellation_rect_sptr + digital_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); + +}; + +/************************************************************/ +/* digital_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 digital_constellation_psk; +typedef boost::shared_ptr<digital_constellation_psk> digital_constellation_psk_sptr; + +// public constructor +digital_constellation_psk_sptr +digital_make_constellation_psk (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int n_sectors); + +class digital_constellation_psk : public digital_constellation_sector +{ + public: + + digital_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 digital_constellation_psk_sptr + digital_make_constellation_psk (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int n_sectors); + +}; + +/************************************************************/ +/* digital_constellation_bpsk */ +/* */ +/* Only works for BPSK. */ +/* */ +/************************************************************/ + +class digital_constellation_bpsk; +typedef boost::shared_ptr<digital_constellation_bpsk> digital_constellation_bpsk_sptr; + +// public constructor +digital_constellation_bpsk_sptr +digital_make_constellation_bpsk (); + +class digital_constellation_bpsk : public digital_constellation +{ + public: + + digital_constellation_bpsk (); + unsigned int decision_maker (const gr_complex *sample); + + friend digital_constellation_bpsk_sptr + digital_make_constellation_bpsk (); + +}; + +/************************************************************/ +/* digital_constellation_qpsk */ +/* */ +/* Only works for QPSK. */ +/* */ +/************************************************************/ + +class digital_constellation_qpsk; +typedef boost::shared_ptr<digital_constellation_qpsk> digital_constellation_qpsk_sptr; + +// public constructor +digital_constellation_qpsk_sptr +digital_make_constellation_qpsk (); + +class digital_constellation_qpsk : public digital_constellation +{ + public: + + digital_constellation_qpsk (); + unsigned int decision_maker (const gr_complex *sample); + + friend digital_constellation_qpsk_sptr + digital_make_constellation_qpsk (); + +}; + + +/************************************************************/ +/* digital_constellation_8psk */ +/* */ +/* Only works for 8PSK. */ +/* */ +/************************************************************/ + +class digital_constellation_8psk; +typedef boost::shared_ptr<digital_constellation_8psk> digital_constellation_8psk_sptr; + +// public constructor +digital_constellation_8psk_sptr +digital_make_constellation_8psk (); + +class digital_constellation_8psk : public digital_constellation +{ + public: + + digital_constellation_8psk (); + unsigned int decision_maker (const gr_complex *sample); + + friend digital_constellation_8psk_sptr + digital_make_constellation_8psk (); + +}; + +#endif diff --git a/gr-digital/lib/digital_costas_loop_cc.cc b/gr-digital/lib/digital_costas_loop_cc.cc new file mode 100644 index 000000000..5d98bde4c --- /dev/null +++ b/gr-digital/lib/digital_costas_loop_cc.cc @@ -0,0 +1,201 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_costas_loop_cc.h> +#include <gr_io_signature.h> +#include <gr_expj.h> +#include <gr_sincos.h> +#include <gr_math.h> + +#define M_TWOPI (2*M_PI) + +digital_costas_loop_cc_sptr +digital_make_costas_loop_cc (float damping, float nat_freq, + int order + ) throw (std::invalid_argument) +{ + return gnuradio::get_initial_sptr(new digital_costas_loop_cc (damping, + nat_freq, + order)); +} + +digital_costas_loop_cc::digital_costas_loop_cc (float damping, float nat_freq, + int order + ) throw (std::invalid_argument) + : gr_sync_block ("costas_loop_cc", + gr_make_io_signature (1, 1, sizeof (gr_complex)), + gr_make_io_signature2 (1, 2, sizeof (gr_complex), sizeof(float))), + d_max_freq(1.0), d_min_freq(-1.0), d_phase(0), d_freq(0.0), + d_nat_freq(nat_freq), d_damping(damping), + d_order(order), d_phase_detector(NULL) +{ + // initialize gains from the natural freq and damping factors + update_gains(); + + switch(d_order) { + case 2: + d_phase_detector = &digital_costas_loop_cc::phase_detector_2; + break; + + case 4: + d_phase_detector = &digital_costas_loop_cc::phase_detector_4; + break; + + case 8: + d_phase_detector = &digital_costas_loop_cc::phase_detector_8; + break; + + default: + throw std::invalid_argument("order must be 2, 4, or 8"); + break; + } +} + +float +digital_costas_loop_cc::phase_detector_8(gr_complex sample) const +{ + /* This technique splits the 8PSK constellation into 2 squashed + QPSK constellations, one when I is larger than Q and one where + Q is larger than I. The error is then calculated proportionally + to these squashed constellations by the const K = sqrt(2)-1. + + The signal magnitude must be > 1 or K will incorrectly bias + the error value. + + Ref: Z. Huang, Z. Yi, M. Zhang, K. Wang, "8PSK demodulation for + new generation DVB-S2", IEEE Proc. Int. Conf. Communications, + Circuits and Systems, Vol. 2, pp. 1447 - 1450, 2004. + */ + + float K = (sqrt(2.0) - 1); + if(fabsf(sample.real()) >= fabsf(sample.imag())) { + return ((sample.real()>0 ? 1.0 : -1.0) * sample.imag() - + (sample.imag()>0 ? 1.0 : -1.0) * sample.real() * K); + } + else { + return ((sample.real()>0 ? 1.0 : -1.0) * sample.imag() * K - + (sample.imag()>0 ? 1.0 : -1.0) * sample.real()); + } +} + +float +digital_costas_loop_cc::phase_detector_4(gr_complex sample) const +{ + + return ((sample.real()>0 ? 1.0 : -1.0) * sample.imag() - + (sample.imag()>0 ? 1.0 : -1.0) * sample.real()); +} + +float +digital_costas_loop_cc::phase_detector_2(gr_complex sample) const +{ + return (sample.real()*sample.imag()); +} + +void +digital_costas_loop_cc::set_natural_freq(float w) +{ + d_nat_freq = w; + update_gains(); +} + +void +digital_costas_loop_cc::set_damping_factor(float eta) +{ + d_damping = eta; + update_gains(); +} + +void +digital_costas_loop_cc::update_gains() +{ + d_beta = d_nat_freq*d_nat_freq; + d_alpha = 2*d_damping*d_nat_freq; +} + +int +digital_costas_loop_cc::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const gr_complex *iptr = (gr_complex *) input_items[0]; + gr_complex *optr = (gr_complex *) output_items[0]; + float *foptr = (float *) output_items[1]; + + bool write_foptr = output_items.size() >= 2; + + float error; + gr_complex nco_out; + + if (write_foptr) { + + for (int i = 0; i < noutput_items; i++){ + nco_out = gr_expj(-d_phase); + optr[i] = iptr[i] * nco_out; + + error = (*this.*d_phase_detector)(optr[i]); + error = gr_branchless_clip(error, 1.0); + + d_freq = d_freq + d_beta * error; + d_phase = d_phase + d_freq + d_alpha * error; + + while(d_phase>M_TWOPI) + d_phase -= M_TWOPI; + while(d_phase<-M_TWOPI) + d_phase += M_TWOPI; + + if (d_freq > d_max_freq) + d_freq = d_min_freq; + else if (d_freq < d_min_freq) + d_freq = d_max_freq; + + foptr[i] = d_freq; + } + } else { + for (int i = 0; i < noutput_items; i++){ + nco_out = gr_expj(-d_phase); + optr[i] = iptr[i] * nco_out; + + error = (*this.*d_phase_detector)(optr[i]); + error = gr_branchless_clip(error, 1.0); + + d_freq = d_freq + d_beta * error; + d_phase = d_phase + d_freq + d_alpha * error; + + while(d_phase>M_TWOPI) + d_phase -= M_TWOPI; + while(d_phase<-M_TWOPI) + d_phase += M_TWOPI; + + if (d_freq > d_max_freq) + d_freq = d_min_freq; + else if (d_freq < d_min_freq) + d_freq = d_max_freq; + + } + } + return noutput_items; +} diff --git a/gr-digital/lib/digital_costas_loop_cc.h b/gr-digital/lib/digital_costas_loop_cc.h new file mode 100644 index 000000000..2c4c38e8b --- /dev/null +++ b/gr-digital/lib/digital_costas_loop_cc.h @@ -0,0 +1,150 @@ +/* -*- 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_DIGITAL_COSTAS_LOOP_CC_H +#define INCLUDED_DIGITAL_COSTAS_LOOP_CC_H + +#include <gr_sync_block.h> +#include <stdexcept> +#include <fstream> + + +/*! \brief A Costas loop carrier recovery module. + * \ingroup sync_blk + * + * The Costas loop locks to the center frequency of a signal and + * downconverts it to baseband. The second (order=2) order loop is + * used for BPSK where the real part of the output signal is the + * baseband BPSK signal and the imaginary part is the error + * signal. When order=4, it can be used for quadrature modulations + * where both I and Q (real and imaginary) are outputted. + * + * More details can be found online: + * + * J. Feigin, "Practical Costas loop design: Designing a simple and inexpensive + * BPSK Costas loop carrier recovery circuit," RF signal processing, pp. 20-36, + * 2002. + * + * http://rfdesign.com/images/archive/0102Feigin20.pdf + * + * \param alpha the loop gain used for phase adjustment + * \param beta the loop gain for frequency adjustments + * \param max_freq the maximum frequency deviation (radians/sample) the loop can handle + * \param min_freq the minimum frequency deviation (radians/sample) the loop can handle + * \param order the loop order, either 2 or 4 + */ +class digital_costas_loop_cc; +typedef boost::shared_ptr<digital_costas_loop_cc> digital_costas_loop_cc_sptr; + + +digital_costas_loop_cc_sptr +digital_make_costas_loop_cc (float damping, float nat_freq, + int order + ) throw (std::invalid_argument); + + +/*! + * \brief Carrier tracking PLL for QPSK + * \ingroup sync_blk + * input: complex; output: complex + * <br>The Costas loop can have two output streams: + * stream 1 is the baseband I and Q; + * stream 2 is the normalized frequency of the loop + * + * \p order must be 2 or 4. + */ +class digital_costas_loop_cc : public gr_sync_block +{ + friend digital_costas_loop_cc_sptr + digital_make_costas_loop_cc (float damping, float nat_freq, + int order + ) throw (std::invalid_argument); + + float d_alpha, d_beta, d_max_freq, d_min_freq, d_phase, d_freq; + float d_damping, d_nat_freq; + int d_order; + + digital_costas_loop_cc (float damping, float nat_freq, + int order + ) throw (std::invalid_argument); + + + /*! \brief update the system gains from omega and eta + * + * This function updates the system gains based on the natural + * frequency (omega) and damping factor (eta) of the system. + * These two factors can be set separately through their own + * set functions. + * + * These equations are summarized nicely in this paper from Berkeley: + * http://www.complextoreal.com/chapters/pll.pdf + */ + void update_gains(); + + /*! \brief the phase detector circuit for 8th-order PSK loops + * \param sample complex sample + * \return the phase error + */ + float phase_detector_8(gr_complex sample) const; // for 8PSK + + /*! \brief the phase detector circuit for fourth-order loops + * \param sample complex sample + * \return the phase error + */ + float phase_detector_4(gr_complex sample) const; // for QPSK + + /*! \brief the phase detector circuit for second-order loops + * \param sample a complex sample + * \return the phase error + */ + float phase_detector_2(gr_complex sample) const; // for BPSK + + + float (digital_costas_loop_cc::*d_phase_detector)(gr_complex sample) const; + +public: + + void set_natural_freq(float w); + void set_damping_factor(float eta); + + /*! \brief get the first order gain + * + */ + float alpha() const { return d_alpha; } + + /*! \brief get the second order gain + * + */ + float beta() const { return d_beta; } + + int work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + /*! \brief returns the current NCO frequency in radians/sample + * + */ + float freq() const { return d_freq; } +}; + +#endif diff --git a/gr-digital/lib/digital_kurtotic_equalizer_cc.cc b/gr-digital/lib/digital_kurtotic_equalizer_cc.cc new file mode 100644 index 000000000..c95b56021 --- /dev/null +++ b/gr-digital/lib/digital_kurtotic_equalizer_cc.cc @@ -0,0 +1,51 @@ +/* -*- c++ -*- */ +/* + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <digital_kurtotic_equalizer_cc.h> + +digital_kurtotic_equalizer_cc_sptr +digital_make_kurtotic_equalizer_cc(int num_taps, float mu) +{ + return gnuradio::get_initial_sptr(new digital_kurtotic_equalizer_cc(num_taps, mu)); +} + +digital_kurtotic_equalizer_cc::digital_kurtotic_equalizer_cc(int num_taps, float mu) + : gr_adaptive_fir_ccc("kurtotic_equalizer_cc", 1, std::vector<gr_complex>(num_taps)) +{ + set_gain(mu); + if (num_taps > 0) + d_taps[0] = 1.0; + + d_alpha_p = 0.01; + d_alpha_q = 0.01; + d_alpha_m = 0.01; + + d_p = 0.0f; + d_m = 0.0f; + d_q = gr_complex(0,0); + d_u = gr_complex(0,0); +} + diff --git a/gr-digital/lib/digital_kurtotic_equalizer_cc.h b/gr-digital/lib/digital_kurtotic_equalizer_cc.h new file mode 100644 index 000000000..e01cbd6e6 --- /dev/null +++ b/gr-digital/lib/digital_kurtotic_equalizer_cc.h @@ -0,0 +1,111 @@ +/* -*- c++ -*- */ +/* + * 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. + */ + +#ifndef INCLUDED_DIGITAL_KURTOTIC_EQUALIZER_CC_H +#define INCLUDED_DIGITAL_KURTOTIC_EQUALIZER_CC_H + +#include <gr_adaptive_fir_ccc.h> +#include <gr_math.h> +#include <iostream> + +class digital_kurtotic_equalizer_cc; +typedef boost::shared_ptr<digital_kurtotic_equalizer_cc> digital_kurtotic_equalizer_cc_sptr; + +digital_kurtotic_equalizer_cc_sptr +digital_make_kurtotic_equalizer_cc(int num_taps, float mu); + +/*! + * \brief Implements a kurtosis-based adaptive equalizer on complex stream + * \ingroup eq_blk + * + * Y. Guo, J. Zhao, Y. Sun, "Sign kurtosis maximization based blind + * equalization algorithm," IEEE Conf. on Control, Automation, + * Robotics and Vision, Vol. 3, Dec. 2004, pp. 2052 - 2057. + */ +class digital_kurtotic_equalizer_cc : public gr_adaptive_fir_ccc +{ +private: + float d_mu; + float d_p, d_m; + gr_complex d_q, d_u; + float d_alpha_p, d_alpha_q, d_alpha_m; + + friend digital_kurtotic_equalizer_cc_sptr digital_make_kurtotic_equalizer_cc(int num_taps, + float mu); + digital_kurtotic_equalizer_cc(int num_taps, float mu); + + gr_complex sign(gr_complex x) + { + float re = (float)(x.real() >= 0.0f); + float im = (float)(x.imag() >= 0.0f); + return gr_complex(re, im); + } + +protected: + + virtual gr_complex error(const gr_complex &out) + { + + // p = E[|z|^2] + // q = E[z^2] + // m = E[|z|^4] + // u = E[kurtosis(z)] + + float nrm = norm(out); + gr_complex cnj = conj(out); + float epsilon_f = 1e-12; + gr_complex epsilon_c = gr_complex(1e-12, 1e-12); + + + d_p = (1-d_alpha_p)*d_p + (d_alpha_p)*nrm + epsilon_f; + d_q = (1-d_alpha_q)*d_q + (d_alpha_q)*out*out + epsilon_c; + d_m = (1-d_alpha_m)*d_m + (d_alpha_m)*nrm*nrm + epsilon_f; + d_u = d_m - 2.0f*(d_p*d_p) - d_q*d_q; + + gr_complex F = (1.0f / (d_p*d_p*d_p)) * + (sign(d_u) * (nrm*cnj - 2.0f*d_p*cnj - conj(d_q)*out) - + abs(d_u)*cnj); + + //std::cout << "out: " << out << " p: " << d_p << " q: " << d_q; + //std::cout << " m: " << d_m << " u: " << d_u << std::endl; + //std::cout << "error: " << F << std::endl; + + float re = gr_clip(F.real(), 1.0); + float im = gr_clip(F.imag(), 1.0); + return gr_complex(re, im); + } + + virtual void update_tap(gr_complex &tap, const gr_complex &in) + { + tap += d_mu*in*d_error; + } + +public: + void set_gain(float mu) + { + if(mu < 0) + throw std::out_of_range("digital_kurtotic_equalizer::set_gain: Gain value must be >= 0"); + d_mu = mu; + } +}; + +#endif diff --git a/gr-digital/python/.gitignore b/gr-digital/python/.gitignore new file mode 100644 index 000000000..604b402c5 --- /dev/null +++ b/gr-digital/python/.gitignore @@ -0,0 +1,3 @@ +/Makefile +/Makefile.in +/run_tests diff --git a/gr-digital/python/Makefile.am b/gr-digital/python/Makefile.am new file mode 100644 index 000000000..13a3d1157 --- /dev/null +++ b/gr-digital/python/Makefile.am @@ -0,0 +1,42 @@ +# +# 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. +# + +include $(top_srcdir)/Makefile.common + +TESTS = +EXTRA_DIST += run_tests.in + +if PYTHON +TESTS += run_tests + +digitaldir = $(grpythondir)/digital + +noinst_PYTHON = \ + qa_digital.py \ + qa_costas_loop_cc.py + +digital_PYTHON = \ + __init__.py \ + psk.py \ + dbpsk.py \ + dqpsk.py \ + d8psk.py +endif diff --git a/gr-digital/python/__init__.py b/gr-digital/python/__init__.py new file mode 100644 index 000000000..4bbb9850b --- /dev/null +++ b/gr-digital/python/__init__.py @@ -0,0 +1,27 @@ +# +# 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. +# + +# The presence of this file turns this directory into a Python package + +from digital_swig import * +from dbpsk import * +from dqpsk import * +from d8psk import * diff --git a/gr-digital/python/d8psk.py b/gr-digital/python/d8psk.py new file mode 100644 index 000000000..8bed395a7 --- /dev/null +++ b/gr-digital/python/d8psk.py @@ -0,0 +1,369 @@ +# +# Copyright 2005,2006,2007,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. +# + +# See gnuradio-examples/python/digital for examples + +""" +differential 8PSK modulation and demodulation. +""" + +from gnuradio import gr, modulation_utils2 +from math import pi, sqrt +import digital_swig, psk +import cmath +from pprint import pprint + +# default values (used in __init__ and add_options) +_def_samples_per_symbol = 3 +_def_excess_bw = 0.35 +_def_gray_code = True +_def_verbose = False +_def_log = False + +_def_freq_alpha = 0.010 +_def_phase_damping = 0.4 +_def_phase_natfreq = 0.25 +_def_timing_alpha = 0.100 +_def_timing_beta = 0.010 +_def_timing_max_dev = 1.5 + +# ///////////////////////////////////////////////////////////////////////////// +# D8PSK modulator +# ///////////////////////////////////////////////////////////////////////////// + +class d8psk_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 differential 8PSK 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 log: Log modulation data to files? + @type log: bool + """ + + gr.hier_block2.__init__(self, "d8psk_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) + + 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(psk.binary_to_gray[arity]) + else: + self.symbol_mapper = gr.map_bb(psk.binary_to_ungray[arity]) + + self.diffenc = gr.diff_encoder_bb(arity) + + rot = 1 + rotated_const = map(lambda pt: pt * rot, psk.constellation[arity]) + self.chunks2symbols = gr.chunks_to_symbols_bc(rotated_const) + + # pulse shaping filter + nfilts = 32 + ntaps = 11 * int(nfilts * self._samples_per_symbol) # make nfilts filters of ntaps each + self.rrc_taps = gr.firdes.root_raised_cosine( + nfilts, # gain + nfilts, # sampling rate based on 32 filters in resampler + 1.0, # symbol rate + self._excess_bw, # excess bandwidth (roll-off factor) + ntaps) + self.rrc_filter = gr.pfb_arb_resampler_ccf(self._samples_per_symbol, self.rrc_taps) + + if verbose: + self._print_verbage() + + if log: + self._setup_logging() + + # Connect & Initialize base class + 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 "\nModulator:" + print "bits per symbol: %d" % self.bits_per_symbol() + print "Gray code: %s" % self._gray_code + print "RRC 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, "tx_bytes2chunks.dat")) + self.connect(self.symbol_mapper, + gr.file_sink(gr.sizeof_char, "tx_graycoder.dat")) + 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")) + + def add_options(parser): + """ + Adds 8PSK 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_utils2.extract_kwargs_from_options(d8psk_mod.__init__, + ('self',), options) + extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) + + +# ///////////////////////////////////////////////////////////////////////////// +# D8PSK demodulator +# +# Differentially coherent detection of differentially encoded 8psk +# ///////////////////////////////////////////////////////////////////////////// + +class d8psk_demod(gr.hier_block2): + + def __init__(self, + samples_per_symbol=_def_samples_per_symbol, + excess_bw=_def_excess_bw, + freq_alpha=_def_freq_alpha, + phase_damping=_def_phase_damping, + phase_natfreq=_def_phase_natfreq, + timing_alpha=_def_timing_alpha, + timing_max_dev=_def_timing_max_dev, + gray_code=_def_gray_code, + verbose=_def_verbose, + log=_def_log, + sync_out=False): + """ + Hierarchical block for RRC-filtered DQPSK demodulation + + The input is the complex modulated signal at baseband. + The output is a stream of bits packed 1 bit per byte (LSB) + + @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 phase_damping: loop filter damping factor for phase/fine frequency recovery + @type phase_damping: float + @param phase_natfreq: loop filter natural frequency for phase/fine frequency recovery + @type phase_natfreq: float + @param timing_alpha: timing loop alpha gain + @type timing_alpha: float + @param timing_max: timing loop maximum rate deviations + @type timing_max: 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 + """ + if sync_out: io_sig_out = gr.io_signaturev(2, 2, (gr.sizeof_char, gr.sizeof_gr_complex)) + else: io_sig_out = gr.io_signature(1, 1, gr.sizeof_char) + + gr.hier_block2.__init__(self, "d8psk_demod", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + io_sig_out) # Output signature + + self._samples_per_symbol = samples_per_symbol + self._excess_bw = excess_bw + self._freq_alpha = freq_alpha + self._freq_beta = 0.25*self._freq_alpha**2 + self._phase_damping = phase_damping + self._phase_natfreq = phase_natfreq + self._timing_alpha = timing_alpha + self._timing_beta = _def_timing_beta + self._timing_max_dev=timing_max_dev + self._gray_code = gray_code + + if samples_per_symbol < 2: + raise TypeError, "sbp must be >= 2, is %d" % 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) + #self.agc = gr.feedforward_agc_cc(16, 2.0) + + # 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(samples_per_symbol*nfilts) + taps = gr.firdes.root_raised_cosine(1.41*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) + + # Perform phase / fine frequency correction + self.phase_recov = digital_swig.costas_loop_cc(self._phase_damping, + self._phase_natfreq, + arity) + + # Perform Differential decoding on the constellation + self.diffdec = gr.diff_phasor_cc() + + # find closest constellation point + rot = cmath.exp(1j*cmath.pi/8.0) + rotated_const = map(lambda pt: pt * rot, psk.constellation[arity]) + self.slicer = gr.constellation_decoder_cb(rotated_const, range(arity)) + + if self._gray_code: + self.symbol_mapper = gr.map_bb(psk.gray_to_binary[arity]) + else: + self.symbol_mapper = gr.map_bb(psk.ungray_to_binary[arity]) + + # 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 + self.connect(self, self.agc, + self.freq_recov, self.time_recov, self.phase_recov, + self.diffdec, self.slicer, self.symbol_mapper, self.unpack, self) + if sync_out: self.connect(self.phase_recov, (self, 1)) + + 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 "\nDemodulator:" + print "bits per symbol: %d" % self.bits_per_symbol() + print "Gray code: %s" % self._gray_code + print "RRC roll-off factor: %.2f" % self._excess_bw + print "FLL gain: %.2f" % self._freq_alpha + print "Timing alpha gain: %.2f" % self._timing_alpha + print "Timing beta gain: %.2f" % 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, + gr.file_sink(gr.sizeof_gr_complex, "rx_freq_recov.dat")) + self.connect(self.time_recov, + gr.file_sink(gr.sizeof_gr_complex, "rx_time_recov.dat")) + self.connect(self.phase_recov, + gr.file_sink(gr.sizeof_gr_complex, "rx_phase_recov.dat")) + self.connect(self.diffdec, + gr.file_sink(gr.sizeof_gr_complex, "rx_diffdec.dat")) + self.connect(self.slicer, + gr.file_sink(gr.sizeof_char, "rx_slicer.dat")) + self.connect(self.symbol_mapper, + gr.file_sink(gr.sizeof_char, "rx_gray_decoder.dat")) + self.connect(self.unpack, + gr.file_sink(gr.sizeof_char, "rx_unpack.dat")) + + def add_options(parser): + """ + Adds D8PSK demodulation-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)") + parser.add_option("", "--freq-alpha", type="float", default=_def_freq_alpha, + help="set frequency lock loop alpha gain value [default=%default] (PSK)") + parser.add_option("", "--phase-alpha", type="float", default=_def_phase_alpha, + help="set phase tracking loop alpha value [default=%default] (PSK)") + parser.add_option("", "--timing-alpha", type="float", default=_def_timing_alpha, + help="set timing symbol sync loop gain alpha value [default=%default] (GMSK/PSK)") + parser.add_option("", "--timing-beta", type="float", default=_def_timing_beta, + help="set timing symbol sync loop gain beta value [default=%default] (GMSK/PSK)") + parser.add_option("", "--timing-max-dev", type="float", default=_def_timing_max_dev, + help="set timing symbol sync loop maximum deviation [default=%default] (GMSK/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_utils2.extract_kwargs_from_options( + d8psk_demod.__init__, ('self',), options) + extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) + + +# +# Add these to the mod/demod registry +# +modulation_utils2.add_type_1_mod('d8psk', d8psk_mod) +modulation_utils2.add_type_1_demod('d8psk', d8psk_demod) diff --git a/gr-digital/python/dbpsk.py b/gr-digital/python/dbpsk.py new file mode 100644 index 000000000..2e9b756e6 --- /dev/null +++ b/gr-digital/python/dbpsk.py @@ -0,0 +1,370 @@ +# +# Copyright 2009,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. +# + +# See gnuradio-examples/python/digital for examples + +""" +differential BPSK modulation and demodulation. +""" + +from gnuradio import gr, modulation_utils2 +from math import pi, sqrt, ceil +import digital_swig, psk +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_freq_alpha = 0.010 +_def_phase_damping = 0.4 +_def_phase_natfreq = 0.25 +_def_timing_alpha = 0.100 +_def_timing_beta = 0.010 +_def_timing_max_dev = 1.5 + + +# ///////////////////////////////////////////////////////////////////////////// +# DBPSK modulator +# ///////////////////////////////////////////////////////////////////////////// + +class dbpsk_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 differential BPSK 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 log: Log modulation data to files? + @type log: bool + """ + + gr.hier_block2.__init__(self, "dbpsk_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 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()) + + # 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(psk.binary_to_gray[arity]) + else: + self.symbol_mapper = gr.map_bb(psk.binary_to_ungray[arity]) + + self.diffenc = gr.diff_encoder_bb(arity) + + self.chunks2symbols = gr.chunks_to_symbols_bc(psk.constellation[arity]) + + # pulse shaping filter + nfilts = 32 + ntaps = nfilts * 11 * int(self._samples_per_symbol) # make nfilts filters of ntaps each + self.rrc_taps = gr.firdes.root_raised_cosine( + nfilts, # gain + nfilts, # sampling rate based on 32 filters in resampler + 1.0, # symbol rate + self._excess_bw, # excess bandwidth (roll-off factor) + ntaps) + self.rrc_filter = gr.pfb_arb_resampler_ccf(self._samples_per_symbol, self.rrc_taps) + + # Connect + self.connect(self, self.bytes2chunks, self.symbol_mapper, self.diffenc, + self.chunks2symbols, self.rrc_filter, self) + + 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=None): # static method that's also callable on an instance + return 1 + bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM + + def add_options(parser): + """ + Adds DBPSK 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]") + parser.add_option("", "--no-gray-code", dest="gray_code", + action="store_false", default=True, + 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_utils2.extract_kwargs_from_options(dbpsk_mod.__init__, + ('self',), options) + extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) + + + def _print_verbage(self): + print "\nModulator:" + print "bits per symbol: %d" % self.bits_per_symbol() + print "Gray code: %s" % self._gray_code + 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")) + self.connect(self.symbol_mapper, + gr.file_sink(gr.sizeof_char, "tx_graycoder.dat")) + 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")) + + +# ///////////////////////////////////////////////////////////////////////////// +# DBPSK demodulator +# +# Differentially coherent detection of differentially encoded BPSK +# ///////////////////////////////////////////////////////////////////////////// + +class dbpsk_demod(gr.hier_block2): + + def __init__(self, + samples_per_symbol=_def_samples_per_symbol, + excess_bw=_def_excess_bw, + freq_alpha=_def_freq_alpha, + phase_damping=_def_phase_damping, + phase_natfreq=_def_phase_natfreq, + timing_alpha=_def_timing_alpha, + timing_max_dev=_def_timing_max_dev, + gray_code=_def_gray_code, + verbose=_def_verbose, + log=_def_log, + sync_out=False): + """ + Hierarchical block for RRC-filtered differential BPSK demodulation + + The input is the complex modulated signal at baseband. + The output is a stream of bits packed 1 bit per byte (LSB) + + @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 phase_damping: loop filter damping factor for phase/fine frequency recovery + @type phase_damping: float + @param phase_natfreq: loop filter natural frequency for phase/fine frequency recovery + @type phase_natfreq: float + @param timing_alpha: loop alpha gain for timing recovery + @type timing_alpha: float + @param timing_max: timing loop maximum rate deviations + @type timing_max: 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 log: Print modualtion data to files? + @type log: bool + @param sync_out: Output a sync signal on :1? + @type sync_out: bool + """ + if sync_out: io_sig_out = gr.io_signaturev(2, 2, (gr.sizeof_char, gr.sizeof_gr_complex)) + else: io_sig_out = gr.io_signature(1, 1, gr.sizeof_char) + + gr.hier_block2.__init__(self, "dbpsk_demod", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + io_sig_out) # Output signature + + self._samples_per_symbol = samples_per_symbol + self._excess_bw = excess_bw + self._freq_alpha = freq_alpha + self._freq_beta = 0.10*self._freq_alpha + self._phase_damping = phase_damping + self._phase_natfreq = phase_natfreq + self._timing_alpha = timing_alpha + self._timing_beta = _def_timing_beta + self._timing_max_dev=timing_max_dev + self._gray_code = gray_code + + if samples_per_symbol < 2: + raise TypeError, "samples_per_symbol must be >= 2, is %r" % (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) + #self.agc = gr.feedforward_agc_cc(16, 1.0) + + # 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) + + # Perform phase / fine frequency correction + self.phase_recov = digital_swig.costas_loop_cc(self._phase_damping, + self._phase_natfreq, + arity) + + # Do differential decoding based on phase change of symbols + self.diffdec = gr.diff_phasor_cc() + + # find closest constellation point + rot = 1 + rotated_const = map(lambda pt: pt * rot, psk.constellation[arity]) + self.slicer = gr.constellation_decoder_cb(rotated_const, range(arity)) + + if self._gray_code: + self.symbol_mapper = gr.map_bb(psk.gray_to_binary[arity]) + else: + self.symbol_mapper = gr.map_bb(psk.ungray_to_binary[arity]) + + # 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 + self.connect(self, self.agc, + self.freq_recov, self.time_recov, self.phase_recov, + self.diffdec, self.slicer, self.symbol_mapper, self.unpack, self) + if sync_out: self.connect(self.phase_recov, (self, 1)) + + 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 1 + bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM + + def _print_verbage(self): + print "\nDemodulator:" + print "bits per symbol: %d" % self.bits_per_symbol() + print "Gray code: %s" % self._gray_code + 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, + gr.file_sink(gr.sizeof_gr_complex, "rx_freq_recov.dat")) + self.connect(self.time_recov, + gr.file_sink(gr.sizeof_gr_complex, "rx_time_recov.dat")) + self.connect(self.phase_recov, + gr.file_sink(gr.sizeof_gr_complex, "rx_phase_recov.dat")) + self.connect(self.diffdec, + gr.file_sink(gr.sizeof_gr_complex, "rx_diffdec.dat")) + self.connect(self.slicer, + gr.file_sink(gr.sizeof_char, "rx_slicer.dat")) + 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 DBPSK demodulation-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)") + parser.add_option("", "--freq-alpha", type="float", default=_def_freq_alpha, + help="set frequency lock loop alpha gain value [default=%default] (PSK)") + parser.add_option("", "--phase-alpha", type="float", default=_def_phase_alpha, + help="set phase tracking loop alpha value [default=%default] (PSK)") + parser.add_option("", "--timing-alpha", type="float", default=_def_timing_alpha, + help="set timing symbol sync loop gain alpha value [default=%default] (GMSK/PSK)") + parser.add_option("", "--timing-beta", type="float", default=_def_timing_beta, + help="set timing symbol sync loop gain beta value [default=%default] (GMSK/PSK)") + parser.add_option("", "--timing-max-dev", type="float", default=_def_timing_max_dev, + help="set timing symbol sync loop maximum deviation [default=%default] (GMSK/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_utils2.extract_kwargs_from_options( + dbpsk_demod.__init__, ('self',), options) + extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) + +# +# Add these to the mod/demod registry +# +modulation_utils2.add_type_1_mod('dbpsk3', dbpsk_mod) +modulation_utils2.add_type_1_demod('dbpsk3', dbpsk_demod) diff --git a/gr-digital/python/dqpsk.py b/gr-digital/python/dqpsk.py new file mode 100644 index 000000000..29afd5530 --- /dev/null +++ b/gr-digital/python/dqpsk.py @@ -0,0 +1,374 @@ +# +# Copyright 2009,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. +# + +# See gnuradio-examples/python/digital for examples + +""" +differential QPSK modulation and demodulation. +""" + +from gnuradio import gr, modulation_utils2 +from math import pi, sqrt +import digital_swig, psk +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_freq_alpha = 0.010 +_def_phase_damping = 0.4 +_def_phase_natfreq = 0.25 +_def_timing_alpha = 0.100 +_def_timing_beta = 0.010 +_def_timing_max_dev = 1.5 + + +# ///////////////////////////////////////////////////////////////////////////// +# DQPSK modulator +# ///////////////////////////////////////////////////////////////////////////// + +class dqpsk_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, "dqpsk_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 samples_per_symbol < 2: + raise TypeError, ("sbp must be >= 2, is %f" % 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(psk.binary_to_gray[arity]) + else: + self.symbol_mapper = gr.map_bb(psk.binary_to_ungray[arity]) + + self.diffenc = gr.diff_encoder_bb(arity) + + rot = .707 + .707j + rotated_const = map(lambda pt: pt * rot, psk.constellation[arity]) + self.chunks2symbols = gr.chunks_to_symbols_bc(rotated_const) + + # pulse shaping filter + nfilts = 32 + ntaps = 11 * int(nfilts * self._samples_per_symbol) # make nfilts filters of ntaps each + self.rrc_taps = gr.firdes.root_raised_cosine( + nfilts, # gain + nfilts, # sampling rate based on 32 filters in resampler + 1.0, # symbol rate + self._excess_bw, # excess bandwidth (roll-off factor) + ntaps) + self.rrc_filter = gr.pfb_arb_resampler_ccf(self._samples_per_symbol, self.rrc_taps) + + if verbose: + self._print_verbage() + + if log: + self._setup_logging() + + # Connect & Initialize base class + 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 2 + bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM + + def _print_verbage(self): + print "\nModulator:" + print "bits per symbol: %d" % self.bits_per_symbol() + print "Gray code: %s" % self._gray_code + print "RRC 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, "tx_bytes2chunks.dat")) + self.connect(self.symbol_mapper, + gr.file_sink(gr.sizeof_char, "tx_graycoder.dat")) + 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")) + + def add_options(parser): + """ + Adds QPSK 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_utils2.extract_kwargs_from_options(dqpsk_mod.__init__, + ('self',), options) + extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) + + +# ///////////////////////////////////////////////////////////////////////////// +# DQPSK demodulator +# +# Differentially coherent detection of differentially encoded qpsk +# ///////////////////////////////////////////////////////////////////////////// + +class dqpsk_demod(gr.hier_block2): + + def __init__(self, + samples_per_symbol=_def_samples_per_symbol, + excess_bw=_def_excess_bw, + freq_alpha=_def_freq_alpha, + phase_damping=_def_phase_damping, + phase_natfreq=_def_phase_natfreq, + timing_alpha=_def_timing_alpha, + timing_max_dev=_def_timing_max_dev, + gray_code=_def_gray_code, + verbose=_def_verbose, + log=_def_log, + sync_out=False): + """ + Hierarchical block for RRC-filtered DQPSK demodulation + + The input is the complex modulated signal at baseband. + The output is a stream of bits packed 1 bit per byte (LSB) + + @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 phase_damping: loop filter damping factor for phase/fine frequency recovery + @type phase_damping: float + @param phase_natfreq: loop filter natural frequency for phase/fine frequency recovery + @type phase_natfreq: float + @param timing_alpha: timing loop alpha gain + @type timing_alpha: float + @param timing_max: timing loop maximum rate deviations + @type timing_max: 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 log: Print modualtion data to files? + @type log: bool + @param sync_out: Output a sync signal on :1? + @type sync_out: bool + """ + if sync_out: io_sig_out = gr.io_signaturev(2, 2, (gr.sizeof_char, gr.sizeof_gr_complex)) + else: io_sig_out = gr.io_signature(1, 1, gr.sizeof_char) + + gr.hier_block2.__init__(self, "dqpsk_demod", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + io_sig_out) # Output signature + + self._samples_per_symbol = samples_per_symbol + self._excess_bw = excess_bw + self._freq_alpha = freq_alpha + self._freq_beta = 0.25*self._freq_alpha**2 + self._phase_damping = phase_damping + self._phase_natfreq = phase_natfreq + self._timing_alpha = timing_alpha + self._timing_beta = _def_timing_beta + self._timing_max_dev=timing_max_dev + self._gray_code = gray_code + + if samples_per_symbol < 2: + raise TypeError, "sbp must be >= 2, is %d" % 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) + #self.agc = gr.feedforward_agc_cc(16, 2.0) + + # 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(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) + + + # Perform phase / fine frequency correction + self.phase_recov = digital_swig.costas_loop_cc(self._phase_damping, + self._phase_natfreq, + arity) + + # Perform Differential decoding on the constellation + self.diffdec = gr.diff_phasor_cc() + + # find closest constellation point + rot = 1 + rotated_const = map(lambda pt: pt * rot, psk.constellation[arity]) + self.slicer = gr.constellation_decoder_cb(rotated_const, range(arity)) + + if self._gray_code: + self.symbol_mapper = gr.map_bb(psk.gray_to_binary[arity]) + else: + self.symbol_mapper = gr.map_bb(psk.ungray_to_binary[arity]) + + # 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 + self.connect(self, self.agc, + self.freq_recov, self.time_recov, self.phase_recov, + self.diffdec, self.slicer, self.symbol_mapper, self.unpack, self) + if sync_out: self.connect(self.phase_recov, (self, 1)) + + 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 2 + bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM + + def _print_verbage(self): + print "\nDemodulator:" + print "bits per symbol: %d" % self.bits_per_symbol() + print "Gray code: %s" % self._gray_code + print "RRC roll-off factor: %.2f" % self._excess_bw + print "FLL gain: %.2f" % self._freq_alpha + print "Timing alpha gain: %.2f" % self._timing_alpha + print "Timing beta gain: %.2f" % 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, + gr.file_sink(gr.sizeof_gr_complex, "rx_freq_recov.dat")) + self.connect(self.time_recov, + gr.file_sink(gr.sizeof_gr_complex, "rx_time_recov.dat")) + self.connect(self.phase_recov, + gr.file_sink(gr.sizeof_gr_complex, "rx_phase_recov.dat")) + self.connect(self.diffdec, + gr.file_sink(gr.sizeof_gr_complex, "rx_diffdec.dat")) + self.connect(self.slicer, + gr.file_sink(gr.sizeof_char, "rx_slicer.dat")) + self.connect(self.symbol_mapper, + gr.file_sink(gr.sizeof_char, "rx_gray_decoder.dat")) + self.connect(self.unpack, + gr.file_sink(gr.sizeof_char, "rx_unpack.dat")) + + def add_options(parser): + """ + Adds DQPSK demodulation-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)") + parser.add_option("", "--freq-alpha", type="float", default=_def_freq_alpha, + help="set frequency lock loop alpha gain value [default=%default] (PSK)") + parser.add_option("", "--phase-alpha", type="float", default=_def_phase_alpha, + help="set phase tracking loop alpha value [default=%default] (PSK)") + parser.add_option("", "--timing-alpha", type="float", default=_def_timing_alpha, + help="set timing symbol sync loop gain alpha value [default=%default] (GMSK/PSK)") + parser.add_option("", "--timing-beta", type="float", default=_def_timing_beta, + help="set timing symbol sync loop gain beta value [default=%default] (GMSK/PSK)") + parser.add_option("", "--timing-max-dev", type="float", default=_def_timing_max_dev, + help="set timing symbol sync loop maximum deviation [default=%default] (GMSK/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_utils2.extract_kwargs_from_options( + dqpsk_demod.__init__, ('self',), options) + extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) + + +# +# Add these to the mod/demod registry +# +modulation_utils2.add_type_1_mod('dqpsk', dqpsk_mod) +modulation_utils2.add_type_1_demod('dqpsk', dqpsk_demod) diff --git a/gr-digital/python/psk.py b/gr-digital/python/psk.py new file mode 100644 index 000000000..acedf3b69 --- /dev/null +++ b/gr-digital/python/psk.py @@ -0,0 +1,94 @@ +# +# 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. +# + +from math import pi, sqrt, log10 +import math, cmath + +# The following algorithm generates Gray coded constellations for M-PSK for M=[2,4,8] +def make_gray_constellation(m): + # number of bits/symbol (log2(M)) + k = int(log10(m) / log10(2.0)) + + coeff = 1 + const_map = [] + bits = [0]*3 + for i in range(m): + # get a vector of the k bits to use in this mapping + bits[3-k:3] = [((i&(0x01 << k-j-1)) >> k-j-1) for j in range(k)] + + theta = -(2*bits[0]-1)*(2*pi/m)*(bits[0]+abs(bits[1]-bits[2])+2*bits[1]) + re = math.cos(theta) + im = math.sin(theta) + const_map.append(complex(re, im)) # plug it into the constellation + + # return the constellation; by default, it is normalized + return const_map + +# This makes a constellation that increments around the unit circle +def make_constellation(m): + return [cmath.exp(i * 2 * pi / m * 1j) for i in range(m)] + +# Common definition of constellations for Tx and Rx +constellation = { + 2 : make_constellation(2), # BPSK + 4 : make_constellation(4), # QPSK + 8 : make_constellation(8) # 8PSK + } + +gray_constellation = { + 2 : make_gray_constellation(2), # BPSK + 4 : make_gray_constellation(4), # QPSK + 8 : make_gray_constellation(8) # 8PSK + } + +# ----------------------- +# Do Gray code +# ----------------------- +# binary to gray coding -- constellation does Gray coding +binary_to_gray = { + 2 : range(2), + 4 : [0,1,3,2], + 8 : [0, 1, 3, 2, 7, 6, 4, 5] + } + +# gray to binary +gray_to_binary = { + 2 : range(2), + 4 : [0,1,3,2], + 8 : [0, 1, 3, 2, 6, 7, 5, 4] + } + +# ----------------------- +# Don't Gray code +# ----------------------- +# identity mapping +binary_to_ungray = { + 2 : range(2), + 4 : range(4), + 8 : range(8) + } + +# identity mapping +ungray_to_binary = { + 2 : range(2), + 4 : range(4), + 8 : range(8) + } diff --git a/gr-digital/python/qa_costas_loop_cc.py b/gr-digital/python/qa_costas_loop_cc.py new file mode 100644 index 000000000..368704093 --- /dev/null +++ b/gr-digital/python/qa_costas_loop_cc.py @@ -0,0 +1,156 @@ +#!/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. +# + +from gnuradio import gr, blks2, gr_unittest +import digital_swig +import random, cmath + +class test_digital(gr_unittest.TestCase): + + def setUp (self): + self.tb = gr.top_block () + + def tearDown (self): + self.tb = None + + def test01 (self): + # test basic functionality by setting all gains to 0 + damp = 0.4 + natfreq = 0.25 + order = 2 + self.test = digital_swig.costas_loop_cc(damp, natfreq, order) + + data = 100*[complex(1,0),] + self.src = gr.vector_source_c(data, False) + self.snk = gr.vector_sink_c() + + self.tb.connect(self.src, self.test, self.snk) + self.tb.run() + + expected_result = data + dst_data = self.snk.data() + self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 5) + + def test02 (self): + # Make sure it doesn't diverge given perfect data + damp = 0.4 + natfreq = 0.25 + order = 2 + self.test = digital_swig.costas_loop_cc(damp, natfreq, order) + + data = [complex(2*random.randint(0,1)-1, 0) for i in xrange(100)] + self.src = gr.vector_source_c(data, False) + self.snk = gr.vector_sink_c() + + self.tb.connect(self.src, self.test, self.snk) + self.tb.run() + + expected_result = data + dst_data = self.snk.data() + + self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 5) + + def test03 (self): + # BPSK Convergence test with static rotation + damp = 0.4 + natfreq = 0.25 + order = 2 + self.test = digital_swig.costas_loop_cc(damp, natfreq, order) + + rot = cmath.exp(0.2j) # some small rotation + data = [complex(2*random.randint(0,1)-1, 0) for i in xrange(100)] + + N = 40 # settling time + expected_result = data[N:] + data = [rot*d for d in data] + + self.src = gr.vector_source_c(data, False) + self.snk = gr.vector_sink_c() + + self.tb.connect(self.src, self.test, self.snk) + self.tb.run() + + dst_data = self.snk.data()[N:] + + # generously compare results; the loop will converge near to, but + # not exactly on, the target data + self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 2) + + def test04 (self): + # QPSK Convergence test with static rotation + damp = 0.4 + natfreq = 0.25 + order = 4 + self.test = digital_swig.costas_loop_cc(damp, natfreq, order) + + rot = cmath.exp(0.2j) # some small rotation + data = [complex(2*random.randint(0,1)-1, 2*random.randint(0,1)-1) + for i in xrange(100)] + + N = 40 # settling time + expected_result = data[N:] + data = [rot*d for d in data] + + self.src = gr.vector_source_c(data, False) + self.snk = gr.vector_sink_c() + + self.tb.connect(self.src, self.test, self.snk) + self.tb.run() + + dst_data = self.snk.data()[N:] + + # generously compare results; the loop will converge near to, but + # not exactly on, the target data + self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 2) + + def test05 (self): + # 8PSK Convergence test with static rotation + damp = 0.5 + natfreq = 0.5 + order = 8 + self.test = digital_swig.costas_loop_cc(damp, natfreq, order) + + rot = cmath.exp(-cmath.pi/8.0j) # rotate to match Costas rotation + const = blks2.psk.make_constellation(order) + data = [random.randint(0,7) for i in xrange(100)] + data = [2*rot*const[d] for d in data] + + N = 40 # settling time + expected_result = data[N:] + + rot = cmath.exp(0.1j) # some small rotation + data = [rot*d for d in data] + + self.src = gr.vector_source_c(data, False) + self.snk = gr.vector_sink_c() + + self.tb.connect(self.src, self.test, self.snk) + self.tb.run() + + dst_data = self.snk.data()[N:] + + # generously compare results; the loop will converge near to, but + # not exactly on, the target data + self.assertComplexTuplesAlmostEqual (expected_result, dst_data, 2) + +if __name__ == '__main__': + gr_unittest.run(test_digital, "test_digital.xml") diff --git a/gr-digital/python/qa_digital.py b/gr-digital/python/qa_digital.py new file mode 100755 index 000000000..97e35da56 --- /dev/null +++ b/gr-digital/python/qa_digital.py @@ -0,0 +1,35 @@ +#!/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. +# + +from gnuradio import gr, gr_unittest +import digital_swig + +class test_digital(gr_unittest.TestCase): + + def setUp (self): + self.tb = gr.top_block () + + def tearDown (self): + self.tb = None + +if __name__ == '__main__': + gr_unittest.run(test_digital, "test_digital.xml") diff --git a/gr-digital/python/run_tests.in b/gr-digital/python/run_tests.in new file mode 100644 index 000000000..b39e7e847 --- /dev/null +++ b/gr-digital/python/run_tests.in @@ -0,0 +1,10 @@ +#!/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 + +@top_builddir@/run_tests.sh \ + @abs_top_srcdir@/gr-digital \ + @abs_top_builddir@/gr-digital \ + @srcdir@ diff --git a/gr-digital/swig/.gitignore b/gr-digital/swig/.gitignore new file mode 100644 index 000000000..7e864f43f --- /dev/null +++ b/gr-digital/swig/.gitignore @@ -0,0 +1,9 @@ +/Makefile +/Makefile.in +/pager_swig.py +/pager_swig.cc +/*.pyc +/run_tests +/run_guile_tests +/guile +/python diff --git a/gr-digital/swig/Makefile.am b/gr-digital/swig/Makefile.am new file mode 100644 index 000000000..edbda6bda --- /dev/null +++ b/gr-digital/swig/Makefile.am @@ -0,0 +1,67 @@ +# +# 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. +# + +include $(top_srcdir)/Makefile.common +include $(top_srcdir)/Makefile.swig + +TESTS = +EXTRA_DIST += $(nobase_guile_DATA) + +AM_CPPFLAGS = \ + -I$(top_srcdir)/gr-digital/lib \ + $(STD_DEFINES_AND_INCLUDES) \ + $(PYTHON_CPPFLAGS) \ + $(WITH_INCLUDES) + +if GUILE +nobase_guile_DATA = \ + gnuradio/digital.scm +endif + +noinst_GUILE = digital.test + + +############################## +# SWIG interface and library +TOP_SWIG_IFILES = \ + digital_swig.i + +# Install so that they end up available as: +# import gnuradio.digital +# This ends up at: +# ${prefix}/lib/python${python_version}/site-packages/gnuradio/digital +digital_swig_pythondir_category = \ + gnuradio/digital + +# additional libraries for linking with the SWIG-generated library +digital_swig_la_swig_libadd = \ + $(abs_top_builddir)/gr-digital/lib/libgnuradio-digital.la + +# additional SWIG files to be installed +digital_swig_swiginclude_headers = \ + digital_constellation.i \ + digital_costas_loop_cc.i \ + digital_cma_equalizer_cc.i \ + digital_kurtotic_equalizer_cc.i + +if GUILE +TESTS += run_guile_tests +endif
\ No newline at end of file diff --git a/gr-digital/swig/Makefile.swig.gen b/gr-digital/swig/Makefile.swig.gen new file mode 100644 index 000000000..bd9aabcea --- /dev/null +++ b/gr-digital/swig/Makefile.swig.gen @@ -0,0 +1,145 @@ +# -*- Makefile -*- +# +# Copyright 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. +# + +# Makefile.swig.gen for digital_swig.i + +## Default install locations for these files: +## +## Default location for the Python directory is: +## ${prefix}/lib/python${python_version}/site-packages/[category]/digital_swig +## Default location for the Python exec directory is: +## ${exec_prefix}/lib/python${python_version}/site-packages/[category]/digital_swig +## +## The following can be overloaded to change the install location, but +## this has to be done in the including Makefile.am -before- +## Makefile.swig is included. + +digital_swig_pythondir_category ?= gnuradio/digital_swig +digital_swig_pylibdir_category ?= $(digital_swig_pythondir_category) +digital_swig_pythondir = $(pythondir)/$(digital_swig_pythondir_category) +digital_swig_pylibdir = $(pyexecdir)/$(digital_swig_pylibdir_category) + +# The .so libraries for the guile modules get installed whereever guile +# is installed, usually /usr/lib/guile/gnuradio/ +# FIXME: determince whether these should be installed with gnuradio. +digital_swig_scmlibdir = $(libdir) + +# The scm files for the guile modules get installed where ever guile +# is installed, usually /usr/share/guile/site/digital_swig +# FIXME: determince whether these should be installed with gnuradio. +digital_swig_scmdir = $(guiledir) + +## SWIG headers are always installed into the same directory. + +digital_swig_swigincludedir = $(swigincludedir) + +## This is a template file for a "generated" Makefile addition (in +## this case, "Makefile.swig.gen"). By including the top-level +## Makefile.swig, this file will be used to generate the SWIG +## dependencies. Assign the variable TOP_SWIG_FILES to be the list of +## SWIG .i files to generated wrappings for; there can be more than 1 +## so long as the names are unique (no sorting is done on the +## TOP_SWIG_FILES list). This file explicitly assumes that a SWIG .i +## file will generate .cc, .py, and possibly .h files -- meaning that +## all of these files will have the same base name (that provided for +## the SWIG .i file). +## +## This code is setup to ensure parallel MAKE ("-j" or "-jN") does the +## right thing. For more info, see < +## http://sources.redhat.com/automake/automake.html#Multiple-Outputs > + +## Other cleaned files: dependency files generated by SWIG or this Makefile + +MOSTLYCLEANFILES += $(DEPDIR)/*.S* + +## Various SWIG variables. These can be overloaded in the including +## Makefile.am by setting the variable value there, then including +## Makefile.swig . + +digital_swig_swiginclude_HEADERS = \ + digital_swig.i \ + $(digital_swig_swiginclude_headers) + +if PYTHON +digital_swig_pylib_LTLIBRARIES = \ + _digital_swig.la + +_digital_swig_la_SOURCES = \ + python/digital_swig.cc \ + $(digital_swig_la_swig_sources) + +digital_swig_python_PYTHON = \ + digital_swig.py \ + $(digital_swig_python) + +_digital_swig_la_LIBADD = \ + $(STD_SWIG_LA_LIB_ADD) \ + $(digital_swig_la_swig_libadd) + +_digital_swig_la_LDFLAGS = \ + $(STD_SWIG_LA_LD_FLAGS) \ + $(digital_swig_la_swig_ldflags) + +_digital_swig_la_CXXFLAGS = \ + $(STD_SWIG_CXX_FLAGS) \ + -I$(top_builddir) \ + $(digital_swig_la_swig_cxxflags) + +python/digital_swig.cc: digital_swig.py +digital_swig.py: digital_swig.i + +# Include the python dependencies for this file +-include python/digital_swig.d + +endif # end of if python + +if GUILE + +digital_swig_scmlib_LTLIBRARIES = \ + libguile-gnuradio-digital_swig.la +libguile_gnuradio_digital_swig_la_SOURCES = \ + guile/digital_swig.cc \ + $(digital_swig_la_swig_sources) +nobase_digital_swig_scm_DATA = \ + gnuradio/digital_swig.scm \ + gnuradio/digital_swig-primitive.scm +libguile_gnuradio_digital_swig_la_LIBADD = \ + $(STD_SWIG_LA_LIB_ADD) \ + $(digital_swig_la_swig_libadd) +libguile_gnuradio_digital_swig_la_LDFLAGS = \ + $(STD_SWIG_LA_LD_FLAGS) \ + $(digital_swig_la_swig_ldflags) +libguile_gnuradio_digital_swig_la_CXXFLAGS = \ + $(STD_SWIG_CXX_FLAGS) \ + -I$(top_builddir) \ + $(digital_swig_la_swig_cxxflags) + +guile/digital_swig.cc: gnuradio/digital_swig.scm +gnuradio/digital_swig.scm: digital_swig.i +gnuradio/digital_swig-primitive.scm: gnuradio/digital_swig.scm + +# Include the guile dependencies for this file +-include guile/digital_swig.d + +endif # end of GUILE + + diff --git a/gr-digital/swig/digital_cma_equalizer_cc.i b/gr-digital/swig/digital_cma_equalizer_cc.i new file mode 100644 index 000000000..346b744db --- /dev/null +++ b/gr-digital/swig/digital_cma_equalizer_cc.i @@ -0,0 +1,42 @@ +/* -*- c++ -*- */ +/* + * 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. + */ + +GR_SWIG_BLOCK_MAGIC(digital,cma_equalizer_cc) + +// retrieve info on the base class, without generating wrappers since +// the base class has a pure virual method. +%import "gr_adaptive_fir_ccc.i" + +digital_cma_equalizer_cc_sptr +digital_make_cma_equalizer_cc(int num_taps, + float modulus, + float mu); + +class digital_cma_equalizer_cc : public gr_adaptive_fir_ccc +{ +private: + digital_cma_equalizer_cc(int num_taps, float modulus, float mu); + +public: + void set_gain(float mu); + void set_modulus(float mod); +}; diff --git a/gr-digital/swig/digital_constellation.i b/gr-digital/swig/digital_constellation.i new file mode 100644 index 000000000..0eb0c59e2 --- /dev/null +++ b/gr-digital/swig/digital_constellation.i @@ -0,0 +1,146 @@ +/* -*- 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. + */ + +%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 digital_constellation; +typedef boost::shared_ptr<digital_constellation> digital_constellation_sptr; +%template(digital_constellation_sptr) boost::shared_ptr<digital_constellation>; + +class digital_constellation +{ +public: + digital_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 (); + digital_constellation_sptr base (); + bool apply_pre_diff_code(); + std::vector<unsigned int> pre_diff_code(); + unsigned int rotational_symmetry(); + unsigned int dimensionality(); +}; + +class digital_constellation_calcdist; +typedef boost::shared_ptr<digital_constellation_calcdist> digital_constellation_calcdist_sptr; +%template(digital_constellation_calcdist_sptr) boost::shared_ptr<digital_constellation_calcdist>; +%rename(constellation_calcdist) digital_make_constellation_calcdist; +digital_constellation_calcdist_sptr +digital_make_constellation_calcdist(std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int rotational_symmetry, + unsigned int dimensionality); +%ignore digital_constellation_calcdist; + +class digital_constellation_calcdist: public digital_constellation +{ + public: + digital_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 digital_constellation_sector: public digital_constellation +{ +}; + +class digital_constellation_rect; +typedef boost::shared_ptr<digital_constellation_rect> digital_constellation_rect_sptr; +%template(digital_constellation_rect_sptr) boost::shared_ptr<digital_constellation_rect>; +%rename(constellation_rect) digital_make_constellation_rect; +digital_constellation_rect_sptr digital_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 digital_constellation_rect; + +class digital_constellation_rect : public digital_constellation_sector +{ +public: + digital_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 digital_constellation_psk; +typedef boost::shared_ptr<digital_constellation_psk> digital_constellation_psk_sptr; +%template(digital_constellation_psk_sptr) boost::shared_ptr<digital_constellation_psk>; +%rename(constellation_psk) digital_make_constellation_psk; +digital_constellation_psk_sptr digital_make_constellation_psk(std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int n_sectors); +%ignore digital_constellation_psk; + +class digital_constellation_psk : public digital_constellation_sector +{ +public: + digital_constellation_psk (std::vector<gr_complex> constellation, + std::vector<unsigned int> pre_diff_code, + unsigned int n_sectors); +}; + +class digital_constellation_bpsk; +typedef boost::shared_ptr<digital_constellation_bpsk> digital_constellation_bpsk_sptr; +%template(digital_constellation_bpsk_sptr) boost::shared_ptr<digital_constellation_bpsk>; +%rename(constellation_bpsk) digital_make_constellation_bpsk; +digital_constellation_bpsk_sptr digital_make_constellation_bpsk(); +%ignore digital_constellation_bpsk; + +class digital_constellation_bpsk : public digital_constellation +{ +public: + digital_constellation_bpsk (); +}; + +class digital_constellation_qpsk; +typedef boost::shared_ptr<digital_constellation_qpsk> digital_constellation_qpsk_sptr; +%template(digital_constellation_qpsk_sptr) boost::shared_ptr<digital_constellation_qpsk>; +%rename(constellation_qpsk) digital_make_constellation_qpsk; +digital_constellation_qpsk_sptr digital_make_constellation_qpsk(); +%ignore digital_constellation_qpsk; + +class digital_constellation_qpsk : public digital_constellation +{ +public: + digital_constellation_qpsk (); +}; + diff --git a/gr-digital/swig/digital_costas_loop_cc.i b/gr-digital/swig/digital_costas_loop_cc.i new file mode 100644 index 000000000..6d3d009f8 --- /dev/null +++ b/gr-digital/swig/digital_costas_loop_cc.i @@ -0,0 +1,44 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,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(digital,costas_loop_cc); + +digital_costas_loop_cc_sptr +digital_make_costas_loop_cc (float damping, float nat_freq, + int order + ) throw (std::invalid_argument); + + +class digital_costas_loop_cc : public gr_sync_block +{ + private: + digital_costas_loop_cc (float damping, float nat_freq, + int order); + + public: + float alpha(); + float beta(); + float freq(); + + void set_natural_freq(float w); + void set_damping_factor(float eta); +}; diff --git a/gr-digital/swig/digital_kurtotic_equalizer_cc.i b/gr-digital/swig/digital_kurtotic_equalizer_cc.i new file mode 100644 index 000000000..67a9dc6fd --- /dev/null +++ b/gr-digital/swig/digital_kurtotic_equalizer_cc.i @@ -0,0 +1,40 @@ +/* -*- c++ -*- */ +/* + * 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. + */ + +GR_SWIG_BLOCK_MAGIC(digital,kurtotic_equalizer_cc) + +// retrieve info on the base class, without generating wrappers since +// the base class has a pure virual method. +%import "gr_adaptive_fir_ccc.i" + +digital_kurtotic_equalizer_cc_sptr +digital_make_kurtotic_equalizer_cc(int num_taps, + float mu); + +class digital_kurtotic_equalizer_cc : public gr_adaptive_fir_ccc +{ +private: + digital_kurtotic_equalizer_cc(int num_taps, float mu); + +public: + void set_gain(float mu); +}; diff --git a/gr-digital/swig/digital_swig.i b/gr-digital/swig/digital_swig.i new file mode 100644 index 000000000..73d8edd8d --- /dev/null +++ b/gr-digital/swig/digital_swig.i @@ -0,0 +1,44 @@ +/* + * 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. + */ + +%include "gnuradio.i" + +%{ +#include "digital_constellation.h" +#include "digital_costas_loop_cc.h" +#include "digital_cma_equalizer_cc.h" +#include "digital_kurtotic_equalizer_cc.h" +%} + +%include "digital_constellation.i" +%include "digital_costas_loop_cc.i" +%include "digital_cma_equalizer_cc.i" +%include "digital_kurtotic_equalizer_cc.i" + +#if SWIGGUILE +%scheme %{ +(load-extension-global "libguile-gnuradio-digital_swig" "scm_init_gnuradio_digital_swig_module") +%} + +%goops %{ +(use-modules (gnuradio gnuradio_core_runtime)) +%} +#endif diff --git a/gr-digital/swig/gnuradio/.gitignore b/gr-digital/swig/gnuradio/.gitignore new file mode 100644 index 000000000..c264c571a --- /dev/null +++ b/gr-digital/swig/gnuradio/.gitignore @@ -0,0 +1,2 @@ +digital_swig-primitive.scm +digital_swig.scm diff --git a/gr-digital/swig/gnuradio/digital.scm b/gr-digital/swig/gnuradio/digital.scm new file mode 100644 index 000000000..834bc8d6d --- /dev/null +++ b/gr-digital/swig/gnuradio/digital.scm @@ -0,0 +1,28 @@ +;;; +;;; 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 this program. If not, see <http://www.gnu.org/licenses/>. +;;; + +;;; Module that just re-exports the digital_swig module + +(define-module (gnuradio digital) + #:use-module (gnuradio export-safely) + #:use-module (gnuradio digital_swig) + #:duplicates (merge-generics replace check)) + +(re-export-all '(gnuradio digital_swig)) + diff --git a/gr-digital/swig/run_guile_tests.in b/gr-digital/swig/run_guile_tests.in new file mode 100644 index 000000000..5d08b0dd5 --- /dev/null +++ b/gr-digital/swig/run_guile_tests.in @@ -0,0 +1,14 @@ +#!/bin/sh + +. @top_builddir@/setup_guile_test_env + +# 1st argument is absolute path to hand coded guile source directory +# 2nd argument is absolute path to component C++ shared library build directory +# 3nd argument is absolute path to component SWIG build directory + +add_local_paths \ + @srcdir@ \ + @abs_builddir@ \ + @abs_builddir@ + +@GUILE@ -e main -c '(use-modules (gnuradio test-suite guile-test))' -t @srcdir@ |