1
0
Fork 0
mirror of https://github.com/hb9fxq/gr-digitalhf synced 2024-11-05 05:55:53 +00:00

python->C++ information transfer

This commit is contained in:
cmayer 2018-10-25 18:01:24 +02:00
parent 37d98db318
commit cdbe8dcd88
10 changed files with 225 additions and 121 deletions

View file

@ -72,15 +72,13 @@ if(CMAKE_COMPILER_IS_GNUCXX AND NOT WIN32)
add_definitions(-fvisibility=hidden) add_definitions(-fvisibility=hidden)
endif() endif()
#include(GrPython)
find_package(PythonInterp) find_package(PythonInterp)
if(NOT PYTHONINTERP_FOUND) if(NOT PYTHONINTERP_FOUND)
message(FATAL_ERROR "Python interpreter required by the build system.") message(FATAL_ERROR "Python interpreter required by the build system.")
endif(NOT PYTHONINTERP_FOUND) endif(NOT PYTHONINTERP_FOUND)
# locate python # locate python
include(FindPythonLibs) include(FindPythonLibs)
MESSAGE("YYY ${PYTHON_LIBRARIES}")
#)
######################################################################## ########################################################################
# Find boost # Find boost
@ -150,7 +148,7 @@ find_package(Doxygen)
# components required to the list of GR_REQUIRED_COMPONENTS (in all # components required to the list of GR_REQUIRED_COMPONENTS (in all
# caps such as FILTER or FFT) and change the version to the minimum # caps such as FILTER or FFT) and change the version to the minimum
# API compatible version required. # API compatible version required.
set(GR_REQUIRED_COMPONENTS RUNTIME) set(GR_REQUIRED_COMPONENTS RUNTIME DIGITAL)
find_package(Gnuradio "3.7.2" REQUIRED) find_package(Gnuradio "3.7.2" REQUIRED)
list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_SOURCE_DIR}/cmake/Modules) list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_SOURCE_DIR}/cmake/Modules)
include(GrVersion) include(GrVersion)

View file

@ -413,7 +413,7 @@
</param> </param>
<param> <param>
<key>py_obj_name</key> <key>py_obj_name</key>
<value>/Users/chm/Software/gr-digitalhf/examples/s4285.py</value> <value>STANAG_4285</value>
</param> </param>
</block> </block>
<connection> <connection>

View file

@ -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

View file

@ -50,7 +50,7 @@ class DIGITALHF_API adaptive_dfe : virtual public gr::block
int nB, // number of forward FIR taps int nB, // number of forward FIR taps
int nF, // number of backward FIR taps int nF, // number of backward FIR taps
int nW, // number of feedback taps int nW, // number of feedback taps
std::string py_file_name); std::string physical_layer_description);
} ; } ;

View file

@ -23,7 +23,6 @@
#endif #endif
#include <gnuradio/io_signature.h> #include <gnuradio/io_signature.h>
#include <gnuradio/digital/constellation.h>
#include "adaptive_dfe_impl.h" #include "adaptive_dfe_impl.h"
namespace gr { namespace gr {
@ -45,10 +44,10 @@ adaptive_dfe::make(int sps, // samples per symbol
int nB, // number of forward FIR taps int nB, // number of forward FIR taps
int nF, // number of backward FIR taps int nF, // number of backward FIR taps
int nW, // number of feedback taps int nW, // number of feedback taps
std::string python_file_name) std::string python_module_name)
{ {
return gnuradio::get_initial_sptr 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 nB, // number of forward FIR taps
int nF, // number of backward FIR taps int nF, // number of backward FIR taps
int nW, // number of feedback taps int nW, // number of feedback taps
std::string python_file_name) std::string python_module_name)
: gr::block("adaptive_dfe", : gr::block("adaptive_dfe",
gr::io_signature::make(1, 1, sizeof(gr_complex)), gr::io_signature::make(1, 1, sizeof(gr_complex)),
gr::io_signature::make(0, 0, 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) , _nB(nB)
, _nF(nF) , _nF(nF)
, _nW(nW) , _nW(nW)
, _py_file_name(python_file_name) , _py_module_name(python_module_name)
, _physicalLayer() , _physicalLayer()
, _taps_samples(nB+nF+1) , _taps_samples(nB+nF+1)
, _taps_symbols(nW) { , _taps_symbols(nW)
, _constellations()
, _constellation_index()
, _symbols()
, _scramble()
{
// make sure python is ready for threading // make sure python is ready for threading
if( Py_IsInitialized() ){ if( Py_IsInitialized() ){
if(PyEval_ThreadsInitialized() != 1 ){ 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_const_void_star &input_items,
gr_vector_void_star &output_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; GILLock lock;
std::cout << "bits_per_symbol: " << boost::python::extract<int>(_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); consume_each (noutput_items);
@ -112,39 +117,16 @@ adaptive_dfe_impl::general_work(int noutput_items,
return 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() bool adaptive_dfe_impl::start()
{ {
std::cout << "adaptive_dfe_impl::start()" << std::endl; std::cout << "adaptive_dfe_impl::start()" << std::endl;
GILLock lock; GILLock lock;
try { try {
boost::python::object main = boost::python::import("__main__"); boost::python::object module = boost::python::import(boost::python::str("digitalhf.physical_layer." + _py_module_name));
boost::python::object globals = main.attr("__dict__");
boost::python::object module = import("physicalLayer", _py_file_name, globals);
boost::python::object PhysicalLayer = module.attr("PhysicalLayer"); boost::python::object PhysicalLayer = module.attr("PhysicalLayer");
_physicalLayer = PhysicalLayer(); _physicalLayer = PhysicalLayer();
update_constellations(_physicalLayer.attr("get_constellations")());
_physicalLayer.attr("get_frame")(); } catch (boost::python::error_already_set const&) {
} catch (const boost::python::error_already_set& ) {
PyErr_Print(); PyErr_Print();
return false; return false;
} }
@ -158,5 +140,48 @@ bool adaptive_dfe_impl::stop()
return true; return true;
} }
void adaptive_dfe_impl::update_constellations(boost::python::object obj)
{
int const n = boost::python::extract<int>(obj.attr("__len__")());
_constellations.resize(n);
for (int i=0; i<n; ++i) {
boost::python::numpy::ndarray const& array = boost::python::numpy::array(obj[i]);
char const* data = array.get_data();
int const m = array.shape(0);
std::vector<gr_complex> constell(m);
std::vector<int> pre_diff_code(m);
for (int j=0; j<m; ++j) {
std::memcpy(&constell[j], data+9*j, sizeof(gr_complex));
pre_diff_code[j] = (data+9*j)[8];
}
unsigned int const rotational_symmetry = 0;
unsigned int const dimensionality = 1;
_constellations[i] = gr::digital::constellation_calcdist::make(constell, pre_diff_code, rotational_symmetry, dimensionality);
}
}
void adaptive_dfe_impl::update_frame_information(boost::python::object obj)
{
int const n = boost::python::extract<int>(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<m; ++i) {
std::memcpy(&_symbols[i], data+16*i, sizeof(gr_complex));
std::memcpy(&_scramble[i], data+16*i+8, sizeof(gr_complex));
}
_constellation_index = boost::python::extract<int>(obj[1]);
}
void adaptive_dfe_impl::update_doppler_information(boost::python::object obj)
{
int const n = boost::python::extract<int>(obj.attr("__len__")());
assert(n==2);
double const do_continue = boost::python::extract<bool>(obj[0]);
double const doppler = boost::python::extract<float>(obj[1]);
// TODO
}
} /* namespace digitalhf */ } /* namespace digitalhf */
} /* namespace gr */ } /* namespace gr */

View file

@ -21,10 +21,10 @@
#ifndef INCLUDED_DIGITALHF_ADAPTIVE_DFE_IMPL_H #ifndef INCLUDED_DIGITALHF_ADAPTIVE_DFE_IMPL_H
#define INCLUDED_DIGITALHF_ADAPTIVE_DFE_IMPL_H #define INCLUDED_DIGITALHF_ADAPTIVE_DFE_IMPL_H
#include <digitalhf/adaptive_dfe.h>
#include <boost/python.hpp> #include <boost/python.hpp>
#include <boost/python/numpy.hpp> #include <boost/python/numpy.hpp>
#include <gnuradio/digital/constellation.h>
#include <digitalhf/adaptive_dfe.h>
namespace gr { namespace gr {
namespace digitalhf { namespace digitalhf {
@ -34,26 +34,32 @@ private:
int _sps; int _sps;
int _nB, _nF, _nW; int _nB, _nF, _nW;
std::string _py_file_name; // module name w.r.t. digitalhf.physical_layer containing a PhysicalLayer class
boost::python::object _physicalLayer; std::string _py_module_name;
boost::python::object _physicalLayer; // class instance of physical layer description
std::vector<gr_complex> _taps_samples; std::vector<gr_complex> _taps_samples;
std::vector<gr_complex> _taps_symbols; std::vector<gr_complex> _taps_symbols;
// boost::python::numpy::ndarray _symbols; std::vector<gr::digital::constellation_sptr> _constellations;
boost::python::object _constellation; int _constellation_index;
std::vector<gr_complex> _symbols;
std::vector<gr_complex> _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: public:
adaptive_dfe_impl(int sps, // samples per symbol adaptive_dfe_impl(int sps, // samples per symbol
int nB, // number of forward FIR taps int nB, // number of forward FIR taps
int nF, // number of backward FIR taps int nF, // number of backward FIR taps
int nW, // number of symbol taps int nW, // number of symbol taps
std::string python_file_name); std::string physical_layer_description);
virtual ~adaptive_dfe_impl(); virtual ~adaptive_dfe_impl();
// Where all the action really happens
void forecast (int noutput_items, gr_vector_int &ninput_items_required); void forecast (int noutput_items, gr_vector_int &ninput_items_required);
virtual bool start(); virtual bool start();

View file

@ -43,3 +43,5 @@ include(GrTest)
set(GR_TEST_TARGET_DEPS gnuradio-digitalhf) set(GR_TEST_TARGET_DEPS gnuradio-digitalhf)
set(GR_TEST_PYTHON_DIRS ${CMAKE_BINARY_DIR}/swig) 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) GR_ADD_TEST(qa_adaptive_dfe ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_adaptive_dfe.py)
add_subdirectory(physical_layer)

View file

@ -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
)

View file

@ -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

View file

@ -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
#