From a520a46ed8b17e080b314cfafe32e588a8d313ae Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Fri, 8 Jan 2021 11:37:12 +0100 Subject: [PATCH] gr39: Add pybind11 bindings Signed-off-by: Sylvain Munaut --- CMakeLists.txt | 10 +++++ python/CMakeLists.txt | 2 + python/__init__.py | 5 ++- python/bindings/CMakeLists.txt | 65 ++++++++++++++++++++++++++++ python/bindings/fix_cc_python.cc | 58 +++++++++++++++++++++++++ python/bindings/optimize_c_python.cc | 61 ++++++++++++++++++++++++++ python/bindings/python_bindings.cc | 41 ++++++++++++++++++ 7 files changed, 240 insertions(+), 2 deletions(-) create mode 100644 python/bindings/CMakeLists.txt create mode 100644 python/bindings/fix_cc_python.cc create mode 100644 python/bindings/optimize_c_python.cc create mode 100644 python/bindings/python_bindings.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 567aefb..38a0d3d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -145,6 +145,16 @@ endif(APPLE) ######################################################################## find_package(Doxygen) +######################################################################## +# PyBind11 Related +######################################################################## + +find_package(pybind11 REQUIRED) +execute_process( + COMMAND "${PYTHON_EXECUTABLE}" -c + "try:\n import numpy\n import os\n inc_path = numpy.get_include()\n if os.path.exists(os.path.join(inc_path, 'numpy', 'arrayobject.h')):\n print(inc_path, end='')\nexcept:\n pass" + OUTPUT_VARIABLE PYTHON_NUMPY_INCLUDE_DIR) + ######################################################################## # Setup doxygen option ######################################################################## diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 6730db5..affde20 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -13,6 +13,8 @@ if(NOT PYTHONINTERP_FOUND) return() endif() +add_subdirectory(bindings) + ######################################################################## # Install python sources ######################################################################## diff --git a/python/__init__.py b/python/__init__.py index 4c5ba74..a98432c 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -5,7 +5,8 @@ # SPDX-License-Identifier: GPL-3.0-or-later ''' -This is the GNU Radio IQBALANCE module. Place your Python package -description here (python/__init__.py). +This is the GNU Radio IQBALANCE module. ''' +# import pybind11 generated symbols into the iqbalance namespace +from .iqbalance_python import * diff --git a/python/bindings/CMakeLists.txt b/python/bindings/CMakeLists.txt new file mode 100644 index 0000000..519b04d --- /dev/null +++ b/python/bindings/CMakeLists.txt @@ -0,0 +1,65 @@ +# Copyright 2011-2020 Free Software Foundation, Inc. +# Copyright 2013-2021 Sylvain Munaut +# +# This file is part of gr-iqbalance +# +# SPDX-License-Identifier: GPL-3.0-or-later + +######################################################################## +# Check for pygccxml +######################################################################## +GR_PYTHON_CHECK_MODULE_RAW( + "pygccxml" + "import pygccxml" + PYGCCXML_FOUND + ) + +# Official module is broken, makes too many assumptions that are not true +#include(GrPybind) + +include(GrPython) + +macro(GR_PYBIND_MAKE name updir filter files) + +configure_file(${CMAKE_SOURCE_DIR}/docs/doxygen/pydoc_macros.h ${CMAKE_CURRENT_BINARY_DIR} COPYONLY) + +pybind11_add_module(${name}_python ${files}) + +SET(MODULE_NAME ${name}) +if (${name} STREQUAL gr) + SET(MODULE_NAME "runtime") +endif() + +target_include_directories(${name}_python PUBLIC + ${CMAKE_CURRENT_BINARY_DIR} + ${PYTHON_NUMPY_INCLUDE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/${updir}/lib + ${CMAKE_CURRENT_SOURCE_DIR}/${updir}/include + ${PYBIND11_INCLUDE_DIR} +) +target_link_libraries(${name}_python PUBLIC ${Boost_LIBRARIES} ${PYTHON_LIBRARIES} gnuradio-${MODULE_NAME}) +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR + CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(${name}_python PRIVATE -Wno-unused-variable) # disable warnings for docstring templates +endif(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR + CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + +endmacro(GR_PYBIND_MAKE) + + + +######################################################################## +# Python Bindings +######################################################################## + +list(APPEND iqbalance_python_files + fix_cc_python.cc + optimize_c_python.cc + python_bindings.cc) + +GR_PYBIND_MAKE(iqbalance + ../.. + gr::iqbalance + "${iqbalance_python_files}") + +install(TARGETS iqbalance_python DESTINATION ${GR_PYTHON_DIR}/gnuradio/iqbalance COMPONENT pythonapi) diff --git a/python/bindings/fix_cc_python.cc b/python/bindings/fix_cc_python.cc new file mode 100644 index 0000000..be64a14 --- /dev/null +++ b/python/bindings/fix_cc_python.cc @@ -0,0 +1,58 @@ +/* + * Copyright 2013-2021 Sylvain Munaut + * + * This file is part of gr-iqbalance + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include +#include + +namespace py = pybind11; + +#include + +#define D(...) "" + +void bind_fix_cc(py::module& m) +{ + using fix_cc = gr::iqbalance::fix_cc; + + py::class_>(m, "fix_cc", D(fix_cc)) + + .def(py::init(&fix_cc::make), + py::arg("mag") = 0.0f, + py::arg("phase") = 0.0f, + D(fix_cc,make) + ) + + .def("set_mag", + &fix_cc::set_mag, + py::arg("mag"), + D(fix_cc,set_mag) + ) + + .def("mag", + &fix_cc::mag, + D(fix_cc,mag) + ) + + .def("set_phase", + &fix_cc::set_phase, + py::arg("phase"), + D(fix_cc,set_phase) + ) + + .def("phase", + &fix_cc::phase, + D(fix_cc,phase) + ) + + ; +} diff --git a/python/bindings/optimize_c_python.cc b/python/bindings/optimize_c_python.cc new file mode 100644 index 0000000..73aee10 --- /dev/null +++ b/python/bindings/optimize_c_python.cc @@ -0,0 +1,61 @@ +/* + * Copyright 2013-2021 Sylvain Munaut + * + * This file is part of gr-iqbalance + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include +#include + +namespace py = pybind11; + +#include + +#define D(...) "" + +void bind_optimize_c(py::module& m) +{ + using optimize_c = gr::iqbalance::optimize_c; + + py::class_>(m, "optimize_c", D(optimize_c)) + + .def(py::init(&optimize_c::make), + py::arg("period") = 0, + D(optimize_c,make) + ) + + .def("set_period", + &optimize_c::set_period, + py::arg("period"), + D(optimize_c,set_period) + ) + + .def("period", + &optimize_c::period, + D(optimize_c,period) + ) + + .def("mag", + &optimize_c::mag, + D(optimize_c,mag) + ) + + .def("phase", + &optimize_c::phase, + D(optimize_c,phase) + ) + + .def("reset", + &optimize_c::reset, + D(optimize_c,reset) + ) + + ; +} diff --git a/python/bindings/python_bindings.cc b/python/bindings/python_bindings.cc new file mode 100644 index 0000000..fbb69fe --- /dev/null +++ b/python/bindings/python_bindings.cc @@ -0,0 +1,41 @@ +/* + * Copyright 2020-2021 Free Software Foundation, Inc. + * Copyright 2013-2021 Sylvain Munaut + * + * This file is part of gr-iqbalance + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include + +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +#include + +namespace py = pybind11; + +void bind_fix_cc(py::module& m); +void bind_optimize_c(py::module& m); + +// 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(iqbalance_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"); + + bind_fix_cc(m); + bind_optimize_c(m); +}