diff options
108 files changed, 15158 insertions, 514 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 271e93cd1d..d355b7e801 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,8 +46,8 @@ message(STATUS "Build type set to ${CMAKE_BUILD_TYPE}.") # Set the version information here set(VERSION_INFO_MAJOR_VERSION 3) set(VERSION_INFO_API_COMPAT 7) -set(VERSION_INFO_MINOR_VERSION 7) -set(VERSION_INFO_MAINT_VERSION 0) +set(VERSION_INFO_MINOR_VERSION 8) +set(VERSION_INFO_MAINT_VERSION git) include(GrVersion) #setup version info # Append -O2 optimization flag for Debug builds @@ -409,6 +409,14 @@ add_subdirectory(gr-zeromq) if(ENABLE_GR_CTRLPORT) set(GR_CTRLPORT True) add_definitions(-DGR_CTRLPORT) + + if(CTRLPORT_BACKENDS GREATER 0) + set(GR_RPCSERVER_ENABLED True) + + if(THRIFT_FOUND) + set(GR_RPCSERVER_THRIFT True) + endif(THRIFT_FOUND) + endif(CTRLPORT_BACKENDS GREATER 0) endif(ENABLE_GR_CTRLPORT) # Install our Cmake modules into $prefix/lib/cmake/gnuradio diff --git a/cmake/Modules/FindThrift.cmake b/cmake/Modules/FindThrift.cmake new file mode 100644 index 0000000000..f12bce01e6 --- /dev/null +++ b/cmake/Modules/FindThrift.cmake @@ -0,0 +1,79 @@ +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(PC_THRIFT thrift) + +set(THRIFT_REQ_VERSION "0.9.2") + +# If pkg-config found Thrift and it doesn't meet our version +# requirement, warn and exit -- does not cause an error; just doesn't +# enable Thrift. +if(PC_THRIFT_FOUND AND PC_THRIFT_VERSION VERSION_LESS ${THRIFT_REQ_VERSION}) + message(STATUS "Could not find appropriate version of Thrift: ${PC_THRIFT_VERSION} < ${THRIFT_REQ_VERSION}") + return() +endif(PC_THRIFT_FOUND AND PC_THRIFT_VERSION VERSION_LESS ${THRIFT_REQ_VERSION}) + + +# Else, look for it ourselves + +FIND_PATH(THRIFT_INCLUDE_DIRS + NAMES thrift/Thrift.h + HINTS ${PC_THRIFT_INCLUDE_DIR} + ${CMAKE_INSTALL_PREFIX}/include + PATHS + /usr/local/include + /usr/include + ) + +FIND_LIBRARY(THRIFT_LIBRARIES + NAMES thrift + HINTS ${PC_THRIFT_LIBDIR} + ${CMAKE_INSTALL_PREFIX}/lib + ${CMAKE_INSTALL_PREFIX}/lib64 + PATHS + ${THRIFT_INCLUDE_DIRS}/../lib + /usr/local/lib + /usr/lib + ) + +# Get the thrift binary to build our files during cmake +FIND_PROGRAM(THRIFT_BIN thrift) + +# Use binary to get version string and test against THRIFT_REQ_VERSION +EXECUTE_PROCESS( + COMMAND ${THRIFT_BIN} --version + OUTPUT_VARIABLE THRIFT_VERSION + ERROR_VARIABLE THRIFT_VERSION_ERROR + ) + +if(NOT THRIFT_BIN) + message(STATUS "Binary 'thrift' not found.") + return() +endif(NOT THRIFT_BIN) + +STRING(REGEX MATCH "[0-9]+.[0-9]+.[0-9]+" + THRIFT_VERSION "${THRIFT_VERSION}") + +if(THRIFT_VERSION VERSION_LESS THRIFT_REQ_VERSION) + message(STATUS "Could not find appropriate version of Thrift: ${THRIFT_VERSION} < ${THRIFT_REQ_VERSION}") + return() +endif(THRIFT_VERSION VERSION_LESS THRIFT_REQ_VERSION) + + +# Check that Thrift for Python is available +include(GrPython) +GR_PYTHON_CHECK_MODULE("Thrift" thrift "1" PYTHON_THRIFT_FOUND) + +# Set to found if we've made it this far +if(THRIFT_INCLUDE_DIRS AND THRIFT_LIBRARIES AND PYTHON_THRIFT_FOUND) + set(THRIFT_FOUND TRUE CACHE BOOL "If Thift has been found") +endif(THRIFT_INCLUDE_DIRS AND THRIFT_LIBRARIES AND PYTHON_THRIFT_FOUND) + + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(THRIFT DEFAULT_MSG + THRIFT_LIBRARIES THRIFT_INCLUDE_DIRS + THRIFT_BIN PYTHON_THRIFT_FOUND THRIFT_FOUND +) +MARK_AS_ADVANCED( + THRIFT_LIBRARIES THRIFT_INCLUDE_DIRS + THRIFT_BIN PYTHON_THRIFT_FOUND THRIFT_FOUND +) diff --git a/cmake/Modules/GrTest.cmake b/cmake/Modules/GrTest.cmake index 62caab4b51..ff78ed2726 100644 --- a/cmake/Modules/GrTest.cmake +++ b/cmake/Modules/GrTest.cmake @@ -66,7 +66,8 @@ function(GR_ADD_TEST test_name) file(TO_NATIVE_PATH "${GR_TEST_LIBRARY_DIRS}" libpath) #ok to use on dir list? file(TO_NATIVE_PATH "${GR_TEST_PYTHON_DIRS}" pypath) #ok to use on dir list? - set(environs "VOLK_GENERIC=1" "GR_DONT_LOAD_PREFS=1" "srcdir=${srcdir}") + set(environs "VOLK_GENERIC=1" "GR_DONT_LOAD_PREFS=1" "srcdir=${srcdir}" + "GR_CONF_CONTROLPORT_ON=False") list(APPEND environs ${GR_TEST_ENVIRONS}) #http://www.cmake.org/pipermail/cmake/2009-May/029464.html diff --git a/config.h.in b/config.h.in index 82751a3cf7..ad6e3d022c 100644 --- a/config.h.in +++ b/config.h.in @@ -30,6 +30,12 @@ #ifndef GR_CTRLPORT #cmakedefine GR_CTRLPORT #endif +#ifndef GR_RPCSERVER_ENABLED +#cmakedefine GR_RPCSERVER_ENABLED +#endif +#ifndef GR_RPCSERVER_THRIFT +#cmakedefine GR_RPCSERVER_THRIFT +#endif #ifndef ENABLE_GR_LOG #cmakedefine ENABLE_GR_LOG #endif diff --git a/docs/doxygen/other/build_guide.dox b/docs/doxygen/other/build_guide.dox index 56bcce7f96..8fc3f911fb 100644 --- a/docs/doxygen/other/build_guide.dox +++ b/docs/doxygen/other/build_guide.dox @@ -63,7 +63,7 @@ first. Most recent systems have these packages available. \li audio-osx \li audio-windows -* Optional but recommended dependencies. +<b>Optional but recommended dependencies.</b> It is not necessary to satisfy all of these dependencies; just the one(s) that are right for your system. On Linux, don't expect @@ -82,6 +82,17 @@ audio-osx and audio-windows to be either satisfied or built. \li log4cpp (>= 1.0) http://log4cpp.sourceforge.net/ +<b>Optional</b> + +\ref page_ctrlport may use various backends to perform the RPC +process, and each is its own dependency. + +Currently, ControlPort only supports the Apache Thrift backend. + +\li thrift (>= 0.9.2) https://thrift.apache.org/ + + + \section build_gr_cmake Building GNU Radio GNU Radio is built using the CMake build system diff --git a/docs/doxygen/other/ctrlport.dox b/docs/doxygen/other/ctrlport.dox index 64bf9f7d38..94a768e429 100644 --- a/docs/doxygen/other/ctrlport.dox +++ b/docs/doxygen/other/ctrlport.dox @@ -16,8 +16,427 @@ gnuradio.ctrlport module, imported as: from gnuradio import ctrlport \endcode -ControlPort is currently a temporary stub implementation of a set of -RPC calls we would like to enable that would allow remote viewing, -command, and control of GNU Radio flowgraphs and blocks. + +\section ctrlport_conf Configuration + +ControlPort is configured using two files. The first is the GNU Radio +preferences file while the second file is specific to the type of +middleware used. + +The GNU Radio preferences file has three options. The 'on' option is +used to enable or disable the use of ControlPort, and is disabled by +default. The 'config' option allows a user to specify the +middleware-specific configuration file. The 'edges_list' is a special +option that exports the list of nodes and edges of the flowgraph +across ControlPort. This latter option is mainly used for redrawing +the flowgraph for the Performance Counter applications. + +\code + [ControlPort] + on = True + edges_list = True + config = path-to/ctrlport.conf +\endcode + +The ControlPort preferences are installed by default into +'gnuradio-runtime.conf'. These can always be overridden in the local +~/.gnuradio/config.conf file. + + + +\section ctrlport_deps Dependencies + +ControlPort is an abstracted remote procedure call tool that. It is +built on top of other middleware libraries. The following subsections +explain some details about the use of the particular middleware +project. + +Currently, the only implemented middleware library is the Apache +Thrift project. + +\subsection ctrlport_thrift Apache Thrift + +Current version support: >= 0.9.2 + +Apache Thrift is a middleware layer that defines interfaces of a +program using its own Thrift language. GNU Radio's interface file is: + +gnuradio-runtime/lib/controlport/thrift/gnuradio.thrift + +This file defines the interfaces set, get, trigger, and properties. It +also defines a set of data structure Knobs to allow us to pass any +type of data over the interfaces. + +To use Thrift in ControlPort requires a minimum Thrift version of +0.9.0. If a Thrift version greater than or equal to this version is +not found, the Thrift backend to ControlPort will not be installed, +through ControlPort itself still will be. During cmake configuration +time, it prints out information about finding Thrift and requires: + +\li Thrift header files by looking for thrift/Thrift.h +\li Thrift C++ libraries: libthrift.so +\li Thrift Python bindings: "import thrift" + +If all of these are not satisfied, the Thrift backend will not be +installed. Upon completion, cmake outputs a notification of what +components will be built. You will see this if Thrift was found and +can be used: + +\code +* gr-ctrlport +* * thrift +\endcode + +Cmake also uses the Thrift compiler ("thrift") to build the C++ and +Python files necessary for compiling ControlPort. It runs "thrift +--gen cpp" the C++ bindings in the build directory, and then +it runs "thrift --gen py" to build the Python bindings, also in the +build directory. These are used to compile the Thrift ControlPort +features and are necessary files to run the Python clients. If cmake +fails to produce these bindings, it should error out. + + + +\subsubsection ctrlport_thrift_prefs Configuration + +Thrift does not support its own concept of a configuration file, so we +have built one for our purposes in GNU Radio. The 'config' option in +the ControlPort section of the preference files tells ControlPort +where to find the backend-specific file format. GNU Radio's Thrift +format follows the same "[Section] key = value" scheme used in all of +its other preference files. Currently supported configuration options +are: + +\code +[thrift] +port = 9090 +nthreads = 2 +buffersize = 1434 +init_attempts = 100 +\endcode + + +\subsubsection ctrlport_thrift_issues Thrift: Current Issues + +Thrift uses a thread pool system to handle each connection, but it +will only allow up to a specified number of threads in the server. The +default value is 10 threads, but the Thrift configuration file allows +the user to change this value. + +Thrift also does not find and use a free ephemeral port when launching +the server. It must be told explicitly which port to launch on, which +we set in the configuration file. This makes it difficult to launch +multiple flowgraphs on the same machine because that will cause a port +collision. Until this is fixed, a way around this is to use the +environmental variable GR_CONF_THRIFT_PORT=xxx to set the port number +for that specific application. + +Efficiency issues of Thrift come from the over-the-wire formatting +done by the transport protocol. It defaults to using 512 byte packets, +which can lead to a lot of fragmentation of the data over the +connection. The buffersize configuration allows the user to set this +value to whatever number fits their network needs. The default 1434 is +designed around the standard 1500 byte Ethernet frame size limit minus +the TCP/IP and Ethernet header size. + + +\subsection ctrlport_client_translation Translation Layer for Clients + +Different backends will produce different ways to interface with the +system. ControlPort in the running flowgraph acts as the server by +exposing interfaces to blocks. The interfaces and API in GNU Radio to +communicate with ControlPort are all abstracted completely away from +the backend methods and data types. That is, the code in GNU Radio's +scheduler and in the blocks that expose their ControlPort interfaces +will work regardless of the backend used. + +We are building better abstractions on the clients sides now, as +well. Although certain backends will support other features of +discovery and services that work well with their products, GNU Radio +wants to make sure that clients can access the data from the +interfaces in the same way for any backend used. This abstraction is +done through the GNURadioControlPortClient. This class is told which +type of backend is used, and defaults to Thrift, and can be passed +information about the server's endpoint such as the host name and port +number to attach to. The GNURadioControlPortClient returns a 'radio' +object that represents the connection to the running flowgraph. + + +\section ctrlport_using Using ControlPort to Export Variables + +The ability to export variables from a block is inherited from +gr::block. Then, when the flowgraph is started, the function +<b>setup_rpc()</b> is called in turn for each block. By default, this +is an empty function. A block overloads this function and defines and +exports variables in it. + +Say we have a class <b>gr::blocks::foo</b> that has variables <b>a</b> +and <b>b</b> that we want to export. Specifically, we want to be able +to read the values of both <b>a</b> and <b>b</b> and also set the +value of <b>b</b>. The class <b>gr::blocks::foo</b> has setters and +getters all set up. So our class implementation header file looks +something like: + +\code +namespace gr { + namespace blocks { + + class foo_impl : public foo + { + private: + float d_a, d_b; + + public: + foo_impl(float a, float b); + ~foo_impl(); + + float a() const { return d_a; } + float b() const { return d_a; } + void set_a(float a) { d_a = a; } + void set_b(float b) { d_b = b; } + void setup_rpc(); + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace blocks */ +} /* namespace gr */ +\endcode + +The source code then sets up the class and fills in +<b>setup_rpc()</b>. + +\code +namespace gr { + namespace blocks { + + foo_impl::foo_impl(float a, float b): + sync_bloc(....), + d_a(a), d_b(b) + { } + + foo_impl::~foo_impl() + { } + + void + foo_impl::setup_rpc() + { +#ifdef GR_CTRLPORT + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_get<foo, float>( + alias(), "a", + &foo::a, + pmt::mp(-2.0f), pmt::mp(2.0f), pmt::mp(0.0f), + "", "Get value of a", RPC_PRIVLVL_MIN, + DISPTIME | DISPOPTSTRIP))); + + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_get<foo, float>( + alias(), "b", + &foo::b, + pmt::mp(0.0f), pmt::mp(20.0f), pmt::mp(10.0f), + "", "Get value of b", RPC_PRIVLVL_MIN, + DISPTIME | DISPOPTSTRIP))); + + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_set<foo, float>( + alias(), "b", + &foo::set_b, + pmt::mp(0.0f), pmt::mp(20.0f), pmt::mp(10.0f), + "", "Set value of b", RPC_PRIVLVL_MIN, + DISPNULL))); +#endif /* GR_CTRLPORT */ + } + + int + foo_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { .... } + + } /* namespace blocks */ +} /* namespace gr */ +\endcode + +In the above example, we're ignoring some of the basic semantics of +the class as a GNU Radio block and focus just on the call to set up +the get and set functions over ControlPort. Each block has a function +that allows us to add a new ControlPort interface object to a list, +the <b>add_rpc_variable</b>. We don't care about that list anymore; +that's for ControlPort to worry about. We just add new variables, +either setters or getters. + +Without dissecting every piece of the above calls, notice that we use +the public class, <b>gr::blocks::foo</b> as the class, not the +implementation class. We also use the block's alias, which GNU Radio +uses as a database entry to connect a block by name to the pointer in +memory. This allows ControlPort to know where the object in memory is +at any given time to access the setters and getters. + +The three PMTs specified are simply an expected minimum, maximum, and +default value. None of these are strictly enforced and only serve as +guides. The RPC_PRIVLVL_MIN is currently a placeholder for a +privilege level setting. In many cases, reading <b>b</b> might be +fine for everyone, but we want strong restrictions on who has the +ability to set <b>b</b>. + +And finally, we can specify display options to hint at the right way +to display this variable when remotely plotting it. More on that in +the following section. + +Finally, note that we put \#ifdefs around the code. We always want +<b>setup_rpc</b> to be there and callable, but if ControlPort was not +built for GNU Radio, we cannot register any variables with it. This is +just a nicety to allow us to set up our code for use with ControlPort +without requiring it. + + +\subsection ctrlport_alt_reg Alternative Registers + +If using the concept above, <b>setup_rpc</b> automatically gets called +when the flowgraph is started. In most instances, this is all we ever +need since there's nothing interesting going on until then. However, +if not using a gr::block or needing access before we run the flowgraph, +the above method won't work (it comes down to when the block's alias +has meaning). + +There are alternate variable registration functions for the sets and +gets. These take the form: + +\code + rpcbasic_register_get(const std::string& name, + const char* functionbase, + T* obj, + Tfrom (T::*function)(), + const pmt::pmt_t &min, const pmt::pmt_t &max, const pmt::pmt_t &def, + const char* units_ = "", + const char* desc_ = "", + priv_lvl_t minpriv_ = RPC_PRIVLVL_MIN, + DisplayType display_ = DISPNULL) + + rpcbasic_register_set(const std::string& name, + const char* functionbase, + T* obj, + void (T::*function)(Tto), + const pmt::pmt_t &min, const pmt::pmt_t &max, const pmt::pmt_t &def, + const char* units_ = "", + const char* desc_ = "", + priv_lvl_t minpriv_ = RPC_PRIVLVL_MIN, + DisplayType display_ = DISPNULL) +\endcode + +The only thing different about the above code is that instead of +taking a single 'alias()' name, which provides us access to the +objects pointer, we instead provide a unique name +(<b>fucntionbase</b>) and a pointer to the object itself +(<b>obj</b>). These are templated functions, so the class T is known +from that. + +If using this method, the recommended way is to create a new function +(not <b>setup_rpc</b>), register the variable using +<b>add_rpc_variable</b> but with the different <b>register_get/set</b> +shown here, and then call this function either in the object's +constructor or make it a public member function to be called when you +need it. + + +\section ctrlport_disp Display Options + +When exporting a new RPC variable over ControlPort, one argument is a +display options mask. These options are useful to a remote client to +tell identify activities like default plotters and initial +conditions. The <b>gr-ctrlport-monitor</b> application uses this +heavily in determining how to plot ControlPort variables. + +The options mask is just a 32-bit value with options OR'd +together. Certain options are only appropriate for certain types of +plots. Options on plots where that option is not available will +simply be ignored. + +The main caveat to be aware of is that the DISPXY plot type is +specific to complex values. Therefore, DISPOPTCPLX is assumed. + +These options are specified in rpccallbackregister_base.h and are +exposed through SWIG to live in the \b gr namespace. + +<b>Plot Types</b> +\li <b>DISPNULL:</b> Nothing specified. +\li <b>DISPTIME:</b> Time-domain plot. +\li <b>DISPXY:</b> XY or constellation plot (complex only). +\li <b>DISPPSD:</b> PSD plot. +\li <b>DISPSPEC:</b> Spectrogram plot. +\li <b>DISPRAST:</b> Time raster plot (non-complex only) + +<b>Plot Options</b> +\li <b>DISPOPTCPLX:</b> Signal is complex. +\li <b>DISPOPTLOG:</b> Start plot in semilog-y mode (time domain only). +\li <b>DISPOPTSTEM:</b> Start plot in stem mode (time domain only). +\li <b>DISPOPTSTRIP:</b> Run plot as a stripchart (time domain only). +\li <b>DISPOPTSCATTER:</b> Do scatter plot instead of lines (XY plot only). + + +\section ctrlport_probes ControlPort Probes + +ControlPort provides a set of probes that can be used as sinks that +pass vectors of data across ControlPort. These probes are used to +sample or visualize data remotely. We can place a ControlPort probe +anywhere in the flowgraph to grab the latest sample of data from the +block it's connected to. + +The main ControlPort probe to use is +<b>blocks.ctrlport_probe2_x</b>. From GRC, this is simply "CtrlPort +Probe", which can handle complex, floats, ints, shorts, and bytes. The +blocks are named and given a description to identify them over +ControlPort. The blocks also take a vector length for how many samples +to pass back at a time. Finally, these blocks take a display hint, +as described in the above section. This allows us to specify the +default behavior for how to display the samples. + +Another block that can be used is the <b>fft.ctrlport_probe_psd</b> to +calculate the PSD and pass that over the ControlPort interface. + +\section ctrlport_monitors ControlPort Monitors + +There are two main ControlPort monitor applications provided with GNU +Radio. Both act similarly. The first is a standard ControlPort monitor +application. This connects to a running flowgraph and displays all +exported interfaces in a table format. The name, unit, latest sample, +and description of all interfaces are display in a +row. Double-clicking will open up the default display. Right clicking +any item will allow the user to select the type of plot to use to +display the data. + +When a display is active, using the buttons at the top, the subwindows +can all be tiled or windowed as needed to manage the full +interface. We can then drag-and-drop any other item on top of a +currently running display plot. + +To launch the ControlPort monitor application, know the IP address and +port of the ControlPort endpoint established by the flowgraph and run: + +<pre> +gr-ctrlport-monitor \<ip-addr\> -p \<port\> +</pre> + + +\subsection perfmonitor Performance Monitor + +A second application is used to locally redraw the flowgraph and +display some of the Performance Counters. In this application, the +nodes are blue boxes where the size of the box is proportional to the +work time and the color depth and line width are proportional to the +output buffer fullness. + +The controls at the top of the Performance Monitor application allow +us to select the instantaneous, average, and variance values of the +Performance Counters. And the work time and buffer fullness can be +displayed as a table or bar graph. + +To launch the Performance Monitor, run: + +<pre> +gr-perf-monitorx \<ip-addr\> -p \<port\> +</pre> */ diff --git a/docs/doxygen/other/perf_counters.dox b/docs/doxygen/other/perf_counters.dox index 1a5bf40cba..9bca38268a 100644 --- a/docs/doxygen/other/perf_counters.dox +++ b/docs/doxygen/other/perf_counters.dox @@ -85,4 +85,14 @@ The options for the [PerfCounters] section are: \li clock: sets the type of clock used when calculating work_time ('thread' or 'monotonic'). + +\section pc_perfmonitor Performance Monitor + +See \ref perfmonitor for some details of using a ControlPort-based +monitor application, gr-perf-monitorx, for visualizing the +counters. This application is particularly useful in learning which +blocks are the computationally complex blocks that could use extra +optimization or work to improve their performance. It can also be used +to understand the current 'health' of the application. + */ diff --git a/gnuradio-runtime/include/gnuradio/CMakeLists.txt b/gnuradio-runtime/include/gnuradio/CMakeLists.txt index 3fc2fe7bd8..472f91847b 100644 --- a/gnuradio-runtime/include/gnuradio/CMakeLists.txt +++ b/gnuradio-runtime/include/gnuradio/CMakeLists.txt @@ -86,6 +86,16 @@ install(FILES COMPONENT "runtime_devel" ) +if(THRIFT_FOUND) +install(FILES + rpcserver_booter_thrift.h + thrift_application_base.h + thrift_server_template.h + DESTINATION ${GR_INCLUDE_DIR}/gnuradio + COMPONENT "runtime_devel" +) +endif(THRIFT_FOUND) + ########################################################################## # Configure logger ########################################################################## diff --git a/gnuradio-runtime/include/gnuradio/prefs.h b/gnuradio-runtime/include/gnuradio/prefs.h index a9a28586ab..4dc92b3631 100644 --- a/gnuradio-runtime/include/gnuradio/prefs.h +++ b/gnuradio-runtime/include/gnuradio/prefs.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2006,2013 Free Software Foundation, Inc. + * Copyright 2006,2013,2015 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -47,10 +47,38 @@ namespace gr { public: static prefs *singleton(); + /*! + * \brief Creates an object to read preference files. + * + * \details + * + * If no file name is given (empty arg list or ""), this opens up + * the standard GNU Radio configuration files in + * prefix/etc/gnuradio/conf.d as well as ~/.gnuradio/config.conf. + * + * Only access this through the singleton defined here: + * \code + * prefs *p = prefs::singleton(); + * \endcode + */ prefs(); + virtual ~prefs(); /*! + * If specifying a file name, this opens that specific + * configuration file of the standard form containing sections and + * key-value pairs: + * + * \code + * [SectionName] + * key0 = value0 + * key1 = value1 + * \endcode + */ + void add_config_file(const std::string &configfile); + + /*! * \brief Returns the configuration options as a string. */ std::string to_string(); @@ -137,7 +165,7 @@ namespace gr { protected: virtual std::vector<std::string> _sys_prefs_filenames(); - virtual void _read_files(); + virtual std::string _read_files(const std::vector<std::string> &filenames); virtual void _convert_to_map(const std::string &conf); virtual char * option_to_env(std::string section, std::string option); diff --git a/gnuradio-runtime/include/gnuradio/rpcbufferedget.h b/gnuradio-runtime/include/gnuradio/rpcbufferedget.h new file mode 100644 index 0000000000..ad05551d1a --- /dev/null +++ b/gnuradio-runtime/include/gnuradio/rpcbufferedget.h @@ -0,0 +1,64 @@ +/* -*- c++ -*- */ +/* + * Copyright 2015 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef RPCBUFFEREDGET_H +#define RPCBUFFEREDGET_H + +#include <boost/thread/condition_variable.hpp> +#include <boost/thread/mutex.hpp> + +template<typename TdataType> +class rpcbufferedget { +public: + rpcbufferedget(const unsigned int init_buffer_size = 4096) : + d_data_needed(false), d_data_ready(), d_buffer_lock(), d_buffer(init_buffer_size) {;} + + ~rpcbufferedget() { + d_data_ready.notify_all(); + } + + void offer_data(const TdataType& data) { + if (!d_data_needed) + return; + { + boost::mutex::scoped_lock lock(d_buffer_lock); + d_buffer = data; + d_data_needed = false; + } + d_data_ready.notify_one(); + } + + TdataType get() { + boost::mutex::scoped_lock lock(d_buffer_lock); + d_data_needed = true; + d_data_ready.wait(lock); + return d_buffer; + } + +private: + bool d_data_needed; + boost::condition_variable d_data_ready; + boost::mutex d_buffer_lock; + TdataType d_buffer; +}; + +#endif diff --git a/gnuradio-runtime/include/gnuradio/rpcmanager.h b/gnuradio-runtime/include/gnuradio/rpcmanager.h index 5635572a8b..e7ee4c4942 100644 --- a/gnuradio-runtime/include/gnuradio/rpcmanager.h +++ b/gnuradio-runtime/include/gnuradio/rpcmanager.h @@ -54,7 +54,7 @@ class GR_RUNTIME_API rpcmanager : public virtual rpcmanager_base static bool booter_registered; static bool aggregator_registered; static void rpcserver_booter_base_sptr_dest(rpcserver_booter_base* b) {;} - static rpcserver_booter_base* boot; + static std::auto_ptr<rpcserver_booter_base> boot; static std::auto_ptr<rpcserver_booter_aggregator> aggregator; }; diff --git a/gnuradio-runtime/include/gnuradio/rpcpmtconverters_thrift.h b/gnuradio-runtime/include/gnuradio/rpcpmtconverters_thrift.h new file mode 100644 index 0000000000..6523165a11 --- /dev/null +++ b/gnuradio-runtime/include/gnuradio/rpcpmtconverters_thrift.h @@ -0,0 +1,74 @@ +/* + * Copyright 2014,2015 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef RPCPMTCONVERTERS_THRIFT_H +#define RPCPMTCONVERTERS_THRIFT_H + +#include <pmt/pmt.h> +#include <boost/noncopyable.hpp> +#include <boost/ptr_container/ptr_map.hpp> +#include "thrift/gnuradio_types.h" + + +namespace rpcpmtconverter +{ + GNURadio::Knob from_pmt(const pmt::pmt_t& knob); + + struct to_pmt_f { + to_pmt_f() {;} + virtual pmt::pmt_t operator()(const GNURadio::Knob& knob); + }; + + struct to_pmt_byte_f : public to_pmt_f { pmt::pmt_t operator()(const GNURadio::Knob& knob); }; + struct to_pmt_short_f : public to_pmt_f { pmt::pmt_t operator()(const GNURadio::Knob& knob); }; + struct to_pmt_int_f : public to_pmt_f { pmt::pmt_t operator()(const GNURadio::Knob& knob); }; + struct to_pmt_long_f : public to_pmt_f { pmt::pmt_t operator()(const GNURadio::Knob& knob); }; + struct to_pmt_double_f : public to_pmt_f { pmt::pmt_t operator()(const GNURadio::Knob& knob); }; + struct to_pmt_string_f : public to_pmt_f { pmt::pmt_t operator()(const GNURadio::Knob& knob); }; + struct to_pmt_bool_f : public to_pmt_f { pmt::pmt_t operator()(const GNURadio::Knob& knob); }; + struct to_pmt_complex_f: public to_pmt_f { pmt::pmt_t operator()(const GNURadio::Knob& knob); }; + struct to_pmt_f32vect_f: public to_pmt_f { pmt::pmt_t operator()(const GNURadio::Knob& knob); }; + struct to_pmt_f64vect_f: public to_pmt_f { pmt::pmt_t operator()(const GNURadio::Knob& knob); }; + struct to_pmt_s64vect_f: public to_pmt_f { pmt::pmt_t operator()(const GNURadio::Knob& knob); }; + struct to_pmt_s32vect_f: public to_pmt_f { pmt::pmt_t operator()(const GNURadio::Knob& knob); }; + struct to_pmt_s16vect_f: public to_pmt_f { pmt::pmt_t operator()(const GNURadio::Knob& knob); }; + struct to_pmt_s8vect_f : public to_pmt_f { pmt::pmt_t operator()(const GNURadio::Knob& knob); }; + struct to_pmt_c32vect_f: public to_pmt_f { pmt::pmt_t operator()(const GNURadio::Knob& knob); }; + + class To_PMT : private boost::noncopyable { + public: + static To_PMT instance; + template<typename TO_PMT_F> friend struct to_pmt_reg; + pmt::pmt_t operator()(const GNURadio::Knob& knob); + + protected: + boost::ptr_map<GNURadio::BaseTypes::type, to_pmt_f> to_pmt_map; + + private: + To_PMT() {;} + }; + + template<typename TO_PMT_F> struct to_pmt_reg { + to_pmt_reg(To_PMT& instance, const GNURadio::BaseTypes::type type); + }; +} + +#endif /* RPCPMTCONVERTERS_THRIFT_H */ diff --git a/gnuradio-runtime/include/gnuradio/rpcregisterhelpers.h b/gnuradio-runtime/include/gnuradio/rpcregisterhelpers.h index 0999dea747..f82f5ed0aa 100644 --- a/gnuradio-runtime/include/gnuradio/rpcregisterhelpers.h +++ b/gnuradio-runtime/include/gnuradio/rpcregisterhelpers.h @@ -672,7 +672,7 @@ struct rpcbasic_register_set : public rpcbasic_base d_minpriv = minpriv_; d_display = display_; d_object = dynamic_cast<T*>(global_block_registry.block_lookup(pmt::intern(block_alias)).get()); -#ifdef RPCSERVER_ENABLED +#ifdef GR_RPCSERVER_ENABLED callbackregister_base::configureCallback_t extractor(new rpcbasic_extractor<T,Tto>(d_object, function), minpriv_, std::string(units_), @@ -726,7 +726,7 @@ struct rpcbasic_register_set : public rpcbasic_base d_minpriv = minpriv_; d_display = display_; d_object = obj; -#ifdef RPCSERVER_ENABLED +#ifdef GR_RPCSERVER_ENABLED callbackregister_base::configureCallback_t extractor(new rpcbasic_extractor<T,Tto>(d_object, function), minpriv_, std::string(units_), @@ -741,7 +741,7 @@ struct rpcbasic_register_set : public rpcbasic_base ~rpcbasic_register_set() { -#ifdef RPCSERVER_ENABLED +#ifdef GR_RPCSERVER_ENABLED rpcmanager::get()->i()->unregisterConfigureCallback(d_id); #endif } @@ -830,7 +830,7 @@ struct rpcbasic_register_trigger : public rpcbasic_base d_desc = desc_; d_minpriv = minpriv_; d_object = dynamic_cast<T*>(global_block_registry.block_lookup(pmt::intern(block_alias)).get()); -#ifdef RPCSERVER_ENABLED +#ifdef GR_RPCSERVER_ENABLED callbackregister_base::configureCallback_t extractor(new rpcbasic_extractor<T,void>(d_object, function), minpriv_, std::string(desc_)); @@ -870,7 +870,7 @@ struct rpcbasic_register_trigger : public rpcbasic_base d_desc = desc_; d_minpriv = minpriv_; d_object = obj; -#ifdef RPCSERVER_ENABLED +#ifdef GR_RPCSERVER_ENABLED callbackregister_base::configureCallback_t extractor(new rpcbasic_extractor<T,void>(d_object, function), minpriv_, std::string(desc_)); @@ -884,7 +884,7 @@ struct rpcbasic_register_trigger : public rpcbasic_base ~rpcbasic_register_trigger() { -#ifdef RPCSERVER_ENABLED +#ifdef GR_RPCSERVER_ENABLED rpcmanager::get()->i()->unregisterConfigureCallback(d_id); #endif } @@ -988,7 +988,7 @@ public: d_minpriv = minpriv_; d_display = display_; d_object = dynamic_cast<T*>(global_block_registry.block_lookup(pmt::intern(block_alias)).get()); -#ifdef RPCSERVER_ENABLED +#ifdef GR_RPCSERVER_ENABLED callbackregister_base::queryCallback_t inserter(new rpcbasic_inserter<T,Tfrom>(d_object, function), minpriv_, std::string(units_), display_, std::string(desc_), min, max, def); @@ -1022,7 +1022,7 @@ public: d_minpriv = minpriv_; d_display = display_; d_object = dynamic_cast<T*>(global_block_registry.block_lookup(pmt::intern(block_alias)).get()); -#ifdef RPCSERVER_ENABLED +#ifdef GR_RPCSERVER_ENABLED callbackregister_base::queryCallback_t inserter(new rpcbasic_inserter<T,Tfrom>(d_object, (Tfrom (T::*)())function), minpriv_, std::string(units_), display_, std::string(desc_), min, max, def); @@ -1076,7 +1076,7 @@ public: d_minpriv = minpriv_; d_display = display_; d_object = obj; -#ifdef RPCSERVER_ENABLED +#ifdef GR_RPCSERVER_ENABLED callbackregister_base::queryCallback_t inserter(new rpcbasic_inserter<T,Tfrom>(d_object, function), minpriv_, std::string(units_), display_, std::string(desc_), min, max, def); @@ -1111,7 +1111,7 @@ public: d_minpriv = minpriv_; d_display = display_; d_object = obj; -#ifdef RPCSERVER_ENABLED +#ifdef GR_RPCSERVER_ENABLED callbackregister_base::queryCallback_t inserter(new rpcbasic_inserter<T,Tfrom>(d_object, (Tfrom (T::*)())function), minpriv_, std::string(units_), display_, std::string(desc_), min, max, def); @@ -1125,7 +1125,7 @@ public: ~rpcbasic_register_get() { -#ifdef RPCSERVER_ENABLED +#ifdef GR_RPCSERVER_ENABLED rpcmanager::get()->i()->unregisterQueryCallback(d_id); #endif } diff --git a/gnuradio-runtime/include/gnuradio/rpcserver_booter_thrift.h b/gnuradio-runtime/include/gnuradio/rpcserver_booter_thrift.h new file mode 100644 index 0000000000..fd1da09fa6 --- /dev/null +++ b/gnuradio-runtime/include/gnuradio/rpcserver_booter_thrift.h @@ -0,0 +1,51 @@ +/* -*- c++ -*- */ +/* + * Copyright 2015 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef RPCSERVER_BOOTER_THRIFT_H +#define RPCSERVER_BOOTER_THRIFT_H + +#include <gnuradio/rpcserver_booter_base.h> +#include <gnuradio/thrift_server_template.h> +#include "thrift/ControlPort.h" + +class rpcserver_base; +class rpcserver_thrift; + +class rpcserver_booter_thrift + : public virtual rpcserver_booter_base, + public virtual thrift_server_template<rpcserver_base, + rpcserver_thrift, + rpcserver_booter_thrift> +{ + public: + rpcserver_booter_thrift(); + ~rpcserver_booter_thrift(); + + rpcserver_base* i(); + const std::string & type() {return d_type;} + const std::vector<std::string> endpoints(); + + private: + std::string d_type; +}; + +#endif /* RPCSERVER_BOOTER_THRIFT_H */ diff --git a/gnuradio-runtime/include/gnuradio/rpcserver_selector.h b/gnuradio-runtime/include/gnuradio/rpcserver_selector.h index 8a14f78d99..31ab6cea0b 100644 --- a/gnuradio-runtime/include/gnuradio/rpcserver_selector.h +++ b/gnuradio-runtime/include/gnuradio/rpcserver_selector.h @@ -23,10 +23,12 @@ #ifndef RPCSERVER_SELECTOR #define RPCSERVER_SELECTOR -//#define RPCSERVER_ENABLED +#include <gnuradio/config.h> -//#define RPCSERVER_ICE -//#define RPCSERVER_ERLANG -//#define RPCSERVER_XMLRPC +//#define GR_RPCSERVER_ENABLED +//#define GR_RPCSERVER_ICE +//#define GR_RPCSERVER_THRIFT +//#define GR_RPCSERVER_ERLANG +//#define GR_RPCSERVER_XMLRPC #endif diff --git a/gnuradio-runtime/include/gnuradio/rpcserver_thrift.h b/gnuradio-runtime/include/gnuradio/rpcserver_thrift.h new file mode 100644 index 0000000000..203be66e9a --- /dev/null +++ b/gnuradio-runtime/include/gnuradio/rpcserver_thrift.h @@ -0,0 +1,233 @@ +/* -*- c++ -*- */ +/* + * Copyright 2014,2015 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef RPCSERVER_THRIFT_H +#define RPCSERVER_THRIFT_H + +#include <gnuradio/rpcserver_base.h> +#include <gnuradio/rpcpmtconverters_thrift.h> +#include <string> +#include <iostream> +#include <sstream> +#include <map> +#include "thrift/ControlPort.h" +#include "thrift/gnuradio_types.h" +#include <boost/format.hpp> +#include <boost/thread/mutex.hpp> + +#define S(x) #x +#define S_(x) S(x) +#define S__LINE__ S_(__LINE__) + +class rpcserver_thrift : public virtual rpcserver_base, public GNURadio::ControlPortIf +{ +public: + rpcserver_thrift(); + virtual ~rpcserver_thrift(); + + void registerConfigureCallback(const std::string &id, + const configureCallback_t callback); + void unregisterConfigureCallback(const std::string &id); + + void registerQueryCallback(const std::string &id, + const queryCallback_t callback); + void unregisterQueryCallback(const std::string &id); + + void setKnobs(const GNURadio::KnobMap&); + void getKnobs(GNURadio::KnobMap&, + const GNURadio::KnobIDList&); + void getRe(GNURadio::KnobMap&, + const GNURadio::KnobIDList&); + void properties(GNURadio::KnobPropMap&, + const GNURadio::KnobIDList& knobs); + virtual void shutdown(); + + private: + boost::mutex d_callback_map_lock; + + typedef std::map<std::string, configureCallback_t> ConfigureCallbackMap_t; + ConfigureCallbackMap_t d_setcallbackmap; + + typedef std::map<std::string, queryCallback_t> QueryCallbackMap_t; + QueryCallbackMap_t d_getcallbackmap; + + template<typename T, typename TMap> struct set_f + : public std::unary_function<T,void> + { + set_f(TMap &_setcallbackmap, const priv_lvl_t &_cur_priv) + : d_setcallbackmap(_setcallbackmap), cur_priv(_cur_priv) + { + ; + } + + void operator()(const T& p) + { + ConfigureCallbackMap_t::const_iterator iter(d_setcallbackmap.find(p.first)); + if(iter != d_setcallbackmap.end()) { + if(cur_priv <= iter->second.priv) { + (*iter->second.callback).post(pmt::PMT_NIL, rpcpmtconverter::To_PMT::instance(p.second)); + } + else { + std::cout << "Key " << p.first << " requires PRIVLVL <= " + << iter->second.priv << " to set, currently at: " + << cur_priv << std::endl; + } + } + else { + throw apache::thrift::TApplicationException(__FILE__ " " S__LINE__); + } + } + + TMap& d_setcallbackmap; + const priv_lvl_t& cur_priv; + }; + + template<typename T, typename TMap> + struct get_f : public std::unary_function<T,void> + { + get_f(TMap &_getcallbackmap, const priv_lvl_t &_cur_priv, GNURadio::KnobMap &_outknobs) : + d_getcallbackmap(_getcallbackmap), cur_priv(_cur_priv), outknobs(_outknobs) + {} + + void operator()(const T& p) + { + QueryCallbackMap_t::const_iterator iter(d_getcallbackmap.find(p)); + if(iter != d_getcallbackmap.end()) { + if(cur_priv <= iter->second.priv) { + outknobs[p] = rpcpmtconverter::from_pmt((*iter->second.callback).retrieve()); + } + else { + std::cout << "Key " << iter->first << " requires PRIVLVL: <= " + << iter->second.priv << " to get, currently at: " + << cur_priv << std::endl; + } + } + else { + std::stringstream ss; + ss << "Ctrlport Key called with unregistered key (" << p << ")\n"; + std::cout << ss.str(); + throw apache::thrift::TApplicationException(__FILE__ " " S__LINE__); + } + } + + TMap& d_getcallbackmap; + const priv_lvl_t& cur_priv; + GNURadio::KnobMap& outknobs; + }; + + template<typename T, typename TMap, typename TKnobMap> + struct get_all_f : public std::unary_function<T,void> + { + get_all_f(TMap &_getcallbackmap, const priv_lvl_t &_cur_priv, TKnobMap &_outknobs) : + d_getcallbackmap(_getcallbackmap), cur_priv(_cur_priv), outknobs(_outknobs) + {;} + + void operator()(const T& p) + { + if(cur_priv <= p.second.priv) { + outknobs[p.first] = rpcpmtconverter::from_pmt(p.second.callback->retrieve()); + } + else { + std::cout << "Key " << p.first << " requires PRIVLVL <= " + << p.second.priv << " to get, currently at: " + << cur_priv << std::endl; + } + } + + TMap& d_getcallbackmap; + const priv_lvl_t& cur_priv; + TKnobMap& outknobs; + }; + + template<typename T, typename TMap, typename TKnobMap> + struct properties_all_f : public std::unary_function<T,void> + { + properties_all_f(QueryCallbackMap_t &_getcallbackmap, + const priv_lvl_t &_cur_priv, + GNURadio::KnobPropMap &_outknobs) + : d_getcallbackmap(_getcallbackmap), + cur_priv(_cur_priv), + outknobs(_outknobs) + {;} + + void operator()(const T& p) + { + if(cur_priv <= p.second.priv) { + GNURadio::KnobProp prop; + prop.type = GNURadio::KnobType::KNOBDOUBLE; + prop.units = p.second.units; + prop.description = p.second.description; + prop.min = rpcpmtconverter::from_pmt(p.second.min); + prop.max = rpcpmtconverter::from_pmt(p.second.max); + prop.display = static_cast<uint32_t>(p.second.display); + outknobs[p.first] = prop; + } + else { + std::cout << "Key " << p.first << " requires PRIVLVL <= " + << p.second.priv << " to get, currently at: " + << cur_priv << std::endl; + } + } + + TMap& d_getcallbackmap; + const priv_lvl_t& cur_priv; + TKnobMap& outknobs; + }; + + template<class T, typename TMap, typename TKnobMap> + struct properties_f : public std::unary_function<T,void> + { + properties_f(TMap &_getcallbackmap, const priv_lvl_t &_cur_priv, TKnobMap &_outknobs) : + d_getcallbackmap(_getcallbackmap), cur_priv(_cur_priv), outknobs(_outknobs) + {;} + + void operator()(const T& p) + { + typename TMap::const_iterator iter(d_getcallbackmap.find(p)); + if(iter != d_getcallbackmap.end()) { + if(cur_priv <= iter->second.priv) { + GNURadio::KnobProp prop; + prop.type = GNURadio::KnobType::KNOBDOUBLE; + prop.units = iter->second.units; + prop.description = iter->second.description; + prop.min = rpcpmtconverter::from_pmt(iter->second.min); + prop.max = rpcpmtconverter::from_pmt(iter->second.max); + prop.display = static_cast<uint32_t>(iter->second.display); + outknobs[p] = prop; + } + else { + std::cout << "Key " << iter->first << " requires PRIVLVL: <= " + << iter->second.priv << " to get, currently at: " << cur_priv << std::endl; + } + } + else { + throw apache::thrift::TApplicationException(__FILE__ " " S__LINE__); + } + } + + TMap& d_getcallbackmap; + const priv_lvl_t& cur_priv; + TKnobMap& outknobs; + }; +}; + +#endif /* RPCSERVER_THRIFT_H */ diff --git a/gnuradio-runtime/include/gnuradio/thrift_application_base.h b/gnuradio-runtime/include/gnuradio/thrift_application_base.h new file mode 100644 index 0000000000..4af5e8898e --- /dev/null +++ b/gnuradio-runtime/include/gnuradio/thrift_application_base.h @@ -0,0 +1,254 @@ +/* -*- c++ -*- */ +/* + * Copyright 2015 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef THRIFT_APPLICATION_BASE_H +#define THRIFT_APPLICATION_BASE_H + +#include <gnuradio/api.h> +#include <gnuradio/logger.h> +#include <gnuradio/prefs.h> +#include <gnuradio/thread/thread.h> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/scoped_ptr.hpp> + +namespace { + // Time, in milliseconds, to wait between checks to the Thrift runtime to see if + // it has fully initialized. + static const unsigned int THRIFTAPPLICATION_ACTIVATION_TIMEOUT_MS(200); +}; + +namespace apache { namespace thrift { namespace server { class TServer; } } } + +/*! + * \brief Class to be statically initialized by thrift_application_base. Used + * to store state for thrift_application_base's singleton functions. + */ + +class thrift_application_base_impl +{ +public: + thrift_application_base_impl() : + d_application_initilized(false), + d_endpointStr(""), + d_start_thrift_thread() {;} + + // Used to ensure the Thrift runtime is initialized on the first call to ::i(). + bool d_application_initilized; + // Stores the generated endpoint string after the Thrift runtime has initialized. + std::string d_endpointStr; + // Thread to execute the Thrift runtime's blocking serve() function. + boost::shared_ptr<gr::thread::thread> d_start_thrift_thread; +}; + +/*! + * \brief Base class for a Thrift application with a singleton with + * instance function thrift_application_base::i(). Lazy initialization + * is used to start the Thrift runtime, therefore the Thrift runtime + * is not started unless thrift_application_base::i() is called at + * least once. This typically means that at least one rpc variable + * must be registered by a block before the runtime will start. + * + * \param TserverBase Template parameter naming the type of the server + * base, which is typically rpcserverbase. + * \param TserverClass Template parameter naming the eventual type of + * the fully derived application. + * \param _app Reference to the fully derived application instance to + * be returned by thrift_application_base::i(). + */ + +template<typename TserverBase, typename TserverClass> +class thrift_application_base +{ +public: + thrift_application_base(TserverClass* _app); + + /*! + * Destructor for the application. Since shutdown and cleanup of the + * runtime is typically custom to a particular booter + * implementation, this must be implemented as a specialized function + * for a particular booter. Thus a template implementation is not + * provided here. + */ + ~thrift_application_base(); + + /*! + * The application singleton instance function. + */ + static TserverBase* i(); + + /*! + * Returns the endpoint string of this application. + */ + static const std::vector<std::string> endpoints(); + +protected: + /*! + * Allows this application's booter to set the endpoint string after + * the Thrift runtime has initialized. + * + * \param[in] endpoint The endpoint string reported by this class. + */ + void set_endpoint(const std::string& endpoint); + + virtual TserverBase* i_impl() = 0; + + /*! + * Reference to the fully derived application instance. + */ + static TserverClass* d_application; + + /*! + * Reference to the Thrift runtime. + */ + boost::scoped_ptr<apache::thrift::server::TServer> d_thriftserver; + + /*! + * Max number of attempts when checking the Thrift runtime for + * Initialization before giving up. Set in the Thrift config file + * (see \ref ctrlport_thrift_prefs). + */ + static const unsigned int d_default_max_init_attempts; + + /*! + * Default port for the runtime to listen on, if a static port is + * not specified. Set in the Thrift config file (see \ref + * ctrlport_thrift_prefs). + */ + static const unsigned int d_default_thrift_port; + + /*! + * Maximum number of threads to create when serving multiple rpc + * clients. Set in the Thrift config file (see \ref + * ctrlport_thrift_prefs). + */ + static const unsigned int d_default_num_thrift_threads; + + /*! + * Default packet size for the IP payload of thrift packets. Set in + * the Thrift config file (see \ref ctrlport_thrift_prefs). + */ + static const unsigned int d_default_thrift_buffer_size; + + /*! + * \ref page_logger instances. + */ + gr::logger_ptr d_logger, d_debug_logger; + +private: + + // Function to be called in a separate thread to invoke the blocking + // ThriftServer::serve() function. Must be specialized for a particular + // booter implementation, therefore a template implementation is + // not provided here. + void start_thrift(); + + // Non-blocking function that returns true when the Thrift + // runtime has finished initialization. Must be implemented + // as a specialized template function for a particular booter + // implementation, therefore template implementation is not + // provided here. + bool application_started(); + + // Internal function to start the initialization of the runtime. + // Since this singleton uses lazy instantiation, this function + // will be called on the first call to the instance function ::i(), + // and since ::i() is static, this function must be static as well. + static void start_application(); + + // Pointer to the structure containing staticly allocated + // state information for the applicaiton_base singleton. + static boost::scoped_ptr<thrift_application_base_impl > p_impl; + + // Mutex to protect the endpoint string. + gr::thread::mutex d_lock; + + // Will be set to true by a the application_started() function, + // specialized for a particular booter implementation, once the + // thrift runtime has successfully initialized. + bool d_thirft_is_running; +}; + +template<typename TserverBase, typename TserverClass> +TserverClass* thrift_application_base<TserverBase, TserverClass>::d_application(0); + +template<typename TserverBase, typename TserverClass> +thrift_application_base<TserverBase, TserverClass>::thrift_application_base(TserverClass* _app) + : d_lock(), + d_thirft_is_running(false) +{ + gr::configure_default_loggers(d_logger, d_debug_logger, "controlport"); + d_application = _app; + //GR_LOG_DEBUG(d_debug_logger, "thrift_application_base: ctor"); +} + +template<typename TserverBase, typename TserverClass> +void thrift_application_base<TserverBase, TserverClass>::start_application() +{ + //std::cerr << "thrift_application_base: start_application" << std::endl; + + unsigned int max_init_attempts = \ + static_cast<unsigned int>(gr::prefs::singleton()->get_long("thrift", "init_attempts", + d_default_max_init_attempts)); + + if(!p_impl->d_application_initilized) { + p_impl->d_start_thrift_thread.reset( + (new gr::thread::thread(boost::bind(&thrift_application_base::start_thrift, d_application)))); + + bool app_started(false); + for(unsigned int attempts(0); (!app_started && attempts < max_init_attempts); ++attempts) { + boost::this_thread::sleep(boost::posix_time::milliseconds(THRIFTAPPLICATION_ACTIVATION_TIMEOUT_MS)); + app_started = d_application->application_started(); + } + + if(!app_started) { + std::cerr << "thrift_application_base::start_application(), timeout waiting to port number might have failed?" << std::endl; + } + + p_impl->d_application_initilized = true; + } +} + +template<typename TserverBase, typename TserverClass> +const std::vector<std::string> thrift_application_base<TserverBase, TserverClass>::endpoints() +{ + std::vector<std::string> ep; + ep.push_back(p_impl->d_endpointStr); + return ep; +} + +template<typename TserverBase, typename TserverClass> +void thrift_application_base<TserverBase, TserverClass>::set_endpoint(const std::string& endpoint) +{ + gr::thread::scoped_lock guard(d_lock); + p_impl->d_endpointStr = endpoint; +} + +template<typename TserverBase, typename TserverClass> +TserverBase* thrift_application_base<TserverBase, TserverClass>::i() +{ + if(!p_impl->d_application_initilized) { + start_application(); + } + return d_application->i_impl(); +} + +#endif diff --git a/gnuradio-runtime/include/gnuradio/thrift_server_template.h b/gnuradio-runtime/include/gnuradio/thrift_server_template.h new file mode 100644 index 0000000000..b8f5448fdc --- /dev/null +++ b/gnuradio-runtime/include/gnuradio/thrift_server_template.h @@ -0,0 +1,153 @@ +/* -*- c++ -*- */ +/* + * Copyright 2015 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef THRIFT_SERVER_TEMPLATE_H +#define THRIFT_SERVER_TEMPLATE_H + +#include <gnuradio/prefs.h> +#include <gnuradio/logger.h> +#include <gnuradio/rpcserver_thrift.h> +#include <gnuradio/thrift_application_base.h> +#include <iostream> + +#include <thrift/server/TSimpleServer.h> +#include <thrift/server/TThreadPoolServer.h> +#include <thrift/concurrency/ThreadManager.h> +#include <thrift/concurrency/PlatformThreadFactory.h> +#include <thrift/transport/TServerSocket.h> +#include <thrift/transport/TBufferTransports.h> +#include "thrift/ControlPort.h" + +using namespace apache; + +template<typename TserverBase, typename TserverClass, typename TImplClass> +class thrift_server_template : public thrift_application_base<TserverBase, TImplClass> +{ +public: + thrift_server_template(TImplClass* _this); + ~thrift_server_template(); + +protected: + TserverBase* i_impl(); + friend class thrift_application_base<TserverBase, TImplClass>; + +private: + boost::shared_ptr<TserverClass> d_handler; + boost::shared_ptr<thrift::TProcessor> d_processor; + boost::shared_ptr<thrift::transport::TServerTransport> d_serverTransport; + boost::shared_ptr<thrift::transport::TTransportFactory> d_transportFactory; + boost::shared_ptr<thrift::protocol::TProtocolFactory> d_protocolFactory; + /** + * Custom TransportFactory that allows you to override the default Thrift buffer size + * of 512 bytes. + * + */ + class TBufferedTransportFactory : public thrift::transport::TTransportFactory + { + public: + TBufferedTransportFactory(const unsigned int _bufferSize) : bufferSize(_bufferSize) {;} + + virtual ~TBufferedTransportFactory() {} + + virtual boost::shared_ptr<thrift::transport::TTransport> getTransport( + boost::shared_ptr<thrift::transport::TTransport> trans) + { + return boost::shared_ptr<thrift::transport::TTransport> + (new thrift::transport::TBufferedTransport(trans, bufferSize)); + } + private: + unsigned int bufferSize; + }; +}; + +template<typename TserverBase, typename TserverClass, typename TImplClass> +thrift_server_template<TserverBase, TserverClass, TImplClass>::thrift_server_template +(TImplClass* _this) : thrift_application_base<TserverBase, TImplClass>(_this), +d_handler(new TserverClass()), +d_processor(new GNURadio::ControlPortProcessor(d_handler)), +d_serverTransport(), +d_transportFactory(), +d_protocolFactory(new thrift::protocol::TBinaryProtocolFactory()) +{ + gr::logger_ptr logger, debug_logger; + gr::configure_default_loggers(logger, debug_logger, "controlport"); + + unsigned int port, nthreads, buffersize; + std::string thrift_config_file = gr::prefs::singleton()->get_string("ControlPort", "config", ""); + + if(thrift_config_file.length() > 0) { + gr::prefs::singleton()->add_config_file(thrift_config_file); + } + + // Collect configuration options from the Thrift config file; + // defaults if the config file doesn't exist or list the specific + // options. + port = static_cast<unsigned int>(gr::prefs::singleton()->get_long("thrift", "port", + thrift_application_base<TserverBase, TImplClass>::d_default_thrift_port)); + nthreads = static_cast<unsigned int>(gr::prefs::singleton()->get_long("thrift", "nthreads", + thrift_application_base<TserverBase, TImplClass>::d_default_num_thrift_threads)); + buffersize = static_cast<unsigned int>(gr::prefs::singleton()->get_long("thrift", "buffersize", + thrift_application_base<TserverBase, TImplClass>::d_default_thrift_buffer_size)); + + d_serverTransport.reset(new thrift::transport::TServerSocket(port)); + + d_transportFactory.reset(new thrift_server_template::TBufferedTransportFactory(buffersize)); + + + if(nthreads <= 1) { + // "Thrift: Single-threaded server" + //std::cout << "Thrift Single-threaded server" << std::endl; + thrift_application_base<TserverBase, TImplClass>::d_thriftserver.reset( + new thrift::server::TSimpleServer(d_processor, d_serverTransport, d_transportFactory, d_protocolFactory)); + } + else { + //std::cout << "Thrift Multi-threaded server : " << d_nthreads << std::endl; + boost::shared_ptr<thrift::concurrency::ThreadManager> threadManager( + thrift::concurrency::ThreadManager::newSimpleThreadManager(nthreads)); + + threadManager->threadFactory( + boost::shared_ptr<thrift::concurrency::PlatformThreadFactory>( + new thrift::concurrency::PlatformThreadFactory())); + + threadManager->start(); + + thrift_application_base<TserverBase, TImplClass>::d_thriftserver.reset( + new thrift::server::TThreadPoolServer(d_processor, d_serverTransport, + d_transportFactory, d_protocolFactory, + threadManager)); + } +} + +template<typename TserverBase, typename TserverClass, typename TImplClass> +thrift_server_template<TserverBase, TserverClass,TImplClass>::~thrift_server_template() +{ +} + +template<typename TserverBase, typename TserverClass, typename TImplClass> +TserverBase* thrift_server_template<TserverBase, TserverClass, TImplClass>::i_impl() +{ + //std::cerr << "thrift_server_template: i_impl" << std::endl; + + return d_handler.get(); +} + +#endif /* THRIFT_SERVER_TEMPLATE_H */ diff --git a/gnuradio-runtime/lib/block.cc b/gnuradio-runtime/lib/block.cc index f26d6bb933..6edb73966d 100644 --- a/gnuradio-runtime/lib/block.cc +++ b/gnuradio-runtime/lib/block.cc @@ -842,42 +842,42 @@ namespace gr { d_rpc_vars.push_back( rpcbasic_sptr(new rpcbasic_register_get<block, std::vector<float> >( alias(), "input \% full", &block::pc_input_buffers_full, - pmt::make_c32vector(0,0), pmt::make_c32vector(0,1), pmt::make_c32vector(0,0), + pmt::make_f32vector(0,0), pmt::make_f32vector(0,1), pmt::make_f32vector(0,0), "", "how full input buffers are", RPC_PRIVLVL_MIN, DISPTIME | DISPOPTSTRIP))); d_rpc_vars.push_back( rpcbasic_sptr(new rpcbasic_register_get<block, std::vector<float> >( alias(), "avg input \% full", &block::pc_input_buffers_full_avg, - pmt::make_c32vector(0,0), pmt::make_c32vector(0,1), pmt::make_c32vector(0,0), + pmt::make_f32vector(0,0), pmt::make_f32vector(0,1), pmt::make_f32vector(0,0), "", "Average of how full input buffers are", RPC_PRIVLVL_MIN, DISPTIME | DISPOPTSTRIP))); d_rpc_vars.push_back( rpcbasic_sptr(new rpcbasic_register_get<block, std::vector<float> >( alias(), "var input \% full", &block::pc_input_buffers_full_var, - pmt::make_c32vector(0,0), pmt::make_c32vector(0,1), pmt::make_c32vector(0,0), + pmt::make_f32vector(0,0), pmt::make_f32vector(0,1), pmt::make_f32vector(0,0), "", "Var. of how full input buffers are", RPC_PRIVLVL_MIN, DISPTIME | DISPOPTSTRIP))); d_rpc_vars.push_back( rpcbasic_sptr(new rpcbasic_register_get<block, std::vector<float> >( alias(), "output \% full", &block::pc_output_buffers_full, - pmt::make_c32vector(0,0), pmt::make_c32vector(0,1), pmt::make_c32vector(0,0), + pmt::make_f32vector(0,0), pmt::make_f32vector(0,1), pmt::make_f32vector(0,0), "", "how full output buffers are", RPC_PRIVLVL_MIN, DISPTIME | DISPOPTSTRIP))); d_rpc_vars.push_back( rpcbasic_sptr(new rpcbasic_register_get<block, std::vector<float> >( alias(), "avg output \% full", &block::pc_output_buffers_full_avg, - pmt::make_c32vector(0,0), pmt::make_c32vector(0,1), pmt::make_c32vector(0,0), + pmt::make_f32vector(0,0), pmt::make_f32vector(0,1), pmt::make_f32vector(0,0), "", "Average of how full output buffers are", RPC_PRIVLVL_MIN, DISPTIME | DISPOPTSTRIP))); d_rpc_vars.push_back( rpcbasic_sptr(new rpcbasic_register_get<block, std::vector<float> >( alias(), "var output \% full", &block::pc_output_buffers_full_var, - pmt::make_c32vector(0,0), pmt::make_c32vector(0,1), pmt::make_c32vector(0,0), + pmt::make_f32vector(0,0), pmt::make_f32vector(0,1), pmt::make_f32vector(0,0), "", "Var. of how full output buffers are", RPC_PRIVLVL_MIN, DISPTIME | DISPOPTSTRIP))); #endif /* defined(GR_CTRLPORT) && defined(GR_PERFORMANCE_COUNTERS) */ diff --git a/gnuradio-runtime/lib/controlport/CMakeLists.txt b/gnuradio-runtime/lib/controlport/CMakeLists.txt index 262c5adb8b..f0d2618c01 100644 --- a/gnuradio-runtime/lib/controlport/CMakeLists.txt +++ b/gnuradio-runtime/lib/controlport/CMakeLists.txt @@ -19,8 +19,13 @@ if(ENABLE_GR_CTRLPORT) +# Keep track of the number of backends ControlPort supports +SET(CTRLPORT_BACKENDS 0) + # Add definition so we can compile in ControlPort to the blocks. -ADD_DEFINITIONS(-DGR_CTRLPORT) +add_definitions(-DGR_CTRLPORT) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) list(APPEND gnuradio_ctrlport_sources ${CMAKE_CURRENT_SOURCE_DIR}/rpcmanager.cc @@ -29,14 +34,71 @@ list(APPEND gnuradio_ctrlport_sources ${CMAKE_CURRENT_SOURCE_DIR}/rpcserver_selector.cc ) + +OPTION(ENABLE_CTRLPORT_THRIFT "Enable ControlPort Thrift support" ON) + +if(ENABLE_CTRLPORT_THRIFT) + +# Look if Thrift is installed and use it as a ControlPort backend. +FIND_PACKAGE(Thrift) + +if(THRIFT_FOUND) + +MATH(EXPR CTRLPORT_BACKENDS "${CTRLPORT_BACKENDS} + 1") + +# Indicate thrift as an installed backend in the cmake summary. +message(STATUS "Found and enabling Thrift backend to ControlPort") +GR_APPEND_SUBCOMPONENT("thrift") + +# Run Thrrift To compile C++ and Python files +message(STATUS "Running thrift to build C++ bindings") +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/thrift/) +EXECUTE_PROCESS( + COMMAND ${THRIFT_BIN} --gen cpp -out ${CMAKE_CURRENT_BINARY_DIR}/thrift/ ${CMAKE_CURRENT_SOURCE_DIR}/thrift/gnuradio.thrift + OUTPUT_VARIABLE THRIFT_CPP_OUTPUT + ERROR_VARIABLE THRIFT_CPP_ERROR + ) + +list(APPEND gnuradio_ctrlport_sources + ${CMAKE_CURRENT_SOURCE_DIR}/thrift/rpcserver_thrift.cc + ${CMAKE_CURRENT_SOURCE_DIR}/thrift/rpcpmtconverters_thrift.cc + ${CMAKE_CURRENT_SOURCE_DIR}/thrift/rpcserver_booter_thrift.cc + ${CMAKE_CURRENT_SOURCE_DIR}/thrift/thrift_application_base.cc +) + +# add files built by compiling gnuradio.thrift +list(APPEND gnuradio_ctrlport_sources + ${CMAKE_CURRENT_BINARY_DIR}/thrift/gnuradio_types.cpp + ${CMAKE_CURRENT_BINARY_DIR}/thrift/gnuradio_constants.cpp + ${CMAKE_CURRENT_BINARY_DIR}/thrift/ControlPort.cpp +) + +# Add required libraries here +list(APPEND gnuradio_runtime_libs + ${THRIFT_LIBRARIES} +) + +# Add install rule to move example Thrift configuration file into +# $prefix/etc/gnuradio +install( + FILES ${CMAKE_CURRENT_SOURCE_DIR}/thrift/thrift.conf.example + DESTINATION ${SYSCONFDIR}/${CMAKE_PROJECT_NAME} + COMPONENT "runtime_runtime" +) + +endif(THRIFT_FOUND) +endif(ENABLE_CTRLPORT_THRIFT) + ######################################################################## # Add controlport stuff to gnuradio-runtime ######################################################################## include_directories(${CMAKE_CURRENT_BINARY_DIR}) -# Add any required libraries here -#list(APPEND gnuradio_runtime_libs -#) +# Save the number of backends for testing against later +set( + CTRLPORT_BACKENDS ${CTRLPORT_BACKENDS} + CACHE INTERNAL "Number of ControlPort backends available" +) endif(ENABLE_GR_CTRLPORT) diff --git a/gnuradio-runtime/lib/controlport/rpcmanager.cc b/gnuradio-runtime/lib/controlport/rpcmanager.cc index 0c7bc135be..a67febe386 100644 --- a/gnuradio-runtime/lib/controlport/rpcmanager.cc +++ b/gnuradio-runtime/lib/controlport/rpcmanager.cc @@ -26,16 +26,12 @@ bool rpcmanager::booter_registered(false); bool rpcmanager::aggregator_registered(false); -rpcserver_booter_base* rpcmanager::boot(0); +std::auto_ptr<rpcserver_booter_base> rpcmanager::boot(0); std::auto_ptr<rpcserver_booter_aggregator> rpcmanager::aggregator(0); rpcmanager::rpcmanager() {;} -rpcmanager::~rpcmanager() -{ - if(boot) - delete boot; -} +rpcmanager::~rpcmanager() {;} rpcserver_booter_base* rpcmanager::get() @@ -44,10 +40,10 @@ rpcmanager::get() return aggregator.get(); } else if(booter_registered) { - return boot; + return boot.get(); } assert(booter_registered || aggregator_registered); - return boot; + return boot.get(); } void @@ -63,7 +59,7 @@ rpcmanager::register_booter(rpcserver_booter_base* booter) aggregator->agg()->registerServer(bootreg); } else if(!booter_registered) { - boot = booter; + boot.reset(booter); booter_registered = true; } else { diff --git a/gnuradio-runtime/lib/controlport/rpcserver_booter_aggregator.cc b/gnuradio-runtime/lib/controlport/rpcserver_booter_aggregator.cc index 201dfb3929..a1983b4ac5 100644 --- a/gnuradio-runtime/lib/controlport/rpcserver_booter_aggregator.cc +++ b/gnuradio-runtime/lib/controlport/rpcserver_booter_aggregator.cc @@ -23,7 +23,8 @@ #include <gnuradio/rpcserver_booter_aggregator.h> rpcserver_booter_aggregator::rpcserver_booter_aggregator() : - d_type(std::string("aggregator")), server(new rpcserver_aggregator()) + d_type(std::string("aggregator")), + server(new rpcserver_aggregator()) {;} rpcserver_booter_aggregator::~rpcserver_booter_aggregator() diff --git a/gnuradio-runtime/lib/controlport/rpcserver_selector.cc b/gnuradio-runtime/lib/controlport/rpcserver_selector.cc index 692f151958..8f3b4557c2 100644 --- a/gnuradio-runtime/lib/controlport/rpcserver_selector.cc +++ b/gnuradio-runtime/lib/controlport/rpcserver_selector.cc @@ -26,14 +26,23 @@ bool rpcmanager::make_aggregator(false); -#ifdef RPCSERVER_ICE +#ifdef GR_RPCSERVER_ENABLED +rpcmanager manager_instance; +#endif + +#ifdef GR_RPCSERVER_ICE #error TODO ICE #endif -#ifdef RPCSERVER_ERLANG +#ifdef GR_RPCSERVER_THRIFT +#include <gnuradio/rpcserver_booter_thrift.h> +rpcmanager::rpcserver_booter_register_helper<rpcserver_booter_thrift> boot_thrift; +#endif + +#ifdef GR_RPCSERVER_ERLANG #error TODO ERLANG #endif -#ifdef RPCSERVER_XMLRPC +#ifdef GR_RPCSERVER_XMLRPC #error TODO XMLRPC #endif diff --git a/gnuradio-runtime/lib/controlport/thrift/README b/gnuradio-runtime/lib/controlport/thrift/README new file mode 100644 index 0000000000..5448e512f7 --- /dev/null +++ b/gnuradio-runtime/lib/controlport/thrift/README @@ -0,0 +1,41 @@ +For info on ControlPort and Thrift, see the wiki page: + +http://gnuradio.org/redmine/projects/gnuradio/wiki/ControlPort + +This readme is to address the patch file in the repo for Thrift. We +believe that there is a bug in Thrift itself that occasionally causes +a segfault when shutting down the server. If you run into a problem +when shutting down an application running ControlPort, this might be +the cause. If so, we have included a patch with the source code in +this directory that should be applied to Thrift before rebuilding the +Thrift library: + +thrift-codebase-shutdown-patch.diff + +Note that we are working off Thrift version 0.9.2, so this patch may +not be relevant or even cleanly apply in later versions of Thrift. + +We are also still trying to track down another shutdown problem with +Thrift that again appears to be related to their code. If a segfault +persists after the above patch is applied to Thrift, do a backtrace in +GDB to see what the error looks like. If it is different than the +following backtrace, definitely report it to the Discuss GNU Radio +Mailing List as a known bug. If you see the same backtrace as here, +this is a known bug that occurs very irregularly, but help in tracing +down the cause is appreciated. + +#0 0x00007f07b5b478c2 in ?? () from /lib/x86_64-linux-gnu/libc.so.6 +(gdb) bt +#0 0x00007f07b5b478c2 in ?? () from /lib/x86_64-linux-gnu/libc.so.6 +#1 0x00007f07b5b4856d in ?? () from /lib/x86_64-linux-gnu/libc.so.6 +#2 0x00007f07afc924c5 in std::locale::_Impl::~_Impl() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 +#3 0x00007f07afc925fd in std::locale::~locale() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 +#4 0x00007f07b5b05259 in ?? () from /lib/x86_64-linux-gnu/libc.so.6 +#5 0x00007f07b5b052a5 in exit () from /lib/x86_64-linux-gnu/libc.so.6 +#6 0x000000000042fb83 in Py_Exit () +#7 0x000000000042fcb9 in ?? () +#8 0x000000000042ec32 in PyErr_PrintEx () +#9 0x0000000000469f2f in PyRun_SimpleFileExFlags () +#10 0x000000000046ab81 in Py_Main () +#11 0x00007f07b5aeaec5 in __libc_start_main () from /lib/x86_64-linux-gnu/libc.so.6 +#12 0x000000000057497e in _start () diff --git a/gnuradio-runtime/lib/controlport/thrift/gnuradio.thrift b/gnuradio-runtime/lib/controlport/thrift/gnuradio.thrift new file mode 100644 index 0000000000..ae7f839a2f --- /dev/null +++ b/gnuradio-runtime/lib/controlport/thrift/gnuradio.thrift @@ -0,0 +1,108 @@ +/* + * Copyright 2014,2015 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +namespace cpp GNURadio +namespace py GNURadio + +struct complex { + 1: double re; + 2: double im; +} + +typedef list<bool> VectorB +typedef binary VectorC +typedef list<i16> VectorT +typedef list<i32> VectorI +typedef list<i64> VectorL +typedef list<double> VectorF +typedef list<double> VectorD +typedef list<string> VectorS +typedef list<complex> VectorZ + +enum BaseTypes { BOOL, BYTE, SHORT, INT, LONG, DOUBLE, STRING, COMPLEX, + F32VECTOR, F64VECTOR, S64VECTOR, S32VECTOR, S16VECTOR, + S8VECTOR, C32VECTOR } + +union KnobBase { + 1: bool a_bool; + 2: byte a_byte; + 3: i16 a_short; + 4: i32 a_int; + 5: i64 a_long; + 6: double a_double; + 7: string a_string; + 8: complex a_complex; + 9: VectorF a_f32vector; + 10: VectorD a_f64vector; + 11: VectorL a_s64vector; + 12: VectorI a_s32vector; + 13: VectorT a_s16vector; + 14: VectorC a_s8vector; + 15: VectorZ a_c32vector; +} + +struct Knob { + 1: BaseTypes type; + 2: KnobBase value; +} + +enum KnobType { KNOBBOOL, KNOBCHAR, KNOBINT, KNOBDOUBLE, KNOBSTRING, + KNOBLONG, KNOBVECBOOL, KNOBVECCHAR, KNOBVECINT, + KNOBVECDOUBLE, KNOBVECSTRING, KNOBVECLONG, KNOBSHORT} + +const i32 DISPNULL = 0x0000 +const i32 DISPTIME = 0x0001 +const i32 DISPXY = 0x0002 +const i32 DISPPSD = 0x0004 +const i32 DISPSPEC = 0x0008 +const i32 DISPRAST = 0x0010 +const i32 DISPOPTCPLX = 0x0100 +const i32 DISPOPTLOG = 0x0200 +const i32 DISPOPTSTEM = 0x0400 +const i32 DISPOPTSTRIP = 0x0800 +const i32 DISPOPTSCATTER = 0x1000 + +struct KnobProp { + 1: KnobType type, + 2: string units, + 3: string description, + 4: i32 display, + 5: Knob min, + 6: Knob max, + 7: Knob defaultvalue +} + +typedef list<string> KnobIDList +typedef map<string, Knob> KnobMap +typedef map<string, KnobProp> KnobPropMap +typedef map<string, string> WaveformArgMap + +service StreamReceiver { + void push(1:VectorC data); +} + +service ControlPort { + void setKnobs(1:KnobMap knobs); + KnobMap getKnobs(1:KnobIDList knobs); + KnobMap getRe(1:KnobIDList knobs); + KnobPropMap properties(1:KnobIDList knobs); + void shutdown(); +} diff --git a/gnuradio-runtime/lib/controlport/thrift/rpcpmtconverters_thrift.cc b/gnuradio-runtime/lib/controlport/thrift/rpcpmtconverters_thrift.cc new file mode 100644 index 0000000000..19da05f787 --- /dev/null +++ b/gnuradio-runtime/lib/controlport/thrift/rpcpmtconverters_thrift.cc @@ -0,0 +1,303 @@ +/* + * Copyright 2014,2015 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include <boost/assign/ptr_map_inserter.hpp> +#include <gnuradio/rpcpmtconverters_thrift.h> +#include <gnuradio/gr_complex.h> +#include "thrift/gnuradio_types.h" +#include <iostream> + +GNURadio::Knob +rpcpmtconverter::from_pmt(const pmt::pmt_t& knob) +{ + if(pmt::is_real(knob)) { + GNURadio::Knob result; + result.type = GNURadio::BaseTypes::DOUBLE; + result.value.__set_a_double(pmt::to_double(knob)); + return result; + } + else if(pmt::is_symbol(knob)) { + std::string value = pmt::symbol_to_string(knob); + GNURadio::Knob result; + result.type = GNURadio::BaseTypes::STRING; + result.value.__set_a_string(value); + return result; + } + else if(pmt::is_integer(knob)) { + GNURadio::Knob result; + result.type = GNURadio::BaseTypes::LONG; + result.value.__set_a_long(pmt::to_long(knob)); + return result; + } + else if(pmt::is_bool(knob)) { + GNURadio::Knob result; + result.type = GNURadio::BaseTypes::BOOL; + result.value.__set_a_bool(pmt::to_bool(knob)); + return result; + } + else if(pmt::is_uint64(knob)) { + GNURadio::Knob result; + result.type = GNURadio::BaseTypes::LONG; + result.value.__set_a_long(pmt::to_uint64(knob)); + return result; + } + else if(pmt::is_complex(knob)) { + GNURadio::Knob result; + result.type = GNURadio::BaseTypes::COMPLEX; + std::complex<double> tmp = pmt::to_complex(knob); + GNURadio::complex cpx; + cpx.re = tmp.real(); + cpx.im = tmp.imag(); + result.value.__set_a_complex(cpx); + return result; + } + else if(pmt::is_f32vector(knob)) { + GNURadio::Knob result; + result.type = GNURadio::BaseTypes::F32VECTOR; + size_t size(pmt::length(knob)); + const float* start((const float*)pmt::f32vector_elements(knob,size)); + result.value.__set_a_f32vector(std::vector<double>(start,start+size)); + return result; + } + else if(pmt::is_f64vector(knob)) { + GNURadio::Knob result; + result.type = GNURadio::BaseTypes::F64VECTOR; + size_t size(pmt::length(knob)); + const double* start((const double*)pmt::f64vector_elements(knob,size)); + result.value.__set_a_f64vector(std::vector<double>(start,start+size)); + return result; + } + else if(pmt::is_s64vector(knob)) { + GNURadio::Knob result; + result.type = GNURadio::BaseTypes::S64VECTOR; + size_t size(pmt::length(knob)); + const int64_t* start((const int64_t*)pmt::s64vector_elements(knob,size)); + result.value.__set_a_s64vector(std::vector<int64_t>(start,start+size)); + return result; + } + else if(pmt::is_s32vector(knob)) { + GNURadio::Knob result; + result.type = GNURadio::BaseTypes::S32VECTOR; + size_t size(pmt::length(knob)); + const int32_t* start((const int32_t*)pmt::s32vector_elements(knob,size)); + result.value.__set_a_s32vector(std::vector<int32_t>(start,start+size)); + return result; + } + else if(pmt::is_s16vector(knob)) { + GNURadio::Knob result; + result.type = GNURadio::BaseTypes::S16VECTOR; + size_t size(pmt::length(knob)); + const int16_t* start((const int16_t*)pmt::s16vector_elements(knob,size)); + result.value.__set_a_s16vector(std::vector<int16_t>(start,start+size)); + return result; + } + else if(pmt::is_s8vector(knob)) { + GNURadio::Knob result; + result.type = GNURadio::BaseTypes::S8VECTOR; + size_t size(pmt::length(knob)); + const int8_t* start((const int8_t*)pmt::s8vector_elements(knob,size)); + result.value.__set_a_s8vector(std::basic_string<char>(start,start+size)); + return result; + } + else if(pmt::is_c32vector(knob)) { + std::vector< GNURadio::complex > z; + + GNURadio::Knob result; + result.type = GNURadio::BaseTypes::C32VECTOR; + size_t size(pmt::length(knob)); + const gr_complex* start((const gr_complex*)pmt::c32vector_elements(knob,size)); + for(size_t s = 0; s < size; s++) { + GNURadio::complex z0; + gr_complex z1 = gr_complex(*(start+s)); + z0.__set_re(z1.real()); + z0.__set_im(z1.imag()); + z.push_back(z0); + } + result.value.__set_a_c32vector(z); + return result; + } + else { + std::cerr << "Error: Don't know how to handle Knob Type (from): " << knob << std::endl; + assert(0); + } + return GNURadio::Knob(); +} + +pmt::pmt_t +rpcpmtconverter::to_pmt_byte_f::operator()(const GNURadio::Knob& knob) +{ + return pmt::mp(knob.value.a_byte); +} + +pmt::pmt_t +rpcpmtconverter::to_pmt_short_f::operator()(const GNURadio::Knob& knob) +{ + return pmt::mp(knob.value.a_short); +} + +pmt::pmt_t +rpcpmtconverter::to_pmt_int_f::operator()(const GNURadio::Knob& knob) +{ + return pmt::mp(knob.value.a_int); +} + +pmt::pmt_t +rpcpmtconverter::to_pmt_long_f::operator()(const GNURadio::Knob& knob) +{ + return pmt::mp(knob.value.a_long); +} + +pmt::pmt_t +rpcpmtconverter::to_pmt_double_f::operator()(const GNURadio::Knob& knob) +{ + return pmt::mp(knob.value.a_double); +} + +pmt::pmt_t +rpcpmtconverter::to_pmt_string_f::operator()(const GNURadio::Knob& knob) +{ + return pmt::string_to_symbol(knob.value.a_string); +} + +pmt::pmt_t +rpcpmtconverter::to_pmt_bool_f::operator()(const GNURadio::Knob& knob) +{ + if(knob.value.a_bool) + return pmt::PMT_T; + else + return pmt::PMT_F; +} + +pmt::pmt_t +rpcpmtconverter::to_pmt_complex_f::operator()(const GNURadio::Knob& knob) +{ + gr_complexd cpx(knob.value.a_complex.re, knob.value.a_complex.im); + return pmt::from_complex(cpx); +} + +pmt::pmt_t +rpcpmtconverter::to_pmt_f32vect_f::operator()(const GNURadio::Knob& knob) +{ + std::vector<double> v_double = knob.value.a_f32vector; + std::vector<float> v(v_double.begin(), v_double.end()); + return pmt::init_f32vector(v.size(), v); +} + +pmt::pmt_t +rpcpmtconverter::to_pmt_f64vect_f::operator()(const GNURadio::Knob& knob) +{ + std::vector<double> v = knob.value.a_f64vector; + return pmt::init_f64vector(v.size(), v); +} + +pmt::pmt_t +rpcpmtconverter::to_pmt_s64vect_f::operator()(const GNURadio::Knob& knob) +{ + std::vector<int64_t> v = knob.value.a_s64vector; + return pmt::init_s64vector(v.size(), v); +} + +pmt::pmt_t +rpcpmtconverter::to_pmt_s32vect_f::operator()(const GNURadio::Knob& knob) +{ + std::vector<int32_t> v = knob.value.a_s32vector; + return pmt::init_s32vector(v.size(), v); +} + +pmt::pmt_t +rpcpmtconverter::to_pmt_s16vect_f::operator()(const GNURadio::Knob& knob) +{ + std::vector<int16_t> v = knob.value.a_s16vector; + return pmt::init_s16vector(v.size(), v); +} + +pmt::pmt_t +rpcpmtconverter::to_pmt_s8vect_f::operator()(const GNURadio::Knob& knob) +{ + std::basic_string<char> v = knob.value.a_s8vector; + return pmt::init_s8vector(v.size(), reinterpret_cast<const int8_t*>(v.data())); +} + +pmt::pmt_t +rpcpmtconverter::to_pmt_c32vect_f::operator()(const GNURadio::Knob& knob) +{ + std::vector<GNURadio::complex> v0 = knob.value.a_c32vector; + std::vector<GNURadio::complex>::iterator vitr; + std::vector<gr_complex> v; + for(vitr = v0.begin(); vitr != v0.end(); vitr++) { + v.push_back(gr_complex(vitr->re, vitr->im)); + } + return pmt::init_c32vector(v.size(), v); +} + +rpcpmtconverter::To_PMT rpcpmtconverter::To_PMT::instance; + +rpcpmtconverter::to_pmt_reg<rpcpmtconverter::to_pmt_bool_f> reg_bool(rpcpmtconverter::To_PMT::instance, + GNURadio::BaseTypes::BOOL); +rpcpmtconverter::to_pmt_reg<rpcpmtconverter::to_pmt_byte_f> reg_byte(rpcpmtconverter::To_PMT::instance, + GNURadio::BaseTypes::BYTE); +rpcpmtconverter::to_pmt_reg<rpcpmtconverter::to_pmt_short_f> reg_short(rpcpmtconverter::To_PMT::instance, + GNURadio::BaseTypes::SHORT); +rpcpmtconverter::to_pmt_reg<rpcpmtconverter::to_pmt_int_f> reg_int(rpcpmtconverter::To_PMT::instance, + GNURadio::BaseTypes::INT); +rpcpmtconverter::to_pmt_reg<rpcpmtconverter::to_pmt_long_f> reg_long(rpcpmtconverter::To_PMT::instance, + GNURadio::BaseTypes::LONG); +rpcpmtconverter::to_pmt_reg<rpcpmtconverter::to_pmt_double_f> reg_double(rpcpmtconverter::To_PMT::instance, + GNURadio::BaseTypes::DOUBLE); +rpcpmtconverter::to_pmt_reg<rpcpmtconverter::to_pmt_string_f> reg_string(rpcpmtconverter::To_PMT::instance, + GNURadio::BaseTypes::STRING); +rpcpmtconverter::to_pmt_reg<rpcpmtconverter::to_pmt_complex_f> reg_complex(rpcpmtconverter::To_PMT::instance, + GNURadio::BaseTypes::COMPLEX); +rpcpmtconverter::to_pmt_reg<rpcpmtconverter::to_pmt_f32vect_f> reg_f32v(rpcpmtconverter::To_PMT::instance, + GNURadio::BaseTypes::F32VECTOR); +rpcpmtconverter::to_pmt_reg<rpcpmtconverter::to_pmt_f64vect_f> reg_f64v(rpcpmtconverter::To_PMT::instance, + GNURadio::BaseTypes::F64VECTOR); +rpcpmtconverter::to_pmt_reg<rpcpmtconverter::to_pmt_s64vect_f> reg_s64v(rpcpmtconverter::To_PMT::instance, + GNURadio::BaseTypes::S64VECTOR); +rpcpmtconverter::to_pmt_reg<rpcpmtconverter::to_pmt_s32vect_f> reg_s32v(rpcpmtconverter::To_PMT::instance, + GNURadio::BaseTypes::S32VECTOR); +rpcpmtconverter::to_pmt_reg<rpcpmtconverter::to_pmt_s16vect_f> reg_s16v(rpcpmtconverter::To_PMT::instance, + GNURadio::BaseTypes::S16VECTOR); +rpcpmtconverter::to_pmt_reg<rpcpmtconverter::to_pmt_s8vect_f> reg_s8v(rpcpmtconverter::To_PMT::instance, + GNURadio::BaseTypes::S8VECTOR); +rpcpmtconverter::to_pmt_reg<rpcpmtconverter::to_pmt_c32vect_f> reg_c32v(rpcpmtconverter::To_PMT::instance, + GNURadio::BaseTypes::C32VECTOR); + +template<typename TO_PMT_F> +rpcpmtconverter::to_pmt_reg<TO_PMT_F>::to_pmt_reg(To_PMT& instance, + const GNURadio::BaseTypes::type type) +{ + boost::assign::ptr_map_insert<TO_PMT_F>(instance.to_pmt_map)(type); +} + +pmt::pmt_t +rpcpmtconverter::to_pmt_f::operator()(const GNURadio::Knob& knob) +{ + std::cerr << "Error: Don't know how to handle Knob Type: " << knob.type << std::endl; + assert(0); + return pmt::pmt_t(); +} + +pmt::pmt_t +rpcpmtconverter::To_PMT::operator()(const GNURadio::Knob& knob) +{ + return to_pmt_map[knob.type](knob); +} diff --git a/gnuradio-runtime/lib/controlport/thrift/rpcserver_booter_thrift.cc b/gnuradio-runtime/lib/controlport/thrift/rpcserver_booter_thrift.cc new file mode 100644 index 0000000000..1d6cafe0c0 --- /dev/null +++ b/gnuradio-runtime/lib/controlport/thrift/rpcserver_booter_thrift.cc @@ -0,0 +1,133 @@ +/* -*- c++ -*- */ +/* + * Copyright 2015 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include <gnuradio/rpcserver_thrift.h> +#include <gnuradio/rpcserver_booter_thrift.h> + +#include <boost/asio/ip/host_name.hpp> + +namespace { + static const char* const CONTROL_PORT_CLASS("thrift"); + static const unsigned int ETHERNET_HEADER_SIZE(14); + static const unsigned int IP_HEADER_SIZE(20); + static const unsigned int TCP_HEADER_SIZE(32); + static const unsigned int ETHERNET_TYPICAL_MTU(1500); + static const unsigned int ALRIGHT_DEFAULT_BUFFER_SIZE( + ETHERNET_TYPICAL_MTU - ETHERNET_HEADER_SIZE - IP_HEADER_SIZE - TCP_HEADER_SIZE); +}; + +/*! + * \brief A booter implementation for a Thrift application class. + */ + +rpcserver_booter_thrift::rpcserver_booter_thrift() : + thrift_server_template<rpcserver_base, + rpcserver_thrift, + rpcserver_booter_thrift>(this), + d_type(std::string(CONTROL_PORT_CLASS)) +{;} + +rpcserver_booter_thrift::~rpcserver_booter_thrift() +{;} + +rpcserver_base* +rpcserver_booter_thrift::i() +{ + return thrift_server_template<rpcserver_base, rpcserver_thrift, + rpcserver_booter_thrift>::i(); +} + +/*! + * \brief Returns the endpoint string for the application + */ + +const std::vector<std::string> +rpcserver_booter_thrift::endpoints() +{ + return thrift_server_template<rpcserver_base, rpcserver_thrift, + rpcserver_booter_thrift>::endpoints(); +} + +// Specialized thrift_application_base attributes and functions +// for this rpcserver_booter instance. + +template<class rpcserver_base, class rpcserver_booter_thrift> +const unsigned int thrift_application_base<rpcserver_base, rpcserver_booter_thrift>::d_default_max_init_attempts(100U); + +template<class rpcserver_base, class rpcserver_booter_thrift> +const unsigned int thrift_application_base<rpcserver_base, rpcserver_booter_thrift>::d_default_thrift_port(0U); + +template<class rpcserver_base, class rpcserver_booter_thrift> +const unsigned int thrift_application_base<rpcserver_base, rpcserver_booter_thrift>::d_default_num_thrift_threads(10U); + +template<class rpcserver_base, class rpcserver_booter_thrift> +const unsigned int thrift_application_base<rpcserver_base, rpcserver_booter_thrift>::d_default_thrift_buffer_size( + ALRIGHT_DEFAULT_BUFFER_SIZE); + +template<class rpcserver_base, class rpcserver_booter_thrift> +boost::scoped_ptr<thrift_application_base_impl> + thrift_application_base<rpcserver_base, rpcserver_booter_thrift>::p_impl( + new thrift_application_base_impl()); + +template<class rpcserver_base, class rpcserver_booter_thrift> +thrift_application_base<rpcserver_base, rpcserver_booter_thrift>::~thrift_application_base() +{ + if(d_thirft_is_running) { + d_thriftserver->stop(); + d_thirft_is_running = false; + } +} + +template<class rpcserver_base, class rpcserver_booter_thrift> +void thrift_application_base<rpcserver_base, rpcserver_booter_thrift>::start_thrift() +{ + d_thriftserver->serve(); +} + +template<class rpcserver_base, typename rpcserver_booter_thrift> +bool thrift_application_base<rpcserver_base, rpcserver_booter_thrift>::application_started() +{ + if (d_thirft_is_running) return true; + + bool result(false); + // Define the endpoint. + apache::thrift::transport::TServerTransport *thetransport = + d_thriftserver->getServerTransport().get(); + + // Determine the specified endpoint port number, or the port number selected by bind() if + int used_port = ((apache::thrift::transport::TServerSocket*)thetransport)->getPort(); + + if (used_port > 0) { + // Determine the hostname of this host + const std::string boost_hostname(boost::asio::ip::host_name()); + + std::string endpoint = boost::str(boost::format("-h %1% -p %2%") % boost_hostname % used_port); + + set_endpoint(endpoint); + + GR_LOG_INFO(d_logger, "Apache Thrift: " + endpoint); + d_thirft_is_running = true; + result = true; + } + + return result; +} diff --git a/gnuradio-runtime/lib/controlport/thrift/rpcserver_thrift.cc b/gnuradio-runtime/lib/controlport/thrift/rpcserver_thrift.cc new file mode 100644 index 0000000000..3e6eabc854 --- /dev/null +++ b/gnuradio-runtime/lib/controlport/thrift/rpcserver_thrift.cc @@ -0,0 +1,202 @@ +/* -*- c++ -*- */ +/* + * Copyright 2014,2015 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include <gnuradio/rpcserver_thrift.h> +#include <iostream> +#include <sstream> +#include <stdexcept> +#include <pmt/pmt.h> +#include <thrift/protocol/TBinaryProtocol.h> +#include <thrift/transport/TSocket.h> +#include <thrift/transport/TTransportUtils.h> +#include <boost/xpressive/xpressive.hpp> +#include "thrift/ControlPort.h" + +#define DEBUG 0 + +using namespace rpcpmtconverter; + +rpcserver_thrift::rpcserver_thrift() +{ + //std::cerr << "rpcserver_thrift::ctor" << std::endl; +} + +rpcserver_thrift::~rpcserver_thrift() +{ + //std::cerr << "rpcserver_thrift::dtor" << std::endl; +} + +void +rpcserver_thrift::registerConfigureCallback(const std::string &id, + const configureCallback_t callback) +{ + boost::mutex::scoped_lock lock(d_callback_map_lock); + { + ConfigureCallbackMap_t::const_iterator iter(d_setcallbackmap.find(id)); + if(iter != d_setcallbackmap.end()) { + std::stringstream s; + s << "rpcserver_thrift:: rpcserver_thrift ERROR registering set, already registered: " + << id << std::endl; + throw std::runtime_error(s.str().c_str()); + } + } + + if(DEBUG) { + std::cerr << "rpcserver_thrift registering set: " << id << std::endl; + } + d_setcallbackmap.insert(ConfigureCallbackMap_t::value_type(id, callback)); +} + +void +rpcserver_thrift::unregisterConfigureCallback(const std::string &id) +{ + boost::mutex::scoped_lock lock(d_callback_map_lock); + ConfigureCallbackMap_t::iterator iter(d_setcallbackmap.find(id)); + if(iter == d_setcallbackmap.end()) { + std::stringstream s; + s << "rpcserver_thrift:: rpcserver_thrift ERROR unregistering set, not registered: " + << id << std::endl; + throw std::runtime_error(s.str().c_str()); + } + + if(DEBUG) + std::cerr << "rpcserver_thrift unregistering set: " << id << std::endl; + + d_setcallbackmap.erase(iter); +} + +void +rpcserver_thrift::registerQueryCallback(const std::string &id, + const queryCallback_t callback) +{ + boost::mutex::scoped_lock lock(d_callback_map_lock); + { + QueryCallbackMap_t::const_iterator iter(d_getcallbackmap.find(id)); + if(iter != d_getcallbackmap.end()) { + std::stringstream s; + s << "rpcserver_thrift:: rpcserver_thrift ERROR registering get, already registered: " + << id << std::endl; + throw std::runtime_error(s.str().c_str()); + } + } + + if(DEBUG) { + std::cerr << "rpcserver_thrift registering get: " << id << std::endl; + } + d_getcallbackmap.insert(QueryCallbackMap_t::value_type(id, callback)); +} + +void +rpcserver_thrift::unregisterQueryCallback(const std::string &id) +{ + boost::mutex::scoped_lock lock(d_callback_map_lock); + QueryCallbackMap_t::iterator iter(d_getcallbackmap.find(id)); + if(iter == d_getcallbackmap.end()) { + std::stringstream s; + s << "rpcserver_thrift:: rpcserver_thrift ERROR unregistering get, registered: " + << id << std::endl; + throw std::runtime_error(s.str().c_str()); + } + + if(DEBUG) { + std::cerr << "rpcserver_thrift unregistering get: " << id << std::endl; + } + + d_getcallbackmap.erase(iter); +} + +void +rpcserver_thrift::setKnobs(const GNURadio::KnobMap& knobs) +{ + boost::mutex::scoped_lock lock(d_callback_map_lock); + std::for_each(knobs.begin(), knobs.end(), + set_f<GNURadio::KnobMap::value_type,ConfigureCallbackMap_t> + (d_setcallbackmap, cur_priv)); +} + + +void +rpcserver_thrift::getKnobs(GNURadio::KnobMap& _return, + const GNURadio::KnobIDList& knobs) +{ + boost::mutex::scoped_lock lock(d_callback_map_lock); + if(knobs.size() == 0) { + std::for_each(d_getcallbackmap.begin(), d_getcallbackmap.end(), + get_all_f<QueryCallbackMap_t::value_type, QueryCallbackMap_t, GNURadio::KnobMap> + (d_getcallbackmap, cur_priv, _return)); + } + else { + std::for_each(knobs.begin(), knobs.end(), + get_f<GNURadio::KnobIDList::value_type, QueryCallbackMap_t> + (d_getcallbackmap, cur_priv, _return)); + } +} + +void +rpcserver_thrift::getRe(GNURadio::KnobMap& _return, const GNURadio::KnobIDList& knobs) +{ + boost::mutex::scoped_lock lock(d_callback_map_lock); + if(knobs.size() == 0) { + std::for_each(d_getcallbackmap.begin(), d_getcallbackmap.end(), + get_all_f<QueryCallbackMap_t::value_type, QueryCallbackMap_t, GNURadio::KnobMap> + (d_getcallbackmap, cur_priv, _return)); + } + else { + QueryCallbackMap_t::iterator it; + for(it = d_getcallbackmap.begin(); it != d_getcallbackmap.end(); it++){ + for(size_t j=0; j<knobs.size(); j++) { + const boost::xpressive::sregex re(boost::xpressive::sregex::compile(knobs[j])); + if(boost::xpressive::regex_match(it->first, re)) { + get_f<GNURadio::KnobIDList::value_type, QueryCallbackMap_t> + (d_getcallbackmap, cur_priv, _return)(it->first); + break; + } + } + } + } +} + +void +rpcserver_thrift::properties(GNURadio::KnobPropMap& _return, + const GNURadio::KnobIDList& knobs) +{ + boost::mutex::scoped_lock lock(d_callback_map_lock); + if(knobs.size() == 0) { + std::for_each(d_getcallbackmap.begin(), d_getcallbackmap.end(), + properties_all_f<QueryCallbackMap_t::value_type, + QueryCallbackMap_t, GNURadio::KnobPropMap>(d_getcallbackmap, + cur_priv, _return)); + } + else { + std::for_each(knobs.begin(), knobs.end(), + properties_f<GNURadio::KnobIDList::value_type, + QueryCallbackMap_t, GNURadio::KnobPropMap>(d_getcallbackmap, + cur_priv, _return)); + } +} + +void +rpcserver_thrift::shutdown() { + if (DEBUG) { + std::cerr << "Shutting down..." << std::endl; + } +} diff --git a/gnuradio-runtime/lib/controlport/thrift/thrift-codebase-shutdown-patch.diff b/gnuradio-runtime/lib/controlport/thrift/thrift-codebase-shutdown-patch.diff new file mode 100644 index 0000000000..eaf54d5c70 --- /dev/null +++ b/gnuradio-runtime/lib/controlport/thrift/thrift-codebase-shutdown-patch.diff @@ -0,0 +1,98 @@ +diff --git a/lib/cpp/src/thrift/concurrency/ThreadManager.cpp b/lib/cpp/src/thrift/concurrency/ThreadManager.cpp +index 255d237..9954a8a 100644 +--- a/lib/cpp/src/thrift/concurrency/ThreadManager.cpp ++++ b/lib/cpp/src/thrift/concurrency/ThreadManager.cpp +@@ -409,6 +409,8 @@ void ThreadManager::Impl::removeWorker(size_t value) { + + workerMaxCount_ -= value; + ++ shutdown_mutex_.unlock(); ++ + if (idleCount_ < value) { + for (size_t ix = 0; ix < idleCount_; ix++) { + monitor_.notify(); +diff --git a/lib/cpp/src/thrift/concurrency/ThreadManager.h b/lib/cpp/src/thrift/concurrency/ThreadManager.h +index 7bb71d1..e97fd25 100644 +--- a/lib/cpp/src/thrift/concurrency/ThreadManager.h ++++ b/lib/cpp/src/thrift/concurrency/ThreadManager.h +@@ -24,6 +24,7 @@ + #include <thrift/cxxfunctional.h> + #include <sys/types.h> + #include <thrift/concurrency/Thread.h> ++#include <thrift/concurrency/Mutex.h> + + namespace apache { + namespace thrift { +@@ -59,6 +60,7 @@ protected: + ThreadManager() {} + + public: ++ Mutex shutdown_mutex_; + typedef apache::thrift::stdcxx::function<void(boost::shared_ptr<Runnable>)> ExpireCallback; + + virtual ~ThreadManager() {} +diff --git a/lib/cpp/src/thrift/server/TThreadPoolServer.cpp b/lib/cpp/src/thrift/server/TThreadPoolServer.cpp +index 0530d8d..d6b73dc 100644 +--- a/lib/cpp/src/thrift/server/TThreadPoolServer.cpp ++++ b/lib/cpp/src/thrift/server/TThreadPoolServer.cpp +@@ -194,19 +194,28 @@ void TThreadPoolServer::serve() { + } + } + +- // If stopped manually, join the existing threads +- if (stop_) { +- try { +- serverTransport_->close(); +- threadManager_->join(); +- } catch (TException& tx) { +- string errStr = string("TThreadPoolServer: Exception shutting down: ") + tx.what(); +- GlobalOutput(errStr.c_str()); ++ { ++ Guard g(threadManager_->shutdown_mutex_); ++ // If stopped manually, join the existing threads ++ if (stop_) { ++ try { ++ serverTransport_->close(); ++ threadManager_->join(); ++ } catch (TException& tx) { ++ string errStr = string("TThreadPoolServer: Exception shutting down: ") + tx.what(); ++ GlobalOutput(errStr.c_str()); ++ } ++ stop_ = false; + } +- stop_ = false; + } + } + ++void TThreadPoolServer::stop() { ++ threadManager_->shutdown_mutex_.lock(); ++ stop_ = true; ++ serverTransport_->interrupt(); ++} ++ + int64_t TThreadPoolServer::getTimeout() const { + return timeout_; + } +diff --git a/lib/cpp/src/thrift/server/TThreadPoolServer.h b/lib/cpp/src/thrift/server/TThreadPoolServer.h +index ad7e7ef..9b89846 100644 +--- a/lib/cpp/src/thrift/server/TThreadPoolServer.h ++++ b/lib/cpp/src/thrift/server/TThreadPoolServer.h +@@ -24,6 +24,7 @@ + #include <thrift/server/TServer.h> + #include <thrift/transport/TServerTransport.h> + ++ + #include <boost/shared_ptr.hpp> + + namespace apache { +@@ -113,10 +114,7 @@ public: + + virtual void setTimeout(int64_t value); + +- virtual void stop() { +- stop_ = true; +- serverTransport_->interrupt(); +- } ++ virtual void stop(); + + virtual int64_t getTaskExpiration() const; diff --git a/gnuradio-runtime/lib/controlport/thrift/thrift.conf.example b/gnuradio-runtime/lib/controlport/thrift/thrift.conf.example new file mode 100644 index 0000000000..71cc506249 --- /dev/null +++ b/gnuradio-runtime/lib/controlport/thrift/thrift.conf.example @@ -0,0 +1,4 @@ +[thrift] +port = 9090 +nthreads = 2 +buffersize = 1434 diff --git a/gnuradio-runtime/lib/controlport/thrift/thrift_application_base.cc b/gnuradio-runtime/lib/controlport/thrift/thrift_application_base.cc new file mode 100644 index 0000000000..282ed9fc63 --- /dev/null +++ b/gnuradio-runtime/lib/controlport/thrift/thrift_application_base.cc @@ -0,0 +1,23 @@ +/* -*- c++ -*- */ +/* + * Copyright 2015 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include <gnuradio/thrift_application_base.h> diff --git a/gnuradio-runtime/lib/prefs.cc b/gnuradio-runtime/lib/prefs.cc index b7fcaada9d..b303ffdaf9 100644 --- a/gnuradio-runtime/lib/prefs.cc +++ b/gnuradio-runtime/lib/prefs.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2006,2013 Free Software Foundation, Inc. + * Copyright 2006,2013,2015 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -46,7 +46,10 @@ namespace gr { prefs::prefs() { - _read_files(); + std::string config = _read_files(_sys_prefs_filenames()); + + // Convert the string into a map + _convert_to_map(config); } prefs::~prefs() @@ -83,13 +86,12 @@ namespace gr { return fnames; } - void - prefs::_read_files() + std::string + prefs::_read_files(const std::vector<std::string> &filenames) { std::string config; - std::vector<std::string> filenames = _sys_prefs_filenames(); - std::vector<std::string>::iterator sitr; + std::vector<std::string>::const_iterator sitr; char tmp[1024]; for(sitr = filenames.begin(); sitr != filenames.end(); sitr++) { fs::ifstream fin(*sitr); @@ -142,8 +144,7 @@ namespace gr { fin.close(); } - // Convert the string into a map - _convert_to_map(config); + return config; } void @@ -188,6 +189,17 @@ namespace gr { } } + void + prefs::add_config_file(const std::string &configfile) + { + std::vector<std::string> filenames; + filenames.push_back(configfile); + + std::string config = _read_files(filenames); + _convert_to_map(config); + } + + std::string prefs::to_string() { diff --git a/gnuradio-runtime/python/gnuradio/ctrlport/CMakeLists.txt b/gnuradio-runtime/python/gnuradio/ctrlport/CMakeLists.txt index 1d5a292429..f40f253a72 100644 --- a/gnuradio-runtime/python/gnuradio/ctrlport/CMakeLists.txt +++ b/gnuradio-runtime/python/gnuradio/ctrlport/CMakeLists.txt @@ -27,10 +27,53 @@ install( COMPONENT "runtime_python" ) + GR_PYTHON_INSTALL( FILES + ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py ${CMAKE_CURRENT_SOURCE_DIR}/GrDataPlotter.py ${CMAKE_CURRENT_SOURCE_DIR}/monitor.py + ${CMAKE_CURRENT_SOURCE_DIR}/GNURadioControlPortClient.py + ${CMAKE_CURRENT_SOURCE_DIR}/RPCConnection.py + DESTINATION ${GR_PYTHON_DIR}/gnuradio/ctrlport/ + COMPONENT "runtime_python" +) + +GR_PYTHON_INSTALL( + FILES + ${CMAKE_CURRENT_SOURCE_DIR}/gr-perf-monitorx + ${CMAKE_CURRENT_SOURCE_DIR}/gr-ctrlport-monitor + DESTINATION ${GR_RUNTIME_DIR} + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE + COMPONENT "runtime_python" +) + +if(THRIFT_FOUND) + +EXECUTE_PROCESS( + COMMAND ${THRIFT_BIN} --gen py -out ${CMAKE_CURRENT_BINARY_DIR}/ ${CMAKE_SOURCE_DIR}/gnuradio-runtime/lib/controlport/thrift/gnuradio.thrift + OUTPUT_VARIABLE THRIFT_PY_OUTPUT + ERROR_VARIABLE THRIFT_PY_ERROR + ) + +GR_PYTHON_INSTALL( + FILES + ${CMAKE_CURRENT_SOURCE_DIR}/RPCConnectionThrift.py DESTINATION ${GR_PYTHON_DIR}/gnuradio/ctrlport/ COMPONENT "runtime_python" ) + +GR_PYTHON_INSTALL( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/GNURadio/__init__.py + ${CMAKE_CURRENT_BINARY_DIR}/GNURadio/constants.py + ${CMAKE_CURRENT_BINARY_DIR}/GNURadio/ControlPort.py + ${CMAKE_CURRENT_BINARY_DIR}/GNURadio/ControlPort-remote + ${CMAKE_CURRENT_BINARY_DIR}/GNURadio/StreamReceiver.py + ${CMAKE_CURRENT_BINARY_DIR}/GNURadio/StreamReceiver-remote + ${CMAKE_CURRENT_BINARY_DIR}/GNURadio/ttypes.py + DESTINATION ${GR_PYTHON_DIR}/gnuradio/ctrlport/GNURadio + COMPONENT "runtime_python" +) + +endif(THRIFT_FOUND) diff --git a/gnuradio-runtime/python/gnuradio/ctrlport/GNURadioControlPortClient.py b/gnuradio-runtime/python/gnuradio/ctrlport/GNURadioControlPortClient.py new file mode 100644 index 0000000000..87d2cf5658 --- /dev/null +++ b/gnuradio-runtime/python/gnuradio/ctrlport/GNURadioControlPortClient.py @@ -0,0 +1,132 @@ +# +# Copyright 2015 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +""" +Python Client classes for interfacing with the GNU Radio ControlPort interface +and for accessing Performance Counters. + +While ControlPort and these client classes are designed to support multiple +Remote Procedure Call (RPC) transports, the Apache Thrift middle-ware RPC +is currently the only supported transport. + +""" + +import exceptions + +""" +GNURadioControlPortClient is the main class for creating a GNU Radio +ControlPort client application for all transports. + +Two constructors are provided for creating a connection to ControlPort. + +""" + +class GNURadioControlPortClient(): + """ + Constructor for creating a ControlPort connection to a specified host / port + + Args: + host: hostname of the connection. Specifying None (default) will + select the loopback interface. + + port: port number to use for the connection. Specifying None (default) + will select the specified RPC transport's default port number, if + the transport has a default. + + rpcmethod: This string specifies the RPC transport to use for the + client connection. The default implementation currently uses + the Apache Thrift RPC transport. The value specified here must + be one of the transport keys listed in the RPCMethods dictionary + above + + callback: This optional parameter is a callback function that will be passed + a reference to the Client implementation for the RPC transport specified + by rpcmethod. The callback will be executed after the client has been + constructed, but before __init__ returns. + + blockingcallback: This optional parameter is a callback function with + no parameters that will be executed after callback() is executed, + but before __init__ returns. It is useful if your application + requires that a blocking function be called to start the application, + such as QtGui.QApplication.exec_ + + """ + + def __init__(self, host = None, port = None, rpcmethod = 'thrift', callback = None, blockingcallback = None): + __init__([host, port], rpcmethod, callback, blockingcallback) + + """ + Constructor for creating a ControlPort from a tuple of command line arguments (i.e. sys.argv) + + Args: + argv: List of command line arguments. Future implementations may parse the argument list + for OptionParser style key / value pairs, however the current implementation + simply takes argv[1] and argv[2] as the connection hostname and port, respectively. + + Example Usage: + + In the following QT client example, the ControlPort host and port are specified to + the Client application as the first two command line arguments. The MAINWindow class is + of the type QtGui.QMainWindow, and is the main window for the QT application. MyApp + is a simple helper class for starting the application. + + class MAINWindow(QtGui.QMainWindow): + ... QT Application implementation ... + + class MyApp(object): + def __init__(self, args): + from GNURadioControlPortClient import GNURadioControlPortClient + GNURadioControlPortClient(args, 'thrift', self.run, QtGui.QApplication(sys.argv).exec_) + + def run(self, client): + MAINWindow(client).show() + + MyApp(sys.argv) + + + """ + + def __init__(self, argv = [], rpcmethod = 'thrift', callback = None, blockingcallback = None): + if len(argv) > 1: host = argv[1] + else: host = None + + if len(argv) > 2: port = argv[2] + else: port = None + + self.client = None + + from gnuradio.ctrlport.RPCConnection import RPCMethods + if RPCMethods.has_key(rpcmethod): + from gnuradio.ctrlport.RPCConnectionThrift import RPCConnectionThrift + if rpcmethod == 'thrift': + #print("making RPCConnectionThrift") + self.client = RPCConnectionThrift(host, port) + #print("made %s" % self.client) + + #print("making callback call") + if not callback is None: + callback(self.client) + + #print("making blockingcallback call") + if not blockingcallback is None: + blockingcallback() + else: + print("Unsupported RPC method: ", rpcmethod) + raise exceptions.ValueError() diff --git a/gnuradio-runtime/python/gnuradio/ctrlport/GrDataPlotter.py b/gnuradio-runtime/python/gnuradio/ctrlport/GrDataPlotter.py index 661705d613..c5bfd0a8cb 100644 --- a/gnuradio-runtime/python/gnuradio/ctrlport/GrDataPlotter.py +++ b/gnuradio-runtime/python/gnuradio/ctrlport/GrDataPlotter.py @@ -23,7 +23,7 @@ from gnuradio import gr from gnuradio import blocks from gnuradio import filter -from gnuradio.ctrlport import GNURadio +from gnuradio.ctrlport.GNURadio import ControlPort import sys, time, struct try: @@ -442,7 +442,7 @@ class GrDataPlotterValueTable: units = str(knobprops[itemKey].units) descr = str(knobprops[itemKey].description) - if(type(v) == GNURadio.complex): + if(type(v) == ControlPort.complex): v = v.re + v.im*1j # If it's a byte stream, Python thinks it's a string. # Unpack and convert to floats for plotting. @@ -468,7 +468,7 @@ class GrDataPlotterValueTable: for k in knobs.keys(): if k not in foundKeys: v = knobs[k].value - if(type(v) == GNURadio.complex): + if(type(v) == ControlPort.complex): v = v.re + v.im*1j # If it's a byte stream, Python thinks it's a string. # Unpack and convert to floats for plotting. diff --git a/gnuradio-runtime/python/gnuradio/ctrlport/RPCConnection.py b/gnuradio-runtime/python/gnuradio/ctrlport/RPCConnection.py new file mode 100644 index 0000000000..e14cc0cea7 --- /dev/null +++ b/gnuradio-runtime/python/gnuradio/ctrlport/RPCConnection.py @@ -0,0 +1,115 @@ +# +# Copyright 2015 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +import exceptions + +""" +RPCMethods is a dictionary listing RPC transports currently supported +by this client. + +Args: + function: the function whose parameter list will be examined + excluded_args: function arguments that are NOT to be added to the dictionary (sequence of strings) + options: result of command argument parsing (optparse.Values) +""" + +RPCMethods = {'thrift': 'Apache Thrift', + #'ice': 'Zeroc ICE' + } + + +""" +Base class for RPC transport clients + +Methods that all RPC clients should implement include: + + def newConnection(host,port): Method for re-establishing a new client + connection to a different host / port + + def properties([]): Given a list of ControlPort property names, + or an empty list to specify all currently registered properties, + this method returns a dictionary of metadata describing the + the specified properties. The dictionary key contains the name + of each returned properties. + + def getKnobs([]): Given a list of ControlPort property names, + or an empty list to specify all currently registered properties, + this method returns a dictionary of the current value of + the specified properties. + + def getRe([]): Given a list of regular expression strings, + this method returns a dictionary of the current value of + the all properties with names that match the specified + expressions. + + def setKnobs({}): Given a dictionary of ControlPort property + key / value pairs, this method requests that ControlPort + attempt to set the specified named properties to the + value given. Success in setting each property to the + value specified requires that the property be registered + as a 'setable' ControlPort property, that the client have the + requisite privilege level to set the property, and + the underlying Block's implementation in handling + the set request. + +Args: + method: name of the RPC transport + port: port number of the connection + host: hostname of the connection +""" + +class RPCConnection(object): + def __init__(self, method, port, host=None): + (self.method, self.port) = (method, port) + if host is None: self.host = '127.0.0.1' + else: self.host = host + + def __str__(self): + return "%s connection on %s:%s"%(self.getName(), self.getHost(), self.getPort()) + + def getName(self): + return RPCMethods[self.method] + + def getHost(self): + return self.host + + def getPort(self): + return self.port + + def newConnection(self, host=None, port=None): + raise exceptions.NotImplementedError() + + def properties(self, *args): + raise exceptions.NotImplementedError() + + def getKnobs(self, *args): + raise exceptions.NotImplementedError() + + def getRe(self,*args): + raise exceptions.NotImplementedError() + + def setKnobs(self,*args): + raise exceptions.NotImplementedError() + + def shutdown(self): + raise exceptions.NotImplementedError() + + def printProperties(self, props): + raise exceptions.NotImplementedError() diff --git a/gnuradio-runtime/python/gnuradio/ctrlport/RPCConnectionThrift.py b/gnuradio-runtime/python/gnuradio/ctrlport/RPCConnectionThrift.py new file mode 100644 index 0000000000..9a2a302af5 --- /dev/null +++ b/gnuradio-runtime/python/gnuradio/ctrlport/RPCConnectionThrift.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python +# +# Copyright 2015 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from thrift import Thrift +from thrift.transport import TSocket +from thrift.transport import TTransport +from thrift.protocol import TBinaryProtocol +from gnuradio.ctrlport.GNURadio import ControlPort +from gnuradio.ctrlport import RPCConnection +from gnuradio import gr +import sys + +class ThriftRadioClient: + def __init__(self, host, port): + self.tsocket = TSocket.TSocket(host, port) + self.transport = TTransport.TBufferedTransport(self.tsocket) + self.protocol = TBinaryProtocol.TBinaryProtocol(self.transport) + + self.radio = ControlPort.Client(self.protocol) + self.transport.open() + + def __del__(self): + self.radio.shutdown() + self.transport.close() + + def getRadio(self, host, port): + return self.radio + +""" +RPC Client interface for the Apache Thrift middle-ware RPC transport. + +Args: + port: port number of the connection + host: hostname of the connection +""" + +class RPCConnectionThrift(RPCConnection.RPCConnection): + class Knob(): + def __init__(self, key, value=None, ktype=0): + (self.key, self.value, self.ktype) = (key, value, ktype) + + def __repr__(self): + return "({0} = {1})".format(self.key, self.value) + + def __init__(self, host=None, port=None): + from gnuradio.ctrlport.GNURadio import ttypes + self.BaseTypes = ttypes.BaseTypes + self.KnobBase = ttypes.KnobBase + + # If not set by the user, get the port number from the thrift + # config file, if one is set. Defaults to 9090 otherwise. + if port is None: + p = gr.prefs() + thrift_config_file = p.get_string("ControlPort", "config", ""); + if(len(thrift_config_file) > 0): + p.add_config_file(thrift_config_file) + port = p.get_long("thrift", "port", 9090) + else: + port = 9090 + else: + port = int(port) + + super(RPCConnectionThrift, self).__init__(method='thrift', port=port, host=host) + self.newConnection(host, port) + + self.unpack_dict = { + self.BaseTypes.BOOL: lambda k,b: self.Knob(k, b.value.a_bool, self.BaseTypes.BOOL), + self.BaseTypes.BYTE: lambda k,b: self.Knob(k, b.value.a_byte, self.BaseTypes.BYTE), + self.BaseTypes.SHORT: lambda k,b: self.Knob(k, b.value.a_short, self.BaseTypes.SHORT), + self.BaseTypes.INT: lambda k,b: self.Knob(k, b.value.a_int, self.BaseTypes.INT), + self.BaseTypes.LONG: lambda k,b: self.Knob(k, b.value.a_long, self.BaseTypes.LONG), + self.BaseTypes.DOUBLE: lambda k,b: self.Knob(k, b.value.a_double, self.BaseTypes.DOUBLE), + self.BaseTypes.STRING: lambda k,b: self.Knob(k, b.value.a_string, self.BaseTypes.STRING), + self.BaseTypes.COMPLEX: lambda k,b: self.Knob(k, b.value.a_complex, self.BaseTypes.COMPLEX), + self.BaseTypes.F32VECTOR: lambda k,b: self.Knob(k, b.value.a_f32vector, self.BaseTypes.F32VECTOR), + self.BaseTypes.F64VECTOR: lambda k,b: self.Knob(k, b.value.a_f64vector, self.BaseTypes.F64VECTOR), + self.BaseTypes.S64VECTOR: lambda k,b: self.Knob(k, b.value.a_s64vector, self.BaseTypes.S64VECTOR), + self.BaseTypes.S32VECTOR: lambda k,b: self.Knob(k, b.value.a_s32vector, self.BaseTypes.S32VECTOR), + self.BaseTypes.S16VECTOR: lambda k,b: self.Knob(k, b.value.a_s16vector, self.BaseTypes.S16VECTOR), + self.BaseTypes.S8VECTOR: lambda k,b: self.Knob(k, b.value.a_s8vector, self.BaseTypes.S8VECTOR), + self.BaseTypes.C32VECTOR: lambda k,b: self.Knob(k, b.value.a_c32vector, self.BaseTypes.C32VECTOR), + } + + self.pack_dict = { + self.BaseTypes.BOOL: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_bool = k.value)), + self.BaseTypes.BYTE: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_byte = k.value)), + self.BaseTypes.SHORT: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_short = k.value)), + self.BaseTypes.INT: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_int = k.value)), + self.BaseTypes.LONG: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_long = k.value)), + self.BaseTypes.DOUBLE: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_double = k.value)), + self.BaseTypes.STRING: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_string = k.value)), + self.BaseTypes.COMPLEX: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_complex = k.value)), + self.BaseTypes.F32VECTOR: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_f32vector = k.value)), + self.BaseTypes.F64VECTOR: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_f64vector = k.value)), + self.BaseTypes.S64VECTOR: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_s64vector = k.value)), + self.BaseTypes.S32VECTOR: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_s32vector = k.value)), + self.BaseTypes.S16VECTOR: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_s16vector = k.value)), + self.BaseTypes.S8VECTOR: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_s8vector = k.value)), + self.BaseTypes.C32VECTOR: lambda k: ttypes.Knob(type=k.ktype, value=ttypes.KnobBase(a_c32vector = k.value)), + } + + def unpackKnob(self, key, knob): + f = self.unpack_dict.get(knob.type, None) + if(f): + return f(key, knob) + else: + sys.stderr.write("unpackKnobs: Incorrect Knob type: {0}\n".format(knob.type)) + raise exceptions.ValueError + + def packKnob(self, knob): + f = self.pack_dict.get(knob.ktype, None) + if(f): + return f(knob) + else: + sys.stderr.write("packKnobs: Incorrect Knob type: {0}\n".format(knob.type)) + raise exceptions.ValueError + + def newConnection(self, host=None, port=None): + try: + self.thriftclient = ThriftRadioClient(self.getHost(), self.getPort()) + except TTransport.TTransportException: + sys.stderr.write("Could not connect to ControlPort endpoint at {0}:{1}.\n\n".format(host, port)) + sys.exit(1) + + def properties(self, *args): + knobprops = self.thriftclient.radio.properties(*args) + for key, knobprop in knobprops.iteritems(): + #print("key:", key, "value:", knobprop, "type:", knobprop.type) + knobprops[key].min = self.unpackKnob(key, knobprop.min) + knobprops[key].max = self.unpackKnob(key, knobprop.max) + knobprops[key].defaultvalue = self.unpackKnob(key, knobprop.defaultvalue) + return knobprops + + def getKnobs(self, *args): + result = {} + for key, knob in self.thriftclient.radio.getKnobs(*args).iteritems(): + #print("key:", key, "value:", knob, "type:", knob.type) + result[key] = self.unpackKnob(key, knob) + + # If complex, convert to Python complex + # FIXME: better list iterator way to handle this? + if(knob.type == self.BaseTypes.C32VECTOR): + for i in xrange(len(result[key].value)): + result[key].value[i] = complex(result[key].value[i].re, + result[key].value[i].im) + return result + + def getKnobsRaw(self, *args): + result = {} + for key, knob in self.thriftclient.radio.getKnobs(*args).iteritems(): + #print("key:", key, "value:", knob, "type:", knob.type) + result[key] = knob + return result + + def getRe(self,*args): + result = {} + for key, knob in self.thriftclient.radio.getRe(*args).iteritems(): + result[key] = self.unpackKnob(key, knob) + return result + + def setKnobs(self, *args): + if(type(*args) == dict): + a = dict(*args) + result = {} + for key, knob in a.iteritems(): + result[key] = self.packKnob(knob) + self.thriftclient.radio.setKnobs(result) + elif(type(*args) == list or type(*args) == tuple): + a = list(*args) + result = {} + for k in a: + result[k.key] = self.packKnob(k) + self.thriftclient.radio.setKnobs(result) + else: + sys.stderr.write("setKnobs: Invalid type; must be dict, list, or tuple\n") + + def shutdown(self): + self.thriftclient.radio.shutdown() + + def printProperties(self, props): + info = "" + info += "Item:\t\t{0}\n".format(props.description) + info += "units:\t\t{0}\n".format(props.units) + info += "min:\t\t{0}\n".format(props.min.value) + info += "max:\t\t{0}\n".format(props.max.value) + info += "default:\t\t{0}\n".format(props.defaultvalue.value) + info += "Type Code:\t0x{0:x}\n".format(props.type) + info += "Disp Code:\t0x{0:x}\n".format(props.display) + return info diff --git a/gnuradio-runtime/python/gnuradio/ctrlport/gr-ctrlport-monitor b/gnuradio-runtime/python/gnuradio/ctrlport/gr-ctrlport-monitor new file mode 100644 index 0000000000..c866776355 --- /dev/null +++ b/gnuradio-runtime/python/gnuradio/ctrlport/gr-ctrlport-monitor @@ -0,0 +1,771 @@ +#!/usr/bin/env python +# +# Copyright 2012,2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from PyQt4 import QtCore,Qt +import PyQt4.QtGui as QtGui +import os, sys, time, struct + +from gnuradio import gr, ctrlport +from gnuradio.ctrlport.GrDataPlotter import * + +class RateDialog(QtGui.QDialog): + def __init__(self, delay, parent=None): + super(RateDialog, self).__init__(parent) + self.gridLayout = QtGui.QGridLayout(self) + self.setWindowTitle("Update Delay (ms)"); + self.delay = QtGui.QLineEdit(self); + self.delay.setText(str(delay)); + self.buttonBox = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel) + self.gridLayout.addWidget(self.delay); + self.gridLayout.addWidget(self.buttonBox); + self.buttonBox.accepted.connect(self.accept) + self.buttonBox.rejected.connect(self.reject) + def accept(self): + self.done(1); + def reject(self): + self.done(0); + +class MAINWindow(QtGui.QMainWindow): + def minimumSizeHint(self): + return Qtgui.QSize(800,600) + + def __init__(self, radioclient): + + super(MAINWindow, self).__init__() + self.radioclient = radioclient + self.updateRate = 1000; + self.conns = [] + self.plots = [] + self.knobprops = [] + + self.mdiArea = QtGui.QMdiArea() + self.mdiArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) + self.mdiArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) + self.setCentralWidget(self.mdiArea) + + self.mdiArea.subWindowActivated.connect(self.updateMenus) + self.windowMapper = QtCore.QSignalMapper(self) + self.windowMapper.mapped[QtGui.QWidget].connect(self.setActiveSubWindow) + + self.createActions() + self.createMenus() + self.createToolBars() + self.createStatusBar() + self.updateMenus() + + self.setWindowTitle("GNU Radio Control Port Monitor") + self.setUnifiedTitleAndToolBarOnMac(True) + + self.newCon(radioclient) + icon = QtGui.QIcon(ctrlport.__path__[0] + "/icon.png" ) + self.setWindowIcon(icon) + + # Locally turn off ControlPort export from GR. This prevents + # our GR-based plotters from launching their own ControlPort + # instance (and possibly causing a port collision if one has + # been specified). + os.environ['GR_CONF_CONTROLPORT_ON'] = 'False' + + def setUpdateRate(self,nur): + self.updateRate = int(nur); + for c in self.conns: + c.updateRate = self.updateRate; + c.timer.setInterval(self.updateRate); + + def newCon(self, csomeBool): + child = MForm(self.radioclient, len(self.conns), parent = self, dialogprompt = not csomeBool) + if(child.radioclient is not None): + child.setWindowTitle(str(child.radioclient)) + self.mdiArea.addSubWindow(child) + self.mdiArea.currentSubWindow().showMaximized() + self.conns.append(child) + self.plots.append([]) + + def propertiesMenu(self, key, radio, uid): + title = str(radio) + + props = radio.properties([key]) + + pmin,pmax = get_minmax(props[key]) + + # Use display option mask of item to set up available plot + # types and default options. + disp = self.knobprops[uid][key].display + cplx = disp & gr.DISPOPTCPLX | disp & gr.DISPXY + strip = disp & gr.DISPOPTSTRIP + stem = disp & gr.DISPOPTSTEM + log = disp & gr.DISPOPTLOG + scatter = disp & gr.DISPOPTSCATTER + + def newUpdaterProxy(): + self.newUpdater(key, radio) + + def newPlotterFProxy(): + self.newPlotF(key, uid, title, pmin, pmax, + log, strip, stem) + + def newPlotterCProxy(): + self.newPlotC(key, uid, title, pmin, pmax, + log, strip, stem) + + def newPlotterConstProxy(): + self.newPlotConst(key, uid, title, pmin, pmax, + scatter, strip) + + def newPlotterPsdFProxy(): + self.newPlotPsdF(key, uid, title) + + def newPlotterPsdCProxy(): + self.newPlotPsdC(key, uid, title) + + def newPlotterRasterFProxy(): + self.newPlotRasterF(key, uid, title, pmin, pmax) + + def newPlotterRasterBProxy(): + self.newPlotRasterB(key, uid, title, pmin, pmax) + + menu = QtGui.QMenu(self) + menu.setTitle("Item Actions") + menu.setTearOffEnabled(False) + + # object properties + menu.addAction("Properties", newUpdaterProxy) + + # displays available + if(cplx == 0): + menu.addAction("Plot Time", newPlotterFProxy) + menu.addAction("Plot PSD", newPlotterPsdFProxy) + menu.addAction("Plot Raster (real)", newPlotterRasterFProxy) + #menu.addAction("Plot Raster (bits)", newPlotterRasterBProxy) + else: + menu.addAction("Plot Time", newPlotterCProxy) + menu.addAction("Plot PSD", newPlotterPsdCProxy) + menu.addAction("Plot Constellation", newPlotterConstProxy) + + menu.popup(QtGui.QCursor.pos()) + + def newUpdater(self, key, radio): + updater = UpdaterWindow(key, radio, None) + updater.setWindowTitle("Updater: " + key) + updater.setModal(False) + updater.exec_() + + def newSub(self, e): + tag = str(e.text(0)) + tree = e.treeWidget().parent() + uid = tree.uid + knobprop = self.knobprops[uid][tag] + + strr = str(tree.radioclient) + print(strr) +# r = strr.split(" ") + title = strr #title = "{0}:{1}".format(r[3], r[5]) + pmin,pmax = get_minmax(knobprop) + + disp = knobprop.display + if(disp & gr.DISPTIME): + strip = disp & gr.DISPOPTSTRIP + stem = disp & gr.DISPOPTSTEM + log = disp & gr.DISPOPTLOG + if(disp & gr.DISPOPTCPLX == 0): + self.newPlotF(tag, uid, title, pmin, pmax, + log, strip, stem) + else: + self.newPlotC(tag, uid, title, pmin, pmax, + log, strip, stem) + + elif(disp & gr.DISPXY): + scatter = disp & gr.DISPOPTSCATTER + self.newPlotConst(tag, uid, title, pmin, pmax, scatter) + + elif(disp & gr.DISPPSD): + if(disp & gr.DISPOPTCPLX == 0): + self.newPlotPsdF(tag, uid, title) + else: + self.newPlotPsdC(tag, uid, title) + + def startDrag(self, e): + drag = QtGui.QDrag(self) + mime_data = QtCore.QMimeData() + + tag = str(e.text(0)) + tree = e.treeWidget().parent() + knobprop = self.knobprops[tree.uid][tag] + disp = knobprop.display + iscomplex = (disp & gr.DISPOPTCPLX) or (disp & gr.DISPXY) + + if(disp != gr.DISPNULL): + data = "PlotData:::{0}:::{1}".format(tag, iscomplex) + else: + data = "OtherData:::{0}:::{1}".format(tag, iscomplex) + + mime_data.setText(data) + drag.setMimeData(mime_data) + + drop = drag.start() + + def createPlot(self, plot, uid, title): + plot.start() + self.plots[uid].append(plot) + + self.mdiArea.addSubWindow(plot) + plot.setWindowTitle("{0}: {1}".format(title, plot.name())) + self.connect(plot.qwidget(), + QtCore.SIGNAL('destroyed(QObject*)'), + self.destroyPlot) + + # when the plot is updated via drag-and-drop, we need to be + # notified of the new qwidget that's created so we can + # properly destroy it. + plot.plotupdated.connect(self.plotUpdated) + + plot.show() + + def plotUpdated(self, q): + # the plot has been updated with a new qwidget; make sure this + # gets dies to the destroyPlot function. + for i, plots in enumerate(self.plots): + for p in plots: + if(p == q): + #plots.remove(p) + #plots.append(q) + self.connect(q.qwidget(), + QtCore.SIGNAL('destroyed(QObject*)'), + self.destroyPlot) + break + + def destroyPlot(self, obj): + for plots in self.plots: + for p in plots: + if p.qwidget() == obj: + plots.remove(p) + break + + def newPlotConst(self, tag, uid, title="", pmin=None, pmax=None, + scatter=False, stripchart=False): + plot = GrDataPlotterConst(tag, 32e6, pmin, pmax, stripchart) + plot.scatter(scatter) + self.createPlot(plot, uid, title) + + def newPlotF(self, tag, uid, title="", pmin=None, pmax=None, + logy=False, stripchart=False, stem=False): + plot = GrDataPlotterF(tag, 32e6, pmin, pmax, stripchart) + plot.semilogy(logy) + plot.stem(stem) + self.createPlot(plot, uid, title) + + def newPlotC(self, tag, uid, title="", pmin=None, pmax=None, + logy=False, stripchart=False, stem=False): + plot = GrDataPlotterC(tag, 32e6, pmin, pmax, stripchart) + plot.semilogy(logy) + plot.stem(stem) + self.createPlot(plot, uid, title) + + def newPlotPsdF(self, tag, uid, title="", pmin=None, pmax=None): + plot = GrDataPlotterPsdF(tag, 32e6, pmin, pmax) + self.createPlot(plot, uid, title) + + def newPlotPsdC(self, tag, uid, title="", pmin=None, pmax=None): + plot = GrDataPlotterPsdC(tag, 32e6, pmin, pmax) + self.createPlot(plot, uid, title) + + def newPlotRasterF(self, tag, uid, title="", pmin=None, pmax=None): + plot = GrTimeRasterF(tag, 32e6, pmin, pmax) + self.createPlot(plot, uid, title) + + def newPlotRasterB(self, tag, uid, title="", pmin=None, pmax=None): + plot = GrTimeRasterB(tag, 32e6, pmin, pmax) + self.createPlot(plot, uid, title) + + def update(self, knobs, uid): + #sys.stderr.write("KNOB KEYS: {0}\n".format(knobs.keys())) + for plot in self.plots[uid]: + data = [] + for n in plot.knobnames: + d = knobs[n].value + # TODO: FIX COMPLEX! +# if(type(d) == GNURadio.complex): +# d = [d.re, d.im] + + # If it's a byte stream, Python thinks it's a string. + # Unpack and convert to floats for plotting. + if(type(d) == str and n.find('probe2_b') == 0): + d = struct.unpack(len(d)*'b', d) + d = [float(di) for di in d] + + data.append(d) + plot.update(data) + plot.stop() + plot.wait() + plot.start() + + def setActiveSubWindow(self, window): + if window: + self.mdiArea.setActiveSubWindow(window) + + + def createActions(self): + self.newConAct = QtGui.QAction("&New Connection", + self, shortcut=QtGui.QKeySequence.New, + statusTip="Create a new file", triggered=self.newCon) + + self.exitAct = QtGui.QAction("E&xit", self, shortcut="Ctrl+Q", + statusTip="Exit the application", + triggered=QtGui.qApp.closeAllWindows) + + self.closeAct = QtGui.QAction("Cl&ose", self, shortcut="Ctrl+F4", + statusTip="Close the active window", + triggered=self.mdiArea.closeActiveSubWindow) + + self.closeAllAct = QtGui.QAction("Close &All", self, + statusTip="Close all the windows", + triggered=self.mdiArea.closeAllSubWindows) + + self.urAct = QtGui.QAction("Update Rate", self, shortcut="F5", + statusTip="Change Update Rate", + triggered=self.updateRateShow) + + qks = QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_T); + self.tileAct = QtGui.QAction("&Tile", self, + statusTip="Tile the windows", + triggered=self.mdiArea.tileSubWindows, + shortcut=qks) + + qks = QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_C); + self.cascadeAct = QtGui.QAction("&Cascade", self, + statusTip="Cascade the windows", shortcut=qks, + triggered=self.mdiArea.cascadeSubWindows) + + self.nextAct = QtGui.QAction("Ne&xt", self, + shortcut=QtGui.QKeySequence.NextChild, + statusTip="Move the focus to the next window", + triggered=self.mdiArea.activateNextSubWindow) + + self.previousAct = QtGui.QAction("Pre&vious", self, + shortcut=QtGui.QKeySequence.PreviousChild, + statusTip="Move the focus to the previous window", + triggered=self.mdiArea.activatePreviousSubWindow) + + self.separatorAct = QtGui.QAction(self) + self.separatorAct.setSeparator(True) + + self.aboutAct = QtGui.QAction("&About", self, + statusTip="Show the application's About box", + triggered=self.about) + + self.aboutQtAct = QtGui.QAction("About &Qt", self, + statusTip="Show the Qt library's About box", + triggered=QtGui.qApp.aboutQt) + + def createMenus(self): + self.fileMenu = self.menuBar().addMenu("&File") + self.fileMenu.addAction(self.newConAct) + self.fileMenu.addAction(self.urAct) + self.fileMenu.addSeparator() + self.fileMenu.addAction(self.exitAct) + + self.windowMenu = self.menuBar().addMenu("&Window") + self.updateWindowMenu() + self.windowMenu.aboutToShow.connect(self.updateWindowMenu) + + self.menuBar().addSeparator() + + self.helpMenu = self.menuBar().addMenu("&Help") + self.helpMenu.addAction(self.aboutAct) + self.helpMenu.addAction(self.aboutQtAct) + + def updateRateShow(self): + askrate = RateDialog(self.updateRate, self); + if askrate.exec_(): + ur = float(str(askrate.delay.text())); + self.setUpdateRate(ur); + return; + else: + return; + + def createToolBars(self): + self.fileToolBar = self.addToolBar("File") + self.fileToolBar.addAction(self.newConAct) + self.fileToolBar.addAction(self.urAct) + + self.fileToolBar = self.addToolBar("Window") + self.fileToolBar.addAction(self.tileAct) + self.fileToolBar.addAction(self.cascadeAct) + + def createStatusBar(self): + self.statusBar().showMessage("Ready") + + + def activeMdiChild(self): + activeSubWindow = self.mdiArea.activeSubWindow() + if activeSubWindow: + return activeSubWindow.widget() + return None + + def updateMenus(self): + hasMdiChild = (self.activeMdiChild() is not None) + self.closeAct.setEnabled(hasMdiChild) + self.closeAllAct.setEnabled(hasMdiChild) + self.tileAct.setEnabled(hasMdiChild) + self.cascadeAct.setEnabled(hasMdiChild) + self.nextAct.setEnabled(hasMdiChild) + self.previousAct.setEnabled(hasMdiChild) + self.separatorAct.setVisible(hasMdiChild) + + def updateWindowMenu(self): + self.windowMenu.clear() + self.windowMenu.addAction(self.closeAct) + self.windowMenu.addAction(self.closeAllAct) + self.windowMenu.addSeparator() + self.windowMenu.addAction(self.tileAct) + self.windowMenu.addAction(self.cascadeAct) + self.windowMenu.addSeparator() + self.windowMenu.addAction(self.nextAct) + self.windowMenu.addAction(self.previousAct) + self.windowMenu.addAction(self.separatorAct) + + def about(self): + about_info = \ +'''Copyright 2012 Free Software Foundation, Inc.\n +This program is part of GNU Radio.\n +GNU Radio is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version.\n +GNU Radio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n +You should have received a copy of the GNU General Public License along with GNU Radio; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Boston, MA 02110-1301, USA.''' + + QtGui.QMessageBox.about(None, "gr-ctrlport-monitor", about_info) + + +class ConInfoDialog(QtGui.QDialog): + def __init__(self, parent=None): + super(ConInfoDialog, self).__init__(parent) + + self.gridLayout = QtGui.QGridLayout(self) + + + self.host = QtGui.QLineEdit(self); + self.port = QtGui.QLineEdit(self); + self.host.setText("localhost"); + self.port.setText("43243"); + + self.buttonBox = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel) + + self.gridLayout.addWidget(self.host); + self.gridLayout.addWidget(self.port); + self.gridLayout.addWidget(self.buttonBox); + + self.buttonBox.accepted.connect(self.accept) + self.buttonBox.rejected.connect(self.reject) + + + def accept(self): + self.done(1); + + def reject(self): + self.done(0); + + +class UpdaterWindow(QtGui.QDialog): + def __init__(self, key, radio, parent): + QtGui.QDialog.__init__(self, parent) + + self.key = key; + self.radio = radio + + self.resize(300,200) + self.layout = QtGui.QVBoxLayout() + + self.props = radio.properties([key])[key] + info = radio.printProperties(self.props) + + self.infoLabel = QtGui.QLabel(info) + self.layout.addWidget(self.infoLabel) + + # Test here to make sure that a 'set' function exists + try: + radio.setKnobs(radio.getKnobs([key])) + has_set = True + except: + has_set = False + + + if(has_set is False): + self.cancelButton = QtGui.QPushButton("Ok") + self.cancelButton.connect(self.cancelButton, QtCore.SIGNAL('clicked()'), self.reject) + + self.buttonlayout = QtGui.QHBoxLayout() + self.buttonlayout.addWidget(self.cancelButton) + self.layout.addLayout(self.buttonlayout) + + else: # we have a set function + self.textInput = QtGui.QLineEdit() + self.layout.addWidget(self.textInput) + + self.applyButton = QtGui.QPushButton("Apply") + self.setButton = QtGui.QPushButton("OK") + self.cancelButton = QtGui.QPushButton("Cancel") + + rv = radio.getKnobs([key]) + val = rv[key].value + if(type(val) == ControlPort.complex): + val = val.re + val.im*1j + + self.textInput.setText(str(val)) + self.sv = rv[key] + + self.applyButton.connect(self.applyButton, QtCore.SIGNAL('clicked()'), self._apply) + self.setButton.connect(self.setButton, QtCore.SIGNAL('clicked()'), self._set) + self.cancelButton.connect(self.cancelButton, QtCore.SIGNAL('clicked()'), self.reject) + + self.is_num = ((type(self.sv.value)==float) or (type(self.sv.value)==int)) + if(self.is_num): + self.sliderlayout = QtGui.QHBoxLayout() + + self.slider = QtGui.QSlider(QtCore.Qt.Horizontal) + + self.sliderlayout.addWidget(QtGui.QLabel(str(self.props.min.value))) + self.sliderlayout.addWidget(self.slider) + self.sliderlayout.addWidget(QtGui.QLabel(str(self.props.max.value))) + + self.steps = 10000 + self.valspan = self.props.max.value - self.props.min.value + + self.slider.setRange(0, 10000) + self._set_slider_value(self.sv.value) + + self.connect(self.slider, QtCore.SIGNAL("sliderReleased()"), self._slide) + + self.layout.addLayout(self.sliderlayout) + else: + self._set_slider_value = None + + self.buttonlayout = QtGui.QHBoxLayout() + self.buttonlayout.addWidget(self.applyButton) + self.buttonlayout.addWidget(self.setButton) + self.buttonlayout.addWidget(self.cancelButton) + self.layout.addLayout(self.buttonlayout) + + # set layout and go... + self.setLayout(self.layout) + + def _set_slider_value(self, val): + self.slider.setValue(self.steps*(val-self.props.min.value)/self.valspan) + + def _slide(self): + val = (self.slider.value()*self.valspan + self.props.min.value)/float(self.steps) + self.textInput.setText(str(val)) + + def _apply(self): + if(type(self.sv.value) == str): + val = str(self.textInput.text()) + elif(type(self.sv.value) == int): + val = int(round(float(self.textInput.text()))) + elif(type(self.sv.value) == float): + val = float(self.textInput.text()) + elif(type(self.sv.value) == ControlPort.complex): + t = str(self.textInput.text()) + t = complex(t.strip("(").strip(")").replace(" ", "")) + val = ControlPort.complex() + val.re = t.real + val.im = t.imag + else: + sys.stderr.write("set type not supported! ({0})\n".format(type(self.sv.value))) + return + + self.sv.value = val + km = {} + km[self.key] = self.sv + self.radio.setKnobs(km) + if self._set_slider_value: + self._set_slider_value(self.sv.value) + + def _set(self): + self._apply() + self.done(0) + + +class MForm(QtGui.QWidget): + def update(self): + # TODO: revisit this try-except block, figure out what it's doing, and if we need to keep it. at very lease makes debugging dificult + if True: #try: + st = time.time(); + knobs = self.radioclient.getKnobs([]) + ft = time.time(); + latency = ft-st; + self.parent.statusBar().showMessage("Current GNU Radio Control Port Query Latency: %f ms"%(latency*1000)) + +# except Exception, e: +# sys.stderr.write("ctrlport-monitor: radio.get threw exception ({0}).\n".format(e)) +# if(type(self.parent) is MAINWindow): +# # Find window of connection +# remove = [] +# for p in self.parent.mdiArea.subWindowList(): +# if self.parent.conns[self.uid] == p.widget(): +# remove.append(p) +# +# # Find any subplot windows of connection +# for p in self.parent.mdiArea.subWindowList(): +# for plot in self.parent.plots[self.uid]: +# if plot.qwidget() == p.widget(): +# remove.append(p) +# +# # Clean up local references to these +# self.parent.conns.remove(self.parent.conns[self.uid]) +# self.parent.plots.remove(self.parent.plots[self.uid]) +# +# # Remove subwindows for connection and plots +# for r in remove: +# self.parent.mdiArea.removeSubWindow(r) +# +# # Clean up self +# self.close() +# else: +# sys.exit(1) +# return + + tableitems = knobs.keys() + + #UPDATE TABLE: + #try: + self.table.updateItems(knobs, self.knobprops) + #except: + # self.knobprops = self.radioclient.properties([]) + # print("knobsprops1:", len(self.knobprops)) + + #UPDATE PLOTS + self.parent.update(knobs, self.uid) + + + def __init__(self, radioclient, uid=0, updateRate=2000, parent=None, dialogprompt = False): + + super(MForm, self).__init__() + self.radioclient = radioclient +# print("before radioclient.getHost()", radioclient.getHost(), radioclient.getPort(), "prompt", prompt) + if(dialogprompt or radioclient.getHost() is None or radioclient.getPort() is None): +# print("before ConInfoDialog") + askinfo = ConInfoDialog(self); + if askinfo.exec_(): + host = str(askinfo.host.text()); + port = str(askinfo.port.text()); +# print("before radioclient.newConnection host: %s port: %s"%(host,port)) + newradio = self.radioclient.newConnection(host, port) + if newradio is None: + print("Error making a %s connection to %s:%s from %s" % (radioclient.getName(), host, port, radioclient)) + else: + self.radioclient = newradio + + else: + self.radioclient = Nonclient = None + return + + + self.uid = uid + self.parent = parent + self.horizontalLayout = QtGui.QVBoxLayout(self) + self.gridLayout = QtGui.QGridLayout() + + self.knobprops = self.radioclient.properties([]) + #print("props5:", self.knobprops) + self.parent.knobprops.append(self.knobprops) + self.resize(775,500) + self.timer = QtCore.QTimer() + self.constupdatediv = 0 + self.tableupdatediv = 0 + plotsize=250 + + # make table + self.table = GrDataPlotterValueTable(uid, self, 0, 0, 400, 200) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred) + self.table.treeWidget.setSizePolicy(sizePolicy) + self.table.treeWidget.setEditTriggers(QtGui.QAbstractItemView.EditKeyPressed) + self.table.treeWidget.setSortingEnabled(True) + self.table.treeWidget.setDragEnabled(True) + + # add things to layouts + self.horizontalLayout.addWidget(self.table.treeWidget) + + # set up timer + self.connect(self.timer, QtCore.SIGNAL('timeout()'), self.update) + self.updateRate = updateRate; + self.timer.start(self.updateRate) + + # set up context menu .. + self.table.treeWidget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + self.table.treeWidget.customContextMenuRequested.connect(self.openMenu) + + # Set up double-click to launch default plotter + self.connect(self.table.treeWidget, + QtCore.SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), + self.parent.newSub); + + # Allow drag/drop event from table item to plotter + self.connect(self.table.treeWidget, + QtCore.SIGNAL('itemPressed(QTreeWidgetItem*, int)'), + self.parent.startDrag) + + def openMenu(self, pos): + index = self.table.treeWidget.selectedIndexes() + item = self.table.treeWidget.itemFromIndex(index[0]) + itemname = str(item.text(0)) + self.parent.propertiesMenu(itemname, self.radioclient, self.uid) + + +def get_minmax(p): + pmin = p.min.value + pmax = p.max.value + + # Find min/max or real or imag for GNURadio::complex + # TODO: fix complex + if(type(pmin) == ControlPort.complex): + pmin = min(pmin.re, pmin.im) + if(type(pmax) == ControlPort.complex): + pmax = max(pmax.re, pmax.im) + + # If it's a byte stream, Python thinks it's a string. + try: + if(type(pmin) == str): + pmin = struct.unpack('b', pmin)[0] + if(type(pmax) == str): + pmax = struct.unpack('b', pmax)[0] + except struct.error: + pmin = [] + pmax = [] + + if pmin == []: + pmin = None + else: + pmin = 1.1*float(pmin) + if pmax == []: + pmax = None + else: + pmax = 1.1*float(pmax) + + return pmin, pmax + +class MyApp(object): + def __init__(self, args): + from gnuradio.ctrlport.GNURadioControlPortClient import GNURadioControlPortClient + GNURadioControlPortClient(args, 'thrift', self.run, QtGui.QApplication(sys.argv).exec_) + + def run(self, client): + MAINWindow(client).show() + +MyApp(sys.argv) diff --git a/gnuradio-runtime/python/gnuradio/ctrlport/gr-perf-monitorx b/gnuradio-runtime/python/gnuradio/ctrlport/gr-perf-monitorx new file mode 100644 index 0000000000..cebc00dcf4 --- /dev/null +++ b/gnuradio-runtime/python/gnuradio/ctrlport/gr-perf-monitorx @@ -0,0 +1,875 @@ +#!/usr/bin/env python +# +# Copyright 2012-2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import random,math,operator +import networkx as nx +import matplotlib +matplotlib.use("QT4Agg") +import matplotlib.pyplot as plt +from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas +from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar +from matplotlib.figure import Figure + +from PyQt4 import QtCore,Qt,Qwt5 +import PyQt4.QtGui as QtGui +import sys, time, re, pprint +import itertools + +from gnuradio import gr, ctrlport +from gnuradio.ctrlport.GrDataPlotter import * + +class MAINWindow(QtGui.QMainWindow): + def minimumSizeHint(self): + return QtGui.QSize(800,600) + + def __init__(self, radioclient): + + super(MAINWindow, self).__init__() + self.radioclient = radioclient + self.conns = [] + self.plots = [] + self.knobprops = [] + + self.mdiArea = QtGui.QMdiArea() + self.mdiArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) + self.mdiArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) + self.setCentralWidget(self.mdiArea) + + self.mdiArea.subWindowActivated.connect(self.updateMenus) + self.windowMapper = QtCore.QSignalMapper(self) + self.windowMapper.mapped[QtGui.QWidget].connect(self.setActiveSubWindow) + + self.createActions() + self.createMenus() + self.createToolBars() + self.createStatusBar() + self.updateMenus() + + self.setWindowTitle("GNU Radio Performance Monitor") + self.setUnifiedTitleAndToolBarOnMac(True) + + self.newCon(radioclient) + + icon = QtGui.QIcon(ctrlport.__path__[0] + "/icon.png" ) + self.setWindowIcon(icon) + + def newSubWindow(self, window, title): + child = window; + child.setWindowTitle(title) + self.mdiArea.addSubWindow(child) + self.conns.append(child) + child.show(); + self.mdiArea.currentSubWindow().showMaximized() + + def newCon(self, csomeBool): + child = MForm(self.radioclient, len(self.conns), self, dialogprompt = not csomeBool) + if(child.radioclient is not None): + child.setWindowTitle(str(child.radioclient)) + self.mdiArea.addSubWindow(child) + self.mdiArea.currentSubWindow().showMaximized() + + self.conns.append(child) + self.plots.append([]) + + def update(self, knobs, uid): + #sys.stderr.write("KNOB KEYS: {0}\n".format(knobs.keys())) + for plot in self.plots[uid]: + data = knobs[plot.name()].value + plot.update(data) + plot.stop() + plot.wait() + plot.start() + + def setActiveSubWindow(self, window): + if window: + self.mdiArea.setActiveSubWindow(window) + + + def createActions(self): + self.newConAct = QtGui.QAction("&New Connection", + self, shortcut=QtGui.QKeySequence.New, + statusTip="Create a new file", triggered=self.newCon) + + self.exitAct = QtGui.QAction("E&xit", self, shortcut="Ctrl+Q", + statusTip="Exit the application", + triggered=QtGui.qApp.closeAllWindows) + + self.closeAct = QtGui.QAction("Cl&ose", self, shortcut="Ctrl+F4", + statusTip="Close the active window", + triggered=self.mdiArea.closeActiveSubWindow) + + self.closeAllAct = QtGui.QAction("Close &All", self, + statusTip="Close all the windows", + triggered=self.mdiArea.closeAllSubWindows) + + qks = QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_T); + self.tileAct = QtGui.QAction("&Tile", self, + statusTip="Tile the windows", + triggered=self.mdiArea.tileSubWindows, + shortcut=qks) + + qks = QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_C); + self.cascadeAct = QtGui.QAction("&Cascade", self, + statusTip="Cascade the windows", shortcut=qks, + triggered=self.mdiArea.cascadeSubWindows) + + self.nextAct = QtGui.QAction("Ne&xt", self, + shortcut=QtGui.QKeySequence.NextChild, + statusTip="Move the focus to the next window", + triggered=self.mdiArea.activateNextSubWindow) + + self.previousAct = QtGui.QAction("Pre&vious", self, + shortcut=QtGui.QKeySequence.PreviousChild, + statusTip="Move the focus to the previous window", + triggered=self.mdiArea.activatePreviousSubWindow) + + self.separatorAct = QtGui.QAction(self) + self.separatorAct.setSeparator(True) + + self.aboutAct = QtGui.QAction("&About", self, + statusTip="Show the application's About box", + triggered=self.about) + + self.aboutQtAct = QtGui.QAction("About &Qt", self, + statusTip="Show the Qt library's About box", + triggered=QtGui.qApp.aboutQt) + + def createMenus(self): + self.fileMenu = self.menuBar().addMenu("&File") + self.fileMenu.addAction(self.newConAct) + self.fileMenu.addSeparator() + self.fileMenu.addAction(self.exitAct) + + self.windowMenu = self.menuBar().addMenu("&Window") + self.updateWindowMenu() + self.windowMenu.aboutToShow.connect(self.updateWindowMenu) + + self.menuBar().addSeparator() + + self.helpMenu = self.menuBar().addMenu("&Help") + self.helpMenu.addAction(self.aboutAct) + self.helpMenu.addAction(self.aboutQtAct) + + def createToolBars(self): + self.fileToolBar = self.addToolBar("File") + self.fileToolBar.addAction(self.newConAct) + + self.fileToolBar = self.addToolBar("Window") + self.fileToolBar.addAction(self.tileAct) + self.fileToolBar.addAction(self.cascadeAct) + + def createStatusBar(self): + self.statusBar().showMessage("Ready") + + + def activeMdiChild(self): + activeSubWindow = self.mdiArea.activeSubWindow() + if activeSubWindow: + return activeSubWindow.widget() + return None + + def updateMenus(self): + hasMdiChild = (self.activeMdiChild() is not None) + self.closeAct.setEnabled(hasMdiChild) + self.closeAllAct.setEnabled(hasMdiChild) + self.tileAct.setEnabled(hasMdiChild) + self.cascadeAct.setEnabled(hasMdiChild) + self.nextAct.setEnabled(hasMdiChild) + self.previousAct.setEnabled(hasMdiChild) + self.separatorAct.setVisible(hasMdiChild) + + def updateWindowMenu(self): + self.windowMenu.clear() + self.windowMenu.addAction(self.closeAct) + self.windowMenu.addAction(self.closeAllAct) + self.windowMenu.addSeparator() + self.windowMenu.addAction(self.tileAct) + self.windowMenu.addAction(self.cascadeAct) + self.windowMenu.addSeparator() + self.windowMenu.addAction(self.nextAct) + self.windowMenu.addAction(self.previousAct) + self.windowMenu.addAction(self.separatorAct) + + def about(self): + about_info = \ +'''Copyright 2012 Free Software Foundation, Inc.\n +This program is part of GNU Radio.\n +GNU Radio is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version.\n +GNU Radio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n +You should have received a copy of the GNU General Public License along with GNU Radio; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Boston, MA 02110-1301, USA.''' + + QtGui.QMessageBox.about(None, "gr-perf-monitorx", about_info) + + +class ConInfoDialog(QtGui.QDialog): + def __init__(self, parent=None): + super(ConInfoDialog, self).__init__(parent) + + self.gridLayout = QtGui.QGridLayout(self) + + + self.host = QtGui.QLineEdit(self); + self.port = QtGui.QLineEdit(self); + self.host.setText("localhost"); + self.port.setText("43243"); + + self.buttonBox = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | + QtGui.QDialogButtonBox.Cancel) + + self.gridLayout.addWidget(self.host); + self.gridLayout.addWidget(self.port); + self.gridLayout.addWidget(self.buttonBox); + + self.buttonBox.accepted.connect(self.accept) + self.buttonBox.rejected.connect(self.reject) + + + def accept(self): + self.done(1); + + def reject(self): + self.done(0); + + +class DataTable(QtGui.QWidget): + def update(self): + print "update" + + def closeEvent(self, event): + self.timer = None + + def __init__(self, radioclient, G): + QtGui.QWidget.__init__( self) + + self.layout = QtGui.QVBoxLayout(self) + self.hlayout = QtGui.QHBoxLayout() + self.layout.addLayout(self.hlayout) + + self.G = G + self.radioclient = radioclient + + self._keymap = None + + self.disp = None + + # Create a combobox to set the type of statistic we want. + self._statistic = "Instantaneous" + self._statistics_table = {"Instantaneous": "", + "Average": "avg ", + "Variance": "var "} + self.stattype = QtGui.QComboBox() + self.stattype.addItem("Instantaneous") + self.stattype.addItem("Average") + self.stattype.addItem("Variance") + self.stattype.setMaximumWidth(200) + self.hlayout.addWidget(self.stattype) + self.stattype.currentIndexChanged.connect(self.stat_changed) + + # Create a checkbox to toggle sorting of graphs + self._sort = False + self.checksort = QtGui.QCheckBox("Sort") + self.checksort.setCheckState(self._sort) + self.hlayout.addWidget(self.checksort); + self.checksort.stateChanged.connect(self.checksort_changed) + + # set up table + self.perfTable = Qt.QTableWidget() + self.perfTable.setColumnCount(2) + self.perfTable.verticalHeader().hide() + self.perfTable.setHorizontalHeaderLabels( ["Block Name", "Percent Runtime"] ) + self.perfTable.horizontalHeader().setStretchLastSection(True) + self.perfTable.setSortingEnabled(True) + nodes = self.G.nodes(data=True) + + # set up plot + self.f = plt.figure(figsize=(10,8), dpi=90) + self.sp = self.f.add_subplot(111) + self.sp.autoscale_view(True,True,True) + self.sp.set_autoscale_on(True) + self.canvas = FigureCanvas(self.f) + + # set up tabs + self.tabber = QtGui.QTabWidget(); + self.layout.addWidget(self.tabber); + self.tabber.addTab(self.perfTable,"Table View"); + self.tabber.addTab(self.canvas, "Graph View"); + + # set up timer + self.timer = QtCore.QTimer() + self.connect(self.timer, QtCore.SIGNAL('timeout()'), self.update) + self.timer.start(500) + + for i in range(0,len(nodes)): + self.perfTable.setItem( + i,0, + Qt.QTableWidgetItem(nodes[i][0])) + + def table_update(self,data): + for k in data.keys(): + weight = data[k] + existing = self.perfTable.findItems(str(k),QtCore.Qt.MatchFixedString) + if(len(existing) == 0): + i = self.perfTable.rowCount(); + self.perfTable.setRowCount( i+1) + self.perfTable.setItem( i,0, Qt.QTableWidgetItem(str(k))) + self.perfTable.setItem( i,1, Qt.QTableWidgetItem(str(weight))) + else: + self.perfTable.setItem( self.perfTable.row(existing[0]),1, Qt.QTableWidgetItem(str(weight))) + + def stat_changed(self, index): + self._statistic = str(self.stattype.currentText()) + + def checksort_changed(self, state): + self._sort = state > 0 + +class DataTableBuffers(DataTable): + def __init__(self, radioclient, G): + super(DataTableBuffers, self).__init__(radioclient, G) + self.perfTable.setHorizontalHeaderLabels( ["Block Name", "Percent Buffer Full"] ) + + def update(self): + nodes = self.G.nodes(); + + # get buffer fullness for all blocks + kl = map(lambda x: "%s::%soutput %% full" % \ + (x, self._statistics_table[self._statistic]), + nodes); + buf_knobs = self.radioclient.getKnobs(kl) + + # strip values out of ctrlport response + buffer_fullness = dict(zip( + map(lambda x: x.split("::")[0], buf_knobs.keys()), + map(lambda x: x.value, buf_knobs.values()))) + + blockport_fullness = {} + for blk in buffer_fullness: + bdata = buffer_fullness[blk] + if bdata: + for port in range(0,len(bdata)): + blockport_fullness["%s:%d"%(blk,port)] = bdata[port] + + if(self.perfTable.isVisible()): + self.table_update(blockport_fullness); + + else: + if(self._sort): + sorted_fullness = sorted(blockport_fullness.iteritems(), + key=operator.itemgetter(1)) + self._keymap = map(operator.itemgetter(0), sorted_fullness) + else: + if self._keymap: + sorted_fullness = len(self._keymap)*['',] + for b in blockport_fullness: + sorted_fullness[self._keymap.index(b)] = (b, blockport_fullness[b]) + else: + sorted_fullness = blockport_fullness.items() + + if(not self.disp): + self.disp = self.sp.bar(range(0,len(sorted_fullness)), + map(lambda x: x[1], sorted_fullness), + alpha=0.5) + self.sp.set_ylabel("% Buffers Full"); + self.sp.set_xticks( map(lambda x: x+0.5, range(0,len(sorted_fullness)))) + self.sp.set_xticklabels(map(lambda x: " " + x, map(lambda x: x[0], sorted_fullness)), + rotation="vertical", verticalalignment="bottom") + else: + self.sp.set_xticklabels(map(lambda x: " " + x, map(lambda x: x[0], sorted_fullness)), + rotation="vertical", verticalalignment="bottom") + for r,w in zip(self.disp, sorted_fullness): + r.set_height(w[1]) + + self.canvas.draw() + +class DataTableRuntimes(DataTable): + def __init__(self, radioclient, G): + super(DataTableRuntimes, self).__init__( radioclient, G) + + def update(self): + nodes = self.G.nodes(); + + # get work time for all blocks + kl = map(lambda x: "%s::%swork time" % \ + (x, self._statistics_table[self._statistic]), + nodes); + wrk_knobs = self.radioclient.getKnobs(kl) + + # strip values out of ctrlport response + total_work = sum(map(lambda x: x.value, wrk_knobs.values())) + if(total_work == 0): + total_work = 1 + work_times = dict(zip( + map(lambda x: x.split("::")[0], wrk_knobs.keys()), + map(lambda x: x.value/total_work, wrk_knobs.values()))) + + # update table view + if(self.perfTable.isVisible()): + self.table_update(work_times) + + else: + if(self._sort): + sorted_work = sorted(work_times.iteritems(), key=operator.itemgetter(1)) + self._keymap = map(operator.itemgetter(0), sorted_work) + else: + if self._keymap: + sorted_work = len(self._keymap)*['',] + for b in work_times: + sorted_work[self._keymap.index(b)] = (b, work_times[b]) + else: + sorted_work = work_times.items() + + f = plt.figure(self.f.number) + if(not self.disp): + self.disp = self.sp.bar(range(0,len(sorted_work)), + map(lambda x: x[1], sorted_work), + alpha=0.5) + self.sp.set_ylabel("% Runtime"); + self.sp.set_xticks( map(lambda x: x+0.5, range(0,len(sorted_work)))) + self.sp.set_xticklabels( map(lambda x: " " + x[0], sorted_work), + rotation="vertical", verticalalignment="bottom" ) + else: + self.sp.set_xticklabels( map(lambda x: " " + x[0], sorted_work), + rotation="vertical", verticalalignment="bottom" ) + for r,w in zip(self.disp, sorted_work): + r.set_height(w[1]) + + self.canvas.draw() + +class MForm(QtGui.QWidget): + def update(self): + try: + try: + # update current clock type + self.prevent_clock_change = True; + kl1 = None; + if(self.clockKey == None): + kl1 = self.radioclient.getRe([".*perfcounter_clock"]) + else: + kl1 = self.radioclient.getKnobs([self.clockKey]) + self.clockKey = kl1.keys()[0] + self.currClock = kl1[self.clockKey].value + self.clockSelIdx = self.clocks.values().index(self.currClock) + self.clockSel.setCurrentIndex(self.clockSelIdx) + self.prevent_clock_change = False + except: + print "WARNING: Failed to get current clock setting!" + + nodes_stream = self.G_stream.nodes() + nodes_msg = self.G_msg.nodes() + + # get current buffer depths of all output buffers + kl = map(lambda x: "%s::%soutput %% full" % \ + (x, self._statistics_table[self._statistic]), + nodes_stream); + + st = time.time() + buf_knobs = self.radioclient.getKnobs(kl) + td1 = time.time() - st; + + # strip values out of ctrlport response + buf_vals = dict(zip( + map(lambda x: x.split("::")[0], buf_knobs.keys()), + map(lambda x: x.value, buf_knobs.values()))) + + # get work time for all blocks + kl = map(lambda x: "%s::%swork time" % \ + (x, self._statistics_table[self._statistic]), + nodes_stream); + st = time.time() + wrk_knobs = self.radioclient.getKnobs(kl) + td2 = time.time() - st; + + # strip values out of ctrlport response + total_work = sum(map(lambda x: x.value, wrk_knobs.values())) + if(total_work == 0): + total_work = 1 + work_times = dict(zip( + map(lambda x: x.split("::")[0], wrk_knobs.keys()), + map(lambda x: x.value/total_work, wrk_knobs.values()))) + work_times_padded = dict(zip( + self.G.nodes(), + [0.1]*len(self.G.nodes()))) + work_times_padded.update(work_times) + + for n in nodes_stream: + # ne is the list of edges away from this node! + ne = self.G.edges([n],True); + #for e in ne: # iterate over edges from this block + for e in ne: # iterate over edges from this block + # get the right output buffer/port weight for each edge + sourceport = e[2]["sourceport"]; + if(e[2]["type"] == "stream"): + newweight = buf_vals[n][sourceport] + e[2]["weight"] = newweight; + + for n in nodes_msg: + ne = self.G.edges([n],True); + for e in ne: # iterate over edges from this block + sourceport = e[2]["sourceport"]; + if(e[2]["type"] == "msg"): + newweight = 0.01; + e[2]["weight"] = newweight; + + # set updated weights + #self.node_weights = map(lambda x: 20+2000*work_times[x], nodes_stream); + self.node_weights = map(lambda x: 20+2000*work_times_padded[x], self.G.nodes()); + self.edge_weights = map(lambda x: 100.0*x[2]["weight"], self.G.edges(data=True)); + + # draw graph updates + self.updateGraph(); + + latency = td1 + td2; + self.parent.statusBar().showMessage("Current GNU Radio Control Port Query Latency: %f ms"%\ + (latency*1000)) + + except Exception, e: + sys.stderr.write("gr-perf-monitorx: radio.getKnobs threw exception ({0}).\n".format(e)) + if(type(self.parent) is MAINWindow): + # Find window of connection + remove = [] + for p in self.parent.mdiArea.subWindowList(): + if self.parent.conns[self.uid] == p.widget(): + remove.append(p) + + # Remove subwindows for connection and plots + for r in remove: + self.parent.mdiArea.removeSubWindow(r) + + # Clean up self + self.close() + else: + sys.exit(1) + return + + def rtt(self): + self.parent.newSubWindow( DataTableRuntimes(self.radioclient, self.G_stream), "Runtime Table" ); + + def bpt(self): + self.parent.newSubWindow( DataTableBuffers(self.radioclient, self.G_stream), "Buffers Table" ); + + def resetPCs(self): + knobs = [] + for b in self.blocks_list: + knobs += [self.radioclient.Knob(b + "::reset_perf_counters"),] + k = self.radioclient.setKnobs(knobs) + + def toggleFlowgraph(self): + if self.pauseFGAct.isChecked(): + self.pauseFlowgraph() + else: + self.unpauseFlowgraph() + + def pauseFlowgraph(self): + knobs = [self.radioclient.Knob(self.top_block + "::lock"), + self.radioclient.Knob(self.top_block + "::stop")] + k = self.radioclient.setKnobs(knobs) + + def unpauseFlowgraph(self): + knobs = [self.radioclient.Knob(self.top_block + "::unlock")] + k = self.radioclient.setKnobs(knobs) + + def stat_changed(self, index): + self._statistic = str(self.stattype.currentText()) + + def update_clock(self, clkidx): + if(self.prevent_clock_change): + return; + idx = self.clockSel.currentIndex(); + clk = self.clocks.values()[idx] +# print "UPDATE CLOCK!!! %d -> %d"%(idx,clk); + k = self.radioclient.getKnobs([self.clockKey]); + k[self.clockKey].value = clk; + km = {}; + km[self.clockKey] = k[self.clockKey]; + self.radioclient.setKnobs(km); + + def __init__(self, radioclient, uid=0, parent=None, dialogprompt = False): + + super(MForm, self).__init__() + self.radioclient = radioclient +# print("before radioclient.getHost()", radioclient.getHost(), radioclient.getPort(), "prompt", prompt) + if(dialogprompt or radioclient.getHost() is None or radioclient.getPort() is None): +# print("before ConInfoDialog") + askinfo = ConInfoDialog(self); + if askinfo.exec_(): + host = str(askinfo.host.text()); + port = str(askinfo.port.text()); +# print("before radioclient.newConnection host: %s port: %s"%(host,port)) + newradio = self.radioclient.newConnection(host, port) + if newradio is None: + print("Error making a %s connection to %s:%s from %s" % (radioclient.getName(), host, port, radioclient)) + else: + self.radioclient = newradio + + else: + self.radioclient = None + return + + self.uid = uid + self.parent = parent + + self.layoutTop = QtGui.QVBoxLayout(self) + self.ctlBox = QtGui.QHBoxLayout(); + self.layout = QtGui.QHBoxLayout() + + self.layoutTop.addLayout(self.ctlBox); + self.layoutTop.addLayout(self.layout); + + self.rttAct = QtGui.QAction("Runtime Table", + self, statusTip="Runtime Table", triggered=self.rtt) + self.rttBut = Qt.QToolButton() + self.rttBut.setDefaultAction(self.rttAct); + self.ctlBox.addWidget(self.rttBut); + + self.bptAct = QtGui.QAction("Buffer Table", + self, statusTip="Buffer Table", triggered=self.bpt) + self.bptBut = Qt.QToolButton() + self.bptBut.setDefaultAction(self.bptAct); + self.ctlBox.addWidget(self.bptBut); + + self.resetPCsAct = QtGui.QAction("Reset", self, + statusTip="Reset all Performance Counters", + triggered=self.resetPCs) + self.resetPCsBut = Qt.QToolButton() + self.resetPCsBut.setDefaultAction(self.resetPCsAct); + self.ctlBox.addWidget(self.resetPCsBut); + + self.pauseFGAct = QtGui.QAction("Pause", self, + statusTip="Pause the Flowgraph", + triggered=self.toggleFlowgraph) + self.pauseFGAct.setCheckable(True) + self.pauseFGBut = Qt.QToolButton() + self.pauseFGBut.setDefaultAction(self.pauseFGAct); + self.ctlBox.addWidget(self.pauseFGBut); + + self.prevent_clock_change = True; + self.clockKey = None; + self.clocks = {"MONOTONIC":1, "THREAD":3}; + self.clockSel = QtGui.QComboBox(self); + map(lambda x: self.clockSel.addItem(x), self.clocks.keys()); + self.ctlBox.addWidget(self.clockSel); + self.clockSel.currentIndexChanged.connect(self.update_clock); + self.prevent_clock_change = False; + + self._statistic = "Instantaneous" + self._statistics_table = {"Instantaneous": "", + "Average": "avg ", + "Variance": "var "} + self.stattype = QtGui.QComboBox() + self.stattype.addItem("Instantaneous") + self.stattype.addItem("Average") + self.stattype.addItem("Variance") + self.stattype.setMaximumWidth(200) + self.ctlBox.addWidget(self.stattype); + self.stattype.currentIndexChanged.connect(self.stat_changed) + +# self.setLayout(self.layout); + + self.radio = radioclient + self.knobprops = self.radio.properties([]) + self.parent.knobprops.append(self.knobprops) + + self.timer = QtCore.QTimer() + self.constupdatediv = 0 + self.tableupdatediv = 0 + plotsize=250 + + + # Set up the graph of blocks + input_name = lambda x: x+"::avg input % full" + output_name = lambda x: x+"::avg output % full" + wtime_name = lambda x: x+"::avg work time" + nout_name = lambda x: x+"::avg noutput_items" + nprod_name = lambda x: x+"::avg nproduced" + + tmplist = [] + knobs = self.radio.getKnobs([]) + edgelist = None + msgedgelist = None + for k in knobs: + propname = k.split("::") + blockname = propname[0] + keyname = propname[1] + if(keyname == "edge list"): + edgelist = knobs[k].value + self.top_block = blockname + elif(keyname == "msg edges list"): + msgedgelist = knobs[k].value + elif(blockname not in tmplist): + # only take gr_blocks (no hier_block2) + if(knobs.has_key(input_name(blockname))): + tmplist.append(blockname) + + + if not edgelist: + sys.stderr.write("Could not find list of edges from flowgraph. " + \ + "Make sure the option 'edges_list' is enabled " + \ + "in the ControlPort configuration.\n\n") + sys.exit(1) + + self.blocks_list = tmplist + edges = edgelist.split("\n")[0:-1] + msgedges = msgedgelist.split("\n")[0:-1] + + edgepairs_stream = []; + edgepairs_msg = []; + + # add stream connections + for e in edges: + _e = e.split("->") + edgepairs_stream.append( (_e[0].split(":")[0], _e[1].split(":")[0], + {"type":"stream", "sourceport":int(_e[0].split(":")[1])}) ); + + # add msg connections + for e in msgedges: + _e = e.split("->") + edgepairs_msg.append( (_e[0].split(":")[0], _e[1].split(":")[0], + {"type":"msg", "sourceport":_e[0].split(":")[1]}) ); + + self.G = nx.MultiDiGraph(); + self.G_stream = nx.MultiDiGraph(); + self.G_msg = nx.MultiDiGraph(); + + self.G.add_edges_from(edgepairs_stream); + self.G.add_edges_from(edgepairs_msg); + + self.G_stream.add_edges_from(edgepairs_stream); + self.G_msg.add_edges_from(edgepairs_msg); + + n_edges = self.G.edges(data=True); + for e in n_edges: + e[2]["weight"] = 5+random.random()*10; + + self.G.clear(); + self.G.add_edges_from(n_edges); + + + self.f = plt.figure(figsize=(10,8), dpi=90) + self.sp = self.f.add_subplot(111); + self.sp.autoscale_view(True,True,True); + self.sp.set_autoscale_on(True) + + self.canvas = FigureCanvas(self.f) + self.layout.addWidget(self.canvas); + + self.pos = nx.graphviz_layout(self.G); + #self.pos = nx.pygraphviz_layout(self.G); + #self.pos = nx.spectral_layout(self.G); + #self.pos = nx.circular_layout(self.G); + #self.pos = nx.shell_layout(self.G); + #self.pos = nx.spring_layout(self.G); + + # generate weights and plot + self.update(); + + # set up timer + self.timer = QtCore.QTimer() + self.connect(self.timer, QtCore.SIGNAL('timeout()'), self.update) + self.timer.start(1000) + + # Set up mouse callback functions to move blocks around. + self._grabbed = False + self._current_block = '' + self.f.canvas.mpl_connect('button_press_event', + self.button_press) + self.f.canvas.mpl_connect('motion_notify_event', + self.mouse_move) + self.f.canvas.mpl_connect('button_release_event', + self.button_release) + + def button_press(self, event): + x, y = event.xdata, event.ydata + thrsh = 100 + + if(x is not None and y is not None): + nearby = map(lambda z: math.sqrt( math.pow(x-z[0],2) + math.pow(y-z[1],2)), self.pos.values()) + i = nearby.index(min(nearby)) + if(abs(self.pos.values()[i][0] - x) < thrsh and + abs(self.pos.values()[i][1]-y) < thrsh): + self._current_block = self.pos.keys()[i] + #print "MOVING BLOCK: ", self._current_block + #print "CUR POS: ", self.pos.values()[i] + self._grabbed = True + + def mouse_move(self, event): + if self._grabbed: + x, y = event.xdata, event.ydata + if(x is not None and y is not None): + #print "NEW POS: ", (x,y) + self.pos[self._current_block] = (x,y) + self.updateGraph(); + + def button_release(self, event): + self._grabbed = False + + + def openMenu(self, pos): + index = self.table.treeWidget.selectedIndexes() + item = self.table.treeWidget.itemFromIndex(index[0]) + itemname = str(item.text(0)) + self.parent.propertiesMenu(itemname, self.radioclient, self.uid) + + def updateGraph(self): + + self.canvas.updateGeometry() + self.sp.clear(); + plt.figure(self.f.number) + plt.subplot(111); + nx.draw(self.G, self.pos, + edge_color=self.edge_weights, + node_color='#A0CBE2', + width=map(lambda x: 3+math.log(x), self.edge_weights), + node_shape="s", + node_size=self.node_weights, + #edge_cmap=plt.cm.Blues, + edge_cmap=plt.cm.Reds, + ax=self.sp, + arrows=False + ) + nx.draw_networkx_labels(self.G, self.pos, + font_size=12) + + self.canvas.draw(); + self.canvas.show(); + + +class MyApp(object): + def __init__(self, args): + p = gr.prefs() + cp_on = p.get_bool("ControlPort", "on", False) + cp_edges = p.get_bool("ControlPort", "edges_list", False) + pcs_on = p.get_bool("PerfCounters", "on", False) + pcs_exported = p.get_bool("PerfCounters", "export", False) + if(not (pcs_on and cp_on and pcs_exported and cp_edges)): + print("Configuration has not turned on all of the appropriate ControlPort features:") + print("\t[ControlPort] on = {0}".format(cp_on)) + print("\t[ControlPort] edges_list = {0}".format(cp_edges)) + print("\t[PerfCounters] on = {0}".format(pcs_on)) + print("\t[PerfCounters] export = {0}".format(pcs_exported)) + exit(1) + + from gnuradio.ctrlport.GNURadioControlPortClient import GNURadioControlPortClient + GNURadioControlPortClient(args, 'thrift', self.run, QtGui.QApplication(sys.argv).exec_) + + def run(self, client): + MAINWindow(client).show() + +MyApp(sys.argv) diff --git a/gnuradio-runtime/python/gnuradio/ctrlport/monitor.py b/gnuradio-runtime/python/gnuradio/ctrlport/monitor.py index 8bb26d93a1..f651be2449 100644 --- a/gnuradio-runtime/python/gnuradio/ctrlport/monitor.py +++ b/gnuradio-runtime/python/gnuradio/ctrlport/monitor.py @@ -48,9 +48,9 @@ class monitor: print "monitor::endpoints() = %s" % (gr.rpcmanager_get().endpoints()) try: cmd = map(lambda a: [self.tool, - re.search("\d+\.\d+\.\d+\.\d+",a).group(0), - re.search("-p (\d+)",a).group(1)], - gr.rpcmanager_get().endpoints())[0] + re.search("-h (\S+|\d+\.\d+\.\d+\.\d+)",a).group(1), + re.search("-p (\d+)",a).group(1)], + gr.rpcmanager_get().endpoints())[0] print "running: %s"%(str(cmd)) self.proc = subprocess.Popen(cmd); self.started = True diff --git a/gnuradio-runtime/python/gnuradio/gr/qa_tag_utils.py b/gnuradio-runtime/python/gnuradio/gr/qa_tag_utils.py index 1a08ac5ae6..55b62a12ac 100755 --- a/gnuradio-runtime/python/gnuradio/gr/qa_tag_utils.py +++ b/gnuradio-runtime/python/gnuradio/gr/qa_tag_utils.py @@ -76,6 +76,38 @@ class test_tag_utils (gr_unittest.TestCase): self.assertTrue(pmt.equal(t_tuple.value, value)) self.assertEqual(t_tuple.offset, offset) + def test_003(self): + offsets = (6, 3, 8) + key = pmt.string_to_symbol('key') + srcid = pmt.string_to_symbol('qa_tag_utils') + tags = [] + + for k in offsets: + t = gr.tag_t() + t.offset = k + t.key = key + t.value = pmt.from_long(k) + t.srcid = srcid + tags.append(t) + + for k, t in zip(sorted(offsets), + sorted(tags, key=gr.tag_t_offset_compare_key())): + self.assertEqual(t.offset, k) + self.assertTrue(pmt.equal(t.key, key)) + self.assertTrue(pmt.equal(t.value, pmt.from_long(k))) + self.assertTrue(pmt.equal(t.srcid, srcid)) + + tmin = min(tags, key=gr.tag_t_offset_compare_key()) + self.assertEqual(tmin.offset, min(offsets)) + self.assertTrue(pmt.equal(tmin.key, key)) + self.assertTrue(pmt.equal(tmin.value, pmt.from_long(min(offsets)))) + self.assertTrue(pmt.equal(tmin.srcid, srcid)) + + tmax = max(tags, key=gr.tag_t_offset_compare_key()) + self.assertEqual(tmax.offset, max(offsets)) + self.assertTrue(pmt.equal(tmax.key, key)) + self.assertTrue(pmt.equal(tmax.value, pmt.from_long(max(offsets)))) + self.assertTrue(pmt.equal(tmax.srcid, srcid)) if __name__ == '__main__': diff --git a/gnuradio-runtime/python/gnuradio/gr/tag_utils.py b/gnuradio-runtime/python/gnuradio/gr/tag_utils.py index dc36e05250..a7745428c7 100644 --- a/gnuradio-runtime/python/gnuradio/gr/tag_utils.py +++ b/gnuradio-runtime/python/gnuradio/gr/tag_utils.py @@ -108,3 +108,36 @@ def python_to_tag(tag_struct): return tag else: return None + +def tag_t_offset_compare_key(): + """ + Convert a tag_t_offset_compare function into a key=function + This method is modeled after functools.cmp_to_key(_func_). + It can be used by functions that accept a key function, such as + sorted(), min(), max(), etc. to compare tags by their offsets, + e.g., sorted(tag_list, key=gr.tag_t_offset_compare_key()). + """ + class K(object): + def __init__(self, obj, *args): + self.obj = obj + def __lt__(self, other): + # x.offset < y.offset + return gr.tag_t_offset_compare(self.obj, other.obj) + def __gt__(self, other): + # y.offset < x.offset + return gr.tag_t_offset_compare(other.obj, self.obj) + def __eq__(self, other): + # not (x.offset < y.offset) and not (y.offset < x.offset) + return not gr.tag_t_offset_compare(self.obj, other.obj) and \ + not gr.tag_t_offset_compare(other.obj, self.obj) + def __le__(self, other): + # not (y.offset < x.offset) + return not gr.tag_t_offset_compare(other.obj, self.obj) + def __ge__(self, other): + # not (x.offset < y.offset) + return not gr.tag_t_offset_compare(self.obj, other.obj) + def __ne__(self, other): + # (x.offset < y.offset) or (y.offset < x.offset) + return gr.tag_t_offset_compare(self.obj, other.obj) or \ + gr.tag_t_offset_compare(other.obj, self.obj) + return K diff --git a/gnuradio-runtime/swig/prefs.i b/gnuradio-runtime/swig/prefs.i index ac5fab7adc..4774146b69 100644 --- a/gnuradio-runtime/swig/prefs.i +++ b/gnuradio-runtime/swig/prefs.i @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2006,2013 Free Software Foundation, Inc. + * Copyright 2006,2015 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -25,6 +25,8 @@ class gr::prefs public: static gr::prefs *singleton(); + void add_config_file(const std::string &configfile); + virtual ~prefs(); std::string to_string(); diff --git a/gr-analog/grc/analog_pwr_squelch_xx.xml b/gr-analog/grc/analog_pwr_squelch_xx.xml index 32d9c0e947..a75f85cf10 100644 --- a/gr-analog/grc/analog_pwr_squelch_xx.xml +++ b/gr-analog/grc/analog_pwr_squelch_xx.xml @@ -34,11 +34,13 @@ <param> <name>Alpha</name> <key>alpha</key> + <value>1e-4</value> <type>real</type> </param> <param> <name>Ramp</name> <key>ramp</key> + <value>0</value> <type>int</type> </param> <param> diff --git a/gr-analog/include/gnuradio/analog/pwr_squelch_cc.h b/gr-analog/include/gnuradio/analog/pwr_squelch_cc.h index 6913d62934..324743d965 100644 --- a/gr-analog/include/gnuradio/analog/pwr_squelch_cc.h +++ b/gr-analog/include/gnuradio/analog/pwr_squelch_cc.h @@ -49,10 +49,14 @@ namespace gr { * \brief Make power-based squelch block. * * \param db threshold (in dB) for power squelch - * \param alpha Gain of averaging filter - * \param ramp sets response characteristic. + * \param alpha Gain of averaging filter. Defaults to 0.0001. + * \param ramp sets response characteristic. Defaults to 0. * \param gate if true, no output if no squelch tone. - * if false, output 0's if no squelch tone. + * if false, output 0's if no squelch tone (default). + * + * The block will emit a tag with the key pmt::intern("squelch_sob") + * with the value of pmt::PMT_NIL on the first item it passes, and with + * the key pmt::intern("squelch:eob") on the last item it passes. */ static sptr make(double db, double alpha=0.0001, int ramp=0, bool gate=false); diff --git a/gr-analog/include/gnuradio/analog/pwr_squelch_ff.h b/gr-analog/include/gnuradio/analog/pwr_squelch_ff.h index 46046eae07..a65cedaa57 100644 --- a/gr-analog/include/gnuradio/analog/pwr_squelch_ff.h +++ b/gr-analog/include/gnuradio/analog/pwr_squelch_ff.h @@ -49,10 +49,14 @@ namespace gr { * \brief Make power-based squelch block. * * \param db threshold (in dB) for power squelch - * \param alpha Gain of averaging filter - * \param ramp sets response characteristic. + * \param alpha Gain of averaging filter. Defaults to 0.0001. + * \param ramp sets response characteristic. Defaults to 0. * \param gate if true, no output if no squelch tone. - * if false, output 0's if no squelch tone. + * if false, output 0's if no squelch tone (default). + * + * The block will emit a tag with the key pmt::intern("squelch_sob") + * with the value of pmt::PMT_NIL on the first item it passes, and with + * the key pmt::intern("squelch:eob") on the last item it passes. */ static sptr make(double db, double alpha=0.0001, int ramp=0, bool gate=false); diff --git a/gr-analog/lib/squelch_base_cc_impl.cc b/gr-analog/lib/squelch_base_cc_impl.cc index 3255d3bde4..b5c153558b 100644 --- a/gr-analog/lib/squelch_base_cc_impl.cc +++ b/gr-analog/lib/squelch_base_cc_impl.cc @@ -32,8 +32,11 @@ namespace gr { squelch_base_cc_impl::squelch_base_cc_impl(const char *name, int ramp, bool gate) : block(name, - io_signature::make(1, 1, sizeof(float)), - io_signature::make(1, 1, sizeof(float))) + io_signature::make(1, 1, sizeof(float)), + io_signature::make(1, 1, sizeof(float))), + d_sob_key(pmt::intern("squelch_sob")), + d_eob_key(pmt::intern("squelch_eob")), + d_tag_next_unmuted(true) { set_ramp(ramp); set_gate(gate); @@ -92,48 +95,58 @@ namespace gr { gr::thread::scoped_lock l(d_setlock); for(int i = 0; i < noutput_items; i++) { - update_state(in[i]); - - // Adjust envelope based on current state - switch(d_state) { - case ST_MUTED: - if(!mute()) { - d_state = d_ramp ? ST_ATTACK : ST_UNMUTED; // If not ramping, go straight to unmuted - } - break; - - case ST_UNMUTED: - if(mute()) { - d_state = d_ramp ? ST_DECAY : ST_MUTED; // If not ramping, go straight to muted - } - break; - - case ST_ATTACK: - d_envelope = 0.5-std::cos(M_PI*(++d_ramped)/d_ramp)/2.0; // FIXME: precalculate window for speed - if(d_ramped >= d_ramp) { // use >= in case d_ramp is set to lower value elsewhere - d_state = ST_UNMUTED; - d_envelope = 1.0; - } - break; - - case ST_DECAY: - d_envelope = 0.5-std::cos(M_PI*(--d_ramped)/d_ramp)/2.0; // FIXME: precalculate window for speed - if(d_ramped == 0.0) { - d_state = ST_MUTED; - } - break; - }; - - // If unmuted, copy input times envelope to output - // Otherwise, if not gating, copy zero to output - if(d_state != ST_MUTED) { - out[j++] = in[i]*gr_complex(d_envelope, 0.0); - } - else { - if(!d_gate) { - out[j++] = 0.0; - } - } + update_state(in[i]); + + // Adjust envelope based on current state + switch(d_state) { + case ST_MUTED: + if(!mute()) { + d_state = d_ramp ? ST_ATTACK : ST_UNMUTED; // If not ramping, go straight to unmuted + if(d_state == ST_UNMUTED) + d_tag_next_unmuted = true; + } + break; + + case ST_UNMUTED: + if(d_tag_next_unmuted) { + d_tag_next_unmuted = false; + add_item_tag(0, nitems_written(0) + j, d_sob_key, pmt::PMT_NIL); + } + if(mute()) { + d_state = d_ramp ? ST_DECAY : ST_MUTED; // If not ramping, go straight to muted + if(d_state == ST_MUTED) + add_item_tag(0, nitems_written(0) + j, d_eob_key, pmt::PMT_NIL); + } + break; + + case ST_ATTACK: + d_envelope = 0.5-std::cos(M_PI*(++d_ramped)/d_ramp)/2.0; // FIXME: precalculate window for speed + if(d_ramped >= d_ramp) { // use >= in case d_ramp is set to lower value elsewhere + d_state = ST_UNMUTED; + d_tag_next_unmuted = true; + d_envelope = 1.0; + } + break; + + case ST_DECAY: + d_envelope = 0.5-std::cos(M_PI*(--d_ramped)/d_ramp)/2.0; // FIXME: precalculate window for speed + if(d_ramped == 0.0) { + d_state = ST_MUTED; + add_item_tag(0, nitems_written(0) + j, d_eob_key, pmt::PMT_NIL); + } + break; + }; + + // If unmuted, copy input times envelope to output + // Otherwise, if not gating, copy zero to output + if(d_state != ST_MUTED) { + out[j++] = in[i]*gr_complex(d_envelope, 0.0); + } + else { + if(!d_gate) { + out[j++] = 0.0; + } + } } consume_each(noutput_items); // Use all the inputs diff --git a/gr-analog/lib/squelch_base_cc_impl.h b/gr-analog/lib/squelch_base_cc_impl.h index 58802df91c..68ed1bb2b8 100644 --- a/gr-analog/lib/squelch_base_cc_impl.h +++ b/gr-analog/lib/squelch_base_cc_impl.h @@ -36,6 +36,8 @@ namespace gr { bool d_gate; double d_envelope; enum { ST_MUTED, ST_ATTACK, ST_UNMUTED, ST_DECAY } d_state; + const pmt::pmt_t d_sob_key, d_eob_key; + bool d_tag_next_unmuted; protected: virtual void update_state(const gr_complex &sample) {}; diff --git a/gr-analog/lib/squelch_base_ff_impl.cc b/gr-analog/lib/squelch_base_ff_impl.cc index a729fedb24..ea2d29bd97 100644 --- a/gr-analog/lib/squelch_base_ff_impl.cc +++ b/gr-analog/lib/squelch_base_ff_impl.cc @@ -26,14 +26,18 @@ #include "squelch_base_ff_impl.h" #include <gnuradio/io_signature.h> +#include <pmt/pmt.h> namespace gr { namespace analog { squelch_base_ff_impl::squelch_base_ff_impl(const char *name, int ramp, bool gate) : block(name, - io_signature::make(1, 1, sizeof(float)), - io_signature::make(1, 1, sizeof(float))) + io_signature::make(1, 1, sizeof(float)), + io_signature::make(1, 1, sizeof(float))), + d_sob_key(pmt::intern("squelch_sob")), + d_eob_key(pmt::intern("squelch_eob")), + d_tag_next_unmuted(true) { set_ramp(ramp); set_gate(gate); @@ -88,48 +92,61 @@ namespace gr { int j = 0; for(int i = 0; i < noutput_items; i++) { - update_state(in[i]); - - // Adjust envelope based on current state - switch(d_state) { - case ST_MUTED: - if(!mute()) - // If not ramping, go straight to unmuted - d_state = d_ramp ? ST_ATTACK : ST_UNMUTED; - break; - - case ST_UNMUTED: - if(mute()) - // If not ramping, go straight to muted - d_state = d_ramp ? ST_DECAY : ST_MUTED; - break; - - case ST_ATTACK: - // FIXME: precalculate window for speed - d_envelope = 0.5-std::cos(M_PI*(++d_ramped)/d_ramp)/2.0; - - // use >= in case d_ramp is set to lower value elsewhere - if(d_ramped >= d_ramp) { - d_state = ST_UNMUTED; - d_envelope = 1.0; - } - break; - - case ST_DECAY: - // FIXME: precalculate window for speed - d_envelope = 0.5-std::cos(M_PI*(--d_ramped)/d_ramp)/2.0; - if(d_ramped == 0.0) - d_state = ST_MUTED; - break; - }; - - // If unmuted, copy input times envelope to output - // Otherwise, if not gating, copy zero to output - if(d_state != ST_MUTED) - out[j++] = in[i]*d_envelope; - else - if(!d_gate) - out[j++] = 0.0; + update_state(in[i]); + + // Adjust envelope based on current state + switch(d_state) { + case ST_MUTED: + if(!mute()) { + // If not ramping, go straight to unmuted + d_state = d_ramp ? ST_ATTACK : ST_UNMUTED; + if(d_state == ST_UNMUTED) + d_tag_next_unmuted = true; + } + break; + + case ST_UNMUTED: + if(d_tag_next_unmuted) { + d_tag_next_unmuted = false; + add_item_tag(0, nitems_written(0) + j, d_sob_key, pmt::PMT_NIL); + } + if(mute()) { + // If not ramping, go straight to muted + d_state = d_ramp ? ST_DECAY : ST_MUTED; + if(d_state == ST_MUTED) + add_item_tag(0, nitems_written(0) + j, d_eob_key, pmt::PMT_NIL); + } + break; + + case ST_ATTACK: + // FIXME: precalculate window for speed + d_envelope = 0.5-std::cos(M_PI*(++d_ramped)/d_ramp)/2.0; + + // use >= in case d_ramp is set to lower value elsewhere + if(d_ramped >= d_ramp) { + d_state = ST_UNMUTED; + d_tag_next_unmuted = true; + d_envelope = 1.0; + } + break; + + case ST_DECAY: + // FIXME: precalculate window for speed + d_envelope = 0.5-std::cos(M_PI*(--d_ramped)/d_ramp)/2.0; + if(d_ramped == 0.0) { + d_state = ST_MUTED; + add_item_tag(0, nitems_written(0) + j, d_eob_key, pmt::PMT_NIL); + } + break; + }; + + // If unmuted, copy input times envelope to output + // Otherwise, if not gating, copy zero to output + if(d_state != ST_MUTED) + out[j++] = in[i]*d_envelope; + else + if(!d_gate) + out[j++] = 0.0; } consume_each(noutput_items); // Use all the inputs diff --git a/gr-analog/lib/squelch_base_ff_impl.h b/gr-analog/lib/squelch_base_ff_impl.h index 343dc5f610..b6a7efe609 100644 --- a/gr-analog/lib/squelch_base_ff_impl.h +++ b/gr-analog/lib/squelch_base_ff_impl.h @@ -24,6 +24,7 @@ #define INCLUDED_GR_SQUELCH_BASE_FF_IMPL_H #include <gnuradio/analog/squelch_base_ff.h> +#include <pmt/pmt.h> namespace gr { namespace analog { @@ -36,6 +37,8 @@ namespace gr { bool d_gate; double d_envelope; enum { ST_MUTED, ST_ATTACK, ST_UNMUTED, ST_DECAY } d_state; + const pmt::pmt_t d_sob_key, d_eob_key; + bool d_tag_next_unmuted; protected: virtual void update_state(const float &sample) {}; diff --git a/gr-blocks/examples/CMakeLists.txt b/gr-blocks/examples/CMakeLists.txt index bb07cdc2b5..0ecf9d7a91 100644 --- a/gr-blocks/examples/CMakeLists.txt +++ b/gr-blocks/examples/CMakeLists.txt @@ -20,6 +20,7 @@ install( FILES matrix_multiplexer.grc + peak_detector2.grc vector_source_with_tags.grc DESTINATION ${GR_PKG_DATA_DIR}/examples/blocks COMPONENT "runtime_python" diff --git a/gr-blocks/examples/peak_detector2.grc b/gr-blocks/examples/peak_detector2.grc new file mode 100644 index 0000000000..c49febdce4 --- /dev/null +++ b/gr-blocks/examples/peak_detector2.grc @@ -0,0 +1,1045 @@ +<?xml version='1.0' encoding='ASCII'?> +<?grc format='1' created='3.7.8'?> +<flow_graph> + <timestamp>Wed Apr 8 18:17:58 2015</timestamp> + <block> + <key>options</key> + <param> + <key>id</key> + <value>test_peak2</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>title</key> + <value></value> + </param> + <param> + <key>author</key> + <value></value> + </param> + <param> + <key>description</key> + <value></value> + </param> + <param> + <key>window_size</key> + <value>1280, 1024</value> + </param> + <param> + <key>generate_options</key> + <value>qt_gui</value> + </param> + <param> + <key>category</key> + <value>Custom</value> + </param> + <param> + <key>run_options</key> + <value>run</value> + </param> + <param> + <key>run</key> + <value>True</value> + </param> + <param> + <key>max_nouts</key> + <value>0</value> + </param> + <param> + <key>realtime_scheduling</key> + <value></value> + </param> + <param> + <key>thread_safe_setters</key> + <value></value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(16, 11)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>factor</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>0.3</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(264, 11)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>alpha</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>0.001</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(440, 11)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>lookahead</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>int(samp_rate/1e3/1.1)</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(352, 11)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>samp_rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>100e3</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(176, 11)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>qtgui_time_sink_x</key> + <param> + <key>id</key> + <value>qtgui_time_sink_x_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>name</key> + <value>""</value> + </param> + <param> + <key>ylabel</key> + <value>Amplitude</value> + </param> + <param> + <key>yunit</key> + <value>""</value> + </param> + <param> + <key>size</key> + <value>512</value> + </param> + <param> + <key>srate</key> + <value>samp_rate</value> + </param> + <param> + <key>grid</key> + <value>True</value> + </param> + <param> + <key>autoscale</key> + <value>False</value> + </param> + <param> + <key>ymin</key> + <value>-1.5</value> + </param> + <param> + <key>ymax</key> + <value>1.5</value> + </param> + <param> + <key>nconnections</key> + <value>3</value> + </param> + <param> + <key>update_time</key> + <value>0.10</value> + </param> + <param> + <key>entags</key> + <value>True</value> + </param> + <param> + <key>gui_hint</key> + <value></value> + </param> + <param> + <key>tr_mode</key> + <value>qtgui.TRIG_MODE_AUTO</value> + </param> + <param> + <key>tr_slope</key> + <value>qtgui.TRIG_SLOPE_POS</value> + </param> + <param> + <key>tr_level</key> + <value>0.0</value> + </param> + <param> + <key>tr_delay</key> + <value>0</value> + </param> + <param> + <key>tr_chan</key> + <value>0</value> + </param> + <param> + <key>tr_tag</key> + <value>""</value> + </param> + <param> + <key>ctrlpanel</key> + <value>True</value> + </param> + <param> + <key>legend</key> + <value>True</value> + </param> + <param> + <key>label1</key> + <value>Input</value> + </param> + <param> + <key>width1</key> + <value>1</value> + </param> + <param> + <key>color1</key> + <value>"blue"</value> + </param> + <param> + <key>style1</key> + <value>1</value> + </param> + <param> + <key>marker1</key> + <value>-1</value> + </param> + <param> + <key>alpha1</key> + <value>1.0</value> + </param> + <param> + <key>label2</key> + <value>Peaks</value> + </param> + <param> + <key>width2</key> + <value>1</value> + </param> + <param> + <key>color2</key> + <value>"red"</value> + </param> + <param> + <key>style2</key> + <value>1</value> + </param> + <param> + <key>marker2</key> + <value>-1</value> + </param> + <param> + <key>alpha2</key> + <value>1.0</value> + </param> + <param> + <key>label3</key> + <value>Average</value> + </param> + <param> + <key>width3</key> + <value>1</value> + </param> + <param> + <key>color3</key> + <value>"green"</value> + </param> + <param> + <key>style3</key> + <value>1</value> + </param> + <param> + <key>marker3</key> + <value>-1</value> + </param> + <param> + <key>alpha3</key> + <value>1.0</value> + </param> + <param> + <key>label4</key> + <value></value> + </param> + <param> + <key>width4</key> + <value>1</value> + </param> + <param> + <key>color4</key> + <value>"black"</value> + </param> + <param> + <key>style4</key> + <value>1</value> + </param> + <param> + <key>marker4</key> + <value>-1</value> + </param> + <param> + <key>alpha4</key> + <value>1.0</value> + </param> + <param> + <key>label5</key> + <value></value> + </param> + <param> + <key>width5</key> + <value>1</value> + </param> + <param> + <key>color5</key> + <value>"cyan"</value> + </param> + <param> + <key>style5</key> + <value>1</value> + </param> + <param> + <key>marker5</key> + <value>-1</value> + </param> + <param> + <key>alpha5</key> + <value>1.0</value> + </param> + <param> + <key>label6</key> + <value></value> + </param> + <param> + <key>width6</key> + <value>1</value> + </param> + <param> + <key>color6</key> + <value>"magenta"</value> + </param> + <param> + <key>style6</key> + <value>1</value> + </param> + <param> + <key>marker6</key> + <value>-1</value> + </param> + <param> + <key>alpha6</key> + <value>1.0</value> + </param> + <param> + <key>label7</key> + <value></value> + </param> + <param> + <key>width7</key> + <value>1</value> + </param> + <param> + <key>color7</key> + <value>"yellow"</value> + </param> + <param> + <key>style7</key> + <value>1</value> + </param> + <param> + <key>marker7</key> + <value>-1</value> + </param> + <param> + <key>alpha7</key> + <value>1.0</value> + </param> + <param> + <key>label8</key> + <value></value> + </param> + <param> + <key>width8</key> + <value>1</value> + </param> + <param> + <key>color8</key> + <value>"dark red"</value> + </param> + <param> + <key>style8</key> + <value>1</value> + </param> + <param> + <key>marker8</key> + <value>-1</value> + </param> + <param> + <key>alpha8</key> + <value>1.0</value> + </param> + <param> + <key>label9</key> + <value></value> + </param> + <param> + <key>width9</key> + <value>1</value> + </param> + <param> + <key>color9</key> + <value>"dark green"</value> + </param> + <param> + <key>style9</key> + <value>1</value> + </param> + <param> + <key>marker9</key> + <value>-1</value> + </param> + <param> + <key>alpha9</key> + <value>1.0</value> + </param> + <param> + <key>label10</key> + <value></value> + </param> + <param> + <key>width10</key> + <value>1</value> + </param> + <param> + <key>color10</key> + <value>"blue"</value> + </param> + <param> + <key>style10</key> + <value>1</value> + </param> + <param> + <key>marker10</key> + <value>-1</value> + </param> + <param> + <key>alpha10</key> + <value>1.0</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(808, 152)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_char_to_float</key> + <param> + <key>id</key> + <value>blocks_char_to_float_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>scale</key> + <value>1</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(624, 83)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_peak_detector2_fb</key> + <param> + <key>id</key> + <value>blocks_peak_detector2_fb_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>threshold_factor_rise</key> + <value>factor</value> + </param> + <param> + <key>look_ahead</key> + <value>lookahead</value> + </param> + <param> + <key>alpha</key> + <value>alpha</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(408, 83)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_file_sink</key> + <param> + <key>id</key> + <value>blocks_file_sink_0_1</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>file</key> + <value>flag.data</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>unbuffered</key> + <value>False</value> + </param> + <param> + <key>append</key> + <value>False</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(808, 51)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_file_sink</key> + <param> + <key>id</key> + <value>blocks_file_sink_0_0</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>file</key> + <value>avg.data</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>unbuffered</key> + <value>False</value> + </param> + <param> + <key>append</key> + <value>False</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(784, 259)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_file_sink</key> + <param> + <key>id</key> + <value>blocks_file_sink_0</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>file</key> + <value>in.data</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>unbuffered</key> + <value>False</value> + </param> + <param> + <key>append</key> + <value>False</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(408, 187)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>offset</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value></value> + </param> + <param> + <key>value</key> + <value>0.1</value> + </param> + <param> + <key>start</key> + <value>-1.5</value> + </param> + <param> + <key>stop</key> + <value>1.5</value> + </param> + <param> + <key>step</key> + <value>0.1</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value></value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(16, 235)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>analog_sig_source_x</key> + <param> + <key>id</key> + <value>analog_sig_source_x_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>waveform</key> + <value>analog.GR_SAW_WAVE</value> + </param> + <param> + <key>freq</key> + <value>1000</value> + </param> + <param> + <key>amp</key> + <value>1</value> + </param> + <param> + <key>offset</key> + <value>offset</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(16, 115)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_throttle</key> + <param> + <key>id</key> + <value>blocks_throttle_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>samples_per_second</key> + <value>samp_rate</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>ignoretag</key> + <value>True</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(224, 147)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <connection> + <source_block_id>blocks_char_to_float_0</source_block_id> + <sink_block_id>blocks_file_sink_0_1</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_peak_detector2_fb_0</source_block_id> + <sink_block_id>blocks_file_sink_0_0</sink_block_id> + <source_key>1</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_throttle_0</source_block_id> + <sink_block_id>qtgui_time_sink_x_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_char_to_float_0</source_block_id> + <sink_block_id>qtgui_time_sink_x_0</sink_block_id> + <source_key>0</source_key> + <sink_key>1</sink_key> + </connection> + <connection> + <source_block_id>blocks_peak_detector2_fb_0</source_block_id> + <sink_block_id>qtgui_time_sink_x_0</sink_block_id> + <source_key>1</source_key> + <sink_key>2</sink_key> + </connection> + <connection> + <source_block_id>blocks_throttle_0</source_block_id> + <sink_block_id>blocks_peak_detector2_fb_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_peak_detector2_fb_0</source_block_id> + <sink_block_id>blocks_char_to_float_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_throttle_0</source_block_id> + <sink_block_id>blocks_file_sink_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>analog_sig_source_x_0</source_block_id> + <sink_block_id>blocks_throttle_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> +</flow_graph> diff --git a/gr-blocks/grc/blocks_block_tree.xml b/gr-blocks/grc/blocks_block_tree.xml index cc7b4b8c6f..fb4e10d1ba 100644 --- a/gr-blocks/grc/blocks_block_tree.xml +++ b/gr-blocks/grc/blocks_block_tree.xml @@ -51,6 +51,8 @@ </cat> <cat> <name>Control Port</name> + <block>blocks_ctrlport_monitor</block> + <block>blocks_ctrlport_monitor_performance</block> <block>blocks_ctrlport_probe2_x</block> <block>blocks_ctrlport_probe2_c</block> <block>blocks_ctrlport_probe_c</block> diff --git a/gr-blocks/grc/blocks_ctrlport_performance.xml b/gr-blocks/grc/blocks_ctrlport_performance.xml new file mode 100644 index 0000000000..ab17c9e263 --- /dev/null +++ b/gr-blocks/grc/blocks_ctrlport_performance.xml @@ -0,0 +1,48 @@ +<?xml version="1.0"?> + +<!-- + Copyright 2012 Free Software Foundation, Inc. + + This file is part of GNU Radio + + GNU Radio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GNU Radio is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Radio; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, + Boston, MA 02110-1301, USA. +--> + +<block> + <name>CtrlPort Performance Monitor</name> + <key>blocks_ctrlport_monitor_performance</key> + <import>from gnuradio.ctrlport.monitor import *</import> + <make>not $en or monitor("gr-perf-monitorx")</make> + <param> + <name>Enabled</name> + <key>en</key> + <value></value> + <type>enum</type> + <option> + <name>True</name> + <key>True</key> + </option> + <option> + <name>False</name> + <key>False</key> + </option> + </param> + + <doc> + Place this in a graph to launch a QtPy GR CtrlPort Performance Monitor app. + </doc> + +</block> diff --git a/gr-blocks/grc/blocks_ctrlport_viewer.xml b/gr-blocks/grc/blocks_ctrlport_viewer.xml new file mode 100644 index 0000000000..1d5a2e5931 --- /dev/null +++ b/gr-blocks/grc/blocks_ctrlport_viewer.xml @@ -0,0 +1,48 @@ +<?xml version="1.0"?> + +<!-- + Copyright 2012 Free Software Foundation, Inc. + + This file is part of GNU Radio + + GNU Radio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GNU Radio is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Radio; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, + Boston, MA 02110-1301, USA. +--> + +<block> + <name>CtrlPort Monitor</name> + <key>blocks_ctrlport_monitor</key> + <import>from gnuradio.ctrlport.monitor import *</import> + <make>not $en or monitor()</make> + <param> + <name>Enabled</name> + <key>en</key> + <value></value> + <type>enum</type> + <option> + <name>True</name> + <key>True</key> + </option> + <option> + <name>False</name> + <key>False</key> + </option> + </param> + + <doc> + Place this in a graph to launch a QtPy GR CtrlPort Monitor app. + </doc> + +</block> diff --git a/gr-blocks/grc/blocks_peak_detector2_fb.xml b/gr-blocks/grc/blocks_peak_detector2_fb.xml index 584e7a1fb3..0b25e01680 100644 --- a/gr-blocks/grc/blocks_peak_detector2_fb.xml +++ b/gr-blocks/grc/blocks_peak_detector2_fb.xml @@ -38,4 +38,9 @@ <name>out</name> <type>byte</type> </source> + <source> + <name>debug</name> + <type>float</type> + <optional>1</optional> + </source> </block> diff --git a/gr-blocks/lib/ctrlport_probe2_b_impl.cc b/gr-blocks/lib/ctrlport_probe2_b_impl.cc index e6e2570ea6..996e997d96 100644 --- a/gr-blocks/lib/ctrlport_probe2_b_impl.cc +++ b/gr-blocks/lib/ctrlport_probe2_b_impl.cc @@ -63,39 +63,27 @@ namespace gr { ninput_items_required[i] = d_len; } - // boost::shared_mutex mutex_buffer; - // mutable boost::mutex mutex_notify; - // boost::condition_variable condition_buffer_ready; std::vector<signed char> ctrlport_probe2_b_impl::get() { - mutex_buffer.lock(); - d_buffer.clear(); - mutex_buffer.unlock(); - - // wait for condition - boost::mutex::scoped_lock lock(mutex_notify); - condition_buffer_ready.wait(lock); - - mutex_buffer.lock(); - std::vector<signed char> buf_copy = d_buffer; - assert(buf_copy.size() == d_len); - mutex_buffer.unlock(); - - return buf_copy; + return buffered_get.get(); } void ctrlport_probe2_b_impl::set_length(int len) { + gr::thread::scoped_lock guard(d_setlock); + if(len > 8191) { - std::cerr << "probe2_b: length " << len - << " exceeds maximum buffer size of 8191" << std::endl; + GR_LOG_WARN(d_logger, + boost::format("probe2_b: length %1% exceeds maximum" + " buffer size of 8191") % len); len = 8191; } d_len = len; - d_buffer.reserve(d_len); + d_buffer.resize(d_len); + d_index = 0; } int @@ -111,23 +99,22 @@ namespace gr { { const char *in = (const char*)input_items[0]; + gr::thread::scoped_lock guard(d_setlock); + // copy samples to get buffer if we need samples - mutex_buffer.lock(); - if(d_buffer.size() < d_len) { + if(d_index < d_len) { // copy smaller of remaining buffer space and num inputs to work() - int num_copy = std::min( (int)(d_len - d_buffer.size()), noutput_items ); + int num_copy = std::min( (int)(d_len - d_index), noutput_items ); - // TODO: convert this to a copy operator for speed... - for(int i = 0; i < num_copy; i++) { - d_buffer.push_back(in[i]); - } + memcpy(&d_buffer[d_index], in, num_copy*sizeof(char)); + d_index += num_copy; + } - // notify the waiting get() if we fill up the buffer - if(d_buffer.size() == d_len) { - condition_buffer_ready.notify_one(); - } + // notify the waiting get() if we fill up the buffer + if(d_index == d_len) { + buffered_get.offer_data(d_buffer); + d_index = 0; } - mutex_buffer.unlock(); return noutput_items; } diff --git a/gr-blocks/lib/ctrlport_probe2_b_impl.h b/gr-blocks/lib/ctrlport_probe2_b_impl.h index 155dd4c23c..165f0d33db 100644 --- a/gr-blocks/lib/ctrlport_probe2_b_impl.h +++ b/gr-blocks/lib/ctrlport_probe2_b_impl.h @@ -25,7 +25,7 @@ #include <gnuradio/blocks/ctrlport_probe2_b.h> #include <gnuradio/rpcregisterhelpers.h> -#include <boost/thread/shared_mutex.hpp> +#include <gnuradio/rpcbufferedget.h> namespace gr { namespace blocks { @@ -37,11 +37,10 @@ namespace gr { std::string d_desc; size_t d_len; unsigned int d_disp_mask; - boost::shared_mutex mutex_buffer; - mutable boost::mutex mutex_notify; - boost::condition_variable condition_buffer_ready; + size_t d_index; std::vector<signed char> d_buffer; + rpcbufferedget< std::vector<signed char> > buffered_get; public: ctrlport_probe2_b_impl(const std::string &id, const std::string &desc, @@ -66,4 +65,3 @@ namespace gr { } /* namespace gr */ #endif /* INCLUDED_CTRLPORT_PROBE2_C_IMPL_H */ - diff --git a/gr-blocks/lib/ctrlport_probe2_c_impl.cc b/gr-blocks/lib/ctrlport_probe2_c_impl.cc index aa038c0765..b8ed0af444 100644 --- a/gr-blocks/lib/ctrlport_probe2_c_impl.cc +++ b/gr-blocks/lib/ctrlport_probe2_c_impl.cc @@ -64,39 +64,27 @@ namespace gr { ninput_items_required[i] = d_len; } - // boost::shared_mutex mutex_buffer; - // mutable boost::mutex mutex_notify; - // boost::condition_variable condition_buffer_ready; std::vector<gr_complex> ctrlport_probe2_c_impl::get() { - mutex_buffer.lock(); - d_buffer.clear(); - mutex_buffer.unlock(); - - // wait for condition - boost::mutex::scoped_lock lock(mutex_notify); - condition_buffer_ready.wait(lock); - - mutex_buffer.lock(); - std::vector<gr_complex> buf_copy = d_buffer; - assert(buf_copy.size() == d_len); - mutex_buffer.unlock(); - - return buf_copy; + return buffered_get.get(); } void ctrlport_probe2_c_impl::set_length(int len) { + gr::thread::scoped_lock guard(d_setlock); + if(len > 8191) { - std::cerr << "probe2_c: length " << len - << " exceeds maximum buffer size of 8191" << std::endl; + GR_LOG_WARN(d_logger, + boost::format("probe2_c: length %1% exceeds maximum" + " buffer size of 8191") % len); len = 8191; } d_len = len; - d_buffer.reserve(d_len); + d_buffer.resize(d_len); + d_index = 0; } int @@ -112,23 +100,22 @@ namespace gr { { const gr_complex *in = (const gr_complex*)input_items[0]; + gr::thread::scoped_lock guard(d_setlock); + // copy samples to get buffer if we need samples - mutex_buffer.lock(); - if(d_buffer.size() < d_len) { + if(d_index < d_len) { // copy smaller of remaining buffer space and num inputs to work() - int num_copy = std::min( (int)(d_len - d_buffer.size()), noutput_items ); + int num_copy = std::min( (int)(d_len - d_index), noutput_items ); - // TODO: convert this to a copy operator for speed... - for(int i = 0; i < num_copy; i++) { - d_buffer.push_back(in[i]); - } + memcpy(&d_buffer[d_index], in, num_copy*sizeof(gr_complex)); + d_index += num_copy; + } - // notify the waiting get() if we fill up the buffer - if(d_buffer.size() == d_len) { - condition_buffer_ready.notify_one(); - } + // notify the waiting get() if we fill up the buffer + if(d_index == d_len) { + buffered_get.offer_data(d_buffer); + d_index = 0; } - mutex_buffer.unlock(); return noutput_items; } diff --git a/gr-blocks/lib/ctrlport_probe2_c_impl.h b/gr-blocks/lib/ctrlport_probe2_c_impl.h index 15ff0f4ea2..119738a481 100644 --- a/gr-blocks/lib/ctrlport_probe2_c_impl.h +++ b/gr-blocks/lib/ctrlport_probe2_c_impl.h @@ -25,7 +25,7 @@ #include <gnuradio/blocks/ctrlport_probe2_c.h> #include <gnuradio/rpcregisterhelpers.h> -#include <boost/thread/shared_mutex.hpp> +#include <gnuradio/rpcbufferedget.h> namespace gr { namespace blocks { @@ -37,11 +37,10 @@ namespace gr { std::string d_desc; size_t d_len; unsigned int d_disp_mask; - boost::shared_mutex mutex_buffer; - mutable boost::mutex mutex_notify; - boost::condition_variable condition_buffer_ready; + size_t d_index; std::vector<gr_complex> d_buffer; + rpcbufferedget< std::vector<gr_complex> > buffered_get; public: ctrlport_probe2_c_impl(const std::string &id, const std::string &desc, @@ -66,4 +65,3 @@ namespace gr { } /* namespace gr */ #endif /* INCLUDED_CTRLPORT_PROBE2_C_IMPL_H */ - diff --git a/gr-blocks/lib/ctrlport_probe2_f_impl.cc b/gr-blocks/lib/ctrlport_probe2_f_impl.cc index b53b2dc3cb..ff37401e88 100644 --- a/gr-blocks/lib/ctrlport_probe2_f_impl.cc +++ b/gr-blocks/lib/ctrlport_probe2_f_impl.cc @@ -62,39 +62,27 @@ namespace gr { ninput_items_required[i] = d_len; } - // boost::shared_mutex mutex_buffer; - // mutable boost::mutex mutex_notify; - // boost::condition_variable condition_buffer_ready; std::vector<float> ctrlport_probe2_f_impl::get() { - mutex_buffer.lock(); - d_buffer.clear(); - mutex_buffer.unlock(); - - // wait for condition - boost::mutex::scoped_lock lock(mutex_notify); - condition_buffer_ready.wait(lock); - - mutex_buffer.lock(); - std::vector<float> buf_copy = d_buffer; - assert(buf_copy.size() == d_len); - mutex_buffer.unlock(); - - return buf_copy; + return buffered_get.get(); } void ctrlport_probe2_f_impl::set_length(int len) { + gr::thread::scoped_lock guard(d_setlock); + if(len > 8191) { - std::cerr << "probe2_f: length " << len - << " exceeds maximum buffer size of 8191" << std::endl; + GR_LOG_WARN(d_logger, + boost::format("probe2_f: length %1% exceeds maximum" + " buffer size of 8191") % len); len = 8191; } d_len = len; - d_buffer.reserve(d_len); + d_buffer.resize(d_len); + d_index = 0; } int @@ -110,23 +98,22 @@ namespace gr { { const float *in = (const float*)input_items[0]; + gr::thread::scoped_lock guard(d_setlock); + // copy samples to get buffer if we need samples - mutex_buffer.lock(); - if(d_buffer.size() < d_len) { + if(d_index < d_len) { // copy smaller of remaining buffer space and num inputs to work() - int num_copy = std::min( (int)(d_len - d_buffer.size()), noutput_items ); + int num_copy = std::min( (int)(d_len - d_index), noutput_items ); - // TODO: convert this to a copy operator for speed... - for(int i = 0; i < num_copy; i++) { - d_buffer.push_back(in[i]); - } + memcpy(&d_buffer[d_index], in, num_copy*sizeof(float)); + d_index += num_copy; + } - // notify the waiting get() if we fill up the buffer - if(d_buffer.size() == d_len) { - condition_buffer_ready.notify_one(); - } + // notify the waiting get() if we fill up the buffer + if(d_index == d_len) { + buffered_get.offer_data(d_buffer); + d_index = 0; } - mutex_buffer.unlock(); return noutput_items; } diff --git a/gr-blocks/lib/ctrlport_probe2_f_impl.h b/gr-blocks/lib/ctrlport_probe2_f_impl.h index a4aa099237..6aec0789f4 100644 --- a/gr-blocks/lib/ctrlport_probe2_f_impl.h +++ b/gr-blocks/lib/ctrlport_probe2_f_impl.h @@ -25,7 +25,7 @@ #include <gnuradio/blocks/ctrlport_probe2_f.h> #include <gnuradio/rpcregisterhelpers.h> -#include <boost/thread/shared_mutex.hpp> +#include <gnuradio/rpcbufferedget.h> namespace gr { namespace blocks { @@ -37,11 +37,10 @@ namespace gr { std::string d_desc; size_t d_len; unsigned int d_disp_mask; - boost::shared_mutex mutex_buffer; - mutable boost::mutex mutex_notify; - boost::condition_variable condition_buffer_ready; + size_t d_index; std::vector<float> d_buffer; + rpcbufferedget< std::vector<float> > buffered_get; public: ctrlport_probe2_f_impl(const std::string &id, const std::string &desc, @@ -66,4 +65,3 @@ namespace gr { } /* namespace gr */ #endif /* INCLUDED_CTRLPORT_PROBE2_F_IMPL_H */ - diff --git a/gr-blocks/lib/ctrlport_probe2_i_impl.cc b/gr-blocks/lib/ctrlport_probe2_i_impl.cc index 77dca2ad0d..7e17d8e8fe 100644 --- a/gr-blocks/lib/ctrlport_probe2_i_impl.cc +++ b/gr-blocks/lib/ctrlport_probe2_i_impl.cc @@ -64,39 +64,27 @@ namespace gr { ninput_items_required[i] = d_len; } - // boost::shared_mutex mutex_buffer; - // mutable boost::mutex mutex_notify; - // boost::condition_variable condition_buffer_ready; std::vector<int> ctrlport_probe2_i_impl::get() { - mutex_buffer.lock(); - d_buffer.clear(); - mutex_buffer.unlock(); - - // wait for condition - boost::mutex::scoped_lock lock(mutex_notify); - condition_buffer_ready.wait(lock); - - mutex_buffer.lock(); - std::vector<int> buf_copy = d_buffer; - assert(buf_copy.size() == d_len); - mutex_buffer.unlock(); - - return buf_copy; + return buffered_get.get(); } void ctrlport_probe2_i_impl::set_length(int len) { + gr::thread::scoped_lock guard(d_setlock); + if(len > 8191) { - std::cerr << "probe2_i: length " << len - << " exceeds maximum buffer size of 8191" << std::endl; + GR_LOG_WARN(d_logger, + boost::format("probe2_i: length %1% exceeds maximum" + " buffer size of 8191") % len); len = 8191; } d_len = len; - d_buffer.reserve(d_len); + d_buffer.resize(d_len); + d_index = 0; } int @@ -112,23 +100,22 @@ namespace gr { { const int *in = (const int*)input_items[0]; + gr::thread::scoped_lock guard(d_setlock); + // copy samples to get buffer if we need samples - mutex_buffer.lock(); - if(d_buffer.size() < d_len) { + if(d_index < d_len) { // copy smaller of remaining buffer space and num inputs to work() - int num_copy = std::min( (int)(d_len - d_buffer.size()), noutput_items ); + int num_copy = std::min( (int)(d_len - d_index), noutput_items ); - // TODO: convert this to a copy operator for speed... - for(int i = 0; i < num_copy; i++) { - d_buffer.push_back(in[i]); - } + memcpy(&d_buffer[d_index], in, num_copy*sizeof(int)); + d_index += num_copy; + } - // notify the waiting get() if we fill up the buffer - if(d_buffer.size() == d_len) { - condition_buffer_ready.notify_one(); - } + // notify the waiting get() if we fill up the buffer + if(d_index == d_len) { + buffered_get.offer_data(d_buffer); + d_index = 0; } - mutex_buffer.unlock(); return noutput_items; } diff --git a/gr-blocks/lib/ctrlport_probe2_i_impl.h b/gr-blocks/lib/ctrlport_probe2_i_impl.h index 06493ac23a..2832af07ec 100644 --- a/gr-blocks/lib/ctrlport_probe2_i_impl.h +++ b/gr-blocks/lib/ctrlport_probe2_i_impl.h @@ -25,7 +25,7 @@ #include <gnuradio/blocks/ctrlport_probe2_i.h> #include <gnuradio/rpcregisterhelpers.h> -#include <boost/thread/shared_mutex.hpp> +#include <gnuradio/rpcbufferedget.h> namespace gr { namespace blocks { @@ -37,11 +37,10 @@ namespace gr { std::string d_desc; size_t d_len; unsigned int d_disp_mask; - boost::shared_mutex mutex_buffer; - mutable boost::mutex mutex_notify; - boost::condition_variable condition_buffer_ready; + size_t d_index; std::vector<int> d_buffer; + rpcbufferedget< std::vector<int> > buffered_get; public: ctrlport_probe2_i_impl(const std::string &id, const std::string &desc, @@ -66,4 +65,3 @@ namespace gr { } /* namespace gr */ #endif /* INCLUDED_CTRLPORT_PROBE2_I_IMPL_H */ - diff --git a/gr-blocks/lib/ctrlport_probe2_s_impl.cc b/gr-blocks/lib/ctrlport_probe2_s_impl.cc index 6a4ade462c..9924243db0 100644 --- a/gr-blocks/lib/ctrlport_probe2_s_impl.cc +++ b/gr-blocks/lib/ctrlport_probe2_s_impl.cc @@ -64,39 +64,27 @@ namespace gr { ninput_items_required[i] = d_len; } - // boost::shared_mutex mutex_buffer; - // mutable boost::mutex mutex_notify; - // boost::condition_variable condition_buffer_ready; std::vector<short> ctrlport_probe2_s_impl::get() { - mutex_buffer.lock(); - d_buffer.clear(); - mutex_buffer.unlock(); - - // wait for condition - boost::mutex::scoped_lock lock(mutex_notify); - condition_buffer_ready.wait(lock); - - mutex_buffer.lock(); - std::vector<short> buf_copy = d_buffer; - assert(buf_copy.size() == d_len); - mutex_buffer.unlock(); - - return buf_copy; + return buffered_get.get(); } void ctrlport_probe2_s_impl::set_length(int len) { + gr::thread::scoped_lock guard(d_setlock); + if(len > 8191) { - std::cerr << "probe2_s: length " << len - << " exceeds maximum buffer size of 8191" << std::endl; + GR_LOG_WARN(d_logger, + boost::format("probe2_s: length %1% exceeds maximum" + " buffer size of 8191") % len); len = 8191; } d_len = len; - d_buffer.reserve(d_len); + d_buffer.resize(d_len); + d_index = 0; } int @@ -112,23 +100,21 @@ namespace gr { { const short *in = (const short*)input_items[0]; + gr::thread::scoped_lock guard(d_setlock); + // copy samples to get buffer if we need samples - mutex_buffer.lock(); - if(d_buffer.size() < d_len) { + if(d_index < d_len) { // copy smaller of remaining buffer space and num inputs to work() - int num_copy = std::min( (int)(d_len - d_buffer.size()), noutput_items ); + int num_copy = std::min( (int)(d_len - d_index), noutput_items ); - // TODO: convert this to a copy operator for speed... - for(int i = 0; i < num_copy; i++) { - d_buffer.push_back(in[i]); - } + memcpy(&d_buffer[d_index], in, num_copy*sizeof(short)); + d_index += num_copy; + } - // notify the waiting get() if we fill up the buffer - if(d_buffer.size() == d_len) { - condition_buffer_ready.notify_one(); - } + // notify the waiting get() if we fill up the buffer + if(d_index == d_len) { + buffered_get.offer_data(d_buffer); } - mutex_buffer.unlock(); return noutput_items; } diff --git a/gr-blocks/lib/ctrlport_probe2_s_impl.h b/gr-blocks/lib/ctrlport_probe2_s_impl.h index 078dd56b73..a608a7898c 100644 --- a/gr-blocks/lib/ctrlport_probe2_s_impl.h +++ b/gr-blocks/lib/ctrlport_probe2_s_impl.h @@ -25,7 +25,7 @@ #include <gnuradio/blocks/ctrlport_probe2_s.h> #include <gnuradio/rpcregisterhelpers.h> -#include <boost/thread/shared_mutex.hpp> +#include <gnuradio/rpcbufferedget.h> namespace gr { namespace blocks { @@ -37,11 +37,10 @@ namespace gr { std::string d_desc; size_t d_len; unsigned int d_disp_mask; - boost::shared_mutex mutex_buffer; - mutable boost::mutex mutex_notify; - boost::condition_variable condition_buffer_ready; + size_t d_index; std::vector<short> d_buffer; + rpcbufferedget< std::vector<short> > buffered_get; public: ctrlport_probe2_s_impl(const std::string &id, const std::string &desc, @@ -66,4 +65,3 @@ namespace gr { } /* namespace gr */ #endif /* INCLUDED_CTRLPORT_PROBE2_S_IMPL_H */ - diff --git a/gr-blocks/lib/peak_detector2_fb_impl.cc b/gr-blocks/lib/peak_detector2_fb_impl.cc index 7ff7f542ec..dc13e66dbe 100644 --- a/gr-blocks/lib/peak_detector2_fb_impl.cc +++ b/gr-blocks/lib/peak_detector2_fb_impl.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2007,2010,2013 Free Software Foundation, Inc. + * Copyright 2007,2010,2013,2015 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -45,15 +45,46 @@ namespace gr { : sync_block("peak_detector2_fb", io_signature::make(1, 1, sizeof(float)), io_signature::make2(1, 2, sizeof(char), sizeof(float))), - d_threshold_factor_rise(threshold_factor_rise), - d_look_ahead(look_ahead), d_alpha(alpha), d_avg(0.0f), d_found(false) + d_avg(0.0f), d_found(false) { + set_threshold_factor_rise(threshold_factor_rise); + set_look_ahead(look_ahead); + set_alpha(alpha); } peak_detector2_fb_impl::~peak_detector2_fb_impl() { } + void + peak_detector2_fb_impl::set_threshold_factor_rise(float thr) + { + gr::thread::scoped_lock lock(d_setlock); + d_threshold_factor_rise = thr; + invalidate(); + } + + void + peak_detector2_fb_impl::set_look_ahead(int look) + { + gr::thread::scoped_lock lock(d_setlock); + d_look_ahead = look; + invalidate(); + } + + void + peak_detector2_fb_impl::set_alpha(float alpha) + { + d_alpha = alpha; + } + + void + peak_detector2_fb_impl::invalidate() + { + d_found = false; + set_output_multiple(1); + } + int peak_detector2_fb_impl::work(int noutput_items, gr_vector_const_void_star &input_items, @@ -61,53 +92,53 @@ namespace gr { { float *iptr = (float *)input_items[0]; char *optr = (char *)output_items[0]; + float *sigout; + + if(output_items.size() == 2) + sigout = (float *)output_items[1]; memset(optr, 0, noutput_items*sizeof(char)); - for(int i = 0; i < noutput_items; i++) { - if(!d_found) { - // Have not yet detected presence of peak + gr::thread::scoped_lock lock(d_setlock); + + // have not crossed threshold yet + if(d_found==false) { + for(int i = 0; i < noutput_items; i++) { + d_avg = d_alpha*iptr[i] + (1.0f - d_alpha)*d_avg; + if(output_items.size() == 2) + sigout[i]=d_avg; if(iptr[i] > d_avg * (1.0f + d_threshold_factor_rise)) { d_found = true; - d_look_ahead_remaining = d_look_ahead; d_peak_val = -(float)INFINITY; - } - else { - d_avg = d_alpha*iptr[i] + (1.0f - d_alpha)*d_avg; + set_output_multiple(d_look_ahead); + return i; } } - else { - // Detected presence of peak + return noutput_items; + } // end d_found==false + + // can complete in this call + else if(noutput_items >= d_look_ahead) { + for(int i = 0; i < d_look_ahead; i++) { + d_avg = d_alpha*iptr[i] + (1.0f - d_alpha)*d_avg; + if(output_items.size() == 2) + sigout[i]=d_avg; if(iptr[i] > d_peak_val) { d_peak_val = iptr[i]; - d_peak_ind = i; - } - else if(d_look_ahead_remaining <= 0) { - optr[d_peak_ind] = 1; - d_found = false; - d_avg = iptr[i]; + d_peak_ind =i; } - - // Have not yet located peak, loop and keep searching. - d_look_ahead_remaining--; - } - - // Every iteration of the loop, write debugging signal out if - // connected: - if(output_items.size() == 2) { - float *sigout = (float *)output_items[1]; - sigout[i] = d_avg; } - } // loop - - if(!d_found) - return noutput_items; + optr[d_peak_ind] = 1; - // else if detected presence, keep searching during the next call to work. - int tmp = d_peak_ind; - d_peak_ind = 1; + // restart the search + invalidate(); + return d_look_ahead; + } // end can complete in this call - return tmp - 1; + // cannot complete in this call + else { + return 0; // ask for more + } } } /* namespace blocks */ diff --git a/gr-blocks/lib/peak_detector2_fb_impl.h b/gr-blocks/lib/peak_detector2_fb_impl.h index f5a8ac1a6b..4e16c93dac 100644 --- a/gr-blocks/lib/peak_detector2_fb_impl.h +++ b/gr-blocks/lib/peak_detector2_fb_impl.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2007,2013 Free Software Foundation, Inc. + * Copyright 2007,2013,2015 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -40,14 +40,16 @@ namespace gr { float d_avg; bool d_found; + void invalidate(); + public: peak_detector2_fb_impl(float threshold_factor_rise, int look_ahead, float alpha); ~peak_detector2_fb_impl(); - void set_threshold_factor_rise(float thr) { d_threshold_factor_rise = thr; } - void set_look_ahead(int look) { d_look_ahead = look; } - void set_alpha(float alpha) { d_alpha = alpha; } + void set_threshold_factor_rise(float thr); + void set_look_ahead(int look); + void set_alpha(float alpha); float threshold_factor_rise() { return d_threshold_factor_rise; } int look_ahead() { return d_look_ahead; } diff --git a/gr-blocks/python/blocks/CMakeLists.txt b/gr-blocks/python/blocks/CMakeLists.txt index 44977313cf..19d808b1dd 100644 --- a/gr-blocks/python/blocks/CMakeLists.txt +++ b/gr-blocks/python/blocks/CMakeLists.txt @@ -43,14 +43,14 @@ if(ENABLE_TESTING) include(GrTest) file(GLOB py_qa_test_files "qa_*.py") - # Force out the controlport QA tests if we've disabled it. - if(NOT ENABLE_GR_CTRLPORT) + # Force out the controlport QA tests if we have no backends to use. + if(CTRLPORT_BACKENDS EQUAL 0) list(REMOVE_ITEM py_qa_test_files ${CMAKE_CURRENT_SOURCE_DIR}/qa_cpp_py_binding.py ${CMAKE_CURRENT_SOURCE_DIR}/qa_cpp_py_binding_set.py ${CMAKE_CURRENT_SOURCE_DIR}/qa_ctrlport_probes.py ) - endif(NOT ENABLE_GR_CTRLPORT) + endif(CTRLPORT_BACKENDS EQUAL 0) foreach(py_qa_test_file ${py_qa_test_files}) get_filename_component(py_qa_test_name ${py_qa_test_file} NAME_WE) diff --git a/gr-blocks/python/blocks/qa_cpp_py_binding.py b/gr-blocks/python/blocks/qa_cpp_py_binding.py new file mode 100644 index 0000000000..23a5c9b826 --- /dev/null +++ b/gr-blocks/python/blocks/qa_cpp_py_binding.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python +# +# Copyright 2012,2013,2015 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +# +# This program tests mixed python and c++ ctrlport exports in a single app +# + +import sys, time, random, numpy, re +from gnuradio import gr, gr_unittest, blocks + +from gnuradio.ctrlport import GNURadio +from gnuradio import ctrlport +import os + +def get1(): + return "success" + +def get2(): + return "failure" + +class inc_class: + def __init__(self): + self.val = 1 + def pp(self): + self.val = self.val+1 + return self.val + +get3 = inc_class() + +def get4(): + random.seed(0) + rv = random.random() + return rv + +def get5(): + numpy.random.seed(0) + samp_t = numpy.random.randn(24)+1j*numpy.random.randn(24); + samp_f = numpy.fft.fft(samp_t); + log_pow_f = 20*numpy.log10(numpy.abs(samp_f)) + rv = list(log_pow_f) + return rv; + +def get6(): + numpy.random.seed(0) + samp_t = numpy.random.randn(1024)+1j*numpy.random.randn(1024); + rv = list(samp_t) + return rv; + +class test_cpp_py_binding(gr_unittest.TestCase): + + def setUp(self): + self.tb = gr.top_block() + os.environ['GR_CONF_CONTROLPORT_ON'] = 'True' + + def tearDown(self): + self.tb = None + + def test_001(self): + v1 = gr.RPC_get_string("pyland", "v1", "unit_1_string", + "Python Exported String", "", "", "", + gr.DISPNULL) + v1.activate(get1) + + v2 = gr.RPC_get_string("pyland", "v2", "unit_2_string", + "Python Exported String", "", "", "", + gr.DISPNULL) + v2.activate(get2) + + v3 = gr.RPC_get_int("pyland", "v3", "unit_3_int", + "Python Exported Int", 0, 100, 1, + gr.DISPNULL) + v3.activate(get3.pp) + + v4 = gr.RPC_get_double("pyland", "time", "unit_4_time_double", + "Python Exported Double", 0, 1000, 1, + gr.DISPNULL) + v4.activate(get4) + + v5 = gr.RPC_get_vector_float("pyland", "fvec", "unit_5_float_vector", + "Python Exported Float Vector", [], [], [], + gr.DISPTIME | gr.DISPOPTCPLX) + v5.activate(get5) + + v6 = gr.RPC_get_vector_gr_complex("pyland", "cvec", "unit_6_gr_complex_vector", + "Python Exported Complex Vector", [], [], [], + gr.DISPXY | gr.DISPOPTSCATTER) + v6.activate(get6) + + # print some variables locally + val = get1() + rval = v1.get() + self.assertEqual(val, rval) + + val = get2() + rval = v2.get() + self.assertEqual(val, rval) + + val = get3.pp() + rval = v3.get() + self.assertEqual(val+1, rval) + + val = get4() + rval = v4.get() + self.assertEqual(val, rval) + + val = get5() + rval = v5.get() + self.assertComplexTuplesAlmostEqual(val, rval, 5) + + val = get6() + rval = v6.get() + self.assertComplexTuplesAlmostEqual(val, rval, 5) + + def test_002(self): + data = range(1,9) + + self.src = blocks.vector_source_c(data) + self.p1 = blocks.ctrlport_probe_c("aaa","C++ exported variable") + self.p2 = blocks.ctrlport_probe_c("bbb","C++ exported variable") + probe_name = self.p2.alias() + + self.tb.connect(self.src, self.p1) + self.tb.connect(self.src, self.p2) + self.tb.start() + + # Probes return complex values as list of floats with re, im + # Imaginary parts of this data set are 0. + expected_result = [1, 2, 3, 4, + 5, 6, 7, 8] + + # Make sure we have time for flowgraph to run + time.sleep(0.1) + + # Get available endpoint + ep = gr.rpcmanager_get().endpoints()[0] + hostname = re.search("-h (\S+|\d+\.\d+\.\d+\.\d+)", ep).group(1) + portnum = re.search("-p (\d+)", ep).group(1) + argv = [None, hostname, portnum] + + # Initialize a simple ControlPort client from endpoint + from gnuradio.ctrlport.GNURadioControlPortClient import GNURadioControlPortClient + radiosys = GNURadioControlPortClient(argv=argv, rpcmethod='thrift') + radio = radiosys.client + + # Get all exported knobs + ret = radio.getKnobs([probe_name + "::bbb"]) + for name in ret.keys(): + result = ret[name].value + self.assertEqual(result, expected_result) + + self.tb.stop() + +if __name__ == '__main__': + gr_unittest.run(test_cpp_py_binding, "test_cpp_py_binding.xml") diff --git a/gr-blocks/python/blocks/qa_cpp_py_binding_set.py b/gr-blocks/python/blocks/qa_cpp_py_binding_set.py new file mode 100644 index 0000000000..5b81de08f9 --- /dev/null +++ b/gr-blocks/python/blocks/qa_cpp_py_binding_set.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python +# +# Copyright 2012,2013,2015 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +# +# This program tests mixed python and c++ GRCP sets in a single app +# + +import sys, time, random, numpy, re +from gnuradio import gr, gr_unittest, blocks + +from gnuradio.ctrlport import GNURadio +from gnuradio import ctrlport +import os + +class inc_class: + def __init__(self,val): + self.val = val; + + def _get(self): + #print "returning get (val = %s)"%(str(self.val)); + return self.val; + + def _set(self,val): + #print "updating val to %s"%(str(val)); + self.val = val; + return; + +getset1 = inc_class(10); +getset2 = inc_class(100.0); +getset3 = inc_class("test"); + +class test_cpp_py_binding_set(gr_unittest.TestCase): + def setUp(self): + self.tb = gr.top_block() + os.environ['GR_CONF_CONTROLPORT_ON'] = 'True' + + def tearDown(self): + self.tb = None + + def test_001(self): + + g1 = gr.RPC_get_int("pyland", "v1", "unit_1_int", + "Python Exported Int", 0, 100, 10, + gr.DISPNULL) + g1.activate(getset1._get) + s1 = gr.RPC_get_int("pyland", "v1", "unit_1_int", + "Python Exported Int", 0, 100, 10, + gr.DISPNULL) + s1.activate(getset1._set) + time.sleep(0.01) + + # test int variables + getset1._set(21) + val = getset1._get() + rval = g1.get() + self.assertEqual(val, rval) + + g2 = gr.RPC_get_float("pyland", "v2", "unit_2_float", + "Python Exported Float", -100, 1000.0, 100.0, + gr.DISPNULL) + g2.activate(getset2._get) + s2 = gr.RPC_get_float("pyland", "v2", "unit_2_float", + "Python Exported Float", -100, 1000.0, 100.0, + gr.DISPNULL) + s2.activate(getset2._set) + time.sleep(0.01) + + # test float variables + getset2._set(123.456) + val = getset2._get() + rval = g2.get() + self.assertAlmostEqual(val, rval, 4) + + g3 = gr.RPC_get_string("pyland", "v3", "unit_3_string", + "Python Exported String", "", "", "", + gr.DISPNULL) + g3.activate(getset3._get) + s3 = gr.RPC_get_string("pyland", "v3", "unit_3_string", + "Python Exported String", "", "", "", + gr.DISPNULL) + s3.activate(getset3._set) + time.sleep(0.01) + + # test string variables + getset3._set("third test") + val = getset3._get() + rval = g3.get() + self.assertEqual(val, rval) + + + def test_002(self): + data = range(1, 10) + + self.src = blocks.vector_source_c(data, True) + self.p = blocks.nop(gr.sizeof_gr_complex) + self.p.set_ctrlport_test(0); + probe_info = self.p.alias() + + self.tb.connect(self.src, self.p) + + # Get available endpoint + ep = gr.rpcmanager_get().endpoints()[0] + hostname = re.search("-h (\S+|\d+\.\d+\.\d+\.\d+)", ep).group(1) + portnum = re.search("-p (\d+)", ep).group(1) + argv = [None, hostname, portnum] + + # Initialize a simple ControlPort client from endpoint + from gnuradio.ctrlport.GNURadioControlPortClient import GNURadioControlPortClient + radiosys = GNURadioControlPortClient(argv=argv, rpcmethod='thrift') + radio = radiosys.client + + self.tb.start() + + # Make sure we have time for flowgraph to run + time.sleep(0.1) + + # Get all exported knobs + key_name_test = probe_info+"::test" + ret = radio.getKnobs([key_name_test,]) + + ret[key_name_test].value = 10 + radio.setKnobs({key_name_test: ret[key_name_test]}) + + ret = radio.getKnobs([]) + result_test = ret[key_name_test].value + self.assertEqual(result_test, 10) + + self.tb.stop() + self.tb.wait() + +if __name__ == '__main__': + gr_unittest.run(test_cpp_py_binding_set, "test_cpp_py_binding_set.xml") diff --git a/gr-blocks/python/blocks/qa_ctrlport_probes.py b/gr-blocks/python/blocks/qa_ctrlport_probes.py index 91d96010fd..c678846df0 100644 --- a/gr-blocks/python/blocks/qa_ctrlport_probes.py +++ b/gr-blocks/python/blocks/qa_ctrlport_probes.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2013 Free Software Foundation, Inc. +# Copyright 2013,2015 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -22,33 +22,225 @@ import sys, time, random, numpy from gnuradio import gr, gr_unittest, blocks +import os, struct, re -import os, struct +from gnuradio.ctrlport.GNURadioControlPortClient import GNURadioControlPortClient class test_ctrlport_probes(gr_unittest.TestCase): def setUp(self): - self.tb = gr.top_block() os.environ['GR_CONF_CONTROLPORT_ON'] = 'True' + self.tb = gr.top_block() def tearDown(self): self.tb = None - def xtest_001(self): - pass + def test_001(self): + data = range(1,9) + + self.src = blocks.vector_source_c(data, True) + self.probe = blocks.ctrlport_probe2_c("samples","Complex", + len(data), gr.DISPNULL) + probe_name = self.probe.alias() + + self.tb.connect(self.src, self.probe) + self.tb.start() + + + # Probes return complex values as list of floats with re, im + # Imaginary parts of this data set are 0. + expected_result = [1, 2, 3, 4, + 5, 6, 7, 8] + + # Make sure we have time for flowgraph to run + time.sleep(0.1) + + # Get available endpoint + ep = gr.rpcmanager_get().endpoints()[0] + hostname = re.search("-h (\S+|\d+\.\d+\.\d+\.\d+)", ep).group(1) + portnum = re.search("-p (\d+)", ep).group(1) + argv = [None, hostname, portnum] + + # Initialize a simple ControlPort client from endpoint + from gnuradio.ctrlport.GNURadioControlPortClient import GNURadioControlPortClient + radiosys = GNURadioControlPortClient(argv=argv, rpcmethod='thrift') + radio = radiosys.client + + # Get all exported knobs + ret = radio.getKnobs([probe_name + "::samples"]) + for name in ret.keys(): + # Get data in probe, which might be offset; find the + # beginning and unwrap. + result = ret[name].value + i = result.index(complex(1.0, 0.0)) + result = result[i:] + result[0:i] + self.assertComplexTuplesAlmostEqual(expected_result, result, 4) + + self.tb.stop() + self.tb.wait() + def test_002(self): - pass + data = range(1,9) + + self.src = blocks.vector_source_f(data, True) + self.probe = blocks.ctrlport_probe2_f("samples","Floats", + len(data), gr.DISPNULL) + probe_name = self.probe.alias() + + self.tb.connect(self.src, self.probe) + self.tb.start() + + expected_result = [1, 2, 3, 4, 5, 6, 7, 8,] + + # Make sure we have time for flowgraph to run + time.sleep(0.1) + + # Get available endpoint + ep = gr.rpcmanager_get().endpoints()[0] + hostname = re.search("-h (\S+|\d+\.\d+\.\d+\.\d+)", ep).group(1) + portnum = re.search("-p (\d+)", ep).group(1) + argv = [None, hostname, portnum] + # Initialize a simple ControlPort client from endpoint + from gnuradio.ctrlport.GNURadioControlPortClient import GNURadioControlPortClient + radiosys = GNURadioControlPortClient(argv=argv, rpcmethod='thrift') + radio = radiosys.client + + # Get all exported knobs + ret = radio.getKnobs([probe_name + "::samples"]) + for name in ret.keys(): + # Get data in probe, which might be offset; find the + # beginning and unwrap. + result = ret[name].value + i = result.index(1.0) + result = result[i:] + result[0:i] + self.assertEqual(expected_result, result) + + self.tb.stop() + self.tb.wait() def test_003(self): - pass + data = range(1,9) + + self.src = blocks.vector_source_i(data, True) + self.probe = blocks.ctrlport_probe2_i("samples","Integers", + len(data), gr.DISPNULL) + probe_name = self.probe.alias() + + self.tb.connect(self.src, self.probe) + self.tb.start() + + expected_result = [1, 2, 3, 4, 5, 6, 7, 8,] + + # Make sure we have time for flowgraph to run + time.sleep(0.1) + + # Get available endpoint + ep = gr.rpcmanager_get().endpoints()[0] + hostname = re.search("-h (\S+|\d+\.\d+\.\d+\.\d+)", ep).group(1) + portnum = re.search("-p (\d+)", ep).group(1) + argv = [None, hostname, portnum] + + # Initialize a simple ControlPort client from endpoint + from gnuradio.ctrlport.GNURadioControlPortClient import GNURadioControlPortClient + radiosys = GNURadioControlPortClient(argv=argv, rpcmethod='thrift') + radio = radiosys.client + + # Get all exported knobs + ret = radio.getKnobs([probe_name + "::samples"]) + for name in ret.keys(): + # Get data in probe, which might be offset; find the + # beginning and unwrap. + result = ret[name].value + i = result.index(1.0) + result = result[i:] + result[0:i] + self.assertEqual(expected_result, result) + + self.tb.stop() + self.tb.wait() + def test_004(self): - pass + data = range(1,9) + + self.src = blocks.vector_source_s(data, True) + self.probe = blocks.ctrlport_probe2_s("samples","Shorts", + len(data), gr.DISPNULL) + probe_name = self.probe.alias() + + self.tb.connect(self.src, self.probe) + self.tb.start() + + expected_result = [1, 2, 3, 4, 5, 6, 7, 8,] + + # Make sure we have time for flowgraph to run + time.sleep(0.1) + + # Get available endpoint + ep = gr.rpcmanager_get().endpoints()[0] + hostname = re.search("-h (\S+|\d+\.\d+\.\d+\.\d+)", ep).group(1) + portnum = re.search("-p (\d+)", ep).group(1) + argv = [None, hostname, portnum] + + # Initialize a simple ControlPort client from endpoint + from gnuradio.ctrlport.GNURadioControlPortClient import GNURadioControlPortClient + radiosys = GNURadioControlPortClient(argv=argv, rpcmethod='thrift') + radio = radiosys.client + + # Get all exported knobs + ret = radio.getKnobs([probe_name + "::samples"]) + for name in ret.keys(): + # Get data in probe, which might be offset; find the + # beginning and unwrap. + result = ret[name].value + i = result.index(1.0) + result = result[i:] + result[0:i] + self.assertEqual(expected_result, result) + + self.tb.stop() + self.tb.wait() def test_005(self): - pass + data = range(1,9) + + self.src = blocks.vector_source_b(data, True) + self.probe = blocks.ctrlport_probe2_b("samples","Bytes", + len(data), gr.DISPNULL) + probe_name = self.probe.alias() + + self.tb.connect(self.src, self.probe) + self.tb.start() + + expected_result = [1, 2, 3, 4, 5, 6, 7, 8,] + + # Make sure we have time for flowgraph to run + time.sleep(0.1) + + # Get available endpoint + ep = gr.rpcmanager_get().endpoints()[0] + hostname = re.search("-h (\S+|\d+\.\d+\.\d+\.\d+)", ep).group(1) + portnum = re.search("-p (\d+)", ep).group(1) + argv = [None, hostname, portnum] + + # Initialize a simple ControlPort client from endpoint + from gnuradio.ctrlport.GNURadioControlPortClient import GNURadioControlPortClient + radiosys = GNURadioControlPortClient(argv=argv, rpcmethod='thrift') + radio = radiosys.client + + # Get all exported knobs + ret = radio.getKnobs([probe_name + "::samples"]) + for name in ret.keys(): + # Get data in probe, which might be offset; find the + # beginning and unwrap. + result = ret[name].value + result = list(struct.unpack(len(result)*'b', result)) + i = result.index(1) + result = result[i:] + result[0:i] + self.assertEqual(expected_result, result) + + self.tb.stop() + self.tb.wait() if __name__ == '__main__': gr_unittest.run(test_ctrlport_probes, "test_ctrlport_probes.xml") diff --git a/gr-blocks/python/blocks/qa_peak_detector2.py b/gr-blocks/python/blocks/qa_peak_detector2.py index 475897eac2..d6fd4fe95f 100644 --- a/gr-blocks/python/blocks/qa_peak_detector2.py +++ b/gr-blocks/python/blocks/qa_peak_detector2.py @@ -30,18 +30,20 @@ class test_peak_detector2(gr_unittest.TestCase): def tearDown(self): self.tb = None - def test_regen1(self): + def test_peak1(self): + #print "\n\nTEST 1" tb = self.tb - data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] + n=10 + data = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,)+n*(0,) expected_result = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)+n*(0,) src = blocks.vector_source_f(data, False) - regen = blocks.peak_detector2_fb() + regen = blocks.peak_detector2_fb(7.0, 25, 0.001) dst = blocks.vector_sink_b() tb.connect(src, regen) @@ -52,5 +54,80 @@ class test_peak_detector2(gr_unittest.TestCase): self.assertEqual(expected_result, dst_data) + def test_peak2(self): + #print "\n\nTEST 2" + tb = self.tb + + n=10 + data = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,)+n*(0,) + + expected_result = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)+n*(0,) + + + src = blocks.vector_source_f(data, False) + regen = blocks.peak_detector2_fb(7.0, 1000, 0.001) # called with a LONG window + dst = blocks.vector_sink_b() + + tb.connect(src, regen) + tb.connect(regen, dst) + tb.run() + + dst_data = dst.data() + + # here we know that the block will terminate prematurely, so we compare only part of the expected_result + self.assertEqual(expected_result[0:len(dst_data)], dst_data) + + + def test_peak3(self): + #print "\n\nTEST 3" + tb = self.tb + + l = 8100 + m = 100 + n = 10 + data = l*(0,)+ (10,)+ m*(0,)+(100,)+ n*(0,) + expected_result = l*(0,)+ (0,)+ m*(0,)+(1,)+ n*(0,) + + + src = blocks.vector_source_f(data, False) + regen = blocks.peak_detector2_fb(7.0, 105, 0.001) + dst = blocks.vector_sink_b() + + tb.connect(src, regen) + tb.connect(regen, dst) + tb.run() + + dst_data = dst.data() + + self.assertEqual(expected_result, dst_data) + + + def test_peak4(self): + #print "\n\nTEST 4" + tb = self.tb + + l = 8100 + m = 100 + n = 10 + data = l*(0,)+ (10,)+ m*(0,)+(100,)+ n*(0,) + expected_result = l*(0,)+ (0,)+ m*(0,)+(1,)+ n*(0,) + + + src = blocks.vector_source_f(data, False) + regen = blocks.peak_detector2_fb(7.0, 150, 0.001) + dst = blocks.vector_sink_b() + + tb.connect(src, regen) + tb.connect(regen, dst) + tb.run() + + dst_data = dst.data() + + # here we know that the block will terminate prematurely, so we compare only part of the expected_result + self.assertEqual(expected_result[0:len(dst_data)], dst_data) + + if __name__ == '__main__': gr_unittest.run(test_peak_detector2, "test_peak_detector2.xml") diff --git a/gr-digital/examples/CMakeLists.txt b/gr-digital/examples/CMakeLists.txt index 8faad96708..38ee443ab7 100644 --- a/gr-digital/examples/CMakeLists.txt +++ b/gr-digital/examples/CMakeLists.txt @@ -31,6 +31,13 @@ GR_PYTHON_INSTALL(PROGRAMS COMPONENT "digital_python" ) +install( + FILES + burst_shaper.grc + DESTINATION ${GR_PKG_DIGITAL_EXAMPLES_DIR} + COMPONENT "digital_python" +) + # Narrowband GR_PYTHON_INSTALL(PROGRAMS narrowband/transmit_path.py @@ -82,6 +89,7 @@ install( demod/dpsk_loopback.grc demod/gfsk_loopback.grc demod/test_corr_and_sync.grc + demod/test_corr_est.grc demod/uhd_corr_and_sync_tx.grc demod/uhd_corr_and_sync_rx.grc DESTINATION ${GR_PKG_DIGITAL_EXAMPLES_DIR}/demod diff --git a/gr-digital/examples/burst_shaper.grc b/gr-digital/examples/burst_shaper.grc new file mode 100644 index 0000000000..35a98a3267 --- /dev/null +++ b/gr-digital/examples/burst_shaper.grc @@ -0,0 +1,880 @@ +<?xml version='1.0' encoding='ASCII'?> +<?grc format='1' created='3.7.8'?> +<flow_graph> + <timestamp>Fri Apr 17 12:31:57 2015</timestamp> + <block> + <key>options</key> + <param> + <key>id</key> + <value>burst_shaper</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>title</key> + <value></value> + </param> + <param> + <key>author</key> + <value></value> + </param> + <param> + <key>description</key> + <value></value> + </param> + <param> + <key>window_size</key> + <value>1280, 1024</value> + </param> + <param> + <key>generate_options</key> + <value>no_gui</value> + </param> + <param> + <key>category</key> + <value>Custom</value> + </param> + <param> + <key>run_options</key> + <value>run</value> + </param> + <param> + <key>run</key> + <value>True</value> + </param> + <param> + <key>max_nouts</key> + <value>0</value> + </param> + <param> + <key>realtime_scheduling</key> + <value></value> + </param> + <param> + <key>thread_safe_setters</key> + <value></value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(10, 10)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>samp_rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>32000</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(392, 11)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>window_taps</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>fft.window.hann(10)</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(232, 11)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>import</key> + <param> + <key>id</key> + <value>import_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>import</key> + <value>import pmt</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(8, 99)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>import</key> + <param> + <key>id</key> + <value>import_0_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>import</key> + <value>from gnuradio import fft</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(96, 99)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_vector_source_x</key> + <param> + <key>id</key> + <value>blocks_vector_source_x_0_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>vector</key> + <value>[1.0, -1.0]*15</value> + </param> + <param> + <key>tags</key> + <value>[gr.python_to_tag({'offset':0, 'key':pmt.intern('packet_len'), 'value':pmt.from_long(20), 'srcid':pmt.intern('vector_source')})]</value> + </param> + <param> + <key>repeat</key> + <value>False</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(8, 171)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_vector_source_x</key> + <param> + <key>id</key> + <value>blocks_vector_source_x_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>vector</key> + <value>[complex(1.0), complex(-1.0)]*15</value> + </param> + <param> + <key>tags</key> + <value>[gr.python_to_tag({'offset':0, 'key':pmt.intern('packet_len'), 'value':pmt.from_long(20), 'srcid':pmt.intern('vector_source')})]</value> + </param> + <param> + <key>repeat</key> + <value>False</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(8, 307)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_tagged_stream_to_pdu</key> + <param> + <key>id</key> + <value>blocks_tagged_stream_to_pdu_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>tag</key> + <value>packet_len</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(632, 163)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_message_debug</key> + <param> + <key>id</key> + <value>blocks_message_debug_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(888, 168)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_message_debug</key> + <param> + <key>id</key> + <value>blocks_message_debug_0_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(888, 304)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_tag_debug</key> + <param> + <key>id</key> + <value>blocks_tag_debug_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>name</key> + <value>Float</value> + </param> + <param> + <key>filter</key> + <value>"packet_len"</value> + </param> + <param> + <key>num_inputs</key> + <value>1</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>display</key> + <value>True</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(632, 211)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_tagged_stream_to_pdu</key> + <param> + <key>id</key> + <value>blocks_tagged_stream_to_pdu_1</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>tag</key> + <value>packet_len</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(632, 299)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>digital_burst_shaper_xx</key> + <param> + <key>id</key> + <value>digital_burst_shaper_xx_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>window</key> + <value>window_taps</value> + </param> + <param> + <key>pre_padding</key> + <value>5</value> + </param> + <param> + <key>post_padding</key> + <value>5</value> + </param> + <param> + <key>insert_phasing</key> + <value>False</value> + </param> + <param> + <key>length_tag_name</key> + <value>"packet_len"</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(232, 155)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_throttle</key> + <param> + <key>id</key> + <value>blocks_throttle_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>samples_per_second</key> + <value>samp_rate</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>ignoretag</key> + <value>True</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(456, 187)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>digital_burst_shaper_xx</key> + <param> + <key>id</key> + <value>digital_burst_shaper_xx_0_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>window</key> + <value>window_taps</value> + </param> + <param> + <key>pre_padding</key> + <value>5</value> + </param> + <param> + <key>post_padding</key> + <value>5</value> + </param> + <param> + <key>insert_phasing</key> + <value>False</value> + </param> + <param> + <key>length_tag_name</key> + <value>"packet_len"</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(232, 291)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_throttle</key> + <param> + <key>id</key> + <value>blocks_throttle_0_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>samples_per_second</key> + <value>samp_rate</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>ignoretag</key> + <value>True</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(456, 323)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_tag_debug</key> + <param> + <key>id</key> + <value>blocks_tag_debug_0_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>name</key> + <value>Complex</value> + </param> + <param> + <key>filter</key> + <value>"packet_len"</value> + </param> + <param> + <key>num_inputs</key> + <value>1</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>display</key> + <value>True</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(632, 347)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <connection> + <source_block_id>blocks_throttle_0</source_block_id> + <sink_block_id>blocks_tagged_stream_to_pdu_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_vector_source_x_0_0</source_block_id> + <sink_block_id>digital_burst_shaper_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>digital_burst_shaper_xx_0</source_block_id> + <sink_block_id>blocks_throttle_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>digital_burst_shaper_xx_0_0</source_block_id> + <sink_block_id>blocks_throttle_0_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_vector_source_x_0</source_block_id> + <sink_block_id>digital_burst_shaper_xx_0_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_tagged_stream_to_pdu_1</source_block_id> + <sink_block_id>blocks_message_debug_0_0</sink_block_id> + <source_key>pdus</source_key> + <sink_key>print</sink_key> + </connection> + <connection> + <source_block_id>blocks_tagged_stream_to_pdu_0</source_block_id> + <sink_block_id>blocks_message_debug_0</sink_block_id> + <source_key>pdus</source_key> + <sink_key>print</sink_key> + </connection> + <connection> + <source_block_id>blocks_throttle_0</source_block_id> + <sink_block_id>blocks_tag_debug_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_throttle_0_0</source_block_id> + <sink_block_id>blocks_tagged_stream_to_pdu_1</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_throttle_0_0</source_block_id> + <sink_block_id>blocks_tag_debug_0_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> +</flow_graph> diff --git a/gr-digital/examples/demod/test_corr_est.grc b/gr-digital/examples/demod/test_corr_est.grc new file mode 100644 index 0000000000..6237565a4b --- /dev/null +++ b/gr-digital/examples/demod/test_corr_est.grc @@ -0,0 +1,3500 @@ +<?xml version='1.0' encoding='ASCII'?> +<?grc format='1' created='3.7.7'?> +<flow_graph> + <timestamp>Fri Jul 11 16:54:10 2014</timestamp> + <block> + <key>options</key> + <param> + <key>id</key> + <value>test_corr_est</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>title</key> + <value></value> + </param> + <param> + <key>author</key> + <value></value> + </param> + <param> + <key>description</key> + <value></value> + </param> + <param> + <key>window_size</key> + <value>2000,2000</value> + </param> + <param> + <key>generate_options</key> + <value>qt_gui</value> + </param> + <param> + <key>category</key> + <value>Custom</value> + </param> + <param> + <key>run_options</key> + <value>prompt</value> + </param> + <param> + <key>run</key> + <value>True</value> + </param> + <param> + <key>max_nouts</key> + <value>0</value> + </param> + <param> + <key>realtime_scheduling</key> + <value></value> + </param> + <param> + <key>thread_safe_setters</key> + <value></value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(10, 10)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>data</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>[0]*4+[random.getrandbits(8) for i in range(payload_size)]</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(16, 251)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>rrc_taps</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>firdes.root_raised_cosine(nfilts, nfilts, 1.0/float(sps), eb, 5*sps*nfilts)</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(1075, 73)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>nfilts</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>32</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(1074, 9)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>payload_size</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>992</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(101, 73)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>bb_filter</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>firdes.root_raised_cosine(sps, sps, 1, eb, 101)</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(429, 8)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>sps</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>4</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(278, 72)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>matched_filter</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>firdes.root_raised_cosine(nfilts, nfilts, 1, eb, int(11*sps*nfilts))</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(429, 72)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>samp_rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>100000</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(11, 72)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>gap</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>20000</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(202, 72)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>eb</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>0.35</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(346, 72)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>preamble</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>[0xac, 0xdd, 0xa4, 0xe2, 0xf2, 0x8c, 0x20, 0xfc]</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(279, 8)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>rxmod</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>digital.generic_mod(constel, False, sps, True, eb, False, False)</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(8, 427)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>channels_channel_model</key> + <param> + <key>id</key> + <value>channels_channel_model_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>noise_voltage</key> + <value>noise</value> + </param> + <param> + <key>freq_offset</key> + <value>freq_offset</value> + </param> + <param> + <key>epsilon</key> + <value>time_offset</value> + </param> + <param> + <key>taps</key> + <value>1.0</value> + </param> + <param> + <key>seed</key> + <value>0</value> + </param> + <param> + <key>block_tags</key> + <value>False</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(772, 158)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_throttle</key> + <param> + <key>id</key> + <value>blocks_throttle_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>samples_per_second</key> + <value>samp_rate</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>ignoretag</key> + <value>True</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(586, 190)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_null_source</key> + <param> + <key>id</key> + <value>blocks_null_source_0_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>num_outputs</key> + <value>1</value> + </param> + <param> + <key>bus_conns</key> + <value>[[0,],]</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(402, 322)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>phase</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>Phase offset</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>-2*scipy.pi</value> + </param> + <param> + <key>stop</key> + <value>2*scipy.pi</value> + </param> + <param> + <key>step</key> + <value>0.1</value> + </param> + <param> + <key>widget</key> + <value>slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>3,1,1,1</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(692, 7)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>time_offset</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>Timing Offset</value> + </param> + <param> + <key>value</key> + <value>1</value> + </param> + <param> + <key>start</key> + <value>0.995</value> + </param> + <param> + <key>stop</key> + <value>1.005</value> + </param> + <param> + <key>step</key> + <value>0.00001</value> + </param> + <param> + <key>widget</key> + <value>slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>4,1,1,1</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(950, 8)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>noise</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>Noise</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>0</value> + </param> + <param> + <key>stop</key> + <value>1</value> + </param> + <param> + <key>step</key> + <value>0.005</value> + </param> + <param> + <key>widget</key> + <value>slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>3,0,1,1</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(584, 7)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>digital_pfb_clock_sync_xxx</key> + <param> + <key>id</key> + <value>digital_pfb_clock_sync_xxx_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>ccf</value> + </param> + <param> + <key>sps</key> + <value>sps</value> + </param> + <param> + <key>loop_bw</key> + <value>2*3.14/100.0</value> + </param> + <param> + <key>taps</key> + <value>rrc_taps</value> + </param> + <param> + <key>filter_size</key> + <value>nfilts</value> + </param> + <param> + <key>init_phase</key> + <value>0</value> + </param> + <param> + <key>max_dev</key> + <value>0.5</value> + </param> + <param> + <key>osps</key> + <value>1</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(790, 289)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_sub_xx</key> + <param> + <key>id</key> + <value>blocks_sub_xx_0</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>num_inputs</key> + <value>2</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(1125, 617)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>delay</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>label</key> + <value>delay</value> + </param> + <param> + <key>value</key> + <value>90</value> + </param> + <param> + <key>start</key> + <value>0</value> + </param> + <param> + <key>stop</key> + <value>200</value> + </param> + <param> + <key>step</key> + <value>1</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>5,0,1,2</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(875, 571)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_char_to_float</key> + <param> + <key>id</key> + <value>blocks_char_to_float_0</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>scale</key> + <value>1</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(752, 517)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_unpack_k_bits_bb</key> + <param> + <key>id</key> + <value>blocks_unpack_k_bits_bb_0</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>k</key> + <value>8</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(576, 517)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_stream_mux</key> + <param> + <key>id</key> + <value>blocks_stream_mux_0</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>type</key> + <value>byte</value> + </param> + <param> + <key>lengths</key> + <value>(len(preamble)/8+payload_size), gap/sps/8</value> + </param> + <param> + <key>num_inputs</key> + <value>2</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(390, 504)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_null_source</key> + <param> + <key>id</key> + <value>blocks_null_source_0</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>type</key> + <value>byte</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>num_outputs</key> + <value>1</value> + </param> + <param> + <key>bus_conns</key> + <value>[[0,],]</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(210, 538)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_delay</key> + <param> + <key>id</key> + <value>blocks_delay_0</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>delay</key> + <value>int(delay)</value> + </param> + <param> + <key>num_ports</key> + <value>1</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(923, 518)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_char_to_float</key> + <param> + <key>id</key> + <value>blocks_char_to_float_0_0</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>scale</key> + <value>1</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(1112, 482)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>qtgui_time_sink_x</key> + <param> + <key>id</key> + <value>qtgui_time_sink_x_0_1</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>name</key> + <value></value> + </param> + <param> + <key>ylabel</key> + <value>Amplitude</value> + </param> + <param> + <key>yunit</key> + <value>""</value> + </param> + <param> + <key>size</key> + <value>20000</value> + </param> + <param> + <key>srate</key> + <value>samp_rate</value> + </param> + <param> + <key>grid</key> + <value>False</value> + </param> + <param> + <key>autoscale</key> + <value>False</value> + </param> + <param> + <key>ymin</key> + <value>-2</value> + </param> + <param> + <key>ymax</key> + <value>2</value> + </param> + <param> + <key>nconnections</key> + <value>3</value> + </param> + <param> + <key>update_time</key> + <value>0.10</value> + </param> + <param> + <key>entags</key> + <value>True</value> + </param> + <param> + <key>gui_hint</key> + <value>2,0,1,2</value> + </param> + <param> + <key>tr_mode</key> + <value>qtgui.TRIG_MODE_TAG</value> + </param> + <param> + <key>tr_slope</key> + <value>qtgui.TRIG_SLOPE_POS</value> + </param> + <param> + <key>tr_level</key> + <value>0.0</value> + </param> + <param> + <key>tr_delay</key> + <value>0.010</value> + </param> + <param> + <key>tr_chan</key> + <value>0</value> + </param> + <param> + <key>tr_tag</key> + <value>time_est</value> + </param> + <param> + <key>ctrlpanel</key> + <value>False</value> + </param> + <param> + <key>legend</key> + <value>True</value> + </param> + <param> + <key>label1</key> + <value></value> + </param> + <param> + <key>width1</key> + <value>1</value> + </param> + <param> + <key>color1</key> + <value>"blue"</value> + </param> + <param> + <key>style1</key> + <value>1</value> + </param> + <param> + <key>marker1</key> + <value>-1</value> + </param> + <param> + <key>alpha1</key> + <value>1.0</value> + </param> + <param> + <key>label2</key> + <value></value> + </param> + <param> + <key>width2</key> + <value>1</value> + </param> + <param> + <key>color2</key> + <value>"red"</value> + </param> + <param> + <key>style2</key> + <value>1</value> + </param> + <param> + <key>marker2</key> + <value>-1</value> + </param> + <param> + <key>alpha2</key> + <value>1.0</value> + </param> + <param> + <key>label3</key> + <value></value> + </param> + <param> + <key>width3</key> + <value>1</value> + </param> + <param> + <key>color3</key> + <value>"green"</value> + </param> + <param> + <key>style3</key> + <value>1</value> + </param> + <param> + <key>marker3</key> + <value>-1</value> + </param> + <param> + <key>alpha3</key> + <value>1.0</value> + </param> + <param> + <key>label4</key> + <value></value> + </param> + <param> + <key>width4</key> + <value>1</value> + </param> + <param> + <key>color4</key> + <value>"black"</value> + </param> + <param> + <key>style4</key> + <value>1</value> + </param> + <param> + <key>marker4</key> + <value>-1</value> + </param> + <param> + <key>alpha4</key> + <value>1.0</value> + </param> + <param> + <key>label5</key> + <value></value> + </param> + <param> + <key>width5</key> + <value>1</value> + </param> + <param> + <key>color5</key> + <value>"cyan"</value> + </param> + <param> + <key>style5</key> + <value>1</value> + </param> + <param> + <key>marker5</key> + <value>-1</value> + </param> + <param> + <key>alpha5</key> + <value>1.0</value> + </param> + <param> + <key>label6</key> + <value></value> + </param> + <param> + <key>width6</key> + <value>1</value> + </param> + <param> + <key>color6</key> + <value>"magenta"</value> + </param> + <param> + <key>style6</key> + <value>1</value> + </param> + <param> + <key>marker6</key> + <value>-1</value> + </param> + <param> + <key>alpha6</key> + <value>1.0</value> + </param> + <param> + <key>label7</key> + <value></value> + </param> + <param> + <key>width7</key> + <value>1</value> + </param> + <param> + <key>color7</key> + <value>"yellow"</value> + </param> + <param> + <key>style7</key> + <value>1</value> + </param> + <param> + <key>marker7</key> + <value>-1</value> + </param> + <param> + <key>alpha7</key> + <value>1.0</value> + </param> + <param> + <key>label8</key> + <value></value> + </param> + <param> + <key>width8</key> + <value>1</value> + </param> + <param> + <key>color8</key> + <value>"dark red"</value> + </param> + <param> + <key>style8</key> + <value>1</value> + </param> + <param> + <key>marker8</key> + <value>-1</value> + </param> + <param> + <key>alpha8</key> + <value>1.0</value> + </param> + <param> + <key>label9</key> + <value></value> + </param> + <param> + <key>width9</key> + <value>1</value> + </param> + <param> + <key>color9</key> + <value>"dark green"</value> + </param> + <param> + <key>style9</key> + <value>1</value> + </param> + <param> + <key>marker9</key> + <value>-1</value> + </param> + <param> + <key>alpha9</key> + <value>1.0</value> + </param> + <param> + <key>label10</key> + <value></value> + </param> + <param> + <key>width10</key> + <value>1</value> + </param> + <param> + <key>color10</key> + <value>"blue"</value> + </param> + <param> + <key>style10</key> + <value>1</value> + </param> + <param> + <key>marker10</key> + <value>-1</value> + </param> + <param> + <key>alpha10</key> + <value>1.0</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(1345, 531)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>digital_constellation_decoder_cb</key> + <param> + <key>id</key> + <value>digital_constellation_decoder_cb_0</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>constellation</key> + <value>constel</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(1111, 433)</value> + </param> + <param> + <key>_rotation</key> + <value>180</value> + </param> + </block> + <block> + <key>digital_costas_loop_cc</key> + <param> + <key>id</key> + <value>digital_costas_loop_cc_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>w</key> + <value>1*3.14/50.0</value> + </param> + <param> + <key>order</key> + <value>2</value> + </param> + <param> + <key>use_snr</key> + <value>False</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(1091, 303)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>qtgui_time_sink_x</key> + <param> + <key>id</key> + <value>qtgui_time_sink_x_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>name</key> + <value></value> + </param> + <param> + <key>ylabel</key> + <value>Amplitude</value> + </param> + <param> + <key>yunit</key> + <value>""</value> + </param> + <param> + <key>size</key> + <value>50000</value> + </param> + <param> + <key>srate</key> + <value>samp_rate</value> + </param> + <param> + <key>grid</key> + <value>False</value> + </param> + <param> + <key>autoscale</key> + <value>False</value> + </param> + <param> + <key>ymin</key> + <value>-2</value> + </param> + <param> + <key>ymax</key> + <value>2</value> + </param> + <param> + <key>nconnections</key> + <value>1</value> + </param> + <param> + <key>update_time</key> + <value>0.10</value> + </param> + <param> + <key>entags</key> + <value>True</value> + </param> + <param> + <key>gui_hint</key> + <value>0,0,1,1</value> + </param> + <param> + <key>tr_mode</key> + <value>qtgui.TRIG_MODE_TAG</value> + </param> + <param> + <key>tr_slope</key> + <value>qtgui.TRIG_SLOPE_POS</value> + </param> + <param> + <key>tr_level</key> + <value>1</value> + </param> + <param> + <key>tr_delay</key> + <value>0.1</value> + </param> + <param> + <key>tr_chan</key> + <value>0</value> + </param> + <param> + <key>tr_tag</key> + <value>time_est</value> + </param> + <param> + <key>ctrlpanel</key> + <value>False</value> + </param> + <param> + <key>legend</key> + <value>True</value> + </param> + <param> + <key>label1</key> + <value></value> + </param> + <param> + <key>width1</key> + <value>1</value> + </param> + <param> + <key>color1</key> + <value>"blue"</value> + </param> + <param> + <key>style1</key> + <value>1</value> + </param> + <param> + <key>marker1</key> + <value>-1</value> + </param> + <param> + <key>alpha1</key> + <value>1.0</value> + </param> + <param> + <key>label2</key> + <value></value> + </param> + <param> + <key>width2</key> + <value>1</value> + </param> + <param> + <key>color2</key> + <value>"red"</value> + </param> + <param> + <key>style2</key> + <value>1</value> + </param> + <param> + <key>marker2</key> + <value>-1</value> + </param> + <param> + <key>alpha2</key> + <value>1.0</value> + </param> + <param> + <key>label3</key> + <value></value> + </param> + <param> + <key>width3</key> + <value>1</value> + </param> + <param> + <key>color3</key> + <value>"green"</value> + </param> + <param> + <key>style3</key> + <value>1</value> + </param> + <param> + <key>marker3</key> + <value>-1</value> + </param> + <param> + <key>alpha3</key> + <value>1.0</value> + </param> + <param> + <key>label4</key> + <value></value> + </param> + <param> + <key>width4</key> + <value>1</value> + </param> + <param> + <key>color4</key> + <value>"black"</value> + </param> + <param> + <key>style4</key> + <value>1</value> + </param> + <param> + <key>marker4</key> + <value>-1</value> + </param> + <param> + <key>alpha4</key> + <value>1.0</value> + </param> + <param> + <key>label5</key> + <value></value> + </param> + <param> + <key>width5</key> + <value>1</value> + </param> + <param> + <key>color5</key> + <value>"cyan"</value> + </param> + <param> + <key>style5</key> + <value>1</value> + </param> + <param> + <key>marker5</key> + <value>-1</value> + </param> + <param> + <key>alpha5</key> + <value>1.0</value> + </param> + <param> + <key>label6</key> + <value></value> + </param> + <param> + <key>width6</key> + <value>1</value> + </param> + <param> + <key>color6</key> + <value>"magenta"</value> + </param> + <param> + <key>style6</key> + <value>1</value> + </param> + <param> + <key>marker6</key> + <value>-1</value> + </param> + <param> + <key>alpha6</key> + <value>1.0</value> + </param> + <param> + <key>label7</key> + <value></value> + </param> + <param> + <key>width7</key> + <value>1</value> + </param> + <param> + <key>color7</key> + <value>"yellow"</value> + </param> + <param> + <key>style7</key> + <value>1</value> + </param> + <param> + <key>marker7</key> + <value>-1</value> + </param> + <param> + <key>alpha7</key> + <value>1.0</value> + </param> + <param> + <key>label8</key> + <value></value> + </param> + <param> + <key>width8</key> + <value>1</value> + </param> + <param> + <key>color8</key> + <value>"dark red"</value> + </param> + <param> + <key>style8</key> + <value>1</value> + </param> + <param> + <key>marker8</key> + <value>-1</value> + </param> + <param> + <key>alpha8</key> + <value>1.0</value> + </param> + <param> + <key>label9</key> + <value></value> + </param> + <param> + <key>width9</key> + <value>1</value> + </param> + <param> + <key>color9</key> + <value>"dark green"</value> + </param> + <param> + <key>style9</key> + <value>1</value> + </param> + <param> + <key>marker9</key> + <value>-1</value> + </param> + <param> + <key>alpha9</key> + <value>1.0</value> + </param> + <param> + <key>label10</key> + <value></value> + </param> + <param> + <key>width10</key> + <value>1</value> + </param> + <param> + <key>color10</key> + <value>"blue"</value> + </param> + <param> + <key>style10</key> + <value>1</value> + </param> + <param> + <key>marker10</key> + <value>-1</value> + </param> + <param> + <key>alpha10</key> + <value>1.0</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(1398, 370)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_constellation</key> + <param> + <key>id</key> + <value>constel</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>sym_map</key> + <value>[0,1]</value> + </param> + <param> + <key>const_points</key> + <value>[1,-1]</value> + </param> + <param> + <key>rot_sym</key> + <value>2</value> + </param> + <param> + <key>dims</key> + <value>1</value> + </param> + <param> + <key>precision</key> + <value>8</value> + </param> + <param> + <key>soft_dec_lut</key> + <value>None</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(1237, 27)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>freq_offset</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>Frequency Offset</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>-0.001</value> + </param> + <param> + <key>stop</key> + <value>0.001</value> + </param> + <param> + <key>step</key> + <value>0.00002</value> + </param> + <param> + <key>widget</key> + <value>slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>4,0,1,1</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(808, 7)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>qtgui_const_sink_x</key> + <param> + <key>id</key> + <value>qtgui_const_sink_x_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>name</key> + <value></value> + </param> + <param> + <key>size</key> + <value>(len(preamble)+payload_size)*8</value> + </param> + <param> + <key>grid</key> + <value>False</value> + </param> + <param> + <key>autoscale</key> + <value>False</value> + </param> + <param> + <key>ymin</key> + <value>-2</value> + </param> + <param> + <key>ymax</key> + <value>2</value> + </param> + <param> + <key>xmin</key> + <value>-2</value> + </param> + <param> + <key>xmax</key> + <value>2</value> + </param> + <param> + <key>nconnections</key> + <value>1</value> + </param> + <param> + <key>update_time</key> + <value>0.10</value> + </param> + <param> + <key>gui_hint</key> + <value>0,1,1,1</value> + </param> + <param> + <key>tr_mode</key> + <value>qtgui.TRIG_MODE_TAG</value> + </param> + <param> + <key>tr_slope</key> + <value>qtgui.TRIG_SLOPE_POS</value> + </param> + <param> + <key>tr_level</key> + <value>0</value> + </param> + <param> + <key>tr_chan</key> + <value>0</value> + </param> + <param> + <key>tr_tag</key> + <value>time_est</value> + </param> + <param> + <key>legend</key> + <value>True</value> + </param> + <param> + <key>label1</key> + <value></value> + </param> + <param> + <key>width1</key> + <value>1</value> + </param> + <param> + <key>color1</key> + <value>"blue"</value> + </param> + <param> + <key>style1</key> + <value>0</value> + </param> + <param> + <key>marker1</key> + <value>0</value> + </param> + <param> + <key>alpha1</key> + <value>1.0</value> + </param> + <param> + <key>label2</key> + <value></value> + </param> + <param> + <key>width2</key> + <value>1</value> + </param> + <param> + <key>color2</key> + <value>"red"</value> + </param> + <param> + <key>style2</key> + <value>0</value> + </param> + <param> + <key>marker2</key> + <value>0</value> + </param> + <param> + <key>alpha2</key> + <value>1.0</value> + </param> + <param> + <key>label3</key> + <value></value> + </param> + <param> + <key>width3</key> + <value>1</value> + </param> + <param> + <key>color3</key> + <value>"red"</value> + </param> + <param> + <key>style3</key> + <value>0</value> + </param> + <param> + <key>marker3</key> + <value>0</value> + </param> + <param> + <key>alpha3</key> + <value>1.0</value> + </param> + <param> + <key>label4</key> + <value></value> + </param> + <param> + <key>width4</key> + <value>1</value> + </param> + <param> + <key>color4</key> + <value>"red"</value> + </param> + <param> + <key>style4</key> + <value>0</value> + </param> + <param> + <key>marker4</key> + <value>0</value> + </param> + <param> + <key>alpha4</key> + <value>1.0</value> + </param> + <param> + <key>label5</key> + <value></value> + </param> + <param> + <key>width5</key> + <value>1</value> + </param> + <param> + <key>color5</key> + <value>"red"</value> + </param> + <param> + <key>style5</key> + <value>0</value> + </param> + <param> + <key>marker5</key> + <value>0</value> + </param> + <param> + <key>alpha5</key> + <value>1.0</value> + </param> + <param> + <key>label6</key> + <value></value> + </param> + <param> + <key>width6</key> + <value>1</value> + </param> + <param> + <key>color6</key> + <value>"red"</value> + </param> + <param> + <key>style6</key> + <value>0</value> + </param> + <param> + <key>marker6</key> + <value>0</value> + </param> + <param> + <key>alpha6</key> + <value>1.0</value> + </param> + <param> + <key>label7</key> + <value></value> + </param> + <param> + <key>width7</key> + <value>1</value> + </param> + <param> + <key>color7</key> + <value>"red"</value> + </param> + <param> + <key>style7</key> + <value>0</value> + </param> + <param> + <key>marker7</key> + <value>0</value> + </param> + <param> + <key>alpha7</key> + <value>1.0</value> + </param> + <param> + <key>label8</key> + <value></value> + </param> + <param> + <key>width8</key> + <value>1</value> + </param> + <param> + <key>color8</key> + <value>"red"</value> + </param> + <param> + <key>style8</key> + <value>0</value> + </param> + <param> + <key>marker8</key> + <value>0</value> + </param> + <param> + <key>alpha8</key> + <value>1.0</value> + </param> + <param> + <key>label9</key> + <value></value> + </param> + <param> + <key>width9</key> + <value>1</value> + </param> + <param> + <key>color9</key> + <value>"red"</value> + </param> + <param> + <key>style9</key> + <value>0</value> + </param> + <param> + <key>marker9</key> + <value>0</value> + </param> + <param> + <key>alpha9</key> + <value>1.0</value> + </param> + <param> + <key>label10</key> + <value></value> + </param> + <param> + <key>width10</key> + <value>1</value> + </param> + <param> + <key>color10</key> + <value>"red"</value> + </param> + <param> + <key>style10</key> + <value>0</value> + </param> + <param> + <key>marker10</key> + <value>0</value> + </param> + <param> + <key>alpha10</key> + <value>1.0</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(1399, 283)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_complex_to_mag</key> + <param> + <key>id</key> + <value>blocks_complex_to_mag_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(1235, 149)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_complex_to_float</key> + <param> + <key>id</key> + <value>blocks_complex_to_float_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(1235, 194)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>import</key> + <param> + <key>id</key> + <value>import_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>import</key> + <value>import scipy</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(181, 16)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>import</key> + <param> + <key>id</key> + <value>import_0_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>import</key> + <value>import random</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(176, 147)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_vector_source_x</key> + <param> + <key>id</key> + <value>blocks_vector_source_x_0_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>byte</value> + </param> + <param> + <key>vector</key> + <value>preamble+data</value> + </param> + <param> + <key>tags</key> + <value>[]</value> + </param> + <param> + <key>repeat</key> + <value>True</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(15, 165)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>digital_constellation_modulator</key> + <param> + <key>id</key> + <value>digital_constellation_modulator_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>constellation</key> + <value>constel</value> + </param> + <param> + <key>differential</key> + <value>False</value> + </param> + <param> + <key>samples_per_symbol</key> + <value>sps</value> + </param> + <param> + <key>excess_bw</key> + <value>eb</value> + </param> + <param> + <key>verbose</key> + <value>False</value> + </param> + <param> + <key>log</key> + <value>False</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(312, 166)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_stream_mux</key> + <param> + <key>id</key> + <value>blocks_stream_mux_0_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>lengths</key> + <value>(len(preamble)+len(data))*8*sps, gap</value> + </param> + <param> + <key>num_inputs</key> + <value>2</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(568, 288)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>qtgui_time_sink_x</key> + <param> + <key>id</key> + <value>qtgui_time_sink_x_1</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>name</key> + <value></value> + </param> + <param> + <key>ylabel</key> + <value>Amplitude</value> + </param> + <param> + <key>yunit</key> + <value>""</value> + </param> + <param> + <key>size</key> + <value>80000</value> + </param> + <param> + <key>srate</key> + <value>samp_rate</value> + </param> + <param> + <key>grid</key> + <value>False</value> + </param> + <param> + <key>autoscale</key> + <value>False</value> + </param> + <param> + <key>ymin</key> + <value>-200</value> + </param> + <param> + <key>ymax</key> + <value>400</value> + </param> + <param> + <key>nconnections</key> + <value>3</value> + </param> + <param> + <key>update_time</key> + <value>0.10</value> + </param> + <param> + <key>entags</key> + <value>True</value> + </param> + <param> + <key>gui_hint</key> + <value>1,0,1,2</value> + </param> + <param> + <key>tr_mode</key> + <value>qtgui.TRIG_MODE_NORM</value> + </param> + <param> + <key>tr_slope</key> + <value>qtgui.TRIG_SLOPE_POS</value> + </param> + <param> + <key>tr_level</key> + <value>100</value> + </param> + <param> + <key>tr_delay</key> + <value>0</value> + </param> + <param> + <key>tr_chan</key> + <value>0</value> + </param> + <param> + <key>tr_tag</key> + <value>""</value> + </param> + <param> + <key>ctrlpanel</key> + <value>False</value> + </param> + <param> + <key>legend</key> + <value>True</value> + </param> + <param> + <key>label1</key> + <value>|corr|^2</value> + </param> + <param> + <key>width1</key> + <value>1</value> + </param> + <param> + <key>color1</key> + <value>"blue"</value> + </param> + <param> + <key>style1</key> + <value>1</value> + </param> + <param> + <key>marker1</key> + <value>-1</value> + </param> + <param> + <key>alpha1</key> + <value>1.0</value> + </param> + <param> + <key>label2</key> + <value>Re{corr}</value> + </param> + <param> + <key>width2</key> + <value>1</value> + </param> + <param> + <key>color2</key> + <value>"red"</value> + </param> + <param> + <key>style2</key> + <value>1</value> + </param> + <param> + <key>marker2</key> + <value>-1</value> + </param> + <param> + <key>alpha2</key> + <value>1.0</value> + </param> + <param> + <key>label3</key> + <value>Im{corr}</value> + </param> + <param> + <key>width3</key> + <value>1</value> + </param> + <param> + <key>color3</key> + <value>"green"</value> + </param> + <param> + <key>style3</key> + <value>1</value> + </param> + <param> + <key>marker3</key> + <value>-1</value> + </param> + <param> + <key>alpha3</key> + <value>1.0</value> + </param> + <param> + <key>label4</key> + <value></value> + </param> + <param> + <key>width4</key> + <value>1</value> + </param> + <param> + <key>color4</key> + <value>"black"</value> + </param> + <param> + <key>style4</key> + <value>1</value> + </param> + <param> + <key>marker4</key> + <value>-1</value> + </param> + <param> + <key>alpha4</key> + <value>1.0</value> + </param> + <param> + <key>label5</key> + <value></value> + </param> + <param> + <key>width5</key> + <value>1</value> + </param> + <param> + <key>color5</key> + <value>"cyan"</value> + </param> + <param> + <key>style5</key> + <value>1</value> + </param> + <param> + <key>marker5</key> + <value>-1</value> + </param> + <param> + <key>alpha5</key> + <value>1.0</value> + </param> + <param> + <key>label6</key> + <value></value> + </param> + <param> + <key>width6</key> + <value>1</value> + </param> + <param> + <key>color6</key> + <value>"magenta"</value> + </param> + <param> + <key>style6</key> + <value>1</value> + </param> + <param> + <key>marker6</key> + <value>-1</value> + </param> + <param> + <key>alpha6</key> + <value>1.0</value> + </param> + <param> + <key>label7</key> + <value></value> + </param> + <param> + <key>width7</key> + <value>1</value> + </param> + <param> + <key>color7</key> + <value>"yellow"</value> + </param> + <param> + <key>style7</key> + <value>1</value> + </param> + <param> + <key>marker7</key> + <value>-1</value> + </param> + <param> + <key>alpha7</key> + <value>1.0</value> + </param> + <param> + <key>label8</key> + <value></value> + </param> + <param> + <key>width8</key> + <value>1</value> + </param> + <param> + <key>color8</key> + <value>"dark red"</value> + </param> + <param> + <key>style8</key> + <value>1</value> + </param> + <param> + <key>marker8</key> + <value>-1</value> + </param> + <param> + <key>alpha8</key> + <value>1.0</value> + </param> + <param> + <key>label9</key> + <value></value> + </param> + <param> + <key>width9</key> + <value>1</value> + </param> + <param> + <key>color9</key> + <value>"dark green"</value> + </param> + <param> + <key>style9</key> + <value>1</value> + </param> + <param> + <key>marker9</key> + <value>-1</value> + </param> + <param> + <key>alpha9</key> + <value>1.0</value> + </param> + <param> + <key>label10</key> + <value></value> + </param> + <param> + <key>width10</key> + <value>1</value> + </param> + <param> + <key>color10</key> + <value>"blue"</value> + </param> + <param> + <key>style10</key> + <value>1</value> + </param> + <param> + <key>marker10</key> + <value>-1</value> + </param> + <param> + <key>alpha10</key> + <value>1.0</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(1433, 160)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_modulate_vector</key> + <param> + <key>id</key> + <value>modulated_sync_word</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>mod</key> + <value>rxmod</value> + </param> + <param> + <key>data</key> + <value>preamble</value> + </param> + <param> + <key>taps</key> + <value>[1]</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(8, 491)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>digital_corr_est_cc</key> + <param> + <key>id</key> + <value>digital_corr_est_cc_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>symbols</key> + <value>modulated_sync_word</value> + </param> + <param> + <key>sps</key> + <value>sps</value> + </param> + <param> + <key>mark_delay</key> + <value>1</value> + </param> + <param> + <key>threshold</key> + <value>0.9</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(972, 174)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <connection> + <source_block_id>digital_costas_loop_cc_0</source_block_id> + <sink_block_id>qtgui_time_sink_x_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>digital_costas_loop_cc_0</source_block_id> + <sink_block_id>digital_constellation_decoder_cb_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_complex_to_float_0</source_block_id> + <sink_block_id>qtgui_time_sink_x_1</sink_block_id> + <source_key>0</source_key> + <sink_key>1</sink_key> + </connection> + <connection> + <source_block_id>blocks_complex_to_mag_0</source_block_id> + <sink_block_id>qtgui_time_sink_x_1</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>digital_corr_est_cc_0</source_block_id> + <sink_block_id>digital_pfb_clock_sync_xxx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_null_source_0_0</source_block_id> + <sink_block_id>blocks_stream_mux_0_0</sink_block_id> + <source_key>0</source_key> + <sink_key>1</sink_key> + </connection> + <connection> + <source_block_id>blocks_stream_mux_0_0</source_block_id> + <sink_block_id>blocks_throttle_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_complex_to_float_0</source_block_id> + <sink_block_id>qtgui_time_sink_x_1</sink_block_id> + <source_key>1</source_key> + <sink_key>2</sink_key> + </connection> + <connection> + <source_block_id>channels_channel_model_0</source_block_id> + <sink_block_id>digital_corr_est_cc_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>digital_constellation_modulator_0</source_block_id> + <sink_block_id>blocks_stream_mux_0_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>digital_corr_est_cc_0</source_block_id> + <sink_block_id>blocks_complex_to_mag_0</sink_block_id> + <source_key>1</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>digital_corr_est_cc_0</source_block_id> + <sink_block_id>blocks_complex_to_float_0</sink_block_id> + <source_key>1</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_throttle_0</source_block_id> + <sink_block_id>channels_channel_model_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>digital_pfb_clock_sync_xxx_0</source_block_id> + <sink_block_id>digital_costas_loop_cc_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_delay_0</source_block_id> + <sink_block_id>qtgui_time_sink_x_0_1</sink_block_id> + <source_key>0</source_key> + <sink_key>1</sink_key> + </connection> + <connection> + <source_block_id>blocks_char_to_float_0_0</source_block_id> + <sink_block_id>blocks_sub_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_delay_0</source_block_id> + <sink_block_id>blocks_sub_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>1</sink_key> + </connection> + <connection> + <source_block_id>blocks_sub_xx_0</source_block_id> + <sink_block_id>qtgui_time_sink_x_0_1</sink_block_id> + <source_key>0</source_key> + <sink_key>2</sink_key> + </connection> + <connection> + <source_block_id>blocks_char_to_float_0_0</source_block_id> + <sink_block_id>qtgui_time_sink_x_0_1</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_stream_mux_0</source_block_id> + <sink_block_id>blocks_unpack_k_bits_bb_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_unpack_k_bits_bb_0</source_block_id> + <sink_block_id>blocks_char_to_float_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_null_source_0</source_block_id> + <sink_block_id>blocks_stream_mux_0</sink_block_id> + <source_key>0</source_key> + <sink_key>1</sink_key> + </connection> + <connection> + <source_block_id>blocks_char_to_float_0</source_block_id> + <sink_block_id>blocks_delay_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>digital_constellation_decoder_cb_0</source_block_id> + <sink_block_id>blocks_char_to_float_0_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>digital_costas_loop_cc_0</source_block_id> + <sink_block_id>qtgui_const_sink_x_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_vector_source_x_0_0</source_block_id> + <sink_block_id>digital_constellation_modulator_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> +</flow_graph> diff --git a/gr-digital/grc/digital_block_tree.xml b/gr-digital/grc/digital_block_tree.xml index f6dcceb6d0..6707a8e4db 100644 --- a/gr-digital/grc/digital_block_tree.xml +++ b/gr-digital/grc/digital_block_tree.xml @@ -1,6 +1,6 @@ <?xml version="1.0"?> <!-- - Copyright 2011-2013 Free Software Foundation, Inc. + Copyright 2011-2015 Free Software Foundation, Inc. This file is part of GNU Radio @@ -63,6 +63,7 @@ <block>digital_constellation_receiver_cb</block> <block>variable_constellation</block> <block>variable_constellation_rect</block> + <block>variable_modulate_vector</block> </cat> <cat> <name>Packet Operators</name> @@ -120,6 +121,7 @@ <block>digital_mpsk_receiver_cc</block> <block>digital_pfb_clock_sync_xxx</block> <block>digital_pn_correlator_cc</block> + <block>digital_corr_est_cc</block> <block>digital_correlate_and_sync_cc</block> </cat> <cat> diff --git a/gr-digital/grc/digital_burst_shaper.xml b/gr-digital/grc/digital_burst_shaper.xml new file mode 100644 index 0000000000..5c0bc78d0a --- /dev/null +++ b/gr-digital/grc/digital_burst_shaper.xml @@ -0,0 +1,71 @@ +<?xml version="1.0"?> +<block> + <name>Burst Shaper</name> + <key>digital_burst_shaper_xx</key> + <category>Packet Operators</category> + <import>from gnuradio import digital</import> + <make>digital.burst_shaper_$(type.fcn)($window, $pre_padding, $post_padding, $insert_phasing, $length_tag_name)</make> + <param> + <name>Type</name> + <key>type</key> + <type>enum</type> + <option> + <name>Float</name> + <key>float</key> + <opt>fcn:ff</opt> + <opt>taps:float_vector</opt> + </option> + <option> + <name>Complex</name> + <key>complex</key> + <opt>fcn:cc</opt> + <opt>taps:complex_vector</opt> + </option> + </param> + <param> + <name>Window Taps</name> + <key>window</key> + <value>([])</value> + <type>$(type.taps)</type> + </param> + <param> + <name>Pre-padding Length</name> + <key>pre_padding</key> + <value>0</value> + <type>int</type> + </param> + <param> + <name>Post-padding Length</name> + <key>post_padding</key> + <value>0</value> + <type>int</type> + </param> + <param> + <name>Insert phasing symbols</name> + <key>insert_phasing</key> + <value>False</value> + <type>enum</type> + <option> + <name>No</name> + <key>False</key> + </option> + <option> + <name>Yes</name> + <key>True</key> + </option> + </param> + <param> + <name>Length Tag Name</name> + <key>length_tag_name</key> + <value>"packet_len"</value> + <type>string</type> + </param> + <sink> + <name>in</name> + <type>$type</type> + </sink> + <source> + <name>out</name> + <type>$type</type> + </source> +</block> diff --git a/gr-digital/grc/digital_corr_est_cc.xml b/gr-digital/grc/digital_corr_est_cc.xml new file mode 100644 index 0000000000..cb345e933f --- /dev/null +++ b/gr-digital/grc/digital_corr_est_cc.xml @@ -0,0 +1,50 @@ +<?xml version="1.0"?> +<block> + <name>Correlation Estimator</name> + <key>digital_corr_est_cc</key> + <import>from gnuradio import digital</import> + <make>digital.corr_est_cc($symbols, $sps, $mark_delay, $threshold)</make> + <callback>set_mark_delay($mark_delay)</callback> + <callback>set_theshold($threshold)</callback> + + <param> + <name>Symbols</name> + <key>symbols</key> + <type>complex_vector</type> + </param> + + <param> + <name>Samples per Symbol</name> + <key>sps</key> + <type>float</type> + </param> + + <param> + <name>Tag marking delay</name> + <key>mark_delay</key> + <type>int</type> + </param> + + <param> + <name>Threshold</name> + <key>threshold</key> + <value>0.9</value> + <type>float</type> + </param> + + <sink> + <name>in</name> + <type>complex</type> + </sink> + + <source> + <name>out</name> + <type>complex</type> + </source> + + <source> + <name>corr</name> + <type>complex</type> + <optional>1</optional> + </source> +</block> diff --git a/gr-digital/grc/digital_modulate_vector.xml b/gr-digital/grc/digital_modulate_vector.xml new file mode 100644 index 0000000000..91614dd3fd --- /dev/null +++ b/gr-digital/grc/digital_modulate_vector.xml @@ -0,0 +1,34 @@ +<?xml version="1.0"?> +<!-- +################################################### +# modulate_vector_bc +################################################### + --> +<block> + <name>Modulate Vector</name> + <key>variable_modulate_vector</key> + <category>Modulators</category> + <import>from gnuradio import digital</import> + <var_make>self.$(id) = $(id) = digital.modulate_vector_bc($mod .to_basic_block(), $data, $taps)</var_make> + <var_value>digital.modulate_vector_bc($mod .to_basic_block(), $data, $taps)</var_value> + <make></make> + + <param> + <name>Modulator</name> + <key>mod</key> + <value></value> + <type>raw</type> + </param> + <param> + <name>Data vector</name> + <key>data</key> + <value></value> + <type>int_vector</type> + </param> + <param> + <name>Filter taps</name> + <key>taps</key> + <value></value> + <type>float_vector</type> + </param> +</block> diff --git a/gr-digital/grc/digital_msk_timing_recovery_cc.xml b/gr-digital/grc/digital_msk_timing_recovery_cc.xml new file mode 100644 index 0000000000..cda780d685 --- /dev/null +++ b/gr-digital/grc/digital_msk_timing_recovery_cc.xml @@ -0,0 +1,49 @@ +<?xml version="1.0"?> +<block> + <name>MSK Timing Recovery</name> + <key>digital_msk_timing_recovery_cc</key> + <import>from gnuradio import digital</import> + <make>digital.msk_timing_recovery_cc($sps, $gain, $limit, $osps)</make> + <callback>set_gain($gain)</callback> + <callback>set_sps($sps)</callback> + <callback>set_limit($limit)</callback> + <param> + <name>Gain</name> + <key>gain</key> + <type>float</type> + </param> + <param> + <name>Samples per symbol</name> + <key>sps</key> + <type>float</type> + </param> + <param> + <name>Error limit</name> + <key>limit</key> + <type>float</type> + </param> + <param> + <name>Output samples per symbol</name> + <key>osps</key> + <type>int</type> + </param> + <sink> + <name>in</name> + <type>complex</type> + </sink> + <source> + <name>out</name> + <type>complex</type> + </source> + <source> + <name>err</name> + <type>float</type> + <optional>1</optional> + </source> + <source> + <name>omega</name> + <type>float</type> + <optional>1</optional> + </source> + +</block> diff --git a/gr-digital/include/gnuradio/digital/CMakeLists.txt b/gr-digital/include/gnuradio/digital/CMakeLists.txt index c39b12bed2..d8fe2b6c4d 100644 --- a/gr-digital/include/gnuradio/digital/CMakeLists.txt +++ b/gr-digital/include/gnuradio/digital/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2011-2014 Free Software Foundation, Inc. +# Copyright 2011-2015 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -22,6 +22,7 @@ ####################################################################### include(GrMiscUtils) GR_EXPAND_X_H(digital chunks_to_symbols_XX bf bc sf sc if ic) +GR_EXPAND_X_H(digital burst_shaper_XX cc ff) add_custom_target(digital_generated_includes DEPENDS ${generated_includes} @@ -42,6 +43,7 @@ install(FILES constellation_decoder_cb.h constellation_receiver_cb.h constellation_soft_decoder_cf.h + corr_est_cc.h correlate_access_code_bb.h correlate_access_code_tag_bb.h correlate_access_code_bb_ts.h @@ -69,9 +71,11 @@ install(FILES lms_dd_equalizer_cc.h map_bb.h metric_type.h + modulate_vector.h mpsk_receiver_cc.h mpsk_snr_est.h mpsk_snr_est_cc.h + msk_timing_recovery_cc.h ofdm_carrier_allocator_cvc.h ofdm_chanest_vcvc.h ofdm_cyclic_prefixer.h diff --git a/gr-digital/include/gnuradio/digital/burst_shaper_XX.h.t b/gr-digital/include/gnuradio/digital/burst_shaper_XX.h.t new file mode 100644 index 0000000000..fd7b69060e --- /dev/null +++ b/gr-digital/include/gnuradio/digital/burst_shaper_XX.h.t @@ -0,0 +1,123 @@ +/* -*- c++ -*- */ +/* + * Copyright 2015 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +/* @WARNING@ */ + +#ifndef @GUARD_NAME@ +#define @GUARD_NAME@ + +#include <gnuradio/digital/api.h> +#include <gnuradio/block.h> + +namespace gr { + namespace digital { + + /*! + * \brief Burst shaper block for applying burst padding and ramping. + * \ingroup packet_operators_blk + * + * \details + * + * This block applies a configurable amount of zero padding before + * and/or after a burst indicated by tagged stream length tags. + * + * If phasing symbols are used, an alternating pattern of +1/-1 + * symbols of length ceil(N/2) will be inserted before and after + * each burst, where N is the length of the taps vector. The ramp- + * up/ramp-down shape will be applied to these phasing symbols. + * + * If phasing symbols are not used, the taper will be applied + * directly to the head and tail of each burst. + * + * Length tags will be updated to include the length of any added + * zero padding or phasing symbols and will be placed at the + * beginning of the modified tagged stream. Any other tags found at + * the same offset as a length tag will also be placed at the + * beginning of the modified tagged stream, since these tags are + * assumed to be associated with the burst rather than a specific + * sample. For example, if "tx_time" tags are used to control + * bursts, their offsets should be consistent with their associated + * burst's length tags. Tags at other offsets will be placed with + * the samples on which they were found. + * + * \li input: stream of @I_TYPE@ + * \li output: stream of @O_TYPE@ + */ + class DIGITAL_API @NAME@ : virtual public block + { + public: + // gr::digital::@BASE_NAME@::sptr + typedef boost::shared_ptr<@BASE_NAME@> sptr; + + /*! + * Make a burst shaper block. + * + * \param taps: vector of window taper taps; the first ceil(N/2) + * items are the up flank and the last ceil(N/2) + * items are the down flank. If taps.size() is odd, + * the middle tap will be used as the last item of + * the up flank and first item of the down flank. + * \param pre_padding: number of zero samples to insert before + * the burst. + * \param post_padding: number of zero samples to append after + * the burst. + * \param insert_phasing: if true, insert alternating +1/-1 + * pattern of length ceil(N/2) before and + * after the burst and apply ramp up and + * ramp down taps, respectively, to the + * inserted patterns instead of the head + * and tail items of the burst. + * \param length_tag_name: the name of the tagged stream length + * tag key. + */ + static sptr make(const std::vector<@O_TYPE@> &taps, + int pre_padding=0, int post_padding=0, + bool insert_phasing=false, + const std::string &length_tag_name="packet_len"); + + /*! + * Returns the amount of zero padding inserted before each burst. + */ + virtual int pre_padding() const = 0; + + /*! + * Returns the amount of zero padding inserted after each burst. + */ + virtual int post_padding() const = 0; + + /*! + * Returns the total amount of zero padding and phasing symbols + * inserted before each burst. + */ + virtual int prefix_length() const = 0; + + /*! + * Returns the total amount of zero padding and phasing symbols + * inserted after each burst. + */ + virtual int suffix_length() const = 0; + }; + + } // namespace digital +} // namespace gr + +#endif /* @GUARD_NAME@ */ diff --git a/gr-digital/include/gnuradio/digital/corr_est_cc.h b/gr-digital/include/gnuradio/digital/corr_est_cc.h new file mode 100644 index 0000000000..e8211cf60b --- /dev/null +++ b/gr-digital/include/gnuradio/digital/corr_est_cc.h @@ -0,0 +1,118 @@ +/* -*- c++ -*- */ +/* + * Copyright 2015 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITAL_CORR_EST_CC_CC_H +#define INCLUDED_DIGITAL_CORR_EST_CC_CC_H + +#include <gnuradio/digital/api.h> +#include <gnuradio/sync_block.h> + +namespace gr { + namespace digital { + + /*! + * \brief Correlate stream with a pre-defined sequence and estimate peak + * \ingroup synchronizers_blk + * + * \details + * Input: + * \li Stream of complex samples. + * + * Output: + * \li Output stream that just passes the input complex samples + * \li tag 'phase_est': estimate of phase offset + * \li tag 'time_est': estimate of symbol timing offset + * \li tag 'corr_est': the correlation value of the estimates + * \li tag 'corr_start': the start sample of the correlation and the value + * + * \li Optional 2nd output stream providing the advanced correlator output + * + * This block is designed to search for a sync word by correlation + * and uses the results of the correlation to get a time and phase + * offset estimate. These estimates are passed downstream as + * stream tags for use by follow-on synchronization blocks. + * + * The sync word is provided as a set of symbols along with a + * baseband matched filter which we use to create the filtered and + * upsampled symbols that we will receive over-the-air. + * + * The phase_est tag can be used by downstream blocks to adjust + * their phase estimatopm/correction loops, and is currently + * implemented by the gr::digital::costas_loop_cc block. + * + * The time_est tag can be used to adjust the sampling timing + * estimate of any downstream synchronization blocks and is + * currently implemented by the gr::digital::pfb_clock_sync_ccf + * block. + * + * The caller must provide a "time_est" and "phase_est" tag + * marking delay from the start of the correlated signal segment, + * in order to mark the proper point in the sync word for + * downstream synchronization blocks. Generally this block cannot + * know where the actual sync word symbols are located relative to + * "corr_start", given that some modulations have pulses with + * intentional ISI. The user should manually examine the primary + * output and the "corr_start" tag postition to determine the + * required tag delay settings for the particular modulation, + * sync word, and downstream blocks used. + * + * For a discussion of the properties of complex correlations, + * with respect to signal processing, see: + * Marple, Jr., S. L., "Estimating Group Delay and Phase Delay + * via Discrete-Time 'Analytic' Cross-Correlation, _IEEE_Transcations_ + * _on_Signal_Processing_, Volume 47, No. 9, September 1999 + * + */ + class DIGITAL_API corr_est_cc : virtual public sync_block + { + public: + typedef boost::shared_ptr<corr_est_cc> sptr; + + /*! + * Make a block that correlates against the \p symbols vector + * and outputs a phase and symbol timing estimate. + * + * \param symbols Set of symbols to correlate against (e.g., a + * sync word). + * \param sps Samples per symbol + * \param mark_delay tag marking delay in samples after the + * corr_start tag + * \param threshold Threshold of correlator, relative to a 100% + * correlation (1.0). Default is 0.9. + */ + static sptr make(const std::vector<gr_complex> &symbols, + float sps, unsigned int mark_delay, float threshold=0.9); + + virtual std::vector<gr_complex> symbols() const = 0; + virtual void set_symbols(const std::vector<gr_complex> &symbols) = 0; + + virtual unsigned int mark_delay() const = 0; + virtual void set_mark_delay(unsigned int mark_delay) = 0; + + virtual float threshold() const = 0; + virtual void set_threshold(float threshold) = 0; + }; + + } // namespace digital +} // namespace gr + +#endif /* INCLUDED_DIGITAL_CORR_EST_CC_H */ diff --git a/gr-digital/include/gnuradio/digital/modulate_vector.h b/gr-digital/include/gnuradio/digital/modulate_vector.h new file mode 100644 index 0000000000..6083099a89 --- /dev/null +++ b/gr-digital/include/gnuradio/digital/modulate_vector.h @@ -0,0 +1,70 @@ +/* + * Copyright 2015 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITAL_MODULATE_VECTOR_H +#define INCLUDED_DIGITAL_MODULATE_VECTOR_H + +#include <gnuradio/digital/api.h> +#include <gnuradio/types.h> + +namespace gr { + namespace digital { + + /*! + * \brief Modulate a vector of data and apply a shaping filter. + * + * \p modulator: Pointer to a byte-to-complex modulator block. + * \p data: Vector of bytes to modulate into symbols. + * \p taps: Post-modulation symbol shaping filter taps. + * + * \details + * This function modulates the input vector and applies a + * symbol shaping filter. It is intended for use with the + * corr_est_cc block as the symbol stream to correlate + * against. + * + * Any differential encoding or other data coding must be + * performed on the input vector before this modulation + * operation. + * + * Be aware that the format of the incoming data must match + * the format the modulator block is expecting. GNURadio + * modulator blocks are inconsistent in their data type + * expectations. For instance, cpmmod_bc expects unpacked, + * signed bytes in (-1, 1), while gmsk_mod expects packed, + * unsigned bytes in (0, 1). In other words, the output of + * gmsk_mod given the input vector [0xaa, 0x00] is equivalent + * to the output of cpmmod_bc given the input vector + * [1,255,1,255,1,255,1,255,255,255,255,255,255,255,255,255] + * + * Please check the documentation or source of the modulator + * before using this function. + * + */ + DIGITAL_API std::vector<gr_complex> + modulate_vector_bc(basic_block_sptr modulator, + std::vector<uint8_t> data, + std::vector<float> taps); + + } /* namespace digital */ +} /* namespace gr */ + +#endif /* INCLUDED_DIGITAL_MODULATE_VECTOR_H */ diff --git a/gr-digital/include/gnuradio/digital/msk_timing_recovery_cc.h b/gr-digital/include/gnuradio/digital/msk_timing_recovery_cc.h new file mode 100644 index 0000000000..770bd91da1 --- /dev/null +++ b/gr-digital/include/gnuradio/digital/msk_timing_recovery_cc.h @@ -0,0 +1,76 @@ +/* -*- c++ -*- */ +/* + * Copyright 2014 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITAL_MSK_TIMING_RECOVERY_CC_H +#define INCLUDED_DIGITAL_MSK_TIMING_RECOVERY_CC_H + +#include <gnuradio/digital/api.h> +#include <gnuradio/block.h> + +namespace gr { + namespace digital { + + /*! + * \brief MSK/GMSK timing recovery + * \ingroup synchronizers_blk + * + * This block performs timing synchronization on CPM modulations using a + * fourth-order nonlinearity feedback method which is non-data-aided. The + * block does not require prior phase synchronization but is relatively + * sensitive to frequency offset (keep offset to 0.1x symbol rate). + * + * For details on the algorithm, see: + * A.N. D'Andrea, U. Mengali, R. Reggiannini: A digital approach to clock + * recovery in generalized minimum shift keying. IEEE Transactions on + * Vehicular Technology, Vol. 39, Issue 3. + */ + class DIGITAL_API msk_timing_recovery_cc : virtual public gr::block + { + public: + typedef boost::shared_ptr<msk_timing_recovery_cc> sptr; + + /*! + * \brief Make an MSK timing recovery block. + * + * \param sps: Samples per symbol + * \param gain: Loop gain of timing error filter (try 0.05) + * \param limit: Relative limit of timing error (try 0.1 for 10% error max) + * \param osps: Output samples per symbol + * + */ + static sptr make(float sps, float gain, float limit, int osps); + + virtual void set_gain(float gain)=0; + virtual float get_gain(void)=0; + + virtual void set_limit(float limit)=0; + virtual float get_limit(void)=0; + + virtual void set_sps(float sps)=0; + virtual float get_sps(void)=0; + }; + + } // namespace digital +} // namespace gr + +#endif /* INCLUDED_DIGITAL_MSK_TIMING_RECOVERY_CC_H */ + diff --git a/gr-digital/lib/CMakeLists.txt b/gr-digital/lib/CMakeLists.txt index eb1008423b..daa577f56f 100644 --- a/gr-digital/lib/CMakeLists.txt +++ b/gr-digital/lib/CMakeLists.txt @@ -45,6 +45,7 @@ endif(ENABLE_GR_CTRLPORT) ######################################################################## include(GrMiscUtils) GR_EXPAND_X_CC_H(digital chunks_to_symbols_XX_impl bf bc sf sc if ic) +GR_EXPAND_X_CC_H(digital burst_shaper_XX_impl cc ff) ######################################################################## # Setup library @@ -60,6 +61,7 @@ list(APPEND digital_sources constellation_decoder_cb_impl.cc constellation_receiver_cb_impl.cc constellation_soft_decoder_cf_impl.cc + corr_est_cc_impl.cc correlate_access_code_bb_impl.cc correlate_access_code_tag_bb_impl.cc correlate_access_code_bb_ts_impl.cc @@ -85,9 +87,11 @@ list(APPEND digital_sources kurtotic_equalizer_cc_impl.cc lms_dd_equalizer_cc_impl.cc map_bb_impl.cc + modulate_vector.cc mpsk_receiver_cc_impl.cc mpsk_snr_est.cc mpsk_snr_est_cc_impl.cc + msk_timing_recovery_cc_impl.cc ofdm_carrier_allocator_cvc_impl.cc ofdm_chanest_vcvc_impl.cc ofdm_cyclic_prefixer_impl.cc diff --git a/gr-digital/lib/burst_shaper_XX_impl.cc.t b/gr-digital/lib/burst_shaper_XX_impl.cc.t new file mode 100644 index 0000000000..85add49115 --- /dev/null +++ b/gr-digital/lib/burst_shaper_XX_impl.cc.t @@ -0,0 +1,353 @@ +/* -*- c++ -*- */ +/* + * Copyright 2015 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +/* @WARNING@ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <boost/format.hpp> +#include <gnuradio/io_signature.h> +#include <volk/volk.h> +#include "@IMPL_NAME@.h" + +#ifndef VOLK_MULT_gr_complex +#define VOLK_MULT_gr_complex volk_32fc_x2_multiply_32fc +#endif +#ifndef VOLK_MULT_float +#define VOLK_MULT_float volk_32f_x2_multiply_32f +#endif + +namespace gr { + namespace digital { + + @BASE_NAME@::sptr + @BASE_NAME@::make(const std::vector<@I_TYPE@> &taps, + int pre_padding, int post_padding, + bool insert_phasing, + const std::string &length_tag_name) + { + return gnuradio::get_initial_sptr + (new @IMPL_NAME@(taps, pre_padding, post_padding, + insert_phasing, length_tag_name)); + } + + @IMPL_NAME@::@IMPL_NAME@(const std::vector<@I_TYPE@> &taps, + int pre_padding, int post_padding, + bool insert_phasing, + const std::string &length_tag_name) + : gr::block("@BASE_NAME@", + gr::io_signature::make(1, 1, sizeof(@I_TYPE@)), + gr::io_signature::make(1, 1, sizeof(@O_TYPE@))), + d_up_ramp(taps.begin(), taps.begin() + taps.size()/2 + taps.size()%2), + d_down_ramp(taps.begin() + taps.size()/2, taps.end()), + d_nprepad(pre_padding), + d_npostpad(post_padding), + d_insert_phasing(insert_phasing), + d_length_tag_key(pmt::string_to_symbol(length_tag_name)), + d_ncopy(0), + d_limit(0), + d_index(0), + d_length_tag_offset(0), + d_finished(false), + d_state(STATE_WAIT) + { + assert(d_up_ramp.size() == d_down_ramp.size()); + + d_up_phasing.resize(d_up_ramp.size()); + d_down_phasing.resize(d_down_ramp.size()); + + @I_TYPE@ symbol; + for(unsigned int i = 0; i < d_up_ramp.size(); i++) { + symbol = (i%2 == 0) ? @I_TYPE@(1.0f) : @I_TYPE@(-1.0f); + d_up_phasing[i] = symbol * d_up_ramp[i]; + d_down_phasing[i] = symbol * d_down_ramp[i]; + } + + //set_relative_rate(1.0); + set_tag_propagation_policy(TPP_DONT); + } + + @IMPL_NAME@::~@IMPL_NAME@() + { + } + + void + @IMPL_NAME@::forecast(int noutput_items, + gr_vector_int &ninput_items_required) + { + ninput_items_required[0] = noutput_items; + } + + int + @IMPL_NAME@::general_work(int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + const @I_TYPE@ *in = reinterpret_cast<const @I_TYPE@ *>(input_items[0]); + @O_TYPE@ *out = reinterpret_cast<@O_TYPE@ *>(output_items[0]); + + int nwritten = 0; + int nread = 0; + int nspace = 0; + int nskip = 0; + int curr_tag_index = 0; + + std::vector<tag_t> length_tags; + get_tags_in_window(length_tags, 0, 0, ninput_items[0], d_length_tag_key); + std::sort(length_tags.rbegin(), length_tags.rend(), tag_t::offset_compare); + + while((nwritten < noutput_items) && (nread < ninput_items[0])) { + if(d_finished) { + d_finished = false; + break; + } + nspace = noutput_items - nwritten; + switch(d_state) { + case(STATE_WAIT): + if(!length_tags.empty()) { + d_length_tag_offset = length_tags.back().offset; + curr_tag_index = (int)(d_length_tag_offset - nitems_read(0)); + d_ncopy = pmt::to_long(length_tags.back().value); + length_tags.pop_back(); + nskip = curr_tag_index - nread; + add_length_tag(nwritten); + propagate_tags(curr_tag_index, nwritten, 1, false); + enter_prepad(); + } + else { + nskip = ninput_items[0] - nread; + } + if(nskip > 0) { + GR_LOG_WARN(d_logger, + boost::format("Dropping %1% samples") % + nskip); + nread += nskip; + in += nskip; + } + break; + + case(STATE_PREPAD): + write_padding(out, nwritten, nspace); + if(d_index == d_limit) + enter_rampup(); + break; + + case(STATE_RAMPUP): + apply_ramp(out, in, nwritten, nread, nspace); + if(d_index == d_limit) + enter_copy(); + break; + + case(STATE_COPY): + copy_items(out, in, nwritten, nread, nspace); + if(d_index == d_limit) + enter_rampdown(); + break; + + case(STATE_RAMPDOWN): + apply_ramp(out, in, nwritten, nread, nspace); + if(d_index == d_limit) + enter_postpad(); + break; + + case(STATE_POSTPAD): + write_padding(out, nwritten, nspace); + if(d_index == d_limit) + enter_wait(); + break; + + default: + throw std::runtime_error("@BASE_NAME@: invalid state"); + } + } + + consume_each(nread); + + return nwritten; + } + + int + @IMPL_NAME@::prefix_length() const + { + return (d_insert_phasing) ? + d_nprepad + d_up_ramp.size() : d_nprepad; + } + + int + @IMPL_NAME@::suffix_length() const + { + return (d_insert_phasing) ? + d_npostpad + d_down_ramp.size() : d_npostpad; + } + + void + @IMPL_NAME@::write_padding(@O_TYPE@ *&dst, int &nwritten, int nspace) + { + int nprocess = std::min(d_limit - d_index, nspace); + std::memset(dst, 0x00, nprocess * sizeof(@O_TYPE@)); + dst += nprocess; + nwritten += nprocess; + d_index += nprocess; + } + + void + @IMPL_NAME@::copy_items(@O_TYPE@ *&dst, const @I_TYPE@ *&src, int &nwritten, + int &nread, int nspace) + { + int nprocess = std::min(d_limit - d_index, nspace); + propagate_tags(nread, nwritten, nprocess); + std::memcpy(dst, src, nprocess * sizeof(@O_TYPE@)); + dst += nprocess; + nwritten += nprocess; + src += nprocess; + nread += nprocess; + d_index += nprocess; + } + + void + @IMPL_NAME@::apply_ramp(@O_TYPE@ *&dst, const @I_TYPE@ *&src, int &nwritten, + int &nread, int nspace) + { + int nprocess = std::min(d_limit - d_index, nspace); + @O_TYPE@ *phasing; + const @O_TYPE@ *ramp; + + if(d_state == STATE_RAMPUP) { + phasing = &d_up_phasing[d_index]; + ramp = &d_up_ramp[d_index]; + } + else { + phasing = &d_down_phasing[d_index]; + ramp = &d_down_ramp[d_index]; + } + + if(d_insert_phasing) + std::memcpy(dst, phasing, nprocess * sizeof(@O_TYPE@)); + else { + propagate_tags(nread, nwritten, nprocess); + VOLK_MULT_@O_TYPE@(dst, src, ramp, nprocess); + src += nprocess; + nread += nprocess; + } + + dst += nprocess; + nwritten += nprocess; + d_index += nprocess; + } + + void + @IMPL_NAME@::add_length_tag(int offset) + { + add_item_tag(0, nitems_written(0) + offset, d_length_tag_key, + pmt::from_long(d_ncopy + prefix_length() + + suffix_length()), + pmt::string_to_symbol(name())); + } + + void + @IMPL_NAME@::propagate_tags(int in_offset, int out_offset, int count, bool skip) + { + uint64_t abs_start = nitems_read(0) + in_offset; + uint64_t abs_end = abs_start + count; + uint64_t abs_offset = nitems_written(0) + out_offset; + tag_t temp_tag; + + std::vector<tag_t> tags; + std::vector<tag_t>::iterator it; + + get_tags_in_range(tags, 0, abs_start, abs_end); + + for(it = tags.begin(); it != tags.end(); it++) { + if(!pmt::equal(it->key, d_length_tag_key)) { + if(skip && (it->offset == d_length_tag_offset)) + continue; + temp_tag = *it; + temp_tag.offset = abs_offset + it->offset - abs_start; + add_item_tag(0, temp_tag); + } + } + } + + void + @IMPL_NAME@::enter_wait() + { + d_finished = true; + d_index = 0; + d_state = STATE_WAIT; + } + + void + @IMPL_NAME@::enter_prepad() + { + d_limit = d_nprepad; + d_index = 0; + d_state = STATE_PREPAD; + } + + void + @IMPL_NAME@::enter_rampup() + { + if(d_insert_phasing) + d_limit = d_up_ramp.size(); + else + d_limit = std::min((size_t)(d_ncopy/2), d_up_ramp.size()); + d_index = 0; + d_state = STATE_RAMPUP; + } + + void + @IMPL_NAME@::enter_copy() + { + if(d_insert_phasing) + d_limit = d_ncopy; + else + d_limit = d_ncopy - std::min((size_t)((d_ncopy/2)*2), + d_up_ramp.size() + + d_down_ramp.size()); + d_index = 0; + d_state = STATE_COPY; + } + + void + @IMPL_NAME@::enter_rampdown() + { + if(d_insert_phasing) + d_limit = d_down_ramp.size(); + else + d_limit = std::min((size_t)(d_ncopy/2), d_down_ramp.size()); + d_index = 0; + d_state = STATE_RAMPDOWN; + } + + void + @IMPL_NAME@::enter_postpad() + { + d_limit = d_npostpad; + d_index = 0; + d_state = STATE_POSTPAD; + } + + } /* namespace digital */ +} /* namespace gr */ diff --git a/gr-digital/lib/burst_shaper_XX_impl.h.t b/gr-digital/lib/burst_shaper_XX_impl.h.t new file mode 100644 index 0000000000..99ad7fb08a --- /dev/null +++ b/gr-digital/lib/burst_shaper_XX_impl.h.t @@ -0,0 +1,90 @@ +/* -*- c++ -*- */ +/* + * Copyright 2015 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +/* @WARNING@ */ + +#ifndef @GUARD_NAME@ +#define @GUARD_NAME@ + +#include <gnuradio/digital/@BASE_NAME@.h> + +namespace gr { + namespace digital { + + class @IMPL_NAME@ : public @BASE_NAME@ + { + protected: + enum state_t {STATE_WAIT, STATE_PREPAD, STATE_RAMPUP, + STATE_COPY, STATE_RAMPDOWN, STATE_POSTPAD}; + + private: + const std::vector<@O_TYPE@> d_up_ramp; + const std::vector<@O_TYPE@> d_down_ramp; + const int d_nprepad; + const int d_npostpad; + const bool d_insert_phasing; + const pmt::pmt_t d_length_tag_key; + std::vector<@O_TYPE@> d_up_phasing; + std::vector<@O_TYPE@> d_down_phasing; + int d_ncopy; + int d_limit; + int d_index; + uint64_t d_length_tag_offset; + bool d_finished; + state_t d_state; + + void write_padding(@O_TYPE@ *&dst, int &nwritten, int nspace); + void copy_items(@O_TYPE@ *&dst, const @I_TYPE@ *&src, int &nwritten, + int &nread, int nspace); + void apply_ramp(@O_TYPE@ *&dst, const @I_TYPE@ *&src, int &nwritten, + int &nread, int nspace); + void add_length_tag(int offset); + void propagate_tags(int in_offset, int out_offset, int count, bool skip=true); + void enter_wait(); + void enter_prepad(); + void enter_rampup(); + void enter_copy(); + void enter_rampdown(); + void enter_postpad(); + + public: + @IMPL_NAME@(const std::vector<@O_TYPE@> &taps, int pre_padding, + int post_padding, bool insert_phasing, + const std::string &length_tag_name); + ~@IMPL_NAME@(); + + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + + int general_work(int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + int pre_padding() const { return d_nprepad; } + int post_padding() const { return d_npostpad; } + int prefix_length() const; + int suffix_length() const; + }; + + } // namespace digital +} // namespace gr + +#endif /* @GUARD_NAME@ */ diff --git a/gr-digital/lib/corr_est_cc_impl.cc b/gr-digital/lib/corr_est_cc_impl.cc new file mode 100644 index 0000000000..ac753fbba5 --- /dev/null +++ b/gr-digital/lib/corr_est_cc_impl.cc @@ -0,0 +1,331 @@ +/* -*- c++ -*- */ +/* + * Copyright 2015 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gnuradio/io_signature.h> +#include <gnuradio/math.h> +#include "corr_est_cc_impl.h" +#include <volk/volk.h> +#include <boost/format.hpp> +#include <boost/math/special_functions/round.hpp> +#include <gnuradio/filter/pfb_arb_resampler.h> +#include <gnuradio/filter/firdes.h> + +namespace gr { + namespace digital { + + corr_est_cc::sptr + corr_est_cc::make(const std::vector<gr_complex> &symbols, + float sps, unsigned int mark_delay, + float threshold) + { + return gnuradio::get_initial_sptr + (new corr_est_cc_impl(symbols, sps, mark_delay, threshold)); + } + + corr_est_cc_impl::corr_est_cc_impl(const std::vector<gr_complex> &symbols, + float sps, unsigned int mark_delay, + float threshold) + : sync_block("corr_est_cc", + io_signature::make(1, 1, sizeof(gr_complex)), + io_signature::make(1, 2, sizeof(gr_complex))), + d_src_id(pmt::intern(alias())) + { + d_sps = sps; + + // Create time-reversed conjugate of symbols + d_symbols = symbols; + for(size_t i=0; i < d_symbols.size(); i++) { + d_symbols[i] = conj(d_symbols[i]); + } + std::reverse(d_symbols.begin(), d_symbols.end()); + + set_mark_delay(mark_delay); + set_threshold(threshold); + + // Correlation filter + d_filter = new kernel::fft_filter_ccc(1, d_symbols); + + // Per comments in gr-filter/include/gnuradio/filter/fft_filter.h, + // set the block output multiple to the FFT filter kernel's internal, + // assumed "nsamples", to ensure the scheduler always passes a + // proper number of samples. + int nsamples; + nsamples = d_filter->set_taps(d_symbols); + set_output_multiple(nsamples); + + // It looks like the kernel::fft_filter_ccc stashes a tail between + // calls, so that contains our filtering history (I think). The + // fft_filter_ccc block (which calls the kernel::fft_filter_ccc) sets + // the history to 1 (0 history items), so let's follow its lead. + //set_history(1); + + // We'll (ab)use the history for our own purposes of tagging back in time. + // Keep a history of the length of the sync word to delay for tagging. + set_history(d_symbols.size()+1); + + declare_sample_delay(1, 0); + declare_sample_delay(0, d_symbols.size()); + + // Setting the alignment multiple for volk causes problems with the + // expected behavior of setting the output multiple for the FFT filter. + // Don't set the alignment multiple. + //const int alignment_multiple = + // volk_get_alignment() / sizeof(gr_complex); + //set_alignment(std::max(1,alignment_multiple)); + + // In order to easily support the optional second output, + // don't deal with an unbounded max number of output items. + // For the common case of not using the optional second output, + // this ensures we optimally call the volk routines. + const size_t nitems = 24*1024; + set_max_noutput_items(nitems); + d_corr = (gr_complex *) + volk_malloc(sizeof(gr_complex)*nitems, volk_get_alignment()); + d_corr_mag = (float *) + volk_malloc(sizeof(float)*nitems, volk_get_alignment()); + } + + corr_est_cc_impl::~corr_est_cc_impl() + { + delete d_filter; + volk_free(d_corr); + volk_free(d_corr_mag); + } + + std::vector<gr_complex> + corr_est_cc_impl::symbols() const + { + return d_symbols; + } + + void + corr_est_cc_impl::set_symbols(const std::vector<gr_complex> &symbols) + { + gr::thread::scoped_lock lock(d_setlock); + + d_symbols = symbols; + + // Per comments in gr-filter/include/gnuradio/filter/fft_filter.h, + // set the block output multiple to the FFT filter kernel's internal, + // assumed "nsamples", to ensure the scheduler always passes a + // proper number of samples. + int nsamples; + nsamples = d_filter->set_taps(d_symbols); + set_output_multiple(nsamples); + + // It looks like the kernel::fft_filter_ccc stashes a tail between + // calls, so that contains our filtering history (I think). The + // fft_filter_ccc block (which calls the kernel::fft_filter_ccc) sets + // the history to 1 (0 history items), so let's follow its lead. + //set_history(1); + + // We'll (ab)use the history for our own purposes of tagging back in time. + // Keep a history of the length of the sync word to delay for tagging. + set_history(d_symbols.size()+1); + + declare_sample_delay(1, 0); + declare_sample_delay(0, d_symbols.size()); + + _set_mark_delay(d_stashed_mark_delay); + _set_threshold(d_stashed_threshold); + } + + unsigned int + corr_est_cc_impl::mark_delay() const + { + return d_mark_delay; + } + + void + corr_est_cc_impl::_set_mark_delay(unsigned int mark_delay) + { + d_stashed_mark_delay = mark_delay; + + if(mark_delay >= d_symbols.size()) { + d_mark_delay = d_symbols.size()-1; + GR_LOG_WARN(d_logger, boost::format("set_mark_delay: asked for %1% but due " + "to the symbol size constraints, " + "mark delay set to %2%.") \ + % mark_delay % d_mark_delay); + } + else { + d_mark_delay = mark_delay; + } + } + + void + corr_est_cc_impl::set_mark_delay(unsigned int mark_delay) + { + gr::thread::scoped_lock lock(d_setlock); + _set_mark_delay(mark_delay); + } + + float + corr_est_cc_impl::threshold() const + { + return d_thresh; + } + + void + corr_est_cc_impl::_set_threshold(float threshold) + { + d_stashed_threshold = threshold; + + // Compute a correlation threshold. + // Compute the value of the discrete autocorrelation of the matched + // filter with offset 0 (aka the autocorrelation peak). + float corr = 0; + for(size_t i = 0; i < d_symbols.size(); i++) + corr += abs(d_symbols[i]*conj(d_symbols[i])); + d_thresh = threshold*corr*corr; + } + + void + corr_est_cc_impl::set_threshold(float threshold) + { + gr::thread::scoped_lock lock(d_setlock); + _set_threshold(threshold); + } + + int + corr_est_cc_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + gr::thread::scoped_lock lock(d_setlock); + + const gr_complex *in = (gr_complex *)input_items[0]; + gr_complex *out = (gr_complex*)output_items[0]; + gr_complex *corr; + if (output_items.size() > 1) + corr = (gr_complex *) output_items[1]; + else + corr = d_corr; + + // Our correlation filter length + unsigned int hist_len = history() - 1; + + // Delay the output by our correlation filter length so we can + // tag backwards in time + memcpy(out, &in[0], sizeof(gr_complex)*noutput_items); + + // Calculate the correlation of the non-delayed input with the + // known symbols. + d_filter->filter(noutput_items, &in[hist_len], corr); + + // Find the magnitude squared of the correlation + volk_32fc_magnitude_squared_32f(&d_corr_mag[0], corr, noutput_items); + + int isps = (int)(d_sps + 0.5f); + int i = 0; + while(i < noutput_items) { + // Look for the correlator output to cross the threshold + if (d_corr_mag[i] <= d_thresh) { + i++; + continue; + } + // Go to (just past) the current correlator output peak + while ((i < (noutput_items-1)) && + (d_corr_mag[i] < d_corr_mag[i+1])) + i++; + + // Delaying the primary signal output by the matched filter + // length using history(), means that the the peak output of + // the matched filter aligns with the start of the desired + // sync word in the primary signal output. This corr_start + // tag is not offset to another sample, so that downstream + // data-aided blocks (like adaptive equalizers) know exactly + // where the start of the correlated symbols are. + add_item_tag(0, nitems_written(0) + i, pmt::intern("corr_start"), + pmt::from_double(d_corr_mag[i]), d_src_id); + + // Peak detector using a "center of mass" approach center + // holds the +/- fraction of a sample index from the found + // peak index to the estimated actual peak index. + double center = 0.0; + if (i > 0 and i < (noutput_items - 1)) { + double nom = 0, den = 0; + for(int s = 0; s < 3; s++) { + nom += (s+1)*d_corr_mag[i+s-1]; + den += d_corr_mag[i+s-1]; + } + center = nom / den - 2.0; + } + + // Calculate the phase offset of the incoming signal. + // + // The analytic cross-correlation is: + // + // 2A*e_bb(t-t_d)*exp(-j*2*pi*f*(t-t_d) - j*phi_bb(t-t_d) - j*theta_c) + // + + // The analytic auto-correlation's envelope, e_bb(), has its + // peak at the "group delay" time, t = t_d. The analytic + // cross-correlation's center frequency phase shift, theta_c, + // is determined from the argument of the analytic + // cross-correlation at the "group delay" time, t = t_d. + // + // Taking the argument of the analytic cross-correlation at + // any other time will include the baseband auto-correlation's + // phase term, phi_bb(t-t_d), and a frequency dependent term + // of the cross-correlation, which I don't believe maps simply + // to expected symbol phase differences. + float phase = fast_atan2f(corr[i].imag(), corr[i].real()); + int index = i + d_mark_delay; + + add_item_tag(0, nitems_written(0) + index, pmt::intern("phase_est"), + pmt::from_double(phase), d_src_id); + add_item_tag(0, nitems_written(0) + index, pmt::intern("time_est"), + pmt::from_double(center), d_src_id); + // N.B. the appropriate d_corr_mag[] index is "i", not "index". + add_item_tag(0, nitems_written(0) + index, pmt::intern("corr_est"), + pmt::from_double(d_corr_mag[i]), d_src_id); + + if (output_items.size() > 1) { + // N.B. these debug tags are not offset to avoid walking off out buf + add_item_tag(1, nitems_written(0) + i, pmt::intern("phase_est"), + pmt::from_double(phase), d_src_id); + add_item_tag(1, nitems_written(0) + i, pmt::intern("time_est"), + pmt::from_double(center), d_src_id); + add_item_tag(1, nitems_written(0) + i, pmt::intern("corr_est"), + pmt::from_double(d_corr_mag[i]), d_src_id); + } + + // Skip ahead to the next potential symbol peak + // (for non-offset/interleaved symbols) + i += isps; + } + + //if (output_items.size() > 1) + // add_item_tag(1, nitems_written(0) + noutput_items - 1, + // pmt::intern("ce_eow"), pmt::from_uint64(noutput_items), + // d_src_id); + + return noutput_items; + } + + } /* namespace digital */ +} /* namespace gr */ diff --git a/gr-digital/lib/corr_est_cc_impl.h b/gr-digital/lib/corr_est_cc_impl.h new file mode 100644 index 0000000000..6e8dd17083 --- /dev/null +++ b/gr-digital/lib/corr_est_cc_impl.h @@ -0,0 +1,73 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITAL_CORR_EST_CC_IMPL_H +#define INCLUDED_DIGITAL_CORR_EST_CC_IMPL_H + +#include <gnuradio/digital/corr_est_cc.h> +#include <gnuradio/filter/fft_filter.h> + +using namespace gr::filter; + +namespace gr { + namespace digital { + + class corr_est_cc_impl : public corr_est_cc + { + private: + pmt::pmt_t d_src_id; + std::vector<gr_complex> d_symbols; + float d_sps; + unsigned int d_mark_delay, d_stashed_mark_delay; + float d_thresh, d_stashed_threshold; + kernel::fft_filter_ccc *d_filter; + + gr_complex *d_corr; + float *d_corr_mag; + + void _set_mark_delay(unsigned int mark_delay); + void _set_threshold(float threshold); + + public: + corr_est_cc_impl(const std::vector<gr_complex> &symbols, + float sps, unsigned int mark_delay, + float threshold=0.9); + ~corr_est_cc_impl(); + + std::vector<gr_complex> symbols() const; + void set_symbols(const std::vector<gr_complex> &symbols); + + unsigned int mark_delay() const; + void set_mark_delay(unsigned int mark_delay); + + float threshold() const; + void set_threshold(float threshold); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } // namespace digital +} // namespace gr + +#endif /* INCLUDED_DIGITAL_CORR_EST_CC_IMPL_H */ diff --git a/gr-digital/lib/modulate_vector.cc b/gr-digital/lib/modulate_vector.cc new file mode 100644 index 0000000000..281e3435c5 --- /dev/null +++ b/gr-digital/lib/modulate_vector.cc @@ -0,0 +1,70 @@ +/* +* Copyright 2015 Free Software Foundation, Inc. +* +* This file is part of GNU Radio +* +* GNU Radio is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 3, or (at your option) +* any later version. +* +* GNU Radio is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with GNU Radio; see the file COPYING. If not, write to +* the Free Software Foundation, Inc., 51 Franklin Street, +* Boston, MA 02110-1301, USA. +* +*/ + +/* +* Generate a modulated transmit vector corresponding to a particular +* data sequence, resampling rate, and shaping filter. The output is +* suitable for use as a candidate filter for the correlate_and_sync +* block, or just for prototyping. +* +* It accepts a sptr to a modulator block as an argument; given +* suitable data vectors and arguments you should be able to use any of +* the GNU Radio modulator blocks as a modulator. Be careful that some +* modulators expect packed words, some expect unpacked words, and some +* have unique expectations, requirements, or limitations. + +* modulator as: gr.block or gr.hier_block +* taps as: list or array +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gnuradio/blocks/vector_source_b.h> +#include <gnuradio/blocks/vector_sink_c.h> +#include <gnuradio/filter/fir_filter_ccf.h> +#include <gnuradio/top_block.h> +#include <gnuradio/digital/modulate_vector.h> + +namespace gr { + namespace digital { + std::vector<gr_complex> modulate_vector_bc(basic_block_sptr modulator, + std::vector<uint8_t> data, + std::vector<float> taps) + { + blocks::vector_source_b::sptr vector_src = blocks::vector_source_b::make(data); + filter::fir_filter_ccf::sptr filter = filter::fir_filter_ccf::make(1, taps); + blocks::vector_sink_c::sptr vector_sink = blocks::vector_sink_c::make(); + + top_block_sptr tb = make_top_block("modulate_vector"); + + tb->connect(vector_src, 0, modulator, 0); + tb->connect(modulator, 0, filter, 0); + tb->connect(filter, 0, vector_sink, 0); + + tb->run(); + + return vector_sink->data(); + } + } /* namespace digital */ +} /* namespace gr */ diff --git a/gr-digital/lib/msk_timing_recovery_cc_impl.cc b/gr-digital/lib/msk_timing_recovery_cc_impl.cc new file mode 100644 index 0000000000..a567357019 --- /dev/null +++ b/gr-digital/lib/msk_timing_recovery_cc_impl.cc @@ -0,0 +1,210 @@ +/* -*- c++ -*- */ +/* + * Copyright 2014 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gnuradio/io_signature.h> +#include <gnuradio/math.h> +#include "msk_timing_recovery_cc_impl.h" +#include <gnuradio/filter/firdes.h> + +namespace gr { + namespace digital { + + msk_timing_recovery_cc::sptr + msk_timing_recovery_cc::make(float sps, float gain, float limit, int osps=1) + { + return gnuradio::get_initial_sptr + (new msk_timing_recovery_cc_impl(sps, gain, limit, osps)); + } + + /* + * The private constructor + */ + msk_timing_recovery_cc_impl::msk_timing_recovery_cc_impl(float sps, float gain, float limit, int osps) + : gr::block("msk_timing_recovery_cc", + gr::io_signature::make(1, 1, sizeof(gr_complex)), + gr::io_signature::make3(1, 3, sizeof(gr_complex), sizeof(float), sizeof(float))), + d_limit(limit), + d_interp(new filter::mmse_fir_interpolator_cc()), + d_dly_conj_1(0), + d_dly_conj_2(0), + d_dly_diff_1(0), + d_mu(0.5), + d_div(0), + d_osps(osps) + { + set_sps(sps); + enable_update_rate(true); //fixes tag propagation through variable rate blox + set_gain(gain); + if(d_osps != 1 && d_osps != 2) throw std::out_of_range("osps must be 1 or 2"); + } + + msk_timing_recovery_cc_impl::~msk_timing_recovery_cc_impl() + { + delete d_interp; + } + + void msk_timing_recovery_cc_impl::set_sps(float sps) { + d_sps = sps/2.0; //loop runs at 2x sps + d_omega = d_sps; + set_relative_rate(d_osps/sps); +// set_history(d_sps); + } + + float msk_timing_recovery_cc_impl::get_sps(void) { + return d_sps; + } + + void msk_timing_recovery_cc_impl::set_gain(float gain) { + d_gain = gain; + if(d_gain <= 0) throw std::out_of_range("Gain must be positive"); + d_gain_omega = d_gain*d_gain*0.25; + } + + float msk_timing_recovery_cc_impl::get_gain(void) { + return d_gain; + } + + void msk_timing_recovery_cc_impl::set_limit(float limit) { + d_limit = limit; + } + + float msk_timing_recovery_cc_impl::get_limit(void) { + return d_limit; + } + + void + msk_timing_recovery_cc_impl::forecast (int noutput_items, gr_vector_int &ninput_items_required) + { + unsigned ninputs = ninput_items_required.size(); + for(unsigned i=0; i<ninputs; i++) { + ninput_items_required[i] = (int)ceil((noutput_items*d_sps*2) + 3.0*d_sps + d_interp->ntaps()); + } + } + + int + msk_timing_recovery_cc_impl::general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + const gr_complex *in = (const gr_complex *) input_items[0]; + gr_complex *out = (gr_complex *) output_items[0]; + float *out2, *out3; + if(output_items.size() >= 2) out2 = (float *) output_items[1]; + if(output_items.size() >= 3) out3 = (float *) output_items[2]; + int oidx=0, iidx=0; + int ninp=ninput_items[0] - 3.0*d_sps; + if(ninp <= 0) { + consume_each(0); + return(0); + } + + std::vector<tag_t> tags; + get_tags_in_range(tags, + 0, + nitems_read(0), + nitems_read(0)+ninp, + pmt::intern("time_est")); + + gr_complex sq, //Squared input + dly_conj, //Input delayed sps and conjugated + nlin_out, //output of the nonlinearity + in_interp; //interpolated input + float err_out=0; //error output + + while(oidx < noutput_items && iidx < ninp) { + //check to see if there's a tag to reset the timing estimate + if(tags.size() > 0) { + int offset = tags[0].offset - nitems_read(0); + if((offset >= iidx) && (offset < (iidx+d_sps))) { + float center = (float) pmt::to_double(tags[0].value); + if(center != center) { //test for NaN, it happens somehow + tags.erase(tags.begin()); + goto out; + } + d_mu = center; + iidx = offset; + if(d_mu<0) { + d_mu++; + iidx--; + } + d_div = 0; + d_omega = d_sps; + d_dly_conj_2 = d_dly_conj_1; + //this keeps the block from outputting an odd number of + //samples and throwing off downstream blocks which depend + //on proper alignment -- for instance, a decimating FIR + //filter. +// if(d_div == 0 and d_osps == 2) oidx++; + tags.erase(tags.begin()); + } + } + +out: + //the actual equation for the nonlinearity is as follows: + //e(n) = in[n]^2 * in[n-sps].conj()^2 + //we then differentiate the error by subtracting the sample delayed by d_sps/2 + in_interp = d_interp->interpolate(&in[iidx], d_mu); + sq = in_interp*in_interp; + //conjugation is distributive. + dly_conj = std::conj(d_dly_conj_2*d_dly_conj_2); + nlin_out = sq*dly_conj; + //TODO: paper argues that some improvement can be had + //if you either operate at >2sps or use a better numeric + //differentiation method. + err_out = std::real(nlin_out - d_dly_diff_1); + if(d_div % 2) { //error loop calc once per symbol + err_out = gr::branchless_clip(err_out, 3.0); + d_omega += d_gain_omega*err_out; + d_omega = d_sps + gr::branchless_clip(d_omega-d_sps, d_limit); + d_mu += d_gain*err_out; + } + //output every other d_sps by default. + if(!(d_div % 2) or d_osps==2) { + out[oidx] = in_interp; + if(output_items.size() >= 2) out2[oidx] = err_out; + if(output_items.size() >= 3) out3[oidx] = d_mu; + oidx++; + } + d_div++; + + d_dly_conj_1 = in_interp; + d_dly_conj_2 = d_dly_conj_1; + d_dly_diff_1 = nlin_out; + + //update interpolator twice per symbol + d_mu += d_omega; + iidx += (int)floor(d_mu); + d_mu -= floor(d_mu); + } + + consume_each (iidx); + return oidx; + } + + } /* namespace digital */ +} /* namespace gr */ + diff --git a/gr-digital/lib/msk_timing_recovery_cc_impl.h b/gr-digital/lib/msk_timing_recovery_cc_impl.h new file mode 100644 index 0000000000..02c29dc6ec --- /dev/null +++ b/gr-digital/lib/msk_timing_recovery_cc_impl.h @@ -0,0 +1,72 @@ +/* -*- c++ -*- */ +/* + * Copyright 2014 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DIGITAL_MSK_TIMING_RECOVERY_CC_IMPL_H +#define INCLUDED_DIGITAL_MSK_TIMING_RECOVERY_CC_IMPL_H + +#include <gnuradio/digital/msk_timing_recovery_cc.h> +#include <gnuradio/filter/mmse_fir_interpolator_cc.h> +#include <boost/circular_buffer.hpp> +#include <gnuradio/filter/fir_filter_with_buffer.h> + +namespace gr { + namespace digital { + + class msk_timing_recovery_cc_impl : public msk_timing_recovery_cc + { + private: + float d_sps; + float d_gain; + float d_limit; + filter::mmse_fir_interpolator_cc *d_interp; + filter::kernel::fir_filter_with_buffer_fff *d_decim; + gr_complex d_dly_conj_1, d_dly_conj_2, d_dly_diff_1; + float d_mu, d_omega, d_gain_omega; + int d_div; + int d_osps; + int d_loop_rate; + + public: + msk_timing_recovery_cc_impl(float sps, float gain, float limit, int osps); + ~msk_timing_recovery_cc_impl(); + + // Where all the action really happens + void forecast (int noutput_items, gr_vector_int &ninput_items_required); + + int general_work(int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + void set_gain(float gain); + float get_gain(void); + + void set_limit(float limit); + float get_limit(void); + + void set_sps(float sps); + float get_sps(void); + }; + } // namespace digital +} // namespace gr + +#endif /* INCLUDED_DIGITAL_MSK_TIMING_RECOVERY_CC_IMPL_H */ + diff --git a/gr-digital/python/digital/qa_burst_shaper.py b/gr-digital/python/digital/qa_burst_shaper.py new file mode 100755 index 0000000000..f85b79ceec --- /dev/null +++ b/gr-digital/python/digital/qa_burst_shaper.py @@ -0,0 +1,340 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2015 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest +from gnuradio import blocks, digital +import pmt +import numpy as np +import sys + +def make_length_tag(offset, length): + return gr.python_to_tag({'offset' : offset, + 'key' : pmt.intern('packet_len'), + 'value' : pmt.from_long(length), + 'srcid' : pmt.intern('qa_burst_shaper')}) + +def make_tag(offset, key, value): + return gr.python_to_tag({'offset' : offset, + 'key' : pmt.intern(key), + 'value' : value, + 'srcid' : pmt.intern('qa_burst_shaper')}) + +def compare_tags(a, b): + return a.offset == b.offset and pmt.equal(a.key, b.key) and \ + pmt.equal(a.value, b.value) + +class qa_burst_shaper (gr_unittest.TestCase): + + def setUp (self): + self.tb = gr.top_block () + + def tearDown (self): + self.tb = None + + def test_ff (self): + prepad = 10 + postpad = 10 + length = 20 + data = np.ones(length + 10) # need 10 more to push things through + window = np.concatenate((-2.0*np.ones(5), -4.0*np.ones(5))) + tags = (make_length_tag(0, length),) + expected = np.concatenate((np.zeros(prepad), window[0:5], + np.ones(length - len(window)), window[5:10], + np.zeros(postpad))) + etag = make_length_tag(0, length + prepad + postpad) + + # flowgraph + source = blocks.vector_source_f(data, tags=tags) + shaper = digital.burst_shaper_ff(window, pre_padding=prepad, + post_padding=postpad) + sink = blocks.vector_sink_f() + self.tb.connect(source, shaper, sink) + self.tb.run () + + # checks + self.assertFloatTuplesAlmostEqual(sink.data(), expected, 6) + self.assertTrue(compare_tags(sink.tags()[0], etag)) + + def test_cc (self): + prepad = 10 + postpad = 10 + length = 20 + data = np.ones(length + 10, + dtype=complex) # need 10 more to push things through + window = np.concatenate((-2.0*np.ones(5, dtype=complex), + -4.0*np.ones(5, dtype=complex))) + tags = (make_length_tag(0, length),) + expected = np.concatenate((np.zeros(prepad, dtype=complex), window[0:5], + np.ones(length - len(window), dtype=complex), + window[5:10], np.zeros(postpad, + dtype=complex))) + etag = make_length_tag(0, length + prepad + postpad) + + # flowgraph + source = blocks.vector_source_c(data, tags=tags) + shaper = digital.burst_shaper_cc(window, pre_padding=prepad, + post_padding=postpad) + sink = blocks.vector_sink_c() + self.tb.connect(source, shaper, sink) + self.tb.run () + + # checks + self.assertComplexTuplesAlmostEqual(sink.data(), expected, 6) + self.assertTrue(compare_tags(sink.tags()[0], etag)) + + def test_ff_with_phasing (self): + prepad = 10 + postpad = 10 + length = 20 + data = np.ones(length + 10) # need 10 more to push things through + window = np.concatenate((-2.0*np.ones(5), -4.0*np.ones(5))) + tags = (make_length_tag(0, length),) + phasing = np.zeros(5) + for i in xrange(5): + phasing[i] = ((-1.0)**i) + expected = np.concatenate((np.zeros(prepad), phasing*window[0:5], + np.ones(length), phasing*window[5:10], + np.zeros(postpad))) + etag = make_length_tag(0, length + prepad + postpad + len(window)) + + # flowgraph + source = blocks.vector_source_f(data, tags=tags) + shaper = digital.burst_shaper_ff(window, pre_padding=prepad, + post_padding=postpad, + insert_phasing=True) + sink = blocks.vector_sink_f() + self.tb.connect(source, shaper, sink) + self.tb.run () + + # checks + self.assertFloatTuplesAlmostEqual(sink.data(), expected, 6) + self.assertTrue(compare_tags(sink.tags()[0], etag)) + + def test_cc_with_phasing (self): + prepad = 10 + postpad = 10 + length = 20 + data = np.ones(length + 10, + dtype=complex) # need 10 more to push things through + window = np.concatenate((-2.0*np.ones(5, dtype=complex), + -4.0*np.ones(5, dtype=complex))) + tags = (make_length_tag(0, length),) + phasing = np.zeros(5, dtype=complex) + for i in xrange(5): + phasing[i] = complex((-1.0)**i) + expected = np.concatenate((np.zeros(prepad, dtype=complex), + phasing*window[0:5], + np.ones(length, dtype=complex), + phasing*window[5:10], + np.zeros(postpad, dtype=complex))) + etag = make_length_tag(0, length + prepad + postpad + len(window)) + + # flowgraph + source = blocks.vector_source_c(data, tags=tags) + shaper = digital.burst_shaper_cc(window, pre_padding=prepad, + post_padding=postpad, + insert_phasing=True) + sink = blocks.vector_sink_c() + self.tb.connect(source, shaper, sink) + self.tb.run () + + # checks + self.assertComplexTuplesAlmostEqual(sink.data(), expected, 6) + self.assertTrue(compare_tags(sink.tags()[0], etag)) + + def test_odd_window (self): + prepad = 10 + postpad = 10 + length = 20 + data = np.ones(length + 10) # need 10 more to push things through + window = np.concatenate((-2.0*np.ones(5), -3.0*np.ones(1), + -4.0*np.ones(5))) + tags = (make_length_tag(0, length),) + expected = np.concatenate((np.zeros(prepad), window[0:6], + np.ones(length - len(window) - 1), + window[5:11], np.zeros(postpad))) + etag = make_length_tag(0, length + prepad + postpad) + + # flowgraph + source = blocks.vector_source_f(data, tags=tags) + shaper = digital.burst_shaper_ff(window, pre_padding=prepad, + post_padding=postpad) + sink = blocks.vector_sink_f() + self.tb.connect(source, shaper, sink) + self.tb.run () + + # checks + self.assertFloatTuplesAlmostEqual(sink.data(), expected, 6) + self.assertTrue(compare_tags(sink.tags()[0], etag)) + + def test_short_burst (self): + prepad = 10 + postpad = 10 + length = 9 + data = np.ones(length + 10) # need 10 more to push things through + window = np.concatenate((-2.0*np.ones(5), -3.0*np.ones(1), + -4.0*np.ones(5))) + tags = (make_length_tag(0, length),) + expected = np.concatenate((np.zeros(prepad), window[0:4], + np.ones(1), window[5:9], + np.zeros(postpad))) + etag = make_length_tag(0, length + prepad + postpad) + + # flowgraph + source = blocks.vector_source_f(data, tags=tags) + shaper = digital.burst_shaper_ff(window, pre_padding=prepad, + post_padding=postpad) + sink = blocks.vector_sink_f() + self.tb.connect(source, shaper, sink) + self.tb.run () + + # checks + self.assertFloatTuplesAlmostEqual(sink.data(), expected, 6) + self.assertTrue(compare_tags(sink.tags()[0], etag)) + + def test_consecutive_bursts (self): + prepad = 10 + postpad = 10 + length1 = 15 + length2 = 25 + data = np.concatenate((np.ones(length1), -1.0*np.ones(length2), + np.zeros(10))) # need 10 more to push things through + window = np.concatenate((-2.0*np.ones(5), -4.0*np.ones(5))) + tags = (make_length_tag(0, length1), make_length_tag(length1, length2)) + expected = np.concatenate((np.zeros(prepad), window[0:5], + np.ones(length1 - len(window)), window[5:10], + np.zeros(postpad + prepad), -1.0*window[0:5], + -1.0*np.ones(length2 - len(window)), + -1.0*window[5:10], np.zeros(postpad))) + etags = (make_length_tag(0, length1 + prepad + postpad), + make_length_tag(length1 + prepad + postpad, + length2 + prepad + postpad)) + + # flowgraph + source = blocks.vector_source_f(data, tags=tags) + shaper = digital.burst_shaper_ff(window, pre_padding=prepad, + post_padding=postpad) + sink = blocks.vector_sink_f() + self.tb.connect(source, shaper, sink) + self.tb.run () + + # checks + self.assertFloatTuplesAlmostEqual(sink.data(), expected, 6) + for i in xrange(len(etags)): + self.assertTrue(compare_tags(sink.tags()[i], etags[i])) + + def test_tag_gap (self): + prepad = 10 + postpad = 10 + length = 20 + gap_len = 5 + data = np.arange(2*length + 10, + dtype=float) # need 10 more to push things through + window = np.concatenate((-2.0*np.ones(5), -4.0*np.ones(5))) + ewindow = window * np.array([1,-1,1,-1,1,1,-1,1,-1,1],dtype=float) + tags = (make_length_tag(0, length), + make_length_tag(length + gap_len, length)) + expected = np.concatenate((np.zeros(prepad), ewindow[0:5], + np.arange(0, length, dtype=float), + ewindow[5:10], np.zeros(postpad), + np.zeros(prepad), ewindow[0:5], + np.arange(length + gap_len, + 2*length + gap_len, dtype=float), + ewindow[5:10], np.zeros(postpad))) + burst_len = length + len(window) + prepad + postpad + etags = (make_length_tag(0, burst_len), + make_length_tag(burst_len, burst_len)) + + # flowgraph + source = blocks.vector_source_f(data, tags=tags) + shaper = digital.burst_shaper_ff(window, pre_padding=prepad, + post_padding=postpad, + insert_phasing=True) + sink = blocks.vector_sink_f() + self.tb.connect(source, shaper, sink) + self.tb.run () + + # checks + self.assertFloatTuplesAlmostEqual(sink.data(), expected, 6) + for i in xrange(len(etags)): + self.assertTrue(compare_tags(sink.tags()[i], etags[i])) + + def test_tag_propagation (self): + prepad = 10 + postpad = 10 + length1 = 15 + length2 = 25 + gap_len = 5 + lentag1_offset = 0 + lentag2_offset = length1 + gap_len + tag1_offset = 0 # accompanies first length tag + tag2_offset = length1 + gap_len # accompanies second length tag + tag3_offset = 2 # in ramp-up state + tag4_offset = length1 + 2 # in gap; tag will be dropped + tag5_offset = length1 + gap_len + 7 # in copy state + + data = np.concatenate((np.ones(length1), np.zeros(gap_len), + -1.0*np.ones(length2), np.zeros(10))) + window = np.concatenate((-2.0*np.ones(5), -4.0*np.ones(5))) + tags = (make_length_tag(lentag1_offset, length1), + make_length_tag(lentag2_offset, length2), + make_tag(tag1_offset, 'head', pmt.intern('tag1')), + make_tag(tag2_offset, 'head', pmt.intern('tag2')), + make_tag(tag3_offset, 'body', pmt.intern('tag3')), + make_tag(tag4_offset, 'body', pmt.intern('tag4')), + make_tag(tag5_offset, 'body', pmt.intern('tag5'))) + expected = np.concatenate((np.zeros(prepad), window[0:5], + np.ones(length1 - len(window)), window[5:10], + np.zeros(postpad + prepad), -1.0*window[0:5], + -1.0*np.ones(length2 - len(window)), + -1.0*window[5:10], np.zeros(postpad))) + elentag1_offset = 0 + elentag2_offset = length1 + prepad + postpad + etag1_offset = 0 + etag2_offset = elentag2_offset + etag3_offset = prepad + tag3_offset + etag5_offset = 2*prepad + postpad + tag5_offset - gap_len + etags = (make_length_tag(elentag1_offset, length1 + prepad + postpad), + make_length_tag(elentag2_offset, length2 + prepad + postpad), + make_tag(etag1_offset, 'head', pmt.intern('tag1')), + make_tag(etag2_offset, 'head', pmt.intern('tag2')), + make_tag(etag3_offset, 'body', pmt.intern('tag3')), + make_tag(etag5_offset, 'body', pmt.intern('tag5'))) + + # flowgraph + source = blocks.vector_source_f(data, tags=tags) + shaper = digital.burst_shaper_ff(window, pre_padding=prepad, + post_padding=postpad) + sink = blocks.vector_sink_f() + self.tb.connect(source, shaper, sink) + self.tb.run () + + # checks + self.assertFloatTuplesAlmostEqual(sink.data(), expected, 6) + for x, y in zip(sorted(sink.tags(), key=gr.tag_t_offset_compare_key()), + sorted(etags, key=gr.tag_t_offset_compare_key())): + self.assertTrue(compare_tags(x, y)) + + +if __name__ == '__main__': + gr_unittest.run(qa_burst_shaper, "qa_burst_shaper.xml") diff --git a/gr-digital/swig/digital_swig.i b/gr-digital/swig/digital_swig.i index 43f68a3d6e..d7a3b81c15 100644 --- a/gr-digital/swig/digital_swig.i +++ b/gr-digital/swig/digital_swig.i @@ -1,5 +1,5 @@ /* - * Copyright 2011,2012 Free Software Foundation, Inc. + * Copyright 2011-2015 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -39,6 +39,8 @@ %{ #include "gnuradio/digital/additive_scrambler_bb.h" #include "gnuradio/digital/binary_slicer_fb.h" +#include "gnuradio/digital/burst_shaper_cc.h" +#include "gnuradio/digital/burst_shaper_ff.h" #include "gnuradio/digital/chunks_to_symbols_bc.h" #include "gnuradio/digital/chunks_to_symbols_bf.h" #include "gnuradio/digital/chunks_to_symbols_ic.h" @@ -52,6 +54,7 @@ #include "gnuradio/digital/constellation_decoder_cb.h" #include "gnuradio/digital/constellation_receiver_cb.h" #include "gnuradio/digital/constellation_soft_decoder_cf.h" +#include "gnuradio/digital/corr_est_cc.h" #include "gnuradio/digital/correlate_access_code_bb.h" #include "gnuradio/digital/correlate_access_code_tag_bb.h" #include "gnuradio/digital/correlate_access_code_bb_ts.h" @@ -78,9 +81,11 @@ #include "gnuradio/digital/lms_dd_equalizer_cc.h" #include "gnuradio/digital/map_bb.h" #include "gnuradio/digital/metric_type.h" +#include "gnuradio/digital/modulate_vector.h" #include "gnuradio/digital/mpsk_receiver_cc.h" #include "gnuradio/digital/mpsk_snr_est.h" #include "gnuradio/digital/mpsk_snr_est_cc.h" +#include "gnuradio/digital/msk_timing_recovery_cc.h" #include "gnuradio/digital/ofdm_carrier_allocator_cvc.h" #include "gnuradio/digital/ofdm_chanest_vcvc.h" #include "gnuradio/digital/ofdm_cyclic_prefixer.h" @@ -115,6 +120,8 @@ %include "gnuradio/digital/additive_scrambler_bb.h" %include "gnuradio/digital/binary_slicer_fb.h" +%include "gnuradio/digital/burst_shaper_cc.h" +%include "gnuradio/digital/burst_shaper_ff.h" %include "gnuradio/digital/chunks_to_symbols_bc.h" %include "gnuradio/digital/chunks_to_symbols_bf.h" %include "gnuradio/digital/chunks_to_symbols_ic.h" @@ -128,6 +135,7 @@ %include "gnuradio/digital/constellation_decoder_cb.h" %include "gnuradio/digital/constellation_receiver_cb.h" %include "gnuradio/digital/constellation_soft_decoder_cf.h" +%include "gnuradio/digital/corr_est_cc.h" %include "gnuradio/digital/correlate_access_code_bb.h" %include "gnuradio/digital/correlate_access_code_tag_bb.h" %include "gnuradio/digital/correlate_access_code_bb_ts.h" @@ -154,9 +162,11 @@ %include "gnuradio/digital/lms_dd_equalizer_cc.h" %include "gnuradio/digital/map_bb.h" %include "gnuradio/digital/metric_type.h" +%include "gnuradio/digital/modulate_vector.h" %include "gnuradio/digital/mpsk_receiver_cc.h" %include "gnuradio/digital/mpsk_snr_est.h" %include "gnuradio/digital/mpsk_snr_est_cc.h" +%include "gnuradio/digital/msk_timing_recovery_cc.h" %include "gnuradio/digital/ofdm_carrier_allocator_cvc.h" %include "gnuradio/digital/ofdm_chanest_vcvc.h" %include "gnuradio/digital/ofdm_cyclic_prefixer.h" @@ -187,6 +197,8 @@ GR_SWIG_BLOCK_MAGIC2(digital, additive_scrambler_bb); GR_SWIG_BLOCK_MAGIC2(digital, binary_slicer_fb); +GR_SWIG_BLOCK_MAGIC2(digital, burst_shaper_cc); +GR_SWIG_BLOCK_MAGIC2(digital, burst_shaper_ff); GR_SWIG_BLOCK_MAGIC2(digital, chunks_to_symbols_bc); GR_SWIG_BLOCK_MAGIC2(digital, chunks_to_symbols_bf); GR_SWIG_BLOCK_MAGIC2(digital, chunks_to_symbols_ic); @@ -199,6 +211,7 @@ GR_SWIG_BLOCK_MAGIC2(digital, cma_equalizer_cc); GR_SWIG_BLOCK_MAGIC2(digital, constellation_decoder_cb); GR_SWIG_BLOCK_MAGIC2(digital, constellation_receiver_cb); GR_SWIG_BLOCK_MAGIC2(digital, constellation_soft_decoder_cf); +GR_SWIG_BLOCK_MAGIC2(digital, corr_est_cc); GR_SWIG_BLOCK_MAGIC2(digital, correlate_access_code_bb); GR_SWIG_BLOCK_MAGIC2(digital, correlate_access_code_tag_bb); GR_SWIG_BLOCK_MAGIC2(digital, correlate_access_code_bb_ts); @@ -224,6 +237,7 @@ GR_SWIG_BLOCK_MAGIC2(digital, lms_dd_equalizer_cc); GR_SWIG_BLOCK_MAGIC2(digital, map_bb); GR_SWIG_BLOCK_MAGIC2(digital, mpsk_receiver_cc); GR_SWIG_BLOCK_MAGIC2(digital, mpsk_snr_est_cc); +GR_SWIG_BLOCK_MAGIC2(digital, msk_timing_recovery_cc); GR_SWIG_BLOCK_MAGIC2(digital, ofdm_carrier_allocator_cvc); GR_SWIG_BLOCK_MAGIC2(digital, ofdm_chanest_vcvc); GR_SWIG_BLOCK_MAGIC2(digital, ofdm_cyclic_prefixer); diff --git a/gr-filter/examples/CMakeLists.txt b/gr-filter/examples/CMakeLists.txt index 48b59ef099..a6229f38f4 100644 --- a/gr-filter/examples/CMakeLists.txt +++ b/gr-filter/examples/CMakeLists.txt @@ -42,6 +42,7 @@ GR_PYTHON_INSTALL(PROGRAMS install( FILES + filter_taps.grc resampler_demo.grc DESTINATION ${GR_PKG_FILTER_EXAMPLES_DIR} COMPONENT "filter_python" diff --git a/gr-filter/examples/filter_taps.grc b/gr-filter/examples/filter_taps.grc new file mode 100644 index 0000000000..48d37b228a --- /dev/null +++ b/gr-filter/examples/filter_taps.grc @@ -0,0 +1,1299 @@ +<?xml version='1.0' encoding='ASCII'?> +<?grc format='1' created='3.7.7'?> +<flow_graph> + <timestamp>Wed Apr 8 11:00:48 2015</timestamp> + <block> + <key>options</key> + <param> + <key>id</key> + <value>filter_taps</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>title</key> + <value></value> + </param> + <param> + <key>author</key> + <value></value> + </param> + <param> + <key>description</key> + <value></value> + </param> + <param> + <key>window_size</key> + <value>1280, 1024</value> + </param> + <param> + <key>generate_options</key> + <value>qt_gui</value> + </param> + <param> + <key>category</key> + <value>Custom</value> + </param> + <param> + <key>run_options</key> + <value>prompt</value> + </param> + <param> + <key>run</key> + <value>True</value> + </param> + <param> + <key>max_nouts</key> + <value>0</value> + </param> + <param> + <key>realtime_scheduling</key> + <value></value> + </param> + <param> + <key>thread_safe_setters</key> + <value></value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(10, 10)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>sym_rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>samp_rate/sps</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(1080, 171)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>sps</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>2</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(1016, 171)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>bp_low</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>6000</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(120, 139)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>cutoff_high</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>14000</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(8, 211)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>samp_rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>32000</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(8, 83)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>transition</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>1000</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(8, 283)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>cutoff_low</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>2000</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(8, 147)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>bp_high</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>10000</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(120, 211)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>len_taps</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>len(lp_taps)</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(288, 35)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_band_pass_filter_taps</key> + <param> + <key>id</key> + <value>bp_taps</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>taps_real</value> + </param> + <param> + <key>gain</key> + <value>1.0</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>low_cutoff_freq</key> + <value>bp_low</value> + </param> + <param> + <key>high_cutoff_freq</key> + <value>bp_high</value> + </param> + <param> + <key>width</key> + <value>transition</value> + </param> + <param> + <key>win</key> + <value>firdes.WIN_HAMMING</value> + </param> + <param> + <key>beta</key> + <value>6.76</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(696, 35)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_high_pass_filter_taps</key> + <param> + <key>id</key> + <value>hp_taps</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>gain</key> + <value>1.0</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>cutoff_freq</key> + <value>cutoff_low</value> + </param> + <param> + <key>width</key> + <value>transition</value> + </param> + <param> + <key>win</key> + <value>firdes.WIN_HAMMING</value> + </param> + <param> + <key>beta</key> + <value>6.76</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(536, 35)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_low_pass_filter_taps</key> + <param> + <key>id</key> + <value>lp_taps</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>gain</key> + <value>1.0</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>cutoff_freq</key> + <value>cutoff_high</value> + </param> + <param> + <key>width</key> + <value>transition</value> + </param> + <param> + <key>win</key> + <value>firdes.WIN_HAMMING</value> + </param> + <param> + <key>beta</key> + <value>6.76</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(376, 35)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_band_reject_filter_taps</key> + <param> + <key>id</key> + <value>br_taps</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>gain</key> + <value>1.0</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>low_cutoff_freq</key> + <value>bp_low</value> + </param> + <param> + <key>high_cutoff_freq</key> + <value>bp_high</value> + </param> + <param> + <key>width</key> + <value>transition</value> + </param> + <param> + <key>win</key> + <value>firdes.WIN_HAMMING</value> + </param> + <param> + <key>beta</key> + <value>6.76</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(856, 35)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>fir_filter_xxx</key> + <param> + <key>id</key> + <value>lp_filter</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>fff</value> + </param> + <param> + <key>decim</key> + <value>1</value> + </param> + <param> + <key>taps</key> + <value>lp_taps</value> + </param> + <param> + <key>samp_delay</key> + <value>0</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value>Low-pass filter</value> + </param> + <param> + <key>_coordinate</key> + <value>(392, 203)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>fir_filter_xxx</key> + <param> + <key>id</key> + <value>hp_filter</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>fff</value> + </param> + <param> + <key>decim</key> + <value>1</value> + </param> + <param> + <key>taps</key> + <value>hp_taps</value> + </param> + <param> + <key>samp_delay</key> + <value>0</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value>High-pass filter</value> + </param> + <param> + <key>_coordinate</key> + <value>(392, 307)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>fir_filter_xxx</key> + <param> + <key>id</key> + <value>bp_filter</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>fff</value> + </param> + <param> + <key>decim</key> + <value>1</value> + </param> + <param> + <key>taps</key> + <value>bp_taps</value> + </param> + <param> + <key>samp_delay</key> + <value>0</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value>Band-pass filter</value> + </param> + <param> + <key>_coordinate</key> + <value>(392, 403)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>fir_filter_xxx</key> + <param> + <key>id</key> + <value>br_filter</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>fff</value> + </param> + <param> + <key>decim</key> + <value>1</value> + </param> + <param> + <key>taps</key> + <value>br_taps</value> + </param> + <param> + <key>samp_delay</key> + <value>0</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value>Band-reject filter</value> + </param> + <param> + <key>_coordinate</key> + <value>(392, 499)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_rrc_filter_taps</key> + <param> + <key>id</key> + <value>rrc_taps</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>gain</key> + <value>1.0</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>sym_rate</key> + <value>sym_rate</value> + </param> + <param> + <key>alpha</key> + <value>0.35</value> + </param> + <param> + <key>ntaps</key> + <value>11*sps</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(1016, 35)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>analog_fastnoise_source_x</key> + <param> + <key>id</key> + <value>analog_fastnoise_source_x_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>noise_type</key> + <value>analog.GR_GAUSSIAN</value> + </param> + <param> + <key>amp</key> + <value>1</value> + </param> + <param> + <key>seed</key> + <value>0</value> + </param> + <param> + <key>samples</key> + <value>8192</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(176, 387)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>fir_filter_xxx</key> + <param> + <key>id</key> + <value>rrc_filter</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>fff</value> + </param> + <param> + <key>decim</key> + <value>1</value> + </param> + <param> + <key>taps</key> + <value>rrc_taps</value> + </param> + <param> + <key>samp_delay</key> + <value>0</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value>RRC filter</value> + </param> + <param> + <key>_coordinate</key> + <value>(392, 587)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>qtgui_freq_sink_x</key> + <param> + <key>id</key> + <value>qtgui_freq_sink_x_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>name</key> + <value>""</value> + </param> + <param> + <key>fftsize</key> + <value>4096</value> + </param> + <param> + <key>freqhalf</key> + <value>False</value> + </param> + <param> + <key>wintype</key> + <value>firdes.WIN_BLACKMAN_hARRIS</value> + </param> + <param> + <key>fc</key> + <value>0</value> + </param> + <param> + <key>bw</key> + <value>samp_rate</value> + </param> + <param> + <key>grid</key> + <value>True</value> + </param> + <param> + <key>autoscale</key> + <value>False</value> + </param> + <param> + <key>average</key> + <value>0.2</value> + </param> + <param> + <key>ymin</key> + <value>-140</value> + </param> + <param> + <key>ymax</key> + <value>10</value> + </param> + <param> + <key>nconnections</key> + <value>5</value> + </param> + <param> + <key>update_time</key> + <value>0.10</value> + </param> + <param> + <key>gui_hint</key> + <value></value> + </param> + <param> + <key>showports</key> + <value>True</value> + </param> + <param> + <key>tr_mode</key> + <value>qtgui.TRIG_MODE_FREE</value> + </param> + <param> + <key>tr_level</key> + <value>0.0</value> + </param> + <param> + <key>tr_chan</key> + <value>0</value> + </param> + <param> + <key>tr_tag</key> + <value>""</value> + </param> + <param> + <key>ctrlpanel</key> + <value>True</value> + </param> + <param> + <key>legend</key> + <value>True</value> + </param> + <param> + <key>label1</key> + <value>Low-pass</value> + </param> + <param> + <key>width1</key> + <value>1</value> + </param> + <param> + <key>color1</key> + <value>"blue"</value> + </param> + <param> + <key>alpha1</key> + <value>0.9</value> + </param> + <param> + <key>label2</key> + <value>High-pass</value> + </param> + <param> + <key>width2</key> + <value>1</value> + </param> + <param> + <key>color2</key> + <value>"red"</value> + </param> + <param> + <key>alpha2</key> + <value>0.9</value> + </param> + <param> + <key>label3</key> + <value>Band-pass</value> + </param> + <param> + <key>width3</key> + <value>1</value> + </param> + <param> + <key>color3</key> + <value>"green"</value> + </param> + <param> + <key>alpha3</key> + <value>0.9</value> + </param> + <param> + <key>label4</key> + <value>Band-reject</value> + </param> + <param> + <key>width4</key> + <value>1</value> + </param> + <param> + <key>color4</key> + <value>"cyan"</value> + </param> + <param> + <key>alpha4</key> + <value>0.9</value> + </param> + <param> + <key>label5</key> + <value>RRC</value> + </param> + <param> + <key>width5</key> + <value>1</value> + </param> + <param> + <key>color5</key> + <value>"magenta"</value> + </param> + <param> + <key>alpha5</key> + <value>0.9</value> + </param> + <param> + <key>label6</key> + <value></value> + </param> + <param> + <key>width6</key> + <value>1</value> + </param> + <param> + <key>color6</key> + <value>"magenta"</value> + </param> + <param> + <key>alpha6</key> + <value>1.0</value> + </param> + <param> + <key>label7</key> + <value></value> + </param> + <param> + <key>width7</key> + <value>1</value> + </param> + <param> + <key>color7</key> + <value>"yellow"</value> + </param> + <param> + <key>alpha7</key> + <value>1.0</value> + </param> + <param> + <key>label8</key> + <value></value> + </param> + <param> + <key>width8</key> + <value>1</value> + </param> + <param> + <key>color8</key> + <value>"dark red"</value> + </param> + <param> + <key>alpha8</key> + <value>1.0</value> + </param> + <param> + <key>label9</key> + <value></value> + </param> + <param> + <key>width9</key> + <value>1</value> + </param> + <param> + <key>color9</key> + <value>"dark green"</value> + </param> + <param> + <key>alpha9</key> + <value>1.0</value> + </param> + <param> + <key>label10</key> + <value></value> + </param> + <param> + <key>width10</key> + <value>1</value> + </param> + <param> + <key>color10</key> + <value>"dark blue"</value> + </param> + <param> + <key>alpha10</key> + <value>1.0</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(728, 416)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <connection> + <source_block_id>bp_filter</source_block_id> + <sink_block_id>qtgui_freq_sink_x_0</sink_block_id> + <source_key>0</source_key> + <sink_key>2</sink_key> + </connection> + <connection> + <source_block_id>analog_fastnoise_source_x_0</source_block_id> + <sink_block_id>bp_filter</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>hp_filter</source_block_id> + <sink_block_id>qtgui_freq_sink_x_0</sink_block_id> + <source_key>0</source_key> + <sink_key>1</sink_key> + </connection> + <connection> + <source_block_id>analog_fastnoise_source_x_0</source_block_id> + <sink_block_id>hp_filter</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>lp_filter</source_block_id> + <sink_block_id>qtgui_freq_sink_x_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>analog_fastnoise_source_x_0</source_block_id> + <sink_block_id>lp_filter</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>analog_fastnoise_source_x_0</source_block_id> + <sink_block_id>br_filter</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>br_filter</source_block_id> + <sink_block_id>qtgui_freq_sink_x_0</sink_block_id> + <source_key>0</source_key> + <sink_key>3</sink_key> + </connection> + <connection> + <source_block_id>rrc_filter</source_block_id> + <sink_block_id>qtgui_freq_sink_x_0</sink_block_id> + <source_key>0</source_key> + <sink_key>4</sink_key> + </connection> + <connection> + <source_block_id>analog_fastnoise_source_x_0</source_block_id> + <sink_block_id>rrc_filter</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> +</flow_graph> diff --git a/gr-filter/grc/CMakeLists.txt b/gr-filter/grc/CMakeLists.txt index 575f6675d6..b3bc2e1828 100644 --- a/gr-filter/grc/CMakeLists.txt +++ b/gr-filter/grc/CMakeLists.txt @@ -45,6 +45,11 @@ install(FILES filter_band_pass_filter.xml filter_band_reject_filter.xml filter_root_raised_cosine_filter.xml + variable_low_pass_filter_taps.xml + variable_high_pass_filter_taps.xml + variable_band_pass_filter_taps.xml + variable_band_reject_filter_taps.xml + variable_rrc_filter_taps.xml DESTINATION ${GRC_BLOCKS_DIR} COMPONENT "filter_python" ) diff --git a/gr-filter/grc/filter_block_tree.xml b/gr-filter/grc/filter_block_tree.xml index fbba9daf09..ea869a75bd 100644 --- a/gr-filter/grc/filter_block_tree.xml +++ b/gr-filter/grc/filter_block_tree.xml @@ -1,7 +1,7 @@ <?xml version="1.0"?> <!-- - Copyright 2012 Free Software Foundation, Inc. + Copyright 2012,2015 Free Software Foundation, Inc. This file is part of GNU Radio @@ -30,6 +30,12 @@ <name></name> <!-- Blank for Root Name --> <cat> <name>Filters</name> + <!-- FIR filter tap generators --> + <block>variable_low_pass_filter_taps</block> + <block>variable_high_pass_filter_taps</block> + <block>variable_band_pass_filter_taps</block> + <block>variable_band_reject_filter_taps</block> + <block>variable_rrc_filter_taps</block> <!-- FIR convenience filters --> <block>low_pass_filter</block> <block>high_pass_filter</block> diff --git a/gr-filter/grc/variable_band_pass_filter_taps.xml b/gr-filter/grc/variable_band_pass_filter_taps.xml new file mode 100644 index 0000000000..6cdfcc0373 --- /dev/null +++ b/gr-filter/grc/variable_band_pass_filter_taps.xml @@ -0,0 +1,97 @@ +<?xml version="1.0"?> +<!-- +####################################################### +# Convenience wrapper for calling firdes.band_pass(...) +# or firdes.complex_bandpass(...) +####################################################### + --> +<block> + <name>Band-pass Filter Taps</name> + <key>variable_band_pass_filter_taps</key> + <import>from gnuradio.filter import firdes</import> + <var_make> +self.$(id) = $(id) = firdes.$(type.fcn)($gain, $samp_rate, $low_cutoff_freq, $high_cutoff_freq, $width, $win, $beta) + </var_make> + <var_value>firdes.$(type.fcn)($gain, $samp_rate, $low_cutoff_freq, $high_cutoff_freq, $width, $win, $beta)</var_value> + <make></make> + + <param> + <name>Tap Type</name> + <key>type</key> + <type>enum</type> + <option> + <name>Real</name> + <key>taps_real</key> + <opt>fcn:band_pass</opt> + </option> + <option> + <name>Complex</name> + <key>taps_complex</key> + <opt>fcn:complex_band_pass</opt> + </option> + </param> + <param> + <name>Gain</name> + <key>gain</key> + <value>1.0</value> + <type>float</type> + </param> + <param> + <name>Sample Rate (Hz)</name> + <key>samp_rate</key> + <value>samp_rate</value> + <type>float</type> + </param> + <param> + <name>Low Cutoff Freq (Hz)</name> + <key>low_cutoff_freq</key> + <type>float</type> + </param> + <param> + <name>High Cutoff Freq (Hz)</name> + <key>high_cutoff_freq</key> + <type>float</type> + </param> + <param> + <name>Transition Width (Hz)</name> + <key>width</key> + <type>float</type> + </param> + <param> + <name>Window</name> + <key>win</key> + <value>firdes.WIN_HAMMING</value> + <type>int</type> + <option> + <name>Hamming</name> + <key>firdes.WIN_HAMMING</key> + </option> + <option> + <name>Hann</name> + <key>firdes.WIN_HANN</key> + </option> + <option> + <name>Blackman</name> + <key>firdes.WIN_BLACKMAN</key> + </option> + <option> + <name>Rectangular</name> + <key>firdes.WIN_RECTANGULAR</key> + </option> + <option> + <name>Kaiser</name> + <key>firdes.WIN_KAISER</key> + </option> + </param> + <param> + <name>Beta</name> + <key>beta</key> + <value>6.76</value> + <type>float</type> + </param> + <doc> + This is a convenience wrapper for calling firdes.band_pass() or firdes.complex_band_pass() + + The beta paramater only applies to the Kaiser window. + </doc> +</block> diff --git a/gr-filter/grc/variable_band_reject_filter_taps.xml b/gr-filter/grc/variable_band_reject_filter_taps.xml new file mode 100644 index 0000000000..fbf25886d3 --- /dev/null +++ b/gr-filter/grc/variable_band_reject_filter_taps.xml @@ -0,0 +1,81 @@ +<?xml version="1.0"?> +<!-- +######################################################### +# Convenience wrapper for calling firdes.band_reject(...) +######################################################### + --> +<block> + <name>Band-reject Filter Taps</name> + <key>variable_band_reject_filter_taps</key> + <import>from gnuradio.filter import firdes</import> + <var_make> +self.$(id) = $(id) = firdes.band_reject($gain, $samp_rate, $low_cutoff_freq, $high_cutoff_freq, $width, $win, $beta) + </var_make> + <var_value>firdes.band_reject($gain, $samp_rate, $low_cutoff_freq, $high_cutoff_freq, $width, $win, $beta)</var_value> + <make></make> + + <param> + <name>Gain</name> + <key>gain</key> + <value>1.0</value> + <type>float</type> + </param> + <param> + <name>Sample Rate (Hz)</name> + <key>samp_rate</key> + <value>samp_rate</value> + <type>float</type> + </param> + <param> + <name>Low Cutoff Freq (Hz)</name> + <key>low_cutoff_freq</key> + <type>float</type> + </param> + <param> + <name>High Cutoff Freq (Hz)</name> + <key>high_cutoff_freq</key> + <type>float</type> + </param> + <param> + <name>Transition Width (Hz)</name> + <key>width</key> + <type>float</type> + </param> + <param> + <name>Window</name> + <key>win</key> + <value>firdes.WIN_HAMMING</value> + <type>int</type> + <option> + <name>Hamming</name> + <key>firdes.WIN_HAMMING</key> + </option> + <option> + <name>Hann</name> + <key>firdes.WIN_HANN</key> + </option> + <option> + <name>Blackman</name> + <key>firdes.WIN_BLACKMAN</key> + </option> + <option> + <name>Rectangular</name> + <key>firdes.WIN_RECTANGULAR</key> + </option> + <option> + <name>Kaiser</name> + <key>firdes.WIN_KAISER</key> + </option> + </param> + <param> + <name>Beta</name> + <key>beta</key> + <value>6.76</value> + <type>float</type> + </param> + <doc> + This is a convenience wrapper for calling firdes.band_reject(). + + The beta paramater only applies to the Kaiser window. + </doc> +</block> diff --git a/gr-filter/grc/variable_high_pass_filter_taps.xml b/gr-filter/grc/variable_high_pass_filter_taps.xml new file mode 100644 index 0000000000..08768d808a --- /dev/null +++ b/gr-filter/grc/variable_high_pass_filter_taps.xml @@ -0,0 +1,75 @@ +<?xml version="1.0"?> +<!-- +###################################################### +## Convenience wrapper around call to fides.high_pass() +###################################################### + --> +<block> + <name>High-pass Filter Taps</name> + <key>variable_high_pass_filter_taps</key> + <import>from gnuradio.filter import firdes</import> + <var_make> +self.$(id) = $(id) = firdes.high_pass($gain, $samp_rate, $cutoff_freq, $width, $win, $beta) + </var_make> + <var_value>firdes.high_pass($gain, $samp_rate, $cutoff_freq, $width, $win, $beta)</var_value> + <make></make> + <param> + <name>Gain</name> + <key>gain</key> + <value>1.0</value> + <type>float</type> + </param> + <param> + <name>Sample Rate (Hz)</name> + <key>samp_rate</key> + <value>samp_rate</value> + <type>float</type> + </param> + <param> + <name>Cutoff Freq (Hz)</name> + <key>cutoff_freq</key> + <type>float</type> + </param> + <param> + <name>Transition Width (Hz)</name> + <key>width</key> + <type>float</type> + </param> + <param> + <name>Window</name> + <key>win</key> + <value>firdes.WIN_HAMMING</value> + <type>int</type> + <option> + <name>Hamming</name> + <key>firdes.WIN_HAMMING</key> + </option> + <option> + <name>Hann</name> + <key>firdes.WIN_HANN</key> + </option> + <option> + <name>Blackman</name> + <key>firdes.WIN_BLACKMAN</key> + </option> + <option> + <name>Rectangular</name> + <key>firdes.WIN_RECTANGULAR</key> + </option> + <option> + <name>Kaiser</name> + <key>firdes.WIN_KAISER</key> + </option> + </param> + <param> + <name>Beta</name> + <key>beta</key> + <value>6.76</value> + <type>float</type> + </param> + <doc> + This variable is a convenience wrapper around a call to firdes.high_pass(...). + + The beta paramater only applies to the Kaiser window. + </doc> +</block> diff --git a/gr-filter/grc/variable_low_pass_filter_taps.xml b/gr-filter/grc/variable_low_pass_filter_taps.xml new file mode 100644 index 0000000000..e3367b1ec5 --- /dev/null +++ b/gr-filter/grc/variable_low_pass_filter_taps.xml @@ -0,0 +1,75 @@ +<?xml version="1.0"?> +<!-- +###################################################### +## Convenience wrapper around call to fides.low_pass() +###################################################### + --> +<block> + <name>Low-pass Filter Taps</name> + <key>variable_low_pass_filter_taps</key> + <import>from gnuradio.filter import firdes</import> + <var_make> +self.$(id) = $(id) = firdes.low_pass($gain, $samp_rate, $cutoff_freq, $width, $win, $beta) + </var_make> + <var_value>firdes.low_pass($gain, $samp_rate, $cutoff_freq, $width, $win, $beta)</var_value> + <make></make> + <param> + <name>Gain</name> + <key>gain</key> + <value>1.0</value> + <type>float</type> + </param> + <param> + <name>Sample Rate (Hz)</name> + <key>samp_rate</key> + <value>samp_rate</value> + <type>float</type> + </param> + <param> + <name>Cutoff Freq (Hz)</name> + <key>cutoff_freq</key> + <type>float</type> + </param> + <param> + <name>Transition Width (Hz)</name> + <key>width</key> + <type>float</type> + </param> + <param> + <name>Window</name> + <key>win</key> + <value>firdes.WIN_HAMMING</value> + <type>int</type> + <option> + <name>Hamming</name> + <key>firdes.WIN_HAMMING</key> + </option> + <option> + <name>Hann</name> + <key>firdes.WIN_HANN</key> + </option> + <option> + <name>Blackman</name> + <key>firdes.WIN_BLACKMAN</key> + </option> + <option> + <name>Rectangular</name> + <key>firdes.WIN_RECTANGULAR</key> + </option> + <option> + <name>Kaiser</name> + <key>firdes.WIN_KAISER</key> + </option> + </param> + <param> + <name>Beta</name> + <key>beta</key> + <value>6.76</value> + <type>float</type> + </param> + <doc> + This variable is a convenience wrapper around a call to firdes.low_pass(...). + + The beta paramater only applies to the Kaiser window. + </doc> +</block> diff --git a/gr-filter/grc/variable_rrc_filter_taps.xml b/gr-filter/grc/variable_rrc_filter_taps.xml new file mode 100644 index 0000000000..2824ba75b2 --- /dev/null +++ b/gr-filter/grc/variable_rrc_filter_taps.xml @@ -0,0 +1,50 @@ +<?xml version="1.0"?> +<!-- +######################################################################## +## Convenience wrapper for calling firdes.root_raised_cosine_filter(...) +######################################################################## + --> +<block> + <name>RRC Filter Taps</name> + <key>variable_rrc_filter_taps</key> + <import>from gnuradio.filter import firdes</import> + <var_make> +self.$(id) = $(id) = firdes.root_raised_cosine($gain, $samp_rate, $sym_rate, $alpha, $ntaps) + </var_make> + <var_value>firdes.root_raised_cosine($gain, $samp_rate, $sym_rate, $alpha, $ntaps)</var_value> + <make></make> + + <param> + <name>Gain</name> + <key>gain</key> + <value>1.0</value> + <type>float</type> + </param> + <param> + <name>Sample Rate (Hz)</name> + <key>samp_rate</key> + <value>samp_rate</value> + <type>float</type> + </param> + <param> + <name>Symbol Rate (Hz)</name> + <key>sym_rate</key> + <value></value> + <type>float</type> + </param> + <param> + <name>Excess BW</name> + <key>alpha</key> + <value>0.35</value> + <type>float</type> + </param> + <param> + <name>Num Taps</name> + <key>ntaps</key> + <value>11*sps</value> + <type>int</type> + </param> + <doc> + This is a convenience wrapper for calling firdes.root_raised_cosine(...). + </doc> +</block> diff --git a/gr-qtgui/grc/qtgui_range.xml b/gr-qtgui/grc/qtgui_range.xml index 05f3ffce9f..71b614cc5e 100644 --- a/gr-qtgui/grc/qtgui_range.xml +++ b/gr-qtgui/grc/qtgui_range.xml @@ -6,105 +6,126 @@ ################################################### --> <block> - <name>QT GUI Range</name> - <key>variable_qtgui_range</key> - <import>from gnuradio.qtgui import Range, RangeWidget</import> - <var_make>self.$(id) = $(id) = $value</var_make> - <make>#set $win = 'self._%s_win'%$id + <name>QT GUI Range</name> + <key>variable_qtgui_range</key> + <import>from gnuradio.qtgui import Range, RangeWidget</import> + <var_make>self.$(id) = $(id) = $value</var_make> + <make>#set $win = 'self._%s_win'%$id #set $range = 'self._%s_range'%$id #if not $label() #set $label = '"%s"'%$id #end if $(range) = Range($start, $stop, $step, $value, $min_len) -$(win) = RangeWidget($range, self.set_$(id), $label, "$widget") +$(win) = RangeWidget($range, self.set_$(id), $label, "$widget", $rangeType) $(gui_hint()($win))</make> - <callback>self.set_$(id)($value)</callback> - <param> - <name>Label</name> - <key>label</key> - <value></value> - <type>string</type> - <hide>#if $label() then 'none' else 'part'#</hide> - </param> - <param> - <name>Default Value</name> - <key>value</key> - <value>50</value> - <type>real</type> - </param> - <param> - <name>Start</name> - <key>start</key> - <value>0</value> - <type>real</type> - </param> - <param> - <name>Stop</name> - <key>stop</key> - <value>100</value> - <type>real</type> - </param> - <param> - <name>Step</name> - <key>step</key> - <value>1</value> - <type>real</type> - </param> - <param> - <name>Widget</name> - <key>widget</key> - <value>counter_slider</value> - <type>enum</type> - <hide>part</hide> - <option><name>Counter + Slider</name><key>counter_slider</key></option> - <option><name>Counter</name><key>counter</key></option> - <option><name>Slider</name><key>slider</key></option> - <option><name>Knob</name><key>dial</key></option> - </param> - <param> - <name>Orientation</name> - <key>orient</key> - <value>Qt.Horizontal</value> - <type>enum</type> - <hide>#if $widget() == "slider" then 'part' else 'all'#</hide> - <option> - <name>Horizontal</name> - <key>Qt.Horizontal</key> - <opt>scalepos:BottomScale</opt> - <opt>minfcn:setMinimumWidth</opt> - </option> - <option> - <name>Vertical</name> - <key>Qt.Vertical</key> - <opt>scalepos:LeftScale</opt> - <opt>minfcn:setMinimumHeight</opt> - </option> - </param> - <param> - <name>Minimum Length</name> - <key>min_len</key> - <value>200</value> - <type>int</type> - <hide>part</hide> - </param> -<!-- from min_len <hide>#if $widget().split('_')[0] in ("slider", "counter") then 'part' else 'all'#</hide>--> - <param> - <name>GUI Hint</name> - <key>gui_hint</key> - <value></value> - <type>gui_hint</type> - <hide>part</hide> - </param> - <check>$start <= $value <= $stop</check> - <check>$start < $stop</check> - <doc> -This block creates a variable with a slider. \ -Leave the label blank to use the variable id as the label. \ -The value must be a real number. \ -The value must be between the start and the stop. + <callback>self.set_$(id)($value)</callback> -The GUI hint can be used to position the widget within the application. \ -The hint is of the form [tab_id@tab_index]: [row, col, row_span, col_span]. \ -Both the tab specification and the grid position are optional. - </doc> + <param> + <name>Label</name> + <key>label</key> + <value></value> + <type>string</type> + <hide>#if $label() then 'none' else 'part'#</hide> + </param> + + <param> + <name>Type</name> + <key>rangeType</key> + <value>"float"</value> + <type>enum</type> + <hide>part</hide> + <option><name>Float</name><key>float</key><opt>type:float</opt></option> + <option><name>Int</name><key>int</key><opt>type:int</opt></option> + </param> + + <param> + <name>Default Value</name> + <key>value</key> + <value>50</value> + <type>$rangeType.type</type> + </param> + + <param> + <name>Start</name> + <key>start</key> + <value>0</value> + <type>$rangeType.type</type> + </param> + + <param> + <name>Stop</name> + <key>stop</key> + <value>100</value> + <type>$rangeType.type</type> + </param> + + <param> + <name>Step</name> + <key>step</key> + <value>1</value> + <type>$rangeType.type</type> + </param> + + <param> + <name>Widget</name> + <key>widget</key> + <value>counter_slider</value> + <type>enum</type> + <hide>part</hide> + <option><name>Counter + Slider</name><key>counter_slider</key></option> + <option><name>Counter</name><key>counter</key></option> + <option><name>Slider</name><key>slider</key></option> + <option><name>Knob</name><key>dial</key></option> + </param> + + <param> + <name>Orientation</name> + <key>orient</key> + <value>Qt.Horizontal</value> + <type>enum</type> + <hide>#if $widget() == "slider" then 'part' else 'all'#</hide> + <option> + <name>Horizontal</name> + <key>Qt.Horizontal</key> + <opt>scalepos:BottomScale</opt> + <opt>minfcn:setMinimumWidth</opt> + </option> + <option> + <name>Vertical</name> + <key>Qt.Vertical</key> + <opt>scalepos:LeftScale</opt> + <opt>minfcn:setMinimumHeight</opt> + </option> + </param> + + <param> + <name>Minimum Length</name> + <key>min_len</key> + <value>200</value> + <type>int</type> + <hide>part</hide> + </param> + <!-- from min_len <hide>#if $widget().split('_')[0] in ("slider", "counter") then 'part' else 'all'#</hide>--> + + <param> + <name>GUI Hint</name> + <key>gui_hint</key> + <value></value> + <type>gui_hint</type> + <hide>part</hide> + </param> + + <check>$start <= $value <= $stop</check> + <check>$start < $stop</check> + + <doc> + This block creates a variable with a slider. \ + Leave the label blank to use the variable id as the label. \ + The value must be a real number. \ + The value must be between the start and the stop. + + The GUI hint can be used to position the widget within the application. \ + The hint is of the form [tab_id@tab_index]: [row, col, row_span, col_span]. \ + Both the tab specification and the grid position are optional. + </doc> </block> diff --git a/gr-qtgui/python/qtgui/range.py b/gr-qtgui/python/qtgui/range.py index 168e6662c3..3eafc9002e 100755 --- a/gr-qtgui/python/qtgui/range.py +++ b/gr-qtgui/python/qtgui/range.py @@ -64,13 +64,16 @@ class Range(object): class RangeWidget(QtGui.QWidget): - def __init__(self, ranges, slot, label, style): + def __init__(self, ranges, slot, label, style, rangeType=float): """ Creates the QT Range widget """ QtGui.QWidget.__init__(self) self.range = ranges self.style = style + # rangeType tells the block how to return the value as a standard + self.rangeType = rangeType + # Top-block function to call when any value changes # Some widgets call this directly when their value changes. # Others have intermediate functions to map the value into the right range. @@ -81,24 +84,26 @@ class RangeWidget(QtGui.QWidget): layout.addWidget(label) if style == "dial": - self.d_widget = self.Dial(self, self.range, self.notifyChanged) + self.d_widget = self.Dial(self, self.range, self.notifyChanged, rangeType) elif style == "slider": - self.d_widget = self.Slider(self, self.range, self.notifyChanged) + self.d_widget = self.Slider(self, self.range, self.notifyChanged, rangeType) elif style == "counter": # The counter widget can be directly wired to the notifyChanged slot - self.d_widget = self.Counter(self, self.range, self.notifyChanged) + self.d_widget = self.Counter(self, self.range, self.notifyChanged, rangeType) else: # The CounterSlider needs its own internal handlers before calling notifyChanged - self.d_widget = self.CounterSlider(self, self.range, self.notifyChanged) + self.d_widget = self.CounterSlider(self, self.range, self.notifyChanged, rangeType) layout.addWidget(self.d_widget) self.setLayout(layout) class Dial(QtGui.QDial): """ Creates the range using a dial """ - def __init__(self, parent, ranges, slot): + def __init__(self, parent, ranges, slot, rangeType=float): QtGui.QDial.__init__(self, parent) + self.rangeType = rangeType + # Setup the dial self.setRange(0, ranges.nsteps-1) self.setSingleStep(1) @@ -116,13 +121,15 @@ class RangeWidget(QtGui.QWidget): def changed(self, value): """ Handles maping the value to the right range before calling the slot. """ val = self.range.map_range(value) - self.notifyChanged(val) + self.notifyChanged(self.rangeType(val)) class Slider(QtGui.QSlider): """ Creates the range using a slider """ - def __init__(self, parent, ranges, slot): + def __init__(self, parent, ranges, slot, rangeType=float): QtGui.QSlider.__init__(self, QtCore.Qt.Horizontal, parent) + self.rangeType = rangeType + # Setup the slider #self.setFocusPolicy(QtCore.Qt.NoFocus) self.setRange(0, ranges.nsteps - 1) @@ -149,7 +156,7 @@ class RangeWidget(QtGui.QWidget): def changed(self, value): """ Handle the valueChanged signal and map the value into the correct range """ val = self.range.map_range(value) - self.notifyChanged(val) + self.notifyChanged(self.rangeType(val)) def mousePressEvent(self, event): if((event.button() == QtCore.Qt.LeftButton)): @@ -168,9 +175,11 @@ class RangeWidget(QtGui.QWidget): class Counter(QtGui.QDoubleSpinBox): """ Creates the range using a counter """ - def __init__(self, parent, ranges, slot): + def __init__(self, parent, ranges, slot, rangeType=float): QtGui.QDoubleSpinBox.__init__(self, parent) + self.rangeType = rangeType + # Setup the counter self.setRange(ranges.min, ranges.max) self.setValue(ranges.default) @@ -179,18 +188,25 @@ class RangeWidget(QtGui.QWidget): self.setDecimals(ranges.precision) # The counter already handles floats and can be connected directly. - self.valueChanged.connect(slot) + self.valueChanged.connect(self.changed) + self.notifyChanged = slot + + def changed(self, value): + """ Handle the valueChanged signal by converting to the right type """ + self.notifyChanged(self.rangeType(value)) class CounterSlider(QtGui.QWidget): """ Creates the range using a counter and slider """ - def __init__(self, parent, ranges, slot): + def __init__(self, parent, ranges, slot, rangeType=float): QtGui.QWidget.__init__(self, parent) + self.rangeType = rangeType + # Slot to call in the parent self.notifyChanged = slot - self.slider = RangeWidget.Slider(parent, ranges, self.sliderChanged) - self.counter = RangeWidget.Counter(parent, ranges, self.counterChanged) + self.slider = RangeWidget.Slider(parent, ranges, self.sliderChanged, rangeType) + self.counter = RangeWidget.Counter(parent, ranges, self.counterChanged, rangeType) # Need another horizontal layout to wrap the other widgets. layout = Qt.QHBoxLayout() @@ -207,8 +223,8 @@ class RangeWidget(QtGui.QWidget): # If the counter was changed, ignore any of these events if not self.ignoreSlider: # Value is already float. Just set the counter - self.counter.setValue(value) - self.notifyChanged(value) + self.counter.setValue(self.rangeType(value)) + self.notifyChanged(self.rangeType(value)) self.ignoreSlider = False def counterChanged(self, value): @@ -223,4 +239,21 @@ class RangeWidget(QtGui.QWidget): self.ignoreSlider = True self.slider.setValue(new) - self.notifyChanged(value) + self.notifyChanged(self.rangeType(value)) + + +if __name__ == "__main__": + from PyQt4 import Qt + import sys + + def valueChanged(frequency): + print("Value updated - " + str(frequency)) + + app = Qt.QApplication(sys.argv) + widget = RangeWidget(Range(0, 100, 10, 1, 100), valueChanged, "Test", "counter_slider", int) + + widget.show() + widget.setWindowTitle("Test Qt Range") + app.exec_() + + widget = None diff --git a/grc/scripts/gnuradio-companion b/grc/scripts/gnuradio-companion index 7a407eaaf8..b8b960a91e 100755 --- a/grc/scripts/gnuradio-companion +++ b/grc/scripts/gnuradio-companion @@ -55,6 +55,14 @@ def show_gtk_error_dialog(title, message): d.run() +def check_gtk_init(): + try: + gtk.init_check() + except RuntimeError: + print 'GTK initialization failed - bailing' + exit(-1) + + def check_gnuradio_import(): try: from gnuradio import gr @@ -108,6 +116,7 @@ def main(): if __name__ == '__main__': + check_gtk_init() check_gnuradio_import() ensure_blocks_path() main() |