diff options
Diffstat (limited to 'host/python')
| -rw-r--r-- | host/python/CMakeLists.txt | 83 | ||||
| -rw-r--r-- | host/python/__init__.py | 18 | ||||
| -rw-r--r-- | host/python/pyuhd.cpp | 512 | ||||
| -rw-r--r-- | host/python/pyuhd.py | 120 | ||||
| -rwxr-xr-x | host/python/setup.py.in | 44 | 
5 files changed, 777 insertions, 0 deletions
diff --git a/host/python/CMakeLists.txt b/host/python/CMakeLists.txt new file mode 100644 index 000000000..f4effa1c4 --- /dev/null +++ b/host/python/CMakeLists.txt @@ -0,0 +1,83 @@ +# +# 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/>. +# + +######################################################################## +# This file included, use CMake directory variables +######################################################################## + + +PYTHON_CHECK_MODULE( +      "virtualenv" +      "sys" "hasattr(sys, 'real_prefix')" +      HAVE_PYTHON_VIRTUALENV +  ) +# Get include dirs +INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_DIRS}) +EXECUTE_PROCESS( +    COMMAND "${PYTHON_EXECUTABLE}" -c +    "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 +ADD_LIBRARY(pyuhd SHARED pyuhd.cpp) +TARGET_INCLUDE_DIRECTORIES(pyuhd PUBLIC +    ${PYTHON_NUMPY_INCLUDE_DIR} +    ${CMAKE_SOURCE_DIR}/lib +) +TARGET_LINK_LIBRARIES(pyuhd ${BOOST_PYTHON_LIBRARY} ${Boost_LIBRARIES} ${PYTHON_LIBRARY} uhd) +# 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) + +SET(PYUHD_FILES +  ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py +  ${CMAKE_CURRENT_SOURCE_DIR}/types.py +  ${CMAKE_CURRENT_SOURCE_DIR}/usrp.py +  ${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(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}) + +    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() + diff --git a/host/python/__init__.py b/host/python/__init__.py new file mode 100644 index 000000000..39b6b170d --- /dev/null +++ b/host/python/__init__.py @@ -0,0 +1,18 @@ +# +# 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/>. +# + +from pyuhd import * diff --git a/host/python/pyuhd.cpp b/host/python/pyuhd.cpp new file mode 100644 index 000000000..5bb6271f3 --- /dev/null +++ b/host/python/pyuhd.cpp @@ -0,0 +1,512 @@ +// +// 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/>. +// + +#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> + +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +#include <numpy/arrayobject.h> + + +namespace bp = boost::python; + + +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; +    } + +    // 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); +} + +// 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; + +    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 +{ +    static PyObject* convert(uhd::dict<Dtype1, Dtype2> const& input_dict) +    { +        bp::dict py_dict; +        for (const auto& key: input_dict.keys()){ +            py_dict[key] = input_dict[key]; +        } +        return bp::incref(py_dict.ptr()); +    } + +}; + +template<typename Container> +struct iterable_to_python_list +{ +    static PyObject* convert(Container const& input) +    { +        bp::list py_list; +        for (const auto& element: input){ +            py_list.append(element); +        } +        return bp::incref(py_list.ptr()); +    } +}; + + +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. +#if PY_MAJOR_VERSION >= 3 +void* init_numpy() +{ +    import_array(); +    return NULL; +} +#else +void init_numpy() +{ +    import_array(); +} +#endif + +BOOST_PYTHON_MODULE(libpyuhd) +{ +    bp::object package = bp::scope(); +    package.attr("__path__") = "libpyuhd"; +    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(); +} + diff --git a/host/python/pyuhd.py b/host/python/pyuhd.py new file mode 100644 index 000000000..32279afb3 --- /dev/null +++ b/host/python/pyuhd.py @@ -0,0 +1,120 @@ +# +# 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 new file mode 100755 index 000000000..76044fe72 --- /dev/null +++ b/host/python/setup.py.in @@ -0,0 +1,44 @@ +#!/usr/bin/env python +# +# 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/>. +# + + + +from setuptools import setup + +setup(name='uhd', +      version='${UHD_VERSION_MAJOR}.${UHD_VERSION_API}.${UHD_VERSION_ABI}', +      description='Universal Software Radio Peripheral (USRP) Hardware Driver Python API', +      classifiers=[ +          'Development Status :: 4 - Beta', +          'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', +          'Programming Language :: C++', +          'Programming Language :: Python', +          'Topic :: System :: Hardware :: Hardware Drivers', +      ], +      keywords='SDR UHD USRP SDR', +      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"]}, +      zip_safe=False, +      packages=['uhd'], +      install_requires=[ +          'numpy' +      ])  | 
