mirror of
https://github.com/hb9fxq/gr-digitalhf
synced 2024-11-04 21:55:53 +00:00
python->C++ information transfer
This commit is contained in:
parent
37d98db318
commit
cdbe8dcd88
|
@ -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)
|
||||
|
|
|
@ -413,7 +413,7 @@
|
|||
</param>
|
||||
<param>
|
||||
<key>py_obj_name</key>
|
||||
<value>/Users/chm/Software/gr-digitalhf/examples/s4285.py</value>
|
||||
<value>STANAG_4285</value>
|
||||
</param>
|
||||
</block>
|
||||
<connection>
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
|
||||
} ;
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#endif
|
||||
|
||||
#include <gnuradio/io_signature.h>
|
||||
#include <gnuradio/digital/constellation.h>
|
||||
#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<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);
|
||||
|
||||
|
@ -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<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 gr */
|
||||
|
|
|
@ -21,10 +21,10 @@
|
|||
#ifndef 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/numpy.hpp>
|
||||
#include <gnuradio/digital/constellation.h>
|
||||
#include <digitalhf/adaptive_dfe.h>
|
||||
|
||||
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<gr_complex> _taps_samples;
|
||||
std::vector<gr_complex> _taps_symbols;
|
||||
|
||||
// boost::python::numpy::ndarray _symbols;
|
||||
boost::python::object _constellation;
|
||||
std::vector<gr::digital::constellation_sptr> _constellations;
|
||||
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:
|
||||
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();
|
||||
|
|
|
@ -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)
|
||||
|
|
38
python/physical_layer/CMakeLists.txt
Normal file
38
python/physical_layer/CMakeLists.txt
Normal 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
|
||||
)
|
||||
|
80
python/physical_layer/STANAG_4285.py
Normal file
80
python/physical_layer/STANAG_4285.py
Normal 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
|
26
python/physical_layer/__init__.py
Normal file
26
python/physical_layer/__init__.py
Normal 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
|
||||
#
|
Loading…
Reference in a new issue