diff options
| author | Martin Braun <martin.braun@ettus.com> | 2017-03-08 09:28:55 -0800 | 
|---|---|---|
| committer | Martin Braun <martin.braun@ettus.com> | 2017-12-22 15:03:44 -0800 | 
| commit | 1a4348038d0eb57d53475074dca49e8192aeb2d7 (patch) | |
| tree | 04c8e87fa9c95d2702aac410f6fb271461acb9bb /mpm/python | |
| parent | fc8cd827f6b16b9c8c354a216889e6a9d7f37456 (diff) | |
| download | uhd-1a4348038d0eb57d53475074dca49e8192aeb2d7.tar.gz uhd-1a4348038d0eb57d53475074dca49e8192aeb2d7.tar.bz2 uhd-1a4348038d0eb57d53475074dca49e8192aeb2d7.zip  | |
Initial commit for N3xx development.
- Creates mpm/ subdirectory
- First pass at hardware daemon/MPM
- New code for LMK04828, AD9371
- spidev integration
Contributions by:
Martin Braun <martin.braun@ettus.com>
Derek Kozel <derek.kozel@ettus.com>
Mark Meserve <mark.meserve@ni.com>
Andrej Rode <andrej.rode@ettus.com>
Diffstat (limited to 'mpm/python')
| -rw-r--r-- | mpm/python/CMakeLists.txt | 56 | ||||
| -rw-r--r-- | mpm/python/lib_periphs.cpp | 49 | ||||
| -rw-r--r-- | mpm/python/lib_periphs.hpp | 4 | ||||
| -rw-r--r-- | mpm/python/n310_periphs.cpp | 21 | ||||
| -rw-r--r-- | mpm/python/n310_periphs.hpp | 3 | ||||
| -rw-r--r-- | mpm/python/pyusrp_periphs.cpp | 19 | ||||
| -rwxr-xr-x | mpm/python/setup.py.in | 44 | ||||
| -rwxr-xr-x | mpm/python/socket_test.py | 22 | ||||
| -rwxr-xr-x | mpm/python/test_lmk.py | 7 | ||||
| -rwxr-xr-x | mpm/python/test_rpc.py | 63 | ||||
| -rw-r--r-- | mpm/python/tests_periphs.cpp | 18 | ||||
| -rw-r--r-- | mpm/python/tests_periphs.hpp | 3 | ||||
| -rwxr-xr-x | mpm/python/usrp_hwd.py | 57 | ||||
| -rw-r--r-- | mpm/python/usrp_mpm/CMakeLists.txt | 30 | ||||
| -rw-r--r-- | mpm/python/usrp_mpm/__init__.py | 23 | ||||
| -rw-r--r-- | mpm/python/usrp_mpm/discovery.py | 76 | ||||
| -rw-r--r-- | mpm/python/usrp_mpm/periphs.py | 71 | ||||
| -rw-r--r-- | mpm/python/usrp_mpm/rpc_process.py | 48 | ||||
| -rw-r--r-- | mpm/python/usrp_mpm/rpc_server.py | 69 | ||||
| -rw-r--r-- | mpm/python/usrp_mpm/types.py | 38 | 
20 files changed, 721 insertions, 0 deletions
diff --git a/mpm/python/CMakeLists.txt b/mpm/python/CMakeLists.txt new file mode 100644 index 000000000..7d3b251d7 --- /dev/null +++ b/mpm/python/CMakeLists.txt @@ -0,0 +1,56 @@ +# +# Copyright 2017 Ettus Research (National Instruments) +# +# 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 +######################################################################## + + +SET(UHD_HOST_ROOT ${CMAKE_SOURCE_DIR}/../host) +LIST(APPEND +  pyusrp_periphs_sources +  pyusrp_periphs.cpp +  lib_periphs.cpp +  n310_periphs.cpp +  # tests_periphs.cpp +  ) + +ADD_LIBRARY(pyusrp_periphs SHARED ${pyusrp_periphs_sources}) +TARGET_INCLUDE_DIRECTORIES(pyusrp_periphs PUBLIC ${PYTHON_INCLUDE_DIRS} ${UHD_HOST_ROOT}/lib/usrp/common) +TARGET_LINK_LIBRARIES(pyusrp_periphs ${Boost_PYTHON_LIBRARY} ${Boost_LIBRARIES} usrp-periphs) +ADD_CUSTOM_COMMAND(TARGET pyusrp_periphs POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/libpyusrp_periphs.so ${CMAKE_CURRENT_BINARY_DIR}/usrp_mpm/libpyusrp_periphs.so) + +ADD_SUBDIRECTORY(usrp_mpm) + +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 ${USRP_MPM_FILES} ${CMAKE_CURRENT_BINARY_DIR}/usrp_mpm +    COMMAND ${PYTHON} ${SETUP_PY} -q build +    COMMAND ${CMAKE_COMMAND} -E touch ${OUTPUT} +    DEPENDS ${USRP_MPM_FILES}) +ADD_CUSTOM_TARGET(usrp_mpm ALL DEPENDS ${OUTPUT} pyusrp_periphs) + +EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} -c +    "from distutils import sysconfig; print sysconfig.get_python_lib(plat_specific=True, prefix='')" +    OUTPUT_VARIABLE USRP_MPM_PYTHON_DIR OUTPUT_STRIP_TRAILING_WHITESPACE +) +INSTALL(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/build/lib/usrp_mpm DESTINATION ${CMAKE_INSTALL_PREFIX}/${USRP_MPM_PYTHON_DIR}) diff --git a/mpm/python/lib_periphs.cpp b/mpm/python/lib_periphs.cpp new file mode 100644 index 000000000..030752ede --- /dev/null +++ b/mpm/python/lib_periphs.cpp @@ -0,0 +1,49 @@ +#include "lib_periphs.hpp" +#include "lmk04828.hpp" +#include <mpm/spi_iface.hpp> +#include <boost/python.hpp> + +namespace bp = boost::python; + +void export_lmk(){ +    //Register submodule types +    bp::object lmk_module(bp::handle<>(bp::borrowed(PyImport_AddModule("libpyusrp_periphs.lmk")))); +    bp::scope().attr("lmk") = lmk_module; +    bp::scope io_scope = lmk_module; + +    bp::class_<lmk04828_iface, boost::shared_ptr<lmk04828_iface>, boost::noncopyable >("lmk04828_iface", bp::no_init) +               .def("make", &lmk04828_iface::make) +               .def("verify_chip_id", &lmk04828_iface::verify_chip_id) +               .def("init", &lmk04828_iface::init) +               .def("send_sysref_pulse", &lmk04828_iface::send_sysref_pulse) +               ; +} + +void export_spi(){ +    //Register submodule types +    bp::object spi_module(bp::handle<>(bp::borrowed(PyImport_AddModule("libpyusrp_periphs.spi")))); +    bp::scope().attr("spi") = spi_module; +    bp::scope io_scope = spi_module; + +    bp::class_<mpm::spi_iface, boost::noncopyable>("spi_iface", bp::no_init) +        .def("write_byte", &mpm::spi_iface::write_byte) +        .def("write_bytes", &mpm::spi_iface::write_bytes) +        .def("read_byte", &mpm::spi_iface::read_byte) +        .def("write_field", &mpm::spi_iface::write_field) +        .def("read_field", &mpm::spi_iface::read_field) +        .def("get_wire_mode", &mpm::spi_iface::get_wire_mode) +        .def("get_endianness", &mpm::spi_iface::get_endianness) +        .def("get_chip_select", &mpm::spi_iface::get_chip_select) +        ; + +    bp::enum_<mpm::spi_iface::spi_endianness_t>("spi_endianness") +        .value("lsb_first", mpm::spi_iface::spi_endianness_t::LSB_FIRST) +        .value("msb_first", mpm::spi_iface::spi_endianness_t::MSB_FIRST) +        ; + +    bp::enum_<mpm::spi_iface::spi_wire_mode_t>("spi_wire_mode") +        .value("three_wire_mode", mpm::spi_iface::spi_wire_mode_t::THREE_WIRE_MODE) +        .value("four_wire_mode", mpm::spi_iface::spi_wire_mode_t::FOUR_WIRE_MODE) +        ; +} + diff --git a/mpm/python/lib_periphs.hpp b/mpm/python/lib_periphs.hpp new file mode 100644 index 000000000..2ea023c5c --- /dev/null +++ b/mpm/python/lib_periphs.hpp @@ -0,0 +1,4 @@ +#pragma once + +void export_lmk(); +void export_spi(); diff --git a/mpm/python/n310_periphs.cpp b/mpm/python/n310_periphs.cpp new file mode 100644 index 000000000..42fea61b1 --- /dev/null +++ b/mpm/python/n310_periphs.cpp @@ -0,0 +1,21 @@ +#include "n310_periphs.hpp" +#include "../n310/periph_manager.hpp" +#include <boost/python.hpp> +#include <memory> + +namespace bp = boost::python; + +void export_n3xx(){ +    //Register submodule types +    bp::object n3xx_module(bp::handle<>(bp::borrowed(PyImport_AddModule("libpyusrp_periphs.n3xx")))); +    bp::scope().attr("n3xx") = n3xx_module; +    bp::scope io_scope = n3xx_module; + +    bp::class_<mpm::n3xx::n3xx_dboard_periph_manager, boost::noncopyable>("dboard_periph_manager", bp::no_init) +        .def("get_clock_gen()", &mpm::n3xx::n3xx_dboard_periph_manager::get_clock_gen) +        ; +    bp::class_<mpm::n3xx::periph_manager, boost::noncopyable, std::shared_ptr<mpm::n3xx::periph_manager> >("periph_manager", bp::init<std::string>()) +        .def("get_dboard_A", &mpm::n3xx::periph_manager::get_dboard_A) +        ; +} + diff --git a/mpm/python/n310_periphs.hpp b/mpm/python/n310_periphs.hpp new file mode 100644 index 000000000..b4e418947 --- /dev/null +++ b/mpm/python/n310_periphs.hpp @@ -0,0 +1,3 @@ +#pragma once + +void export_n3xx(); diff --git a/mpm/python/pyusrp_periphs.cpp b/mpm/python/pyusrp_periphs.cpp new file mode 100644 index 000000000..5a49398c4 --- /dev/null +++ b/mpm/python/pyusrp_periphs.cpp @@ -0,0 +1,19 @@ +#include "n310_periphs.hpp" +// #include "tests_periphs.hpp" +#include "lib_periphs.hpp" +#include <mpm/print_foo.hpp> +#include <boost/python.hpp> +#include <boost/noncopyable.hpp> + +namespace bp = boost::python; + +BOOST_PYTHON_MODULE(libpyusrp_periphs) +{ +    bp::object package = bp::scope(); +    package.attr("__path__") = "libpyusrp_periphs"; +    bp::def("print_foo", &mpm::print_foo); +    export_spi(); +    // export_tests(); +    export_lmk(); +    export_n3xx(); +} diff --git a/mpm/python/setup.py.in b/mpm/python/setup.py.in new file mode 100755 index 000000000..ad41eb480 --- /dev/null +++ b/mpm/python/setup.py.in @@ -0,0 +1,44 @@ +#!/usr/bin/env python +# +# Copyright 2017 Ettus Research (National Instruments) +# +# 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='usrp_mpm', +      version='', +      description='Universal Software Radio Peripheral (USRP) Machiavellian Puppet Master (MPM) 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={"usrp_mpm": ["*.so"]}, +      zip_safe=False, +      packages=['usrp_mpm'], +      install_requires=[ +          'numpy' +      ]) diff --git a/mpm/python/socket_test.py b/mpm/python/socket_test.py new file mode 100755 index 000000000..1799d2010 --- /dev/null +++ b/mpm/python/socket_test.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +import socket +import binascii + +UDP_IP = "0.0.0.0" +UDP_PORT = 5000 + +sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +sock.bind(((UDP_IP, UDP_PORT))) + +send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + + +while True: +    buf = bytearray(4096) +    nbytes, sender = sock.recvfrom_into(buf, 4096) +    print(sender) +    print("number of bytes: {}".format(nbytes)) +    print("received bytes: {}".format(binascii.b2a_hex(buf[:nbytes]))) + +    send_sock.sendto(buf[:nbytes], sender) diff --git a/mpm/python/test_lmk.py b/mpm/python/test_lmk.py new file mode 100755 index 000000000..5534bf381 --- /dev/null +++ b/mpm/python/test_lmk.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python + +import libpyusrp_periphs as p + +dev = p.n3xx.periph_manager("/dev/spidev1.0") +lmk = dev.get_clock_gen() +lmk.verify_chip_id() diff --git a/mpm/python/test_rpc.py b/mpm/python/test_rpc.py new file mode 100755 index 000000000..1a97a103c --- /dev/null +++ b/mpm/python/test_rpc.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +import socket +from mprpc import RPCClient +import usrp_mpm as mpm +import argparse + + +def parse_args(): +    parser = argparse.ArgumentParser() +    parser.add_argument("-a", "--address", default="0.0.0.0", type=str, help="Destination address") +    parser.add_argument("-p", "--port", default=0, type=int, help="Destination port") +    sub_parsers = parser.add_subparsers(dest="command") + +    rpc_parser = sub_parsers.add_parser("rpc", help="Issue RPC") +    rpc_parser.add_argument("-c", "--call", required=True, help="command to issue") +    rpc_parser.add_argument("arguments", nargs="*") + +    disc_parser = sub_parsers.add_parser("disc", help="Issue discovery") +    return parser.parse_args() + + +def rpc(address, port, command, *args): +    if not port: +        port = mpm.types.MPM_RPC_PORT +    client = RPCClient(address, port) +    if args: +        result = client.call(command, *args) +    else: +        result = client.call(command) +    return result + + +def discovery(address, port): +    if not port: +        port = mpm.types.MPM_DISCOVERY_PORT +    sock = socket.socket( +        socket.AF_INET, +        socket.SOCK_DGRAM) +    sock.sendto(mpm.types.MPM_DISCOVERY_MESSAGE, (address, port)) +    sock.settimeout(1.0) # wait max 1 second +    while True: +        try: +            data, sender = sock.recvfrom(4096) +            print("Received respons from: {}".format(sender[0])) +            print("Dicovery data: {}".format(data)) +        except: +            break + + +def main(): +    args = parse_args() +    if args.command == "rpc": +        if args.arguments: +            result = rpc(args.address, args.port, args.call, *args.arguments) +        else: +            result = rpc(args.address, args.port, args.call) +        print(result) +    elif args.command == "disc": +        discovery(args.address, args.port) + + +if __name__ == "__main__": +    exit(not(main())) diff --git a/mpm/python/tests_periphs.cpp b/mpm/python/tests_periphs.cpp new file mode 100644 index 000000000..15c1b08b6 --- /dev/null +++ b/mpm/python/tests_periphs.cpp @@ -0,0 +1,18 @@ +#include "tests_periphs.hpp" +#include <mpm/tests/tests_spi_iface.hpp> +#include <mpm/spi_iface.hpp> +#include <boost/python.hpp> + +namespace bp = boost::python; + +void export_tests(){ +    //Register submodule types +    bp::object tests_module(bp::handle<>(bp::borrowed(PyImport_AddModule("libpyusrp_periphs.tests")))); +    bp::scope().attr("tests") = tests_module; +    bp::scope io_scope = tests_module; + +    bp::class_<mpm::tests_spi_iface, bp::bases<mpm::spi_iface>, std::shared_ptr<mpm::tests_spi_iface> >("test_spi_iface", bp::init<>()) +        .def("make", &mpm::tests_spi_iface::make) +        .staticmethod("make") +        ; +} diff --git a/mpm/python/tests_periphs.hpp b/mpm/python/tests_periphs.hpp new file mode 100644 index 000000000..4b978269d --- /dev/null +++ b/mpm/python/tests_periphs.hpp @@ -0,0 +1,3 @@ +#pragma once + +void export_tests(); diff --git a/mpm/python/usrp_hwd.py b/mpm/python/usrp_hwd.py new file mode 100755 index 000000000..dc72ce3b9 --- /dev/null +++ b/mpm/python/usrp_hwd.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# +# Copyright 2017 Ettus Research (National Instruments) +# +# 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/>. +# +""" +Main executable for the USRP Hardware Daemon +""" + +from __future__ import print_function +from multiprocessing import Value +import ctypes +import signal +import time +import usrp_mpm as mpm +from usrp_mpm.types import shared_state +from usrp_mpm.types import graceful_exit + + +def signal_handler(signum, frame): +    raise graceful_exit() + + +def main(): +    """ +    Go, go, go! + +    Main process loop. +    """ +    procs = [] +    signal.signal(signal.SIGTERM, signal_handler) +    signal.signal(signal.SIGINT, signal_handler) +    shared = shared_state() +    procs.append(mpm.spawn_discovery_process({'a': "foo"}, shared)) +    # procs.append(mpm.spawn_rpc_process("Echo", 5000)) +    procs.append(mpm.spawn_rpc_process("MPM", mpm.types.MPM_RPC_PORT, shared)) +    try: +        for proc in procs: +            proc.join() +    except mpm.types.graceful_exit: +        pass + +if __name__ == '__main__': +    exit(not main()) + diff --git a/mpm/python/usrp_mpm/CMakeLists.txt b/mpm/python/usrp_mpm/CMakeLists.txt new file mode 100644 index 000000000..ad3ce41fd --- /dev/null +++ b/mpm/python/usrp_mpm/CMakeLists.txt @@ -0,0 +1,30 @@ +# +# Copyright 2017 Ettus Research (National Instruments) +# +# 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 +######################################################################## + +SET(USRP_MPM_FILES +    ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py +    ${CMAKE_CURRENT_SOURCE_DIR}/types.py +    ${CMAKE_CURRENT_SOURCE_DIR}/discovery.py +    ${CMAKE_CURRENT_SOURCE_DIR}/rpc_process.py +    ${CMAKE_CURRENT_SOURCE_DIR}/rpc_server.py +    ${CMAKE_CURRENT_SOURCE_DIR}/periphs.py +    PARENT_SCOPE +    ) diff --git a/mpm/python/usrp_mpm/__init__.py b/mpm/python/usrp_mpm/__init__.py new file mode 100644 index 000000000..dafb6940c --- /dev/null +++ b/mpm/python/usrp_mpm/__init__.py @@ -0,0 +1,23 @@ +# +# Copyright 2017 Ettus Research (National Instruments) +# +# 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/>. +# +""" +MPM Module +""" + +from discovery import spawn_discovery_process +from rpc_process import spawn_rpc_process +import types diff --git a/mpm/python/usrp_mpm/discovery.py b/mpm/python/usrp_mpm/discovery.py new file mode 100644 index 000000000..727fe2b94 --- /dev/null +++ b/mpm/python/usrp_mpm/discovery.py @@ -0,0 +1,76 @@ +# +# Copyright 2017 Ettus Research (National Instruments) +# +# 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/>. +# +""" +Code to run the discovery port +""" + +from __future__ import print_function +import time +import ctypes +from multiprocessing import Process, Value +from six import iteritems +import socket +from types import MPM_DISCOVERY_PORT, graceful_exit + +RESPONSE_PREAMBLE = "USRP-MPM" +RESPONSE_SEP = ";" +RESPONSE_CLAIMED_KEY = "claimed" + + + +def spawn_discovery_process(device_info, shared_state): +    """ +    Returns a process that contains the device discovery. + +    Arguments: +    device_info -- A dictionary of type string -> string. All of these items +                   will be included in the response string. +    """ +    # claim_status = Value(ctypes.c_bool, False) +    p = Process(target=_discovery_process, args=(device_info, shared_state)) +    p.start() +    return p + + +def _discovery_process(device_info, state): +    """ +    The actual process for device discovery. Is spawned by +    spawn_discovery_process(). +    """ +    def create_response_string(): +        " Generate the string that gets sent back to the requester. " +        return RESPONSE_SEP.join( +            [RESPONSE_PREAMBLE] + \ +            ["{k}={v}".format(k=k, v=v) for k, v in iteritems(device_info)] + \ +            ["{k}={v}".format(k=RESPONSE_CLAIMED_KEY, v=state.claim_status.value)] + \ +            ["{k}={v}".format(k="token", v=state.claim_token.value)] +        ) + +    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +    sock.bind((("0.0.0.0", MPM_DISCOVERY_PORT))) + +    send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + +    try: +        while True: +            data, sender = sock.recvfrom(4096) +            if data == "MPM-DISC": +                send_data = create_response_string() +                send_sock.sendto(send_data, sender) +    except graceful_exit: +        sock.close() +        print("I'm done") diff --git a/mpm/python/usrp_mpm/periphs.py b/mpm/python/usrp_mpm/periphs.py new file mode 100644 index 000000000..3a78f224b --- /dev/null +++ b/mpm/python/usrp_mpm/periphs.py @@ -0,0 +1,71 @@ +# +# Copyright 2017 Ettus Research (National Instruments) +# +# 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/>. +# +""" + Module +""" + +import libpyusrp_periphs as lib +import re +import logging +log = logging.Logger("usrp_mpm.periphs") + + +def init_periph_manager(mb_type=None, db_types={}, fpga=None): +    # Detect motherboard type if not already specified +    if mb_type is None: +        mb_files = lib.helper.find_mb_file() +        with open(mb_files, "r") as f: +            info = "".join(f.readlines()) +            device_type = re.match("^.*USRP;([-\w])+;.*", info) +        if device_type is None: +            log.fatal("Could not determine device type from {}".format(info)) +            exit(1) +        mb_type = device_type.group(1) +    # Check if we have an implementation for this motherboard type +    try: +        device_class = getattr(lib, mb_type) +    except AttributeError: +        log.fatal("Motherboard class implementation for {} device not found!".format(mb_type)) +        exit(1) + +    # Detect daughterboard type if not already specified +    if not db_types: +        db_files = lib.helper.find_db_files() +        db_types = {} +        for db in db_files: +            with open(db, "r") as f: +                info = "".join(f.readlines()) +                device_type = re.match("^.*SLOT;([\w]);DB;([-\w])+;.*", info) +                if device_type is None: +                    log.fatal("Could not determine device type from: {}".format(info)) +                    exit(2) +                slot = device_type.group(1) +                db_type = device_type.group(2) +                db_types.update({slot: db_type}) +    # Check if we have an implementation for the daughterboard types +    for db in db_types.values(): +        try: +            getattr(lib.db, db) +        except AttributeError: +            log.fatal("Daughterboard class implementation for {} device not found!".format(db)) +            exit(1) + +    # Next steps +    # +    # 1. Load FPGA image +    # 2. Use motherboard and daughterboard types to load the FPGA image +    # 3. Create periph_manager object wth given mb_type + db_types information diff --git a/mpm/python/usrp_mpm/rpc_process.py b/mpm/python/usrp_mpm/rpc_process.py new file mode 100644 index 000000000..b8783f325 --- /dev/null +++ b/mpm/python/usrp_mpm/rpc_process.py @@ -0,0 +1,48 @@ +# +# Copyright 2017 Ettus Research (National Instruments) +# +# 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/>. +# +""" +Code to run the discovery port +""" + +from __future__ import print_function +from multiprocessing import Process + +from gevent.server import StreamServer +from mprpc import RPCServer +from types import graceful_exit, MPM_RPC_PORT +import rpc_server + + + +def spawn_rpc_process(server, state, udp_port=MPM_RPC_PORT): +    """ +    Returns a process that contains the RPC server +    """ + +    p_args = [server, udp_port, state] +    p = Process(target=_rpc_server_process, args=p_args) +    p.start() +    return p + + +def _rpc_server_process(server, shared_state, port): +    try: +        rpc_class = getattr(rpc_server, server+"Server") +        server = StreamServer(('0.0.0.0', port), handle=rpc_class(shared_state)) +        server.serve_forever() +    except graceful_exit: +        server.close() diff --git a/mpm/python/usrp_mpm/rpc_server.py b/mpm/python/usrp_mpm/rpc_server.py new file mode 100644 index 000000000..b092abdf7 --- /dev/null +++ b/mpm/python/usrp_mpm/rpc_server.py @@ -0,0 +1,69 @@ +# +# Copyright 2017 Ettus Research (National Instruments) +# +# 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/>. +# +""" +Implemented RPC Servers +""" + +from __future__ import print_function +from mprpc import RPCServer +from usrp_mpm import periphs + + +class EchoServer(RPCServer): +    def echo(self, arg): +        print(arg) +        return arg + + +class ClaimServer(RPCServer): +    def __init__(self, state): +        self._state = state +        super(ClaimServer, self).__init__() + +    def claim(self, token): +        'claim `token` - claims the MPM device with given token' +        if self._state.claim_status.value: +            if self._state.claim_token.value == token: +                return True +            return False +        self._state.claim_status.value = True +        self._state.claim_token.value = token +        return True + +    def unclaim(self, token): +        'unclaim `token` - unclaims the MPM device if it is claimed with this token' +        if self._state.claim_status.value and self._state.claim_token.value == token: +            self._state.claim_status.value = False +            self._state.claim_token.value = "" +            return True +        return False + +    def list_methods(self): +        methods = filter(lambda entry: not entry.startswith('_'), dir(self)) # Return public methods +        methods_with_docs = map(lambda m: (m, getattr(self,m).__doc__), methods) +        return methods_with_docs + + +class MPMServer(RPCServer): +    def __init__(self, state): +        # Instead do self.mboard = periphs.init_periph_manager(args...) +        self.mboard = periphs.lib.n3xx.periph_manager("/dev/spidev1.0", "") + +    def get_clock_id(self, dboard): +        dboard = getattr(self.mboard, "get_dboard_"+dboard) +        clk = dboard.get_clock_gen() +        return clk.get_chip_id() diff --git a/mpm/python/usrp_mpm/types.py b/mpm/python/usrp_mpm/types.py new file mode 100644 index 000000000..bd7a874a8 --- /dev/null +++ b/mpm/python/usrp_mpm/types.py @@ -0,0 +1,38 @@ +# +# Copyright 2017 Ettus Research (National Instruments) +# +# 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/>. +# +""" +MPM types +""" +from multiprocessing import Value +from multiprocessing import Array +from multiprocessing import Lock +import ctypes + +MPM_RPC_PORT = 49601 +MPM_DISCOVERY_PORT = 49600 + +MPM_DISCOVERY_MESSAGE = "MPM-DISC" + +class graceful_exit(Exception): +    pass + + +class shared_state: +    def __init__(self): +        self.lock = Lock() +        self.claim_status = Value(ctypes.c_bool, False, lock=self.lock) # lock +        self.claim_token = Array(ctypes.c_char, 32, lock=self.lock) # String with max length of 32  | 
