From cdbe8dcd88f5ee4b63d28df54544acf309e2278e Mon Sep 17 00:00:00 2001 From: cmayer Date: Thu, 25 Oct 2018 18:01:24 +0200 Subject: [PATCH] python->C++ information transfer --- CMakeLists.txt | 6 +- examples/1st.grc | 2 +- examples/s4285.py | 71 --------------------- include/digitalhf/adaptive_dfe.h | 2 +- lib/adaptive_dfe_impl.cc | 95 ++++++++++++++++++---------- lib/adaptive_dfe_impl.h | 24 ++++--- python/CMakeLists.txt | 2 + python/physical_layer/CMakeLists.txt | 38 +++++++++++ python/physical_layer/STANAG_4285.py | 80 +++++++++++++++++++++++ python/physical_layer/__init__.py | 26 ++++++++ 10 files changed, 225 insertions(+), 121 deletions(-) delete mode 100644 examples/s4285.py create mode 100644 python/physical_layer/CMakeLists.txt create mode 100644 python/physical_layer/STANAG_4285.py create mode 100644 python/physical_layer/__init__.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 5540e8e..38f2fd2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,15 +72,13 @@ if(CMAKE_COMPILER_IS_GNUCXX AND NOT WIN32) add_definitions(-fvisibility=hidden) endif() -#include(GrPython) find_package(PythonInterp) if(NOT PYTHONINTERP_FOUND) message(FATAL_ERROR "Python interpreter required by the build system.") endif(NOT PYTHONINTERP_FOUND) # locate python include(FindPythonLibs) -MESSAGE("YYY ${PYTHON_LIBRARIES}") -#) + ######################################################################## # Find boost @@ -150,7 +148,7 @@ find_package(Doxygen) # components required to the list of GR_REQUIRED_COMPONENTS (in all # caps such as FILTER or FFT) and change the version to the minimum # API compatible version required. -set(GR_REQUIRED_COMPONENTS RUNTIME) +set(GR_REQUIRED_COMPONENTS RUNTIME DIGITAL) find_package(Gnuradio "3.7.2" REQUIRED) list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_SOURCE_DIR}/cmake/Modules) include(GrVersion) diff --git a/examples/1st.grc b/examples/1st.grc index 806dd42..50a7750 100644 --- a/examples/1st.grc +++ b/examples/1st.grc @@ -413,7 +413,7 @@ py_obj_name - /Users/chm/Software/gr-digitalhf/examples/s4285.py + STANAG_4285 diff --git a/examples/s4285.py b/examples/s4285.py deleted file mode 100644 index de9585e..0000000 --- a/examples/s4285.py +++ /dev/null @@ -1,71 +0,0 @@ -## -*- python -*- - -import numpy as np -from gnuradio import digital - -class PhysicalLayer(object): - """Physical layer description for STANAG 4285""" - - def __init__(self, mode=0): - """For STANAG 4258 the mode has to be set manually: mode=0 -> BPSK, mode=1 -> QPSK, mode=2 -> 8PSK""" - print('Hello from PhysicalLayer!\n') - self._preamble = [get_preamble(), digital.constellation_bpsk()] - self._constellations = [digital.constellation_bpsk(), - digital.constellation_qpsk(), - digital.constellation_8psk()] - self._data = [get_data(), self._constellations[mode]] - self._counter = 0 - - def __del__(self): - print('Bye from PhysicalLayer!\n') - - def set_mode(self): - """For STANAG 4258 the mode has to be set manually: mode=0 -> BPSK, mode=1 -> QPSK, mode=2 -> 8PSK""" - self._data[1] = self._constellations[mode]; - - def get_frame(self): - """returns the known+unknown symbols and scrambling""" - print('get_frame', self._counter, self._preamble, self._preamble[1].__deref__()) - if self._counter == 0: - return self._preamble - else: - return self._data - - def get_doppler(self, symbols): - """used for doppler shift update, for determining which frame to provide next, - and for stopping at end of data/when the signal quality is too low""" - self._counter = (self._counter+1)&2 - ## TODO: doppler calculations - doppler = 0.0 - return [True, doppler] - -def get_preamble(): - """preamble symbols + scrambler(=1)""" - state = np.array([1,1,0,1,0], dtype='b') - taps = np.array([0,0,1,0,1], dtype='b') - p = np.zeros(80, dtype='f') - for i in range(80): - p[i] = state[-1]; - state = np.concatenate(([np.sum(state&taps)&1], state[0:-1])) - a = np.zeros(80, dtype=[('symb','c'), ('scramble', 'c')]) - ## BPSK modulation - a['symb'] = np.exp(np.pi*1j*p) - a['scramble'] = 1; - return a - -def get_data(): - """data symbols + scrambler; for unknown symbols 'symb'=0""" - state = np.array([1,1,1,1,1,1,1,1,1], dtype='b') - taps = np.array([0,0,0,0,1,0,0,0,1], dtype='b') - p = np.zeros(176, dtype='f') - for i in range(176): - p[i] = np.sum(state[-3:]*[4,2,1]); - for j in range(3): - state = np.concatenate(([np.sum(state&taps)&1], state[0:-1])) - a=np.zeros(176, dtype=[('symb','c'), ('scramble', 'c')]) - ## PSK-8 modulation - a['scramble'] = np.exp(np.pi*1j*p/4) - a['symb'][ 32: 48] = 1; ## mini-probe 1 - a['symb'][ 80: 96] = 1; ## mini-probe 2 - a['symb'][128:144] = 1; ## mini-probe 3 - return a diff --git a/include/digitalhf/adaptive_dfe.h b/include/digitalhf/adaptive_dfe.h index 886c488..ebdb7be 100644 --- a/include/digitalhf/adaptive_dfe.h +++ b/include/digitalhf/adaptive_dfe.h @@ -50,7 +50,7 @@ class DIGITALHF_API adaptive_dfe : virtual public gr::block int nB, // number of forward FIR taps int nF, // number of backward FIR taps int nW, // number of feedback taps - std::string py_file_name); + std::string physical_layer_description); } ; diff --git a/lib/adaptive_dfe_impl.cc b/lib/adaptive_dfe_impl.cc index c732cfe..aaa714c 100644 --- a/lib/adaptive_dfe_impl.cc +++ b/lib/adaptive_dfe_impl.cc @@ -23,7 +23,6 @@ #endif #include -#include #include "adaptive_dfe_impl.h" namespace gr { @@ -45,10 +44,10 @@ adaptive_dfe::make(int sps, // samples per symbol int nB, // number of forward FIR taps int nF, // number of backward FIR taps int nW, // number of feedback taps - std::string python_file_name) + std::string python_module_name) { return gnuradio::get_initial_sptr - (new adaptive_dfe_impl(sps, nB, nF, nW, python_file_name)); + (new adaptive_dfe_impl(sps, nB, nF, nW, python_module_name)); } /* @@ -58,7 +57,7 @@ adaptive_dfe_impl::adaptive_dfe_impl(int sps, // samples per symbol int nB, // number of forward FIR taps int nF, // number of backward FIR taps int nW, // number of feedback taps - std::string python_file_name) + std::string python_module_name) : gr::block("adaptive_dfe", gr::io_signature::make(1, 1, sizeof(gr_complex)), gr::io_signature::make(0, 0, sizeof(gr_complex))) @@ -66,10 +65,15 @@ adaptive_dfe_impl::adaptive_dfe_impl(int sps, // samples per symbol , _nB(nB) , _nF(nF) , _nW(nW) - , _py_file_name(python_file_name) + , _py_module_name(python_module_name) , _physicalLayer() , _taps_samples(nB+nF+1) - , _taps_symbols(nW) { + , _taps_symbols(nW) + , _constellations() + , _constellation_index() + , _symbols() + , _scramble() +{ // make sure python is ready for threading if( Py_IsInitialized() ){ if(PyEval_ThreadsInitialized() != 1 ){ @@ -100,11 +104,12 @@ adaptive_dfe_impl::general_work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) { - const gr_complex *in = (const gr_complex *) input_items[0]; + gr_complex const* in = (gr_complex const *)input_items[0]; - get_next_frame(); GILLock lock; - std::cout << "bits_per_symbol: " << boost::python::extract(_constellation.attr("bits_per_symbol")()) << std::endl; + // TODO: wait for preamble correlation tag etc... + update_frame_information(_physicalLayer.attr("get_frame")()); + update_doppler_information(_physicalLayer.attr("get_doppler")()); // symbols consume_each (noutput_items); @@ -112,39 +117,16 @@ adaptive_dfe_impl::general_work(int noutput_items, return noutput_items; } -void adaptive_dfe_impl::get_next_frame() -{ - GILLock lock; - boost::python::object const& obj = _physicalLayer.attr("get_frame")(); - std::cout << "get_frame" << std::endl; - boost::python::numpy::ndarray symbols = boost::python::numpy::array(obj[0]); - _constellation = obj[1]; -} -boost::python::object import(const std::string& module, const std::string& path, boost::python::object& globals) -{ - boost::python::dict locals; - locals["module_name"] = module; - locals["path"] = path; - - boost::python::exec("import imp\n" - "new_module = imp.load_module(module_name, open(path), path, ('py', 'U', imp.PY_SOURCE))\n", - globals, - locals); - return locals["new_module"]; -} bool adaptive_dfe_impl::start() { std::cout << "adaptive_dfe_impl::start()" << std::endl; GILLock lock; try { - boost::python::object main = boost::python::import("__main__"); - boost::python::object globals = main.attr("__dict__"); - boost::python::object module = import("physicalLayer", _py_file_name, globals); + boost::python::object module = boost::python::import(boost::python::str("digitalhf.physical_layer." + _py_module_name)); boost::python::object PhysicalLayer = module.attr("PhysicalLayer"); _physicalLayer = PhysicalLayer(); - - _physicalLayer.attr("get_frame")(); - } catch (const boost::python::error_already_set& ) { + update_constellations(_physicalLayer.attr("get_constellations")()); + } catch (boost::python::error_already_set const&) { PyErr_Print(); return false; } @@ -158,5 +140,48 @@ bool adaptive_dfe_impl::stop() return true; } +void adaptive_dfe_impl::update_constellations(boost::python::object obj) +{ + int const n = boost::python::extract(obj.attr("__len__")()); + _constellations.resize(n); + for (int i=0; i constell(m); + std::vector pre_diff_code(m); + for (int j=0; j(obj.attr("__len__")()); + assert(n==2); + boost::python::numpy::ndarray array = boost::python::numpy::array(obj[0]); + char const* data = array.get_data(); + int const m = array.shape(0); + _symbols.resize(m); + _scramble.resize(m); + for (int i=0; i(obj[1]); +} +void adaptive_dfe_impl::update_doppler_information(boost::python::object obj) +{ + int const n = boost::python::extract(obj.attr("__len__")()); + assert(n==2); + double const do_continue = boost::python::extract(obj[0]); + double const doppler = boost::python::extract(obj[1]); + // TODO +} + } /* namespace digitalhf */ } /* namespace gr */ diff --git a/lib/adaptive_dfe_impl.h b/lib/adaptive_dfe_impl.h index d188932..69b7bf0 100644 --- a/lib/adaptive_dfe_impl.h +++ b/lib/adaptive_dfe_impl.h @@ -21,10 +21,10 @@ #ifndef INCLUDED_DIGITALHF_ADAPTIVE_DFE_IMPL_H #define INCLUDED_DIGITALHF_ADAPTIVE_DFE_IMPL_H -#include - #include #include +#include +#include namespace gr { namespace digitalhf { @@ -34,26 +34,32 @@ private: int _sps; int _nB, _nF, _nW; - std::string _py_file_name; - boost::python::object _physicalLayer; + // module name w.r.t. digitalhf.physical_layer containing a PhysicalLayer class + std::string _py_module_name; + boost::python::object _physicalLayer; // class instance of physical layer description std::vector _taps_samples; std::vector _taps_symbols; -// boost::python::numpy::ndarray _symbols; - boost::python::object _constellation; + std::vector _constellations; + int _constellation_index; + std::vector _symbols; + std::vector _scramble; - void get_next_frame(); + int _sample_counter; + + void update_constellations(boost::python::object obj); + void update_frame_information(boost::python::object obj); + void update_doppler_information(boost::python::object obj); public: adaptive_dfe_impl(int sps, // samples per symbol int nB, // number of forward FIR taps int nF, // number of backward FIR taps int nW, // number of symbol taps - std::string python_file_name); + std::string physical_layer_description); virtual ~adaptive_dfe_impl(); - // Where all the action really happens void forecast (int noutput_items, gr_vector_int &ninput_items_required); virtual bool start(); diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index a7a5a57..4d5bf2e 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -43,3 +43,5 @@ include(GrTest) set(GR_TEST_TARGET_DEPS gnuradio-digitalhf) set(GR_TEST_PYTHON_DIRS ${CMAKE_BINARY_DIR}/swig) GR_ADD_TEST(qa_adaptive_dfe ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_adaptive_dfe.py) + +add_subdirectory(physical_layer) diff --git a/python/physical_layer/CMakeLists.txt b/python/physical_layer/CMakeLists.txt new file mode 100644 index 0000000..ddf7615 --- /dev/null +++ b/python/physical_layer/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright 2011 Free Software Foundation, Inc. +# +# This file was generated by gr_modtool, a tool from the GNU Radio framework +# This file is a part of gr-digitalhf +# +# 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 python install macros +######################################################################## +include(GrPython) +if(NOT PYTHONINTERP_FOUND) + return() +endif() + +######################################################################## +# Install python sources +######################################################################## +GR_PYTHON_INSTALL( + FILES + __init__.py + STANAG_4285.py + DESTINATION ${GR_PYTHON_DIR}/digitalhf/physical_layer +) + diff --git a/python/physical_layer/STANAG_4285.py b/python/physical_layer/STANAG_4285.py new file mode 100644 index 0000000..5dbc4c4 --- /dev/null +++ b/python/physical_layer/STANAG_4285.py @@ -0,0 +1,80 @@ +## -*- python -*- + +import numpy as np +from gnuradio import digital + +class PhysicalLayer(object): + """Physical layer description for STANAG 4285""" + + def __init__(self, mode=0): + """For STANAG 4258 the mode has to be set manually: mode=0 -> BPSK, mode=1 -> QPSK, mode=2 -> 8PSK""" + self._constellations = [PhysicalLayer.make_psk(2, [0,1]), + PhysicalLayer.make_psk(4, [0,1,3,2]), + PhysicalLayer.make_psk(8, [1,0,2,3,6,7,5,4])] + self._preamble = [PhysicalLayer.get_preamble(), 0] ## BPSK + self._data = [PhysicalLayer.get_data(), mode] ## according to the mode + self._counter = 0 + + def set_mode(self): + """For STANAG 4258 the mode has to be set manually: mode=0 -> BPSK, mode=1 -> QPSK, mode=2 -> 8PSK""" + self._data[1] = mode + + def get_constellations(self): + return self._constellations + + def get_frame(self): + """returns the known+unknown symbols and scrambling""" + if self._counter == 0: + return self._preamble + else: + return self._data + + def get_doppler(self): ## symbols + """used for doppler shift update, for determining which frame to provide next, + and for stopping at end of data/when the signal quality is too low""" + self._counter = (self._counter+1)&1 + ## TODO: doppler calculations + doppler = 0.1234 + return [True, doppler] + + @staticmethod + def get_preamble(): + """preamble symbols + scrambler(=1)""" + state = np.array([1,1,0,1,0], dtype=np.bool) + taps = np.array([0,0,1,0,1], dtype=np.bool) + p = np.zeros(80, dtype=np.uint8) + for i in range(80): + p[i] = state[-1] + state = np.concatenate(([np.sum(state&taps)&1], state[0:-1])) + a = np.zeros(80, dtype=[('symb',np.complex64), ('scramble', np.complex64)]) + ## BPSK modulation + constellation = PhysicalLayer.make_psk(2,range(2))['points'] + a['symb'] = constellation[p,] + a['scramble'] = 1 + return a + + @staticmethod + def get_data(): + """data symbols + scrambler; for unknown symbols 'symb'=0""" + state = np.array([1,1,1,1,1,1,1,1,1], dtype=np.bool) + taps = np.array([0,0,0,0,1,0,0,0,1], dtype=np.bool) + p = np.zeros(176, dtype=np.uint8) + for i in range(176): + p[i] = np.sum(state[-3:]*[4,2,1]) + for j in range(3): + state = np.concatenate(([np.sum(state&taps)&1], state[0:-1])) + a=np.zeros(176, dtype=[('symb',np.complex64), ('scramble', np.complex64)]) + ## PSK-8 modulation + constellation = PhysicalLayer.make_psk(8,range(8))['points'] + a['scramble'] = constellation[p,] + a['symb'][ 32: 48] = 1 ## mini-probe 1 + a['symb'][ 80: 96] = 1 ## mini-probe 2 + a['symb'][128:144] = 1 ## mini-probe 3 + return a + + @staticmethod + def make_psk(n, gray_code): + c = np.zeros(n, dtype=[('points', np.complex64), ('symbols', np.uint8)]) + c['points'] = np.exp(2*np.pi*1j*np.array(range(n))/n) + c['symbols'] = gray_code + return c diff --git a/python/physical_layer/__init__.py b/python/physical_layer/__init__.py new file mode 100644 index 0000000..323c58c --- /dev/null +++ b/python/physical_layer/__init__.py @@ -0,0 +1,26 @@ +# +# Copyright 2008,2009 Free Software Foundation, Inc. +# +# This application 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. +# +# This application 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, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +# The presence of this file turns this directory into a Python package + +''' +Physical layer descriptions for digital HF modes +''' + +# import any pure python here +#