diff options
27 files changed, 1624 insertions, 712 deletions
diff --git a/host/CMakeLists.txt b/host/CMakeLists.txt index 2fb78cb0d..5b19be476 100644 --- a/host/CMakeLists.txt +++ b/host/CMakeLists.txt @@ -1,6 +1,5 @@ # -# Copyright 2010-2016 Ettus Research LLC -# Copyright 2018 Ettus Research, a National Instruments Company +# Copyright 2010-2018 Ettus Research, a National Instruments Company # # SPDX-License-Identifier: GPL-3.0 # @@ -281,9 +280,18 @@ ENDIF(CYGWIN) IF(WIN32) ADD_DEFINITIONS(-D_WIN32_WINNT=0x0501) #minimum version required is windows xp ADD_DEFINITIONS(-DNOMINMAX) #disables stupidity and enables std::min and std::max - ENDIF(WIN32) - +ENDIF(WIN32) +######################################################################## +# Choose a version of Python +######################################################################## +OPTION(ENABLE_PYTHON3 "Enable Python 3. Default is Python 2" OFF) +SET(BOOST_PYTHON_COMPONENT python) +SET(BOOST_PYTHON_VERSION 2.7) +IF(${ENABLE_PYTHON3}) + SET(BOOST_PYTHON_COMPONENT python3) + SET(BOOST_PYTHON_VERSION 3) +ENDIF() ######################################################################## # Setup Boost @@ -299,11 +307,9 @@ SET(BOOST_REQUIRED_COMPONENTS system unit_test_framework serialization - ) - -SET(BOOST_OPTIONAL_COMPONENTS - python ) + +SET(BOOST_OPTIONAL_COMPONENTS ${BOOST_PYTHON_COMPONENT}) IF(MINGW) LIST(APPEND BOOST_REQUIRED_COMPONENTS thread_win32) ELSE() @@ -330,20 +336,27 @@ SET(Boost_ADDITIONAL_VERSIONS ) #Python API requirements -MESSAGE(STATUS "Looking for optional Boost copmponents...") +MESSAGE(STATUS "Looking for optional Boost components...") FIND_PACKAGE(Boost ${BOOST_MIN_VERSION} OPTIONAL_COMPONENTS ${BOOST_OPTIONAL_COMPONENTS}) -MESSAGE(STATUS "Looking for required Boost copmponents...") +MESSAGE(STATUS "Looking for required Boost components...") FIND_PACKAGE(Boost ${BOOST_MIN_VERSION} COMPONENTS ${BOOST_REQUIRED_COMPONENTS}) INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS}) LINK_DIRECTORIES(${Boost_LIBRARY_DIRS}) +#Check to see if the Python version we're looking for exists +SET(BOOST_PYTHON_FOUND ${Boost_PYTHON_FOUND}) +SET(BOOST_PYTHON_LIBRARY ${Boost_PYTHON_LIBRARY}) +IF(${ENABLE_PYTHON3}) + SET(BOOST_PYTHON_FOUND ${Boost_PYTHON3_FOUND}) + SET(BOOST_PYTHON_LIBRARY ${Boost_PYTHON3_LIBRARY}) +ENDIF() + MESSAGE(STATUS "Boost include directories: ${Boost_INCLUDE_DIRS}") MESSAGE(STATUS "Boost library directories: ${Boost_LIBRARY_DIRS}") MESSAGE(STATUS "Boost libraries: ${Boost_LIBRARIES}") - ######################################################################## # Additional settings for build environment ######################################################################## @@ -355,11 +368,18 @@ INCLUDE(UHDLog) ######################################################################## # Check Python Modules ######################################################################## +FIND_PACKAGE(PythonInterp ${BOOST_PYTHON_VERSION}) +FIND_PACKAGE(PythonLibs ${BOOST_PYTHON_VERSION}) INCLUDE(UHDPython) +SET(PYTHON_VERSION "platform.python_version() >= '2.7' and platform.python_version() < '3.0'") +IF(${ENABLE_PYTHON3}) + SET(PYTHON_VERSION "platform.python_version() >= '3.0'") +ENDIF() + PYTHON_CHECK_MODULE( - "Python version 2.7 or greater" - "platform" "platform.python_version() >= '2.7'" + "Python version ${BOOST_PYTHON_VERSION} or greater" + "platform" ${PYTHON_VERSION} HAVE_PYTHON_PLAT_MIN_VERSION ) @@ -373,16 +393,16 @@ PYTHON_CHECK_MODULE( "requests 2.0 or greater" "requests" "requests.__version__ >= '2.0'" HAVE_PYTHON_MODULE_REQUESTS - ) +) PYTHON_CHECK_MODULE( "numpy 1.7 or greater" "numpy" "LooseVersion(numpy.__version__) >= LooseVersion('1.7')" HAVE_PYTHON_MODULE_NUMPY - ) +) SET(PYTHON_ADDITIONAL_VERSIONS 2.7 3.4 3.5) -FIND_PACKAGE(PythonLibs) + ######################################################################## # Create Uninstall Target ######################################################################## @@ -410,7 +430,7 @@ UHD_INSTALL(FILES ######################################################################## LIBUHD_REGISTER_COMPONENT("LibUHD" ENABLE_LIBUHD ON "Boost_FOUND;HAVE_PYTHON_PLAT_MIN_VERSION;HAVE_PYTHON_MODULE_MAKO" OFF ON) LIBUHD_REGISTER_COMPONENT("LibUHD - C API" ENABLE_C_API ON "ENABLE_LIBUHD" OFF OFF) -LIBUHD_REGISTER_COMPONENT("LibUHD - Python API" ENABLE_PYTHON_API ON "ENABLE_LIBUHD;Boost_PYTHON_FOUND;HAVE_PYTHON_MODULE_NUMPY;PythonLibs_FOUND" OFF OFF) +LIBUHD_REGISTER_COMPONENT("LibUHD - Python API" ENABLE_PYTHON_API OFF "ENABLE_LIBUHD;BOOST_PYTHON_FOUND;HAVE_PYTHON_MODULE_NUMPY;PythonLibs_FOUND" OFF OFF) LIBUHD_REGISTER_COMPONENT("Examples" ENABLE_EXAMPLES ON "ENABLE_LIBUHD" OFF OFF) LIBUHD_REGISTER_COMPONENT("Utils" ENABLE_UTILS ON "ENABLE_LIBUHD" OFF OFF) LIBUHD_REGISTER_COMPONENT("Tests" ENABLE_TESTS ON "ENABLE_LIBUHD" OFF OFF) diff --git a/host/docs/CMakeLists.txt b/host/docs/CMakeLists.txt index 41f248557..a5f6751bf 100644 --- a/host/docs/CMakeLists.txt +++ b/host/docs/CMakeLists.txt @@ -74,7 +74,7 @@ IF(ENABLE_DOXYGEN) LIST(APPEND header_files ${h_files}) SET(DOXYGEN_DEPENDENCIES ${DOXYGEN_DEPENDENCIES} ${header_files}) IF(ENABLE_DOXYGEN_FULL) - SET(DOXYGEN_INPUT_DIRS "${DOXYGEN_INPUT_DIRS} ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/lib") + SET(DOXYGEN_INPUT_DIRS "${DOXYGEN_INPUT_DIRS} ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/lib ${CMAKE_SOURCE_DIR}/python") ELSE(ENABLE_DOXYGEN_FULL) SET(DOXYGEN_INPUT_DIRS "${DOXYGEN_INPUT_DIRS} ${CMAKE_SOURCE_DIR}/include") ENDIF(ENABLE_DOXYGEN_FULL) diff --git a/host/docs/Doxyfile.in b/host/docs/Doxyfile.in index 009d02a3e..678ea6b8d 100644 --- a/host/docs/Doxyfile.in +++ b/host/docs/Doxyfile.in @@ -672,7 +672,7 @@ INPUT_ENCODING = UTF-8 # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl -FILE_PATTERNS = *.hpp *.dox *.h *.ipp *.md +FILE_PATTERNS = *.hpp *.dox *.h *.ipp *.md *.py # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. diff --git a/host/examples/python/CMakeLists.txt b/host/examples/python/CMakeLists.txt index 628bf10a2..4b75f57bd 100644 --- a/host/examples/python/CMakeLists.txt +++ b/host/examples/python/CMakeLists.txt @@ -1,23 +1,12 @@ # -# Copyright 2010-2012,2015 Ettus Research LLC +# Copyright 2017-2018 Ettus Research, a National Instruments Company # -# This program 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 of the License, or -# (at your option) any later version. -# -# This program 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, see <http://www.gnu.org/licenses/>. +# SPDX-License-Identifier: GPL-3.0-or-later # SET(python_examples - pyuhd_rx_to_file.py - pyuhd_tx_waveforms.py + rx_to_file.py + tx_waveforms.py ) -UHD_INSTALL(PROGRAMS ${python_examples} DESTINATION ${PKG_LIB_DIR}/examples COMPONENT examples) +UHD_INSTALL(PROGRAMS ${python_examples} DESTINATION ${PKG_LIB_DIR}/examples/python COMPONENT examples) diff --git a/host/examples/python/pyuhd_rx_to_file.py b/host/examples/python/rx_to_file.py index 0b4956b33..7017a08c2 100755 --- a/host/examples/python/pyuhd_rx_to_file.py +++ b/host/examples/python/rx_to_file.py @@ -1,27 +1,20 @@ -#! /usr/bin/env python +#!/usr/bin/env python # -# Copyright 2017 Ettus Research LLC +# Copyright 2017-2018 Ettus Research, a National Instruments Company # -# This program 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 of the License, or -# (at your option) any later version. -# -# This program 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, see <http://www.gnu.org/licenses/>. +# SPDX-License-Identifier: GPL-3.0-or-later # +""" +RX samples to file using Python API +""" - +import argparse import numpy as np import uhd -import argparse + def parse_args(): + """Parse the command line arguments""" parser = argparse.ArgumentParser() parser.add_argument("-a", "--args", default="", type=str) parser.add_argument("-o", "--output-file", type=str, required=True) @@ -30,17 +23,24 @@ def parse_args(): parser.add_argument("-d", "--duration", default=5.0, type=float) parser.add_argument("-c", "--channels", default=0, nargs="+", type=int) parser.add_argument("-g", "--gain", type=int, default=10) + parser.add_argument("-n", "--numpy", default=False, action="store_true", + help="Save output file in NumPy format (default: No)") return parser.parse_args() + def main(): + """RX samples and write to file""" args = parse_args() - usrp = uhd.multi_usrp(args.args) + usrp = uhd.usrp.MultiUSRP(args.args) num_samps = int(np.ceil(args.duration*args.rate)) if not isinstance(args.channels, list): args.channels = [args.channels] samps = usrp.recv_num_samps(num_samps, args.freq, args.rate, args.channels, args.gain) - with open(args.output_file, 'wb') as f: - np.save(f, samps, allow_pickle=False, fix_imports=False) + with open(args.output_file, 'wb') as out_file: + if args.numpy: + np.save(out_file, samps, allow_pickle=False, fix_imports=False) + else: + samps.tofile(out_file) if __name__ == "__main__": main() diff --git a/host/examples/python/pyuhd_tx_waveforms.py b/host/examples/python/tx_waveforms.py index d7d431f44..6db50fa2b 100755 --- a/host/examples/python/pyuhd_tx_waveforms.py +++ b/host/examples/python/tx_waveforms.py @@ -1,35 +1,28 @@ -#! /usr/bin/env python +#!/usr/bin/env python # -# Copyright 2017 Ettus Research LLC +# Copyright 2017-2018 Ettus Research, a National Instruments Company # -# This program 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 of the License, or -# (at your option) any later version. -# -# This program 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, see <http://www.gnu.org/licenses/>. +# SPDX-License-Identifier: GPL-3.0-or-later # +""" +Generate and TX samples using a set of waveforms, and waveform characteristics +""" +import argparse import numpy as np import uhd -import argparse - waveforms = { "sine": lambda n, tone_offset, rate: np.exp(n * 2j * np.pi * tone_offset / rate), "square": lambda n, tone_offset, rate: np.sign(waveforms["sine"](n, tone_offset, rate)), "const": lambda n, tone_offset, rate: 1 + 1j, - "ramp": lambda n, tone_offset, rate: 2*(n*(tone_offset/rate) - np.floor(float(0.5 + n*(tone_offset/rate)))) + "ramp": lambda n, tone_offset, rate: + 2*(n*(tone_offset/rate) - np.floor(float(0.5 + n*(tone_offset/rate)))) } def parse_args(): + """Parse the command line arguments""" parser = argparse.ArgumentParser() parser.add_argument("-a", "--args", default="", type=str) parser.add_argument( @@ -45,15 +38,16 @@ def parse_args(): def main(): + """TX samples based on input arguments""" args = parse_args() - usrp = uhd.multi_usrp(args.args) + usrp = uhd.usrp.MultiUSRP(args.args) if not isinstance(args.channels, list): args.channels = [args.channels] data = np.array( - map(lambda n: args.wave_ampl * waveforms[args.waveform](n, args.wave_freq, args.rate), + list(map(lambda n: args.wave_ampl * waveforms[args.waveform](n, args.wave_freq, args.rate), np.arange( int(10 * np.floor(args.rate / args.wave_freq)), - dtype=np.complex64)), + dtype=np.complex64))), dtype=np.complex64) # One period usrp.send_waveform(data, args.duration, args.freq, args.rate, diff --git a/host/lib/stream_python.hpp b/host/lib/stream_python.hpp new file mode 100644 index 000000000..c283d34a6 --- /dev/null +++ b/host/lib/stream_python.hpp @@ -0,0 +1,199 @@ +// +// Copyright 2017-2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_UHD_STREAM_PYTHON_HPP +#define INCLUDED_UHD_STREAM_PYTHON_HPP + +#include "utils/gil_release_python.hpp" +#include <uhd/stream.hpp> +#include <uhd/types/metadata.hpp> + +#include <boost/format.hpp> + +static size_t wrap_recv(uhd::rx_streamer *rx_stream, + bp::object &np_array, + bp::object &metadata) +{ + // Release the GIL + scoped_gil_release gil_release; + + // Extract the metadata + bp::extract<uhd::rx_metadata_t&> get_metadata(metadata); + if (not get_metadata.check()) + { + return 0; + } + + // Get a numpy array object from given python object + // No sanity checking possible! + PyObject* array_obj = PyArray_FROM_OF(np_array.ptr(), NPY_ARRAY_CARRAY); + PyArrayObject* array_type_obj = reinterpret_cast<PyArrayObject*>(array_obj); + + // Get dimensions of the numpy array + const size_t dims = PyArray_NDIM(array_type_obj); + const npy_intp* shape = PyArray_SHAPE(array_type_obj); + + // How many bytes to jump to get to the next element of this stride + // (next row) + const npy_intp* strides = PyArray_STRIDES(array_type_obj); + const size_t channels = rx_stream->get_num_channels(); + + // Check if numpy array sizes are okay + if (((channels > 1) && (dims != 2)) + or ((size_t) shape[0] < channels)) + { + // Manually decrement the ref count + Py_DECREF(array_obj); + // If we don't have a 2D NumPy array, assume we have a 1D array + size_t input_channels = (dims != 2) ? 1 : shape[0]; + throw uhd::runtime_error(str(boost::format( + "Number of RX channels (%d) does not match the dimensions of the data array (%d)") + % channels % input_channels)); + } + + // Get a pointer to the storage + std::vector<void*> channel_storage; + char* data = PyArray_BYTES(array_type_obj); + for (size_t i = 0; i < channels; ++i) + { + channel_storage.push_back((void*)(data + i * strides[0])); + } + + // Get data buffer and size of the array + size_t nsamps_per_buff; + if (dims > 1) { + nsamps_per_buff = (size_t) shape[1]; + } else { + nsamps_per_buff = PyArray_SIZE(array_type_obj); + } + + // Call the real recv() + const size_t result = rx_stream->recv( + channel_storage, + nsamps_per_buff, + get_metadata() + ); + + // Manually decrement the ref count + Py_DECREF(array_obj); + return result; +} + +static size_t wrap_send(uhd::tx_streamer *tx_stream, + bp::object &np_array, + bp::object &metadata) +{ + // Release the GIL + scoped_gil_release gil_release; + + // Extract the metadata + bp::extract<uhd::tx_metadata_t&> get_metadata(metadata); + // TODO: throw an error here? + if (not get_metadata.check()) + { + return 0; + } + + // Get a numpy array object from given python object + // No sanity checking possible! + // Note: this increases the ref count, which we'll need to manually decrease at the end + PyObject* array_obj = PyArray_FROM_OF(np_array.ptr(),NPY_ARRAY_CARRAY); + PyArrayObject* array_type_obj = reinterpret_cast<PyArrayObject*>(array_obj); + + // Get dimensions of the numpy array + const size_t dims = PyArray_NDIM(array_type_obj); + const npy_intp* shape = PyArray_SHAPE(array_type_obj); + + // How many bytes to jump to get to the next element of the stride + // (next row) + const npy_intp* strides = PyArray_STRIDES(array_type_obj); + const size_t channels = tx_stream->get_num_channels(); + + // Check if numpy array sizes are ok + if (((channels > 1) && (dims != 2)) + or ((size_t) shape[0] < channels)) + { + // Manually decrement the ref count + Py_DECREF(array_obj); + // If we don't have a 2D NumPy array, assume we have a 1D array + size_t input_channels = (dims != 2) ? 1 : shape[0]; + throw uhd::runtime_error(str(boost::format( + "Number of TX channels (%d) does not match the dimensions of the data array (%d)") + % channels % input_channels)); + } + + // Get a pointer to the storage + std::vector<void*> channel_storage; + char* data = PyArray_BYTES(array_type_obj); + for (size_t i = 0; i < channels; ++i) + { + channel_storage.push_back((void*)(data + i * strides[0])); + } + + // Get data buffer and size of the array + size_t nsamps_per_buff = (dims > 1) ? (size_t) shape[1] : PyArray_SIZE(array_type_obj); + + // Call the real recv() + const size_t result = tx_stream->send( + channel_storage, + nsamps_per_buff, + get_metadata() + ); + + // Manually decrement the ref count + Py_DECREF(array_obj); + return result; +} + +void export_stream() +{ + using stream_args_t = uhd::stream_args_t; + using rx_streamer = uhd::rx_streamer; + using tx_streamer = uhd::tx_streamer; + using async_metadata_t = uhd::async_metadata_t; + + bp::class_<stream_args_t> + ("stream_args", bp::init<const std::string&, const std::string&>()) + + // Properties + .def_readwrite("cpu_format", &stream_args_t::cpu_format) + .def_readwrite("otw_format", &stream_args_t::otw_format) + .def_readwrite("args" , &stream_args_t::args ) + .def_readwrite("channels" , &stream_args_t::channels ) + ; + + bp::class_< + rx_streamer, + boost::shared_ptr<rx_streamer>, + boost::noncopyable>("rx_streamer", "See: uhd::rx_streamer", bp::no_init) + + // Methods + .def("recv" , &wrap_recv ) + .def("get_num_channels" , &uhd::rx_streamer::get_num_channels ) + .def("get_max_num_samps", &uhd::rx_streamer::get_max_num_samps) + .def("issue_stream_cmd" , &uhd::rx_streamer::issue_stream_cmd ) + ; + + bp::class_< + tx_streamer, + boost::shared_ptr<tx_streamer>, + boost::noncopyable>("tx_streamer", "See: uhd::tx_streamer", bp::no_init) + + // Methods + .def("send" , &wrap_send ) + .def("get_num_channels" , &tx_streamer::get_num_channels ) + .def("get_max_num_samps", &tx_streamer::get_max_num_samps) + // FIXME: the timeout isn't actually an optional argument right now in Python + .def("recv_async_msg" , +[](tx_streamer& self, async_metadata_t& metadata, double timeout = 0.1) { + // Release the GIL + scoped_gil_release gil_release; + + return self.recv_async_msg(metadata, timeout); + }) + ; +} + +#endif /* INCLUDED_UHD_STREAM_PYTHON_HPP */ diff --git a/host/lib/types/filters_python.hpp b/host/lib/types/filters_python.hpp new file mode 100644 index 000000000..ff5785345 --- /dev/null +++ b/host/lib/types/filters_python.hpp @@ -0,0 +1,60 @@ +// +// Copyright 2017-2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_UHD_FILTERS_PYTHON_HPP +#define INCLUDED_UHD_FILTERS_PYTHON_HPP + +#include <uhd/types/filters.hpp> + +void export_filters() +{ + using filter_info_base = uhd::filter_info_base; + using filter_info_type = filter_info_base::filter_type; + using analog_filter_base = uhd::analog_filter_base; + using analog_filter_lp = uhd::analog_filter_lp; + + bp::enum_<filter_info_type>("filter_type") + .value("analog_low_pass" , filter_info_base::ANALOG_LOW_PASS ) + .value("analog_band_pass", filter_info_base::ANALOG_BAND_PASS) + .value("digital_i16" , filter_info_base::DIGITAL_I16 ) + .value("digital_fir_i16" , filter_info_base::DIGITAL_FIR_I16 ) + ; + + bp::class_< + filter_info_base, + boost::shared_ptr<filter_info_base> > + ("filter_info_base", bp::init<filter_info_type, bool, size_t>()) + + // Methods + .def("is_bypassed", &filter_info_base::is_bypassed ) + .def("get_type" , &filter_info_base::get_type ) + .def("__str__" , &filter_info_base::to_pp_string) + ; + + bp::class_< + analog_filter_base, + boost::shared_ptr<analog_filter_base>, + bp::bases<filter_info_base> > + ("analog_filter_base", bp::init<filter_info_type, bool, size_t, std::string>()) + + // Methods + .def("get_analog_type", &analog_filter_base::get_analog_type, bp::return_value_policy<bp::copy_const_reference>()) + ; + + bp::class_< + analog_filter_lp, + boost::shared_ptr<analog_filter_lp>, + bp::bases<analog_filter_base> > + ("analog_filter_lp", bp::init<filter_info_type, bool, size_t, const std::string, double, double>()) + + // Methods + .def("get_cutoff" , &analog_filter_lp::get_cutoff ) + .def("get_rolloff", &analog_filter_lp::get_rolloff) + .def("set_cutoff" , &analog_filter_lp::set_cutoff ) + ; +} + +#endif /* INCLUDED_UHD_FILTERS_PYTHON_HPP */ diff --git a/host/lib/types/metadata_python.hpp b/host/lib/types/metadata_python.hpp new file mode 100644 index 000000000..876756d73 --- /dev/null +++ b/host/lib/types/metadata_python.hpp @@ -0,0 +1,111 @@ +// +// Copyright 2017-2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_UHD_METADATA_PYTHON_HPP +#define INCLUDED_UHD_METADATA_PYTHON_HPP + +#include <uhd/types/ranges.hpp> + +void export_metadata() +{ + using range_t = uhd::range_t; + using meta_range_t = uhd::meta_range_t; + using rx_metadata_t = uhd::rx_metadata_t; + using error_code_t = rx_metadata_t::error_code_t; + using tx_metadata_t = uhd::tx_metadata_t; + using async_metadata_t = uhd::async_metadata_t; + using event_code_t = async_metadata_t::event_code_t; + + bp::enum_<error_code_t>("rx_metadata_error_code") + .value("none" , error_code_t::ERROR_CODE_NONE ) + .value("timeout" , error_code_t::ERROR_CODE_TIMEOUT ) + .value("late" , error_code_t::ERROR_CODE_LATE_COMMAND) + .value("broken_chain", error_code_t::ERROR_CODE_BROKEN_CHAIN) + .value("overflow" , error_code_t::ERROR_CODE_OVERFLOW ) + .value("alignment" , error_code_t::ERROR_CODE_ALIGNMENT ) + .value("bad_packet" , error_code_t::ERROR_CODE_BAD_PACKET ) + ; + + bp::class_<range_t> + ("range", bp::init<double>()) + + // Constructors + .def(bp::init<double, double, double>()) + + // Methods + .def("start" , &range_t::start ) + .def("stop" , &range_t::stop ) + .def("step" , &range_t::step ) + .def("__str__", &range_t::to_pp_string) + ; + + bp::class_<std::vector<range_t> >("range_vector") + .def(bp::vector_indexing_suite<std::vector<range_t> >()); + + bp::class_<meta_range_t, bp::bases<std::vector<range_t> > > + ("meta_range", bp::init<>()) + + // Constructors + .def(bp::init<double, double, double>()) + + // Methods + .def("start" , &meta_range_t::start ) + .def("stop" , &meta_range_t::stop ) + .def("step" , &meta_range_t::step ) + .def("clip" , &meta_range_t::clip ) + .def("__str__", &meta_range_t::to_pp_string) + ; + + bp::class_<rx_metadata_t>("rx_metadata", bp::init<>()) + + // Methods + .def("reset" , &rx_metadata_t::reset ) + .def("to_pp_string", &rx_metadata_t::to_pp_string) + .def("strerror" , &rx_metadata_t::strerror ) + .def("__str__" , &rx_metadata_t::to_pp_string, bp::args("compact") = false) + + // Properties + .def_readonly("has_time_spec" , &rx_metadata_t::has_time_spec ) + .def_readonly("time_spec" , &rx_metadata_t::time_spec ) + .def_readonly("more_fragments" , &rx_metadata_t::more_fragments ) + .def_readonly("start_of_burst" , &rx_metadata_t::start_of_burst ) + .def_readonly("end_of_burst" , &rx_metadata_t::end_of_burst ) + .def_readonly("error_code" , &rx_metadata_t::error_code ) + .def_readonly("out_of_sequence", &rx_metadata_t::out_of_sequence) + ; + + bp::class_<tx_metadata_t>("tx_metadata", bp::init<>()) + + // Properties + .def_readwrite("has_time_spec" , &tx_metadata_t::has_time_spec ) + .def_readwrite("time_spec" , &tx_metadata_t::time_spec ) + .def_readwrite("start_of_burst", &tx_metadata_t::start_of_burst) + .def_readwrite("end_of_burst" , &tx_metadata_t::end_of_burst ) + ; + + bp::enum_<event_code_t>("tx_metadata_event_code") + .value("burst_ack" , event_code_t::EVENT_CODE_BURST_ACK ) + .value("underflow" , event_code_t::EVENT_CODE_UNDERFLOW ) + .value("seq_error" , event_code_t::EVENT_CODE_SEQ_ERROR ) + .value("time_error" , event_code_t::EVENT_CODE_TIME_ERROR ) + .value("underflow_in_packet", event_code_t::EVENT_CODE_UNDERFLOW_IN_PACKET) + .value("seq_error_in_packet", event_code_t::EVENT_CODE_SEQ_ERROR_IN_BURST ) + .value("user_payload" , event_code_t::EVENT_CODE_USER_PAYLOAD ) + ; + + bp::class_<async_metadata_t>("async_metadata", bp::init<>()) + + // Properties + .def_readwrite("channel" , &async_metadata_t::channel ) + .def_readwrite("has_time_spec", &async_metadata_t::has_time_spec) + .def_readwrite("time_spec" , &async_metadata_t::time_spec ) + .def_readwrite("event_code" , &async_metadata_t::event_code ) + // TODO: Expose user payloads + //.def_readwrite("user_payload" , &async_metadata_t::user_payload ) + ; +} + +#endif /* INCLUDED_UHD_METADATA_PYTHON_HPP */ diff --git a/host/lib/types/sensors_python.hpp b/host/lib/types/sensors_python.hpp new file mode 100644 index 000000000..8fd9da4e3 --- /dev/null +++ b/host/lib/types/sensors_python.hpp @@ -0,0 +1,46 @@ +// +// Copyright 2017-2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_UHD_SENSORS_PYTHON_HPP +#define INCLUDED_UHD_SENSORS_PYTHON_HPP + +#include <uhd/types/sensors.hpp> + +void export_sensors() +{ + using sensor_value_t = uhd::sensor_value_t; + using data_type_t = sensor_value_t::data_type_t; + + bp::enum_<data_type_t>("data_type") + .value("b", data_type_t::BOOLEAN) + .value("i", data_type_t::INTEGER) + .value("r", data_type_t::REALNUM) + .value("s", data_type_t::STRING ) + ; + + bp::class_<sensor_value_t> + ("sensor_value", bp::init<const std::string&, bool, const std::string&, const std::string&>()) + + // Constructors + .def(bp::init<const std::string&, signed, const std::string&, const std::string&>()) + .def(bp::init<const std::string&, double, const std::string&, const std::string&>()) + .def(bp::init<const std::string&, const std::string& , const std::string&>()) + + // Methods + .def("to_bool", &sensor_value_t::to_bool ) + .def("to_int", &sensor_value_t::to_int ) + .def("to_real", &sensor_value_t::to_real ) + .def("__str__", &sensor_value_t::to_pp_string) + + // Properties + .add_property("name", &sensor_value_t::name ) + .add_property("value", &sensor_value_t::value) + .add_property("unit", &sensor_value_t::unit ) + .add_property("type", &sensor_value_t::type ) + ; +} + +#endif /* INCLUDED_UHD_SENSORS_PYTHON_HPP */ diff --git a/host/lib/types/serial_python.hpp b/host/lib/types/serial_python.hpp new file mode 100644 index 000000000..441bcc4d7 --- /dev/null +++ b/host/lib/types/serial_python.hpp @@ -0,0 +1,32 @@ +// +// Copyright 2017-2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_UHD_SERIAL_PYTHON_HPP +#define INCLUDED_UHD_SERIAL_PYTHON_HPP + +#include <uhd/types/serial.hpp> + +void export_spi_config() +{ + using spi_config_t = uhd::spi_config_t; + using spi_edge_t = spi_config_t::edge_t; + + bp::enum_<spi_edge_t>("spi_edge") + .value("EDGE_RISE" , spi_edge_t::EDGE_RISE) + .value("EDGE_FALL", spi_edge_t::EDGE_FALL) + ; + + bp::class_<spi_config_t>("spi_config", bp::init<spi_edge_t>()) + + // Properties + .add_property("mosi_edge" , &spi_config_t::mosi_edge ) + .add_property("miso_edge" , &spi_config_t::miso_edge ) + .add_property("use_custom_divider", &spi_config_t::use_custom_divider) + .add_property("divider" , &spi_config_t::divider ) + ; +} + +#endif /* INCLUDED_UHD_SERIAL_PYTHON_HPP */ diff --git a/host/lib/types/time_spec_python.hpp b/host/lib/types/time_spec_python.hpp new file mode 100644 index 000000000..f411af0b0 --- /dev/null +++ b/host/lib/types/time_spec_python.hpp @@ -0,0 +1,35 @@ +// +// Copyright 2017-2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_UHD_TIME_SPEC_PYTHON_HPP +#define INCLUDED_UHD_TIME_SPEC_PYTHON_HPP + +#include <uhd/types/time_spec.hpp> + +void export_time_spec() +{ + using time_spec_t = uhd::time_spec_t; + + bp::class_<time_spec_t>("time_spec", bp::init<double>()) + + // Methods + .def("from_ticks" , &time_spec_t::from_ticks ) + .staticmethod("from_ticks" ) + + .def("get_tick_count" , &time_spec_t::get_tick_count ) + .def("to_ticks" , &time_spec_t::to_ticks ) + .def("get_real_secs" , &time_spec_t::get_real_secs ) + .def("get_frac_secs" , &time_spec_t::get_frac_secs ) + + .def(bp::self += time_spec_t()) + .def(bp::self += double()) + .def(bp::self + double()) + .def(bp::self + time_spec_t()) + .def(bp::self -= time_spec_t()) + ; +} + +#endif /* INCLUDED_UHD_TIME_SPEC_PYTHON_HPP */ diff --git a/host/lib/types/tune_python.hpp b/host/lib/types/tune_python.hpp new file mode 100644 index 000000000..532cc2f8e --- /dev/null +++ b/host/lib/types/tune_python.hpp @@ -0,0 +1,39 @@ +// +// Copyright 2017-2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_UHD_TUNE_PYTHON_HPP +#define INCLUDED_UHD_TUNE_PYTHON_HPP + +#include <uhd/types/tune_result.hpp> +#include <uhd/types/tune_request.hpp> + +void export_tune() +{ + using tune_request_t = uhd::tune_request_t; + using tune_result_t = uhd::tune_result_t; + using policy_t = tune_request_t::policy_t; + + bp::enum_<policy_t>("tune_request_policy") + .value("none", tune_request_t::POLICY_NONE ) + .value("auto", tune_request_t::POLICY_AUTO ) + .value("manual", tune_request_t::POLICY_MANUAL) + ; + + bp::class_<tune_request_t>("tune_request", bp::init<double>()) + .def(bp::init<double, double>()) + .def_readwrite("target_freq" , &tune_request_t::target_freq ) + .def_readwrite("rf_freq_policy" , &tune_request_t::rf_freq_policy ) + .def_readwrite("dsp_freq_policy", &tune_request_t::dsp_freq_policy) + .def_readwrite("rf_freq" , &tune_request_t::rf_freq ) + .def_readwrite("dsp_freq" , &tune_request_t::dsp_freq ) + .def_readwrite("args" , &tune_request_t::args ) + ; + + bp::class_<tune_result_t>("tune_result", bp::init<>()) + ; +} + +#endif /* INCLUDED_UHD_TUNE_PYTHON_HPP */ diff --git a/host/lib/types/types_python.hpp b/host/lib/types/types_python.hpp new file mode 100644 index 000000000..4cda00d0d --- /dev/null +++ b/host/lib/types/types_python.hpp @@ -0,0 +1,33 @@ +// +// Copyright 2017-2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_UHD_TYPES_PYTHON_HPP +#define INCLUDED_UHD_TYPES_PYTHON_HPP + +#include <uhd/types/stream_cmd.hpp> + +void export_types() +{ + using stream_cmd_t = uhd::stream_cmd_t; + using stream_mode_t = stream_cmd_t::stream_mode_t; + + bp::enum_<stream_mode_t>("stream_mode") + .value("start_cont", stream_cmd_t::STREAM_MODE_START_CONTINUOUS ) + .value("stop_cont" , stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS ) + .value("num_done" , stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE) + .value("num_more" , stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE) + ; + + bp::class_<stream_cmd_t>("stream_cmd", bp::init<stream_cmd_t::stream_mode_t>()) + + // Properties + .def_readwrite("num_samps" , &stream_cmd_t::num_samps ) + .def_readwrite("time_spec" , &stream_cmd_t::time_spec ) + .def_readwrite("stream_now", &stream_cmd_t::stream_now) + ; +} + +#endif /* INCLUDED_UHD_TYPES_PYTHON_HPP */ diff --git a/host/lib/usrp/dboard_iface_python.hpp b/host/lib/usrp/dboard_iface_python.hpp new file mode 100644 index 000000000..df86fdcc8 --- /dev/null +++ b/host/lib/usrp/dboard_iface_python.hpp @@ -0,0 +1,95 @@ +// +// Copyright 2017-2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_UHD_USRP_DBOARD_IFACE_PYTHON_HPP +#define INCLUDED_UHD_USRP_DBOARD_IFACE_PYTHON_HPP + +#include <uhd/usrp/dboard_iface.hpp> +#include <uhd/usrp/gpio_defs.hpp> +#include "../include/uhdlib/usrp/gpio_defs.hpp" + +void export_dboard_iface() +{ + using dboard_iface = uhd::usrp::dboard_iface; + using special_props_t = uhd::usrp::dboard_iface_special_props_t; + using unit_t = dboard_iface::unit_t; + + using aux_dac_t = dboard_iface::aux_dac_t; + using aux_adc_t = dboard_iface::aux_adc_t; + + using gpio_atr_reg_t = uhd::usrp::gpio_atr::gpio_atr_reg_t; + using gpio_atr_mode_t = uhd::usrp::gpio_atr::gpio_atr_mode_t; + + bp::enum_<gpio_atr_reg_t>("gpio_atr_reg") + .value("ATR_REG_IDLE" , gpio_atr_reg_t::ATR_REG_IDLE ) + .value("ATR_REG_TX_ONLY" , gpio_atr_reg_t::ATR_REG_TX_ONLY ) + .value("ATR_REG_RX_ONLY" , gpio_atr_reg_t::ATR_REG_RX_ONLY ) + .value("ATR_REG_FULL_DUPLEX", gpio_atr_reg_t::ATR_REG_FULL_DUPLEX) + ; + + bp::enum_<gpio_atr_mode_t>("gpio_atr_mode") + .value("MODE_ATR" , gpio_atr_mode_t::MODE_ATR ) + .value("MODE_GPIO", gpio_atr_mode_t::MODE_GPIO) + ; + + bp::enum_<unit_t>("unit") + .value("UNIT_RX" , unit_t::UNIT_RX ) + .value("UNIT_TX" , unit_t::UNIT_TX ) + .value("UNIT_BOTH", unit_t::UNIT_BOTH) + ; + + bp::enum_<aux_dac_t>("aux_dac") + .value("AUX_DAC_A", aux_dac_t::AUX_DAC_A) + .value("AUX_DAC_B", aux_dac_t::AUX_DAC_B) + .value("AUX_DAC_C", aux_dac_t::AUX_DAC_C) + .value("AUX_DAC_D", aux_dac_t::AUX_DAC_D) + ; + + bp::enum_<aux_adc_t>("aux_adc") + .value("AUX_ADC_A", aux_adc_t::AUX_ADC_A) + .value("AUX_ADC_B", aux_adc_t::AUX_ADC_B) + ; + + bp::class_<special_props_t>("special_props") + + // Properties + .add_property("soft_clock_divider", &special_props_t::soft_clock_divider) + .add_property("mangle_i2c_addrs" , &special_props_t::mangle_i2c_addrs ) + ; + + bp::class_< + dboard_iface, + boost::shared_ptr<dboard_iface>, + boost::noncopyable>("dboard_iface", bp::no_init) + + // Methods + .def("get_special_props", &dboard_iface::get_special_props) + .def("write_aux_dac" , &dboard_iface::write_aux_dac ) + .def("read_aux_adc" , &dboard_iface::read_aux_adc ) + .def("set_pin_ctrl" , &dboard_iface::set_pin_ctrl ) + .def("get_pin_ctrl" , &dboard_iface::get_pin_ctrl ) + .def("set_atr_reg" , &dboard_iface::set_atr_reg ) + .def("get_atr_reg" , &dboard_iface::get_atr_reg ) + .def("set_gpio_ddr" , &dboard_iface::set_gpio_ddr ) + .def("get_gpio_ddr" , &dboard_iface::get_gpio_ddr ) + .def("get_gpio_out" , &dboard_iface::get_gpio_out ) + .def("set_gpio_out" , &dboard_iface::set_gpio_out ) + .def("read_gpio" , &dboard_iface::read_gpio ) + .def("write_spi" , &dboard_iface::write_spi ) + .def("read_write_spi" , &dboard_iface::read_write_spi ) + .def("set_clock_rate" , &dboard_iface::set_clock_rate ) + .def("get_clock_rate" , &dboard_iface::get_clock_rate ) + .def("get_clock_rates" , &dboard_iface::get_clock_rates ) + .def("set_clock_enabled", &dboard_iface::set_clock_enabled) + .def("get_codec_rate" , &dboard_iface::get_codec_rate ) + .def("set_fe_connection", &dboard_iface::set_fe_connection) + .def("get_command_time" , &dboard_iface::get_command_time ) + .def("set_command_time" , &dboard_iface::set_command_time ) + .def("sleep" , &dboard_iface::sleep ) + ; +} + +#endif /* INCLUDED_UHD_USRP_DBOARD_IFACE_PYTHON_HPP */ diff --git a/host/lib/usrp/fe_connection_python.hpp b/host/lib/usrp/fe_connection_python.hpp new file mode 100644 index 000000000..29bba1746 --- /dev/null +++ b/host/lib/usrp/fe_connection_python.hpp @@ -0,0 +1,39 @@ +// +// Copyright 2017-2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_UHD_USRP_FE_CONNECTION_PYTHON_HPP +#define INCLUDED_UHD_USRP_FE_CONNECTION_PYTHON_HPP + +#include <uhd/usrp/fe_connection.hpp> + +void export_fe_connection() +{ + using fe_connection_t = uhd::usrp::fe_connection_t; + using sampling_t = fe_connection_t::sampling_t; + + bp::enum_<sampling_t>("sampling") + .value("QUADRATURE", sampling_t::QUADRATURE) + .value("HETERODYNE", sampling_t::HETERODYNE) + .value("REAL" , sampling_t::REAL ) + ; + + bp::class_<fe_connection_t> + ("fe_connection", bp::init<sampling_t, bool, bool, bool, double>()) + + // Constructors + .def(bp::init<const std::string&, double>()) + + // Methods + .def("get_sampling_mode", &fe_connection_t::get_sampling_mode) + .def("is_iq_swapped" , &fe_connection_t::is_iq_swapped ) + .def("is_i_inverted" , &fe_connection_t::is_i_inverted ) + .def("is_q_inverted" , &fe_connection_t::is_q_inverted ) + .def("get_if_freq" , &fe_connection_t::get_if_freq ) + .def("set_if_freq" , &fe_connection_t::set_if_freq ) + ; +} + +#endif /* INCLUDED_UHD_USRP_FE_CONNECTION_PYTHON_HPP */ diff --git a/host/lib/usrp/multi_usrp_python.hpp b/host/lib/usrp/multi_usrp_python.hpp new file mode 100644 index 000000000..2c1bc6b59 --- /dev/null +++ b/host/lib/usrp/multi_usrp_python.hpp @@ -0,0 +1,441 @@ +// +// Copyright 2017-2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_UHD_USRP_MULTI_USRP_PYTHON_HPP +#define INCLUDED_UHD_USRP_MULTI_USRP_PYTHON_HPP + +#include <uhd/usrp/multi_usrp.hpp> + +// +// Boost.Python needs overloaded API calls to be defined +// +static void set_rx_gain_0( + uhd::usrp::multi_usrp *multi_usrp, double gain, const std::string &name, size_t chan = 0) +{ + multi_usrp->set_rx_gain(gain, name, chan); +} + +static void set_rx_gain_1( + uhd::usrp::multi_usrp *multi_usrp, double gain, size_t chan = 0) +{ + multi_usrp->set_rx_gain(gain, chan); +} + +BOOST_PYTHON_FUNCTION_OVERLOADS(overload_set_rx_gain_0, set_rx_gain_0, 3, 4); +BOOST_PYTHON_FUNCTION_OVERLOADS(overload_set_rx_gain_1, set_rx_gain_1, 2, 3); + +static void set_tx_gain_0( + uhd::usrp::multi_usrp *multi_usrp, double gain, const std::string &name, size_t chan = 0) +{ + multi_usrp->set_tx_gain(gain, name, chan); +} + +static void set_tx_gain_1( + uhd::usrp::multi_usrp *multi_usrp, double gain, size_t chan = 0) +{ + multi_usrp->set_tx_gain(gain, chan); +} + +BOOST_PYTHON_FUNCTION_OVERLOADS(overload_set_tx_gain_0, set_tx_gain_0, 3, 4); +BOOST_PYTHON_FUNCTION_OVERLOADS(overload_set_tx_gain_1, set_tx_gain_1, 2, 3); + +static double get_rx_gain_0( + uhd::usrp::multi_usrp *multi_usrp, const std::string &name, size_t chan = 0) +{ + return multi_usrp->get_rx_gain(name, chan); +} + +static double get_rx_gain_1( + uhd::usrp::multi_usrp *multi_usrp, size_t chan = 0) +{ + return multi_usrp->get_rx_gain(chan); +} + +BOOST_PYTHON_FUNCTION_OVERLOADS(overload_get_rx_gain_0, get_rx_gain_0, 2, 3); +BOOST_PYTHON_FUNCTION_OVERLOADS(overload_get_rx_gain_1, get_rx_gain_1, 1, 2); + +static double get_tx_gain_0( + uhd::usrp::multi_usrp *multi_usrp, const std::string &name, size_t chan = 0) +{ + return multi_usrp->get_tx_gain(name, chan); +} + +static double get_tx_gain_1( + uhd::usrp::multi_usrp *multi_usrp, size_t chan = 0) +{ + return multi_usrp->get_tx_gain(chan); +} + +BOOST_PYTHON_FUNCTION_OVERLOADS(overload_get_tx_gain_0, get_tx_gain_0, 2, 3); +BOOST_PYTHON_FUNCTION_OVERLOADS(overload_get_tx_gain_1, get_tx_gain_1, 1, 2); + +static uhd::gain_range_t get_rx_gain_range_0( + uhd::usrp::multi_usrp *multi_usrp, const std::string &name, size_t chan = 0) +{ + return multi_usrp->get_rx_gain_range(name, chan); +} + +static uhd::gain_range_t get_rx_gain_range_1( + uhd::usrp::multi_usrp *multi_usrp, size_t chan = 0) +{ + return multi_usrp->get_rx_gain_range(chan); +} + +BOOST_PYTHON_FUNCTION_OVERLOADS(overload_get_rx_gain_range_0, get_rx_gain_range_0, 2, 3); +BOOST_PYTHON_FUNCTION_OVERLOADS(overload_get_rx_gain_range_1, get_rx_gain_range_1, 1, 2); + +static uhd::gain_range_t get_tx_gain_range_0( + uhd::usrp::multi_usrp *multi_usrp, const std::string &name, size_t chan = 0) +{ + return multi_usrp->get_tx_gain_range(name, chan); +} + +static uhd::gain_range_t get_tx_gain_range_1( + uhd::usrp::multi_usrp *multi_usrp, size_t chan = 0) +{ + return multi_usrp->get_tx_gain_range(chan); +} + +BOOST_PYTHON_FUNCTION_OVERLOADS(overload_get_tx_gain_range_0, get_tx_gain_range_0, 2, 3); +BOOST_PYTHON_FUNCTION_OVERLOADS(overload_get_tx_gain_range_1, get_tx_gain_range_1, 1, 2); + +static void set_rx_dc_offset_0( + uhd::usrp::multi_usrp *multi_usrp, const bool enb, size_t chan = 0) +{ + multi_usrp->set_rx_dc_offset(enb, chan); +} + +static void set_rx_dc_offset_1( + uhd::usrp::multi_usrp *multi_usrp, const std::complex<double> &offset, size_t chan = 0) +{ + multi_usrp->set_rx_dc_offset(offset, chan); +} + +BOOST_PYTHON_FUNCTION_OVERLOADS(overload_set_rx_dc_offset_0, set_rx_dc_offset_0, 2, 3); +BOOST_PYTHON_FUNCTION_OVERLOADS(overload_set_rx_dc_offset_1, set_rx_dc_offset_1, 2, 3); + +static void set_tx_dc_offset_0( + uhd::usrp::multi_usrp *multi_usrp, const bool enb, size_t chan = 0) +{ + multi_usrp->set_tx_dc_offset(enb, chan); +} + +static void set_tx_dc_offset_1( + uhd::usrp::multi_usrp *multi_usrp, const std::complex<double> &offset, size_t chan = 0) +{ + multi_usrp->set_tx_dc_offset(offset, chan); +} + +BOOST_PYTHON_FUNCTION_OVERLOADS(overload_set_tx_dc_offset_0, set_tx_dc_offset_0, 2, 3); +BOOST_PYTHON_FUNCTION_OVERLOADS(overload_set_tx_dc_offset_1, set_tx_dc_offset_1, 2, 3); + +static void set_rx_iq_balance_0( + uhd::usrp::multi_usrp *multi_usrp, const bool enb, size_t chan = 0) +{ + multi_usrp->set_rx_iq_balance(enb, chan); +} + +static void set_rx_iq_balance_1( + uhd::usrp::multi_usrp *multi_usrp, const std::complex<double> &offset, size_t chan = 0) +{ + multi_usrp->set_rx_iq_balance(offset, chan); +} + +BOOST_PYTHON_FUNCTION_OVERLOADS(overload_set_rx_iq_balance_0, set_rx_iq_balance_0, 2, 3); +BOOST_PYTHON_FUNCTION_OVERLOADS(overload_set_rx_iq_balance_1, set_rx_iq_balance_1, 2, 3); + +static void set_gpio_attr_0(uhd::usrp::multi_usrp *multi_usrp, + const std::string &bank, + const std::string &attr, + const uint32_t value, + const uint32_t mask = 0xffffffff, + const size_t mboard = 0) +{ + multi_usrp->set_gpio_attr(bank, attr, value, mask, mboard); +} + +static void set_gpio_attr_1(uhd::usrp::multi_usrp *multi_usrp, + const std::string &bank, + const std::string &attr, + const std::string &value, + const uint32_t mask = 0xffffffff, + const size_t mboard = 0) +{ + multi_usrp->set_gpio_attr(bank, attr, value, mask, mboard); +} + +BOOST_PYTHON_FUNCTION_OVERLOADS(overload_set_gpio_attr_0, set_gpio_attr_0, 4, 6); +BOOST_PYTHON_FUNCTION_OVERLOADS(overload_set_gpio_attr_1, set_gpio_attr_1, 4, 6); + +// +// Boost.Python needs to know about default argument overloads +// + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_rx_freq, get_rx_freq, 0, 1); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_rx_rate, get_rx_rate, 0, 1); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_set_rx_freq, set_rx_freq, 1, 2); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_set_rx_rate, set_rx_rate, 1, 2); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_tx_freq, get_tx_freq, 0, 1); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_tx_rate, get_tx_rate, 0, 1); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_set_tx_freq, set_tx_freq, 1, 2); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_set_tx_rate, set_tx_rate, 1, 2); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_usrp_rx_info, get_usrp_rx_info, 0, 1); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_usrp_tx_info, get_usrp_rx_info, 0, 1); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_set_master_clock_rate, set_master_clock_rate, 1, 2); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_master_clock_rate, get_master_clock_rate, 0, 1); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_master_clock_rate_range, get_master_clock_rate_range, 0, 1); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_mboard_name, get_mboard_name, 0, 1); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_time_now, get_time_now, 0, 1); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_time_last_pps, get_time_last_pps, 0, 1); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_set_time_now, set_time_now, 1, 2); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_set_time_next_pps, set_time_next_pps, 1, 2); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_set_command_time, set_command_time, 1, 2); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_clear_command_time, clear_command_time, 0, 1); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_issue_stream_cmd, issue_stream_cmd, 1, 2); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_set_clock_config, set_clock_config, 1, 2); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_set_time_source, set_time_source, 1, 2); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_set_clock_source, set_clock_source, 1, 2); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_set_clock_source_out, set_clock_source_out, 1, 2); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_set_time_source_out, set_time_source_out, 1, 2); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_mboard_sensor, get_mboard_sensor, 1, 2); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_mboard_sensor_names, get_mboard_sensor_names, 0, 1); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_set_user_register, set_user_register, 2, 3); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_set_rx_subdev_spec, set_rx_subdev_spec, 1, 2); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_rx_subdev_spec, get_rx_subdev_spec, 0, 1); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_rx_subdev_name, get_rx_subdev_name, 0, 1); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_rx_rates, get_rx_rates, 0, 1); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_rx_freq_range, get_rx_freq_range, 0, 1); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_fe_rx_freq_range, get_fe_rx_freq_range, 0, 1); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_rx_lo_names, get_rx_lo_names, 0, 1); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_rx_lo_source, get_rx_lo_source, 0, 2); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_set_rx_lo_source, set_rx_lo_source, 1, 3); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_rx_lo_sources, get_rx_lo_sources, 0, 2); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_set_rx_lo_export_enabled, set_rx_lo_export_enabled, 1, 3); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_rx_lo_export_enabled, get_rx_lo_export_enabled, 0, 2); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_set_rx_lo_freq, set_rx_lo_freq, 2, 3); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_rx_lo_freq, get_rx_lo_freq, 1, 2); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_rx_lo_freq_range, get_rx_lo_freq_range, 1, 2); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_set_normalized_rx_gain, set_normalized_rx_gain, 1, 2); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_normalized_rx_gain, get_normalized_rx_gain, 0, 1); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_set_rx_agc, set_rx_agc, 1, 2); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_rx_gain_names, get_rx_gain_names, 0, 1); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_set_rx_antenna, set_rx_antenna, 1, 2); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_rx_antenna, get_rx_antenna, 0, 1); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_rx_antennas, get_rx_antennas, 0, 1); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_set_rx_bandwidth, set_rx_bandwidth, 1, 2); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_rx_bandwidth, get_rx_bandwidth, 0, 1); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_rx_bandwidth_range, get_rx_bandwidth_range, 0, 1); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_rx_dboard_iface, get_rx_dboard_iface, 0, 1); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_rx_sensor, get_rx_sensor, 1, 2); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_rx_sensor_names, get_rx_sensor_names, 0, 1); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_set_tx_subdev_spec, set_tx_subdev_spec, 1, 2); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_tx_subdev_spec, get_tx_subdev_spec, 0, 1); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_tx_subdev_name, get_tx_subdev_name, 0, 1); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_tx_rates, get_tx_rates, 0, 1); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_tx_freq_range, get_tx_freq_range, 0, 1); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_fe_tx_freq_range, get_fe_tx_freq_range, 0, 1); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_set_normalized_tx_gain, set_normalized_tx_gain, 1, 2); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_normalized_tx_gain, get_normalized_tx_gain, 0, 1); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_tx_gain_names, get_tx_gain_names, 0, 1); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_set_tx_antenna, set_tx_antenna, 1, 2); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_tx_antenna, get_tx_antenna, 0, 1); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_tx_antennas, get_tx_antennas, 0, 1); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_set_tx_bandwidth, set_tx_bandwidth, 1, 2); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_tx_bandwidth, get_tx_bandwidth, 0, 1); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_tx_bandwidth_range, get_tx_bandwidth_range, 0, 1); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_tx_dboard_iface, get_tx_dboard_iface, 0, 1); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_tx_sensor, get_tx_sensor, 1, 2); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_tx_sensor_names, get_tx_sensor_names, 0, 1); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_set_tx_iq_balance, set_tx_iq_balance, 1, 2); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_gpio_attr, get_gpio_attr, 2, 3); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_enumerate_registers, enumerate_registers, 0, 1); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_register_info, get_register_info, 1, 2); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_write_register, write_register, 3, 4); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_read_register, read_register, 2, 3); + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(overload_get_filter_names, get_filter_names, 0, 1); + +void export_multi_usrp() +{ + using multi_usrp = uhd::usrp::multi_usrp; + using register_info_t = multi_usrp::register_info_t; + + bp::class_<register_info_t>("register_info") + .add_property("bitwidth", ®ister_info_t::bitwidth) + .add_property("readable", ®ister_info_t::readable) + .add_property("writable", ®ister_info_t::writable) + ; + + bp::class_< + multi_usrp, + boost::shared_ptr<multi_usrp>, + boost::noncopyable>("multi_usrp", bp::no_init) + + .def("__init__", bp::make_constructor(&multi_usrp::make)) + + // Methods + .def("make", &multi_usrp::make) + .staticmethod("make") + + // General USRP methods + .def("get_rx_freq" , &multi_usrp::get_rx_freq, overload_get_rx_freq()) + .def("get_rx_num_channels" , &multi_usrp::get_rx_num_channels) + .def("get_rx_rate" , &multi_usrp::get_rx_rate, overload_get_rx_rate()) + .def("get_rx_stream" , &multi_usrp::get_rx_stream) + .def("set_rx_freq" , &multi_usrp::set_rx_freq, overload_set_rx_freq()) + .def("set_rx_gain" , &set_rx_gain_0, overload_set_rx_gain_0()) + .def("set_rx_gain" , &set_rx_gain_1, overload_set_rx_gain_1()) + .def("set_rx_rate" , &multi_usrp::set_rx_rate, overload_set_rx_rate()) + .def("get_tx_freq" , &multi_usrp::get_tx_freq, overload_get_tx_freq()) + .def("get_tx_num_channels" , &multi_usrp::get_tx_num_channels) + .def("get_tx_rate" , &multi_usrp::get_tx_rate, overload_get_tx_rate()) + .def("get_tx_stream" , &multi_usrp::get_tx_stream) + .def("set_tx_freq" , &multi_usrp::set_tx_freq, overload_set_tx_freq()) + .def("set_tx_gain" , &set_tx_gain_0, overload_set_tx_gain_0()) + .def("set_tx_gain" , &set_tx_gain_1, overload_set_tx_gain_1()) + .def("set_tx_rate" , &multi_usrp::set_tx_rate, overload_set_tx_rate()) + .def("get_usrp_rx_info" , &multi_usrp::get_usrp_rx_info, overload_get_usrp_rx_info()) + .def("get_usrp_tx_info" , &multi_usrp::get_usrp_tx_info, overload_get_usrp_tx_info()) + .def("set_master_clock_rate" , &multi_usrp::set_master_clock_rate, overload_set_master_clock_rate()) + .def("get_master_clock_rate" , &multi_usrp::get_master_clock_rate, overload_get_master_clock_rate()) + .def("get_master_clock_rate_range", &multi_usrp::get_master_clock_rate_range, overload_get_master_clock_rate_range()) + .def("get_pp_string" , &multi_usrp::get_pp_string) + .def("get_mboard_name" , &multi_usrp::get_mboard_name, overload_get_mboard_name()) + .def("get_time_now" , &multi_usrp::get_time_now, overload_get_time_now()) + .def("get_time_last_pps" , &multi_usrp::get_time_last_pps, overload_get_time_last_pps()) + .def("set_time_now" , &multi_usrp::set_time_now, overload_set_time_now()) + .def("set_time_next_pps" , &multi_usrp::set_time_next_pps, overload_set_time_next_pps()) + .def("set_time_unknown_pps" , &multi_usrp::set_time_unknown_pps) + .def("get_time_synchronized" , &multi_usrp::get_time_synchronized) + .def("set_command_time" , &multi_usrp::set_command_time, overload_set_command_time()) + .def("clear_command_time" , &multi_usrp::clear_command_time, overload_clear_command_time()) + .def("issue_stream_cmd" , &multi_usrp::issue_stream_cmd, overload_issue_stream_cmd()) + .def("set_clock_config" , &multi_usrp::set_clock_config, overload_set_clock_config()) + .def("set_time_source" , &multi_usrp::set_time_source, overload_set_time_source()) + .def("get_time_source" , &multi_usrp::get_time_source) + .def("get_time_sources" , &multi_usrp::get_time_sources) + .def("set_clock_source" , &multi_usrp::set_clock_source, overload_set_clock_source()) + .def("get_clock_source" , &multi_usrp::get_clock_source) + .def("get_clock_sources" , &multi_usrp::get_clock_sources) + .def("set_clock_source_out" , &multi_usrp::set_clock_source_out, overload_set_clock_source_out()) + .def("set_time_source_out" , &multi_usrp::set_time_source_out, overload_set_time_source_out()) + .def("get_num_mboards" , &multi_usrp::get_num_mboards) + .def("get_mboard_sensor" , &multi_usrp::get_mboard_sensor, overload_get_mboard_sensor()) + .def("get_mboard_sensor_names" , &multi_usrp::get_mboard_sensor_names, overload_get_mboard_sensor_names()) + .def("set_user_register" , &multi_usrp::set_user_register, overload_set_user_register()) + + // RX methods + .def("set_rx_subdev_spec" , &multi_usrp::set_rx_subdev_spec, overload_set_rx_subdev_spec()) + .def("get_rx_subdev_spec" , &multi_usrp::get_rx_subdev_spec, overload_get_rx_subdev_spec()) + .def("get_rx_subdev_name" , &multi_usrp::get_rx_subdev_name, overload_get_rx_subdev_name()) + .def("get_rx_rates" , &multi_usrp::get_rx_rates, overload_get_rx_rates()) + .def("get_rx_freq_range" , &multi_usrp::get_rx_freq_range, overload_get_rx_freq_range()) + .def("get_fe_rx_freq_range" , &multi_usrp::get_fe_rx_freq_range, overload_get_fe_rx_freq_range()) + .def("get_rx_lo_names" , &multi_usrp::get_rx_lo_names, overload_get_rx_lo_names()) + .def("set_rx_lo_source" , &multi_usrp::set_rx_lo_source, overload_set_rx_lo_source()) + .def("get_rx_lo_source" , &multi_usrp::get_rx_lo_source, overload_get_rx_lo_source()) + .def("get_rx_lo_sources" , &multi_usrp::get_rx_lo_sources, overload_get_rx_lo_sources()) + .def("set_rx_lo_export_enabled", &multi_usrp::set_rx_lo_export_enabled, overload_set_rx_lo_export_enabled()) + .def("get_rx_lo_export_enabled", &multi_usrp::get_rx_lo_export_enabled, overload_get_rx_lo_export_enabled()) + .def("set_rx_lo_freq" , &multi_usrp::set_rx_lo_freq, overload_set_rx_lo_freq()) + .def("get_rx_lo_freq" , &multi_usrp::get_rx_lo_freq, overload_get_rx_lo_freq()) + .def("get_rx_lo_freq_range" , &multi_usrp::get_rx_lo_freq_range, overload_get_rx_lo_freq_range()) + .def("set_normalized_rx_gain" , &multi_usrp::set_normalized_rx_gain, overload_set_normalized_rx_gain()) + .def("get_normalized_rx_gain" , &multi_usrp::get_normalized_rx_gain, overload_get_normalized_rx_gain()) + .def("set_rx_agc" , &multi_usrp::set_rx_agc, overload_set_rx_agc()) + .def("get_rx_gain" , &get_rx_gain_0, overload_get_rx_gain_0()) + .def("get_rx_gain" , &get_rx_gain_1, overload_get_rx_gain_1()) + .def("get_rx_gain_range" , &get_rx_gain_range_0, overload_get_rx_gain_range_0()) + .def("get_rx_gain_range" , &get_rx_gain_range_1, overload_get_rx_gain_range_1()) + .def("get_rx_gain_names" , &multi_usrp::get_rx_gain_names, overload_get_rx_gain_names()) + .def("set_rx_antenna" , &multi_usrp::set_rx_antenna, overload_set_rx_antenna()) + .def("get_rx_antenna" , &multi_usrp::get_rx_antenna, overload_get_rx_antenna()) + .def("get_rx_antennas" , &multi_usrp::get_rx_antennas, overload_get_rx_antennas()) + .def("set_rx_bandwidth" , &multi_usrp::set_rx_bandwidth, overload_set_rx_bandwidth()) + .def("get_rx_bandwidth" , &multi_usrp::get_rx_bandwidth, overload_get_rx_bandwidth()) + .def("get_rx_bandwidth_range" , &multi_usrp::get_rx_bandwidth_range, overload_get_rx_bandwidth_range()) + .def("get_rx_dboard_iface" , &multi_usrp::get_rx_dboard_iface, overload_get_rx_dboard_iface()) + .def("get_rx_sensor" , &multi_usrp::get_rx_sensor, overload_get_rx_sensor()) + .def("get_rx_sensor_names" , &multi_usrp::get_rx_sensor_names, overload_get_rx_sensor_names()) + .def("set_rx_dc_offset" , &set_rx_dc_offset_0, overload_set_rx_dc_offset_0()) + .def("set_rx_dc_offset" , &set_rx_dc_offset_1, overload_set_rx_dc_offset_1()) + .def("set_rx_iq_balance" , &set_rx_iq_balance_0, overload_set_rx_iq_balance_0()) + .def("set_rx_iq_balance" , &set_rx_iq_balance_1, overload_set_rx_iq_balance_1()) + + // TX methods + .def("set_tx_subdev_spec" , &multi_usrp::set_tx_subdev_spec, overload_set_tx_subdev_spec()) + .def("get_tx_subdev_spec" , &multi_usrp::get_tx_subdev_spec, overload_get_tx_subdev_spec()) + .def("get_tx_subdev_name" , &multi_usrp::get_tx_subdev_name, overload_get_tx_subdev_name()) + .def("get_tx_rates" , &multi_usrp::get_tx_rates, overload_get_rx_rates()) + .def("get_tx_freq_range" , &multi_usrp::get_tx_freq_range, overload_get_tx_freq_range()) + .def("get_fe_tx_freq_range" , &multi_usrp::get_fe_tx_freq_range, overload_get_fe_tx_freq_range()) + .def("set_normalized_tx_gain" , &multi_usrp::set_normalized_tx_gain, overload_set_normalized_tx_gain()) + .def("get_normalized_tx_gain" , &multi_usrp::get_normalized_tx_gain, overload_get_normalized_tx_gain()) + .def("get_tx_gain" , &get_tx_gain_0, overload_get_tx_gain_0()) + .def("get_tx_gain" , &get_tx_gain_1, overload_get_tx_gain_1()) + .def("get_tx_gain_range" , &get_tx_gain_range_0, overload_get_tx_gain_range_0()) + .def("get_tx_gain_range" , &get_tx_gain_range_1, overload_get_tx_gain_range_1()) + .def("get_tx_gain_names" , &multi_usrp::get_tx_gain_names, overload_get_tx_gain_names()) + .def("set_tx_antenna" , &multi_usrp::set_tx_antenna, overload_set_tx_antenna()) + .def("get_tx_antenna" , &multi_usrp::get_tx_antenna, overload_get_tx_antenna()) + .def("get_tx_antennas" , &multi_usrp::get_tx_antennas, overload_get_tx_antennas()) + .def("set_tx_bandwidth" , &multi_usrp::set_tx_bandwidth, overload_set_tx_bandwidth()) + .def("get_tx_bandwidth" , &multi_usrp::get_tx_bandwidth, overload_get_tx_bandwidth()) + .def("get_tx_bandwidth_range" , &multi_usrp::get_tx_bandwidth_range, overload_get_tx_bandwidth_range()) + .def("get_tx_dboard_iface" , &multi_usrp::get_tx_dboard_iface, overload_get_tx_dboard_iface()) + .def("get_tx_sensor" , &multi_usrp::get_tx_sensor, overload_get_tx_sensor()) + .def("get_tx_sensor_names" , &multi_usrp::get_tx_sensor_names, overload_get_tx_sensor_names()) + .def("set_tx_dc_offset" , &set_tx_dc_offset_0, overload_set_tx_dc_offset_0()) + .def("set_tx_dc_offset" , &set_tx_dc_offset_1, overload_set_tx_dc_offset_1()) + .def("set_tx_iq_balance" , &multi_usrp::set_tx_iq_balance, overload_set_tx_iq_balance()) + + // GPIO methods + .def("get_gpio_banks" , &multi_usrp::get_gpio_banks) + .def("set_gpio_attr" , &set_gpio_attr_0, overload_set_gpio_attr_0()) + .def("set_gpio_attr" , &set_gpio_attr_1, overload_set_gpio_attr_1()) + .def("get_gpio_attr" , &multi_usrp::get_gpio_attr, overload_get_gpio_attr()) + .def("enumerate_registers" , &multi_usrp::enumerate_registers, overload_enumerate_registers()) + .def("get_register_info" , &multi_usrp::get_register_info, overload_get_register_info()) + .def("write_register" , &multi_usrp::write_register, overload_write_register()) + .def("read_register" , &multi_usrp::read_register, overload_read_register()) + + // Filter API methods + .def("get_filter_names" , &multi_usrp::get_filter_names, overload_get_filter_names()) + .def("get_filter" , &multi_usrp::get_filter) + .def("set_filter" , &multi_usrp::set_filter) + ; +} + +#endif /* INCLUDED_UHD_USRP_MULTI_USRP_PYTHON_HPP */ diff --git a/host/lib/usrp/subdev_spec_python.hpp b/host/lib/usrp/subdev_spec_python.hpp new file mode 100644 index 000000000..ed91099f9 --- /dev/null +++ b/host/lib/usrp/subdev_spec_python.hpp @@ -0,0 +1,37 @@ +// +// Copyright 2017-2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_UHD_USRP_SUBDEV_SPEC_PYTHON_HPP +#define INCLUDED_UHD_USRP_SUBDEV_SPEC_PYTHON_HPP + +#include <uhd/usrp/subdev_spec.hpp> + +void export_subdev_spec() +{ + using subdev_spec_pair_t = uhd::usrp::subdev_spec_pair_t; + using subdev_spec_t = uhd::usrp::subdev_spec_t; + + bp::class_<subdev_spec_pair_t> + ("subdev_spec_pair", bp::init<const std::string&, const std::string &>()) + + // Properties + .add_property("db_name", &subdev_spec_pair_t::db_name) + .add_property("sd_name", &subdev_spec_pair_t::sd_name) + ; + + bp::class_<std::vector<subdev_spec_pair_t> >("subdev_spec_vector") + .def(bp::vector_indexing_suite<std::vector<subdev_spec_pair_t> >()); + + bp::class_<subdev_spec_t, bp::bases<std::vector<subdev_spec_pair_t> > > + ("subdev_spec", bp::init<const std::string &>()) + + // Methods + .def("__str__", &subdev_spec_t::to_pp_string) + .def("to_string", &subdev_spec_t::to_string) + ; +} + +#endif /* INCLUDED_UHD_USRP_SUBDEV_SPEC_PYTHON_HPP */ diff --git a/host/lib/utils/gil_release_python.hpp b/host/lib/utils/gil_release_python.hpp new file mode 100644 index 000000000..287ba240e --- /dev/null +++ b/host/lib/utils/gil_release_python.hpp @@ -0,0 +1,31 @@ +// +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +//! RAII-style GIL release method +// +// To release the GIL using this method, simply instantiate this class in the +// scope that needs to release the GIL. +// +// Note that using this class assumes that threads have already been +// initialized. See also https://docs.python.org/3.5/c-api/init.html for more +// documentation on Python initialization and threads. +class scoped_gil_release +{ +public: + inline scoped_gil_release() + { + _thread_state = PyEval_SaveThread(); + } + + inline ~scoped_gil_release() + { + PyEval_RestoreThread(_thread_state); + _thread_state = nullptr; + } + +private: + PyThreadState* _thread_state; +}; diff --git a/host/python/CMakeLists.txt b/host/python/CMakeLists.txt index f4effa1c4..a650529fa 100644 --- a/host/python/CMakeLists.txt +++ b/host/python/CMakeLists.txt @@ -1,30 +1,19 @@ # -# Copyright 2017 Ettus Research LLC +# Copyright 2017-2018 Ettus Research, a National Instruments Company # -# This program 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 of the License, or -# (at your option) any later version. -# -# This program 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, see <http://www.gnu.org/licenses/>. +# SPDX-License-Identifier: GPL-3.0-or-later # ######################################################################## # This file included, use CMake directory variables ######################################################################## - PYTHON_CHECK_MODULE( - "virtualenv" - "sys" "hasattr(sys, 'real_prefix')" - HAVE_PYTHON_VIRTUALENV - ) + "virtualenv" + "sys" "hasattr(sys, 'real_prefix')" + HAVE_PYTHON_VIRTUALENV +) + # Get include dirs INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_DIRS}) EXECUTE_PROCESS( @@ -32,7 +21,7 @@ EXECUTE_PROCESS( "from __future__ import print_function\ntry:\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) -# Build libpyuhd.so +# Build pyuhd library ADD_LIBRARY(pyuhd SHARED pyuhd.cpp) TARGET_INCLUDE_DIRECTORIES(pyuhd PUBLIC ${PYTHON_NUMPY_INCLUDE_DIR} @@ -42,7 +31,7 @@ TARGET_LINK_LIBRARIES(pyuhd ${BOOST_PYTHON_LIBRARY} ${Boost_LIBRARIES} ${PYTHON_ # Copy pyuhd library to the staging directory SET(PYUHD_LIBRARY_NAME ${CMAKE_SHARED_LIBRARY_PREFIX}pyuhd${CMAKE_SHARED_LIBRARY_SUFFIX}) ADD_CUSTOM_COMMAND(TARGET pyuhd - POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/libpyuhd.so ${CMAKE_CURRENT_BINARY_DIR}/uhd/libpyuhd.so) + POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:pyuhd> ${CMAKE_CURRENT_BINARY_DIR}/uhd/${PYUHD_LIBRARY_NAME}) SET(PYUHD_FILES ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py @@ -51,33 +40,27 @@ SET(PYUHD_FILES ${CMAKE_CURRENT_SOURCE_DIR}/filters.py ) -SET(SETUP_PY_IN "${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in") -SET(SETUP_PY "${CMAKE_CURRENT_BINARY_DIR}/setup.py") -SET(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/build/timestamp") +SET(SETUP_PY_IN "${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in") +SET(SETUP_PY "${CMAKE_CURRENT_BINARY_DIR}/setup.py") +SET(TIMESTAMP_FILE "${CMAKE_CURRENT_BINARY_DIR}/build/timestamp") - SET(PYUHD_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py - ${CMAKE_CURRENT_SOURCE_DIR}/pyuhd.py - ) - SET(SETUP_PY_IN "${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in") - SET(SETUP_PY "${CMAKE_CURRENT_BINARY_DIR}/setup.py") - SET(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/build/timestamp") +CONFIGURE_FILE(${SETUP_PY_IN} ${SETUP_PY}) - CONFIGURE_FILE(${SETUP_PY_IN} ${SETUP_PY}) +ADD_CUSTOM_COMMAND(OUTPUT ${TIMESTAMP_FILE} + COMMAND ${CMAKE_COMMAND} -E copy ${PYUHD_FILES} ${CMAKE_CURRENT_BINARY_DIR}/uhd + COMMAND ${PYTHON} ${SETUP_PY} -q build + COMMAND ${CMAKE_COMMAND} -E touch ${TIMESTAMP_FILE} + DEPENDS ${PYUHD_FILES}) - ADD_CUSTOM_COMMAND(OUTPUT ${OUTPUT} - COMMAND ${CMAKE_COMMAND} -E copy ${PYUHD_FILES} ${CMAKE_CURRENT_BINARY_DIR}/uhd - COMMAND ${PYTHON} ${SETUP_PY} -q build - COMMAND ${CMAKE_COMMAND} -E touch ${OUTPUT} - DEPENDS ${PYUHD_FILES}) - ADD_CUSTOM_TARGET(target ALL DEPENDS ${OUTPUT} pyuhd) - IF(HAVE_PYTHON_VIRTUALENV) - INSTALL(CODE "execute_process(COMMAND ${PYTHON} ${SETUP_PY} -q install --force)") - ELSE() - EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} -c - "from distutils import sysconfig; print sysconfig.get_python_lib(plat_specific=True, prefix='')" - OUTPUT_VARIABLE UHD_PYTHON_DIR OUTPUT_STRIP_TRAILING_WHITESPACE - ) - INSTALL(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/build/lib/uhd DESTINATION ${CMAKE_INSTALL_PREFIX}/${UHD_PYTHON_DIR}) - ENDIF() +ADD_CUSTOM_TARGET(pyuhd_library ALL DEPENDS ${TIMESTAMP_FILE} pyuhd) +IF(HAVE_PYTHON_VIRTUALENV) + INSTALL(CODE "execute_process(COMMAND ${PYTHON} ${SETUP_PY} -q install --force)") +ELSE() + EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} -c + "from distutils import sysconfig; print(sysconfig.get_python_lib(plat_specific=True, prefix=''))" + OUTPUT_VARIABLE UHD_PYTHON_DIR OUTPUT_STRIP_TRAILING_WHITESPACE + ) + MESSAGE(STATUS "Utilizing the python install directory: ${CMAKE_INSTALL_PREFIX}/${UHD_PYTHON_DIR}") + INSTALL(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/uhd DESTINATION ${CMAKE_INSTALL_PREFIX}/${UHD_PYTHON_DIR}) +ENDIF() diff --git a/host/python/__init__.py b/host/python/__init__.py index 39b6b170d..501b599ef 100644 --- a/host/python/__init__.py +++ b/host/python/__init__.py @@ -1,18 +1,12 @@ # -# Copyright 2017 Ettus Research LLC +# Copyright 2017-2018 Ettus Research, a National Instruments Company # -# This program 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 of the License, or -# (at your option) any later version. -# -# This program 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, see <http://www.gnu.org/licenses/>. +# SPDX-License-Identifier: GPL-3.0-or-later # +""" +UHD Python API module +""" -from pyuhd import * +from . import types +from . import usrp +from . import filters diff --git a/host/python/filters.py b/host/python/filters.py new file mode 100644 index 000000000..8479b56e1 --- /dev/null +++ b/host/python/filters.py @@ -0,0 +1,16 @@ +# +# Copyright 2017-2018 Ettus Research, a National Instruments Company +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" @package filters +Python UHD module containing the filter API +""" + + +from . import libpyuhd as lib + +FilterType = lib.filters.filter_type +FilterInfoBase = lib.filters.filter_info_base +AnalogFilterBase = lib.filters.analog_filter_base +AnalogFilterLP = lib.filters.analog_filter_lp diff --git a/host/python/pyuhd.cpp b/host/python/pyuhd.cpp index 5bb6271f3..fe8d6a790 100644 --- a/host/python/pyuhd.cpp +++ b/host/python/pyuhd.cpp @@ -1,181 +1,82 @@ // -// Copyright 2017 Ettus Research LLC +// Copyright 2017-2018 Ettus Research, a National Instruments Company // -// This program 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 of the License, or -// (at your option) any later version. -// -// This program 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, see <http://www.gnu.org/licenses/>. +// SPDX-License-Identifier: GPL-3.0-or-later // -#include <uhd/types/dict.hpp> -#include <uhd/types/ranges.hpp> -#include <uhd/types/stream_cmd.hpp> -#include <uhd/types/tune_result.hpp> -#include <uhd/types/tune_request.hpp> -#include <uhd/usrp/multi_usrp.hpp> -#include <uhd/stream.hpp> #include <boost/shared_ptr.hpp> #include <boost/python.hpp> #include <boost/python/stl_iterator.hpp> +#include <boost/python/suite/indexing/vector_indexing_suite.hpp> #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #include <numpy/arrayobject.h> - namespace bp = boost::python; +#include "stream_python.hpp" -size_t Pyrecv(uhd::rx_streamer* rx_stream, bp::object& np_array, uhd::rx_metadata_t& rx_metadata){ - // Get a numpy array object from given python object !!no sanity checking possible!! - PyObject* array_obj = PyArray_FROM_OF(np_array.ptr(),NPY_ARRAY_CARRAY); - PyArrayObject* array_type_obj = reinterpret_cast<PyArrayObject*>(array_obj); - - // Get dimensions of the numpy array - const size_t dims = PyArray_NDIM(array_type_obj); - const npy_intp* shape = PyArray_SHAPE(array_type_obj); - // How many bytes to jump to get to the next element of this stride (next row) - const npy_intp* strides = PyArray_STRIDES(array_type_obj); - const size_t channels = rx_stream->get_num_channels(); - - - // Check if numpy array sizes are ok - if ((channels > 1) && (dims != 2)){ - return 0; - }else if ((size_t)shape[0] < channels){ - return 0; - } - - // Get a pointer to the storage - std::vector<void*> channel_storage; - char* data = PyArray_BYTES(array_type_obj); - for (size_t i = 0; i<channels; ++i){ - channel_storage.push_back((void*)(data+i*strides[0])); - } - - // Get data buffer and size of the array - size_t nsamps_per_buff; - if (dims > 1){ - nsamps_per_buff = (size_t)shape[1]; - }else{ - nsamps_per_buff = PyArray_SIZE(array_type_obj); - } - - // Call the real recv() - const size_t result = rx_stream->recv( - channel_storage, - nsamps_per_buff, - rx_metadata); - return result; -}; - -size_t Pysend(uhd::tx_streamer* tx_stream, bp::object& np_array, uhd::tx_metadata_t& tx_metadata){ - // Get a numpy array object from given python object !!no sanity checking possible!! - PyObject* array_obj = PyArray_FROM_OF(np_array.ptr(),NPY_ARRAY_CARRAY); - PyArrayObject* array_type_obj = reinterpret_cast<PyArrayObject*>(array_obj); - - // Get dimensions of the numpy array - const size_t dims = PyArray_NDIM(array_type_obj); - const npy_intp* shape = PyArray_SHAPE(array_type_obj); - // How many bytes to jump to get to the next element of the stride (next row) - const npy_intp* strides = PyArray_STRIDES(array_type_obj); - const size_t channels = tx_stream->get_num_channels(); - - // Check if numpy array sizes are ok - if ((channels > 1) && (dims != 2)){ - return 0; - }else if ((size_t)shape[0] < channels){ - return 0; - } +#include "types/types_python.hpp" +#include "types/serial_python.hpp" +#include "types/time_spec_python.hpp" +#include "types/metadata_python.hpp" +#include "types/sensors_python.hpp" +#include "types/filters_python.hpp" +#include "types/tune_python.hpp" - // Get a pointer to the storage - std::vector<void*> channel_storage; - char* data = PyArray_BYTES(array_type_obj); - for (size_t i = 0; i<channels; ++i){ - channel_storage.push_back((void*)(data+i*strides[0])); - } - - // Get data buffer and size of the array - size_t nsamps_per_buff; - if (dims > 1){ - nsamps_per_buff = (size_t)shape[1]; - }else{ - nsamps_per_buff = PyArray_SIZE(array_type_obj); - } - - // Call the real recv() - const size_t result = tx_stream->send( - channel_storage, - nsamps_per_buff, - tx_metadata); - return result; -}; - -// Manual wrapping because of non-standard overloading -void set_rx_gain_conv(uhd::usrp::multi_usrp* multi_usrp, double gain, size_t chan){ - multi_usrp->set_rx_gain(gain, chan); -} - -void set_tx_gain_conv(uhd::usrp::multi_usrp* multi_usrp, double gain, size_t chan){ - multi_usrp->set_tx_gain(gain, chan); -} +#include "usrp/fe_connection_python.hpp" +#include "usrp/dboard_iface_python.hpp" +#include "usrp/subdev_spec_python.hpp" +#include "usrp/multi_usrp_python.hpp" // Converter for std::vector / std::list arguments from python iterables struct iterable_converter { - template <typename Container> - iterable_converter& - from_python() - { - bp::converter::registry::push_back( - &iterable_converter::convertible, - &iterable_converter::construct<Container>, - bp::type_id<Container>()); - return *this; - } - - static void* convertible(PyObject* object) - { - return PyObject_GetIter(object) ? object : NULL; - } - - template <typename Container> - static void construct( - PyObject* object, - bp::converter::rvalue_from_python_stage1_data* data) - { - // Object is a borrowed reference, so create a handle indicting it is - // borrowed for proper reference counting. - bp::handle<> handle(bp::borrowed(object)); - - // Obtain a handle to the memory block that the converter has allocated - // for the C++ type. - typedef bp::converter::rvalue_from_python_storage<Container> - storage_type; - void* storage = reinterpret_cast<storage_type*>(data)->storage.bytes; + template <typename Container> + iterable_converter& from_python() + { + bp::converter::registry::push_back( + &iterable_converter::convertible, + &iterable_converter::construct<Container>, + bp::type_id<Container>() + ); + return *this; + } - typedef bp::stl_input_iterator<typename Container::value_type> - iterator; + static void* convertible(PyObject* object) + { + return PyObject_GetIter(object) ? object : NULL; + } - // Allocate the C++ type into the converter's memory block, and assign - // its handle to the converter's convertible variable. The C++ - // container is populated by passing the begin and end iterators of - // the python object to the container's constructor. - new (storage) Container( - iterator(bp::object(handle)), // begin - iterator()); // end - data->convertible = storage; - } + template <typename Container> + static void construct( + PyObject* object, + bp::converter::rvalue_from_python_stage1_data* data) + { + // Object is a borrowed reference, so create a handle indicting it is + // borrowed for proper reference counting. + bp::handle<> handle(bp::borrowed(object)); + + // Obtain a handle to the memory block that the converter has + // allocated for the C++ type. + typedef bp::converter::rvalue_from_python_storage<Container> storage_type; + + void* storage = reinterpret_cast<storage_type*>(data)->storage.bytes; + typedef bp::stl_input_iterator<typename Container::value_type> iterator; + + // Allocate the C++ type into the converter's memory block, and assign + // its handle to the converter's convertible variable. The C++ + // container is populated by passing the begin and end iterators of + // the python object to the container's constructor. + new (storage) Container( + iterator(bp::object(handle)), // begin + iterator() // end + ); + + data->convertible = storage; + } }; - template<typename Dtype1, typename Dtype2> struct uhd_to_python_dict { @@ -203,280 +104,10 @@ struct iterable_to_python_list } }; - -void export_multi_usrp(void) -{ - //Register submodule multi_usrp - bp::object multi_usrp_module(bp::handle<>(bp::borrowed(PyImport_AddModule("libpyuhd.multi_usrp")))); - bp::scope().attr("multi_usrp") = multi_usrp_module; - bp::scope io_scope = multi_usrp_module; - - bp::class_<uhd::usrp::multi_usrp, boost::shared_ptr<uhd::usrp::multi_usrp>, boost::noncopyable>("multi_usrp", bp::no_init) - .def("make", &uhd::usrp::multi_usrp::make) - .staticmethod("make") - .def("get_rx_freq", &uhd::usrp::multi_usrp::get_rx_freq) - .def("get_rx_num_channels", &uhd::usrp::multi_usrp::get_rx_num_channels) - .def("get_rx_rate", &uhd::usrp::multi_usrp::get_rx_rate) - .def("get_rx_stream", &uhd::usrp::multi_usrp::get_rx_stream) - .def("set_rx_freq", &uhd::usrp::multi_usrp::set_rx_freq) - .def("set_rx_gain", &set_rx_gain_conv) - .def("set_rx_rate", &uhd::usrp::multi_usrp::set_rx_rate) - .def("get_tx_freq", &uhd::usrp::multi_usrp::get_tx_freq) - .def("get_tx_num_channels", &uhd::usrp::multi_usrp::get_tx_num_channels) - .def("get_tx_rate", &uhd::usrp::multi_usrp::get_tx_rate) - .def("get_tx_stream", &uhd::usrp::multi_usrp::get_tx_stream) - .def("set_tx_freq", &uhd::usrp::multi_usrp::set_tx_freq) - .def("set_tx_gain", &set_tx_gain_conv) - .def("set_tx_rate", &uhd::usrp::multi_usrp::set_tx_rate) - .def("get_usrp_rx_info", &uhd::usrp::multi_usrp::get_usrp_rx_info) - .def("get_usrp_tx_info", &uhd::usrp::multi_usrp::get_usrp_tx_info) - .def("set_master_clock_rate", &uhd::usrp::multi_usrp::set_master_clock_rate) - .def("get_master_clock_rate", &uhd::usrp::multi_usrp::get_master_clock_rate) - .def("get_pp_string", &uhd::usrp::multi_usrp::get_pp_string) - .def("get_mboard_name", &uhd::usrp::multi_usrp::get_mboard_name) - .def("get_time_now", &uhd::usrp::multi_usrp::get_time_now) - .def("get_time_last_pps", &uhd::usrp::multi_usrp::get_time_last_pps) - .def("set_time_now", &uhd::usrp::multi_usrp::set_time_now) - .def("set_time_next_pps", &uhd::usrp::multi_usrp::set_time_next_pps) - .def("set_time_unknown_pps", &uhd::usrp::multi_usrp::set_time_unknown_pps) - .def("get_time_synchronized", &uhd::usrp::multi_usrp::get_time_synchronized) - .def("set_command_time", &uhd::usrp::multi_usrp::set_command_time) - .def("clear_command_time", &uhd::usrp::multi_usrp::clear_command_time) - .def("issue_stream_cmd", &uhd::usrp::multi_usrp::issue_stream_cmd) - .def("set_clock_config", &uhd::usrp::multi_usrp::set_clock_config) - .def("set_time_source", &uhd::usrp::multi_usrp::set_time_source) - .def("get_time_source", &uhd::usrp::multi_usrp::get_time_source) - .def("get_time_sources", &uhd::usrp::multi_usrp::get_time_sources) - .def("set_clock_source", &uhd::usrp::multi_usrp::set_clock_source) - .def("get_clock_source", &uhd::usrp::multi_usrp::get_clock_source) - .def("get_clock_sources", &uhd::usrp::multi_usrp::get_clock_sources) - .def("set_clock_source_out", &uhd::usrp::multi_usrp::set_clock_source_out) - .def("set_time_source_out", &uhd::usrp::multi_usrp::set_time_source_out) - .def("get_num_mboards", &uhd::usrp::multi_usrp::get_num_mboards) - .def("get_mboard_sensor", &uhd::usrp::multi_usrp::get_mboard_sensor) // TODO sensor_value_t - .def("get_mboard_sensor_names", &uhd::usrp::multi_usrp::get_mboard_sensor_names) - .def("set_user_register", &uhd::usrp::multi_usrp::set_user_register) - - // RX methods - .def("set_rx_subdev_spec", &uhd::usrp::multi_usrp::set_rx_subdev_spec) // TODO subdev_spec_t - .def("get_rx_subdev_spec", &uhd::usrp::multi_usrp::get_rx_subdev_spec) // TODO subdev_spec_t - .def("get_rx_subdev_name", &uhd::usrp::multi_usrp::get_rx_subdev_name) - .def("get_rx_rates", &uhd::usrp::multi_usrp::get_rx_rates) - .def("get_rx_freq_range", &uhd::usrp::multi_usrp::get_rx_freq_range) - .def("get_fe_rx_freq_range", &uhd::usrp::multi_usrp::get_fe_rx_freq_range) - .def("get_rx_lo_names", &uhd::usrp::multi_usrp::get_rx_lo_names) - .def("set_rx_lo_source", &uhd::usrp::multi_usrp::set_rx_lo_source) - .def("get_rx_lo_source", &uhd::usrp::multi_usrp::get_rx_lo_source) - .def("get_rx_lo_sources", &uhd::usrp::multi_usrp::get_rx_lo_sources) - .def("set_rx_lo_export_enabled", &uhd::usrp::multi_usrp::set_rx_lo_export_enabled) - .def("get_rx_lo_export_enabled", &uhd::usrp::multi_usrp::get_rx_lo_export_enabled) - .def("set_rx_lo_freq", &uhd::usrp::multi_usrp::set_rx_lo_freq) - .def("get_rx_lo_freq", &uhd::usrp::multi_usrp::get_rx_lo_freq) - .def("get_rx_lo_freq_range", &uhd::usrp::multi_usrp::get_rx_lo_freq_range) - .def("set_normalized_rx_gain", &uhd::usrp::multi_usrp::set_normalized_rx_gain) - .def("get_normalized_rx_gain", &uhd::usrp::multi_usrp::get_normalized_rx_gain) - .def("set_rx_agc", &uhd::usrp::multi_usrp::set_rx_agc) - //get_rx_gain (special wrapper) - //.def("get_rx_gain_range", &uhd::usrp::multi_usrp::get_rx_gain_range) // (special wrapper) - .def("get_rx_gain_names", &uhd::usrp::multi_usrp::get_rx_gain_names) - .def("set_rx_antenna", &uhd::usrp::multi_usrp::set_rx_antenna) - .def("get_rx_antenna", &uhd::usrp::multi_usrp::get_rx_antenna) - .def("get_rx_antennas", &uhd::usrp::multi_usrp::get_rx_antennas) - .def("set_rx_bandwidth", &uhd::usrp::multi_usrp::set_rx_bandwidth) - .def("get_rx_bandwidth", &uhd::usrp::multi_usrp::get_rx_bandwidth) - .def("get_rx_bandwidth_range", &uhd::usrp::multi_usrp::get_rx_bandwidth_range) - .def("get_rx_dboard_iface", &uhd::usrp::multi_usrp::get_rx_dboard_iface) // TODO dboard_iface::sptr - .def("get_rx_sensor", &uhd::usrp::multi_usrp::get_rx_sensor) // TODO sensor_value_t - .def("get_rx_sensor_names", &uhd::usrp::multi_usrp::get_rx_sensor_names) - //set_rx_dc_offset (special wrapper) - //set_rx_iq_balance (special wrapper) - - // TX methods - .def("set_tx_subdev_spec", &uhd::usrp::multi_usrp::set_tx_subdev_spec) // TODO subdev_spec_t - .def("get_tx_subdev_spec", &uhd::usrp::multi_usrp::get_tx_subdev_spec) // TODO subdev_spec_t - .def("get_tx_subdev_name", &uhd::usrp::multi_usrp::get_tx_subdev_name) - .def("get_tx_rates", &uhd::usrp::multi_usrp::get_tx_rates) - .def("get_tx_freq_range", &uhd::usrp::multi_usrp::get_tx_freq_range) - .def("get_fe_tx_freq_range", &uhd::usrp::multi_usrp::get_fe_tx_freq_range) - .def("set_normalized_tx_gain", &uhd::usrp::multi_usrp::set_normalized_tx_gain) - .def("get_normalized_tx_gain", &uhd::usrp::multi_usrp::get_normalized_tx_gain) - //get_tx_gain (special wrapper) - //.def("get_tx_gain_range", &uhd::usrp::multi_usrp::get_tx_gain_range) //(special wrapper) - .def("get_tx_gain_names", &uhd::usrp::multi_usrp::get_tx_gain_names) - .def("set_tx_antenna", &uhd::usrp::multi_usrp::set_tx_antenna) - .def("get_tx_antenna", &uhd::usrp::multi_usrp::get_tx_antenna) - .def("get_tx_antennas", &uhd::usrp::multi_usrp::get_tx_antennas) - .def("set_tx_bandwidth", &uhd::usrp::multi_usrp::set_tx_bandwidth) - .def("get_tx_bandwidth", &uhd::usrp::multi_usrp::get_tx_bandwidth) - .def("get_tx_bandwidth_range", &uhd::usrp::multi_usrp::get_tx_bandwidth_range) - .def("get_tx_dboard_iface", &uhd::usrp::multi_usrp::get_tx_dboard_iface) // TODO dboard_iface::sptr - .def("get_tx_sensor", &uhd::usrp::multi_usrp::get_tx_sensor) // TODO sensor_value_t - .def("get_tx_sensor_names", &uhd::usrp::multi_usrp::get_tx_sensor_names) - //set_tx_dc_offset (special wrapper) - //set_tx_iq_balance (special wrapper) - - // GPIO methods - .def("get_gpio_banks", &uhd::usrp::multi_usrp::get_gpio_banks) - .def("set_gpio_attr", &uhd::usrp::multi_usrp::set_gpio_attr) - .def("get_gpio_attr", &uhd::usrp::multi_usrp::get_gpio_attr) - .def("enumerate_registers", &uhd::usrp::multi_usrp::enumerate_registers) - .def("get_register_info", &uhd::usrp::multi_usrp::get_register_info) // TODO register_info_t - .def("write_register", &uhd::usrp::multi_usrp::write_register) - .def("read_register", &uhd::usrp::multi_usrp::read_register) - - //Filter API methods - .def("get_filter_names", &uhd::usrp::multi_usrp::get_filter_names) - .def("get_filter", &uhd::usrp::multi_usrp::get_filter) - .def("set_filter", &uhd::usrp::multi_usrp::set_filter) - ; - - bp::class_<uhd::rx_streamer, boost::shared_ptr<uhd::rx_streamer>, boost::noncopyable>("rx_streamer", bp::no_init) - .def("recv", &Pyrecv) - .def("get_num_channels", &uhd::rx_streamer::get_num_channels) - .def("get_max_num_samps", &uhd::rx_streamer::get_max_num_samps) - .def("issue_stream_cmd", &uhd::rx_streamer::issue_stream_cmd) - ; - - bp::class_<uhd::tx_streamer, boost::shared_ptr<uhd::tx_streamer>, boost::noncopyable>("tx_streamer", bp::no_init) - .def("send", &Pysend) - .def("get_num_channels", &uhd::tx_streamer::get_num_channels) - .def("get_max_num_samps", &uhd::tx_streamer::get_max_num_samps) - ; - -} - -void export_types(void) -{ - //Register submodule types - bp::object types_module(bp::handle<>(bp::borrowed(PyImport_AddModule("libpyuhd.types")))); - bp::scope().attr("types") = types_module; - bp::scope io_scope = types_module; - - bp::implicitly_convertible<std::string, uhd::device_addr_t>(); - - bp::enum_<uhd::stream_cmd_t::stream_mode_t>("stream_mode") - .value("start_cont", uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS) - .value("stop_cont", uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS) - .value("num_done", uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE) - .value("num_more", uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE) - ; - - bp::enum_<uhd::rx_metadata_t::error_code_t>("rx_metadata_error_code") - .value("none", uhd::rx_metadata_t::error_code_t::ERROR_CODE_NONE) - .value("timeout", uhd::rx_metadata_t::error_code_t::ERROR_CODE_TIMEOUT) - .value("late", uhd::rx_metadata_t::error_code_t::ERROR_CODE_LATE_COMMAND) - .value("broken_chain", uhd::rx_metadata_t::error_code_t::ERROR_CODE_BROKEN_CHAIN) - .value("overflow", uhd::rx_metadata_t::error_code_t::ERROR_CODE_OVERFLOW) - .value("alignment", uhd::rx_metadata_t::error_code_t::ERROR_CODE_ALIGNMENT) - .value("bad_packet", uhd::rx_metadata_t::error_code_t::ERROR_CODE_BAD_PACKET) - ; - - bp::enum_<uhd::tune_request_t::policy_t>("tune_request_policy") - .value("none", uhd::tune_request_t::POLICY_NONE) - .value("auto", uhd::tune_request_t::POLICY_AUTO) - .value("manual", uhd::tune_request_t::POLICY_MANUAL) - ; - - - bp::class_<uhd::stream_args_t>("stream_args", bp::init<std::string,std::string>()) - .def_readwrite("cpu_format", &uhd::stream_args_t::cpu_format) - .def_readwrite("otw_format", &uhd::stream_args_t::otw_format) - .def_readwrite("args", &uhd::stream_args_t::args) - .def_readwrite("channels", &uhd::stream_args_t::channels) - ; - - bp::class_<uhd::stream_cmd_t>("stream_cmd", bp::init<uhd::stream_cmd_t::stream_mode_t>()) - .def_readwrite("num_samps", &uhd::stream_cmd_t::num_samps) - .def_readwrite("time_spec", &uhd::stream_cmd_t::time_spec) - .def_readwrite("stream_now", &uhd::stream_cmd_t::stream_now) - ; - - bp::class_<uhd::rx_metadata_t>("rx_metadata", bp::init<>()) - .def("reset", &uhd::rx_metadata_t::reset) - .def("to_pp_string", &uhd::rx_metadata_t::to_pp_string) - .def("strerror", &uhd::rx_metadata_t::strerror) - .def("__str__", &uhd::rx_metadata_t::to_pp_string, bp::args("compact")=false) - .def_readonly("has_time_spec", &uhd::rx_metadata_t::has_time_spec) - .def_readonly("time_spec", &uhd::rx_metadata_t::time_spec) - .def_readonly("more_fragments", &uhd::rx_metadata_t::more_fragments) - .def_readonly("start_of_burst", &uhd::rx_metadata_t::start_of_burst) - .def_readonly("end_of_burst", &uhd::rx_metadata_t::end_of_burst) - .def_readonly("error_code", &uhd::rx_metadata_t::error_code) - .def_readonly("out_of_sequence", &uhd::rx_metadata_t::out_of_sequence) - ; - - bp::class_<uhd::tx_metadata_t>("tx_metadata", bp::init<>()) - .def_readwrite("has_time_spec", &uhd::tx_metadata_t::has_time_spec) - .def_readwrite("time_spec", &uhd::tx_metadata_t::time_spec) - .def_readwrite("start_of_burst", &uhd::tx_metadata_t::start_of_burst) - .def_readwrite("end_of_burst", &uhd::tx_metadata_t::end_of_burst) - ; - - - bp::class_<uhd::tune_request_t>("tune_request", bp::init<double>()) - .def_readwrite("target_freq", &uhd::tune_request_t::target_freq) - .def_readwrite("rf_freq_policy", &uhd::tune_request_t::rf_freq_policy) - .def_readwrite("dsp_freq_policy", &uhd::tune_request_t::dsp_freq_policy) - .def_readwrite("rf_freq", &uhd::tune_request_t::rf_freq) - .def_readwrite("dsp_freq", &uhd::tune_request_t::dsp_freq) - .def_readwrite("args", &uhd::tune_request_t::args) - ; - - bp::class_<uhd::tune_result_t>("tune_result", bp::init<>()) - ; - - bp::class_<uhd::range_t>("range", bp::init<double>()) - .def(bp::init<double, double, double>()) - .def("start", &uhd::range_t::start) - .def("stop", &uhd::range_t::stop) - .def("step", &uhd::range_t::step) - .def("__str__", &uhd::range_t::to_pp_string) - ; - - bp::class_<uhd::meta_range_t>("meta_range", bp::init<>()) - .def(bp::init<double, double, double>()) - .def("start", &uhd::meta_range_t::start) - .def("stop", &uhd::meta_range_t::stop) - .def("step", &uhd::meta_range_t::step) - .def("clip", &uhd::meta_range_t::clip) - .def("__str__", &uhd::meta_range_t::to_pp_string) - ; -} - -void export_filter() -{ - //Register submodule filter - bp::object filter_module(bp::handle<>(bp::borrowed(PyImport_AddModule("libpyuhd.filter")))); - bp::scope().attr("filter") = filter_module; - bp::scope io_scope = filter_module; - - bp::enum_<uhd::filter_info_base::filter_type>("filter_type") - .value("analog_low_pass", uhd::filter_info_base::ANALOG_LOW_PASS) - .value("analog_band_pass", uhd::filter_info_base::ANALOG_BAND_PASS) - .value("digital_i16", uhd::filter_info_base::DIGITAL_I16) - .value("digital_fir_i16", uhd::filter_info_base::DIGITAL_FIR_I16) - ; - - bp::class_<uhd::filter_info_base, boost::shared_ptr<uhd::filter_info_base> >("filter_info_base", bp::init<uhd::filter_info_base::filter_type, bool, size_t>()) - .def("is_bypassed", &uhd::filter_info_base::is_bypassed) - .def("get_type", &uhd::filter_info_base::get_type) - .def("__str__", &uhd::filter_info_base::to_pp_string) - ; - - bp::class_<uhd::analog_filter_base, boost::shared_ptr<uhd::analog_filter_base>, bp::bases<uhd::filter_info_base> >("analog_filter_base", bp::init< uhd::filter_info_base::filter_type, bool, size_t, std::string>()) - .def("get_analog_type", &uhd::analog_filter_base::get_analog_type, bp::return_value_policy<bp::copy_const_reference>() ) - ; - - bp::class_<uhd::analog_filter_lp, boost::shared_ptr<uhd::analog_filter_lp>, bp::bases<uhd::analog_filter_base> >("analog_filter_lp", bp::init<uhd::filter_info_base::filter_type, bool, size_t, const std::string, double, double>()) - .def("get_cutoff", &uhd::analog_filter_lp::get_cutoff) - .def("get_rolloff", &uhd::analog_filter_lp::get_rolloff) - .def("set_cutoff", &uhd::analog_filter_lp::set_cutoff) - ; - -} - -// We need this hack because import_array() returns NULL for newer Python -// versions. +// 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. #if PY_MAJOR_VERSION >= 3 void* init_numpy() { @@ -492,21 +123,69 @@ void init_numpy() BOOST_PYTHON_MODULE(libpyuhd) { + // Initialize the numpy C API + // (otherwise we will see segmentation faults) + init_numpy(); + bp::object package = bp::scope(); package.attr("__path__") = "libpyuhd"; + + // Declare converters iterable_converter() .from_python<std::vector<double> >() .from_python<std::vector<int> >() .from_python<std::vector<size_t> >() ; - bp::to_python_converter<uhd::dict<std::string, std::string>, - uhd_to_python_dict<std::string, std::string>, false >(); - bp::to_python_converter<std::vector<std::string>, - iterable_to_python_list<std::vector<std::string> >, false >(); - export_multi_usrp(); - export_types(); - export_filter(); - init_numpy(); + bp::to_python_converter< + uhd::dict<std::string, std::string>, + uhd_to_python_dict<std::string, std::string>, false >(); + bp::to_python_converter< + std::vector<std::string>, + iterable_to_python_list<std::vector<std::string> >, false >(); + + // Register types submodule + { + bp::object types_module( + bp::handle<>(bp::borrowed(PyImport_AddModule("libpyuhd.types"))) + ); + bp::scope().attr("types") = types_module; + bp::scope io_scope = types_module; + + bp::implicitly_convertible<std::string, uhd::device_addr_t>(); + + export_types(); + export_time_spec(); + export_spi_config(); + export_metadata(); + export_sensors(); + export_tune(); + } + + // Register usrp submodule + { + bp::object usrp_module( + bp::handle<>(bp::borrowed(PyImport_AddModule("libpyuhd.usrp"))) + ); + bp::scope().attr("usrp") = usrp_module; + bp::scope io_scope = usrp_module; + + export_multi_usrp(); + export_subdev_spec(); + export_dboard_iface(); + export_fe_connection(); + export_stream(); + } + + // Register filters submodule + { + bp::object filters_module( + bp::handle<>(bp::borrowed(PyImport_AddModule("libpyuhd.filters"))) + ); + bp::scope().attr("filters") = filters_module; + bp::scope io_scope = filters_module; + + export_filters(); + } } diff --git a/host/python/pyuhd.py b/host/python/pyuhd.py deleted file mode 100644 index 32279afb3..000000000 --- a/host/python/pyuhd.py +++ /dev/null @@ -1,120 +0,0 @@ -# -# Copyright 2017 Ettus Research LLC -# -# This program 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 of the License, or -# (at your option) any later version. -# -# This program 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, see <http://www.gnu.org/licenses/>. -# - -import libpyuhd as lib -import numpy as np - - -class multi_usrp(object): - def __init__(self, args=""): - self.usrp = lib.multi_usrp.multi_usrp.make(args) - - def __del__(self): - # Help the garbage collection - self.usrp = None - - def set_rx_rate(self, rate, chan=None): - if chan is None: - for c in xrange(self.usrp.get_rx_num_channels()): - self.usrp.set_rx_rate(rate, c) - elif isinstance(chan, list): - for c in chan: - self.usrp.set_rx_rate(rate, c) - else: - self.usrp.set_rx_rate(rate, chan) - - def set_tx_rate(self, rate, chan=None): - if chan is None: - for chan in xrange(self.usrp.get_tx_num_channels()): - self.usrp.set_tx_rate(rate, chan) - elif isinstance(chan, list): - for c in chan: - self.usrp.set_tx_rate(rate, c) - else: - self.usrp.set_tx_rate(rate, chan) - - - def recv_num_samps(self, num_samps, freq, rate=1e6, channels=[0], gain=10): - result = np.empty((len(channels), num_samps), dtype=np.complex64) - for chan in channels: - self.usrp.set_rx_rate(rate, chan) - self.usrp.set_rx_freq(lib.types.tune_request(freq), chan) - self.usrp.set_rx_gain(gain, chan) - st_args = lib.types.stream_args("fc32", "sc16") - st_args.channels = channels - metadata = lib.types.rx_metadata() - streamer = self.usrp.get_rx_stream(st_args) - buffer_samps = streamer.get_max_num_samps() - recv_buffer = np.zeros( - (len(channels), buffer_samps), dtype=np.complex64) - recv_samps = 0 - stream_cmd = lib.types.stream_cmd(lib.types.stream_mode.start_cont) - stream_cmd.stream_now = True - streamer.issue_stream_cmd(stream_cmd) - while (recv_samps < num_samps): - samps = streamer.recv(recv_buffer, metadata) - if metadata.error_code != lib.types.rx_metadata_error_code.none: - print(metadata.strerror()) - if samps: - real_samps = min(num_samps - recv_samps, samps) - result[:, recv_samps:recv_samps + real_samps - - 1] = recv_buffer[:, 0:real_samps - 1] - recv_samps += real_samps - stream_cmd = lib.types.stream_cmd(lib.types.stream_mode.stop_cont) - streamer.issue_stream_cmd(stream_cmd) - while samps: - samps = streamer.recv(recv_buffer, metadata) - # Help the garbage collection - streamer = None - return result - - def send_waveform(self, - waveform_proto, - duration, - freq, - rate=1e6, - channels=[0], - gain=10): - self.set_tx_rate(rate) - for chan in channels: - self.usrp.set_tx_rate(rate, chan) - self.usrp.set_tx_freq(lib.types.tune_request(freq), chan) - self.usrp.set_tx_gain(gain, chan) - st_args = lib.types.stream_args("fc32", "sc16") - st_args.channels = channels - metadata = lib.types.rx_metadata() - streamer = self.usrp.get_tx_stream(st_args) - buffer_samps = streamer.get_max_num_samps() - proto_len = waveform_proto.shape[-1] - if proto_len < buffer_samps: - waveform_proto = np.tile(waveform_proto, (1, int(np.ceil(float(buffer_samps)/proto_len)))) - proto_len = waveform_proto.shape[-1] - metadata = lib.types.tx_metadata() - send_samps = 0 - max_samps = int(np.floor(duration * rate)) - if waveform_proto.shape[0] < len(channels): - waveform_proto = np.tile(waveform_proto[0], (len(channels), 1)) - while send_samps < max_samps: - real_samps = min(proto_len, max_samps-send_samps) - if real_samps < proto_len: - samples = streamer.send(waveform_proto[:real_samps], metadata) - else: - samples = streamer.send(waveform_proto, metadata) - send_samps += samples - # Help the garbage collection - streamer = None - return send_samps diff --git a/host/python/setup.py.in b/host/python/setup.py.in index 76044fe72..3c2176c0e 100755 --- a/host/python/setup.py.in +++ b/host/python/setup.py.in @@ -1,22 +1,10 @@ #!/usr/bin/env python # -# Copyright 2017 Ettus Research LLC +# Copyright 2017-2018 Ettus Research, a National Instruments Company # -# This program 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 of the License, or -# (at your option) any later version. +# SPDX-License-Identifier: GPL-3.0-or-later # -# This program 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, see <http://www.gnu.org/licenses/>. -# - - +"""Setup file for uhd module""" from setuptools import setup @@ -30,15 +18,13 @@ setup(name='uhd', 'Programming Language :: Python', 'Topic :: System :: Hardware :: Hardware Drivers', ], - keywords='SDR UHD USRP SDR', + keywords='SDR UHD USRP', author='Ettus Research', author_email='packages@ettus.com', url='https://www.ettus.com/', license='GPLv3', - package_dir={ '': '${CMAKE_CURRENT_BINARY_DIR}' }, - package_data={"uhd": ["*.so"]}, + package_dir={'': '${CMAKE_CURRENT_BINARY_DIR}'}, + package_data={'uhd': ['*.so']}, zip_safe=False, packages=['uhd'], - install_requires=[ - 'numpy' - ]) + install_requires=['numpy']) diff --git a/host/python/types.py b/host/python/types.py new file mode 100644 index 000000000..bcf6e1df2 --- /dev/null +++ b/host/python/types.py @@ -0,0 +1,30 @@ +# +# Copyright 2017-2018 Ettus Research, a National Instruments Company +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" @package types +Python UHD module containing types to be used with a MultiUSRP object +""" + +from . import libpyuhd as lib + + +StreamMode = lib.types.stream_mode +StreamCMD = lib.types.stream_cmd +TimeSpec = lib.types.time_spec +SPIEdge = lib.types.spi_edge +SPIConfig = lib.types.spi_config +RXMetadataErrorCode = lib.types.rx_metadata_error_code +Range = lib.types.range +RangeVector = lib.types.range_vector +MetaRange = lib.types.meta_range +RXMetadata = lib.types.rx_metadata +TXMetadata = lib.types.tx_metadata +TXAsyncMetadata = lib.types.async_metadata +TXMetadataEventCode = lib.types.tx_metadata_event_code +DataType = lib.types.data_type +SensorValue = lib.types.sensor_value +TuneRequestPolicy = lib.types.tune_request_policy +TuneRequest = lib.types.tune_request +TuneResult = lib.types.tune_result diff --git a/host/python/usrp.py b/host/python/usrp.py new file mode 100644 index 000000000..6179a3602 --- /dev/null +++ b/host/python/usrp.py @@ -0,0 +1,143 @@ +# +# Copyright 2017-2018 Ettus Research, a National Instruments Company +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" @package usrp +Python UHD module containing the MultiUSRP and other objects +""" + +import numpy as np +from . import libpyuhd as lib + + +class MultiUSRP(lib.usrp.multi_usrp): + """ + MultiUSRP object for controlling devices + """ + def __init__(self, args=""): + """MultiUSRP constructor""" + super(MultiUSRP, self).__init__(args) + + def recv_num_samps(self, num_samps, freq, rate=1e6, channels=(0,), gain=10): + """ + RX a finite number of samples from the USRP + :param num_samps: number of samples to RX + :param freq: RX frequency (Hz) + :param rate: RX sample rate (Hz) + :param channels: list of channels to RX on + :param gain: RX gain (dB) + :return: numpy array of complex floating-point samples (fc32) + """ + result = np.empty((len(channels), num_samps), dtype=np.complex64) + + for chan in channels: + super(MultiUSRP, self).set_rx_rate(rate, chan) + super(MultiUSRP, self).set_rx_freq(lib.types.tune_request(freq), chan) + super(MultiUSRP, self).set_rx_gain(gain, chan) + + st_args = lib.usrp.stream_args("fc32", "sc16") + st_args.channels = channels + metadata = lib.types.rx_metadata() + streamer = super(MultiUSRP, self).get_rx_stream(st_args) + buffer_samps = streamer.get_max_num_samps() + recv_buffer = np.zeros( + (len(channels), buffer_samps), dtype=np.complex64) + + recv_samps = 0 + stream_cmd = lib.types.stream_cmd(lib.types.stream_mode.start_cont) + stream_cmd.stream_now = True + streamer.issue_stream_cmd(stream_cmd) + + samps = np.array([], dtype=np.complex64) + while recv_samps < num_samps: + samps = streamer.recv(recv_buffer, metadata) + + if metadata.error_code != lib.types.rx_metadata_error_code.none: + print(metadata.strerror()) + if samps: + real_samps = min(num_samps - recv_samps, samps) + result[:, recv_samps:recv_samps + real_samps] = recv_buffer[:, 0:real_samps] + recv_samps += real_samps + + stream_cmd = lib.types.stream_cmd(lib.types.stream_mode.stop_cont) + streamer.issue_stream_cmd(stream_cmd) + + while samps: + samps = streamer.recv(recv_buffer, metadata) + + # Help the garbage collection + streamer = None + return result + + def send_waveform(self, + waveform_proto, + duration, + freq, + rate=1e6, + channels=(0,), + gain=10): + """ + TX a finite number of samples from the USRP + :param waveform_proto: numpy array of samples to TX + :param duration: time in seconds to transmit at the supplied rate + :param freq: TX frequency (Hz) + :param rate: TX sample rate (Hz) + :param channels: list of channels to TX on + :param gain: TX gain (dB) + :return: the number of transmitted samples + """ + super(MultiUSRP, self).set_tx_rate(rate) + for chan in channels: + super(MultiUSRP, self).set_tx_rate(rate, chan) + super(MultiUSRP, self).set_tx_freq(lib.types.tune_request(freq), chan) + super(MultiUSRP, self).set_tx_gain(gain, chan) + + st_args = lib.usrp.stream_args("fc32", "sc16") + st_args.channels = channels + + metadata = lib.types.rx_metadata() + streamer = super(MultiUSRP, self).get_tx_stream(st_args) + buffer_samps = streamer.get_max_num_samps() + proto_len = waveform_proto.shape[-1] + + if proto_len < buffer_samps: + waveform_proto = np.tile(waveform_proto, + (1, int(np.ceil(float(buffer_samps)/proto_len)))) + proto_len = waveform_proto.shape[-1] + + metadata = lib.types.tx_metadata() + send_samps = 0 + max_samps = int(np.floor(duration * rate)) + + if len(waveform_proto.shape) == 1: + waveform_proto = waveform_proto.reshape(1, waveform_proto.size) + if waveform_proto.shape[0] < len(channels): + waveform_proto = np.tile(waveform_proto[0], (len(channels), 1)) + + while send_samps < max_samps: + real_samps = min(proto_len, max_samps-send_samps) + if real_samps < proto_len: + samples = streamer.send(waveform_proto[:real_samps], metadata) + else: + samples = streamer.send(waveform_proto, metadata) + send_samps += samples + + # Help the garbage collection + streamer = None + return send_samps + + +SubdevSpecPair = lib.usrp.subdev_spec_pair +SubdevSpec = lib.usrp.subdev_spec +GPIOAtrReg = lib.usrp.gpio_atr_reg +GPIOAtrMode = lib.usrp.gpio_atr_mode +Unit = lib.usrp.unit +AuxDAC = lib.usrp.aux_dac +AuxADC = lib.usrp.aux_adc +SpecialProps = lib.usrp.special_props +Sampling = lib.usrp.sampling +FEConnection = lib.usrp.fe_connection +StreamArgs = lib.usrp.stream_args +RXStreamer = lib.usrp.rx_streamer +TXStreamer = lib.usrp.tx_streamer |