Browse Source

Replace swig with pybind11 for gr3.9 master compat

Signed-off-by: Eric Wild <ewild@sysmocom.de>
fixeria/gr3.10
Matt Mills 2 years ago committed by Eric Wild
parent
commit
0d727b3ef8
  1. 16
      CMakeLists.txt
  2. 19
      docs/doxygen/pydoc_macros.h
  3. 224
      docs/doxygen/update_pydoc.py
  4. 20
      python/CMakeLists.txt
  5. 29
      python/__init__.py
  6. 30
      python/bindings/CMakeLists.txt
  7. 0
      python/bindings/README.md
  8. 53
      python/bindings/bind_oot_file.py
  9. 1
      python/bindings/docstrings/README.md
  10. 153
      python/bindings/docstrings/sink_pydoc_template.h
  11. 162
      python/bindings/docstrings/source_pydoc_template.h
  12. 78
      python/bindings/header_utils.py
  13. 57
      python/bindings/python_bindings.cc
  14. 320
      python/bindings/sink_python.cc
  15. 342
      python/bindings/source_python.cc
  16. 57
      swig/CMakeLists.txt
  17. 82
      swig/osmosdr_swig.i

16
CMakeLists.txt

@ -41,7 +41,7 @@ set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "")
list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_SOURCE_DIR}/cmake/Modules)
# Find GNURadio (pmt and runtime are core, always included)
find_package(Gnuradio "3.8" REQUIRED COMPONENTS blocks fft filter)
find_package(Gnuradio "3.9" REQUIRED COMPONENTS blocks fft filter)
# Set the version information here
set(VERSION_MAJOR 0)
@ -188,20 +188,11 @@ find_package(Doxygen)
##########
find_package(PythonLibs 3)
find_package(SWIG)
if(SWIG_FOUND)
message(STATUS "Minimum SWIG version required is 1.3.31")
set(SWIG_VERSION_CHECK FALSE)
if("${SWIG_VERSION}" VERSION_GREATER "1.3.30")
set(SWIG_VERSION_CHECK TRUE)
endif()
endif(SWIG_FOUND)
find_package(pybind11)
GR_REGISTER_COMPONENT("Python support" ENABLE_PYTHON
PYTHONLIBS_FOUND
SWIG_FOUND
SWIG_VERSION_CHECK
pybind11_FOUND
)
########################################################################
@ -269,7 +260,6 @@ add_custom_target(uninstall
add_subdirectory(include/osmosdr)
add_subdirectory(lib)
if(ENABLE_PYTHON)
add_subdirectory(swig)
add_subdirectory(python)
add_subdirectory(grc)
add_subdirectory(apps)

19
docs/doxygen/pydoc_macros.h

@ -0,0 +1,19 @@
#ifndef PYDOC_MACROS_H
#define PYDOC_MACROS_H
#define __EXPAND(x) x
#define __COUNT(_1, _2, _3, _4, _5, _6, _7, COUNT, ...) COUNT
#define __VA_SIZE(...) __EXPAND(__COUNT(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1))
#define __CAT1(a, b) a##b
#define __CAT2(a, b) __CAT1(a, b)
#define __DOC1(n1) __doc_##n1
#define __DOC2(n1, n2) __doc_##n1##_##n2
#define __DOC3(n1, n2, n3) __doc_##n1##_##n2##_##n3
#define __DOC4(n1, n2, n3, n4) __doc_##n1##_##n2##_##n3##_##n4
#define __DOC5(n1, n2, n3, n4, n5) __doc_##n1##_##n2##_##n3##_##n4##_##n5
#define __DOC6(n1, n2, n3, n4, n5, n6) __doc_##n1##_##n2##_##n3##_##n4##_##n5##_##n6
#define __DOC7(n1, n2, n3, n4, n5, n6, n7) \
__doc_##n1##_##n2##_##n3##_##n4##_##n5##_##n6##_##n7
#define DOC(...) __EXPAND(__EXPAND(__CAT2(__DOC, __VA_SIZE(__VA_ARGS__)))(__VA_ARGS__))
#endif // PYDOC_MACROS_H

224
docs/doxygen/swig_doc.py → docs/doxygen/update_pydoc.py

@ -2,34 +2,22 @@
# Copyright 2010-2012 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-osmosdr
# This file is a part of gnuradio
#
# 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.
# SPDX-License-Identifier: GPL-3.0-or-later
#
# 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.
#
"""
Creates the swig_doc.i SWIG interface file.
Execute using: python swig_doc.py xml_path outputfilename
Updates the *pydoc_h files for a module
Execute using: python update_pydoc.py xml_path outputfilename
The file instructs SWIG to transfer the doxygen comments into the
The file instructs Pybind11 to transfer the doxygen comments into the
python docstrings.
"""
from __future__ import unicode_literals
import sys, time
import os, sys, time, glob, re, json
from argparse import ArgumentParser
from doxyxml import DoxyIndex, DoxyClass, DoxyFriend, DoxyFunction, DoxyFile
from doxyxml import DoxyOther, base
@ -87,6 +75,7 @@ def utoascii(text):
return ''
out = text.encode('ascii', 'replace')
# swig will require us to replace blackslash with 4 backslashes
# TODO: evaluate what this should be for pybind11
out = out.replace(b'\\', b'\\\\\\\\')
out = out.replace(b'"', b'\\"').decode('ascii')
return str(out)
@ -115,7 +104,7 @@ def format_params(parameteritems):
entry_templ = '%feature("docstring") {name} "{docstring}"'
def make_entry(obj, name=None, templ="{description}", description=None, params=[]):
"""
Create a docstring entry for a swig interface file.
Create a docstring key/value pair, where the key is the object name.
obj - a doxyxml object from which documentation will be extracted.
name - the name of the C object (defaults to obj.name())
@ -126,6 +115,8 @@ def make_entry(obj, name=None, templ="{description}", description=None, params=[
"""
if name is None:
name=obj.name()
if hasattr(obj,'_parse_data') and hasattr(obj._parse_data,'definition'):
name=obj._parse_data.definition.split(' ')[-1]
if "operator " in name:
return ''
if description is None:
@ -134,56 +125,28 @@ def make_entry(obj, name=None, templ="{description}", description=None, params=[
description += '\n\n'
description += utoascii(format_params(params))
docstring = templ.format(description=description)
if not docstring:
return ''
return entry_templ.format(
name=name,
docstring=docstring,
)
def make_func_entry(func, name=None, description=None, params=None):
"""
Create a function docstring entry for a swig interface file.
func - a doxyxml object from which documentation will be extracted.
name - the name of the C object (defaults to func.name())
description - if this optional variable is set then it's value is
used as the description instead of extracting it from func.
params - a parameter list that overrides using func.params.
"""
#if params is None:
# params = func.params
#params = [prm.declname for prm in params]
#if params:
# sig = "Params: (%s)" % ", ".join(params)
#else:
# sig = "Params: (NONE)"
#templ = "{description}\n\n" + sig
#return make_entry(func, name=name, templ=utoascii(templ),
# description=description)
return make_entry(func, name=name, description=description, params=params)
return {name: docstring}
def make_class_entry(klass, description=None, ignored_methods=[], params=None):
"""
Create a class docstring for a swig interface file.
Create a class docstring key/value pair.
"""
if params is None:
params = klass.params
output = []
output.append(make_entry(klass, description=description, params=params))
output = {}
output.update(make_entry(klass, description=description, params=params))
for func in klass.in_category(DoxyFunction):
if func.name() not in ignored_methods:
name = klass.name() + '::' + func.name()
output.append(make_func_entry(func, name=name))
return "\n\n".join(output)
output.update(make_entry(func, name=name))
return output
def make_block_entry(di, block):
"""
Create class and function docstrings of a gnuradio block for a
swig interface file.
Create class and function docstrings of a gnuradio block
"""
descriptions = []
# Get the documentation associated with the class.
@ -208,18 +171,16 @@ def make_block_entry(di, block):
super_description = "\n\n".join(descriptions)
# Associate the combined description with the class and
# the make function.
output = []
output.append(make_class_entry(block, description=super_description))
output.append(make_func_entry(make_func, description=super_description,
output = {}
output.update(make_class_entry(block, description=super_description))
output.update(make_entry(make_func, description=super_description,
params=block.params))
return "\n\n".join(output)
return output
def make_block2_entry(di, block):
"""
Create class and function docstrings of a new style gnuradio block for a
swig interface file.
Create class and function docstrings of a new style gnuradio block
"""
descriptions = []
# For new style blocks all the relevant documentation should be
# associated with the 'make' method.
class_description = combine_descriptions(block)
@ -228,28 +189,21 @@ def make_block2_entry(di, block):
description = class_description + "\n\nConstructor Specific Documentation:\n\n" + make_description
# Associate the combined description with the class and
# the make function.
output = []
output.append(make_class_entry(
output = {}
output.update(make_class_entry(
block, description=description,
ignored_methods=['make'], params=make_func.params))
makename = block.name() + '::make'
output.append(make_func_entry(
output.update(make_entry(
make_func, name=makename, description=description,
params=make_func.params))
return "\n\n".join(output)
return output
def make_swig_interface_file(di, swigdocfilename, custom_output=None):
def get_docstrings_dict(di, custom_output=None):
output = ["""
/*
* This file was automatically generated using swig_doc.py.
*
* Any changes to it will be lost next time it is regenerated.
*/
"""]
if custom_output is not None:
output.append(custom_output)
output = {}
if custom_output:
output.update(custom_output)
# Create docstrings for the blocks.
blocks = di.in_category(Block)
@ -262,7 +216,7 @@ def make_swig_interface_file(di, swigdocfilename, custom_output=None):
# Don't want to risk writing to output twice.
if make_func.name() not in make_funcs:
make_funcs.add(make_func.name())
output.append(make_block_entry(di, block))
output.update(make_block_entry(di, block))
except block.ParsingError:
sys.stderr.write('Parsing error for block {0}\n'.format(block.name()))
raise
@ -274,7 +228,7 @@ def make_swig_interface_file(di, swigdocfilename, custom_output=None):
# Don't want to risk writing to output twice.
if make_func_name not in make_funcs:
make_funcs.add(make_func_name)
output.append(make_block2_entry(di, block))
output.update(make_block2_entry(di, block))
except block.ParsingError:
sys.stderr.write('Parsing error for block {0}\n'.format(block.name()))
raise
@ -285,7 +239,7 @@ def make_swig_interface_file(di, swigdocfilename, custom_output=None):
if f.name() not in make_funcs and not f.name().startswith('std::')]
for f in funcs:
try:
output.append(make_func_entry(f))
output.update(make_entry(f))
except f.ParsingError:
sys.stderr.write('Parsing error for function {0}\n'.format(f.name()))
@ -296,37 +250,97 @@ def make_swig_interface_file(di, swigdocfilename, custom_output=None):
if k.name() not in block_names and not k.name().startswith('std::')]
for k in klasses:
try:
output.append(make_class_entry(k))
output.update(make_class_entry(k))
except k.ParsingError:
sys.stderr.write('Parsing error for class {0}\n'.format(k.name()))
# Docstrings are not created for anything that is not a function or a class.
# If this excludes anything important please add it here.
output = "\n\n".join(output)
swig_doc = open(swigdocfilename, 'w')
swig_doc.write(output)
swig_doc.close()
return output
def sub_docstring_in_pydoc_h(pydoc_files, docstrings_dict, output_dir, filter_str=None):
if filter_str:
docstrings_dict = {k: v for k, v in docstrings_dict.items() if k.startswith(filter_str)}
with open(os.path.join(output_dir,'docstring_status'),'w') as status_file:
for pydoc_file in pydoc_files:
if filter_str:
filter_str2 = "::".join((filter_str,os.path.split(pydoc_file)[-1].split('_pydoc_template.h')[0]))
docstrings_dict2 = {k: v for k, v in docstrings_dict.items() if k.startswith(filter_str2)}
else:
docstrings_dict2 = docstrings_dict
file_in = open(pydoc_file,'r').read()
for key, value in docstrings_dict2.items():
file_in_tmp = file_in
try:
doc_key = key.split("::")
# if 'gr' in doc_key:
# doc_key.remove('gr')
doc_key = '_'.join(doc_key)
regexp = r'(__doc_{} =\sR\"doc\()[^)]*(\)doc\")'.format(doc_key)
regexp = re.compile(regexp, re.MULTILINE)
(file_in, nsubs) = regexp.subn(r'\1'+value+r'\2', file_in, count=1)
if nsubs == 1:
status_file.write("PASS: " + pydoc_file + "\n")
except KeyboardInterrupt:
raise KeyboardInterrupt
except: # be permissive, TODO log, but just leave the docstring blank
status_file.write("FAIL: " + pydoc_file + "\n")
file_in = file_in_tmp
output_pathname = os.path.join(output_dir, os.path.basename(pydoc_file).replace('_template.h','.h'))
# FIXME: Remove this debug print
print('output docstrings to {}'.format(output_pathname))
with open(output_pathname,'w') as file_out:
file_out.write(file_in)
def copy_docstring_templates(pydoc_files, output_dir):
with open(os.path.join(output_dir,'docstring_status'),'w') as status_file:
for pydoc_file in pydoc_files:
file_in = open(pydoc_file,'r').read()
output_pathname = os.path.join(output_dir, os.path.basename(pydoc_file).replace('_template.h','.h'))
# FIXME: Remove this debug print
print('copy docstrings to {}'.format(output_pathname))
with open(output_pathname,'w') as file_out:
file_out.write(file_in)
status_file.write("DONE")
def argParse():
"""Parses commandline args."""
desc='Scrape the doxygen generated xml for docstrings to insert into python bindings'
parser = ArgumentParser(description=desc)
parser.add_argument("function", help="Operation to perform on docstrings", choices=["scrape","sub","copy"])
parser.add_argument("--xml_path")
parser.add_argument("--bindings_dir")
parser.add_argument("--output_dir")
parser.add_argument("--json_path")
parser.add_argument("--filter", default=None)
return parser.parse_args()
if __name__ == "__main__":
# Parse command line options and set up doxyxml.
err_msg = "Execute using: python swig_doc.py xml_path outputfilename"
if len(sys.argv) != 3:
raise Exception(err_msg)
xml_path = sys.argv[1]
swigdocfilename = sys.argv[2]
di = DoxyIndex(xml_path)
# gnuradio.gr.msq_queue.insert_tail and delete_head create errors unless docstrings are defined!
# This is presumably a bug in SWIG.
#msg_q = di.get_member(u'gr_msg_queue', DoxyClass)
#insert_tail = msg_q.get_member(u'insert_tail', DoxyFunction)
#delete_head = msg_q.get_member(u'delete_head', DoxyFunction)
output = []
#output.append(make_func_entry(insert_tail, name='gr_py_msg_queue__insert_tail'))
#output.append(make_func_entry(delete_head, name='gr_py_msg_queue__delete_head'))
custom_output = "\n\n".join(output)
# Generate the docstrings interface file.
make_swig_interface_file(di, swigdocfilename, custom_output=custom_output)
args = argParse()
if args.function.lower() == 'scrape':
di = DoxyIndex(args.xml_path)
docstrings_dict = get_docstrings_dict(di)
with open(args.json_path, 'w') as fp:
json.dump(docstrings_dict, fp)
elif args.function.lower() == 'sub':
with open(args.json_path, 'r') as fp:
docstrings_dict = json.load(fp)
pydoc_files = glob.glob(os.path.join(args.bindings_dir,'*_pydoc_template.h'))
sub_docstring_in_pydoc_h(pydoc_files, docstrings_dict, args.output_dir, args.filter)
elif args.function.lower() == 'copy':
pydoc_files = glob.glob(os.path.join(args.bindings_dir,'*_pydoc_template.h'))
copy_docstring_templates(pydoc_files, args.output_dir)

20
python/CMakeLists.txt

@ -1,21 +1,10 @@
# Copyright 2011 Free Software Foundation, Inc.
#
# This file is part of gr-osmosdr
# This file was generated by gr_modtool, a tool from the GNU Radio framework
# This file is a part of gr-osmosdr
#
# gr-osmosdr 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.
# SPDX-License-Identifier: GPL-3.0-or-later
#
# gr-osmosdr 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 gr-osmosdr; 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
@ -25,6 +14,8 @@ if(NOT PYTHONINTERP_FOUND)
return()
endif()
add_subdirectory(bindings)
########################################################################
# Install python sources
########################################################################
@ -40,4 +31,3 @@ GR_PYTHON_INSTALL(
include(GrTest)
set(GR_TEST_TARGET_DEPS gnuradio-osmosdr)
set(GR_TEST_PYTHON_DIRS ${CMAKE_BINARY_DIR}/swig)

29
python/__init__.py

@ -1,25 +1,24 @@
#
# 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.
# SPDX-License-Identifier: GPL-3.0-or-later
#
# The presence of this file turns this directory into a Python package
'''
This is the GNU Radio OsmoSDR module.
This is the GNU Radio OSMOSDR module. Place your Python package
description here (python/__init__.py).
'''
import os
from .osmosdr_swig import *
# import pybind11 generated symbols into the osmosdr namespace
try:
from .osmosdr_python import *
except ImportError:
dirname, filename = os.path.split(os.path.abspath(__file__))
__path__.append(os.path.join(dirname, "bindings"))
from .osmosdr_python import *
# import any pure python here
#

30
python/bindings/CMakeLists.txt

@ -0,0 +1,30 @@
# Copyright 2020 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
GR_PYTHON_CHECK_MODULE_RAW(
"pygccxml"
"import pygccxml"
PYGCCXML_FOUND
)
include(GrPybind)
########################################################################
# Python Bindings
########################################################################
list(APPEND osmosdr_python_files
sink_python.cc
source_python.cc
python_bindings.cc)
GR_PYBIND_MAKE_OOT(osmosdr
../..
gr::osmosdr
"${osmosdr_python_files}")
install(TARGETS osmosdr_python DESTINATION ${GR_PYTHON_DIR}/osmosdr COMPONENT pythonapi)

0
python/bindings/README.md

53
python/bindings/bind_oot_file.py

@ -0,0 +1,53 @@
import warnings
import argparse
import os
from gnuradio.bindtool import BindingGenerator
import pathlib
import sys
parser = argparse.ArgumentParser(description='Bind a GR Out of Tree Block')
parser.add_argument('--module', type=str,
help='Name of gr module containing file to bind (e.g. fft digital analog)')
parser.add_argument('--output_dir', default='/tmp',
help='Output directory of generated bindings')
parser.add_argument('--prefix', help='Prefix of Installed GNU Radio')
parser.add_argument('--src', help='Directory of gnuradio source tree',
default=os.path.dirname(os.path.abspath(__file__))+'/../../..')
parser.add_argument(
'--filename', help="File to be parsed")
parser.add_argument(
'--include', help='Additional Include Dirs, separated', default=(), nargs='+')
parser.add_argument(
'--status', help='Location of output file for general status (used during cmake)', default=None
)
parser.add_argument(
'--flag_automatic', default='0'
)
parser.add_argument(
'--flag_pygccxml', default='0'
)
args = parser.parse_args()
prefix = args.prefix
output_dir = args.output_dir
includes = args.include
name = args.module
namespace = [name]
prefix_include_root = name
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)
bg = BindingGenerator(prefix, namespace,
prefix_include_root, output_dir, addl_includes=','.join(args.include), catch_exceptions=False, write_json_output=False, status_output=args.status,
flag_automatic=True if args.flag_automatic.lower() in [
'1', 'true'] else False,
flag_pygccxml=True if args.flag_pygccxml.lower() in ['1', 'true'] else False)
bg.gen_file_binding(args.filename)

1
python/bindings/docstrings/README.md

@ -0,0 +1 @@
This directory stores templates for docstrings that are scraped from the include header files for each block

153
python/bindings/docstrings/sink_pydoc_template.h

@ -0,0 +1,153 @@
/*
* Copyright 2020 Free Software Foundation, Inc.
*
* This file is part of GNU Radio
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#include "pydoc_macros.h"
#define D(...) DOC(gr,osmosdr, __VA_ARGS__ )
/*
This file contains placeholders for docstrings for the Python bindings.
Do not edit! These were automatically extracted during the binding process
and will be overwritten during the build process
*/
static const char *__doc_osmosdr_sink = R"doc()doc";
static const char *__doc_osmosdr_sink_sink_0 = R"doc()doc";
static const char *__doc_osmosdr_sink_sink_1 = R"doc()doc";
static const char *__doc_osmosdr_sink_make = R"doc()doc";
static const char *__doc_osmosdr_sink_get_num_channels = R"doc()doc";
static const char *__doc_osmosdr_sink_get_sample_rates = R"doc()doc";
static const char *__doc_osmosdr_sink_set_sample_rate = R"doc()doc";
static const char *__doc_osmosdr_sink_get_sample_rate = R"doc()doc";
static const char *__doc_osmosdr_sink_get_freq_range = R"doc()doc";
static const char *__doc_osmosdr_sink_set_center_freq = R"doc()doc";
static const char *__doc_osmosdr_sink_get_center_freq = R"doc()doc";
static const char *__doc_osmosdr_sink_set_freq_corr = R"doc()doc";
static const char *__doc_osmosdr_sink_get_freq_corr = R"doc()doc";
static const char *__doc_osmosdr_sink_get_gain_names = R"doc()doc";
static const char *__doc_osmosdr_sink_get_gain_range_0 = R"doc()doc";
static const char *__doc_osmosdr_sink_get_gain_range_1 = R"doc()doc";
static const char *__doc_osmosdr_sink_set_gain_mode = R"doc()doc";
static const char *__doc_osmosdr_sink_get_gain_mode = R"doc()doc";
static const char *__doc_osmosdr_sink_set_gain_0 = R"doc()doc";
static const char *__doc_osmosdr_sink_set_gain_1 = R"doc()doc";
static const char *__doc_osmosdr_sink_get_gain_0 = R"doc()doc";
static const char *__doc_osmosdr_sink_get_gain_1 = R"doc()doc";
static const char *__doc_osmosdr_sink_set_if_gain = R"doc()doc";
static const char *__doc_osmosdr_sink_set_bb_gain = R"doc()doc";
static const char *__doc_osmosdr_sink_get_antennas = R"doc()doc";
static const char *__doc_osmosdr_sink_set_antenna = R"doc()doc";
static const char *__doc_osmosdr_sink_get_antenna = R"doc()doc";
static const char *__doc_osmosdr_sink_set_dc_offset = R"doc()doc";
static const char *__doc_osmosdr_sink_set_iq_balance = R"doc()doc";
static const char *__doc_osmosdr_sink_set_bandwidth = R"doc()doc";
static const char *__doc_osmosdr_sink_get_bandwidth = R"doc()doc";
static const char *__doc_osmosdr_sink_get_bandwidth_range = R"doc()doc";
static const char *__doc_osmosdr_sink_set_time_source = R"doc()doc";
static const char *__doc_osmosdr_sink_get_time_source = R"doc()doc";
static const char *__doc_osmosdr_sink_get_time_sources = R"doc()doc";
static const char *__doc_osmosdr_sink_set_clock_source = R"doc()doc";
static const char *__doc_osmosdr_sink_get_clock_source = R"doc()doc";
static const char *__doc_osmosdr_sink_get_clock_sources = R"doc()doc";
static const char *__doc_osmosdr_sink_get_clock_rate = R"doc()doc";
static const char *__doc_osmosdr_sink_set_clock_rate = R"doc()doc";
static const char *__doc_osmosdr_sink_get_time_now = R"doc()doc";
static const char *__doc_osmosdr_sink_get_time_last_pps = R"doc()doc";
static const char *__doc_osmosdr_sink_set_time_now = R"doc()doc";
static const char *__doc_osmosdr_sink_set_time_next_pps = R"doc()doc";
static const char *__doc_osmosdr_sink_set_time_unknown_pps = R"doc()doc";

162
python/bindings/docstrings/source_pydoc_template.h

@ -0,0 +1,162 @@
/*
* Copyright 2020 Free Software Foundation, Inc.
*
* This file is part of GNU Radio
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#include "pydoc_macros.h"
#define D(...) DOC(gr,osmosdr, __VA_ARGS__ )
/*
This file contains placeholders for docstrings for the Python bindings.
Do not edit! These were automatically extracted during the binding process
and will be overwritten during the build process
*/
static const char *__doc_osmosdr_source = R"doc()doc";
static const char *__doc_osmosdr_source_source_0 = R"doc()doc";
static const char *__doc_osmosdr_source_source_1 = R"doc()doc";
static const char *__doc_osmosdr_source_make = R"doc()doc";
static const char *__doc_osmosdr_source_get_num_channels = R"doc()doc";
static const char *__doc_osmosdr_source_seek = R"doc()doc";
static const char *__doc_osmosdr_source_get_sample_rates = R"doc()doc";
static const char *__doc_osmosdr_source_set_sample_rate = R"doc()doc";
static const char *__doc_osmosdr_source_get_sample_rate = R"doc()doc";
static const char *__doc_osmosdr_source_get_freq_range = R"doc()doc";
static const char *__doc_osmosdr_source_set_center_freq = R"doc()doc";
static const char *__doc_osmosdr_source_get_center_freq = R"doc()doc";
static const char *__doc_osmosdr_source_set_freq_corr = R"doc()doc";
static const char *__doc_osmosdr_source_get_freq_corr = R"doc()doc";
static const char *__doc_osmosdr_source_get_gain_names = R"doc()doc";
static const char *__doc_osmosdr_source_get_gain_range_0 = R"doc()doc";
static const char *__doc_osmosdr_source_get_gain_range_1 = R"doc()doc";
static const char *__doc_osmosdr_source_set_gain_mode = R"doc()doc";
static const char *__doc_osmosdr_source_get_gain_mode = R"doc()doc";
static const char *__doc_osmosdr_source_set_gain_0 = R"doc()doc";
static const char *__doc_osmosdr_source_set_gain_1 = R"doc()doc";
static const char *__doc_osmosdr_source_get_gain_0 = R"doc()doc";
static const char *__doc_osmosdr_source_get_gain_1 = R"doc()doc";
static const char *__doc_osmosdr_source_set_if_gain = R"doc()doc";
static const char *__doc_osmosdr_source_set_bb_gain = R"doc()doc";
static const char *__doc_osmosdr_source_get_antennas = R"doc()doc";
static const char *__doc_osmosdr_source_set_antenna = R"doc()doc";
static const char *__doc_osmosdr_source_get_antenna = R"doc()doc";
static const char *__doc_osmosdr_source_set_dc_offset_mode = R"doc()doc";
static const char *__doc_osmosdr_source_set_dc_offset = R"doc()doc";
static const char *__doc_osmosdr_source_set_iq_balance_mode = R"doc()doc";
static const char *__doc_osmosdr_source_set_iq_balance = R"doc()doc";
static const char *__doc_osmosdr_source_set_bandwidth = R"doc()doc";
static const char *__doc_osmosdr_source_get_bandwidth = R"doc()doc";
static const char *__doc_osmosdr_source_get_bandwidth_range = R"doc()doc";
static const char *__doc_osmosdr_source_set_time_source = R"doc()doc";
static const char *__doc_osmosdr_source_get_time_source = R"doc()doc";
static const char *__doc_osmosdr_source_get_time_sources = R"doc()doc";
static const char *__doc_osmosdr_source_set_clock_source = R"doc()doc";
static const char *__doc_osmosdr_source_get_clock_source = R"doc()doc";
static const char *__doc_osmosdr_source_get_clock_sources = R"doc()doc";
static const char *__doc_osmosdr_source_get_clock_rate = R"doc()doc";
static const char *__doc_osmosdr_source_set_clock_rate = R"doc()doc";
static const char *__doc_osmosdr_source_get_time_now = R"doc()doc";
static const char *__doc_osmosdr_source_get_time_last_pps = R"doc()doc";
static const char *__doc_osmosdr_source_set_time_now = R"doc()doc";
static const char *__doc_osmosdr_source_set_time_next_pps = R"doc()doc";
static const char *__doc_osmosdr_source_set_time_unknown_pps = R"doc()doc";

78
python/bindings/header_utils.py

@ -0,0 +1,78 @@
# Utilities for reading values in header files
from argparse import ArgumentParser
import re
class PybindHeaderParser:
def __init__(self, pathname):
with open(pathname,'r') as f:
self.file_txt = f.read()
def get_flag_automatic(self):
# p = re.compile(r'BINDTOOL_GEN_AUTOMATIC\(([^\s])\)')
# m = p.search(self.file_txt)
m = re.search(r'BINDTOOL_GEN_AUTOMATIC\(([^\s])\)', self.file_txt)
if (m and m.group(1) == '1'):
return True
else:
return False
def get_flag_pygccxml(self):
# p = re.compile(r'BINDTOOL_USE_PYGCCXML\(([^\s])\)')
# m = p.search(self.file_txt)
m = re.search(r'BINDTOOL_USE_PYGCCXML\(([^\s])\)', self.file_txt)
if (m and m.group(1) == '1'):
return True
else:
return False
def get_header_filename(self):
# p = re.compile(r'BINDTOOL_HEADER_FILE\(([^\s]*)\)')
# m = p.search(self.file_txt)
m = re.search(r'BINDTOOL_HEADER_FILE\(([^\s]*)\)', self.file_txt)
if (m):
return m.group(1)
else:
return None
def get_header_file_hash(self):
# p = re.compile(r'BINDTOOL_HEADER_FILE_HASH\(([^\s]*)\)')
# m = p.search(self.file_txt)
m = re.search(r'BINDTOOL_HEADER_FILE_HASH\(([^\s]*)\)', self.file_txt)
if (m):
return m.group(1)
else:
return None
def get_flags(self):
return f'{self.get_flag_automatic()};{self.get_flag_pygccxml()};{self.get_header_filename()};{self.get_header_file_hash()};'
def argParse():
"""Parses commandline args."""
desc='Reads the parameters from the comment block in the pybind files'
parser = ArgumentParser(description=desc)
parser.add_argument("function", help="Operation to perform on comment block of pybind file", choices=["flag_auto","flag_pygccxml","header_filename","header_file_hash","all"])
parser.add_argument("pathname", help="Pathname of pybind c++ file to read, e.g. blockname_python.cc")
return parser.parse_args()
if __name__ == "__main__":
# Parse command line options and set up doxyxml.
args = argParse()
pbhp = PybindHeaderParser(args.pathname)
if args.function == "flag_auto":
print(pbhp.get_flag_automatic())
elif args.function == "flag_pygccxml":
print(pbhp.get_flag_pygccxml())
elif args.function == "header_filename":
print(pbhp.get_header_filename())
elif args.function == "header_file_hash":
print(pbhp.get_header_file_hash())
elif args.function == "all":
print(pbhp.get_flags())

57
python/bindings/python_bindings.cc

@ -0,0 +1,57 @@
/*
* Copyright 2020 Free Software Foundation, Inc.
*
* This file is part of GNU Radio
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#include <pybind11/pybind11.h>
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include <numpy/arrayobject.h>
namespace py = pybind11;
// Headers for binding functions
/**************************************/
/* The following comment block is used for
/* gr_modtool to insert function prototypes
/* Please do not delete
/**************************************/
// BINDING_FUNCTION_PROTOTYPES(
void bind_sink(py::module& m);
void bind_source(py::module& m);
// ) END BINDING_FUNCTION_PROTOTYPES
// We need this hack because import_array() returns NULL
// for newer Python versions.
// This function is also necessary because it ensures access to the C API
// and removes a warning.
void* init_numpy()
{
import_array();
return NULL;
}
PYBIND11_MODULE(osmosdr_python, m)
{
// Initialize the numpy C API
// (otherwise we will see segmentation faults)
init_numpy();
// Allow access to base block methods
py::module::import("gnuradio.gr");
/**************************************/
/* The following comment block is used for
/* gr_modtool to insert binding function calls
/* Please do not delete
/**************************************/
// BINDING_FUNCTION_CALLS(
bind_sink(m);
bind_source(m);
// ) END BINDING_FUNCTION_CALLS
}

320
python/bindings/sink_python.cc

@ -0,0 +1,320 @@
/*
* Copyright 2020 Free Software Foundation, Inc.
*
* This file is part of GNU Radio
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
/***********************************************************************************/
/* This file is automatically generated using bindtool and can be manually edited */
/* The following lines can be configured to regenerate this file during cmake */
/* If manual edits are made, the following tags should be modified accordingly. */
/* BINDTOOL_GEN_AUTOMATIC(1) */
/* BINDTOOL_USE_PYGCCXML(0) */
/* BINDTOOL_HEADER_FILE(sink.h) */
/* BINDTOOL_HEADER_FILE_HASH(d4331eb8a19b7a2aa4ed0100039f7a0e) */
/***********************************************************************************/
#include <pybind11/complex.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
namespace py = pybind11;
#include <osmosdr/sink.h>
// pydoc.h is automatically generated in the build directory
#include <sink_pydoc.h>
void bind_sink(py::module& m)
{
using sink = ::osmosdr::sink;
py::class_<sink, gr::hier_block2,
std::shared_ptr<sink>>(m, "sink", D(sink))
.def(py::init(&sink::make),
py::arg("args") = "",
D(sink,make)
)
.def("get_num_channels",&sink::get_num_channels,
D(sink,get_num_channels)
)
.def("get_sample_rates",&sink::get_sample_rates,
D(sink,get_sample_rates)
)
.def("set_sample_rate",&sink::set_sample_rate,
py::arg("rate"),
D(sink,set_sample_rate)
)
.def("get_sample_rate",&sink::get_sample_rate,
D(sink,get_sample_rate)
)
.def("get_freq_range",&sink::get_freq_range,
py::arg("chan") = 0,
D(sink,get_freq_range)
)
.def("set_center_freq",&sink::set_center_freq,
py::arg("freq"),
py::arg("chan") = 0,
D(sink,set_center_freq)
)
.def("get_center_freq",&sink::get_center_freq,
py::arg("chan") = 0,
D(sink,get_center_freq)
)
.def("set_freq_corr",&sink::set_freq_corr,
py::arg("ppm"),
py::arg("chan") = 0,
D(sink,set_freq_corr)
)
.def("get_freq_corr",&sink::get_freq_corr,
py::arg("chan") = 0,
D(sink,get_freq_corr)
)
.def("get_gain_names",&sink::get_gain_names,
py::arg("chan") = 0,
D(sink,get_gain_names)
)
.def("get_gain_range",(osmosdr::gain_range_t (sink::*)(size_t))&sink::get_gain_range,
py::arg("chan") = 0,
D(sink,get_gain_range,0)
)
.def("get_gain_range",(osmosdr::gain_range_t (sink::*)(std::string const &, size_t))&sink::get_gain_range,
py::arg("name"),
py::arg("chan") = 0,
D(sink,get_gain_range,1)
)
.def("set_gain_mode",&sink::set_gain_mode,
py::arg("automatic"),
py::arg("chan") = 0,
D(sink,set_gain_mode)
)
.def("get_gain_mode",&sink::get_gain_mode,
py::arg("chan") = 0,
D(sink,get_gain_mode)
)
.def("set_gain",(double (sink::*)(double, size_t))&sink::set_gain,
py::arg("gain"),
py::arg("chan") = 0,
D(sink,set_gain,0)
)
.def("set_gain",(double (sink::*)(double, std::string const &, size_t))&sink::set_gain,
py::arg("gain"),
py::arg("name"),
py::arg("chan") = 0,
D(sink,set_gain,1)
)
.def("get_gain",(double (sink::*)(size_t))&sink::get_gain,
py::arg("chan") = 0,
D(sink,get_gain,0)
)
.def("get_gain",(double (sink::*)(std::string const &, size_t))&sink::get_gain,
py::arg("name"),
py::arg("chan") = 0,
D(sink,get_gain,1)
)
.def("set_if_gain",&sink::set_if_gain,
py::arg("gain"),
py::arg("chan") = 0,
D(sink,set_if_gain)
)
.def("set_bb_gain",&sink::set_bb_gain,
py::arg("gain"),
py::arg("chan") = 0,
D(sink,set_bb_gain)
)
.def("get_antennas",&sink::get_antennas,
py::arg("chan") = 0,
D(sink,get_antennas)
)
.def("set_antenna",&sink::set_antenna,
py::arg("antenna"),
py::arg("chan") = 0,
D(sink,set_antenna)
)
.def("get_antenna",&sink::get_antenna,
py::arg("chan") = 0,
D(sink,get_antenna)
)
.def("set_dc_offset",&sink::set_dc_offset,
py::arg("offset"),
py::arg("chan") = 0,
D(sink,set_dc_offset)
)
.def("set_iq_balance",&sink::set_iq_balance,
py::arg("balance"),
py::arg("chan") = 0,
D(sink,set_iq_balance)
)
.def("set_bandwidth",&sink::set_bandwidth,
py::arg("bandwidth"),
py::arg("chan") = 0,
D(sink,set_bandwidth)
)
.def("get_bandwidth",&sink::get_bandwidth,
py::arg("chan") = 0,
D(sink,get_bandwidth)
)
.def("get_bandwidth_range",&sink::get_bandwidth_range,
py::arg("chan") = 0,
D(sink,get_bandwidth_range)
)
.def("set_time_source",&sink::set_time_source,
py::arg("source"),
py::arg("mboard") = 0,
D(sink,set_time_source)
)
.def("get_time_source",&sink::get_time_source,
py::arg("mboard"),
D(sink,get_time_source)
)
.def("get_time_sources",&sink::get_time_sources,
py::arg("mboard"),
D(sink,get_time_sources)
)
.def("set_clock_source",&sink::set_clock_source,
py::arg("source"),
py::arg("mboard") = 0,
D(sink,set_clock_source)
)
.def("get_clock_source",&sink::get_clock_source,
py::arg("mboard"),
D(sink,get_clock_source)
)
.def("get_clock_sources",&sink::get_clock_sources,
py::arg("mboard"),
D(sink,get_clock_sources)
)
.def("get_clock_rate",&sink::get_clock_rate,
py::arg("mboard") = 0,
D(sink,get_clock_rate)
)
.def("set_clock_rate",&sink::set_clock_rate,
py::arg("rate"),
py::arg("mboard") = 0,
D(sink,set_clock_rate)
)
.def("get_time_now",&sink::get_time_now,
py::arg("mboard") = 0,
D(sink,get_time_now)
)
.def("get_time_last_pps",&sink::get_time_last_pps,
py::arg("mboard") = 0,
D(sink,get_time_last_pps)
)
.def("set_time_now",&sink::set_time_now,
py::arg("time_spec"),