diff options
361 files changed, 18623 insertions, 7813 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 622b724b38..4b4714eb42 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 9) -set(VERSION_INFO_MAINT_VERSION 2) +set(VERSION_INFO_MINOR_VERSION 10) +set(VERSION_INFO_MAINT_VERSION git) include(GrVersion) #setup version info # Append -O2 optimization flag for Debug builds (Not on MSVC since conflicts with RTC1 flag) @@ -55,6 +55,32 @@ IF (NOT MSVC) SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O2") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O2") ENDIF() + +# Set C/C++ standard for all targets +# NOTE: Starting with cmake v3.1 this should be used: +# set(CMAKE_C_STANDARD 90) +# set(CMAKE_CXX_STANDARD 98) + +IF(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++98") +ELSEIF(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++98") +ELSEIF(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++98") +ELSE() + message(warning "C++ standard could not be set because compiler is not GNU, Clang or MSVC.") +ENDIF() + +IF(CMAKE_C_COMPILER_ID STREQUAL "GNU") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu11") +ELSEIF(CMAKE_C_COMPILER_ID STREQUAL "Clang") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu11") +ELSEIF(CMAKE_C_COMPILER_ID STREQUAL "MSVC") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11") +ELSE() + message(warning "C standard could not be set because compiler is not GNU, Clang or MSVC.") +ENDIF() + ######################################################################## # Environment setup ######################################################################## @@ -327,6 +353,9 @@ if(NOT VOLK_FOUND) set(VOLK_LIBRARIES volk) + set(VOLK_INSTALL_LIBRARY_DIR ${CMAKE_INSTALL_PREFIX}/lib) + set(VOLK_INSTALL_INCLUDE_DIR ${CMAKE_INSTALL_PREFIX}/include) + if(ENABLE_VOLK) include(GrPackage) @@ -348,6 +377,9 @@ if(NOT VOLK_FOUND) else() message(STATUS " An external VOLK has been found and will be used for build.") set(ENABLE_VOLK TRUE) + + get_filename_component(VOLK_INSTALL_LIBRARY_DIR "${VOLK_LIBRARIES}" DIRECTORY) + set(VOLK_INSTALL_INCLUDE_DIR ${VOLK_INCLUDE_DIRS}) endif(NOT VOLK_FOUND) message(STATUS " Override with -DENABLE_INTERNAL_VOLK=ON/OFF") @@ -429,12 +461,17 @@ endif(ENABLE_GR_CTRLPORT) # http://www.cmake.org/Wiki/CMake/Tutorials/Packaging configure_file( + ${CMAKE_SOURCE_DIR}/cmake/Modules/GnuradioConfig.cmake.in + ${CMAKE_BINARY_DIR}/cmake/Modules/GnuradioConfig.cmake +@ONLY) + +configure_file( ${CMAKE_SOURCE_DIR}/cmake/Modules/GnuradioConfigVersion.cmake.in ${CMAKE_BINARY_DIR}/cmake/Modules/GnuradioConfigVersion.cmake @ONLY) SET(cmake_configs - ${CMAKE_SOURCE_DIR}/cmake/Modules/GnuradioConfig.cmake + ${CMAKE_BINARY_DIR}/cmake/Modules/GnuradioConfig.cmake ${CMAKE_BINARY_DIR}/cmake/Modules/GnuradioConfigVersion.cmake ) diff --git a/cmake/Modules/GnuradioConfig.cmake b/cmake/Modules/GnuradioConfig.cmake.in index 31532c605c..fc7499dfdf 100644 --- a/cmake/Modules/GnuradioConfig.cmake +++ b/cmake/Modules/GnuradioConfig.cmake.in @@ -72,6 +72,8 @@ function(GR_MODULE EXTVAR PCNAME INCFILE LIBFILE) ${CMAKE_INSTALL_PREFIX}/include PATHS /usr/local/include /usr/include + "@CMAKE_INSTALL_PREFIX@/include" + "@VOLK_INSTALL_INCLUDE_DIR@" ) # look for libs @@ -87,6 +89,8 @@ function(GR_MODULE EXTVAR PCNAME INCFILE LIBFILE) /usr/local/lib64 /usr/lib /usr/lib64 + "@CMAKE_INSTALL_PREFIX@/lib" + "@VOLK_INSTALL_LIBRARY_DIR@" ) list(APPEND ${LIBVAR_NAME} ${${LIBVAR_NAME}_${libname}}) endforeach(libname) diff --git a/cmake/Modules/GrVersion.cmake b/cmake/Modules/GrVersion.cmake index bafd0a7326..dceac67bab 100644 --- a/cmake/Modules/GrVersion.cmake +++ b/cmake/Modules/GrVersion.cmake @@ -41,7 +41,13 @@ if(GIT_FOUND AND EXISTS ${CMAKE_SOURCE_DIR}/.git) WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) else() - set(GIT_DESCRIBE "v${MAJOR_VERSION}.${API_COMPAT}.x-xxx-xunknown") + if(NOT GR_GIT_COUNT) + set(GR_GIT_COUNT "compat-xxx") + endif() + if(NOT GR_GIT_HASH) + set(GR_GIT_HASH "xunknown") + endif() + set(GIT_DESCRIBE "v${MAJOR_VERSION}.${API_COMPAT}-${GR_GIT_COUNT}-${GR_GIT_HASH}") endif() ######################################################################## diff --git a/docs/exploring-gnuradio/fm_tx.grc b/docs/exploring-gnuradio/fm_tx.grc index 2f047bf09b..bb13417a6b 100644 --- a/docs/exploring-gnuradio/fm_tx.grc +++ b/docs/exploring-gnuradio/fm_tx.grc @@ -792,6 +792,10 @@ <value>75e3</value> </param> <param> + <key>fh</key> + <value>0.925 * tx_rate/2.0</value> + </param> + <param> <key>affinity</key> <value></value> </param> diff --git a/gnuradio-runtime/apps/gnuradio-config-info.cc b/gnuradio-runtime/apps/gnuradio-config-info.cc index c91b080039..813dda138c 100644 --- a/gnuradio-runtime/apps/gnuradio-config-info.cc +++ b/gnuradio-runtime/apps/gnuradio-config-info.cc @@ -25,6 +25,8 @@ #endif #include <gnuradio/constants.h> +#include <gnuradio/sys_paths.h> +#include <gnuradio/prefs.h> #include <boost/program_options.hpp> #include <boost/format.hpp> #include <iostream> @@ -43,6 +45,8 @@ main(int argc, char **argv) ("prefix", "print GNU Radio installation prefix") ("sysconfdir", "print GNU Radio system configuration directory") ("prefsdir", "print GNU Radio preferences directory") + ("userprefsdir", "print GNU Radio user preferences directory") + ("prefs", "print GNU Radio preferences") ("builddate", "print GNU Radio build date (RFC2822 format)") ("enabled-components", "print GNU Radio build time enabled components") ("cc", "print GNU Radio C compiler version") @@ -75,6 +79,12 @@ main(int argc, char **argv) if(vm.count("prefsdir")) std::cout << gr::prefsdir() << std::endl; + if(vm.count("userprefsdir")) + std::cout << gr::userconf_path() << std::endl; + + if(vm.count("prefs")) + std::cout << gr::prefs::singleton()->to_string() << std::endl; + if(vm.count("builddate")) std::cout << gr::build_date() << std::endl; diff --git a/gnuradio-runtime/include/gnuradio/block.h b/gnuradio-runtime/include/gnuradio/block.h index c6185d9f2d..a484ccf429 100644 --- a/gnuradio-runtime/include/gnuradio/block.h +++ b/gnuradio-runtime/include/gnuradio/block.h @@ -80,7 +80,9 @@ namespace gr { * History is the number of x_i's that are examined to produce one y_i. * This comes in handy for FIR filters, where we use history to * ensure that our input contains the appropriate "history" for the - * filter. History should be equal to the number of filter taps. + * filter. History should be equal to the number of filter taps. First + * history samples (when there are no previous samples) are + * initialized with zeroes. */ unsigned history() const; void set_history(unsigned history); diff --git a/gnuradio-runtime/include/gnuradio/block_detail.h b/gnuradio-runtime/include/gnuradio/block_detail.h index 916c0a46c1..a71030b439 100644 --- a/gnuradio-runtime/include/gnuradio/block_detail.h +++ b/gnuradio-runtime/include/gnuradio/block_detail.h @@ -100,6 +100,13 @@ namespace gr { // Return the number of items written on output stream which_output uint64_t nitems_written(unsigned int which_output); + // sets nitems_read and nitems_written to 0 for all input/output + // buffers. + void reset_nitem_counters(); + + // Clears all tags from the input buffers. + void clear_tags(); + /*! * \brief Adds a new tag to the given output stream. * diff --git a/gnuradio-runtime/include/gnuradio/buffer.h b/gnuradio-runtime/include/gnuradio/buffer.h index 5da383dc8f..914661b318 100644 --- a/gnuradio-runtime/include/gnuradio/buffer.h +++ b/gnuradio-runtime/include/gnuradio/buffer.h @@ -100,6 +100,8 @@ namespace gr { uint64_t nitems_written() { return d_abs_write_offset; } + void reset_nitem_counter() { d_abs_write_offset = 0; } + size_t get_sizeof_item() { return d_sizeof_item; } /*! @@ -288,6 +290,8 @@ namespace gr { uint64_t nitems_read() { return d_abs_read_offset; } + void reset_nitem_counter() { d_abs_read_offset = 0; } + size_t get_sizeof_item() { return d_buffer->get_sizeof_item(); } /*! diff --git a/gnuradio-runtime/include/gnuradio/prefs.h b/gnuradio-runtime/include/gnuradio/prefs.h index 4dc92b3631..287f86cc7c 100644 --- a/gnuradio-runtime/include/gnuradio/prefs.h +++ b/gnuradio-runtime/include/gnuradio/prefs.h @@ -165,8 +165,7 @@ namespace gr { protected: virtual std::vector<std::string> _sys_prefs_filenames(); - virtual std::string _read_files(const std::vector<std::string> &filenames); - virtual void _convert_to_map(const std::string &conf); + virtual void _read_files(const std::vector<std::string> &filenames); virtual char * option_to_env(std::string section, std::string option); private: diff --git a/gnuradio-runtime/include/gnuradio/sys_paths.h b/gnuradio-runtime/include/gnuradio/sys_paths.h index 1bd2e0deb7..efea07b599 100644 --- a/gnuradio-runtime/include/gnuradio/sys_paths.h +++ b/gnuradio-runtime/include/gnuradio/sys_paths.h @@ -32,6 +32,9 @@ namespace gr { //! directory to store application data GR_RUNTIME_API const char *appdata_path(); + //! directory to store user configuration + GR_RUNTIME_API const char *userconf_path(); + } /* namespace gr */ #endif /* GR_SYS_PATHS_H */ diff --git a/gnuradio-runtime/lib/block_detail.cc b/gnuradio-runtime/lib/block_detail.cc index 9463e8d13b..484d849652 100644 --- a/gnuradio-runtime/lib/block_detail.cc +++ b/gnuradio-runtime/lib/block_detail.cc @@ -162,6 +162,26 @@ namespace gr { } void + block_detail::reset_nitem_counters() + { + for(unsigned int i = 0; i < d_ninputs; i++) { + d_input[i]->reset_nitem_counter(); + } + for(unsigned int o = 0; o < d_noutputs; o++) { + d_output[o]->reset_nitem_counter(); + } + } + + void + block_detail::clear_tags() + { + for(unsigned int i = 0; i < d_ninputs; i++) { + uint64_t max_time = 0xFFFFFFFFFFFFFFFF; // from now to the end of time + d_input[i]->buffer()->prune_tags(max_time); + } + } + + void block_detail::add_item_tag(unsigned int which_output, const tag_t &tag) { if(!pmt::is_symbol(tag.key)) { diff --git a/gnuradio-runtime/lib/flat_flowgraph.cc b/gnuradio-runtime/lib/flat_flowgraph.cc index 82680bfc2d..434a92fb3d 100644 --- a/gnuradio-runtime/lib/flat_flowgraph.cc +++ b/gnuradio-runtime/lib/flat_flowgraph.cc @@ -177,6 +177,13 @@ namespace gr { catch(std::bad_alloc&) { b = make_buffer(nitems, item_size, grblock); } + + // Set the max noutput items size here to make sure it's always + // set in the block and available in the start() method. + // But don't overwrite if the user has set this externally. + if(!grblock->is_set_max_noutput_items()) + grblock->set_max_noutput_items(nitems); + return b; } @@ -225,9 +232,10 @@ namespace gr { std::cout << "merge: allocating new detail for block " << (*p) << std::endl; block->set_detail(allocate_block_detail(block)); } - else + else { if(FLAT_FLOWGRAPH_DEBUG) std::cout << "merge: reusing original detail for block " << (*p) << std::endl; + } } // Calculate the old edges that will be going away, and clear the @@ -311,6 +319,9 @@ namespace gr { // Now deal with the fact that the block details might have // changed numbers of inputs and outputs vs. in the old // flowgraph. + + block->detail()->reset_nitem_counters(); + block->detail()->clear_tags(); } } diff --git a/gnuradio-runtime/lib/prefs.cc b/gnuradio-runtime/lib/prefs.cc index b303ffdaf9..341028eba8 100644 --- a/gnuradio-runtime/lib/prefs.cc +++ b/gnuradio-runtime/lib/prefs.cc @@ -27,12 +27,18 @@ #include <gnuradio/prefs.h> #include <gnuradio/sys_paths.h> #include <gnuradio/constants.h> + #include <algorithm> +#include <fstream> #include <boost/filesystem/operations.hpp> #include <boost/filesystem/path.hpp> #include <boost/filesystem/fstream.hpp> +#include <boost/program_options.hpp> +#include <boost/foreach.hpp> namespace fs = boost::filesystem; +namespace po = boost::program_options; +typedef std::ifstream::char_type char_t; namespace gr { @@ -46,10 +52,7 @@ namespace gr { prefs::prefs() { - std::string config = _read_files(_sys_prefs_filenames()); - - // Convert the string into a map - _convert_to_map(config); + _read_files(_sys_prefs_filenames()); } prefs::~prefs() @@ -77,115 +80,47 @@ namespace gr { // Find if there is a ~/.gnuradio/config.conf file and add this to // the end of the file list to override any preferences in the // installed path config files. - fs::path homedir = fs::path(gr::appdata_path()); - homedir = homedir/".gnuradio/config.conf"; - if(fs::exists(homedir)) { - fnames.push_back(homedir.string()); + fs::path userconf = fs::path(gr::userconf_path()) / "config.conf"; + if(fs::exists(userconf)) { + fnames.push_back(userconf.string()); } return fnames; } - std::string + void prefs::_read_files(const std::vector<std::string> &filenames) { - std::string config; - - std::vector<std::string>::const_iterator sitr; - char tmp[1024]; - for(sitr = filenames.begin(); sitr != filenames.end(); sitr++) { - fs::ifstream fin(*sitr); - while(!fin.eof()) { - fin.getline(tmp, 1024); - std::string t(tmp); - // ignore empty lines or lines of just comments - if((t.size() > 0) && (t[0] != '#')) { - // remove any comments in the line - size_t hash = t.find("#"); - - // Remove any white space unless it's between quotes - // Search if the string has any quotes in it - size_t quote1 = t.find("\""); - if(quote1 != std::string::npos) { - // If yes, find where the start and end quotes are. - // Note that this isn't robust if there are multiple - // quoted strings in the line; this will just take the - // first and last, and if there is only a single quote, - // this will treat the entire line after the quote as the - // quoted text. - // the inq variable is strips the quotes as well. - size_t quote2 = t.find_last_of("\""); - std::string sol = t.substr(0, quote1); - std::string inq = t.substr(quote1+1, quote2-quote1-1); - std::string eol = t.substr(quote2+1, t.size()-quote2-1); - - // Remove all white space of the text before the first quote - sol.erase(std::remove_if(sol.begin(), sol.end(), - ::isspace), sol.end()); - - // Remove all white space of the text after the end quote - eol.erase(std::remove_if(eol.begin(), eol.end(), - ::isspace), eol.end()); - - // Pack the stripped start and end of lines with the part - // of the string in quotes (but without the quotes). - t = sol + inq + eol; + BOOST_FOREACH( std::string fname, filenames) { + std::ifstream infile(fname.c_str()); + if(infile.good()) { + try { + po::basic_parsed_options<char_t> parsed = po::parse_config_file(infile, po::options_description(), true); + BOOST_FOREACH(po::basic_option<char_t> o, (parsed.options) ){ + std::string okey = o.string_key; + size_t pos = okey.find("."); + std::string section, key; + if(pos != std::string::npos) { + section = okey.substr(0,pos); + key = okey.substr(pos+1); + } else { + section = "default"; + key = okey; + } + std::transform(section.begin(), section.end(), section.begin(), ::tolower); + std::transform(key.begin(), key.end(), key.begin(), ::tolower); + // value of a basic_option is always a std::vector<string>; we only allow single values, so: + std::string value = o.value[0]; + d_config_map[section][key] = value; } - else { - // No quotes, just strip all white space - t.erase(std::remove_if(t.begin(), t.end(), - ::isspace), t.end()); - } - - // Use hash marks at the end of each segment as a delimiter - config += t.substr(0, hash) + '#'; + } catch(const boost::program_options::invalid_config_file_syntax & e) { + std::cerr << "WARNING: Config file '" << fname << "' failed to parse:" << std::endl; + std::cerr << e.what() << std::endl; + std::cerr << "Skipping it" << std::endl; } + } else { // infile.good(); + std::cerr << "WARNING: Config file '" << fname << "' could not be opened for reading." << std::endl; } - fin.close(); - } - - return config; - } - - void - prefs::_convert_to_map(const std::string &conf) - { - // Convert the string into an map of maps - // Map is structured as {section name: map of options} - // And options map is simply: {option name: option value} - std::string sub = conf; - size_t sec_start = sub.find("["); - while(sec_start != std::string::npos) { - sub = sub.substr(sec_start); - - size_t sec_end = sub.find("]"); - if(sec_end == std::string::npos) - throw std::runtime_error("Config file error: Mismatched section label.\n"); - - std::string sec = sub.substr(1, sec_end-1); - size_t next_sec_start = sub.find("[", sec_end); - std::string subsec = sub.substr(sec_end+1, next_sec_start-sec_end-2); - - std::transform(sec.begin(), sec.end(), sec.begin(), ::tolower); - - std::map<std::string, std::string> options_map = d_config_map[sec]; - size_t next_opt = 0; - size_t next_val = 0; - next_opt = subsec.find("#"); - while(next_opt < subsec.size()-1) { - next_val = subsec.find("=", next_opt); - std::string option = subsec.substr(next_opt+1, next_val-next_opt-1); - - next_opt = subsec.find("#", next_val); - std::string value = subsec.substr(next_val+1, next_opt-next_val-1); - - std::transform(option.begin(), option.end(), option.begin(), ::tolower); - options_map[option] = value; - } - - d_config_map[sec] = options_map; - - sec_start = sub.find("[", sec_end); } } @@ -195,8 +130,7 @@ namespace gr { std::vector<std::string> filenames; filenames.push_back(configfile); - std::string config = _read_files(filenames); - _convert_to_map(config); + _read_files(filenames); } @@ -222,10 +156,8 @@ namespace gr { prefs::save() { std::string conf = to_string(); - - fs::path homedir = fs::path(gr::appdata_path()); - homedir = homedir/".gnuradio/config.conf"; - fs::ofstream fout(homedir); + fs::path userconf = fs::path(gr::userconf_path()) / "config.conf"; + fs::ofstream fout(userconf); fout << conf; fout.close(); } diff --git a/gnuradio-runtime/lib/sys_paths.cc b/gnuradio-runtime/lib/sys_paths.cc index 64853c68b2..3bf6697bac 100644 --- a/gnuradio-runtime/lib/sys_paths.cc +++ b/gnuradio-runtime/lib/sys_paths.cc @@ -23,6 +23,8 @@ #include <cstdlib> //getenv #include <cstdio> //P_tmpdir (maybe) +#include <boost/filesystem/path.hpp> + namespace gr { const char *tmp_path() @@ -62,4 +64,11 @@ namespace gr { return tmp_path(); } + const char *userconf_path() + { + boost::filesystem::path p(appdata_path()); + p = p / ".gnuradio"; + return p.c_str(); + } + } /* namespace gr */ diff --git a/gr-analog/examples/fmtest.py b/gr-analog/examples/fmtest.py index 327da8eacb..7ed08cafbe 100755 --- a/gr-analog/examples/fmtest.py +++ b/gr-analog/examples/fmtest.py @@ -48,7 +48,8 @@ class fmtx(gr.hier_block2): gr.io_signature(1, 1, gr.sizeof_float), gr.io_signature(1, 1, gr.sizeof_gr_complex)) - fmtx = analog.nbfm_tx(audio_rate, if_rate, max_dev=5e3, tau=75e-6) + fmtx = analog.nbfm_tx(audio_rate, if_rate, max_dev=5e3, + tau=75e-6, fh=0.925*if_rate/2.0) # Local oscillator lo = analog.sig_source_c(if_rate, # sample rate diff --git a/gr-analog/grc/analog_block_tree.xml b/gr-analog/grc/analog_block_tree.xml index d08e646663..64ccbdd855 100644 --- a/gr-analog/grc/analog_block_tree.xml +++ b/gr-analog/grc/analog_block_tree.xml @@ -27,7 +27,7 @@ ################################################### --> <cat> - <name></name> <!-- Blank for Root Name --> + <name>[Core]</name> <cat> <name>Level Controllers</name> <block>analog_agc_xx</block> diff --git a/gr-analog/grc/analog_fm_preemph.xml b/gr-analog/grc/analog_fm_preemph.xml index fb898b87f3..a754ce9c6f 100644 --- a/gr-analog/grc/analog_fm_preemph.xml +++ b/gr-analog/grc/analog_fm_preemph.xml @@ -8,7 +8,7 @@ <name>FM Preemphasis</name> <key>analog_fm_preemph</key> <import>from gnuradio import analog</import> - <make>analog.fm_preemph(fs=$samp_rate, tau=$tau)</make> + <make>analog.fm_preemph(fs=$samp_rate, tau=$tau, fh=$fh)</make> <param> <name>Sample Rate</name> <key>samp_rate</key> @@ -20,6 +20,12 @@ <value>75e-6</value> <type>real</type> </param> + <param> + <name>High Corner Freq</name> + <key>fh</key> + <value>-1.0</value> + <type>real</type> + </param> <sink> <name>in</name> <type>float</type> diff --git a/gr-analog/grc/analog_nbfm_tx.xml b/gr-analog/grc/analog_nbfm_tx.xml index ea13b8f1c9..bc80fffbcb 100644 --- a/gr-analog/grc/analog_nbfm_tx.xml +++ b/gr-analog/grc/analog_nbfm_tx.xml @@ -13,6 +13,7 @@ quad_rate=$quad_rate, tau=$tau, max_dev=$max_dev, + fh=$fh, )</make> <callback>set_max_deviation($max_dev)</callback> @@ -42,6 +43,13 @@ <type>real</type> </param> + <param> + <name>Preemphasis High Corner Freq</name> + <key>fh</key> + <value>-1.0</value> + <type>real</type> + </param> + <check>($quad_rate)%($audio_rate) == 0</check> <sink> diff --git a/gr-analog/grc/analog_wfm_tx.xml b/gr-analog/grc/analog_wfm_tx.xml index 89844f9ef3..507c2ea894 100644 --- a/gr-analog/grc/analog_wfm_tx.xml +++ b/gr-analog/grc/analog_wfm_tx.xml @@ -13,6 +13,7 @@ quad_rate=$quad_rate, tau=$tau, max_dev=$max_dev, + fh=$fh, )</make> <param> <name>Audio Rate</name> @@ -36,6 +37,12 @@ <value>75e3</value> <type>real</type> </param> + <param> + <name>Preemphasis High Corner Freq</name> + <key>fh</key> + <value>-1.0</value> + <type>real</type> + </param> <check>($quad_rate)%($audio_rate) == 0</check> <sink> <name>in</name> diff --git a/gr-analog/lib/CMakeLists.txt b/gr-analog/lib/CMakeLists.txt index 918f894abe..a75d70337b 100644 --- a/gr-analog/lib/CMakeLists.txt +++ b/gr-analog/lib/CMakeLists.txt @@ -110,17 +110,6 @@ GR_LIBRARY_FOO(gnuradio-analog RUNTIME_COMPONENT "analog_runtime" DEVEL_COMPONEN add_dependencies(gnuradio-analog analog_generated_includes analog_generated_swigs gnuradio-filter) if(ENABLE_STATIC_LIBS) - if(ENABLE_GR_CTRLPORT) - # Remove GR_CTRLPORT set this target's definitions. - # Makes sure we don't try to use ControlPort stuff in source files - GET_DIRECTORY_PROPERTY(STATIC_DEFS COMPILE_DEFINITIONS) - list(REMOVE_ITEM STATIC_DEFS "GR_CTRLPORT") - SET_PROPERTY(DIRECTORY PROPERTY COMPILE_DEFINITIONS "${STATIC_DEFS}") - - # readd it to the target since we removed it from the directory-wide list. - SET_PROPERTY(TARGET gnuradio-analog APPEND PROPERTY COMPILE_DEFINITIONS "GR_CTRLPORT") - endif(ENABLE_GR_CTRLPORT) - add_library(gnuradio-analog_static STATIC ${analog_sources}) add_dependencies(gnuradio-analog_static diff --git a/gr-analog/lib/frequency_modulator_fc_impl.cc b/gr-analog/lib/frequency_modulator_fc_impl.cc index 812eb8bf0b..56fa0f7c17 100644 --- a/gr-analog/lib/frequency_modulator_fc_impl.cc +++ b/gr-analog/lib/frequency_modulator_fc_impl.cc @@ -76,5 +76,27 @@ namespace gr { return noutput_items; } + void + frequency_modulator_fc_impl::setup_rpc() + { +#ifdef GR_CTRLPORT + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_get<frequency_modulator_fc, float>( + alias(), "sensitivity", + &frequency_modulator_fc::sensitivity, + pmt::mp(-1024.0f), pmt::mp(1024.0f), pmt::mp(0.0f), + "", "Sensitivity", RPC_PRIVLVL_MIN, + DISPTIME | DISPOPTSTRIP))); + + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_set<frequency_modulator_fc, float>( + alias(), "sensitivity", + &frequency_modulator_fc::set_sensitivity, + pmt::mp(-1024.0f), pmt::mp(1024.0f), pmt::mp(0.0f), + "", "sensitivity", + RPC_PRIVLVL_MIN, DISPNULL))); +#endif /* GR_CTRLPORT */ + + } } /* namespace analog */ } /* namespace gr */ diff --git a/gr-analog/lib/frequency_modulator_fc_impl.h b/gr-analog/lib/frequency_modulator_fc_impl.h index 9f5310ce97..9f595d1ddb 100644 --- a/gr-analog/lib/frequency_modulator_fc_impl.h +++ b/gr-analog/lib/frequency_modulator_fc_impl.h @@ -41,6 +41,8 @@ namespace gr { void set_sensitivity(float sens) { d_sensitivity = sens; } float sensitivity() const { return d_sensitivity; } + void setup_rpc(); + int work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); diff --git a/gr-analog/lib/sig_source_X_impl.cc.t b/gr-analog/lib/sig_source_X_impl.cc.t index 227d4ba46e..017177eae0 100644 --- a/gr-analog/lib/sig_source_X_impl.cc.t +++ b/gr-analog/lib/sig_source_X_impl.cc.t @@ -53,7 +53,7 @@ namespace gr { d_frequency(frequency), d_ampl(ampl), d_offset(offset) { set_frequency(frequency); - + message_port_register_in(pmt::mp("freq")); set_msg_handler(pmt::mp("freq"), boost::bind(&@IMPL_NAME@::set_frequency_msg, this, _1)); } @@ -62,6 +62,36 @@ namespace gr { { } + void + @IMPL_NAME@::set_frequency_msg(pmt::pmt_t msg) + { + // Accepts either a number that is assumed to be the new + // frequency or a key:value pair message where the key must be + // "freq" and the value is the new frequency. + + if(pmt::is_number(msg)) { + set_frequency(pmt::to_double(msg)); + } + else if(pmt::is_pair(msg)) { + pmt::pmt_t key = pmt::car(msg); + pmt::pmt_t val = pmt::cdr(msg); + if(pmt::eq(key, pmt::intern("freq"))) { + if(pmt::is_number(val)) { + set_frequency(pmt::to_double(val)); + } + } + else { + GR_LOG_WARN(d_logger, boost::format("Set Frequency Message must have " + "the key = 'freq'; got '%1%'.") \ + % pmt::write_string(key)); + } + } + else { + GR_LOG_WARN(d_logger, "Set Frequency Message must be either a number or a " + "key:value pair where the key is 'freq'."); + } + } + int @IMPL_NAME@::work(int noutput_items, gr_vector_const_void_star &input_items, diff --git a/gr-analog/lib/sig_source_X_impl.h.t b/gr-analog/lib/sig_source_X_impl.h.t index bd3609df16..f5dfd5c4f6 100644 --- a/gr-analog/lib/sig_source_X_impl.h.t +++ b/gr-analog/lib/sig_source_X_impl.h.t @@ -59,7 +59,7 @@ namespace gr { void set_sampling_freq(double sampling_freq); void set_waveform(gr_waveform_t waveform); - void set_frequency_msg(pmt::pmt_t msg){ set_frequency(pmt::to_double(msg)); }; + void set_frequency_msg(pmt::pmt_t msg); void set_frequency(double frequency); void set_amplitude(double ampl); void set_offset(@TYPE@ offset); diff --git a/gr-analog/python/analog/fm_emph.py b/gr-analog/python/analog/fm_emph.py index d2a38d4aff..bfa4742ace 100644 --- a/gr-analog/python/analog/fm_emph.py +++ b/gr-analog/python/analog/fm_emph.py @@ -21,17 +21,78 @@ from gnuradio import gr, filter import math +import cmath # -# 1 -# H(s) = ------- -# 1 + s +# An analog deemphasis filter: # -# tau is the RC time constant. -# critical frequency: w_p = 1/tau +# R +# o------/\/\/\/---+----o +# | +# = C +# | +# --- # -# We prewarp and use the bilinear z-transform to get our IIR coefficients. -# See "Digital Signal Processing: A Practical Approach" by Ifeachor and Jervis +# Has this transfer function: +# +# 1 1 +# ---- --- +# RC tau +# H(s) = ---------- = ---------- +# 1 1 +# s + ---- s + --- +# RC tau +# +# And has its -3 dB response, due to the pole, at +# +# |H(j w_c)|^2 = 1/2 => s = j w_c = j (1/(RC)) +# +# Historically, this corner frequency of analog audio deemphasis filters +# been specified by the RC time constant used, called tau. +# So w_c = 1/tau. +# +# FWIW, for standard tau values, some standard analog components would be: +# tau = 75 us = (50K)(1.5 nF) = (50 ohms)(1.5 uF) +# tau = 50 us = (50K)(1.0 nF) = (50 ohms)(1.0 uF) +# +# In specifying tau for this digital deemphasis filter, tau specifies +# the *digital* corner frequency, w_c, desired. +# +# The digital deemphasis filter design below, uses the +# "bilinear transformation" method of designing digital filters: +# +# 1. Convert digitial specifications into the analog domain, by prewarping +# digital frequency specifications into analog frequencies. +# +# w_a = (2/T)tan(wT/2) +# +# 2. Use an analog filter design technique to design the filter. +# +# 3. Use the bilinear transformation to convert the analog filter design to a +# digital filter design. +# +# H(z) = H(s)| +# s = (2/T)(1-z^-1)/(1+z^-1) +# +# +# w_ca 1 1 - (-1) z^-1 +# H(z) = ---- * ----------- * ----------------------- +# 2 fs -w_ca -w_ca +# 1 - ----- 1 + ----- +# 2 fs 2 fs +# 1 - ----------- z^-1 +# -w_ca +# 1 - ----- +# 2 fs +# +# We use this design technique, because it is an easy way to obtain a filter +# design with the -6 dB/octave roll-off required of the deemphasis filter. +# +# Jackson, Leland B., _Digital_Filters_and_Signal_Processing_Second_Edition_, +# Kluwer Academic Publishers, 1989, pp 201-212 +# +# Orfanidis, Sophocles J., _Introduction_to_Signal_Processing_, Prentice Hall, +# 1996, pp 573-583 # @@ -51,15 +112,24 @@ class fm_deemph(gr.hier_block2): gr.io_signature(1, 1, gr.sizeof_float), # Input signature gr.io_signature(1, 1, gr.sizeof_float)) # Output signature - w_p = 1/tau - w_pp = math.tan(w_p / (fs * 2)) # prewarped analog freq + # Digital corner frequency + w_c = 1.0 / tau + + # Prewarped analog corner frequency + w_ca = 2.0 * fs * math.tan(w_c / (2.0 * fs)) - a1 = (w_pp - 1)/(w_pp + 1) - b0 = w_pp/(1 + w_pp) - b1 = b0 + # Resulting digital pole, zero, and gain term from the bilinear + # transformation of H(s) = w_ca / (s + w_ca) to + # H(z) = b0 (1 - z1 z^-1)/(1 - p1 z^-1) + k = -w_ca / (2.0 * fs) + z1 = -1.0 + p1 = (1.0 + k) / (1.0 - k) + b0 = -k / (1.0 - k) - btaps = [b0, b1] - ataps = [1, a1] + btaps = [ b0 * 1.0, b0 * -z1 ] + ataps = [ 1.0, -p1 ] + + # Since H(s = 0) = 1.0, then H(z = 1) = 1.0 and has 0 dB gain at DC if 0: print "btaps =", btaps @@ -67,26 +137,17 @@ class fm_deemph(gr.hier_block2): global plot1 plot1 = gru.gnuplot_freqz(gru.freqz(btaps, ataps), fs, True) - deemph = filter.iir_filter_ffd(btaps, ataps) + deemph = filter.iir_filter_ffd(btaps, ataps, False) self.connect(self, deemph, self) # -# 1 + s*t1 -# H(s) = ---------- -# 1 + s*t2 -# -# I think this is the right transfer function. -# +# An analog preemphasis filter, that flattens out again at the high end: # -# This fine ASCII rendition is based on Figure 5-15 -# in "Digital and Analog Communication Systems", Leon W. Couch II -# -# -# R1 +# C # +-----||------+ # | | # o------+ +-----+--------o -# | C1 | | +# | R1 | | # +----/\/\/\/--+ \ # / # \ R2 @@ -95,28 +156,94 @@ class fm_deemph(gr.hier_block2): # | # o--------------------------+--------o # -# f1 = 1/(2*pi*t1) = 1/(2*pi*R1*C) +# (This fine ASCII rendition is based on Figure 5-15 +# in "Digital and Analog Communication Systems", Leon W. Couch II) +# +# Has this transfer function: # -# 1 R1 + R2 -# f2 = ------- = ------------ -# 2*pi*t2 2*pi*R1*R2*C +# 1 +# s + --- +# R1C +# H(s) = ------------------ +# 1 R1 +# s + --- (1 + --) +# R1C R2 # -# t1 is 75us in US, 50us in EUR -# f2 should be higher than our audio bandwidth. # +# It has a corner due to the numerator, where the rise starts, at # -# The Bode plot looks like this: +# |Hn(j w_cl)|^2 = 2*|Hn(0)|^2 => s = j w_cl = j (1/(R1C)) # +# It has a corner due to the denominator, where it levels off again, at # -# /---------------- -# / -# / <-- slope = 20dB/decade -# / -# -------------/ -# f1 f2 +# |Hn(j w_ch)|^2 = 1/2*|Hd(0)|^2 => s = j w_ch = j (1/(R1C) * (1 + R1/R2)) # -# We prewarp and use the bilinear z-transform to get our IIR coefficients. -# See "Digital Signal Processing: A Practical Approach" by Ifeachor and Jervis +# Historically, the corner frequency of analog audio preemphasis filters +# been specified by the R1C time constant used, called tau. +# +# So +# w_cl = 1/tau = 1/R1C; f_cl = 1/(2*pi*tau) = 1/(2*pi*R1*C) +# w_ch = 1/tau2 = (1+R1/R2)/R1C; f_ch = 1/(2*pi*tau2) = (1+R1/R2)/(2*pi*R1*C) +# +# and note f_ch = f_cl * (1 + R1/R2). +# +# For broadcast FM audio, tau is 75us in the United States and 50us in Europe. +# f_ch should be higher than our digital audio bandwidth. +# +# The Bode plot looks like this: +# +# +# /---------------- +# / +# / <-- slope = 20dB/decade +# / +# -------------/ +# f_cl f_ch +# +# In specifying tau for this digital preemphasis filter, tau specifies +# the *digital* corner frequency, w_cl, desired. +# +# The digital preemphasis filter design below, uses the +# "bilinear transformation" method of designing digital filters: +# +# 1. Convert digitial specifications into the analog domain, by prewarping +# digital frequency specifications into analog frequencies. +# +# w_a = (2/T)tan(wT/2) +# +# 2. Use an analog filter design technique to design the filter. +# +# 3. Use the bilinear transformation to convert the analog filter design to a +# digital filter design. +# +# H(z) = H(s)| +# s = (2/T)(1-z^-1)/(1+z^-1) +# +# +# -w_cla +# 1 + ------ +# 2 fs +# 1 - ------------ z^-1 +# -w_cla -w_cla +# 1 - ------ 1 - ------ +# 2 fs 2 fs +# H(z) = ------------ * ----------------------- +# -w_cha -w_cha +# 1 - ------ 1 + ------ +# 2 fs 2 fs +# 1 - ------------ z^-1 +# -w_cha +# 1 - ------ +# 2 fs +# +# We use this design technique, because it is an easy way to obtain a filter +# design with the 6 dB/octave rise required of the premphasis filter. +# +# Jackson, Leland B., _Digital_Filters_and_Signal_Processing_Second_Edition_, +# Kluwer Academic Publishers, 1989, pp 201-212 +# +# Orfanidis, Sophocles J., _Introduction_to_Signal_Processing_, Prentice Hall, +# 1996, pp 573-583 # @@ -124,21 +251,52 @@ class fm_preemph(gr.hier_block2): """ FM Preemphasis IIR filter. """ - def __init__(self, fs, tau=75e-6): + def __init__(self, fs, tau=75e-6, fh=-1.0): """ Args: fs: sampling frequency in Hz (float) tau: Time constant in seconds (75us in US, 50us in EUR) (float) + fh: High frequency at which to flatten out (< 0 means default of 0.925*fs/2.0) (float) """ - gr.hier_block2.__init__(self, "fm_deemph", + gr.hier_block2.__init__(self, "fm_preemph", gr.io_signature(1, 1, gr.sizeof_float), # Input signature gr.io_signature(1, 1, gr.sizeof_float)) # Output signature - # FIXME make this compute the right answer + # Set fh to something sensible, if needed. + # N.B. fh == fs/2.0 or fh == 0.0 results in a pole on the unit circle + # at z = -1.0 or z = 1.0 respectively. That makes the filter unstable + # and useless. + if fh <= 0.0 or fh >= fs/2.0: + fh = 0.925 * fs/2.0 + + # Digital corner frequencies + w_cl = 1.0 / tau + w_ch = 2.0 * math.pi * fh + + # Prewarped analog corner frequencies + w_cla = 2.0 * fs * math.tan(w_cl / (2.0 * fs)) + w_cha = 2.0 * fs * math.tan(w_ch / (2.0 * fs)) + + # Resulting digital pole, zero, and gain term from the bilinear + # transformation of H(s) = (s + w_cla) / (s + w_cha) to + # H(z) = b0 (1 - z1 z^-1)/(1 - p1 z^-1) + kl = -w_cla / (2.0 * fs) + kh = -w_cha / (2.0 * fs) + z1 = (1.0 + kl) / (1.0 - kl) + p1 = (1.0 + kh) / (1.0 - kh) + b0 = (1.0 - kl) / (1.0 - kh) + + # Since H(s = infinity) = 1.0, then H(z = -1) = 1.0 and + # this filter has 0 dB gain at fs/2.0. + # That isn't what users are going to expect, so adjust with a + # gain, g, so that H(z = 1) = 1.0 for 0 dB gain at DC. + w_0dB = 2.0 * math.pi * 0.0 + g = abs(1.0 - p1 * cmath.rect(1.0, -w_0dB)) \ + / (b0 * abs(1.0 - z1 * cmath.rect(1.0, -w_0dB))) - btaps = [1] - ataps = [1] + btaps = [ g * b0 * 1.0, g * b0 * -z1 ] + ataps = [ 1.0, -p1 ] if 0: print "btaps =", btaps @@ -146,5 +304,5 @@ class fm_preemph(gr.hier_block2): global plot2 plot2 = gru.gnuplot_freqz(gru.freqz(btaps, ataps), fs, True) - preemph = filter.iir_filter_ffd(btaps, ataps) + preemph = filter.iir_filter_ffd(btaps, ataps, False) self.connect(self, preemph, self) diff --git a/gr-analog/python/analog/nbfm_tx.py b/gr-analog/python/analog/nbfm_tx.py index ffd539ec55..aa6c1eccc7 100644 --- a/gr-analog/python/analog/nbfm_tx.py +++ b/gr-analog/python/analog/nbfm_tx.py @@ -29,7 +29,7 @@ except ImportError: import analog_swig as analog class nbfm_tx(gr.hier_block2): - def __init__(self, audio_rate, quad_rate, tau=75e-6, max_dev=5e3): + def __init__(self, audio_rate, quad_rate, tau=75e-6, max_dev=5e3, fh=-1.0): """ Narrow Band FM Transmitter. @@ -41,6 +41,7 @@ class nbfm_tx(gr.hier_block2): quad_rate: sample rate of output stream (integer) tau: preemphasis time constant (default 75e-6) (float) max_dev: maximum deviation in Hz (default 5e3) (float) + fh: high frequency at which to flatten preemphasis; < 0 means default of 0.925*quad_rate/2.0 (float) quad_rate must be an integer multiple of audio_rate. """ @@ -71,7 +72,7 @@ class nbfm_tx(gr.hier_block2): #print "len(interp_taps) =", len(interp_taps) self.interpolator = filter.interp_fir_filter_fff (interp_factor, interp_taps) - self.preemph = fm_preemph(quad_rate, tau=tau) + self.preemph = fm_preemph(quad_rate, tau=tau, fh=fh) k = 2 * math.pi * max_dev / quad_rate self.modulator = analog.frequency_modulator_fc(k) diff --git a/gr-analog/python/analog/wfm_tx.py b/gr-analog/python/analog/wfm_tx.py index be662310db..a1b589350d 100644 --- a/gr-analog/python/analog/wfm_tx.py +++ b/gr-analog/python/analog/wfm_tx.py @@ -30,7 +30,7 @@ except ImportError: import analog_swig as analog class wfm_tx(gr.hier_block2): - def __init__(self, audio_rate, quad_rate, tau=75e-6, max_dev=75e3): + def __init__(self, audio_rate, quad_rate, tau=75e-6, max_dev=75e3, fh=-1.0): """ Wide Band FM Transmitter. @@ -42,6 +42,7 @@ class wfm_tx(gr.hier_block2): quad_rate: sample rate of output stream (integer) tau: preemphasis time constant (default 75e-6) (float) max_dev: maximum deviation in Hz (default 75e3) (float) + fh: high frequency at which to flatten preemphasis; < 0 means default of 0.925*quad_rate/2.0 (float) quad_rate must be an integer multiple of audio_rate. """ @@ -71,7 +72,7 @@ class wfm_tx(gr.hier_block2): print "len(interp_taps) =", len(interp_taps) self.interpolator = filter.interp_fir_filter_fff (interp_factor, interp_taps) - self.preemph = fm_preemph(quad_rate, tau=tau) + self.preemph = fm_preemph(quad_rate, tau=tau, fh=fh) k = 2 * math.pi * max_dev / quad_rate self.modulator = analog.frequency_modulator_fc (k) diff --git a/gr-audio/grc/audio_sink.xml b/gr-audio/grc/audio_sink.xml index 727767b72e..9181055cd9 100644 --- a/gr-audio/grc/audio_sink.xml +++ b/gr-audio/grc/audio_sink.xml @@ -7,7 +7,7 @@ <block> <name>Audio Sink</name> <key>audio_sink</key> - <category>Audio</category> + <category>[Core]/Audio</category> <flags>throttle</flags> <import>from gnuradio import audio</import> <make>audio.sink($samp_rate, $device_name, $ok_to_block)</make> diff --git a/gr-audio/grc/audio_source.xml b/gr-audio/grc/audio_source.xml index aaa3225e8b..044b14466f 100644 --- a/gr-audio/grc/audio_source.xml +++ b/gr-audio/grc/audio_source.xml @@ -7,7 +7,7 @@ <block> <name>Audio Source</name> <key>audio_source</key> - <category>Audio</category> + <category>[Core]/Audio</category> <flags>throttle</flags> <import>from gnuradio import audio</import> <make>audio.source($samp_rate, $device_name, $ok_to_block)</make> diff --git a/gr-audio/lib/windows/windows_sink.cc b/gr-audio/lib/windows/windows_sink.cc index 6598c973d4..4ec798b0ba 100644 --- a/gr-audio/lib/windows/windows_sink.cc +++ b/gr-audio/lib/windows/windows_sink.cc @@ -27,6 +27,8 @@ #include "audio_registry.h" #include <windows_sink.h> #include <gnuradio/io_signature.h> +#include <gnuradio/prefs.h> +#include <gnuradio/logger.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> @@ -36,27 +38,29 @@ #include <stdexcept> #include <string> #include <sstream> +#include "boost/lexical_cast.hpp" namespace gr { namespace audio { sink::sptr windows_sink_fcn(int sampling_rate, - const std::string &device_name, - bool) + const std::string &device_name, + bool) { return sink::sptr (new windows_sink(sampling_rate, device_name)); } - static const double CHUNK_TIME = 0.1; //0.001; // 100 ms - - // FIXME these should query some kind of user preference + static const double CHUNK_TIME = prefs::singleton()->get_double("audio_windows", "period_time", 0.1); // 100 ms (below 3ms distortion will likely occur regardless of number of buffers, will likely be a higher limit on slower machines) + static const int nPeriods = prefs::singleton()->get_long("audio_windows", "nperiods", 4); // 4 should be more than enough with a normal chunk time (2 will likely work as well)... at 3ms chunks 10 was enough on a fast machine + static const bool verbose = prefs::singleton()->get_bool("audio_windows", "verbose", false); + static const std::string default_device = prefs::singleton()->get_string("audio_windows", "standard_output_device", "default"); static std::string default_device_name() { - return "WAVE_MAPPER"; + return (default_device == "default" ? "WAVE_MAPPER" : default_device); } windows_sink::windows_sink(int sampling_freq, const std::string device_name) @@ -65,28 +69,63 @@ namespace gr { io_signature::make(0, 0, 0)), d_sampling_freq(sampling_freq), d_device_name(device_name.empty() ? default_device_name() : device_name), - d_fd(-1), d_buffer(0), d_chunk_size(0) + d_fd(-1), d_buffers(0), d_chunk_size(0) { + /* Initialize the WAVEFORMATEX for 16-bit, 44KHz, stereo */ + wave_format.wFormatTag = WAVE_FORMAT_PCM; + wave_format.nChannels = 2; // changing this will require adjustments to the work routine. + wave_format.wBitsPerSample = 16; // changing this will necessitate changing buffer type from short. + wave_format.nSamplesPerSec = d_sampling_freq; // 44100 is default but up to flowgraph settings; + wave_format.nBlockAlign = + wave_format.nChannels * (wave_format.wBitsPerSample / 8); + wave_format.nAvgBytesPerSec = + wave_format.nSamplesPerSec * wave_format.nBlockAlign; + wave_format.cbSize = 0; + + d_chunk_size = (int)(d_sampling_freq * CHUNK_TIME); // Samples per chunk + set_output_multiple(d_chunk_size); + d_buffer_size = d_chunk_size * wave_format.nChannels * (wave_format.wBitsPerSample / 8); // room for 16-bit audio on two channels. + d_wave_write_event = CreateEvent(NULL, FALSE, FALSE, NULL); - if(open_waveout_device() < 0) { - //fprintf(stderr, "audio_windows_sink:open_waveout_device() failed\n"); + if (open_waveout_device() < 0) { perror("audio_windows_sink:open_waveout_device() failed\n"); throw - std::runtime_error ("audio_windows_sink:open_waveout_device() failed"); + std::runtime_error("audio_windows_sink:open_waveout_device() failed"); } - - d_chunk_size = (int)(d_sampling_freq * CHUNK_TIME); - set_output_multiple(d_chunk_size); - - d_buffer = new short[d_chunk_size * 2]; + else if (verbose) { + GR_LOG_INFO(logger, "Opened windows waveout device"); + } + d_buffers = new LPWAVEHDR[nPeriods]; + for (int i = 0; i < nPeriods; i++) + { + d_buffers[i] = new WAVEHDR; + d_buffers[i]->dwLoops = 0L; + d_buffers[i]->dwFlags = WHDR_DONE; + d_buffers[i]->dwBufferLength = d_buffer_size; + d_buffers[i]->lpData = new CHAR[d_buffer_size]; + } + if (verbose) GR_LOG_INFO(logger, boost::format("Initialized %1% %2%ms audio buffers, total memory used: %3$0.2fkB") % (nPeriods) % (CHUNK_TIME * 1000) % ((d_buffer_size * nPeriods) / 1024.0)); } windows_sink::~windows_sink() { + // stop playback and set all buffers to DONE. + waveOutReset(d_h_waveout); + // Now we can deallocate the buffers + for (int i = 0; i < nPeriods; i++) + { + if (d_buffers[i]->dwFlags & (WHDR_DONE | WHDR_PREPARED)) { + waveOutUnprepareHeader(d_h_waveout, d_buffers[i], sizeof(d_buffers[i])); + } + else { + + } + delete d_buffers[i]->lpData; + } /* Free the callback Event */ CloseHandle(d_wave_write_event); waveOutClose(d_h_waveout); - delete [] d_buffer; + delete [] d_buffers; } int @@ -95,66 +134,71 @@ namespace gr { gr_vector_void_star & output_items) { const float *f0, *f1; - bool playtestsound = false; - if(playtestsound) { - // dummy - f0 = (const float*)input_items[0]; - for(int i = 0; i < noutput_items; i += d_chunk_size) { - for(int j = 0; j < d_chunk_size; j++) { - d_buffer[2*j + 0] = (short)(sin(2.0 * 3.1415926535897932384626 * - (float)j * 1000.0 / (float)d_sampling_freq) * - 8192 + 0); //+32767 - d_buffer[2*j + 1] = d_buffer[2*j + 0]; + // Pick the first available wave header (buffer) + // If none available, then wait until the processing event if fired and check again + // Not all events free up a buffer, so it could take more than one loop to get one + // however, to avoid a lock, only wait 1 second for a freed up buffer then abort. + LPWAVEHDR chosen_header = NULL; + int c = 0; + while (!chosen_header) + { + ResetEvent(d_wave_write_event); + for (int i = 0; i < nPeriods; i++) + { + if (d_buffers[i]->dwFlags & WHDR_DONE) { + // uncomment the below to see which buffers are being consumed + // printf("%d ", i); + chosen_header = d_buffers[i]; + break; } - f0 += d_chunk_size; - if(write_waveout - ((HPSTR)d_buffer, 2*d_chunk_size * sizeof(short)) < 0) { - fprintf(stderr, "audio_windows_sink: write failed\n"); - perror("audio_windows_sink: write failed"); + } + if (!chosen_header) { + WaitForSingleObject(d_wave_write_event, 100); + printf("aO"); + } + if (c++ > 10) { + for (int i = 0; i < nPeriods; i++) { + printf("%d: %d\n", i, d_buffers[i]->dwFlags); } - } - // break; + perror("audio_windows_sink: no audio buffers available"); + return -1; + } } - else { - switch(input_items.size ()) { - case 1: // mono input - f0 = (const float*)input_items[0]; - - for(int i = 0; i < noutput_items; i += d_chunk_size) { - for(int j = 0; j < d_chunk_size; j++) { - d_buffer[2*j + 0] = (short)(f0[j] * 32767); - d_buffer[2*j + 1] = (short)(f0[j] * 32767); - } - f0 += d_chunk_size; - if(write_waveout - ((HPSTR)d_buffer, 2*d_chunk_size * sizeof(short)) < 0) { - //fprintf(stderr, "audio_windows_sink: write failed\n"); - perror("audio_windows_sink: write failed"); - } - } - break; - case 2: // stereo input - f0 = (const float*)input_items[0]; - f1 = (const float*)input_items[1]; + short *d_buffer = (short *)chosen_header->lpData; - for(int i = 0; i < noutput_items; i += d_chunk_size) { - for(int j = 0; j < d_chunk_size; j++) { - d_buffer[2*j + 0] = (short)(f0[j] * 32767); - d_buffer[2*j + 1] = (short)(f1[j] * 32767); - } - f0 += d_chunk_size; - f1 += d_chunk_size; - if(write_waveout - ((HPSTR)d_buffer, 2*d_chunk_size * sizeof(short)) < 0) { - //fprintf(stderr, "audio_windows_sink: write failed\n"); - perror("audio_windows_sink: write failed"); - } + switch (input_items.size()) { + case 1: // mono input + f0 = (const float*)input_items[0]; + + for (int i = 0; i < noutput_items; i += d_chunk_size) { + for (int j = 0; j < d_chunk_size; j++) { + d_buffer[2 * j + 0] = (short)(f0[j] * 32767); + d_buffer[2 * j + 1] = (short)(f0[j] * 32767); } - break; - } + f0 += d_chunk_size; + } + break; + case 2: // stereo input + f0 = (const float*)input_items[0]; + f1 = (const float*)input_items[1]; + + for (int i = 0; i < noutput_items; i += d_chunk_size) { + for (int j = 0; j < d_chunk_size; j++) { + d_buffer[2 * j + 0] = (short)(f0[j] * 32767); + d_buffer[2 * j + 1] = (short)(f1[j] * 32767); + } + f0 += d_chunk_size; + f1 += d_chunk_size; + } + break; + } + if (write_waveout + (chosen_header) < 0) { + perror("audio_windows_sink: write failed"); } + return noutput_items; } @@ -162,154 +206,162 @@ namespace gr { windows_sink::string_to_int(const std::string & s) { int i; - std::istringstream (s) >> i; + std::istringstream(s) >> i; return i; - } //ToInt() + } - int - windows_sink::open_waveout_device(void) + MMRESULT windows_sink::is_format_supported(LPWAVEFORMATEX pwfx, UINT uDeviceID) { - UINT /*UINT_PTR */ u_device_id; - - /** Identifier of the waveform-audio output device to open. It - can be either a device identifier or a handle of an open - waveform-audio input device. You can use the following flag - instead of a device identifier. - * - * Value Meaning - * WAVE_MAPPER The function selects a waveform-audio output - * device capable of playing the given format. - */ - if(d_device_name.empty () || default_device_name () == d_device_name) - u_device_id = WAVE_MAPPER; - else - u_device_id = (UINT) string_to_int (d_device_name); - // Open a waveform device for output using event callback. - - unsigned long result; - //HWAVEOUT outHandle; - WAVEFORMATEX wave_format; + return (waveOutOpen( + NULL, // ptr can be NULL for query + uDeviceID, // the device identifier + pwfx, // defines requested format + NULL, // no callback + NULL, // no instance data + WAVE_FORMAT_QUERY)); // query only, do not open device + } - /* Initialize the WAVEFORMATEX for 16-bit, 44KHz, stereo */ - wave_format.wFormatTag = WAVE_FORMAT_PCM; - wave_format.nChannels = 2; - wave_format.nSamplesPerSec = d_sampling_freq; //44100; - wave_format.wBitsPerSample = 16; - wave_format.nBlockAlign = - wave_format.nChannels * (wave_format.wBitsPerSample / 8); - wave_format.nAvgBytesPerSec = - wave_format.nSamplesPerSec * wave_format.nBlockAlign; - wave_format.cbSize = 0; + bool windows_sink::is_number(const std::string& s) + { + std::string::const_iterator it = s.begin(); + while (it != s.end() && std::isdigit(*it)) ++it; + return !s.empty() && it == s.end(); + } - /* Open the (preferred) Digital Audio Out device. */ - result = waveOutOpen(&d_h_waveout, WAVE_MAPPER, - &wave_format, - (DWORD_PTR)d_wave_write_event, - 0, CALLBACK_EVENT | WAVE_ALLOWSYNC); - //|WAVE_FORMAT_DIRECT | CALLBACK_EVENT| WAVE_ALLOWSYNC + UINT windows_sink::find_device(std::string szDeviceName) + { + UINT result = -1; + UINT num_devices = waveOutGetNumDevs(); + if (num_devices > 0) { + // what the device name passed as a number? + if (is_number(szDeviceName)) + { + // a number, so must be referencing a device ID (which incremement from zero) + UINT num = std::stoul(szDeviceName); + if (num < num_devices) { + result = num; + } + else { + GR_LOG_INFO(logger, boost::format("Warning: waveOut deviceID %d was not found, defaulting to WAVE_MAPPER") % num); + result = WAVE_MAPPER; + } - if(result) { - //fprintf(stderr, "audio_windows_sink: Failed to open waveform output device.\n"); - perror("audio_windows_sink: Failed to open waveform output device."); - //LocalUnlock(hFormat); - //LocalFree(hFormat); - //mmioClose(hmmio, 0); - return -1; + } + else { + // device name passed as string + for (UINT i = 0; i < num_devices; i++) + { + WAVEOUTCAPS woc; + if (waveOutGetDevCaps(i, &woc, sizeof(woc)) != MMSYSERR_NOERROR) + { + perror("Error: Could not retrieve wave out device capabilities for device"); + return -1; + } + if (woc.szPname == szDeviceName) + { + result = i; + } + if (verbose) GR_LOG_INFO(logger, boost::format("WaveOut Device %d: %s") % i % woc.szPname); + } + if (result == -1) { + GR_LOG_INFO(logger, boost::format("Warning: waveOut device '%s' was not found, defaulting to WAVE_MAPPER") % szDeviceName); + result = WAVE_MAPPER; + } + } + } + else { + perror("Error: No WaveOut devices present or accessible"); } + return result; + } - // - // Do not Swallow the "open" event. - // - //WaitForSingleObject(d_wave_write_event, INFINITE); + int + windows_sink::open_waveout_device(void) + { + UINT u_device_id; + unsigned long result; - // Allocate and lock memory for the header. + /** Identifier of the waveform-audio output device to open. It + can be either a device identifier or a handle of an open + waveform-audio input device. You can use the following flag + instead of a device identifier. + WAVE_MAPPER The function selects a waveform-audio output + device capable of playing the given format. + */ + if (d_device_name.empty() || default_device_name() == d_device_name) + u_device_id = WAVE_MAPPER; + else + // The below could be uncommented to allow selection of different device handles + // however it is unclear what other devices are out there and how a user + // would know the device ID so at the moment we will ignore that setting + // and stick with WAVE_MAPPER + u_device_id = find_device(d_device_name); + if (verbose) GR_LOG_INFO(logger, boost::format("waveOut Device ID: %1%") % (u_device_id)); - d_h_wave_hdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, - (DWORD)sizeof(WAVEHDR)); - if(d_h_wave_hdr == NULL) { - //GlobalUnlock(hData); - //GlobalFree(hData); - //fprintf(stderr, "audio_windows_sink: Not enough memory for header.\n"); - perror("audio_windows_sink: Not enough memory for header."); + // Check if the sampling rate/bits/channels are good to go with the device. + MMRESULT supported = is_format_supported(&wave_format, u_device_id); + if (supported != MMSYSERR_NOERROR) { + char err_msg[50]; + waveOutGetErrorText(supported, err_msg, 50); + GR_LOG_INFO(logger, boost::format("format error: %s") % err_msg); + perror("audio_windows_sink: Requested audio format is not supported by device driver"); return -1; } - d_lp_wave_hdr = (LPWAVEHDR)GlobalLock(d_h_wave_hdr); - if(d_lp_wave_hdr == NULL) { - //GlobalUnlock(hData); - //GlobalFree(hData); - //fprintf(stderr, "audio_windows_sink: Failed to lock memory for header.\n"); - perror("audio_windows_sink: Failed to lock memory for header."); + // Open a waveform device for output using event callback. + result = waveOutOpen(&d_h_waveout, u_device_id, + &wave_format, + (DWORD_PTR)d_wave_write_event, + 0, CALLBACK_EVENT | WAVE_ALLOWSYNC); + + if (result) { + perror("audio_windows_sink: Failed to open waveform output device."); return -1; } - //d_lp_wave_hdr->dwFlags = WHDR_DONE; return 0; } int - windows_sink::write_waveout(HPSTR lp_data, DWORD dw_data_size) + windows_sink::write_waveout(LPWAVEHDR lp_wave_hdr) { UINT w_result; - int teller = 100; - // After allocation, set up and prepare header. - /*while ((d_lp_wave_hdr->dwFlags & WHDR_DONE)==0 && teller>0) { - teller--; - Sleep(1); - } */ - // Wait until previous wave write completes (first event is the open event). - WaitForSingleObject(d_wave_write_event, 100); // INFINITE - d_lp_wave_hdr->lpData = lp_data; - d_lp_wave_hdr->dwBufferLength = dw_data_size; - d_lp_wave_hdr->dwFlags = 0L; + /* Clear the WHDR_DONE bit (which the driver set last time that - this WAVEHDR was sent via waveOutWrite and was played). Some - drivers need this to be cleared */ - //d_lp_wave_hdr->dwFlags &= ~WHDR_DONE; + this WAVEHDR was sent via waveOutWrite and was played). Some + drivers need this to be cleared */ + lp_wave_hdr->dwFlags = 0L; - d_lp_wave_hdr->dwLoops = 0L; w_result = - waveOutPrepareHeader(d_h_waveout, d_lp_wave_hdr, sizeof(WAVEHDR)); - if(w_result != 0) { - //GlobalUnlock(hData); - //GlobalFree(hData); - //fprintf(stderr, "audio_windows_sink: Failed to waveOutPrepareHeader. error %i\n",w_result); + waveOutPrepareHeader(d_h_waveout, lp_wave_hdr, sizeof(WAVEHDR)); + if (w_result != 0) { perror("audio_windows_sink: Failed to waveOutPrepareHeader"); + return -1; } - // Now the data block can be sent to the output device. The - // waveOutWrite function returns immediately and waveform - // data is sent to the output device in the background. - //while(!readyforplayback) Sleep(1); - //readyforplayback=false; - - w_result = waveOutWrite(d_h_waveout, d_lp_wave_hdr, sizeof(WAVEHDR)); - if(w_result != 0) { - //GlobalUnlock(hData); - //GlobalFree(hData); - //fprintf(stderr, "audio_windows_sink: Failed to write block to device.error %i\n",w_result); + + w_result = waveOutWrite(d_h_waveout, lp_wave_hdr, sizeof(WAVEHDR)); + if (w_result != 0) { perror("audio_windows_sink: Failed to write block to device"); - switch(w_result) { - case MMSYSERR_INVALHANDLE: - fprintf(stderr, "Specified device handle is invalid.\n"); - break; - case MMSYSERR_NODRIVER: - fprintf(stderr, " No device driver is present.\n"); - break; - case MMSYSERR_NOMEM: - fprintf(stderr, " Unable to allocate or lock memory.\n"); - break; - case WAVERR_UNPREPARED: - fprintf(stderr, - " The data block pointed to by the pwh parameter hasn't been prepared.\n"); - break; - default: - fprintf(stderr, "Unknown error %i\n", w_result); - } - waveOutUnprepareHeader(d_h_waveout, d_lp_wave_hdr, sizeof(WAVEHDR)); + switch (w_result) { + case MMSYSERR_INVALHANDLE: + fprintf(stderr, "Specified device handle is invalid.\n"); + break; + case MMSYSERR_NODRIVER: + fprintf(stderr, " No device driver is present.\n"); + break; + case MMSYSERR_NOMEM: + fprintf(stderr, " Unable to allocate or lock memory.\n"); + break; + case WAVERR_UNPREPARED: + fprintf(stderr, + " The data block pointed to by the pwh parameter hasn't been prepared.\n"); + break; + default: + fprintf(stderr, "Unknown error %i\n", w_result); + } + waveOutUnprepareHeader(d_h_waveout, lp_wave_hdr, sizeof(WAVEHDR)); return -1; } - //WaitForSingleObject(d_wave_write_event, INFINITE); return 0; } - } /* namespace audio */ } /* namespace gr */ diff --git a/gr-audio/lib/windows/windows_sink.h b/gr-audio/lib/windows/windows_sink.h index 3d21cc47a3..2bfdbd318d 100644 --- a/gr-audio/lib/windows/windows_sink.h +++ b/gr-audio/lib/windows/windows_sink.h @@ -47,17 +47,20 @@ namespace gr { int d_sampling_freq; std::string d_device_name; int d_fd; - short *d_buffer; - int d_chunk_size; + LPWAVEHDR *d_buffers; + DWORD d_chunk_size; + DWORD d_buffer_size; HWAVEOUT d_h_waveout; - HGLOBAL d_h_wave_hdr; - LPWAVEHDR d_lp_wave_hdr; HANDLE d_wave_write_event; + WAVEFORMATEX wave_format; protected: int string_to_int(const std::string & s); int open_waveout_device(void); - int write_waveout(HPSTR lp_data, DWORD dw_data_size); + int write_waveout(LPWAVEHDR lp_wave_hdr); + MMRESULT is_format_supported(LPWAVEFORMATEX pwfx, UINT uDeviceID); + bool is_number(const std::string& s); + UINT find_device(std::string szDeviceName); public: windows_sink(int sampling_freq, diff --git a/gr-blocks/CMakeLists.txt b/gr-blocks/CMakeLists.txt index 13b41a2e3c..685f5736fb 100644 --- a/gr-blocks/CMakeLists.txt +++ b/gr-blocks/CMakeLists.txt @@ -87,6 +87,7 @@ add_subdirectory(lib) #endif(ENABLE_TESTING) if(ENABLE_PYTHON) add_subdirectory(python/blocks) + add_subdirectory(python/grc_gnuradio) add_subdirectory(swig) add_subdirectory(grc) add_subdirectory(doc) diff --git a/grc/blocks/blks2_error_rate.xml b/gr-blocks/grc/blks2_error_rate.xml index 91a303206d..c71739f914 100644 --- a/grc/blocks/blks2_error_rate.xml +++ b/gr-blocks/grc/blks2_error_rate.xml @@ -8,6 +8,7 @@ <block> <name>Error Rate</name> <key>blks2_error_rate</key> + <category>[Core]/Deprecated</category> <import>from grc_gnuradio import blks2 as grc_blks2</import> <make>grc_blks2.error_rate( type=$type, diff --git a/grc/blocks/blks2_selector.xml b/gr-blocks/grc/blks2_selector.xml index 2d89df1860..3442b2cdb8 100644 --- a/grc/blocks/blks2_selector.xml +++ b/gr-blocks/grc/blks2_selector.xml @@ -8,6 +8,7 @@ <block> <name>Selector</name> <key>blks2_selector</key> + <category>[Core]/Deprecated</category> <import>from grc_gnuradio import blks2 as grc_blks2</import> <make>grc_blks2.selector( item_size=$type.size*$vlen, diff --git a/grc/blocks/blks2_tcp_sink.xml b/gr-blocks/grc/blks2_tcp_sink.xml index cfe7b42d84..2bff7e6d45 100644 --- a/grc/blocks/blks2_tcp_sink.xml +++ b/gr-blocks/grc/blks2_tcp_sink.xml @@ -7,6 +7,7 @@ <block> <name>TCP Sink</name> <key>blks2_tcp_sink</key> + <category>[Core]/Deprecated</category> <import>from grc_gnuradio import blks2 as grc_blks2</import> <make>grc_blks2.tcp_sink( itemsize=$type.size*$vlen, diff --git a/grc/blocks/blks2_tcp_source.xml b/gr-blocks/grc/blks2_tcp_source.xml index 6bf742aa00..f6cc41015f 100644 --- a/grc/blocks/blks2_tcp_source.xml +++ b/gr-blocks/grc/blks2_tcp_source.xml @@ -7,6 +7,7 @@ <block> <name>TCP Source</name> <key>blks2_tcp_source</key> + <category>[Core]/Deprecated</category> <import>from grc_gnuradio import blks2 as grc_blks2</import> <make>grc_blks2.tcp_source( itemsize=$type.size*$vlen, diff --git a/grc/blocks/blks2_valve.xml b/gr-blocks/grc/blks2_valve.xml index 47c553523f..c3f25163b2 100644 --- a/grc/blocks/blks2_valve.xml +++ b/gr-blocks/grc/blks2_valve.xml @@ -8,6 +8,7 @@ <block> <name>Valve</name> <key>blks2_valve</key> + <category>[Core]/Deprecated</category> <import>from grc_gnuradio import blks2 as grc_blks2</import> <make>grc_blks2.valve(item_size=$type.size*$vlen, open=bool($open))</make> <callback>set_open(bool($open))</callback> diff --git a/gr-blocks/grc/blocks_block_tree.xml b/gr-blocks/grc/blocks_block_tree.xml index f35815b4dd..a9441a9ffb 100644 --- a/gr-blocks/grc/blocks_block_tree.xml +++ b/gr-blocks/grc/blocks_block_tree.xml @@ -27,9 +27,9 @@ ################################################### --> <cat> - <name></name> <!-- Blank for Root Name --> + <name>[Core]</name> <cat> - <name>Audio</name> + <name>Audio</name> <block>blocks_wavfile_source</block> <block>blocks_wavfile_sink</block> </cat> @@ -144,6 +144,8 @@ <block>blocks_null_sink</block> <block>blocks_copy</block> <block>blocks_nop</block> + <block>xmlrpc_server</block> + <block>xmlrpc_client</block> </cat> <cat> <name>Modulators</name> diff --git a/gr-blocks/grc/blocks_repeat.xml b/gr-blocks/grc/blocks_repeat.xml index c6c17c9900..793d0148b2 100644 --- a/gr-blocks/grc/blocks_repeat.xml +++ b/gr-blocks/grc/blocks_repeat.xml @@ -9,6 +9,7 @@ <key>blocks_repeat</key> <import>from gnuradio import blocks</import> <make>blocks.repeat($type.size*$vlen, $interp)</make> + <callback>set_interpolation($interp)</callback> <param> <name>Type</name> <key>type</key> diff --git a/grc/blocks/xmlrpc_client.xml b/gr-blocks/grc/xmlrpc_client.xml index dc4d154d14..dc4d154d14 100644 --- a/grc/blocks/xmlrpc_client.xml +++ b/gr-blocks/grc/xmlrpc_client.xml diff --git a/grc/blocks/xmlrpc_server.xml b/gr-blocks/grc/xmlrpc_server.xml index 602d444161..602d444161 100644 --- a/grc/blocks/xmlrpc_server.xml +++ b/gr-blocks/grc/xmlrpc_server.xml diff --git a/gr-blocks/include/gnuradio/blocks/repeat.h b/gr-blocks/include/gnuradio/blocks/repeat.h index 622b066017..b34bda1ec5 100644 --- a/gr-blocks/include/gnuradio/blocks/repeat.h +++ b/gr-blocks/include/gnuradio/blocks/repeat.h @@ -32,6 +32,10 @@ namespace gr { /*! * \brief repeat each input \p repeat times * \ingroup stream_operators_blk + * + * Message Ports: + * * interpolation (in): + * Takes a pmt_pair(pmt::mp("interpolation"), pmt_long interp), setting the interpolation to interp. */ class BLOCKS_API repeat : virtual public sync_interpolator { @@ -46,6 +50,20 @@ namespace gr { * \param repeat number of times to repeat the input */ static sptr make(size_t itemsize, int repeat); + + /*! + * \brief Return current interpolation + */ + virtual int interpolation() const = 0; + + /*! + * \brief sets the interpolation + * + * Call this method in a callback to adjust the interpolation at run time. + * + * \param interp interpolation to be set + */ + virtual void set_interpolation(int interp) = 0; }; } /* namespace blocks */ diff --git a/gr-blocks/include/gnuradio/blocks/vector_sink_X.h.t b/gr-blocks/include/gnuradio/blocks/vector_sink_X.h.t index 527ebf7dff..a4ef38fd04 100644 --- a/gr-blocks/include/gnuradio/blocks/vector_sink_X.h.t +++ b/gr-blocks/include/gnuradio/blocks/vector_sink_X.h.t @@ -43,6 +43,7 @@ namespace gr { static sptr make(int vlen = 1); + //! Clear the data and tags containers. virtual void reset() = 0; virtual std::vector<@TYPE@> data() const = 0; virtual std::vector<tag_t> tags() const = 0; diff --git a/gr-blocks/include/gnuradio/blocks/vector_source_X.h.t b/gr-blocks/include/gnuradio/blocks/vector_source_X.h.t index d5298e8b47..b0ca6b869f 100644 --- a/gr-blocks/include/gnuradio/blocks/vector_source_X.h.t +++ b/gr-blocks/include/gnuradio/blocks/vector_source_X.h.t @@ -76,6 +76,7 @@ namespace gr { virtual void rewind() = 0; virtual void set_data(const std::vector<@TYPE@> &data, const std::vector<tag_t> &tags=std::vector<tag_t>()) = 0; + virtual void set_repeat(bool repeat) = 0; }; } /* namespace blocks */ diff --git a/gr-blocks/lib/repeat_impl.cc b/gr-blocks/lib/repeat_impl.cc index 9c2ccc63c8..fb62265134 100644 --- a/gr-blocks/lib/repeat_impl.cc +++ b/gr-blocks/lib/repeat_impl.cc @@ -43,6 +43,26 @@ namespace gr { d_itemsize(itemsize), d_interp(interp) { + message_port_register_in(pmt::mp("interpolation")); + set_msg_handler(pmt::mp("interpolation"), + boost::bind(&repeat_impl::msg_set_interpolation, this, _1)); + } + + void + repeat_impl::msg_set_interpolation(pmt::pmt_t msg) + { + // Dynamization by Kevin McQuiggin: + d_interp = pmt::to_long(pmt::cdr(msg)); + sync_interpolator::set_interpolation(d_interp); + } + void + repeat_impl::set_interpolation(int interp) + { + // This ensures that interpolation is only changed between calls to work + // (and not in the middle of an ongoing work) + _post( pmt::mp("interpolation"), /* port */ + pmt::cons(pmt::mp("interpolation"), pmt::from_long(interp)) /* pair */ + ); } int diff --git a/gr-blocks/lib/repeat_impl.h b/gr-blocks/lib/repeat_impl.h index 1942729194..486a47a1ad 100644 --- a/gr-blocks/lib/repeat_impl.h +++ b/gr-blocks/lib/repeat_impl.h @@ -36,9 +36,15 @@ namespace gr { public: repeat_impl(size_t itemsize, int d_interp); + int interpolation() const { return d_interp; } + void set_interpolation(int interp); + + int work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); + private: + void msg_set_interpolation(pmt::pmt_t msg); }; } /* namespace blocks */ diff --git a/gr-blocks/lib/tuntap_pdu_impl.cc b/gr-blocks/lib/tuntap_pdu_impl.cc index 45995e4803..391b33937a 100644 --- a/gr-blocks/lib/tuntap_pdu_impl.cc +++ b/gr-blocks/lib/tuntap_pdu_impl.cc @@ -76,6 +76,14 @@ namespace gr { if (d_fd <= 0) throw std::runtime_error("gr::tuntap_pdu::make: tun_alloc failed (are you running as root?)"); + int err = set_mtu(dev_cstr, MTU); + if(err < 0) + std::cerr << boost::format( + "gr::tuntap_pdu: failed to set MTU to %d.\n" + "You should use ifconfig to set the MTU. E.g.,\n" + " $ sudo ifconfig %s mtu %d\n" + ) % MTU % dev % MTU << std::endl; + std::cout << boost::format( "Allocated virtual ethernet interface: %s\n" "You must now use ifconfig to set its IP address. E.g.,\n" @@ -140,6 +148,31 @@ namespace gr { */ return fd; } + + int + tuntap_pdu_impl::set_mtu(const char *dev, int MTU) + { + struct ifreq ifr; + int sfd, err; + + /* MTU must be set by passing a socket fd to ioctl; + * create an arbitrary socket for this purpose + */ + if ((sfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + return sfd; + + /* preparation of the struct ifr, of type "struct ifreq" */ + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + ifr.ifr_addr.sa_family = AF_INET; /* address family */ + ifr.ifr_mtu = MTU; + + /* try to set MTU */ + if ((err = ioctl(sfd, SIOCSIFMTU, (void *) &ifr)) < 0) + return err; + + return MTU; + } #endif } /* namespace blocks */ diff --git a/gr-blocks/lib/tuntap_pdu_impl.h b/gr-blocks/lib/tuntap_pdu_impl.h index 360f954630..3a53e3acb5 100644 --- a/gr-blocks/lib/tuntap_pdu_impl.h +++ b/gr-blocks/lib/tuntap_pdu_impl.h @@ -40,6 +40,7 @@ namespace gr { std::string d_dev; bool d_istunflag; int tun_alloc(char *dev, int flags); + int set_mtu(const char *dev, int MTU); public: tuntap_pdu_impl(std::string dev, int MTU, bool istunflag); diff --git a/gr-blocks/lib/udp_source_impl.cc b/gr-blocks/lib/udp_source_impl.cc index c3705d8f66..ea2f2b60a0 100644 --- a/gr-blocks/lib/udp_source_impl.cc +++ b/gr-blocks/lib/udp_source_impl.cc @@ -27,6 +27,7 @@ #include "udp_source_impl.h" #include <gnuradio/io_signature.h> #include <gnuradio/math.h> +#include <gnuradio/prefs.h> #include <stdexcept> #include <errno.h> #include <stdio.h> @@ -35,7 +36,8 @@ namespace gr { namespace blocks { - const int udp_source_impl::BUF_SIZE_PAYLOADS = 50; + const int udp_source_impl::BUF_SIZE_PAYLOADS = + gr::prefs::singleton()->get_long("udp_blocks", "buf_size_payloads", 50); udp_source::sptr udp_source::make(size_t itemsize, diff --git a/gr-blocks/lib/vector_sink_X_impl.h.t b/gr-blocks/lib/vector_sink_X_impl.h.t index b5d3bd6432..86f0e8773c 100644 --- a/gr-blocks/lib/vector_sink_X_impl.h.t +++ b/gr-blocks/lib/vector_sink_X_impl.h.t @@ -41,7 +41,7 @@ namespace gr { @NAME_IMPL@(int vlen); ~@NAME_IMPL@(); - void reset() { d_data.clear(); } + void reset() { d_data.clear(); d_tags.clear(); } std::vector<@TYPE@> data() const; std::vector<tag_t> tags() const; diff --git a/gr-blocks/lib/vector_source_X_impl.h.t b/gr-blocks/lib/vector_source_X_impl.h.t index 2641c6661b..bc9b329d8f 100644 --- a/gr-blocks/lib/vector_source_X_impl.h.t +++ b/gr-blocks/lib/vector_source_X_impl.h.t @@ -50,6 +50,7 @@ namespace gr { void rewind() { d_offset=0; } void set_data(const std::vector<@TYPE@> &data, const std::vector<tag_t> &tags); + void set_repeat(bool repeat) { d_repeat=repeat; }; int work(int noutput_items, gr_vector_const_void_star &input_items, diff --git a/gr-blocks/python/blocks/qa_block_behavior.py b/gr-blocks/python/blocks/qa_block_behavior.py new file mode 100644 index 0000000000..a21e423b21 --- /dev/null +++ b/gr-blocks/python/blocks/qa_block_behavior.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# +# Copyright 2016 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, blocks + +class test_block_behavior(gr_unittest.TestCase): + + def setUp(self): + self.tb = gr.top_block() + + def tearDown(self): + self.tb = None + + def test_000(self): + ''' + Tests the max noutput sizes set by the scheduler. When creating + the block, there is no block_detail and so the max buffer size + is 0. When the top_block is run, it builds the detail and + buffers and sets the max value. test_0001 tests when the + max_noutput_items is set by hand. + + ''' + + src = blocks.null_source(gr.sizeof_float) + op = blocks.head(gr.sizeof_float, 100) + snk = blocks.null_sink(gr.sizeof_float) + + maxn_pre = op.max_noutput_items() + + self.tb.connect(src, op, snk) + self.tb.run() + + maxn_post = op.max_noutput_items() + + self.assertEqual(maxn_pre, 0) + self.assertEqual(maxn_post, 16384) + + def test_001(self): + ''' + Tests the max noutput size when being explicitly set. + ''' + + src = blocks.null_source(gr.sizeof_float) + op = blocks.head(gr.sizeof_float, 100) + snk = blocks.null_sink(gr.sizeof_float) + + op.set_max_noutput_items(1024) + + maxn_pre = op.max_noutput_items() + + self.tb.connect(src, op, snk) + self.tb.run() + + maxn_post = op.max_noutput_items() + + self.assertEqual(maxn_pre, 1024) + self.assertEqual(maxn_post, 1024) + +if __name__ == '__main__': + gr_unittest.run(test_block_behavior, "test_block_behavior.xml") diff --git a/gr-blocks/python/blocks/qa_vector_sink_source.py b/gr-blocks/python/blocks/qa_vector_sink_source.py index 5dab7014cd..026713f5f4 100755 --- a/gr-blocks/python/blocks/qa_vector_sink_source.py +++ b/gr-blocks/python/blocks/qa_vector_sink_source.py @@ -46,6 +46,7 @@ class test_vector_sink_source(gr_unittest.TestCase): self.tb = None def test_001(self): + # Test that sink has data set in source for the simplest case src_data = [float(x) for x in range(16)] expected_result = tuple(src_data) @@ -58,6 +59,7 @@ class test_vector_sink_source(gr_unittest.TestCase): self.assertEqual(expected_result, result_data) def test_002(self): + # Test vectors (the gnuradio vector I/O type) src_data = [float(x) for x in range(16)] expected_result = tuple(src_data) @@ -70,11 +72,14 @@ class test_vector_sink_source(gr_unittest.TestCase): self.assertEqual(expected_result, result_data) def test_003(self): + # Test that we can only make vectors (the I/O type) if the input + # vector has sufficient size src_data = [float(x) for x in range(16)] expected_result = tuple(src_data) self.assertRaises(RuntimeError, lambda : blocks.vector_source_f(src_data, False, 3)) def test_004(self): + # Test sending and receiving tagged streams src_data = [float(x) for x in range(16)] expected_result = tuple(src_data) src_tags = tuple([make_tag('key', 'val', 0, 'src')]) @@ -92,6 +97,7 @@ class test_vector_sink_source(gr_unittest.TestCase): self.assertTrue(compare_tags(expected_tags[0], result_tags[0])) def test_005(self): + # Test that repeat works (with tagged streams) length = 16 src_data = [float(x) for x in range(length)] expected_result = tuple(src_data + src_data) @@ -112,6 +118,36 @@ class test_vector_sink_source(gr_unittest.TestCase): self.assertTrue(compare_tags(expected_tags[0], result_tags[0])) self.assertTrue(compare_tags(expected_tags[1], result_tags[1])) + def test_006(self): + # Test set_data + src_data = [float(x) for x in range(16)] + expected_result = tuple(src_data) + + src = blocks.vector_source_f((3,1,4)) + dst = blocks.vector_sink_f() + src.set_data(src_data) + + self.tb.connect(src, dst) + self.tb.run() + result_data = dst.data() + self.assertEqual(expected_result, result_data) + + def test_007(self): + # Test set_repeat + src_data = [float(x) for x in range(16)] + expected_result = tuple(src_data) + + src = blocks.vector_source_f(src_data, True) + dst = blocks.vector_sink_f() + src.set_repeat(False) + + self.tb.connect(src, dst) + # will timeout if set_repeat does not work + self.tb.run() + result_data = dst.data() + self.assertEqual(expected_result, result_data) + + if __name__ == '__main__': gr_unittest.run(test_vector_sink_source, "test_vector_sink_source.xml") diff --git a/grc/grc_gnuradio/CMakeLists.txt b/gr-blocks/python/grc_gnuradio/CMakeLists.txt index e992a60a39..9ff1240997 100644 --- a/grc/grc_gnuradio/CMakeLists.txt +++ b/gr-blocks/python/grc_gnuradio/CMakeLists.txt @@ -18,18 +18,20 @@ # Boston, MA 02110-1301, USA. ######################################################################## + +include(GrPython) + GR_PYTHON_INSTALL( FILES __init__.py DESTINATION ${GR_PYTHON_DIR}/grc_gnuradio - COMPONENT "grc" + COMPONENT "blocks_python" ) GR_PYTHON_INSTALL(FILES blks2/__init__.py blks2/error_rate.py - blks2/packet.py blks2/selector.py blks2/tcp.py DESTINATION ${GR_PYTHON_DIR}/grc_gnuradio/blks2 - COMPONENT "grc" + COMPONENT "blocks_python" ) diff --git a/grc/grc_gnuradio/README b/gr-blocks/python/grc_gnuradio/README index 897eed65ca..897eed65ca 100644 --- a/grc/grc_gnuradio/README +++ b/gr-blocks/python/grc_gnuradio/README diff --git a/grc/grc_gnuradio/__init__.py b/gr-blocks/python/grc_gnuradio/__init__.py index 8b13789179..8b13789179 100644 --- a/grc/grc_gnuradio/__init__.py +++ b/gr-blocks/python/grc_gnuradio/__init__.py diff --git a/grc/grc_gnuradio/blks2/__init__.py b/gr-blocks/python/grc_gnuradio/blks2/__init__.py index e6941ab91b..d3c8210834 100644 --- a/grc/grc_gnuradio/blks2/__init__.py +++ b/gr-blocks/python/grc_gnuradio/blks2/__init__.py @@ -19,8 +19,12 @@ # from selector import selector, valve -from packet import options, packet_encoder, packet_decoder, \ - packet_mod_b, packet_mod_s, packet_mod_i, packet_mod_f, packet_mod_c, \ - packet_demod_b, packet_demod_s, packet_demod_i, packet_demod_f, packet_demod_c from error_rate import error_rate from tcp import tcp_source, tcp_sink + +try: + from packet import options, packet_encoder, packet_decoder, \ + packet_mod_b, packet_mod_s, packet_mod_i, packet_mod_f, packet_mod_c, \ + packet_demod_b, packet_demod_s, packet_demod_i, packet_demod_f, packet_demod_c +except ImportError: + pass # only available if gr-digital is install diff --git a/grc/grc_gnuradio/blks2/error_rate.py b/gr-blocks/python/grc_gnuradio/blks2/error_rate.py index 9bf387030a..9bf387030a 100644 --- a/grc/grc_gnuradio/blks2/error_rate.py +++ b/gr-blocks/python/grc_gnuradio/blks2/error_rate.py diff --git a/grc/grc_gnuradio/blks2/selector.py b/gr-blocks/python/grc_gnuradio/blks2/selector.py index 24e3844658..24e3844658 100644 --- a/grc/grc_gnuradio/blks2/selector.py +++ b/gr-blocks/python/grc_gnuradio/blks2/selector.py diff --git a/grc/grc_gnuradio/blks2/tcp.py b/gr-blocks/python/grc_gnuradio/blks2/tcp.py index aee90fad2c..aee90fad2c 100644 --- a/grc/grc_gnuradio/blks2/tcp.py +++ b/gr-blocks/python/grc_gnuradio/blks2/tcp.py diff --git a/gr-channels/grc/channels_block_tree.xml b/gr-channels/grc/channels_block_tree.xml index 383f4b199c..00886e8eed 100644 --- a/gr-channels/grc/channels_block_tree.xml +++ b/gr-channels/grc/channels_block_tree.xml @@ -27,7 +27,7 @@ ################################################### --> <cat> - <name></name> <!-- Blank for Root Name --> + <name>[Core]</name> <cat> <name>Channel Models</name> <block>channels_channel_model</block> diff --git a/gr-digital/CMakeLists.txt b/gr-digital/CMakeLists.txt index 7a9f8f6c48..c6fa0798d1 100644 --- a/gr-digital/CMakeLists.txt +++ b/gr-digital/CMakeLists.txt @@ -97,6 +97,7 @@ add_subdirectory(doc) if(ENABLE_PYTHON) add_subdirectory(swig) add_subdirectory(python/digital) + add_subdirectory(python/grc_gnuradio) add_subdirectory(grc) add_subdirectory(examples) endif(ENABLE_PYTHON) diff --git a/grc/blocks/blks2_packet_decoder.xml b/gr-digital/grc/blks2_packet_decoder.xml index 07b0d1f2eb..fedb2e74f3 100644 --- a/grc/blocks/blks2_packet_decoder.xml +++ b/gr-digital/grc/blks2_packet_decoder.xml @@ -7,6 +7,8 @@ <block> <name>Packet Decoder</name> <key>blks2_packet_decoder</key> + <category>[Core]/Deprecated</category> + <flags>deprecated</flags> <import>from grc_gnuradio import blks2 as grc_blks2</import> <make>grc_blks2.packet_demod_$(type.fcn)(grc_blks2.packet_decoder( access_code=$access_code, diff --git a/grc/blocks/blks2_packet_encoder.xml b/gr-digital/grc/blks2_packet_encoder.xml index 88e1ba350c..d030f6ae22 100644 --- a/grc/blocks/blks2_packet_encoder.xml +++ b/gr-digital/grc/blks2_packet_encoder.xml @@ -7,6 +7,8 @@ <block> <name>Packet Encoder</name> <key>blks2_packet_encoder</key> + <category>[Core]/Deprecated</category> + <flags>deprecated</flags> <import>from grc_gnuradio import blks2 as grc_blks2</import> <make>grc_blks2.packet_mod_$(type.fcn)(grc_blks2.packet_encoder( samples_per_symbol=$samples_per_symbol, diff --git a/gr-digital/grc/digital_block_tree.xml b/gr-digital/grc/digital_block_tree.xml index 6707a8e4db..9bdf6e993f 100644 --- a/gr-digital/grc/digital_block_tree.xml +++ b/gr-digital/grc/digital_block_tree.xml @@ -26,7 +26,7 @@ ################################################### --> <cat> - <name></name> <!-- Blank for Root Name --> + <name>[Core]</name> <cat> <name>Coding</name> <block>digital_additive_scrambler_bb</block> diff --git a/gr-digital/grc/digital_burst_shaper.xml b/gr-digital/grc/digital_burst_shaper.xml index 5c0bc78d0a..95e1bcbc77 100644 --- a/gr-digital/grc/digital_burst_shaper.xml +++ b/gr-digital/grc/digital_burst_shaper.xml @@ -2,7 +2,7 @@ <block> <name>Burst Shaper</name> <key>digital_burst_shaper_xx</key> - <category>Packet Operators</category> + <category>[Core]/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> diff --git a/gr-digital/grc/digital_constellation.xml b/gr-digital/grc/digital_constellation.xml index 5254e4d799..ef5364d248 100644 --- a/gr-digital/grc/digital_constellation.xml +++ b/gr-digital/grc/digital_constellation.xml @@ -9,7 +9,13 @@ <key>variable_constellation</key> <category>Modulators</category> <import>from gnuradio import digital</import> - <var_make>self.$(id) = $(id) = digital.constellation_calcdist($const_points, $sym_map, $rot_sym, $dims).base() + <var_make> +#if str($type) == "calcdist" +self.$(id) = $(id) = digital.constellation_calcdist($const_points, $sym_map, $rot_sym, $dims).base() +#else +self.$(id) = $(id) = digital.constellation_$(type)().base() +#end if + #if str($soft_dec_lut).lower() == '"auto"' or str($soft_dec_lut).lower() == "'auto'" self.$(id).gen_soft_dec_lut($precision) #else if str($soft_dec_lut) != 'None' @@ -17,16 +23,53 @@ self.$(id).set_soft_dec_lut($soft_dec_lut, $precision) #end if </var_make> - <var_value>digital.constellation_calcdist($const_points, $sym_map, $rot_sym, $dims)</var_value> +<var_value> +#if str($type) == "calcdist" +digital.constellation_calcdist($const_points, $sym_map, $rot_sym, $dims) +#else +digital.constellation_$(type)() +#end if +</var_value> <make></make> <!--<callback></callback>--> <param> + <name>Constellation Type</name> + <key>type</key> + <type>enum</type> + <option> + <name>Variable Constellation</name> + <key>calcdist</key> + </option> + <option> + <name>BPSK</name> + <key>bpsk</key> + </option> + <option> + <name>QPSK</name> + <key>qpsk</key> + </option> + <option> + <name>DQPSK</name> + <key>dqpsk</key> + </option> + <option> + <name>8PSK</name> + <key>8psk</key> + </option> + <option> + <name>16QAM</name> + <key>16qam</key> + </option> + + </param> + <param> <name>Symbol Map</name> <key>sym_map</key> <value>[0, 1, 3, 2]</value> <type>int_vector</type> + <hide> #if str($type) == "calcdist" then 'none' else 'all' #</hide> </param> <param> @@ -34,6 +77,7 @@ self.$(id).set_soft_dec_lut($soft_dec_lut, $precision) <key>const_points</key> <value>[-1-1j, -1+1j, 1+1j, 1-1j]</value> <type>complex_vector</type> + <hide> #if str($type) == "calcdist" then 'none' else 'all' #</hide> </param> <param> @@ -41,6 +85,7 @@ self.$(id).set_soft_dec_lut($soft_dec_lut, $precision) <key>rot_sym</key> <value>4</value> <type>int</type> + <hide> #if str($type) == "calcdist" then 'none' else 'all' #</hide> </param> <param> @@ -48,8 +93,8 @@ self.$(id).set_soft_dec_lut($soft_dec_lut, $precision) <key>dims</key> <value>1</value> <type>int</type> + <hide> #if str($type) == "calcdist" then 'none' else 'all' #</hide> </param> - <param> <name>Soft Decisions Precision</name> <key>precision</key> diff --git a/gr-digital/grc/digital_header_payload_demux.xml b/gr-digital/grc/digital_header_payload_demux.xml index 24c6c5b216..a2fe80e621 100644 --- a/gr-digital/grc/digital_header_payload_demux.xml +++ b/gr-digital/grc/digital_header_payload_demux.xml @@ -13,6 +13,7 @@ $timing_tag_key, $samp_rate, $special_tags, + $header_padding, )</make> <param> <name>Header Length (Symbols)</name> @@ -20,6 +21,12 @@ <type>int</type> </param> <param> + <name>Header Padding (Uncertainty / Symbols)</name> + <key>header_padding</key> + <value>0</value> + <type>int</type> + </param> + <param> <name>Items per symbol</name> <key>items_per_symbol</key> <type>int</type> diff --git a/gr-digital/include/gnuradio/digital/header_payload_demux.h b/gr-digital/include/gnuradio/digital/header_payload_demux.h index 303bebbf32..bcd6bd108a 100644 --- a/gr-digital/include/gnuradio/digital/header_payload_demux.h +++ b/gr-digital/include/gnuradio/digital/header_payload_demux.h @@ -1,5 +1,5 @@ /* -*- c++ -*- */ -/* Copyright 2012 Free Software Foundation, Inc. +/* Copyright 2012-2016 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -29,7 +29,7 @@ namespace gr { namespace digital { /*! - * \brief Header/Payload demuxer. + * \brief Header/Payload demuxer (HPD). * \ingroup packet_operators_blk * * \details @@ -58,6 +58,9 @@ namespace gr { * and taken as the payload length. The payload, together with the header data * as tags, is then copied to output 1. * + * If the header demodulation fails, the header must send a PMT with value + * pmt::PMT_F. The state gets reset and the header is ignored. + * * \section hpd_item_sizes Symbols, Items and Item Sizes * * To generically and transparently handle different kinds of modulations, @@ -68,20 +71,66 @@ namespace gr { * grouping items. In OFDM, we usually don't care about individual samples, but * we do care about full OFDM symbols, so we set \p items_per_symbol to the * IFFT / FFT length of the OFDM modulator / demodulator. - * For most single-carrier modulations, this value can be set to 1 (the default - * value). + * For single-carrier modulations, this value can be set to the number of + * samples per symbol, to handle data in number of symbols, or to 1 to + * handle data in number of samples. * If specified, \p guard_interval items are discarded before every symbol. * This is useful for demuxing bursts of OFDM signals. * * On the output, we can deal with symbols directly by setting \p output_symbols * to true. In that case, the output item size is the <em>symbol size</em>. * - * \b Example: OFDM with 48 sub-carriers, using a length-64 IFFT on the modulator, - * and a cyclic-prefix length of 16 samples. In this case, the itemsize is - * `sizeof(gr_complex)`, because we're receiving complex samples. One OFDM symbol - * has 64 samples, hence \p items_per_symbol is set to 64, and \p guard_interval to - * 16. The header length is specified in number of OFDM symbols. Because we want to - * deal with full OFDM symbols, we set \p output_symbols to true. + * \b Example: OFDM with 48 sub-carriers, using a length-64 IFFT on the + * modulator, and a cyclic-prefix length of 16 samples. In this case, + * \p itemsize is `sizeof(gr_complex)`, because we're receiving complex + * samples. One OFDM symbol has 64 samples, hence \p items_per_symbol is + * set to 64, and \p guard_interval to 16. The header length is specified + * in number of OFDM symbols. Because we want to deal with full OFDM + * symbols, we set \p output_symbols to true. + * + * \b Example: PSK-modulated signals, with 4 samples per symbol. Again, + * \p itemsize is `sizeof(gr_complex)` because we're still dealing with + * complex samples. \p items_per_symbol is 4, because one item is one + * sample. \p guard_interval must be set to 0. The header length is + * given in number of PSK symbols. + * + * \section hpd_uncertainty Handling timing uncertainty on the trigger + * + * By default, the assumption is made that the trigger arrives on *exactly* + * the sample that the header starts. These triggers typically come from + * timing synchronization algorithms which may be suboptimal, and have a + * known timing uncertainty (e.g., we know the trigger might be a sample + * too early or too late). + * + * The demuxer has an option for this case, the \p header_padding. If this + * value is non-zero, it specifies the number of items that are prepended + * and appended to the header before copying it to the header output. + * + * Example: Say our synchronization algorithm can be off by up to two + * samples, and the header length is 20 samples. So we set \p header_len + * to 20, and \p header_padding to 2. + * Now assume a trigger arrives on sample index 100. We copy a total of + * 24 samples to the header port, starting at sample index 98. + * + * The payload is *not* padded. Let's say the header demod reports a + * payload length of 100. In the previous examples, we would copy 100 + * samples to the payload port, starting at sample index 120 (this means + * the padded samples appended to the header are copied to both ports!). + * However, the header demodulator has the option to specify a payload + * offset, which cannot exceed the padding value. To do this, include + * a key `payload_offset` in the message sent back to the HPD. A negative + * value means the payload starts earlier than otherwise. + * (If you wanted to always pad the payload, you could set `payload_offset` + * to `-header_padding` and increase the reported length of the payload). + * + * Because the padding is specified in number of items, and not symbols, + * this value can only be multiples of the number of items per symbol *if* + * either \p output_symbols is true, or a guard interval is specified (or + * both). Note that in practice, it is rare that both a guard interval is + * specified *and* a padding value is required. The difference between the + * padding value and a guard interval is that a guard interval is part of + * the signal, and comes with *every* symbol, whereas the header padding + * is added to only the header, and is not by design. * * \section hpd_tag_handling Tag Handling * @@ -95,12 +144,14 @@ namespace gr { * it belongs to this packet or the following. In this case, it is possible that the * tag might be propagated twice. * - * Tags outside of packets are generally discarded. If this information is important, - * there are two additional mechanisms to preserve the tags: + * Tags outside of packets are generally discarded. If there are tags that + * carry important information that must not be list, there are two + * additional mechanisms to preserve the tags: * - Timing tags might be relevant to know \b when a packet was received. By * specifying the name of a timestamp tag and the sample rate at this block, it * keeps track of the time and will add the time to the first item of every packet. - * The name of the timestamp tag is usually 'rx_time' (see gr::uhd::usrp_source::make()). + * The name of the timestamp tag is usually 'rx_time' (see, e.g., + * gr::uhd::usrp_source::make()). * The time value must be specified in the UHD time format. * - Other tags are simply stored and updated. As an example, the user might want to know the * rx frequency, which UHD stores in the rx_freq tag. In this case, add the tag name 'rx_freq' @@ -124,18 +175,20 @@ namespace gr { * \param timing_tag_key The name of the tag with timing information, usually 'rx_time' or empty (this means timing info is discarded) * \param samp_rate Sampling rate at the input. Necessary to calculate the rx time of packets. * \param special_tags A vector of strings denoting tags which shall be preserved (see \ref hpd_tag_handling) + * \param header_padding A number of items that is appended and prepended to the header. */ static sptr make( - int header_len, - int items_per_symbol=1, - int guard_interval=0, + const int header_len, + const int items_per_symbol=1, + const int guard_interval=0, const std::string &length_tag_key="frame_len", const std::string &trigger_tag_key="", - bool output_symbols=false, - size_t itemsize=sizeof(gr_complex), + const bool output_symbols=false, + const size_t itemsize=sizeof(gr_complex), const std::string &timing_tag_key="", const double samp_rate=1.0, - const std::vector<std::string> &special_tags=std::vector<std::string>() + const std::vector<std::string> &special_tags=std::vector<std::string>(), + const size_t header_padding=0 ); }; diff --git a/gr-digital/lib/header_payload_demux_impl.cc b/gr-digital/lib/header_payload_demux_impl.cc index 89428fa86e..f887ea1eb3 100644 --- a/gr-digital/lib/header_payload_demux_impl.cc +++ b/gr-digital/lib/header_payload_demux_impl.cc @@ -1,5 +1,5 @@ /* -*- c++ -*- */ -/* Copyright 2012-2014 Free Software Foundation, Inc. +/* Copyright 2012-2016 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -23,10 +23,10 @@ #include "config.h" #endif -#include <climits> -#include <boost/format.hpp> -#include <gnuradio/io_signature.h> #include "header_payload_demux_impl.h" +#include <gnuradio/io_signature.h> +#include <boost/format.hpp> +#include <climits> namespace gr { namespace digital { @@ -55,55 +55,63 @@ namespace gr { enum out_port_indexes_t { PORT_HEADER = 0, - PORT_PAYLOAD = 1 + PORT_PAYLOAD = 1, + PORT_INPUTDATA = 0, + PORT_TRIGGER = 1 }; #define msg_port_id pmt::mp("header_data") header_payload_demux::sptr header_payload_demux::make( - int header_len, - int items_per_symbol, - int guard_interval, - const std::string &length_tag_key, - const std::string &trigger_tag_key, - bool output_symbols, - size_t itemsize, - const std::string &timing_tag_key, - const double samp_rate, - const std::vector<std::string> &special_tags + int header_len, + int items_per_symbol, + int guard_interval, + const std::string &length_tag_key, + const std::string &trigger_tag_key, + bool output_symbols, + size_t itemsize, + const std::string &timing_tag_key, + const double samp_rate, + const std::vector<std::string> &special_tags, + const size_t header_padding ){ return gnuradio::get_initial_sptr ( - new header_payload_demux_impl( - header_len, - items_per_symbol, - guard_interval, - length_tag_key, - trigger_tag_key, - output_symbols, - itemsize, - timing_tag_key, - samp_rate, - special_tags - ) + new header_payload_demux_impl( + header_len, + items_per_symbol, + guard_interval, + length_tag_key, + trigger_tag_key, + output_symbols, + itemsize, + timing_tag_key, + samp_rate, + special_tags, + header_padding + ) ); } header_payload_demux_impl::header_payload_demux_impl( - int header_len, - int items_per_symbol, - int guard_interval, - const std::string &length_tag_key, - const std::string &trigger_tag_key, - bool output_symbols, - size_t itemsize, - const std::string &timing_tag_key, - const double samp_rate, - const std::vector<std::string> &special_tags + int header_len, + int items_per_symbol, + int guard_interval, + const std::string &length_tag_key, + const std::string &trigger_tag_key, + bool output_symbols, + size_t itemsize, + const std::string &timing_tag_key, + const double samp_rate, + const std::vector<std::string> &special_tags, + const size_t header_padding ) : block("header_payload_demux", - io_signature::make2(1, 2, itemsize, sizeof(char)), - io_signature::make(2, 2, (output_symbols ? itemsize * items_per_symbol : itemsize))), + io_signature::make2(1, 2, itemsize, sizeof(char)), + io_signature::make(2, 2, (output_symbols ? itemsize * items_per_symbol : itemsize))), d_header_len(header_len), + d_header_padding_symbols(header_padding / items_per_symbol), + d_header_padding_items(header_padding % items_per_symbol), + d_header_padding_total_items(header_padding), d_items_per_symbol(items_per_symbol), d_gi(guard_interval), d_len_tag_key(pmt::string_to_symbol(length_tag_key)), @@ -113,32 +121,42 @@ namespace gr { d_uses_trigger_tag(!trigger_tag_key.empty()), d_state(STATE_FIND_TRIGGER), d_curr_payload_len(0), + d_curr_payload_offset(0), d_payload_tag_keys(0), d_payload_tag_values(0), d_track_time(!timing_tag_key.empty()), d_timing_key(pmt::intern(timing_tag_key)), + d_payload_offset_key(pmt::intern("payload_offset")), d_last_time_offset(0), d_last_time(pmt::make_tuple(pmt::from_uint64(0L), pmt::from_double(0.0))), d_sampling_time(1.0/samp_rate) { if (d_header_len < 1) { - throw std::invalid_argument("Header length must be at least 1 symbol."); + throw std::invalid_argument("Header length must be at least 1 symbol."); + } + if (header_padding < 0) { + throw std::invalid_argument("Header padding must be non-negative."); } if (d_items_per_symbol < 1 || d_gi < 0 || d_itemsize < 1) { - throw std::invalid_argument("Items and symbol sizes must be at least 1."); + throw std::invalid_argument("Items and symbol sizes must be at least 1."); } if (d_output_symbols) { - set_relative_rate(1.0 / (d_items_per_symbol + d_gi)); + set_relative_rate(1.0 / (d_items_per_symbol + d_gi)); } else { - set_relative_rate((double)d_items_per_symbol / (d_items_per_symbol + d_gi)); - set_output_multiple(d_items_per_symbol); + set_relative_rate((double)d_items_per_symbol / (d_items_per_symbol + d_gi)); + set_output_multiple(d_items_per_symbol); + } + if ((d_output_symbols || d_gi) && d_header_padding_items) { + throw std::invalid_argument( + "If output_symbols is true or a guard interval is given, padding must be a multiple of items_per_symbol!" + ); } set_tag_propagation_policy(TPP_DONT); message_port_register_in(msg_port_id); set_msg_handler(msg_port_id, boost::bind(&header_payload_demux_impl::parse_header_data_msg, this, _1)); - for (unsigned i = 0; i < special_tags.size(); i++) { - d_special_tags.push_back(pmt::string_to_symbol(special_tags[i])); - d_special_tags_last_value.push_back(pmt::PMT_NIL); + for (size_t i = 0; i < special_tags.size(); i++) { + d_special_tags.push_back(pmt::string_to_symbol(special_tags[i])); + d_special_tags_last_value.push_back(pmt::PMT_NIL); } } @@ -146,144 +164,219 @@ namespace gr { { } + // forecast() depends on state: + // - When waiting for a header, we require at least the header length + // - when waiting for a payload, we require at least the payload length + // - Otherwise, pretend this is a sync block with a decimation/interpolation + // depending on symbol size and if we output symbols or items void - header_payload_demux_impl::forecast (int noutput_items, gr_vector_int &ninput_items_required) - { + header_payload_demux_impl::forecast( + int noutput_items, + gr_vector_int &ninput_items_required + ) { int n_items_reqd = 0; if (d_state == STATE_HEADER) { - n_items_reqd = d_header_len * (d_items_per_symbol + d_gi); + n_items_reqd = d_header_len * (d_items_per_symbol + d_gi) + + 2*d_header_padding_total_items; } else if (d_state == STATE_PAYLOAD) { - n_items_reqd = d_curr_payload_len * (d_items_per_symbol + d_gi); + n_items_reqd = d_curr_payload_len * (d_items_per_symbol + d_gi); } else { - n_items_reqd = noutput_items * (d_items_per_symbol + d_gi); - if (!d_output_symbols) { - // here, noutput_items is an integer multiple of d_items_per_symbol! - n_items_reqd /= d_items_per_symbol; - } + n_items_reqd = noutput_items * (d_items_per_symbol + d_gi); + if (!d_output_symbols) { + // Here, noutput_items is an integer multiple of d_items_per_symbol! + n_items_reqd /= d_items_per_symbol; + } } for (unsigned i = 0; i < ninput_items_required.size(); i++) { - ninput_items_required[i] = n_items_reqd; + ninput_items_required[i] = n_items_reqd; } } - inline bool - header_payload_demux_impl::check_items_available( - int n_symbols, - gr_vector_int &ninput_items, - int noutput_items, - int nread - ) - { - // Check there's enough items on the input - if ((n_symbols * (d_items_per_symbol + d_gi)) > (ninput_items[0]-nread) - || (ninput_items.size() == 2 && ((n_symbols * (d_items_per_symbol + d_gi)) > (ninput_items[1]-nread)))) { - return false; - } - + bool header_payload_demux_impl::check_buffers_ready( + int output_symbols_reqd, + int extra_output_items_reqd, + int noutput_items, + int input_items_reqd, + gr_vector_int &ninput_items, + int n_items_read + ) { // Check there's enough space on the output buffer if (d_output_symbols) { - if (noutput_items < n_symbols) { - return false; - } + if (noutput_items < output_symbols_reqd + extra_output_items_reqd) { + return false; + } } else { - if (noutput_items < n_symbols * d_items_per_symbol) { - return false; - } + if (noutput_items < (output_symbols_reqd * d_items_per_symbol) + extra_output_items_reqd) { + return false; + } + } + + // Check there's enough items on the input + if (input_items_reqd > (ninput_items[0]-n_items_read) + || (ninput_items.size() == 2 && (input_items_reqd > (ninput_items[1]-n_items_read)))) { + return false; } + // All good return true; } int - header_payload_demux_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 unsigned char *in = (const unsigned char *) input_items[0]; + header_payload_demux_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 unsigned char *in = (const unsigned char *) input_items[PORT_INPUTDATA]; unsigned char *out_header = (unsigned char *) output_items[PORT_HEADER]; unsigned char *out_payload = (unsigned char *) output_items[PORT_PAYLOAD]; - int nread = 0; - int trigger_offset = 0; - + const int n_input_items = (ninput_items.size() == 2) ? + std::min(ninput_items[0], ninput_items[1]) : + ninput_items[0]; + // Items read going into general_work() + const uint64_t n_items_read_base = nitems_read(PORT_INPUTDATA); + // Items read during this call to general_work() + int n_items_read = 0; + + #define CONSUME_ITEMS(items_to_consume) \ + update_special_tags( \ + n_items_read_base + n_items_read, \ + n_items_read_base + n_items_read + (items_to_consume) \ + ); \ + consume_each(items_to_consume); \ + n_items_read += (items_to_consume); \ + in += (items_to_consume) * d_itemsize; switch (d_state) { - case STATE_WAIT_FOR_MSG: - // In an ideal world, this would never be called - return 0; - - case STATE_HEADER_RX_FAIL: - update_special_tags(0, 1); - consume_each (1); - in += d_itemsize; - nread++; - d_state = STATE_FIND_TRIGGER; - // The following break was added to this state as well as STATE_FIND_TRIGGER - // and STATE_HEADER. There appears to be a bug somewhere in this code without - // the breaks that can lead to failure of this block. With the breaks in the code - // testing has shown more stable performance with various block paramters. - // If an offset calculation bug is found and fixed, it should be possible to - // remove these breaks for some performance increase. - break; - - case STATE_FIND_TRIGGER: - trigger_offset = find_trigger_signal(nread, noutput_items, input_items); - if (trigger_offset == -1) { - update_special_tags(0, noutput_items - nread); - consume_each(noutput_items - nread); - break; - } - update_special_tags(0, trigger_offset); - consume_each (trigger_offset); - in += trigger_offset * d_itemsize; - d_state = STATE_HEADER; - break; - - case STATE_HEADER: - if (check_items_available(d_header_len, ninput_items, noutput_items, nread)) { - copy_n_symbols(in, out_header, PORT_HEADER, d_header_len); - d_state = STATE_WAIT_FOR_MSG; - add_special_tags(); - produce( - PORT_HEADER, - d_header_len * (d_output_symbols ? 1 : d_items_per_symbol) - ); - } - break; - - case STATE_HEADER_RX_SUCCESS: - for (unsigned i = 0; i < d_payload_tag_keys.size(); i++) { - add_item_tag( - PORT_PAYLOAD, - nitems_written(PORT_PAYLOAD), - d_payload_tag_keys[i], - d_payload_tag_values[i] - ); - } - nread += d_header_len * (d_items_per_symbol + d_gi); - update_special_tags(0, nread); - consume_each (nread); - in += nread * d_itemsize; - d_state = STATE_PAYLOAD; - break; - - case STATE_PAYLOAD: - if (check_items_available(d_curr_payload_len, ninput_items, noutput_items, nread)) { - // The -1 because we won't consume the last item, it might hold the next trigger. - update_special_tags(0, (d_curr_payload_len - 1) * (d_items_per_symbol + d_gi)); - copy_n_symbols(in, out_payload, PORT_PAYLOAD, d_curr_payload_len); - produce(PORT_PAYLOAD, d_curr_payload_len * (d_output_symbols ? 1 : d_items_per_symbol)); - consume_each ((d_curr_payload_len - 1) * (d_items_per_symbol + d_gi)); // Same here - set_min_noutput_items(d_output_symbols ? 1 : (d_items_per_symbol + d_gi)); - d_state = STATE_FIND_TRIGGER; - } - break; - - default: - throw std::runtime_error("invalid state"); + case STATE_WAIT_FOR_MSG: + // In an ideal world, this would never be called + // parse_header_data_msg() is the only place that can kick us out + // of this state. + return 0; + + case STATE_HEADER_RX_FAIL: + // Actions: + // - Consume a single item to make sure we're not deleting any other + // info + CONSUME_ITEMS(1); + d_state = STATE_FIND_TRIGGER; + break; + + case STATE_FIND_TRIGGER: { + // Assumptions going into this state: + // - No other state was active for this call to general_work() + // - i.e. n_items_read == 0 + // Start looking for a trigger after any header padding. + // The trigger offset is relative to 'in'. + // => The absolute trigger offset is on n_items_read_base + n_items_read_base + trigger_offset + const int max_rel_offset = n_input_items - n_items_read; + const int trigger_offset = find_trigger_signal( + d_header_padding_total_items, + max_rel_offset, + n_items_read_base + n_items_read, + (input_items.size() == 2) ? + ((const unsigned char *) input_items[PORT_TRIGGER]) + n_items_read : NULL + ); + if (trigger_offset < max_rel_offset) { + d_state = STATE_HEADER; + } + // If we're using padding, don't consume everything, or we might + // end up with not enough items before the trigger + const int items_to_consume = trigger_offset - d_header_padding_total_items; + CONSUME_ITEMS(items_to_consume); + break; + } /* case STATE_FIND_TRIGGER */ + + case STATE_HEADER: + // Assumptions going into this state: + // - The first items on `in' are the header samples (including padding) + // - So we can just copy from the beginning of `in' + // - The trigger is on item index `d_header_padding * d_items_per_symbol' + // Actions: + // - Copy the entire header (including padding) to the header port + // - Special tags are added to the header port + if (check_buffers_ready( + d_header_len + 2*d_header_padding_symbols, + d_header_padding_items, + noutput_items, + d_header_len * (d_items_per_symbol + d_gi) + 2*d_header_padding_total_items, + ninput_items, + n_items_read)) { + add_special_tags(); + copy_n_symbols( + in, + out_header, + PORT_HEADER, + n_items_read_base + n_items_read, + d_header_len+2*d_header_padding_symbols, // Number of symbols to copy + 2*d_header_padding_items + ); + d_state = STATE_WAIT_FOR_MSG; + } + break; + + case STATE_HEADER_RX_SUCCESS: + // Copy tags from header to payload + for (size_t i = 0; i < d_payload_tag_keys.size(); i++) { + add_item_tag( + PORT_PAYLOAD, + nitems_written(PORT_PAYLOAD), + d_payload_tag_keys[i], + d_payload_tag_values[i] + ); + } + // Consume header from input + { + // Consume the padding only once, we leave the second + // part in there because it might be part of the payload + const int items_to_consume = + d_header_len * (d_items_per_symbol + d_gi) + + d_header_padding_total_items + + d_curr_payload_offset; + CONSUME_ITEMS(items_to_consume); + d_curr_payload_offset = 0; + d_state = STATE_PAYLOAD; + } + break; + + case STATE_PAYLOAD: + // Assumptions: + // - Input buffer is in the right spot to just start copying + if (check_buffers_ready( + d_curr_payload_len, + 0, + noutput_items, + d_curr_payload_len * (d_items_per_symbol + d_gi), + ninput_items, + n_items_read)) { + // Write payload + copy_n_symbols( + in, + out_payload, + PORT_PAYLOAD, + n_items_read_base + n_items_read, + d_curr_payload_len + ); + // Consume payload + // We can't consume the full payload, because we need to hold off + // at least the padding value. We'll use a minimum padding of 1 + // item here. + const int items_padding = std::max(d_header_padding_total_items, 1); + const int items_to_consume = + d_curr_payload_len * (d_items_per_symbol + d_gi) + - items_padding; + CONSUME_ITEMS(items_to_consume); + set_min_noutput_items(d_output_symbols ? 1 : (d_items_per_symbol + d_gi)); + d_state = STATE_FIND_TRIGGER; + } + break; + + default: + throw std::runtime_error("invalid state"); } /* switch */ return WORK_CALLED_PRODUCE; @@ -292,35 +385,41 @@ namespace gr { int header_payload_demux_impl::find_trigger_signal( - int nread, - int noutput_items, - gr_vector_const_void_star &input_items) - { - if (input_items.size() == 2) { - unsigned char *in_trigger = (unsigned char *) input_items[1]; - in_trigger += nread; - for (int i = 0; i < noutput_items-nread; i++) { - if (in_trigger[i]) { - return i; - } - } + int skip_items, + int max_rel_offset, + uint64_t base_offset, + const unsigned char *in_trigger + ) { + int rel_offset = max_rel_offset; + if (max_rel_offset < skip_items) { + return rel_offset; + } + if (in_trigger) { + for (int i = skip_items; i < max_rel_offset; i++) { + if (in_trigger[i]) { + rel_offset = i; + break; + } + } } if (d_uses_trigger_tag) { std::vector<tag_t> tags; - get_tags_in_range(tags, 0, nitems_read(0), nitems_read(0)+noutput_items, d_trigger_tag_key); - uint64_t min_offset = ULLONG_MAX; - int tag_index = -1; - for (unsigned i = 0; i < tags.size(); i++) { - if (tags[i].offset < min_offset) { - tag_index = (int) i; - min_offset = tags[i].offset; + get_tags_in_range( + tags, + PORT_INPUTDATA, + base_offset + skip_items, + base_offset + max_rel_offset, + d_trigger_tag_key + ); + if (!tags.empty()) { + std::sort(tags.begin(), tags.end(), tag_t::offset_compare); + const int tag_rel_offset = tags[0].offset - base_offset; + if (tag_rel_offset < rel_offset) { + rel_offset = tag_rel_offset; } } - if (tag_index != -1) { - return min_offset - nitems_read(0); - } } - return -1; + return rel_offset; } /* find_trigger_signal() */ @@ -332,77 +431,100 @@ namespace gr { d_state = STATE_HEADER_RX_FAIL; if (pmt::is_integer(header_data)) { - d_curr_payload_len = pmt::to_long(header_data); - d_payload_tag_keys.push_back(d_len_tag_key); - d_payload_tag_values.push_back(header_data); - d_state = STATE_HEADER_RX_SUCCESS; + d_curr_payload_len = pmt::to_long(header_data); + d_payload_tag_keys.push_back(d_len_tag_key); + d_payload_tag_values.push_back(header_data); + d_state = STATE_HEADER_RX_SUCCESS; } else if (pmt::is_dict(header_data)) { - pmt::pmt_t dict_items(pmt::dict_items(header_data)); - while (!pmt::is_null(dict_items)) { - pmt::pmt_t this_item(pmt::car(dict_items)); - d_payload_tag_keys.push_back(pmt::car(this_item)); - d_payload_tag_values.push_back(pmt::cdr(this_item)); - if (pmt::equal(pmt::car(this_item), d_len_tag_key)) { - d_curr_payload_len = pmt::to_long(pmt::cdr(this_item)); - d_state = STATE_HEADER_RX_SUCCESS; - } - dict_items = pmt::cdr(dict_items); - } - if (d_state == STATE_HEADER_RX_FAIL) { - GR_LOG_CRIT(d_logger, "no length tag passed from header data"); - } + pmt::pmt_t dict_items(pmt::dict_items(header_data)); + while (!pmt::is_null(dict_items)) { + pmt::pmt_t this_item(pmt::car(dict_items)); + d_payload_tag_keys.push_back(pmt::car(this_item)); + d_payload_tag_values.push_back(pmt::cdr(this_item)); + if (pmt::equal(pmt::car(this_item), d_len_tag_key)) { + d_curr_payload_len = pmt::to_long(pmt::cdr(this_item)); + d_state = STATE_HEADER_RX_SUCCESS; + } + if (pmt::equal(pmt::car(this_item), d_payload_offset_key)) { + d_curr_payload_offset = pmt::to_long(pmt::cdr(this_item)); + if (std::abs(d_curr_payload_offset) > d_header_padding_total_items) { + GR_LOG_CRIT(d_logger, "Payload offset exceeds padding"); + d_state = STATE_HEADER_RX_FAIL; + return; + } + } + dict_items = pmt::cdr(dict_items); + } + if (d_state == STATE_HEADER_RX_FAIL) { + GR_LOG_CRIT(d_logger, "no payload length passed from header data"); + } } else if (header_data == pmt::PMT_F || pmt::is_null(header_data)) { - GR_LOG_INFO(d_logger, boost::format("Parser returned %1%") % pmt::write_string(header_data)); + GR_LOG_INFO(d_logger, boost::format("Parser returned %1%") % pmt::write_string(header_data)); } else { - GR_LOG_ALERT(d_logger, boost::format("Received illegal header data (%1%)") % pmt::write_string(header_data)); + GR_LOG_ALERT(d_logger, boost::format("Received illegal header data (%1%)") % pmt::write_string(header_data)); } if (d_state == STATE_HEADER_RX_SUCCESS) { - if ((d_curr_payload_len * (d_output_symbols ? 1 : d_items_per_symbol)) > max_output_buffer(1)/2) { - d_state = STATE_HEADER_RX_FAIL; - GR_LOG_INFO(d_logger, boost::format("Detected a packet larger than max frame size (%1% symbols)") % d_curr_payload_len); - } else { - set_min_noutput_items(d_curr_payload_len * (d_output_symbols ? 1 : d_items_per_symbol)); - } + if (d_curr_payload_len < 0) { + GR_LOG_WARN(d_logger, boost::format("Detected a packet larger than max frame size (%1% symbols)") % d_curr_payload_len); + d_curr_payload_len = 0; + d_state = STATE_HEADER_RX_FAIL; + } + if ((d_curr_payload_len * (d_output_symbols ? 1 : d_items_per_symbol)) > max_output_buffer(1)/2) { + d_state = STATE_HEADER_RX_FAIL; + GR_LOG_INFO(d_logger, boost::format("Detected a packet larger than max frame size (%1% symbols)") % d_curr_payload_len); + } else { + set_min_noutput_items(d_curr_payload_len * (d_output_symbols ? 1 : d_items_per_symbol)); + } } } /* parse_header_data_msg() */ void header_payload_demux_impl::copy_n_symbols( - const unsigned char *in, - unsigned char *out, - int port, - int n_symbols - ) - { + const unsigned char *in, + unsigned char *out, + int port, + const uint64_t n_items_read_base, + int n_symbols, + int n_padding_items + ) { // Copy samples if (d_gi) { - for (int i = 0; i < n_symbols; i++) { - memcpy((void *) out, (void *) (in + d_gi * d_itemsize), d_items_per_symbol * d_itemsize); - in += d_itemsize * (d_items_per_symbol + d_gi); - out += d_itemsize * d_items_per_symbol; - } + // Here we know n_padding_items must be 0 (see contract), + // because all padding items will be part of n_symbols + for (int i = 0; i < n_symbols; i++) { + memcpy( + (void *) out, + (void *) (in + d_gi * d_itemsize), + d_items_per_symbol * d_itemsize + ); + in += d_itemsize * (d_items_per_symbol + d_gi); + out += d_itemsize * d_items_per_symbol; + } } else { - memcpy( - (void *) out, - (void *) in, - n_symbols * d_items_per_symbol * d_itemsize - ); + memcpy( + (void *) out, + (void *) in, + (n_symbols * d_items_per_symbol + n_padding_items) * d_itemsize + ); } // Copy tags std::vector<tag_t> tags; get_tags_in_range( - tags, 0, - nitems_read(0), - nitems_read(0) + n_symbols * (d_items_per_symbol + d_gi) + tags, + PORT_INPUTDATA, + n_items_read_base, + n_items_read_base + + n_symbols * (d_items_per_symbol + d_gi) + + n_padding_items ); for (size_t t = 0; t < tags.size(); t++) { // The trigger tag is *not* propagated if (tags[t].key == d_trigger_tag_key) { continue; } - int new_offset = tags[t].offset - nitems_read(0); + int new_offset = tags[t].offset - n_items_read_base; if (d_output_symbols) { new_offset /= (d_items_per_symbol + d_gi); } else if (d_gi) { @@ -418,43 +540,49 @@ namespace gr { tags[t].value ); } + // Advance write pointers + // Items to produce might actually be symbols + const int items_to_produce = d_output_symbols ? + n_symbols : + (n_symbols * d_items_per_symbol + n_padding_items); + produce(port, items_to_produce); } /* copy_n_symbols() */ void header_payload_demux_impl::update_special_tags( - int range_start, - int range_end + uint64_t range_start, + uint64_t range_end ){ if (d_track_time) { - std::vector<tag_t> tags; - get_tags_in_range(tags, 0, - nitems_read(0) + range_start, - nitems_read(0) + range_end, - d_timing_key - ); - for (unsigned t = 0; t < tags.size(); t++) { - if(tags[t].offset >= d_last_time_offset) { - d_last_time = tags[t].value; - d_last_time_offset = tags[t].offset; - } - } + std::vector<tag_t> tags; + get_tags_in_range( + tags, + PORT_INPUTDATA, + range_start, + range_end, + d_timing_key + ); + if (!tags.empty()) { + std::sort(tags.begin(), tags.end(), tag_t::offset_compare); + d_last_time = tags.back().value; + d_last_time_offset = tags.back().offset; + } } std::vector<tag_t> tags; - for (unsigned i = 0; i < d_special_tags.size(); i++) { - uint64_t offset = 0; - // TODO figure out if it's better to get all tags at once instead of doing this for every tag individually - get_tags_in_range(tags, 0, - nitems_read(0) + range_start, - nitems_read(0) + range_end, - d_special_tags[i] - ); - for (unsigned t = 0; t < tags.size(); t++) { - if(tags[t].offset >= offset) { - d_special_tags_last_value[i] = tags[t].value; - offset = tags[t].offset; - } - } + for (size_t i = 0; i < d_special_tags.size(); i++) { + // TODO figure out if it's better to get all tags at once instead of doing this for every tag individually + get_tags_in_range( + tags, + PORT_INPUTDATA, // Read from port 0 + range_start, + range_end, + d_special_tags[i] + ); + std::sort(tags.begin(), tags.end(), tag_t::offset_compare); + for (size_t t = 0; t < tags.size(); t++) { + d_special_tags_last_value[i] = tags[t].value; + } } } /* update_special_tags() */ @@ -462,24 +590,24 @@ namespace gr { header_payload_demux_impl::add_special_tags( ){ if (d_track_time) { - add_item_tag( - PORT_HEADER, - nitems_written(PORT_HEADER), - d_timing_key, - _update_pmt_time( - d_last_time, - d_sampling_time * (nitems_read(0) - d_last_time_offset) - ) - ); + add_item_tag( + PORT_HEADER, + nitems_written(PORT_HEADER), + d_timing_key, + _update_pmt_time( + d_last_time, + d_sampling_time * (nitems_read(PORT_INPUTDATA) - d_last_time_offset) + ) + ); } for (unsigned i = 0; i < d_special_tags.size(); i++) { - add_item_tag( - PORT_HEADER, - nitems_written(PORT_HEADER), - d_special_tags[i], - d_special_tags_last_value[i] - ); + add_item_tag( + PORT_HEADER, + nitems_written(PORT_HEADER), + d_special_tags[i], + d_special_tags_last_value[i] + ); } } /* add_special_tags() */ diff --git a/gr-digital/lib/header_payload_demux_impl.h b/gr-digital/lib/header_payload_demux_impl.h index 1d45dc7ce1..0a70e7da3e 100644 --- a/gr-digital/lib/header_payload_demux_impl.h +++ b/gr-digital/lib/header_payload_demux_impl.h @@ -1,18 +1,18 @@ /* -*- c++ -*- */ -/* Copyright 2012 Free Software Foundation, Inc. - * +/* Copyright 2012-2016 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, @@ -31,6 +31,9 @@ namespace gr { { private: int d_header_len; //!< Number of bytes per header + const int d_header_padding_symbols; //!< Symbols header padding + const int d_header_padding_items; //!< Items header padding + const int d_header_padding_total_items; //!< Items header padding int d_items_per_symbol; //!< Bytes per symbol int d_gi; //!< Bytes per guard interval pmt::pmt_t d_len_tag_key; //!< Key of length tag @@ -40,10 +43,12 @@ namespace gr { bool d_uses_trigger_tag; //!< If a trigger tag is used int d_state; //!< Current read state int d_curr_payload_len; //!< Length of the next payload (symbols) + int d_curr_payload_offset; //!< Offset of the next payload (symbols) std::vector<pmt::pmt_t> d_payload_tag_keys; //!< Temporary buffer for PMTs that go on the payload (keys) std::vector<pmt::pmt_t> d_payload_tag_values; //!< Temporary buffer for PMTs that go on the payload (values) bool d_track_time; //!< Whether or not to keep track of the rx time pmt::pmt_t d_timing_key; //!< Key of the timing tag (usually 'rx_time') + pmt::pmt_t d_payload_offset_key; //!< Key of payload offset (usually 'payload_offset') uint64_t d_last_time_offset; //!< Item number of the last time tag pmt::pmt_t d_last_time; //!< The actual time that was indicated double d_sampling_time; //!< Inverse sampling rate @@ -53,7 +58,14 @@ namespace gr { // Helper functions to make the state machine more readable //! Checks if there are enough items on the inputs and enough space on the output buffers to copy \p n_symbols symbols - inline bool check_items_available(int n_symbols, gr_vector_int &ninput_items, int noutput_items, int nread); + bool check_buffers_ready( + int output_symbols_reqd, + int extra_output_items_reqd, + int noutput_items, + int input_items_reqd, + gr_vector_int &ninput_items, + int n_items_read + ); //! Message handler: Reads the result from the header demod and sets length tag (and other tags) void parse_header_data_msg(pmt::pmt_t header_data); @@ -62,49 +74,54 @@ namespace gr { // Searches input 1 (if active), then the tags. Returns the offset in the input buffer // (or -1 if none is found) int find_trigger_signal( - int nread, - int noutput_items, - gr_vector_const_void_star &input_items); + int skip_items, + int noutput_items, + uint64_t base_offset, + const unsigned char *in_trigger + ); //! Copies n symbols from in to out, makes sure tags are propagated properly. Does neither consume nor produce. void copy_n_symbols( - const unsigned char *in, - unsigned char *out, - int port, - int n_symbols + const unsigned char *in, + unsigned char *out, + int port, + const uint64_t n_items_read_base, + int n_symbols, + int n_padding_items=0 ); //! Scans a given range for tags in d_special_tags void update_special_tags( - int range_start, - int range_end + uint64_t range_start, + uint64_t range_end ); //! Adds all tags in d_special_tags and timing info to the first item of the header. void add_special_tags(); - public: header_payload_demux_impl( - int header_len, - int items_per_symbol, - int guard_interval, - const std::string &length_tag_key, - const std::string &trigger_tag_key, - bool output_symbols, - size_t itemsize, - const std::string &timing_tag_key, - const double samp_rate, - const std::vector<std::string> &special_tags + const int header_len, + const int items_per_symbol, + const int guard_interval, + const std::string &length_tag_key, + const std::string &trigger_tag_key, + const bool output_symbols, + const size_t itemsize, + const std::string &timing_tag_key, + const double samp_rate, + const std::vector<std::string> &special_tags, + const size_t header_padding ); ~header_payload_demux_impl(); 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); + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items + ); }; } // namespace digital diff --git a/gr-digital/python/digital/qa_correlate_access_code_XX_ts.py b/gr-digital/python/digital/qa_correlate_access_code_XX_ts.py new file mode 100755 index 0000000000..5429ce1e07 --- /dev/null +++ b/gr-digital/python/digital/qa_correlate_access_code_XX_ts.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python +# +# Copyright 2006,2007,2010,2011,2013,2016 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, digital, blocks +import pmt + +default_access_code = '\xAC\xDD\xA4\xE2\xF2\x8C\x20\xFC' + +def string_to_1_0_list(s): + r = [] + for ch in s: + x = ord(ch) + for i in range(8): + t = (x >> i) & 0x1 + r.append(t) + return r + +def to_1_0_string(L): + return ''.join(map(lambda x: chr(x + ord('0')), L)) + +class test_correlate_access_code_XX_ts(gr_unittest.TestCase): + + def setUp(self): + self.tb = gr.top_block() + + def tearDown(self): + self.tb = None + + def test_001(self): + payload = "test packet" # payload length is 11 bytes + header = "\x00\xd0\x00\xd0" # header contains packet length, twice (bit-swapped) + packet = header + payload + pad = (0,) * 64 + src_data = (0, 0, 1, 1, 1, 1, 0, 1, 1) + tuple(string_to_1_0_list(packet)) + pad + expected = tuple(map(long, src_data[9+32:-len(pad)])) + src = blocks.vector_source_b(src_data) + op = digital.correlate_access_code_bb_ts("1011", 0, "sync") + dst = blocks.vector_sink_b() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + result_tags = dst.tags() + self.assertEqual(len(result_data), len(payload)*8) + self.assertEqual(result_tags[0].offset, 0) + self.assertEqual(pmt.to_long(result_tags[0].value), len(payload)*8) + self.assertEqual(result_data, expected) + + def test_002(self): + payload = "test packet" # payload length is 11 bytes + header = "\x00\xd0\x00\xd0" # header contains packet length, twice (bit-swapped) + packet = header + payload + pad = (0,) * 64 + src_data = (0, 0, 1, 1, 1, 1, 0, 1, 1) + tuple(string_to_1_0_list(packet)) + pad + src_floats = tuple(2*b-1 for b in src_data) # convert to binary antipodal symbols (-1,1) + expected = src_floats[9+32:-len(pad)] + src = blocks.vector_source_f(src_floats) + op = digital.correlate_access_code_ff_ts("1011", 0, "sync") + dst = blocks.vector_sink_f() + self.tb.connect(src, op, dst) + self.tb.run() + result_data = dst.data() + result_tags = dst.tags() + self.assertEqual(len(result_data), len(payload)*8) + self.assertEqual(result_tags[0].offset, 0) + self.assertEqual(pmt.to_long(result_tags[0].value), len(payload)*8) + self.assertFloatTuplesAlmostEqual(result_data, expected, 5) + + +if __name__ == '__main__': + gr_unittest.run(test_correlate_access_code_XX_ts, "test_correlate_access_code_XX_ts.xml") + diff --git a/gr-digital/python/digital/qa_header_payload_demux.py b/gr-digital/python/digital/qa_header_payload_demux.py index 8006d4442e..f36d71067c 100755 --- a/gr-digital/python/digital/qa_header_payload_demux.py +++ b/gr-digital/python/digital/qa_header_payload_demux.py @@ -1,29 +1,69 @@ #!/usr/bin/env python -# Copyright 2012 Free Software Foundation, Inc. -# +# Copyright 2012-2016 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 __future__ import print_function import time - -from gnuradio import gr, gr_unittest, digital, blocks +import random +import numpy +from gnuradio import gr +from gnuradio import gr_unittest +from gnuradio import digital +from gnuradio import blocks import pmt +def make_tag(key, value, offset): + tag = gr.tag_t() + tag.offset = offset + tag.key = pmt.string_to_symbol(key) + tag.value = pmt.to_pmt(value) + return tag + + +class HeaderToMessageBlock(gr.sync_block): + """ + Helps with testing the HPD. Receives a header, stores it, posts + a predetermined message. + """ + def __init__(self, itemsize, header_len, messages, header_is_symbol=False): + gr.sync_block.__init__( + self, + name="HeaderToMessageBlock", + in_sig=[itemsize], + out_sig=[itemsize], + ) + self.header_len = header_len + self.message_port_register_out(pmt.intern('header_data')) + self.messages = messages + self.msg_count = 0 + + def work(self, input_items, output_items): + for i in xrange(len(input_items[0])/self.header_len): + msg = self.messages[self.msg_count] or False + #print("Sending message: {0}".format(msg)) + self.message_port_pub(pmt.intern('header_data'), pmt.to_pmt(msg)) + self.msg_count += 1 + output_items[0][:] = input_items[0][:] + return len(input_items[0]) + + class qa_header_payload_demux (gr_unittest.TestCase): def setUp (self): @@ -32,6 +72,36 @@ class qa_header_payload_demux (gr_unittest.TestCase): def tearDown (self): self.tb = None + def connect_all_blocks(self, + data_src, trigger_src, + hpd, + mock_header_demod, + payload_sink, header_sink + ): + """ + Connect the standard HPD test flowgraph + """ + self.tb.connect(data_src, (hpd, 0)) + if trigger_src is not None: + self.tb.connect(trigger_src, (hpd, 1)) + self.tb.connect((hpd, 0), mock_header_demod) + self.tb.connect(mock_header_demod, header_sink) + self.tb.msg_connect( + mock_header_demod, 'header_data', + hpd, 'header_data' + ) + self.tb.connect((hpd, 1), payload_sink) + + def run_tb(self, payload_sink, payload_len, header_sink, header_len, timeout=30): + stop_time = time.time() + timeout + self.tb.start() + while len(payload_sink.data()) < payload_len and \ + len(header_sink.data()) < header_len and \ + time.time() < stop_time: + time.sleep(.2) + self.tb.stop() + self.tb.wait() + def test_001_t (self): """ Simplest possible test: put in zeros, then header, then payload, trigger signal, try to demux. @@ -45,25 +115,13 @@ class qa_header_payload_demux (gr_unittest.TestCase): trigger_signal = [0,] * len(data_signal) trigger_signal[n_zeros] = 1 # This is dropped: - testtag1 = gr.tag_t() - testtag1.offset = 0 - testtag1.key = pmt.string_to_symbol('tag1') - testtag1.value = pmt.from_long(0) + testtag1 = make_tag('tag1', 0, 0) # This goes on output 0, item 0: - testtag2 = gr.tag_t() - testtag2.offset = n_zeros - testtag2.key = pmt.string_to_symbol('tag2') - testtag2.value = pmt.from_long(23) + testtag2 = make_tag('tag2', 23, n_zeros) # This goes on output 0, item 2: - testtag3 = gr.tag_t() - testtag3.offset = n_zeros + len(header) - 1 - testtag3.key = pmt.string_to_symbol('tag3') - testtag3.value = pmt.from_long(42) + testtag3 = make_tag('tag3', 42, n_zeros + len(header) - 1) # This goes on output 1, item 3: - testtag4 = gr.tag_t() - testtag4.offset = n_zeros + len(header) + 3 - testtag4.key = pmt.string_to_symbol('tag4') - testtag4.value = pmt.from_long(314) + testtag4 = make_tag('tag4', 314, n_zeros + len(header) + 3) data_src = blocks.vector_source_f( data_signal, False, @@ -73,26 +131,17 @@ class qa_header_payload_demux (gr_unittest.TestCase): hpd = digital.header_payload_demux( len(header), 1, 0, "frame_len", "detect", False, gr.sizeof_float ) + mock_header_demod = HeaderToMessageBlock( + numpy.float32, + len(header), + [len(payload)] + ) self.assertEqual(pmt.length(hpd.message_ports_in()), 2) #extra system port defined for you - header_sink = blocks.vector_sink_f() payload_sink = blocks.vector_sink_f() - - self.tb.connect(data_src, (hpd, 0)) - self.tb.connect(trigger_src, (hpd, 1)) - self.tb.connect((hpd, 0), header_sink) - self.tb.connect((hpd, 1), payload_sink) - self.tb.start() - time.sleep(.2) # Need this, otherwise, the next message is ignored - hpd.to_basic_block()._post( - pmt.intern('header_data'), - pmt.from_long(len(payload)) - ) - while len(payload_sink.data()) < len(payload): - time.sleep(.2) - self.tb.stop() - self.tb.wait() - - self.assertEqual(header_sink.data(), header) + header_sink = blocks.vector_sink_f() + self.connect_all_blocks(data_src, trigger_src, hpd, mock_header_demod, payload_sink, header_sink) + self.run_tb(payload_sink, len(payload), header_sink, len(header)) + self.assertEqual(header_sink.data(), header) self.assertEqual(payload_sink.data(), payload) ptags_header = [] for tag in header_sink.tags(): @@ -122,30 +171,15 @@ class qa_header_payload_demux (gr_unittest.TestCase): payload = tuple(range(5, 20)) data_signal = (0,) * n_zeros + header + payload # Trigger tag - trigger_tag = gr.tag_t() - trigger_tag.offset = n_zeros - trigger_tag.key = pmt.string_to_symbol('detect') - trigger_tag.value = pmt.PMT_T + trigger_tag = make_tag('detect', True, n_zeros) # This is dropped: - testtag1 = gr.tag_t() - testtag1.offset = 0 - testtag1.key = pmt.string_to_symbol('tag1') - testtag1.value = pmt.from_long(0) + testtag1 = make_tag('tag1', 0, 0) # This goes on output 0, item 0: - testtag2 = gr.tag_t() - testtag2.offset = n_zeros - testtag2.key = pmt.string_to_symbol('tag2') - testtag2.value = pmt.from_long(23) + testtag2 = make_tag('tag2', 23, n_zeros) # This goes on output 0, item 2: - testtag3 = gr.tag_t() - testtag3.offset = n_zeros + len(header) - 1 - testtag3.key = pmt.string_to_symbol('tag3') - testtag3.value = pmt.from_long(42) + testtag3 = make_tag('tag3', 42, n_zeros + len(header) - 1) # This goes on output 1, item 3: - testtag4 = gr.tag_t() - testtag4.offset = n_zeros + len(header) + 3 - testtag4.key = pmt.string_to_symbol('tag4') - testtag4.value = pmt.from_long(314) + testtag4 = make_tag('tag4', 314, n_zeros + len(header) + 3) data_src = blocks.vector_source_f( data_signal, False, @@ -157,21 +191,14 @@ class qa_header_payload_demux (gr_unittest.TestCase): self.assertEqual(pmt.length(hpd.message_ports_in()), 2) #extra system port defined for you header_sink = blocks.vector_sink_f() payload_sink = blocks.vector_sink_f() - - self.tb.connect(data_src, (hpd, 0)) - self.tb.connect((hpd, 0), header_sink) - self.tb.connect((hpd, 1), payload_sink) - self.tb.start() - time.sleep(.2) # Need this, otherwise, the next message is ignored - hpd.to_basic_block()._post( - pmt.intern('header_data'), - pmt.from_long(len(payload)) + mock_header_demod = HeaderToMessageBlock( + numpy.float32, + len(header), + [len(payload)] ) - while len(payload_sink.data()) < len(payload): - time.sleep(.2) - self.tb.stop() - self.tb.wait() - + self.connect_all_blocks(data_src, None, hpd, mock_header_demod, payload_sink, header_sink) + self.run_tb(payload_sink, len(payload), header_sink, len(header)) + # Check results self.assertEqual(header_sink.data(), header) self.assertEqual(payload_sink.data(), payload) ptags_header = [] @@ -193,8 +220,143 @@ class qa_header_payload_demux (gr_unittest.TestCase): ] self.assertEqual(expected_tags_payload, ptags_payload) + def test_001_headerpadding (self): + """ Like test 1, but with header padding. """ + n_zeros = 3 + header = (1, 2, 3) + header_padding = 1 + payload = tuple(range(5, 20)) + data_signal = (0,) * n_zeros + header + payload + trigger_signal = [0,] * len(data_signal) + trigger_signal[n_zeros] = 1 + # This is dropped: + testtag1 = make_tag('tag1', 0, 0) + # This goes on output 0, item 0: + testtag2 = make_tag('tag2', 23, n_zeros) + # This goes on output 0, item 2: + testtag3 = make_tag('tag3', 42, n_zeros + len(header) - 1) + # This goes on output 1, item 3: + testtag4 = make_tag('tag4', 314, n_zeros + len(header) + 3) + data_src = blocks.vector_source_f( + data_signal, + False, + tags=(testtag1, testtag2, testtag3, testtag4) + ) + trigger_src = blocks.vector_source_b(trigger_signal, False) + hpd = digital.header_payload_demux( + len(header), + 1, # Items per symbol + 0, # Guard interval + "frame_len", # TSB tag key + "detect", # Trigger tag key + False, # No symbols please + gr.sizeof_float, # Item size + "", # Timing tag key + 1.0, # Samp rate + (), # No special tags + header_padding + ) + mock_header_demod = HeaderToMessageBlock( + numpy.float32, + len(header), + [len(payload)] + ) + header_sink = blocks.vector_sink_f() + payload_sink = blocks.vector_sink_f() + self.connect_all_blocks(data_src, trigger_src, hpd, mock_header_demod, payload_sink, header_sink) + self.run_tb(payload_sink, len(payload), header_sink, len(header)+2) + # Check values + # Header now is padded: + self.assertEqual(header_sink.data(), (0,) + header + (payload[0],)) + self.assertEqual(payload_sink.data(), payload) + ptags_header = [] + for tag in header_sink.tags(): + ptag = gr.tag_to_python(tag) + ptags_header.append({'key': ptag.key, 'offset': ptag.offset}) + expected_tags_header = [ + {'key': 'tag2', 'offset': 1}, + {'key': 'tag3', 'offset': 3}, + ] + self.assertEqual(expected_tags_header, ptags_header) + ptags_payload = [] + for tag in payload_sink.tags(): + ptag = gr.tag_to_python(tag) + ptags_payload.append({'key': ptag.key, 'offset': ptag.offset}) + expected_tags_payload = [ + {'key': 'frame_len', 'offset': 0}, + {'key': 'tag4', 'offset': 3}, + ] + self.assertEqual(expected_tags_payload, ptags_payload) + + def test_001_headerpadding_payload_offset (self): + """ Like test 1, but with header padding + payload offset. """ + n_zeros = 3 + header = (1, 2, 3) + header_padding = 1 + payload_offset = -1 + payload = tuple(range(5, 20)) + data_signal = (0,) * n_zeros + header + payload + (0,) * 100 + trigger_signal = [0,] * len(data_signal) + trigger_signal[n_zeros] = 1 + # This goes on output 1, item 3 + 1 (for payload offset) + testtag4 = make_tag('tag4', 314, n_zeros + len(header) + 3) + data_src = blocks.vector_source_f( + data_signal, + False, + tags=(testtag4,) + ) + trigger_src = blocks.vector_source_b(trigger_signal, False) + hpd = digital.header_payload_demux( + len(header), + 1, # Items per symbol + 0, # Guard interval + "frame_len", # TSB tag key + "detect", # Trigger tag key + False, # No symbols please + gr.sizeof_float, # Item size + "", # Timing tag key + 1.0, # Samp rate + (), # No special tags + header_padding + ) + self.assertEqual(pmt.length(hpd.message_ports_in()), 2) #extra system port defined for you + header_sink = blocks.vector_sink_f() + payload_sink = blocks.vector_sink_f() + self.tb.connect(data_src, (hpd, 0)) + self.tb.connect(trigger_src, (hpd, 1)) + self.tb.connect((hpd, 0), header_sink) + self.tb.connect((hpd, 1), payload_sink) + self.tb.start() + time.sleep(.2) # Need this, otherwise, the next message is ignored + hpd.to_basic_block()._post( + pmt.intern('header_data'), + pmt.to_pmt({'frame_len': len(payload), 'payload_offset': payload_offset}) + ) + while len(payload_sink.data()) < len(payload): + time.sleep(.2) + self.tb.stop() + self.tb.wait() + # Header is now padded: + self.assertEqual(header_sink.data(), (0,) + header + (payload[0],)) + # Payload is now offset: + self.assertEqual( + payload_sink.data(), + data_signal[n_zeros + len(header) + payload_offset:n_zeros + len(header) + payload_offset + len(payload)] + ) + ptags_payload = {} + for tag in payload_sink.tags(): + ptag = gr.tag_to_python(tag) + ptags_payload[ptag.key] = ptag.offset + expected_tags_payload = { + 'frame_len': 0, + 'payload_offset': 0, + 'tag4': 3 - payload_offset, + } + self.assertEqual(expected_tags_payload, ptags_payload) + + def test_002_symbols (self): - """ + """ Same as before, but operate on symbols """ n_zeros = 1 @@ -207,25 +369,13 @@ class qa_header_payload_demux (gr_unittest.TestCase): trigger_signal = [0,] * len(data_signal) trigger_signal[n_zeros] = 1 # This is dropped: - testtag1 = gr.tag_t() - testtag1.offset = 0 - testtag1.key = pmt.string_to_symbol('tag1') - testtag1.value = pmt.from_long(0) + testtag1 = make_tag('tag1', 0, 0) # This goes on output 0, item 0 (from the GI) - testtag2 = gr.tag_t() - testtag2.offset = n_zeros - testtag2.key = pmt.string_to_symbol('tag2') - testtag2.value = pmt.from_long(23) + testtag2 = make_tag('tag2', 23, n_zeros) # This goes on output 0, item 0 (middle of the header symbol) - testtag3 = gr.tag_t() - testtag3.offset = n_zeros + gi + 1 - testtag3.key = pmt.string_to_symbol('tag3') - testtag3.value = pmt.from_long(42) + testtag3 = make_tag('tag3', 42, n_zeros + gi + 1) # This goes on output 1, item 1 (middle of the first payload symbol) - testtag4 = gr.tag_t() - testtag4.offset = n_zeros + (gi + items_per_symbol) * 2 + 1 - testtag4.key = pmt.string_to_symbol('tag4') - testtag4.value = pmt.from_long(314) + testtag4 = make_tag('tag4', 314, n_zeros + (gi + items_per_symbol) * 2 + 1) data_src = blocks.vector_source_f(data_signal, False, tags=(testtag1, testtag2, testtag3, testtag4)) trigger_src = blocks.vector_source_b(trigger_signal, False) hpd = digital.header_payload_demux( @@ -291,25 +441,20 @@ class qa_header_payload_demux (gr_unittest.TestCase): trigger_signal[n_zeros] = 1 trigger_signal[len(data_signal)] = 1 trigger_signal[len(data_signal)+len(header_fail)+n_zeros] = 1 - tx_signal = data_signal + header_fail + (0,) * n_zeros + header + payload2 + (0,) * 1000 + print("Triggers at: {0} {1} {2}".format( + n_zeros, + len(data_signal), + len(data_signal)+len(header_fail)+n_zeros) + ) + tx_signal = data_signal + \ + header_fail + (0,) * n_zeros + \ + header + payload2 + (0,) * 1000 # Timing tag: This is preserved and updated: - timing_tag = gr.tag_t() - timing_tag.offset = 0 - timing_tag.key = pmt.string_to_symbol('rx_time') - timing_tag.value = pmt.to_pmt((0, 0)) + timing_tag = make_tag('rx_time', (0, 0), 0) # Rx freq tags: - rx_freq_tag1 = gr.tag_t() - rx_freq_tag1.offset = 0 - rx_freq_tag1.key = pmt.string_to_symbol('rx_freq') - rx_freq_tag1.value = pmt.from_double(1.0) - rx_freq_tag2 = gr.tag_t() - rx_freq_tag2.offset = 29 - rx_freq_tag2.key = pmt.string_to_symbol('rx_freq') - rx_freq_tag2.value = pmt.from_double(1.5) - rx_freq_tag3 = gr.tag_t() - rx_freq_tag3.offset = 30 - rx_freq_tag3.key = pmt.string_to_symbol('rx_freq') - rx_freq_tag3.value = pmt.from_double(2.0) + rx_freq_tag1 = make_tag('rx_freq', 1.0, 0) + rx_freq_tag2 = make_tag('rx_freq', 1.5, 29) + rx_freq_tag3 = make_tag('rx_freq', 2.0, 30) ### Flow graph data_src = blocks.vector_source_f( tx_signal, False, @@ -388,6 +533,92 @@ class qa_header_payload_demux (gr_unittest.TestCase): self.assertEqual(tags_header, tags_expected_header) self.assertEqual(tags_payload, tags_expected_payload) + def test_004_fuzz(self): + """ + Long random test + """ + def create_signal( + n_bursts, + header_len, + max_gap, + max_burstsize, + fail_rate, + ): + signal = [] + indexes = [] + burst_sizes = [] + total_payload_len = 0 + for burst_count in xrange(n_bursts): + gap_size = random.randint(0, max_gap) + signal += [0] * gap_size + is_failure = random.random() < fail_rate + if not is_failure: + burst_size = random.randint(0, max_burstsize) + else: + burst_size = 0 + total_payload_len += burst_size + indexes += [len(signal)] + signal += [1] * header_len + signal += [2] * burst_size + burst_sizes += [burst_size] + return (signal, indexes, total_payload_len, burst_sizes) + def indexes_to_triggers(indexes, signal_len): + """ + Convert indexes to a mix of trigger signals and tags + """ + trigger_signal = [0] * signal_len + trigger_tags = [] + for index in indexes: + if random.random() > 0.5: + trigger_signal[index] = 1 + else: + trigger_tags += [make_tag('detect', True, index)] + return (trigger_signal, trigger_tags) + ### Go, go, go + # The divide-by-20 means we'll usually get the same random seed + # between the first run and the XML run. + random_seed = int(time.time()/20) + random.seed(random_seed) + print("Random seed: {0}".format(random_seed)) + n_bursts = 400 + header_len = 5 + max_gap = 50 + max_burstsize = 100 + fail_rate = 0.05 + signal, indexes, total_payload_len, burst_sizes = create_signal( + n_bursts, header_len, max_gap, max_burstsize, fail_rate + ) + trigger_signal, trigger_tags = indexes_to_triggers(indexes, len(signal)) + # Flow graph + data_src = blocks.vector_source_f( + signal, False, + tags=trigger_tags + ) + trigger_src = blocks.vector_source_b(trigger_signal, False) + hpd = digital.header_payload_demux( + header_len=header_len, + items_per_symbol=1, + guard_interval=0, + length_tag_key="frame_len", + trigger_tag_key="detect", + output_symbols=False, + itemsize=gr.sizeof_float, + timing_tag_key='rx_time', + samp_rate=1.0, + special_tags=('rx_freq',), + ) + mock_header_demod = HeaderToMessageBlock( + numpy.float32, + header_len, + burst_sizes + ) + header_sink = blocks.vector_sink_f() + payload_sink = blocks.vector_sink_f() + self.connect_all_blocks(data_src, trigger_src, hpd, mock_header_demod, payload_sink, header_sink) + self.run_tb(payload_sink, total_payload_len, header_sink, header_len*n_bursts) + self.assertEqual(header_sink.data(), tuple([1]*header_len*n_bursts)) + self.assertEqual(payload_sink.data(), tuple([2]*total_payload_len)) + if __name__ == '__main__': gr_unittest.run(qa_header_payload_demux, "qa_header_payload_demux.xml") diff --git a/grc/base/CMakeLists.txt b/gr-digital/python/grc_gnuradio/CMakeLists.txt index bdc8a5006f..f021299f1a 100644 --- a/grc/base/CMakeLists.txt +++ b/gr-digital/python/grc_gnuradio/CMakeLists.txt @@ -18,26 +18,13 @@ # Boston, MA 02110-1301, USA. ######################################################################## -GR_PYTHON_INSTALL(FILES - odict.py - ParseXML.py - Block.py - Connection.py - Constants.py - Element.py - FlowGraph.py - Param.py - Platform.py - Port.py - __init__.py - DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc/base - COMPONENT "grc" -) -install(FILES - block_tree.dtd - domain.dtd - flow_graph.dtd - DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc/base - COMPONENT "grc" +include(GrPython) + +# __init__ files come from gr-blocks + +GR_PYTHON_INSTALL(FILES + blks2/packet.py + DESTINATION ${GR_PYTHON_DIR}/grc_gnuradio/blks2 + COMPONENT "digital_python" ) diff --git a/grc/grc_gnuradio/blks2/packet.py b/gr-digital/python/grc_gnuradio/blks2/packet.py index ef79afde64..ef79afde64 100644 --- a/grc/grc_gnuradio/blks2/packet.py +++ b/gr-digital/python/grc_gnuradio/blks2/packet.py diff --git a/gr-dtv/CMakeLists.txt b/gr-dtv/CMakeLists.txt index fc7ab56bef..5a23482b26 100644 --- a/gr-dtv/CMakeLists.txt +++ b/gr-dtv/CMakeLists.txt @@ -41,6 +41,8 @@ GR_SET_GLOBAL(GR_DTV_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/lib ) +SET(GR_PKG_DTV_EXAMPLES_DIR ${GR_PKG_DATA_DIR}/examples/dtv) + ######################################################################## # Begin conditional configuration ######################################################################## diff --git a/gr-dtv/examples/CMakeLists.txt b/gr-dtv/examples/CMakeLists.txt index 82b663d924..8f209bce4f 100644 --- a/gr-dtv/examples/CMakeLists.txt +++ b/gr-dtv/examples/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2014 Free Software Foundation, Inc. +# Copyright 2014-2015 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -21,6 +21,31 @@ include(GrPython) GR_PYTHON_INSTALL( PROGRAMS + atsc_ctrlport_monitor.py + DESTINATION ${GR_PKG_DTV_EXAMPLES_DIR} + COMPONENT "dtv_python" +) + +install( + FILES + README.dvbs + README.dvbs2 + README.dvbt + README.dvbt2 + README.catv + dvbs2_tx.grc + dvbs_tx.grc + dvbt_rx_8k.grc + dvbt_tx_2k.grc + dvbt_tx_8k.grc + file_atsc_rx.grc + file_atsc_tx.grc + uhd_atsc_capture.grc + uhd_rx_atsc.grc + vv003-cr23.grc + vv009-4kfft.grc + vv018-miso.grc + catv_tx_64qam.grc DESTINATION ${GR_PKG_DTV_EXAMPLES_DIR} COMPONENT "dtv_python" ) diff --git a/gr-dtv/examples/README.catv b/gr-dtv/examples/README.catv new file mode 100644 index 0000000000..84c4cd0b26 --- /dev/null +++ b/gr-dtv/examples/README.catv @@ -0,0 +1,31 @@ +A test Transport Stream (26.970352 Mbps) for the example flow graph +is available here: + +http://www.w6rz.net/advqam64.ts + +It is 357,356,980 bytes. + +The three parameters Control_Word, I_taps and J_increment can be +used to control the interleaver from the following table. + +Control_Word I_taps J_increment Burst protection Latency + 0 128 1 95 us 4 ms + 1 128 1 95 us 4 ms + 2 128 2 190 us 8 ms + 3 64 2 47 us 2 ms + 4 128 3 285 us 12 ms + 5 32 4 24 us 0.98 ms + 6 128 4 379 us 16 ms + 7 16 8 12 us 0.48 ms + 8 128 5 474 us 20 ms + 9 8 16 5.9 us 0.22 ms + 10 128 6 569 us 24 ms + 11 Reserved + 12 128 7 664 us 28 ms + 13 Reserved + 14 128 8 759 us 32 ms + 15 Reserved + +The default is Control_Word = 6, I_taps = 128 and J_increment = 4 +which seems to be the most commonly used on CATV systems. + diff --git a/gr-dtv/examples/README.dvbs b/gr-dtv/examples/README.dvbs new file mode 100644 index 0000000000..d9097c4876 --- /dev/null +++ b/gr-dtv/examples/README.dvbs @@ -0,0 +1,26 @@ +Puncturing values for DVB-S code rates: + +1/2 code rate = Puncture Size = 2, Puncture Pattern = 0b11 +2/3 code rate = Puncture Size = 4, Puncture Pattern = 0b1101 +3/4 code rate = Puncture Size = 6, Puncture Pattern = 0b110110 +5/6 code rate = Puncture Size = 10, Puncture Pattern = 0b1101100110 +7/8 code rate = Puncture Size = 14, Puncture Pattern = 0b11010101100110 + +A test Transport Stream for the example flow graph +(8 Msyms/s and 7/8 code rate) is available here: + +http://www.w6rz.net/advdvbs78.ts + +It is 170,950,844 bytes. + +This stream can also be used at: + +14 Msyms/s 1/2 code rate +10.5 MSyms/s 2/3 code rate +9.333333 MSyms/s 3/4 code rate +8.4 Msyms/s 5/6 code rate + +The equation for calculating the correct Transport Stream bit-rate is: + +TS bit-rate = symbol rate * 2 * code rate * (188 / 204) + diff --git a/gr-dtv/examples/atsc_ctrlport_monitor.py b/gr-dtv/examples/atsc_ctrlport_monitor.py new file mode 100755 index 0000000000..7c43aebb77 --- /dev/null +++ b/gr-dtv/examples/atsc_ctrlport_monitor.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python +# +# Copyright 2015 Free Software Foundation +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import sys +import matplotlib +matplotlib.use("QT4Agg") +import matplotlib.pyplot as plt +import matplotlib.animation as animation +from gnuradio.ctrlport.GNURadioControlPortClient import GNURadioControlPortClient +import scipy +from scipy import fftpack + +""" +If a host is running the ATSC receiver chain with ControlPort +turned on, this script will connect to the host using the hostname and +port pair of the ControlPort instance and display metrics of the +receiver. The ATSC publishes information about the succes of the +Reed-Solomon decoder and Viterbi metrics for use here in displaying +the link quality. This also gets the equalizer taps of the receiver +and displays the frequency response. +""" + +class atsc_ctrlport_monitor: + def __init__(self, host, port): + argv = [None, host, port] + radiosys = GNURadioControlPortClient(argv=argv, rpcmethod='thrift') + self.radio = radiosys.client + print self.radio + + + vt_init_key = 'dtv_atsc_viterbi_decoder0::decoder_metrics' + data = self.radio.getKnobs([vt_init_key])[vt_init_key] + init_metric = scipy.mean(data.value) + self._viterbi_metric = 100*[init_metric,] + + table_col_labels = ('Num Packets', 'Error Rate', 'Packet Error Rate', + 'Viterbi Metric', 'SNR') + + self._fig = plt.figure(1, figsize=(12,12), facecolor='w') + self._sp0 = self._fig.add_subplot(4,1,1) + self._sp1 = self._fig.add_subplot(4,1,2) + self._sp2 = self._fig.add_subplot(4,1,3) + self._plot_taps = self._sp0.plot([], [], 'k', linewidth=2) + self._plot_psd = self._sp1.plot([], [], 'k', linewidth=2) + self._plot_data = self._sp2.plot([], [], 'ok', linewidth=2, markersize=4, alpha=0.05) + + self._ax2 = self._fig.add_subplot(4,1,4) + self._table = self._ax2.table(cellText=[len(table_col_labels)*['0']], + colLabels=table_col_labels, + loc='center') + self._ax2.axis('off') + cells = self._table.properties()['child_artists'] + for c in cells: + c.set_lw(0.1) # set's line width + c.set_ls('solid') + c.set_height(0.2) + + ani = animation.FuncAnimation(self._fig, self.update_data, frames=200, + fargs=(self._plot_taps[0], self._plot_psd[0], + self._plot_data[0], self._table), + init_func=self.init_function, + blit=True) + plt.show() + + def update_data(self, x, taps, psd, syms, table): + try: + eqdata_key = 'dtv_atsc_equalizer0::taps' + symdata_key = 'dtv_atsc_equalizer0::data' + rs_nump_key = 'dtv_atsc_rs_decoder0::num_packets' + rs_numbp_key = 'dtv_atsc_rs_decoder0::num_bad_packets' + rs_numerrs_key = 'dtv_atsc_rs_decoder0::num_errors_corrected' + vt_metrics_key = 'dtv_atsc_viterbi_decoder0::decoder_metrics' + snr_key = 'probe2_f0::SNR' + + data = self.radio.getKnobs([]) + eqdata = data[eqdata_key] + symdata = data[symdata_key] + rs_num_packets = data[rs_nump_key] + rs_num_bad_packets = data[rs_numbp_key] + rs_num_errors_corrected = data[rs_numerrs_key] + vt_decoder_metrics = data[vt_metrics_key] + snr_est = data[snr_key] + + vt_decoder_metrics = scipy.mean(vt_decoder_metrics.value) + self._viterbi_metric.pop() + self._viterbi_metric.insert(0, vt_decoder_metrics) + + except: + sys.stderr.write("Lost connection, exiting") + sys.exit(1) + + ntaps = len(eqdata.value) + taps.set_ydata(eqdata.value) + taps.set_xdata(xrange(ntaps)) + self._sp0.set_xlim(0, ntaps) + self._sp0.set_ylim(min(eqdata.value), max(eqdata.value)) + + fs = 6.25e6 + freq = scipy.linspace(-fs/2, fs/2, 10000) + H = fftpack.fftshift(fftpack.fft(eqdata.value, 10000)) + HdB = 20.0*scipy.log10(abs(H)) + psd.set_ydata(HdB) + psd.set_xdata(freq) + self._sp1.set_xlim(0, fs/2) + self._sp1.set_ylim([min(HdB), max(HdB)]) + self._sp1.set_yticks([min(HdB), max(HdB)]) + self._sp1.set_yticklabels(["min", "max"]) + + nsyms = len(symdata.value) + syms.set_ydata(symdata.value) + syms.set_xdata(nsyms*[0,]) + self._sp2.set_xlim([-1, 1]) + self._sp2.set_ylim([-10, 10]) + + per = float(rs_num_bad_packets.value) / float(rs_num_packets.value) + ber = float(rs_num_errors_corrected.value) / float(187*rs_num_packets.value) + + table._cells[(1,0)]._text.set_text("{0}".format(rs_num_packets.value)) + table._cells[(1,1)]._text.set_text("{0:.2g}".format(ber)) + table._cells[(1,2)]._text.set_text("{0:.2g}".format(per)) + table._cells[(1,3)]._text.set_text("{0:.1f}".format(scipy.mean(self._viterbi_metric))) + table._cells[(1,4)]._text.set_text("{0:.4f}".format(snr_est.value[0])) + + return (taps, psd, syms, table) + + def init_function(self): + return self._plot_taps + self._plot_psd + self._plot_data + +if __name__ == "__main__": + host = sys.argv[1] + port = sys.argv[2] + m = atsc_ctrlport_monitor(host, port) diff --git a/gr-dtv/examples/catv_tx_64qam.grc b/gr-dtv/examples/catv_tx_64qam.grc new file mode 100644 index 0000000000..964ef8470f --- /dev/null +++ b/gr-dtv/examples/catv_tx_64qam.grc @@ -0,0 +1,2079 @@ +<?xml version='1.0' encoding='utf-8'?> +<?grc format='1' created='3.7.10'?> +<flow_graph> + <timestamp>Thu Feb 20 21:02:41 2014</timestamp> + <block> + <key>options</key> + <param> + <key>author</key> + <value></value> + </param> + <param> + <key>window_size</key> + <value>1280, 1024</value> + </param> + <param> + <key>category</key> + <value>Custom</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>description</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(8, 11)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>generate_options</key> + <value>wx_gui</value> + </param> + <param> + <key>hier_block_src_path</key> + <value>.:</value> + </param> + <param> + <key>id</key> + <value>catv_tx_64qam</value> + </param> + <param> + <key>max_nouts</key> + <value>0</value> + </param> + <param> + <key>qt_qss_theme</key> + <value></value> + </param> + <param> + <key>realtime_scheduling</key> + <value></value> + </param> + <param> + <key>run_command</key> + <value>{python} -u {filename}</value> + </param> + <param> + <key>run_options</key> + <value>prompt</value> + </param> + <param> + <key>run</key> + <value>True</value> + </param> + <param> + <key>thread_safe_setters</key> + <value></value> + </param> + <param> + <key>title</key> + <value></value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(8, 299)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>Control_Word</value> + </param> + <param> + <key>value</key> + <value>6</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(8, 371)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>I_taps</value> + </param> + <param> + <key>value</key> + <value>128</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(8, 443)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>J_increment</value> + </param> + <param> + <key>value</key> + <value>4</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(8, 155)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>center_freq</value> + </param> + <param> + <key>value</key> + <value>429000000</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(8, 227)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>rrc_taps</value> + </param> + <param> + <key>value</key> + <value>100</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(8, 83)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>samp_rate</value> + </param> + <param> + <key>value</key> + <value>5056941 * 2</value> + </param> + </block> + <block> + <key>variable_slider</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>int_converter</value> + </param> + <param> + <key>value</key> + <value>-8</value> + </param> + <param> + <key>_enabled</key> + <value>1</value> + </param> + <param> + <key>_coordinate</key> + <value>(312, 11)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>vga1_gain</value> + </param> + <param> + <key>label</key> + <value>VGA1 Gain</value> + </param> + <param> + <key>max</key> + <value>-4</value> + </param> + <param> + <key>min</key> + <value>-35</value> + </param> + <param> + <key>notebook</key> + <value></value> + </param> + <param> + <key>num_steps</key> + <value>31</value> + </param> + <param> + <key>style</key> + <value>wx.SL_HORIZONTAL</value> + </param> + </block> + <block> + <key>variable_slider</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>int_converter</value> + </param> + <param> + <key>value</key> + <value>10</value> + </param> + <param> + <key>_enabled</key> + <value>1</value> + </param> + <param> + <key>_coordinate</key> + <value>(448, 11)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>vga2_gain</value> + </param> + <param> + <key>label</key> + <value>VGA2 Gain</value> + </param> + <param> + <key>max</key> + <value>25</value> + </param> + <param> + <key>min</key> + <value>0</value> + </param> + <param> + <key>notebook</key> + <value></value> + </param> + <param> + <key>num_steps</key> + <value>25</value> + </param> + <param> + <key>style</key> + <value>wx.SL_HORIZONTAL</value> + </param> + </block> + <block> + <key>blocks_file_source</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>file</key> + <value>/run/shm/advqam64.ts</value> + </param> + <param> + <key>_coordinate</key> + <value>(128, 163)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blocks_file_source_0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>type</key> + <value>byte</value> + </param> + <param> + <key>repeat</key> + <value>True</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + </block> + <block> + <key>blocks_packed_to_unpacked_xx</key> + <param> + <key>bits_per_chunk</key> + <value>7</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>endianness</key> + <value>gr.GR_MSB_FIRST</value> + </param> + <param> + <key>_coordinate</key> + <value>(624, 163)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blocks_packed_to_unpacked_xx_0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>num_ports</key> + <value>1</value> + </param> + <param> + <key>type</key> + <value>byte</value> + </param> + </block> + <block> + <key>blocks_stream_to_vector</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(536, 251)</value> + </param> + <param> + <key>_rotation</key> + <value>180</value> + </param> + <param> + <key>id</key> + <value>blocks_stream_to_vector_0</value> + </param> + <param> + <key>type</key> + <value>byte</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>num_items</key> + <value>I_taps</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + </block> + <block> + <key>dtv_catv_frame_sync_enc_bb</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>ctrlword</key> + <value>Control_Word</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(584, 347)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>dtv_catv_frame_sync_enc_bb_0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + </block> + <block> + <key>dtv_catv_randomizer_bb</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(424, 353)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>dtv_catv_randomizer_bb_0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + </block> + <block> + <key>dtv_catv_reed_solomon_enc_bb</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(848, 177)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>dtv_catv_reed_solomon_enc_bb_0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + </block> + <block> + <key>dtv_catv_transport_framing_enc_bb</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(360, 177)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>dtv_catv_transport_framing_enc_bb_0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + </block> + <block> + <key>dtv_catv_trellis_enc_bb</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(800, 353)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>dtv_catv_trellis_enc_bb_0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + </block> + <block> + <key>dtv_dvbs2_modulator_bc</key> + <param> + <key>interpolation</key> + <value>INTERPOLATION_ON</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>rate</key> + <value>C_OTHER</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>constellation</key> + <value>MOD_64QAM</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>framesize</key> + <value>FECFRAME_NORMAL</value> + </param> + <param> + <key>_coordinate</key> + <value>(400, 499)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>dtv_dvbs2_modulator_bc_0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + </block> + <block> + <key>dtv_dvbt_convolutional_interleaver</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>blocks</key> + <value>1</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>M</key> + <value>J_increment</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(168, 339)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>dtv_dvbt_convolutional_interleaver_0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>I</key> + <value>I_taps</value> + </param> + </block> + <block> + <key>fft_filter_xxx</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>decim</key> + <value>1</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(712, 507)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>fft_filter_xxx_0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>nthreads</key> + <value>1</value> + </param> + <param> + <key>samp_delay</key> + <value>0</value> + </param> + <param> + <key>taps</key> + <value>firdes.root_raised_cosine(0.14, samp_rate, samp_rate/2, 0.18, rrc_taps)</value> + </param> + <param> + <key>type</key> + <value>ccf</value> + </param> + </block> + <block> + <key>osmosdr_sink</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>ant0</key> + <value></value> + </param> + <param> + <key>bb_gain0</key> + <value>vga1_gain</value> + </param> + <param> + <key>bw0</key> + <value>6000000</value> + </param> + <param> + <key>corr0</key> + <value>0</value> + </param> + <param> + <key>freq0</key> + <value>center_freq</value> + </param> + <param> + <key>if_gain0</key> + <value>0</value> + </param> + <param> + <key>gain0</key> + <value>vga2_gain</value> + </param> + <param> + <key>ant10</key> + <value></value> + </param> + <param> + <key>bb_gain10</key> + <value>20</value> + </param> + <param> + <key>bw10</key> + <value>0</value> + </param> + <param> + <key>corr10</key> + <value>0</value> + </param> + <param> + <key>freq10</key> + <value>100e6</value> + </param> + <param> + <key>if_gain10</key> + <value>20</value> + </param> + <param> + <key>gain10</key> + <value>10</value> + </param> + <param> + <key>ant11</key> + <value></value> + </param> + <param> + <key>bb_gain11</key> + <value>20</value> + </param> + <param> + <key>bw11</key> + <value>0</value> + </param> + <param> + <key>corr11</key> + <value>0</value> + </param> + <param> + <key>freq11</key> + <value>100e6</value> + </param> + <param> + <key>if_gain11</key> + <value>20</value> + </param> + <param> + <key>gain11</key> + <value>10</value> + </param> + <param> + <key>ant12</key> + <value></value> + </param> + <param> + <key>bb_gain12</key> + <value>20</value> + </param> + <param> + <key>bw12</key> + <value>0</value> + </param> + <param> + <key>corr12</key> + <value>0</value> + </param> + <param> + <key>freq12</key> + <value>100e6</value> + </param> + <param> + <key>if_gain12</key> + <value>20</value> + </param> + <param> + <key>gain12</key> + <value>10</value> + </param> + <param> + <key>ant13</key> + <value></value> + </param> + <param> + <key>bb_gain13</key> + <value>20</value> + </param> + <param> + <key>bw13</key> + <value>0</value> + </param> + <param> + <key>corr13</key> + <value>0</value> + </param> + <param> + <key>freq13</key> + <value>100e6</value> + </param> + <param> + <key>if_gain13</key> + <value>20</value> + </param> + <param> + <key>gain13</key> + <value>10</value> + </param> + <param> + <key>ant14</key> + <value></value> + </param> + <param> + <key>bb_gain14</key> + <value>20</value> + </param> + <param> + <key>bw14</key> + <value>0</value> + </param> + <param> + <key>corr14</key> + <value>0</value> + </param> + <param> + <key>freq14</key> + <value>100e6</value> + </param> + <param> + <key>if_gain14</key> + <value>20</value> + </param> + <param> + <key>gain14</key> + <value>10</value> + </param> + <param> + <key>ant15</key> + <value></value> + </param> + <param> + <key>bb_gain15</key> + <value>20</value> + </param> + <param> + <key>bw15</key> + <value>0</value> + </param> + <param> + <key>corr15</key> + <value>0</value> + </param> + <param> + <key>freq15</key> + <value>100e6</value> + </param> + <param> + <key>if_gain15</key> + <value>20</value> + </param> + <param> + <key>gain15</key> + <value>10</value> + </param> + <param> + <key>ant16</key> + <value></value> + </param> + <param> + <key>bb_gain16</key> + <value>20</value> + </param> + <param> + <key>bw16</key> + <value>0</value> + </param> + <param> + <key>corr16</key> + <value>0</value> + </param> + <param> + <key>freq16</key> + <value>100e6</value> + </param> + <param> + <key>if_gain16</key> + <value>20</value> + </param> + <param> + <key>gain16</key> + <value>10</value> + </param> + <param> + <key>ant17</key> + <value></value> + </param> + <param> + <key>bb_gain17</key> + <value>20</value> + </param> + <param> + <key>bw17</key> + <value>0</value> + </param> + <param> + <key>corr17</key> + <value>0</value> + </param> + <param> + <key>freq17</key> + <value>100e6</value> + </param> + <param> + <key>if_gain17</key> + <value>20</value> + </param> + <param> + <key>gain17</key> + <value>10</value> + </param> + <param> + <key>ant18</key> + <value></value> + </param> + <param> + <key>bb_gain18</key> + <value>20</value> + </param> + <param> + <key>bw18</key> + <value>0</value> + </param> + <param> + <key>corr18</key> + <value>0</value> + </param> + <param> + <key>freq18</key> + <value>100e6</value> + </param> + <param> + <key>if_gain18</key> + <value>20</value> + </param> + <param> + <key>gain18</key> + <value>10</value> + </param> + <param> + <key>ant19</key> + <value></value> + </param> + <param> + <key>bb_gain19</key> + <value>20</value> + </param> + <param> + <key>bw19</key> + <value>0</value> + </param> + <param> + <key>corr19</key> + <value>0</value> + </param> + <param> + <key>freq19</key> + <value>100e6</value> + </param> + <param> + <key>if_gain19</key> + <value>20</value> + </param> + <param> + <key>gain19</key> + <value>10</value> + </param> + <param> + <key>ant1</key> + <value></value> + </param> + <param> + <key>bb_gain1</key> + <value>20</value> + </param> + <param> + <key>bw1</key> + <value>0</value> + </param> + <param> + <key>corr1</key> + <value>0</value> + </param> + <param> + <key>freq1</key> + <value>100e6</value> + </param> + <param> + <key>if_gain1</key> + <value>20</value> + </param> + <param> + <key>gain1</key> + <value>10</value> + </param> + <param> + <key>ant20</key> + <value></value> + </param> + <param> + <key>bb_gain20</key> + <value>20</value> + </param> + <param> + <key>bw20</key> + <value>0</value> + </param> + <param> + <key>corr20</key> + <value>0</value> + </param> + <param> + <key>freq20</key> + <value>100e6</value> + </param> + <param> + <key>if_gain20</key> + <value>20</value> + </param> + <param> + <key>gain20</key> + <value>10</value> + </param> + <param> + <key>ant21</key> + <value></value> + </param> + <param> + <key>bb_gain21</key> + <value>20</value> + </param> + <param> + <key>bw21</key> + <value>0</value> + </param> + <param> + <key>corr21</key> + <value>0</value> + </param> + <param> + <key>freq21</key> + <value>100e6</value> + </param> + <param> + <key>if_gain21</key> + <value>20</value> + </param> + <param> + <key>gain21</key> + <value>10</value> + </param> + <param> + <key>ant22</key> + <value></value> + </param> + <param> + <key>bb_gain22</key> + <value>20</value> + </param> + <param> + <key>bw22</key> + <value>0</value> + </param> + <param> + <key>corr22</key> + <value>0</value> + </param> + <param> + <key>freq22</key> + <value>100e6</value> + </param> + <param> + <key>if_gain22</key> + <value>20</value> + </param> + <param> + <key>gain22</key> + <value>10</value> + </param> + <param> + <key>ant23</key> + <value></value> + </param> + <param> + <key>bb_gain23</key> + <value>20</value> + </param> + <param> + <key>bw23</key> + <value>0</value> + </param> + <param> + <key>corr23</key> + <value>0</value> + </param> + <param> + <key>freq23</key> + <value>100e6</value> + </param> + <param> + <key>if_gain23</key> + <value>20</value> + </param> + <param> + <key>gain23</key> + <value>10</value> + </param> + <param> + <key>ant24</key> + <value></value> + </param> + <param> + <key>bb_gain24</key> + <value>20</value> + </param> + <param> + <key>bw24</key> + <value>0</value> + </param> + <param> + <key>corr24</key> + <value>0</value> + </param> + <param> + <key>freq24</key> + <value>100e6</value> + </param> + <param> + <key>if_gain24</key> + <value>20</value> + </param> + <param> + <key>gain24</key> + <value>10</value> + </param> + <param> + <key>ant25</key> + <value></value> + </param> + <param> + <key>bb_gain25</key> + <value>20</value> + </param> + <param> + <key>bw25</key> + <value>0</value> + </param> + <param> + <key>corr25</key> + <value>0</value> + </param> + <param> + <key>freq25</key> + <value>100e6</value> + </param> + <param> + <key>if_gain25</key> + <value>20</value> + </param> + <param> + <key>gain25</key> + <value>10</value> + </param> + <param> + <key>ant26</key> + <value></value> + </param> + <param> + <key>bb_gain26</key> + <value>20</value> + </param> + <param> + <key>bw26</key> + <value>0</value> + </param> + <param> + <key>corr26</key> + <value>0</value> + </param> + <param> + <key>freq26</key> + <value>100e6</value> + </param> + <param> + <key>if_gain26</key> + <value>20</value> + </param> + <param> + <key>gain26</key> + <value>10</value> + </param> + <param> + <key>ant27</key> + <value></value> + </param> + <param> + <key>bb_gain27</key> + <value>20</value> + </param> + <param> + <key>bw27</key> + <value>0</value> + </param> + <param> + <key>corr27</key> + <value>0</value> + </param> + <param> + <key>freq27</key> + <value>100e6</value> + </param> + <param> + <key>if_gain27</key> + <value>20</value> + </param> + <param> + <key>gain27</key> + <value>10</value> + </param> + <param> + <key>ant28</key> + <value></value> + </param> + <param> + <key>bb_gain28</key> + <value>20</value> + </param> + <param> + <key>bw28</key> + <value>0</value> + </param> + <param> + <key>corr28</key> + <value>0</value> + </param> + <param> + <key>freq28</key> + <value>100e6</value> + </param> + <param> + <key>if_gain28</key> + <value>20</value> + </param> + <param> + <key>gain28</key> + <value>10</value> + </param> + <param> + <key>ant29</key> + <value></value> + </param> + <param> + <key>bb_gain29</key> + <value>20</value> + </param> + <param> + <key>bw29</key> + <value>0</value> + </param> + <param> + <key>corr29</key> + <value>0</value> + </param> + <param> + <key>freq29</key> + <value>100e6</value> + </param> + <param> + <key>if_gain29</key> + <value>20</value> + </param> + <param> + <key>gain29</key> + <value>10</value> + </param> + <param> + <key>ant2</key> + <value></value> + </param> + <param> + <key>bb_gain2</key> + <value>20</value> + </param> + <param> + <key>bw2</key> + <value>0</value> + </param> + <param> + <key>corr2</key> + <value>0</value> + </param> + <param> + <key>freq2</key> + <value>100e6</value> + </param> + <param> + <key>if_gain2</key> + <value>20</value> + </param> + <param> + <key>gain2</key> + <value>10</value> + </param> + <param> + <key>ant30</key> + <value></value> + </param> + <param> + <key>bb_gain30</key> + <value>20</value> + </param> + <param> + <key>bw30</key> + <value>0</value> + </param> + <param> + <key>corr30</key> + <value>0</value> + </param> + <param> + <key>freq30</key> + <value>100e6</value> + </param> + <param> + <key>if_gain30</key> + <value>20</value> + </param> + <param> + <key>gain30</key> + <value>10</value> + </param> + <param> + <key>ant31</key> + <value></value> + </param> + <param> + <key>bb_gain31</key> + <value>20</value> + </param> + <param> + <key>bw31</key> + <value>0</value> + </param> + <param> + <key>corr31</key> + <value>0</value> + </param> + <param> + <key>freq31</key> + <value>100e6</value> + </param> + <param> + <key>if_gain31</key> + <value>20</value> + </param> + <param> + <key>gain31</key> + <value>10</value> + </param> + <param> + <key>ant3</key> + <value></value> + </param> + <param> + <key>bb_gain3</key> + <value>20</value> + </param> + <param> + <key>bw3</key> + <value>0</value> + </param> + <param> + <key>corr3</key> + <value>0</value> + </param> + <param> + <key>freq3</key> + <value>100e6</value> + </param> + <param> + <key>if_gain3</key> + <value>20</value> + </param> + <param> + <key>gain3</key> + <value>10</value> + </param> + <param> + <key>ant4</key> + <value></value> + </param> + <param> + <key>bb_gain4</key> + <value>20</value> + </param> + <param> + <key>bw4</key> + <value>0</value> + </param> + <param> + <key>corr4</key> + <value>0</value> + </param> + <param> + <key>freq4</key> + <value>100e6</value> + </param> + <param> + <key>if_gain4</key> + <value>20</value> + </param> + <param> + <key>gain4</key> + <value>10</value> + </param> + <param> + <key>ant5</key> + <value></value> + </param> + <param> + <key>bb_gain5</key> + <value>20</value> + </param> + <param> + <key>bw5</key> + <value>0</value> + </param> + <param> + <key>corr5</key> + <value>0</value> + </param> + <param> + <key>freq5</key> + <value>100e6</value> + </param> + <param> + <key>if_gain5</key> + <value>20</value> + </param> + <param> + <key>gain5</key> + <value>10</value> + </param> + <param> + <key>ant6</key> + <value></value> + </param> + <param> + <key>bb_gain6</key> + <value>20</value> + </param> + <param> + <key>bw6</key> + <value>0</value> + </param> + <param> + <key>corr6</key> + <value>0</value> + </param> + <param> + <key>freq6</key> + <value>100e6</value> + </param> + <param> + <key>if_gain6</key> + <value>20</value> + </param> + <param> + <key>gain6</key> + <value>10</value> + </param> + <param> + <key>ant7</key> + <value></value> + </param> + <param> + <key>bb_gain7</key> + <value>20</value> + </param> + <param> + <key>bw7</key> + <value>0</value> + </param> + <param> + <key>corr7</key> + <value>0</value> + </param> + <param> + <key>freq7</key> + <value>100e6</value> + </param> + <param> + <key>if_gain7</key> + <value>20</value> + </param> + <param> + <key>gain7</key> + <value>10</value> + </param> + <param> + <key>ant8</key> + <value></value> + </param> + <param> + <key>bb_gain8</key> + <value>20</value> + </param> + <param> + <key>bw8</key> + <value>0</value> + </param> + <param> + <key>corr8</key> + <value>0</value> + </param> + <param> + <key>freq8</key> + <value>100e6</value> + </param> + <param> + <key>if_gain8</key> + <value>20</value> + </param> + <param> + <key>gain8</key> + <value>10</value> + </param> + <param> + <key>ant9</key> + <value></value> + </param> + <param> + <key>bb_gain9</key> + <value>20</value> + </param> + <param> + <key>bw9</key> + <value>0</value> + </param> + <param> + <key>corr9</key> + <value>0</value> + </param> + <param> + <key>freq9</key> + <value>100e6</value> + </param> + <param> + <key>if_gain9</key> + <value>20</value> + </param> + <param> + <key>gain9</key> + <value>10</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>args</key> + <value>bladerf=0,buffers=128,buflen=32768</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1056, 467)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>osmosdr_sink_0</value> + </param> + <param> + <key>type</key> + <value>fc32</value> + </param> + <param> + <key>clock_source0</key> + <value></value> + </param> + <param> + <key>time_source0</key> + <value></value> + </param> + <param> + <key>clock_source1</key> + <value></value> + </param> + <param> + <key>time_source1</key> + <value></value> + </param> + <param> + <key>clock_source2</key> + <value></value> + </param> + <param> + <key>time_source2</key> + <value></value> + </param> + <param> + <key>clock_source3</key> + <value></value> + </param> + <param> + <key>time_source3</key> + <value></value> + </param> + <param> + <key>clock_source4</key> + <value></value> + </param> + <param> + <key>time_source4</key> + <value></value> + </param> + <param> + <key>clock_source5</key> + <value></value> + </param> + <param> + <key>time_source5</key> + <value></value> + </param> + <param> + <key>clock_source6</key> + <value></value> + </param> + <param> + <key>time_source6</key> + <value></value> + </param> + <param> + <key>clock_source7</key> + <value></value> + </param> + <param> + <key>time_source7</key> + <value></value> + </param> + <param> + <key>nchan</key> + <value>1</value> + </param> + <param> + <key>num_mboards</key> + <value>1</value> + </param> + <param> + <key>sample_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>sync</key> + <value></value> + </param> + </block> + <block> + <key>wxgui_fftsink2</key> + <param> + <key>avg_alpha</key> + <value>0.1333</value> + </param> + <param> + <key>average</key> + <value>True</value> + </param> + <param> + <key>baseband_freq</key> + <value>center_freq</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>1</value> + </param> + <param> + <key>fft_size</key> + <value>1024</value> + </param> + <param> + <key>freqvar</key> + <value>None</value> + </param> + <param> + <key>_coordinate</key> + <value>(1056, 203)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>wxgui_fftsink2_0_0</value> + </param> + <param> + <key>notebook</key> + <value></value> + </param> + <param> + <key>peak_hold</key> + <value>False</value> + </param> + <param> + <key>ref_level</key> + <value>-10</value> + </param> + <param> + <key>ref_scale</key> + <value>2.0</value> + </param> + <param> + <key>fft_rate</key> + <value>15</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>title</key> + <value>FFT Plot</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>win_size</key> + <value>640,480</value> + </param> + <param> + <key>win</key> + <value>None</value> + </param> + <param> + <key>y_divs</key> + <value>10</value> + </param> + <param> + <key>y_per_div</key> + <value>10</value> + </param> + </block> + <connection> + <source_block_id>blocks_file_source_0</source_block_id> + <sink_block_id>dtv_catv_transport_framing_enc_bb_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_packed_to_unpacked_xx_0</source_block_id> + <sink_block_id>dtv_catv_reed_solomon_enc_bb_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_stream_to_vector_0</source_block_id> + <sink_block_id>dtv_dvbt_convolutional_interleaver_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>dtv_catv_frame_sync_enc_bb_0</source_block_id> + <sink_block_id>dtv_catv_trellis_enc_bb_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>dtv_catv_randomizer_bb_0</source_block_id> + <sink_block_id>dtv_catv_frame_sync_enc_bb_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>dtv_catv_reed_solomon_enc_bb_0</source_block_id> + <sink_block_id>blocks_stream_to_vector_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>dtv_catv_transport_framing_enc_bb_0</source_block_id> + <sink_block_id>blocks_packed_to_unpacked_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>dtv_catv_trellis_enc_bb_0</source_block_id> + <sink_block_id>dtv_dvbs2_modulator_bc_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>dtv_dvbs2_modulator_bc_0</source_block_id> + <sink_block_id>fft_filter_xxx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>dtv_dvbt_convolutional_interleaver_0</source_block_id> + <sink_block_id>dtv_catv_randomizer_bb_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>fft_filter_xxx_0</source_block_id> + <sink_block_id>osmosdr_sink_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>fft_filter_xxx_0</source_block_id> + <sink_block_id>wxgui_fftsink2_0_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> +</flow_graph> diff --git a/gr-dtv/examples/dvbs2_tx.grc b/gr-dtv/examples/dvbs2_tx.grc index 47daa4cb9d..c6de308ae3 100644 --- a/gr-dtv/examples/dvbs2_tx.grc +++ b/gr-dtv/examples/dvbs2_tx.grc @@ -1,23 +1,23 @@ -<?xml version='1.0' encoding='ASCII'?> -<?grc format='1' created='3.7.7'?> +<?xml version='1.0' encoding='utf-8'?> +<?grc format='1' created='3.7.10'?> <flow_graph> <timestamp>Wed Sep 3 03:03:39 2014</timestamp> <block> <key>options</key> <param> - <key>id</key> - <value>dvbs2_tx</value> + <key>author</key> + <value></value> </param> <param> - <key>_enabled</key> - <value>True</value> + <key>window_size</key> + <value>1280, 1024</value> </param> <param> - <key>title</key> - <value></value> + <key>category</key> + <value>Custom</value> </param> <param> - <key>author</key> + <key>comment</key> <value></value> </param> <param> @@ -25,16 +25,44 @@ <value></value> </param> <param> - <key>window_size</key> - <value>1280, 1024</value> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(8, 11)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> </param> <param> <key>generate_options</key> <value>wx_gui</value> </param> <param> - <key>category</key> - <value>Custom</value> + <key>hier_block_src_path</key> + <value>.:</value> + </param> + <param> + <key>id</key> + <value>dvbs2_tx</value> + </param> + <param> + <key>max_nouts</key> + <value>0</value> + </param> + <param> + <key>qt_qss_theme</key> + <value></value> + </param> + <param> + <key>realtime_scheduling</key> + <value></value> + </param> + <param> + <key>run_command</key> + <value>{python} -u {filename}</value> </param> <param> <key>run_options</key> @@ -45,45 +73,79 @@ <value>True</value> </param> <param> - <key>max_nouts</key> - <value>0</value> + <key>thread_safe_setters</key> + <value></value> </param> <param> - <key>realtime_scheduling</key> + <key>title</key> <value></value> </param> + </block> + <block> + <key>variable</key> <param> - <key>alias</key> + <key>comment</key> <value></value> </param> <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> <key>_coordinate</key> - <value>(8, 11)</value> + <value>(8, 203)</value> </param> <param> <key>_rotation</key> <value>0</value> </param> + <param> + <key>id</key> + <value>frequency</value> + </param> + <param> + <key>value</key> + <value>1280e6</value> + </param> </block> <block> <key>variable</key> <param> - <key>id</key> - <value>samp_rate</value> + <key>comment</key> + <value></value> </param> <param> <key>_enabled</key> <value>True</value> </param> <param> + <key>_coordinate</key> + <value>(8, 267)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>rolloff</value> + </param> + <param> <key>value</key> - <value>symbol_rate * 2</value> + <value>0.2</value> </param> + </block> + <block> + <key>variable</key> <param> - <key>alias</key> + <key>comment</key> <value></value> </param> <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> <key>_coordinate</key> <value>(8, 75)</value> </param> @@ -91,107 +153,186 @@ <key>_rotation</key> <value>0</value> </param> + <param> + <key>id</key> + <value>samp_rate</value> + </param> + <param> + <key>value</key> + <value>symbol_rate * 2</value> + </param> </block> <block> <key>variable</key> <param> - <key>id</key> - <value>symbol_rate</value> + <key>comment</key> + <value></value> </param> <param> <key>_enabled</key> <value>True</value> </param> <param> + <key>_coordinate</key> + <value>(8, 139)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>symbol_rate</value> + </param> + <param> <key>value</key> <value>5000000</value> </param> + </block> + <block> + <key>variable</key> <param> - <key>alias</key> + <key>comment</key> <value></value> </param> <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> <key>_coordinate</key> - <value>(8, 139)</value> + <value>(8, 331)</value> </param> <param> <key>_rotation</key> <value>0</value> </param> - </block> - <block> - <key>variable</key> <param> <key>id</key> - <value>rolloff</value> + <value>taps</value> </param> <param> - <key>_enabled</key> - <value>True</value> + <key>value</key> + <value>100</value> </param> + </block> + <block> + <key>blocks_file_sink</key> <param> - <key>value</key> - <value>0.2</value> + <key>append</key> + <value>False</value> </param> <param> <key>alias</key> <value></value> </param> <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>file</key> + <value>adv.cfile</value> + </param> + <param> <key>_coordinate</key> - <value>(8, 203)</value> + <value>(784, 547)</value> </param> <param> <key>_rotation</key> <value>0</value> </param> - </block> - <block> - <key>variable</key> <param> <key>id</key> - <value>taps</value> + <value>blocks_file_sink_0</value> </param> <param> - <key>_enabled</key> - <value>True</value> + <key>type</key> + <value>complex</value> </param> <param> - <key>value</key> - <value>50</value> + <key>unbuffered</key> + <value>False</value> </param> <param> + <key>vlen</key> + <value>1</value> + </param> + </block> + <block> + <key>blocks_file_source</key> + <param> <key>alias</key> <value></value> </param> <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>file</key> + <value>/run/shm/adv16apsk910.ts</value> + </param> + <param> <key>_coordinate</key> - <value>(8, 267)</value> + <value>(176, 43)</value> </param> <param> <key>_rotation</key> <value>0</value> </param> - </block> - <block> - <key>dtv_dvb_bch_bb</key> <param> <key>id</key> - <value>dtv_dvb_bch_bb_0</value> + <value>blocks_file_source_0</value> </param> <param> - <key>_enabled</key> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>type</key> + <value>byte</value> + </param> + <param> + <key>repeat</key> <value>True</value> </param> <param> - <key>standard</key> - <value>STANDARD_DVBS2</value> + <key>vlen</key> + <value>1</value> </param> + </block> + <block> + <key>dtv_dvb_bbheader_bb</key> <param> - <key>framesize</key> + <key>mode</key> <value>FECFRAME_NORMAL</value> </param> <param> + <key>alias</key> + <value></value> + </param> + <param> <key>rate1</key> <value>C1_2</value> </param> @@ -208,7 +349,7 @@ <value>C1_4</value> </param> <param> - <key>alias</key> + <key>comment</key> <value></value> </param> <param> @@ -216,39 +357,59 @@ <value></value> </param> <param> - <key>minoutbuf</key> - <value>0</value> + <key>_enabled</key> + <value>True</value> </param> <param> - <key>maxoutbuf</key> - <value>0</value> + <key>fecblocks</key> + <value>168</value> + </param> + <param> + <key>framesize</key> + <value>FECFRAME_NORMAL</value> </param> <param> <key>_coordinate</key> - <value>(824, 35)</value> + <value>(400, 27)</value> </param> <param> <key>_rotation</key> <value>0</value> </param> - </block> - <block> - <key>dtv_dvb_bbscrambler_bb</key> <param> <key>id</key> - <value>dtv_dvb_bbscrambler_bb_0</value> + <value>dtv_dvb_bbheader_bb_0</value> </param> <param> - <key>_enabled</key> - <value>True</value> + <key>inband</key> + <value>INBAND_OFF</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>rolloff</key> + <value>RO_0_20</value> </param> <param> <key>standard</key> <value>STANDARD_DVBS2</value> </param> <param> - <key>framesize</key> - <value>FECFRAME_NORMAL</value> + <key>tsrate</key> + <value>4000000</value> + </param> + </block> + <block> + <key>dtv_dvb_bbscrambler_bb</key> + <param> + <key>alias</key> + <value></value> </param> <param> <key>rate1</key> @@ -267,7 +428,7 @@ <value>C1_4</value> </param> <param> - <key>alias</key> + <key>comment</key> <value></value> </param> <param> @@ -275,12 +436,12 @@ <value></value> </param> <param> - <key>minoutbuf</key> - <value>0</value> + <key>_enabled</key> + <value>True</value> </param> <param> - <key>maxoutbuf</key> - <value>0</value> + <key>framesize</key> + <value>FECFRAME_NORMAL</value> </param> <param> <key>_coordinate</key> @@ -290,24 +451,28 @@ <key>_rotation</key> <value>0</value> </param> - </block> - <block> - <key>dtv_dvb_ldpc_bb</key> <param> <key>id</key> - <value>dtv_dvb_ldpc_bb_0</value> + <value>dtv_dvb_bbscrambler_bb_0</value> </param> <param> - <key>_enabled</key> - <value>True</value> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> </param> <param> <key>standard</key> <value>STANDARD_DVBS2</value> </param> + </block> + <block> + <key>dtv_dvb_bch_bb</key> <param> - <key>framesize</key> - <value>FECFRAME_NORMAL</value> + <key>alias</key> + <value></value> </param> <param> <key>rate1</key> @@ -326,11 +491,7 @@ <value>C1_4</value> </param> <param> - <key>constellation</key> - <value>MOD_OTHER</value> - </param> - <param> - <key>alias</key> + <key>comment</key> <value></value> </param> <param> @@ -338,239 +499,310 @@ <value></value> </param> <param> - <key>minoutbuf</key> - <value>0</value> + <key>_enabled</key> + <value>True</value> </param> <param> - <key>maxoutbuf</key> - <value>0</value> + <key>framesize</key> + <value>FECFRAME_NORMAL</value> </param> <param> <key>_coordinate</key> - <value>(1048, 27)</value> + <value>(824, 35)</value> </param> <param> <key>_rotation</key> <value>0</value> </param> + <param> + <key>id</key> + <value>dtv_dvb_bch_bb_0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>standard</key> + <value>STANDARD_DVBS2</value> + </param> </block> <block> - <key>dtv_dvbs2_interleaver_bb</key> + <key>dtv_dvb_ldpc_bb</key> <param> - <key>id</key> - <value>dtv_dvbs2_interleaver_bb_0</value> + <key>alias</key> + <value></value> </param> <param> - <key>_enabled</key> - <value>True</value> + <key>rate1</key> + <value>C1_2</value> </param> <param> - <key>framesize</key> - <value>FECFRAME_NORMAL</value> + <key>rate2</key> + <value>C1_3</value> </param> <param> - <key>constellation</key> - <value>MOD_16APSK</value> + <key>rate3</key> + <value>C9_10</value> </param> <param> - <key>rate</key> - <value>C_OTHER</value> + <key>rate4</key> + <value>C1_4</value> </param> <param> - <key>alias</key> + <key>comment</key> <value></value> </param> <param> + <key>constellation</key> + <value>MOD_OTHER</value> + </param> + <param> <key>affinity</key> <value></value> </param> <param> - <key>minoutbuf</key> - <value>0</value> + <key>_enabled</key> + <value>True</value> </param> <param> - <key>maxoutbuf</key> - <value>0</value> + <key>framesize</key> + <value>FECFRAME_NORMAL</value> </param> <param> <key>_coordinate</key> - <value>(192, 235)</value> + <value>(1048, 27)</value> </param> <param> <key>_rotation</key> <value>0</value> </param> - </block> - <block> - <key>wxgui_fftsink2</key> <param> <key>id</key> - <value>wxgui_fftsink2_0</value> + <value>dtv_dvb_ldpc_bb_0</value> </param> <param> - <key>_enabled</key> - <value>True</value> + <key>maxoutbuf</key> + <value>0</value> </param> <param> - <key>type</key> - <value>complex</value> + <key>minoutbuf</key> + <value>0</value> </param> <param> - <key>title</key> - <value>FFT Plot</value> + <key>standard</key> + <value>STANDARD_DVBS2</value> </param> + </block> + <block> + <key>dtv_dvbs2_interleaver_bb</key> <param> - <key>samp_rate</key> - <value>samp_rate</value> + <key>alias</key> + <value></value> </param> <param> - <key>baseband_freq</key> - <value>1280000000</value> + <key>rate</key> + <value>C_OTHER</value> </param> <param> - <key>y_per_div</key> - <value>10</value> + <key>comment</key> + <value></value> </param> <param> - <key>y_divs</key> - <value>10</value> + <key>constellation</key> + <value>MOD_16APSK</value> </param> <param> - <key>ref_level</key> - <value>0</value> + <key>affinity</key> + <value></value> </param> <param> - <key>ref_scale</key> - <value>2.0</value> + <key>_enabled</key> + <value>True</value> </param> <param> - <key>fft_size</key> - <value>1024</value> + <key>framesize</key> + <value>FECFRAME_NORMAL</value> </param> <param> - <key>fft_rate</key> - <value>15</value> + <key>_coordinate</key> + <value>(192, 235)</value> </param> <param> - <key>peak_hold</key> - <value>False</value> + <key>_rotation</key> + <value>0</value> </param> <param> - <key>average</key> - <value>True</value> + <key>id</key> + <value>dtv_dvbs2_interleaver_bb_0</value> </param> <param> - <key>avg_alpha</key> - <value>0.13333</value> + <key>maxoutbuf</key> + <value>0</value> </param> <param> - <key>win</key> - <value>None</value> + <key>minoutbuf</key> + <value>0</value> </param> + </block> + <block> + <key>dtv_dvbs2_modulator_bc</key> <param> - <key>win_size</key> - <value></value> + <key>interpolation</key> + <value>INTERPOLATION_OFF</value> </param> <param> - <key>grid_pos</key> + <key>alias</key> <value></value> </param> <param> - <key>notebook</key> - <value></value> + <key>rate</key> + <value>C9_10</value> </param> <param> - <key>freqvar</key> - <value>None</value> + <key>comment</key> + <value></value> </param> <param> - <key>alias</key> - <value></value> + <key>constellation</key> + <value>MOD_16APSK</value> </param> <param> <key>affinity</key> <value></value> </param> <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>framesize</key> + <value>FECFRAME_NORMAL</value> + </param> + <param> <key>_coordinate</key> - <value>(1000, 395)</value> + <value>(472, 227)</value> </param> <param> <key>_rotation</key> <value>0</value> </param> - </block> - <block> - <key>dtv_dvbs2_modulator_bc</key> <param> <key>id</key> <value>dtv_dvbs2_modulator_bc_0</value> </param> <param> - <key>_enabled</key> - <value>True</value> + <key>maxoutbuf</key> + <value>0</value> </param> <param> - <key>framesize</key> - <value>FECFRAME_NORMAL</value> + <key>minoutbuf</key> + <value>0</value> </param> + </block> + <block> + <key>dtv_dvbs2_physical_cc</key> <param> - <key>constellation</key> - <value>MOD_16APSK</value> + <key>alias</key> + <value></value> </param> <param> <key>rate</key> <value>C9_10</value> </param> <param> - <key>alias</key> + <key>comment</key> <value></value> </param> <param> + <key>constellation</key> + <value>MOD_16APSK</value> + </param> + <param> <key>affinity</key> <value></value> </param> <param> - <key>minoutbuf</key> - <value>0</value> + <key>_enabled</key> + <value>True</value> </param> <param> - <key>maxoutbuf</key> - <value>0</value> + <key>framesize</key> + <value>FECFRAME_NORMAL</value> </param> <param> <key>_coordinate</key> - <value>(472, 235)</value> + <value>(192, 443)</value> </param> <param> <key>_rotation</key> <value>0</value> </param> + <param> + <key>goldcode</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>dtv_dvbs2_physical_cc_0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>pilots</key> + <value>PILOTS_ON</value> + </param> </block> <block> <key>fft_filter_xxx</key> <param> - <key>id</key> - <value>fft_filter_xxx_0</value> + <key>alias</key> + <value></value> </param> <param> - <key>_enabled</key> - <value>True</value> + <key>comment</key> + <value></value> </param> <param> - <key>type</key> - <value>ccc</value> + <key>affinity</key> + <value></value> </param> <param> <key>decim</key> <value>1</value> </param> <param> - <key>taps</key> - <value>firdes.root_raised_cosine(1, samp_rate, samp_rate/2, rolloff, taps)</value> + <key>_enabled</key> + <value>True</value> </param> <param> - <key>samp_delay</key> + <key>_coordinate</key> + <value>(472, 459)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>fft_filter_xxx_0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> <value>0</value> </param> <param> @@ -578,1014 +810,1458 @@ <value>1</value> </param> <param> + <key>samp_delay</key> + <value>0</value> + </param> + <param> + <key>taps</key> + <value>firdes.root_raised_cosine(1.0, samp_rate, samp_rate/2, rolloff, taps)</value> + </param> + <param> + <key>type</key> + <value>ccc</value> + </param> + </block> + <block> + <key>osmosdr_sink</key> + <param> <key>alias</key> <value></value> </param> <param> - <key>affinity</key> + <key>ant0</key> <value></value> </param> <param> - <key>minoutbuf</key> + <key>bb_gain0</key> + <value>-10</value> + </param> + <param> + <key>bw0</key> + <value>6000000</value> + </param> + <param> + <key>corr0</key> <value>0</value> </param> <param> - <key>maxoutbuf</key> + <key>freq0</key> + <value>frequency</value> + </param> + <param> + <key>if_gain0</key> <value>0</value> </param> <param> - <key>_coordinate</key> - <value>(472, 459)</value> + <key>gain0</key> + <value>15</value> </param> <param> - <key>_rotation</key> + <key>ant10</key> + <value></value> + </param> + <param> + <key>bb_gain10</key> + <value>20</value> + </param> + <param> + <key>bw10</key> <value>0</value> </param> - </block> - <block> - <key>dtv_dvbs2_physical_cc</key> <param> - <key>id</key> - <value>dtv_dvbs2_physical_cc_0</value> + <key>corr10</key> + <value>0</value> </param> <param> - <key>_enabled</key> - <value>True</value> + <key>freq10</key> + <value>100e6</value> </param> <param> - <key>framesize</key> - <value>FECFRAME_NORMAL</value> + <key>if_gain10</key> + <value>20</value> </param> <param> - <key>constellation</key> - <value>MOD_16APSK</value> + <key>gain10</key> + <value>10</value> </param> <param> - <key>rate</key> - <value>C9_10</value> + <key>ant11</key> + <value></value> </param> <param> - <key>pilots</key> - <value>PILOTS_ON</value> + <key>bb_gain11</key> + <value>20</value> </param> <param> - <key>goldcode</key> + <key>bw11</key> <value>0</value> </param> <param> - <key>alias</key> - <value></value> + <key>corr11</key> + <value>0</value> </param> <param> - <key>affinity</key> + <key>freq11</key> + <value>100e6</value> + </param> + <param> + <key>if_gain11</key> + <value>20</value> + </param> + <param> + <key>gain11</key> + <value>10</value> + </param> + <param> + <key>ant12</key> <value></value> </param> <param> - <key>minoutbuf</key> + <key>bb_gain12</key> + <value>20</value> + </param> + <param> + <key>bw12</key> <value>0</value> </param> <param> - <key>maxoutbuf</key> + <key>corr12</key> <value>0</value> </param> <param> - <key>_coordinate</key> - <value>(192, 443)</value> + <key>freq12</key> + <value>100e6</value> </param> <param> - <key>_rotation</key> + <key>if_gain12</key> + <value>20</value> + </param> + <param> + <key>gain12</key> + <value>10</value> + </param> + <param> + <key>ant13</key> + <value></value> + </param> + <param> + <key>bb_gain13</key> + <value>20</value> + </param> + <param> + <key>bw13</key> <value>0</value> </param> - </block> - <block> - <key>osmosdr_sink</key> <param> - <key>id</key> - <value>osmosdr_sink_0</value> + <key>corr13</key> + <value>0</value> </param> <param> - <key>_enabled</key> - <value>True</value> + <key>freq13</key> + <value>100e6</value> </param> <param> - <key>type</key> - <value>fc32</value> + <key>if_gain13</key> + <value>20</value> </param> <param> - <key>args</key> - <value>bladerf=0,buffers=128,buflen=32768</value> + <key>gain13</key> + <value>10</value> </param> <param> - <key>sync</key> + <key>ant14</key> <value></value> </param> <param> - <key>num_mboards</key> - <value>1</value> + <key>bb_gain14</key> + <value>20</value> </param> <param> - <key>clock_source0</key> - <value></value> + <key>bw14</key> + <value>0</value> </param> <param> - <key>time_source0</key> - <value></value> + <key>corr14</key> + <value>0</value> </param> <param> - <key>clock_source1</key> - <value></value> + <key>freq14</key> + <value>100e6</value> </param> <param> - <key>time_source1</key> - <value></value> + <key>if_gain14</key> + <value>20</value> </param> <param> - <key>clock_source2</key> - <value></value> + <key>gain14</key> + <value>10</value> </param> <param> - <key>time_source2</key> + <key>ant15</key> <value></value> </param> <param> - <key>clock_source3</key> - <value></value> + <key>bb_gain15</key> + <value>20</value> </param> <param> - <key>time_source3</key> - <value></value> + <key>bw15</key> + <value>0</value> </param> <param> - <key>clock_source4</key> - <value></value> + <key>corr15</key> + <value>0</value> </param> <param> - <key>time_source4</key> - <value></value> + <key>freq15</key> + <value>100e6</value> </param> <param> - <key>clock_source5</key> - <value></value> + <key>if_gain15</key> + <value>20</value> </param> <param> - <key>time_source5</key> - <value></value> + <key>gain15</key> + <value>10</value> </param> <param> - <key>clock_source6</key> + <key>ant16</key> <value></value> </param> <param> - <key>time_source6</key> - <value></value> + <key>bb_gain16</key> + <value>20</value> </param> <param> - <key>clock_source7</key> - <value></value> + <key>bw16</key> + <value>0</value> </param> <param> - <key>time_source7</key> - <value></value> + <key>corr16</key> + <value>0</value> </param> <param> - <key>nchan</key> - <value>1</value> + <key>freq16</key> + <value>100e6</value> </param> <param> - <key>sample_rate</key> - <value>samp_rate</value> + <key>if_gain16</key> + <value>20</value> </param> <param> - <key>freq0</key> - <value>1280e6</value> + <key>gain16</key> + <value>10</value> </param> <param> - <key>corr0</key> - <value>0</value> + <key>ant17</key> + <value></value> </param> <param> - <key>gain0</key> - <value>15</value> + <key>bb_gain17</key> + <value>20</value> </param> <param> - <key>if_gain0</key> + <key>bw17</key> <value>0</value> </param> <param> - <key>bb_gain0</key> - <value>-10</value> + <key>corr17</key> + <value>0</value> </param> <param> - <key>ant0</key> + <key>freq17</key> + <value>100e6</value> + </param> + <param> + <key>if_gain17</key> + <value>20</value> + </param> + <param> + <key>gain17</key> + <value>10</value> + </param> + <param> + <key>ant18</key> <value></value> </param> <param> - <key>bw0</key> - <value>6000000</value> + <key>bb_gain18</key> + <value>20</value> </param> <param> - <key>freq1</key> - <value>100e6</value> + <key>bw18</key> + <value>0</value> </param> <param> - <key>corr1</key> + <key>corr18</key> <value>0</value> </param> <param> - <key>gain1</key> + <key>freq18</key> + <value>100e6</value> + </param> + <param> + <key>if_gain18</key> + <value>20</value> + </param> + <param> + <key>gain18</key> <value>10</value> </param> <param> - <key>if_gain1</key> + <key>ant19</key> + <value></value> + </param> + <param> + <key>bb_gain19</key> <value>20</value> </param> <param> - <key>bb_gain1</key> + <key>bw19</key> + <value>0</value> + </param> + <param> + <key>corr19</key> + <value>0</value> + </param> + <param> + <key>freq19</key> + <value>100e6</value> + </param> + <param> + <key>if_gain19</key> <value>20</value> </param> <param> + <key>gain19</key> + <value>10</value> + </param> + <param> <key>ant1</key> <value></value> </param> <param> + <key>bb_gain1</key> + <value>20</value> + </param> + <param> <key>bw1</key> <value>0</value> </param> <param> - <key>freq2</key> + <key>corr1</key> + <value>0</value> + </param> + <param> + <key>freq1</key> <value>100e6</value> </param> <param> - <key>corr2</key> - <value>0</value> + <key>if_gain1</key> + <value>20</value> </param> <param> - <key>gain2</key> + <key>gain1</key> <value>10</value> </param> <param> - <key>if_gain2</key> - <value>20</value> + <key>ant20</key> + <value></value> </param> <param> - <key>bb_gain2</key> + <key>bb_gain20</key> <value>20</value> </param> <param> - <key>ant2</key> - <value></value> + <key>bw20</key> + <value>0</value> </param> <param> - <key>bw2</key> + <key>corr20</key> <value>0</value> </param> <param> - <key>freq3</key> + <key>freq20</key> <value>100e6</value> </param> <param> - <key>corr3</key> - <value>0</value> + <key>if_gain20</key> + <value>20</value> </param> <param> - <key>gain3</key> + <key>gain20</key> <value>10</value> </param> <param> - <key>if_gain3</key> - <value>20</value> + <key>ant21</key> + <value></value> </param> <param> - <key>bb_gain3</key> + <key>bb_gain21</key> <value>20</value> </param> <param> - <key>ant3</key> - <value></value> + <key>bw21</key> + <value>0</value> </param> <param> - <key>bw3</key> + <key>corr21</key> <value>0</value> </param> <param> - <key>freq4</key> + <key>freq21</key> <value>100e6</value> </param> <param> - <key>corr4</key> - <value>0</value> + <key>if_gain21</key> + <value>20</value> </param> <param> - <key>gain4</key> + <key>gain21</key> <value>10</value> </param> <param> - <key>if_gain4</key> - <value>20</value> + <key>ant22</key> + <value></value> </param> <param> - <key>bb_gain4</key> + <key>bb_gain22</key> <value>20</value> </param> <param> - <key>ant4</key> - <value></value> + <key>bw22</key> + <value>0</value> </param> <param> - <key>bw4</key> + <key>corr22</key> <value>0</value> </param> <param> - <key>freq5</key> + <key>freq22</key> <value>100e6</value> </param> <param> - <key>corr5</key> - <value>0</value> + <key>if_gain22</key> + <value>20</value> </param> <param> - <key>gain5</key> + <key>gain22</key> <value>10</value> </param> <param> - <key>if_gain5</key> - <value>20</value> + <key>ant23</key> + <value></value> </param> <param> - <key>bb_gain5</key> + <key>bb_gain23</key> <value>20</value> </param> <param> - <key>ant5</key> - <value></value> + <key>bw23</key> + <value>0</value> </param> <param> - <key>bw5</key> + <key>corr23</key> <value>0</value> </param> <param> - <key>freq6</key> + <key>freq23</key> <value>100e6</value> </param> <param> - <key>corr6</key> - <value>0</value> + <key>if_gain23</key> + <value>20</value> </param> <param> - <key>gain6</key> + <key>gain23</key> <value>10</value> </param> <param> - <key>if_gain6</key> - <value>20</value> + <key>ant24</key> + <value></value> </param> <param> - <key>bb_gain6</key> + <key>bb_gain24</key> <value>20</value> </param> <param> - <key>ant6</key> - <value></value> + <key>bw24</key> + <value>0</value> </param> <param> - <key>bw6</key> + <key>corr24</key> <value>0</value> </param> <param> - <key>freq7</key> + <key>freq24</key> <value>100e6</value> </param> <param> - <key>corr7</key> - <value>0</value> + <key>if_gain24</key> + <value>20</value> </param> <param> - <key>gain7</key> + <key>gain24</key> <value>10</value> </param> <param> - <key>if_gain7</key> - <value>20</value> + <key>ant25</key> + <value></value> </param> <param> - <key>bb_gain7</key> + <key>bb_gain25</key> <value>20</value> </param> <param> - <key>ant7</key> - <value></value> + <key>bw25</key> + <value>0</value> </param> <param> - <key>bw7</key> + <key>corr25</key> <value>0</value> </param> <param> - <key>freq8</key> + <key>freq25</key> <value>100e6</value> </param> <param> - <key>corr8</key> - <value>0</value> + <key>if_gain25</key> + <value>20</value> </param> <param> - <key>gain8</key> + <key>gain25</key> <value>10</value> </param> <param> - <key>if_gain8</key> - <value>20</value> + <key>ant26</key> + <value></value> </param> <param> - <key>bb_gain8</key> + <key>bb_gain26</key> <value>20</value> </param> <param> - <key>ant8</key> - <value></value> + <key>bw26</key> + <value>0</value> </param> <param> - <key>bw8</key> + <key>corr26</key> <value>0</value> </param> <param> - <key>freq9</key> + <key>freq26</key> <value>100e6</value> </param> <param> - <key>corr9</key> - <value>0</value> + <key>if_gain26</key> + <value>20</value> </param> <param> - <key>gain9</key> + <key>gain26</key> <value>10</value> </param> <param> - <key>if_gain9</key> - <value>20</value> + <key>ant27</key> + <value></value> </param> <param> - <key>bb_gain9</key> + <key>bb_gain27</key> <value>20</value> </param> <param> - <key>ant9</key> - <value></value> + <key>bw27</key> + <value>0</value> </param> <param> - <key>bw9</key> + <key>corr27</key> <value>0</value> </param> <param> - <key>freq10</key> + <key>freq27</key> <value>100e6</value> </param> <param> - <key>corr10</key> - <value>0</value> + <key>if_gain27</key> + <value>20</value> </param> <param> - <key>gain10</key> + <key>gain27</key> <value>10</value> </param> <param> - <key>if_gain10</key> - <value>20</value> + <key>ant28</key> + <value></value> </param> <param> - <key>bb_gain10</key> + <key>bb_gain28</key> <value>20</value> </param> <param> - <key>ant10</key> - <value></value> + <key>bw28</key> + <value>0</value> </param> <param> - <key>bw10</key> + <key>corr28</key> <value>0</value> </param> <param> - <key>freq11</key> + <key>freq28</key> <value>100e6</value> </param> <param> - <key>corr11</key> - <value>0</value> + <key>if_gain28</key> + <value>20</value> </param> <param> - <key>gain11</key> + <key>gain28</key> <value>10</value> </param> <param> - <key>if_gain11</key> - <value>20</value> + <key>ant29</key> + <value></value> </param> <param> - <key>bb_gain11</key> + <key>bb_gain29</key> <value>20</value> </param> <param> - <key>ant11</key> - <value></value> + <key>bw29</key> + <value>0</value> </param> <param> - <key>bw11</key> + <key>corr29</key> <value>0</value> </param> <param> - <key>freq12</key> + <key>freq29</key> <value>100e6</value> </param> <param> - <key>corr12</key> - <value>0</value> + <key>if_gain29</key> + <value>20</value> </param> <param> - <key>gain12</key> + <key>gain29</key> <value>10</value> </param> <param> - <key>if_gain12</key> - <value>20</value> + <key>ant2</key> + <value></value> </param> <param> - <key>bb_gain12</key> + <key>bb_gain2</key> <value>20</value> </param> <param> - <key>ant12</key> - <value></value> + <key>bw2</key> + <value>0</value> </param> <param> - <key>bw12</key> + <key>corr2</key> <value>0</value> </param> <param> - <key>freq13</key> + <key>freq2</key> <value>100e6</value> </param> <param> - <key>corr13</key> - <value>0</value> + <key>if_gain2</key> + <value>20</value> </param> <param> - <key>gain13</key> + <key>gain2</key> <value>10</value> </param> <param> - <key>if_gain13</key> - <value>20</value> + <key>ant30</key> + <value></value> </param> <param> - <key>bb_gain13</key> + <key>bb_gain30</key> <value>20</value> </param> <param> - <key>ant13</key> - <value></value> + <key>bw30</key> + <value>0</value> </param> <param> - <key>bw13</key> + <key>corr30</key> <value>0</value> </param> <param> - <key>freq14</key> + <key>freq30</key> <value>100e6</value> </param> <param> - <key>corr14</key> - <value>0</value> + <key>if_gain30</key> + <value>20</value> </param> <param> - <key>gain14</key> + <key>gain30</key> <value>10</value> </param> <param> - <key>if_gain14</key> - <value>20</value> + <key>ant31</key> + <value></value> </param> <param> - <key>bb_gain14</key> + <key>bb_gain31</key> <value>20</value> </param> <param> - <key>ant14</key> - <value></value> + <key>bw31</key> + <value>0</value> </param> <param> - <key>bw14</key> + <key>corr31</key> <value>0</value> </param> <param> - <key>freq15</key> + <key>freq31</key> <value>100e6</value> </param> <param> - <key>corr15</key> - <value>0</value> + <key>if_gain31</key> + <value>20</value> </param> <param> - <key>gain15</key> + <key>gain31</key> <value>10</value> </param> <param> - <key>if_gain15</key> + <key>ant3</key> + <value></value> + </param> + <param> + <key>bb_gain3</key> <value>20</value> </param> <param> - <key>bb_gain15</key> + <key>bw3</key> + <value>0</value> + </param> + <param> + <key>corr3</key> + <value>0</value> + </param> + <param> + <key>freq3</key> + <value>100e6</value> + </param> + <param> + <key>if_gain3</key> <value>20</value> </param> <param> - <key>ant15</key> + <key>gain3</key> + <value>10</value> + </param> + <param> + <key>ant4</key> <value></value> </param> <param> - <key>bw15</key> + <key>bb_gain4</key> + <value>20</value> + </param> + <param> + <key>bw4</key> <value>0</value> </param> <param> - <key>freq16</key> + <key>corr4</key> + <value>0</value> + </param> + <param> + <key>freq4</key> <value>100e6</value> </param> <param> - <key>corr16</key> - <value>0</value> + <key>if_gain4</key> + <value>20</value> </param> <param> - <key>gain16</key> + <key>gain4</key> <value>10</value> </param> <param> - <key>if_gain16</key> + <key>ant5</key> + <value></value> + </param> + <param> + <key>bb_gain5</key> <value>20</value> </param> <param> - <key>bb_gain16</key> + <key>bw5</key> + <value>0</value> + </param> + <param> + <key>corr5</key> + <value>0</value> + </param> + <param> + <key>freq5</key> + <value>100e6</value> + </param> + <param> + <key>if_gain5</key> <value>20</value> </param> <param> - <key>ant16</key> + <key>gain5</key> + <value>10</value> + </param> + <param> + <key>ant6</key> <value></value> </param> <param> - <key>bw16</key> + <key>bb_gain6</key> + <value>20</value> + </param> + <param> + <key>bw6</key> <value>0</value> </param> <param> - <key>freq17</key> + <key>corr6</key> + <value>0</value> + </param> + <param> + <key>freq6</key> <value>100e6</value> </param> <param> - <key>corr17</key> - <value>0</value> + <key>if_gain6</key> + <value>20</value> </param> <param> - <key>gain17</key> + <key>gain6</key> <value>10</value> </param> <param> - <key>if_gain17</key> + <key>ant7</key> + <value></value> + </param> + <param> + <key>bb_gain7</key> <value>20</value> </param> <param> - <key>bb_gain17</key> + <key>bw7</key> + <value>0</value> + </param> + <param> + <key>corr7</key> + <value>0</value> + </param> + <param> + <key>freq7</key> + <value>100e6</value> + </param> + <param> + <key>if_gain7</key> <value>20</value> </param> <param> - <key>ant17</key> + <key>gain7</key> + <value>10</value> + </param> + <param> + <key>ant8</key> <value></value> </param> <param> - <key>bw17</key> + <key>bb_gain8</key> + <value>20</value> + </param> + <param> + <key>bw8</key> <value>0</value> </param> <param> - <key>freq18</key> + <key>corr8</key> + <value>0</value> + </param> + <param> + <key>freq8</key> <value>100e6</value> </param> <param> - <key>corr18</key> - <value>0</value> + <key>if_gain8</key> + <value>20</value> </param> <param> - <key>gain18</key> + <key>gain8</key> <value>10</value> </param> <param> - <key>if_gain18</key> + <key>ant9</key> + <value></value> + </param> + <param> + <key>bb_gain9</key> <value>20</value> </param> <param> - <key>bb_gain18</key> + <key>bw9</key> + <value>0</value> + </param> + <param> + <key>corr9</key> + <value>0</value> + </param> + <param> + <key>freq9</key> + <value>100e6</value> + </param> + <param> + <key>if_gain9</key> <value>20</value> </param> <param> - <key>ant18</key> + <key>gain9</key> + <value>10</value> + </param> + <param> + <key>comment</key> <value></value> </param> <param> - <key>bw18</key> + <key>affinity</key> + <value></value> + </param> + <param> + <key>args</key> + <value>bladerf=0,buffers=128,buflen=32768</value> + </param> + <param> + <key>_enabled</key> <value>0</value> </param> <param> - <key>freq19</key> - <value>100e6</value> + <key>_coordinate</key> + <value>(1040, 243)</value> </param> <param> - <key>corr19</key> + <key>_rotation</key> <value>0</value> </param> <param> - <key>gain19</key> - <value>10</value> + <key>id</key> + <value>osmosdr_sink_0</value> </param> <param> - <key>if_gain19</key> - <value>20</value> + <key>type</key> + <value>fc32</value> </param> <param> - <key>bb_gain19</key> - <value>20</value> + <key>clock_source0</key> + <value></value> </param> <param> - <key>ant19</key> + <key>time_source0</key> <value></value> </param> <param> - <key>bw19</key> + <key>clock_source1</key> + <value></value> + </param> + <param> + <key>time_source1</key> + <value></value> + </param> + <param> + <key>clock_source2</key> + <value></value> + </param> + <param> + <key>time_source2</key> + <value></value> + </param> + <param> + <key>clock_source3</key> + <value></value> + </param> + <param> + <key>time_source3</key> + <value></value> + </param> + <param> + <key>clock_source4</key> + <value></value> + </param> + <param> + <key>time_source4</key> + <value></value> + </param> + <param> + <key>clock_source5</key> + <value></value> + </param> + <param> + <key>time_source5</key> + <value></value> + </param> + <param> + <key>clock_source6</key> + <value></value> + </param> + <param> + <key>time_source6</key> + <value></value> + </param> + <param> + <key>clock_source7</key> + <value></value> + </param> + <param> + <key>time_source7</key> + <value></value> + </param> + <param> + <key>nchan</key> + <value>1</value> + </param> + <param> + <key>num_mboards</key> + <value>1</value> + </param> + <param> + <key>sample_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>sync</key> + <value></value> + </param> + </block> + <block> + <key>uhd_usrp_sink</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>ant0</key> + <value></value> + </param> + <param> + <key>bw0</key> <value>0</value> </param> <param> - <key>freq20</key> - <value>100e6</value> + <key>center_freq0</key> + <value>uhd.tune_request(frequency, ((symbol_rate * (1 + rolloff)) / 2 ) + 1e5)</value> </param> <param> - <key>corr20</key> + <key>norm_gain0</key> + <value>False</value> + </param> + <param> + <key>gain0</key> + <value>60</value> + </param> + <param> + <key>ant10</key> + <value></value> + </param> + <param> + <key>bw10</key> <value>0</value> </param> <param> - <key>gain20</key> - <value>10</value> + <key>center_freq10</key> + <value>0</value> </param> <param> - <key>if_gain20</key> - <value>20</value> + <key>norm_gain10</key> + <value>False</value> </param> <param> - <key>bb_gain20</key> - <value>20</value> + <key>gain10</key> + <value>0</value> </param> <param> - <key>ant20</key> + <key>ant11</key> <value></value> </param> <param> - <key>bw20</key> + <key>bw11</key> <value>0</value> </param> <param> - <key>freq21</key> - <value>100e6</value> + <key>center_freq11</key> + <value>0</value> </param> <param> - <key>corr21</key> + <key>norm_gain11</key> + <value>False</value> + </param> + <param> + <key>gain11</key> <value>0</value> </param> <param> - <key>gain21</key> - <value>10</value> + <key>ant12</key> + <value></value> </param> <param> - <key>if_gain21</key> - <value>20</value> + <key>bw12</key> + <value>0</value> </param> <param> - <key>bb_gain21</key> - <value>20</value> + <key>center_freq12</key> + <value>0</value> </param> <param> - <key>ant21</key> + <key>norm_gain12</key> + <value>False</value> + </param> + <param> + <key>gain12</key> + <value>0</value> + </param> + <param> + <key>ant13</key> <value></value> </param> <param> - <key>bw21</key> + <key>bw13</key> <value>0</value> </param> <param> - <key>freq22</key> - <value>100e6</value> + <key>center_freq13</key> + <value>0</value> </param> <param> - <key>corr22</key> + <key>norm_gain13</key> + <value>False</value> + </param> + <param> + <key>gain13</key> <value>0</value> </param> <param> - <key>gain22</key> - <value>10</value> + <key>ant14</key> + <value></value> </param> <param> - <key>if_gain22</key> - <value>20</value> + <key>bw14</key> + <value>0</value> </param> <param> - <key>bb_gain22</key> - <value>20</value> + <key>center_freq14</key> + <value>0</value> </param> <param> - <key>ant22</key> + <key>norm_gain14</key> + <value>False</value> + </param> + <param> + <key>gain14</key> + <value>0</value> + </param> + <param> + <key>ant15</key> <value></value> </param> <param> - <key>bw22</key> + <key>bw15</key> <value>0</value> </param> <param> - <key>freq23</key> - <value>100e6</value> + <key>center_freq15</key> + <value>0</value> </param> <param> - <key>corr23</key> + <key>norm_gain15</key> + <value>False</value> + </param> + <param> + <key>gain15</key> <value>0</value> </param> <param> - <key>gain23</key> - <value>10</value> + <key>ant16</key> + <value></value> </param> <param> - <key>if_gain23</key> - <value>20</value> + <key>bw16</key> + <value>0</value> </param> <param> - <key>bb_gain23</key> - <value>20</value> + <key>center_freq16</key> + <value>0</value> </param> <param> - <key>ant23</key> + <key>norm_gain16</key> + <value>False</value> + </param> + <param> + <key>gain16</key> + <value>0</value> + </param> + <param> + <key>ant17</key> <value></value> </param> <param> - <key>bw23</key> + <key>bw17</key> <value>0</value> </param> <param> - <key>freq24</key> - <value>100e6</value> + <key>center_freq17</key> + <value>0</value> </param> <param> - <key>corr24</key> + <key>norm_gain17</key> + <value>False</value> + </param> + <param> + <key>gain17</key> <value>0</value> </param> <param> - <key>gain24</key> - <value>10</value> + <key>ant18</key> + <value></value> </param> <param> - <key>if_gain24</key> - <value>20</value> + <key>bw18</key> + <value>0</value> </param> <param> - <key>bb_gain24</key> - <value>20</value> + <key>center_freq18</key> + <value>0</value> </param> <param> - <key>ant24</key> + <key>norm_gain18</key> + <value>False</value> + </param> + <param> + <key>gain18</key> + <value>0</value> + </param> + <param> + <key>ant19</key> <value></value> </param> <param> - <key>bw24</key> + <key>bw19</key> <value>0</value> </param> <param> - <key>freq25</key> - <value>100e6</value> + <key>center_freq19</key> + <value>0</value> </param> <param> - <key>corr25</key> + <key>norm_gain19</key> + <value>False</value> + </param> + <param> + <key>gain19</key> <value>0</value> </param> <param> - <key>gain25</key> - <value>10</value> + <key>ant1</key> + <value></value> </param> <param> - <key>if_gain25</key> - <value>20</value> + <key>bw1</key> + <value>0</value> </param> <param> - <key>bb_gain25</key> - <value>20</value> + <key>center_freq1</key> + <value>0</value> </param> <param> - <key>ant25</key> + <key>norm_gain1</key> + <value>False</value> + </param> + <param> + <key>gain1</key> + <value>0</value> + </param> + <param> + <key>ant20</key> <value></value> </param> <param> - <key>bw25</key> + <key>bw20</key> <value>0</value> </param> <param> - <key>freq26</key> - <value>100e6</value> + <key>center_freq20</key> + <value>0</value> </param> <param> - <key>corr26</key> + <key>norm_gain20</key> + <value>False</value> + </param> + <param> + <key>gain20</key> <value>0</value> </param> <param> - <key>gain26</key> - <value>10</value> + <key>ant21</key> + <value></value> </param> <param> - <key>if_gain26</key> - <value>20</value> + <key>bw21</key> + <value>0</value> </param> <param> - <key>bb_gain26</key> - <value>20</value> + <key>center_freq21</key> + <value>0</value> </param> <param> - <key>ant26</key> + <key>norm_gain21</key> + <value>False</value> + </param> + <param> + <key>gain21</key> + <value>0</value> + </param> + <param> + <key>ant22</key> <value></value> </param> <param> - <key>bw26</key> + <key>bw22</key> <value>0</value> </param> <param> - <key>freq27</key> - <value>100e6</value> + <key>center_freq22</key> + <value>0</value> </param> <param> - <key>corr27</key> + <key>norm_gain22</key> + <value>False</value> + </param> + <param> + <key>gain22</key> <value>0</value> </param> <param> - <key>gain27</key> - <value>10</value> + <key>ant23</key> + <value></value> </param> <param> - <key>if_gain27</key> - <value>20</value> + <key>bw23</key> + <value>0</value> </param> <param> - <key>bb_gain27</key> - <value>20</value> + <key>center_freq23</key> + <value>0</value> </param> <param> - <key>ant27</key> + <key>norm_gain23</key> + <value>False</value> + </param> + <param> + <key>gain23</key> + <value>0</value> + </param> + <param> + <key>ant24</key> <value></value> </param> <param> - <key>bw27</key> + <key>bw24</key> <value>0</value> </param> <param> - <key>freq28</key> - <value>100e6</value> + <key>center_freq24</key> + <value>0</value> </param> <param> - <key>corr28</key> + <key>norm_gain24</key> + <value>False</value> + </param> + <param> + <key>gain24</key> <value>0</value> </param> <param> - <key>gain28</key> - <value>10</value> + <key>ant25</key> + <value></value> </param> <param> - <key>if_gain28</key> - <value>20</value> + <key>bw25</key> + <value>0</value> </param> <param> - <key>bb_gain28</key> - <value>20</value> + <key>center_freq25</key> + <value>0</value> </param> <param> - <key>ant28</key> + <key>norm_gain25</key> + <value>False</value> + </param> + <param> + <key>gain25</key> + <value>0</value> + </param> + <param> + <key>ant26</key> <value></value> </param> <param> - <key>bw28</key> + <key>bw26</key> <value>0</value> </param> <param> - <key>freq29</key> - <value>100e6</value> + <key>center_freq26</key> + <value>0</value> </param> <param> - <key>corr29</key> + <key>norm_gain26</key> + <value>False</value> + </param> + <param> + <key>gain26</key> <value>0</value> </param> <param> - <key>gain29</key> - <value>10</value> + <key>ant27</key> + <value></value> </param> <param> - <key>if_gain29</key> - <value>20</value> + <key>bw27</key> + <value>0</value> </param> <param> - <key>bb_gain29</key> - <value>20</value> + <key>center_freq27</key> + <value>0</value> + </param> + <param> + <key>norm_gain27</key> + <value>False</value> + </param> + <param> + <key>gain27</key> + <value>0</value> + </param> + <param> + <key>ant28</key> + <value></value> + </param> + <param> + <key>bw28</key> + <value>0</value> + </param> + <param> + <key>center_freq28</key> + <value>0</value> + </param> + <param> + <key>norm_gain28</key> + <value>False</value> + </param> + <param> + <key>gain28</key> + <value>0</value> </param> <param> <key>ant29</key> @@ -1596,24 +2272,36 @@ <value>0</value> </param> <param> - <key>freq30</key> - <value>100e6</value> + <key>center_freq29</key> + <value>0</value> </param> <param> - <key>corr30</key> + <key>norm_gain29</key> + <value>False</value> + </param> + <param> + <key>gain29</key> <value>0</value> </param> <param> - <key>gain30</key> - <value>10</value> + <key>ant2</key> + <value></value> </param> <param> - <key>if_gain30</key> - <value>20</value> + <key>bw2</key> + <value>0</value> </param> <param> - <key>bb_gain30</key> - <value>20</value> + <key>center_freq2</key> + <value>0</value> + </param> + <param> + <key>norm_gain2</key> + <value>False</value> + </param> + <param> + <key>gain2</key> + <value>0</value> </param> <param> <key>ant30</key> @@ -1624,82 +2312,183 @@ <value>0</value> </param> <param> - <key>freq31</key> - <value>100e6</value> + <key>center_freq30</key> + <value>0</value> </param> <param> - <key>corr31</key> + <key>norm_gain30</key> + <value>False</value> + </param> + <param> + <key>gain30</key> <value>0</value> </param> <param> + <key>ant31</key> + <value></value> + </param> + <param> + <key>bw31</key> + <value>0</value> + </param> + <param> + <key>center_freq31</key> + <value>0</value> + </param> + <param> + <key>norm_gain31</key> + <value>False</value> + </param> + <param> <key>gain31</key> - <value>10</value> + <value>0</value> </param> <param> - <key>if_gain31</key> - <value>20</value> + <key>ant3</key> + <value></value> </param> <param> - <key>bb_gain31</key> - <value>20</value> + <key>bw3</key> + <value>0</value> </param> <param> - <key>ant31</key> + <key>center_freq3</key> + <value>0</value> + </param> + <param> + <key>norm_gain3</key> + <value>False</value> + </param> + <param> + <key>gain3</key> + <value>0</value> + </param> + <param> + <key>ant4</key> <value></value> </param> <param> - <key>bw31</key> + <key>bw4</key> <value>0</value> </param> <param> - <key>alias</key> + <key>center_freq4</key> + <value>0</value> + </param> + <param> + <key>norm_gain4</key> + <value>False</value> + </param> + <param> + <key>gain4</key> + <value>0</value> + </param> + <param> + <key>ant5</key> <value></value> </param> <param> - <key>affinity</key> + <key>bw5</key> + <value>0</value> + </param> + <param> + <key>center_freq5</key> + <value>0</value> + </param> + <param> + <key>norm_gain5</key> + <value>False</value> + </param> + <param> + <key>gain5</key> + <value>0</value> + </param> + <param> + <key>ant6</key> <value></value> </param> <param> - <key>_coordinate</key> - <value>(1000, 203)</value> + <key>bw6</key> + <value>0</value> </param> <param> - <key>_rotation</key> + <key>center_freq6</key> <value>0</value> </param> - </block> - <block> - <key>blocks_file_sink</key> <param> - <key>id</key> - <value>blocks_file_sink_0</value> + <key>norm_gain6</key> + <value>False</value> </param> <param> - <key>_enabled</key> + <key>gain6</key> + <value>0</value> + </param> + <param> + <key>ant7</key> + <value></value> + </param> + <param> + <key>bw7</key> + <value>0</value> + </param> + <param> + <key>center_freq7</key> + <value>0</value> + </param> + <param> + <key>norm_gain7</key> <value>False</value> </param> <param> - <key>file</key> - <value>adv.cfile</value> + <key>gain7</key> + <value>0</value> </param> <param> - <key>type</key> - <value>complex</value> + <key>ant8</key> + <value></value> </param> <param> - <key>vlen</key> - <value>1</value> + <key>bw8</key> + <value>0</value> </param> <param> - <key>unbuffered</key> + <key>center_freq8</key> + <value>0</value> + </param> + <param> + <key>norm_gain8</key> <value>False</value> </param> <param> - <key>append</key> + <key>gain8</key> + <value>0</value> + </param> + <param> + <key>ant9</key> + <value></value> + </param> + <param> + <key>bw9</key> + <value>0</value> + </param> + <param> + <key>center_freq9</key> + <value>0</value> + </param> + <param> + <key>norm_gain9</key> <value>False</value> </param> <param> - <key>alias</key> + <key>gain9</key> + <value>0</value> + </param> + <param> + <key>clock_rate</key> + <value>0.0</value> + </param> + <param> + <key>comment</key> <value></value> </param> <param> @@ -1707,163 +2496,270 @@ <value></value> </param> <param> + <key>dev_addr</key> + <value>"send_frame_size=65536,num_send_frames=256,master_clock_rate=" + str(samp_rate*2)</value> + </param> + <param> + <key>dev_args</key> + <value>""</value> + </param> + <param> + <key>_enabled</key> + <value>1</value> + </param> + <param> <key>_coordinate</key> - <value>(792, 547)</value> + <value>(1040, 427)</value> </param> <param> <key>_rotation</key> <value>0</value> </param> - </block> - <block> - <key>dtv_dvb_bbheader_bb</key> <param> <key>id</key> - <value>dtv_dvb_bbheader_bb_0</value> + <value>uhd_usrp_sink_0_0</value> </param> <param> - <key>_enabled</key> - <value>True</value> + <key>type</key> + <value>fc32</value> </param> <param> - <key>standard</key> - <value>STANDARD_DVBS2</value> + <key>clock_source0</key> + <value></value> </param> <param> - <key>framesize</key> - <value>FECFRAME_NORMAL</value> + <key>sd_spec0</key> + <value></value> </param> <param> - <key>rate1</key> - <value>C1_2</value> + <key>time_source0</key> + <value></value> </param> <param> - <key>rate2</key> - <value>C1_3</value> + <key>clock_source1</key> + <value></value> </param> <param> - <key>rate3</key> - <value>C9_10</value> + <key>sd_spec1</key> + <value></value> </param> <param> - <key>rate4</key> - <value>C1_4</value> + <key>time_source1</key> + <value></value> </param> <param> - <key>rolloff</key> - <value>RO_0_20</value> + <key>clock_source2</key> + <value></value> </param> <param> - <key>mode</key> - <value>FECFRAME_NORMAL</value> + <key>sd_spec2</key> + <value></value> </param> <param> - <key>inband</key> - <value>INBAND_OFF</value> + <key>time_source2</key> + <value></value> </param> <param> - <key>fecblocks</key> - <value>168</value> + <key>clock_source3</key> + <value></value> </param> <param> - <key>tsrate</key> - <value>4000000</value> + <key>sd_spec3</key> + <value></value> </param> <param> - <key>alias</key> + <key>time_source3</key> <value></value> </param> <param> - <key>affinity</key> + <key>clock_source4</key> <value></value> </param> <param> - <key>minoutbuf</key> - <value>0</value> + <key>sd_spec4</key> + <value></value> </param> <param> - <key>maxoutbuf</key> - <value>0</value> + <key>time_source4</key> + <value></value> </param> <param> - <key>_coordinate</key> - <value>(400, 27)</value> + <key>clock_source5</key> + <value></value> </param> <param> - <key>_rotation</key> - <value>0</value> + <key>sd_spec5</key> + <value></value> </param> - </block> - <block> - <key>blocks_file_source</key> <param> - <key>id</key> - <value>blocks_file_source_0</value> + <key>time_source5</key> + <value></value> </param> <param> - <key>_enabled</key> - <value>True</value> + <key>clock_source6</key> + <value></value> </param> <param> - <key>file</key> - <value>/run/shm/adv16apsk910.ts</value> + <key>sd_spec6</key> + <value></value> </param> <param> - <key>type</key> - <value>byte</value> + <key>time_source6</key> + <value></value> </param> <param> - <key>repeat</key> - <value>True</value> + <key>clock_source7</key> + <value></value> </param> <param> - <key>vlen</key> + <key>sd_spec7</key> + <value></value> + </param> + <param> + <key>time_source7</key> + <value></value> + </param> + <param> + <key>nchan</key> <value>1</value> </param> <param> + <key>num_mboards</key> + <value>1</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>hide_cmd_port</key> + <value>False</value> + </param> + <param> + <key>stream_args</key> + <value></value> + </param> + <param> + <key>stream_chans</key> + <value>[]</value> + </param> + <param> + <key>sync</key> + <value></value> + </param> + <param> + <key>len_tag_name</key> + <value></value> + </param> + <param> + <key>otw</key> + <value></value> + </param> + </block> + <block> + <key>wxgui_fftsink2</key> + <param> + <key>avg_alpha</key> + <value>0.13333</value> + </param> + <param> + <key>average</key> + <value>True</value> + </param> + <param> + <key>baseband_freq</key> + <value>frequency</value> + </param> + <param> <key>alias</key> <value></value> </param> <param> + <key>comment</key> + <value></value> + </param> + <param> <key>affinity</key> <value></value> </param> <param> - <key>minoutbuf</key> - <value>0</value> + <key>_enabled</key> + <value>True</value> </param> <param> - <key>maxoutbuf</key> - <value>0</value> + <key>fft_size</key> + <value>1024</value> + </param> + <param> + <key>freqvar</key> + <value>None</value> </param> <param> <key>_coordinate</key> - <value>(176, 43)</value> + <value>(784, 203)</value> </param> <param> <key>_rotation</key> <value>0</value> </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>wxgui_fftsink2_0</value> + </param> + <param> + <key>notebook</key> + <value></value> + </param> + <param> + <key>peak_hold</key> + <value>False</value> + </param> + <param> + <key>ref_level</key> + <value>0</value> + </param> + <param> + <key>ref_scale</key> + <value>2.0</value> + </param> + <param> + <key>fft_rate</key> + <value>15</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>title</key> + <value>FFT Plot</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>win_size</key> + <value></value> + </param> + <param> + <key>win</key> + <value>None</value> + </param> + <param> + <key>y_divs</key> + <value>10</value> + </param> + <param> + <key>y_per_div</key> + <value>10</value> + </param> </block> <connection> - <source_block_id>fft_filter_xxx_0</source_block_id> - <sink_block_id>osmosdr_sink_0</sink_block_id> - <source_key>0</source_key> - <sink_key>0</sink_key> - </connection> - <connection> - <source_block_id>fft_filter_xxx_0</source_block_id> - <sink_block_id>wxgui_fftsink2_0</sink_block_id> - <source_key>0</source_key> - <sink_key>0</sink_key> - </connection> - <connection> - <source_block_id>fft_filter_xxx_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>blocks_file_source_0</source_block_id> <sink_block_id>dtv_dvb_bbheader_bb_0</sink_block_id> <source_key>0</source_key> @@ -1900,14 +2796,38 @@ <sink_key>0</sink_key> </connection> <connection> + <source_block_id>dtv_dvbs2_modulator_bc_0</source_block_id> + <sink_block_id>dtv_dvbs2_physical_cc_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> <source_block_id>dtv_dvbs2_physical_cc_0</source_block_id> <sink_block_id>fft_filter_xxx_0</sink_block_id> <source_key>0</source_key> <sink_key>0</sink_key> </connection> <connection> - <source_block_id>dtv_dvbs2_modulator_bc_0</source_block_id> - <sink_block_id>dtv_dvbs2_physical_cc_0</sink_block_id> + <source_block_id>fft_filter_xxx_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>fft_filter_xxx_0</source_block_id> + <sink_block_id>osmosdr_sink_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>fft_filter_xxx_0</source_block_id> + <sink_block_id>uhd_usrp_sink_0_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>fft_filter_xxx_0</source_block_id> + <sink_block_id>wxgui_fftsink2_0</sink_block_id> <source_key>0</source_key> <sink_key>0</sink_key> </connection> diff --git a/gr-dtv/examples/dvbs_tx.grc b/gr-dtv/examples/dvbs_tx.grc new file mode 100644 index 0000000000..c9dd98a0b9 --- /dev/null +++ b/gr-dtv/examples/dvbs_tx.grc @@ -0,0 +1,2853 @@ +<?xml version='1.0' encoding='utf-8'?> +<?grc format='1' created='3.7.10'?> +<flow_graph> + <timestamp>Wed Jun 18 01:23:58 2014</timestamp> + <block> + <key>options</key> + <param> + <key>author</key> + <value></value> + </param> + <param> + <key>window_size</key> + <value>1280, 1024</value> + </param> + <param> + <key>category</key> + <value>Custom</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>description</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(8, 11)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>generate_options</key> + <value>wx_gui</value> + </param> + <param> + <key>hier_block_src_path</key> + <value>.:</value> + </param> + <param> + <key>id</key> + <value>dvbs_tx</value> + </param> + <param> + <key>max_nouts</key> + <value>0</value> + </param> + <param> + <key>qt_qss_theme</key> + <value></value> + </param> + <param> + <key>realtime_scheduling</key> + <value></value> + </param> + <param> + <key>run_command</key> + <value>{python} -u {filename}</value> + </param> + <param> + <key>run_options</key> + <value>prompt</value> + </param> + <param> + <key>run</key> + <value>True</value> + </param> + <param> + <key>thread_safe_setters</key> + <value></value> + </param> + <param> + <key>title</key> + <value></value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(8, 203)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>frequency</value> + </param> + <param> + <key>value</key> + <value>1280e6</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(8, 267)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>rrc_taps</value> + </param> + <param> + <key>value</key> + <value>100</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(8, 75)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>samp_rate</value> + </param> + <param> + <key>value</key> + <value>symbol_rate * 2</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(8, 139)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>symbol_rate</value> + </param> + <param> + <key>value</key> + <value>8000000</value> + </param> + </block> + <block> + <key>blocks_file_sink</key> + <param> + <key>append</key> + <value>False</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>0</value> + </param> + <param> + <key>file</key> + <value>adv.bin</value> + </param> + <param> + <key>_coordinate</key> + <value>(848, 507)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blocks_file_sink_0</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>unbuffered</key> + <value>False</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + </block> + <block> + <key>blocks_file_source</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>file</key> + <value>/run/shm/advdvbs78.ts</value> + </param> + <param> + <key>_coordinate</key> + <value>(184, 59)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blocks_file_source_0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>type</key> + <value>byte</value> + </param> + <param> + <key>repeat</key> + <value>True</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + </block> + <block> + <key>blocks_pack_k_bits_bb</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>1</value> + </param> + <param> + <key>_coordinate</key> + <value>(176, 451)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blocks_pack_k_bits_bb_0</value> + </param> + <param> + <key>k</key> + <value>2</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_packed_to_unpacked_xx</key> + <param> + <key>bits_per_chunk</key> + <value>1</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>1</value> + </param> + <param> + <key>endianness</key> + <value>gr.GR_MSB_FIRST</value> + </param> + <param> + <key>_coordinate</key> + <value>(1080, 59)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blocks_packed_to_unpacked_xx_0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>num_ports</key> + <value>1</value> + </param> + <param> + <key>type</key> + <value>byte</value> + </param> + </block> + <block> + <key>blocks_unpack_k_bits_bb</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>1</value> + </param> + <param> + <key>_coordinate</key> + <value>(408, 235)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blocks_unpack_k_bits_bb_0</value> + </param> + <param> + <key>k</key> + <value>2</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + </block> + <block> + <key>dtv_dvbs2_modulator_bc</key> + <param> + <key>interpolation</key> + <value>INTERPOLATION_ON</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>rate</key> + <value>C_OTHER</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>constellation</key> + <value>MOD_QPSK</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>framesize</key> + <value>FECFRAME_NORMAL</value> + </param> + <param> + <key>_coordinate</key> + <value>(352, 427)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>dtv_dvbs2_modulator_bc_0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + </block> + <block> + <key>dtv_dvbt_convolutional_interleaver</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>blocks</key> + <value>136</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>M</key> + <value>17</value> + </param> + <param> + <key>_enabled</key> + <value>1</value> + </param> + <param> + <key>_coordinate</key> + <value>(832, 51)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>dtv_dvbt_convolutional_interleaver_0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>I</key> + <value>12</value> + </param> + </block> + <block> + <key>dtv_dvbt_energy_dispersal</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>nsize</key> + <value>1</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>1</value> + </param> + <param> + <key>_coordinate</key> + <value>(408, 73)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>dtv_dvbt_energy_dispersal_0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + </block> + <block> + <key>dtv_dvbt_reed_solomon_enc</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>blocks</key> + <value>8</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>1</value> + </param> + <param> + <key>gfpoly</key> + <value>0x11d</value> + </param> + <param> + <key>_coordinate</key> + <value>(600, 19)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>dtv_dvbt_reed_solomon_enc_0</value> + </param> + <param> + <key>k</key> + <value>239</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>n</key> + <value>255</value> + </param> + <param> + <key>s</key> + <value>51</value> + </param> + <param> + <key>m</key> + <value>8</value> + </param> + <param> + <key>p</key> + <value>2</value> + </param> + <param> + <key>t</key> + <value>8</value> + </param> + </block> + <block> + <key>fec_puncture_xx</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>delay</key> + <value>0</value> + </param> + <param> + <key>_enabled</key> + <value>1</value> + </param> + <param> + <key>_coordinate</key> + <value>(600, 219)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>fec_puncture_xx_0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>puncpat</key> + <value>0b11010101100110</value> + </param> + <param> + <key>puncsize</key> + <value>14</value> + </param> + <param> + <key>type</key> + <value>byte</value> + </param> + </block> + <block> + <key>fft_filter_xxx</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>decim</key> + <value>1</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(584, 435)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>fft_filter_xxx_0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>nthreads</key> + <value>1</value> + </param> + <param> + <key>samp_delay</key> + <value>0</value> + </param> + <param> + <key>taps</key> + <value>firdes.root_raised_cosine(1.0, samp_rate, samp_rate/2, 0.35, rrc_taps)</value> + </param> + <param> + <key>type</key> + <value>ccc</value> + </param> + </block> + <block> + <key>osmosdr_sink</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>ant0</key> + <value></value> + </param> + <param> + <key>bb_gain0</key> + <value>-10</value> + </param> + <param> + <key>bw0</key> + <value>10000000</value> + </param> + <param> + <key>corr0</key> + <value>0</value> + </param> + <param> + <key>freq0</key> + <value>frequency</value> + </param> + <param> + <key>if_gain0</key> + <value>0</value> + </param> + <param> + <key>gain0</key> + <value>15</value> + </param> + <param> + <key>ant10</key> + <value></value> + </param> + <param> + <key>bb_gain10</key> + <value>20</value> + </param> + <param> + <key>bw10</key> + <value>0</value> + </param> + <param> + <key>corr10</key> + <value>0</value> + </param> + <param> + <key>freq10</key> + <value>100e6</value> + </param> + <param> + <key>if_gain10</key> + <value>20</value> + </param> + <param> + <key>gain10</key> + <value>10</value> + </param> + <param> + <key>ant11</key> + <value></value> + </param> + <param> + <key>bb_gain11</key> + <value>20</value> + </param> + <param> + <key>bw11</key> + <value>0</value> + </param> + <param> + <key>corr11</key> + <value>0</value> + </param> + <param> + <key>freq11</key> + <value>100e6</value> + </param> + <param> + <key>if_gain11</key> + <value>20</value> + </param> + <param> + <key>gain11</key> + <value>10</value> + </param> + <param> + <key>ant12</key> + <value></value> + </param> + <param> + <key>bb_gain12</key> + <value>20</value> + </param> + <param> + <key>bw12</key> + <value>0</value> + </param> + <param> + <key>corr12</key> + <value>0</value> + </param> + <param> + <key>freq12</key> + <value>100e6</value> + </param> + <param> + <key>if_gain12</key> + <value>20</value> + </param> + <param> + <key>gain12</key> + <value>10</value> + </param> + <param> + <key>ant13</key> + <value></value> + </param> + <param> + <key>bb_gain13</key> + <value>20</value> + </param> + <param> + <key>bw13</key> + <value>0</value> + </param> + <param> + <key>corr13</key> + <value>0</value> + </param> + <param> + <key>freq13</key> + <value>100e6</value> + </param> + <param> + <key>if_gain13</key> + <value>20</value> + </param> + <param> + <key>gain13</key> + <value>10</value> + </param> + <param> + <key>ant14</key> + <value></value> + </param> + <param> + <key>bb_gain14</key> + <value>20</value> + </param> + <param> + <key>bw14</key> + <value>0</value> + </param> + <param> + <key>corr14</key> + <value>0</value> + </param> + <param> + <key>freq14</key> + <value>100e6</value> + </param> + <param> + <key>if_gain14</key> + <value>20</value> + </param> + <param> + <key>gain14</key> + <value>10</value> + </param> + <param> + <key>ant15</key> + <value></value> + </param> + <param> + <key>bb_gain15</key> + <value>20</value> + </param> + <param> + <key>bw15</key> + <value>0</value> + </param> + <param> + <key>corr15</key> + <value>0</value> + </param> + <param> + <key>freq15</key> + <value>100e6</value> + </param> + <param> + <key>if_gain15</key> + <value>20</value> + </param> + <param> + <key>gain15</key> + <value>10</value> + </param> + <param> + <key>ant16</key> + <value></value> + </param> + <param> + <key>bb_gain16</key> + <value>20</value> + </param> + <param> + <key>bw16</key> + <value>0</value> + </param> + <param> + <key>corr16</key> + <value>0</value> + </param> + <param> + <key>freq16</key> + <value>100e6</value> + </param> + <param> + <key>if_gain16</key> + <value>20</value> + </param> + <param> + <key>gain16</key> + <value>10</value> + </param> + <param> + <key>ant17</key> + <value></value> + </param> + <param> + <key>bb_gain17</key> + <value>20</value> + </param> + <param> + <key>bw17</key> + <value>0</value> + </param> + <param> + <key>corr17</key> + <value>0</value> + </param> + <param> + <key>freq17</key> + <value>100e6</value> + </param> + <param> + <key>if_gain17</key> + <value>20</value> + </param> + <param> + <key>gain17</key> + <value>10</value> + </param> + <param> + <key>ant18</key> + <value></value> + </param> + <param> + <key>bb_gain18</key> + <value>20</value> + </param> + <param> + <key>bw18</key> + <value>0</value> + </param> + <param> + <key>corr18</key> + <value>0</value> + </param> + <param> + <key>freq18</key> + <value>100e6</value> + </param> + <param> + <key>if_gain18</key> + <value>20</value> + </param> + <param> + <key>gain18</key> + <value>10</value> + </param> + <param> + <key>ant19</key> + <value></value> + </param> + <param> + <key>bb_gain19</key> + <value>20</value> + </param> + <param> + <key>bw19</key> + <value>0</value> + </param> + <param> + <key>corr19</key> + <value>0</value> + </param> + <param> + <key>freq19</key> + <value>100e6</value> + </param> + <param> + <key>if_gain19</key> + <value>20</value> + </param> + <param> + <key>gain19</key> + <value>10</value> + </param> + <param> + <key>ant1</key> + <value></value> + </param> + <param> + <key>bb_gain1</key> + <value>20</value> + </param> + <param> + <key>bw1</key> + <value>0</value> + </param> + <param> + <key>corr1</key> + <value>0</value> + </param> + <param> + <key>freq1</key> + <value>100e6</value> + </param> + <param> + <key>if_gain1</key> + <value>20</value> + </param> + <param> + <key>gain1</key> + <value>10</value> + </param> + <param> + <key>ant20</key> + <value></value> + </param> + <param> + <key>bb_gain20</key> + <value>20</value> + </param> + <param> + <key>bw20</key> + <value>0</value> + </param> + <param> + <key>corr20</key> + <value>0</value> + </param> + <param> + <key>freq20</key> + <value>100e6</value> + </param> + <param> + <key>if_gain20</key> + <value>20</value> + </param> + <param> + <key>gain20</key> + <value>10</value> + </param> + <param> + <key>ant21</key> + <value></value> + </param> + <param> + <key>bb_gain21</key> + <value>20</value> + </param> + <param> + <key>bw21</key> + <value>0</value> + </param> + <param> + <key>corr21</key> + <value>0</value> + </param> + <param> + <key>freq21</key> + <value>100e6</value> + </param> + <param> + <key>if_gain21</key> + <value>20</value> + </param> + <param> + <key>gain21</key> + <value>10</value> + </param> + <param> + <key>ant22</key> + <value></value> + </param> + <param> + <key>bb_gain22</key> + <value>20</value> + </param> + <param> + <key>bw22</key> + <value>0</value> + </param> + <param> + <key>corr22</key> + <value>0</value> + </param> + <param> + <key>freq22</key> + <value>100e6</value> + </param> + <param> + <key>if_gain22</key> + <value>20</value> + </param> + <param> + <key>gain22</key> + <value>10</value> + </param> + <param> + <key>ant23</key> + <value></value> + </param> + <param> + <key>bb_gain23</key> + <value>20</value> + </param> + <param> + <key>bw23</key> + <value>0</value> + </param> + <param> + <key>corr23</key> + <value>0</value> + </param> + <param> + <key>freq23</key> + <value>100e6</value> + </param> + <param> + <key>if_gain23</key> + <value>20</value> + </param> + <param> + <key>gain23</key> + <value>10</value> + </param> + <param> + <key>ant24</key> + <value></value> + </param> + <param> + <key>bb_gain24</key> + <value>20</value> + </param> + <param> + <key>bw24</key> + <value>0</value> + </param> + <param> + <key>corr24</key> + <value>0</value> + </param> + <param> + <key>freq24</key> + <value>100e6</value> + </param> + <param> + <key>if_gain24</key> + <value>20</value> + </param> + <param> + <key>gain24</key> + <value>10</value> + </param> + <param> + <key>ant25</key> + <value></value> + </param> + <param> + <key>bb_gain25</key> + <value>20</value> + </param> + <param> + <key>bw25</key> + <value>0</value> + </param> + <param> + <key>corr25</key> + <value>0</value> + </param> + <param> + <key>freq25</key> + <value>100e6</value> + </param> + <param> + <key>if_gain25</key> + <value>20</value> + </param> + <param> + <key>gain25</key> + <value>10</value> + </param> + <param> + <key>ant26</key> + <value></value> + </param> + <param> + <key>bb_gain26</key> + <value>20</value> + </param> + <param> + <key>bw26</key> + <value>0</value> + </param> + <param> + <key>corr26</key> + <value>0</value> + </param> + <param> + <key>freq26</key> + <value>100e6</value> + </param> + <param> + <key>if_gain26</key> + <value>20</value> + </param> + <param> + <key>gain26</key> + <value>10</value> + </param> + <param> + <key>ant27</key> + <value></value> + </param> + <param> + <key>bb_gain27</key> + <value>20</value> + </param> + <param> + <key>bw27</key> + <value>0</value> + </param> + <param> + <key>corr27</key> + <value>0</value> + </param> + <param> + <key>freq27</key> + <value>100e6</value> + </param> + <param> + <key>if_gain27</key> + <value>20</value> + </param> + <param> + <key>gain27</key> + <value>10</value> + </param> + <param> + <key>ant28</key> + <value></value> + </param> + <param> + <key>bb_gain28</key> + <value>20</value> + </param> + <param> + <key>bw28</key> + <value>0</value> + </param> + <param> + <key>corr28</key> + <value>0</value> + </param> + <param> + <key>freq28</key> + <value>100e6</value> + </param> + <param> + <key>if_gain28</key> + <value>20</value> + </param> + <param> + <key>gain28</key> + <value>10</value> + </param> + <param> + <key>ant29</key> + <value></value> + </param> + <param> + <key>bb_gain29</key> + <value>20</value> + </param> + <param> + <key>bw29</key> + <value>0</value> + </param> + <param> + <key>corr29</key> + <value>0</value> + </param> + <param> + <key>freq29</key> + <value>100e6</value> + </param> + <param> + <key>if_gain29</key> + <value>20</value> + </param> + <param> + <key>gain29</key> + <value>10</value> + </param> + <param> + <key>ant2</key> + <value></value> + </param> + <param> + <key>bb_gain2</key> + <value>20</value> + </param> + <param> + <key>bw2</key> + <value>0</value> + </param> + <param> + <key>corr2</key> + <value>0</value> + </param> + <param> + <key>freq2</key> + <value>100e6</value> + </param> + <param> + <key>if_gain2</key> + <value>20</value> + </param> + <param> + <key>gain2</key> + <value>10</value> + </param> + <param> + <key>ant30</key> + <value></value> + </param> + <param> + <key>bb_gain30</key> + <value>20</value> + </param> + <param> + <key>bw30</key> + <value>0</value> + </param> + <param> + <key>corr30</key> + <value>0</value> + </param> + <param> + <key>freq30</key> + <value>100e6</value> + </param> + <param> + <key>if_gain30</key> + <value>20</value> + </param> + <param> + <key>gain30</key> + <value>10</value> + </param> + <param> + <key>ant31</key> + <value></value> + </param> + <param> + <key>bb_gain31</key> + <value>20</value> + </param> + <param> + <key>bw31</key> + <value>0</value> + </param> + <param> + <key>corr31</key> + <value>0</value> + </param> + <param> + <key>freq31</key> + <value>100e6</value> + </param> + <param> + <key>if_gain31</key> + <value>20</value> + </param> + <param> + <key>gain31</key> + <value>10</value> + </param> + <param> + <key>ant3</key> + <value></value> + </param> + <param> + <key>bb_gain3</key> + <value>20</value> + </param> + <param> + <key>bw3</key> + <value>0</value> + </param> + <param> + <key>corr3</key> + <value>0</value> + </param> + <param> + <key>freq3</key> + <value>100e6</value> + </param> + <param> + <key>if_gain3</key> + <value>20</value> + </param> + <param> + <key>gain3</key> + <value>10</value> + </param> + <param> + <key>ant4</key> + <value></value> + </param> + <param> + <key>bb_gain4</key> + <value>20</value> + </param> + <param> + <key>bw4</key> + <value>0</value> + </param> + <param> + <key>corr4</key> + <value>0</value> + </param> + <param> + <key>freq4</key> + <value>100e6</value> + </param> + <param> + <key>if_gain4</key> + <value>20</value> + </param> + <param> + <key>gain4</key> + <value>10</value> + </param> + <param> + <key>ant5</key> + <value></value> + </param> + <param> + <key>bb_gain5</key> + <value>20</value> + </param> + <param> + <key>bw5</key> + <value>0</value> + </param> + <param> + <key>corr5</key> + <value>0</value> + </param> + <param> + <key>freq5</key> + <value>100e6</value> + </param> + <param> + <key>if_gain5</key> + <value>20</value> + </param> + <param> + <key>gain5</key> + <value>10</value> + </param> + <param> + <key>ant6</key> + <value></value> + </param> + <param> + <key>bb_gain6</key> + <value>20</value> + </param> + <param> + <key>bw6</key> + <value>0</value> + </param> + <param> + <key>corr6</key> + <value>0</value> + </param> + <param> + <key>freq6</key> + <value>100e6</value> + </param> + <param> + <key>if_gain6</key> + <value>20</value> + </param> + <param> + <key>gain6</key> + <value>10</value> + </param> + <param> + <key>ant7</key> + <value></value> + </param> + <param> + <key>bb_gain7</key> + <value>20</value> + </param> + <param> + <key>bw7</key> + <value>0</value> + </param> + <param> + <key>corr7</key> + <value>0</value> + </param> + <param> + <key>freq7</key> + <value>100e6</value> + </param> + <param> + <key>if_gain7</key> + <value>20</value> + </param> + <param> + <key>gain7</key> + <value>10</value> + </param> + <param> + <key>ant8</key> + <value></value> + </param> + <param> + <key>bb_gain8</key> + <value>20</value> + </param> + <param> + <key>bw8</key> + <value>0</value> + </param> + <param> + <key>corr8</key> + <value>0</value> + </param> + <param> + <key>freq8</key> + <value>100e6</value> + </param> + <param> + <key>if_gain8</key> + <value>20</value> + </param> + <param> + <key>gain8</key> + <value>10</value> + </param> + <param> + <key>ant9</key> + <value></value> + </param> + <param> + <key>bb_gain9</key> + <value>20</value> + </param> + <param> + <key>bw9</key> + <value>0</value> + </param> + <param> + <key>corr9</key> + <value>0</value> + </param> + <param> + <key>freq9</key> + <value>100e6</value> + </param> + <param> + <key>if_gain9</key> + <value>20</value> + </param> + <param> + <key>gain9</key> + <value>10</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>args</key> + <value>bladerf=0,buffers=128,buflen=32768</value> + </param> + <param> + <key>_enabled</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(1064, 211)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>osmosdr_sink_0</value> + </param> + <param> + <key>type</key> + <value>fc32</value> + </param> + <param> + <key>clock_source0</key> + <value></value> + </param> + <param> + <key>time_source0</key> + <value></value> + </param> + <param> + <key>clock_source1</key> + <value></value> + </param> + <param> + <key>time_source1</key> + <value></value> + </param> + <param> + <key>clock_source2</key> + <value></value> + </param> + <param> + <key>time_source2</key> + <value></value> + </param> + <param> + <key>clock_source3</key> + <value></value> + </param> + <param> + <key>time_source3</key> + <value></value> + </param> + <param> + <key>clock_source4</key> + <value></value> + </param> + <param> + <key>time_source4</key> + <value></value> + </param> + <param> + <key>clock_source5</key> + <value></value> + </param> + <param> + <key>time_source5</key> + <value></value> + </param> + <param> + <key>clock_source6</key> + <value></value> + </param> + <param> + <key>time_source6</key> + <value></value> + </param> + <param> + <key>clock_source7</key> + <value></value> + </param> + <param> + <key>time_source7</key> + <value></value> + </param> + <param> + <key>nchan</key> + <value>1</value> + </param> + <param> + <key>num_mboards</key> + <value>1</value> + </param> + <param> + <key>sample_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>sync</key> + <value></value> + </param> + </block> + <block> + <key>trellis_encoder_xx</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>blocklength</key> + <value>0</value> + </param> + <param> + <key>blockwise</key> + <value>False</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>1</value> + </param> + <param> + <key>fsm_args</key> + <value>1, 2, (0171, 0133)</value> + </param> + <param> + <key>_coordinate</key> + <value>(176, 227)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>trellis_encoder_xx_0</value> + </param> + <param> + <key>init_state</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>type</key> + <value>bb</value> + </param> + </block> + <block> + <key>uhd_usrp_sink</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>ant0</key> + <value></value> + </param> + <param> + <key>bw0</key> + <value>0</value> + </param> + <param> + <key>center_freq0</key> + <value>uhd.tune_request(frequency, ((symbol_rate * 1.35) / 2 ) + 1e5)</value> + </param> + <param> + <key>norm_gain0</key> + <value>False</value> + </param> + <param> + <key>gain0</key> + <value>60</value> + </param> + <param> + <key>ant10</key> + <value></value> + </param> + <param> + <key>bw10</key> + <value>0</value> + </param> + <param> + <key>center_freq10</key> + <value>0</value> + </param> + <param> + <key>norm_gain10</key> + <value>False</value> + </param> + <param> + <key>gain10</key> + <value>0</value> + </param> + <param> + <key>ant11</key> + <value></value> + </param> + <param> + <key>bw11</key> + <value>0</value> + </param> + <param> + <key>center_freq11</key> + <value>0</value> + </param> + <param> + <key>norm_gain11</key> + <value>False</value> + </param> + <param> + <key>gain11</key> + <value>0</value> + </param> + <param> + <key>ant12</key> + <value></value> + </param> + <param> + <key>bw12</key> + <value>0</value> + </param> + <param> + <key>center_freq12</key> + <value>0</value> + </param> + <param> + <key>norm_gain12</key> + <value>False</value> + </param> + <param> + <key>gain12</key> + <value>0</value> + </param> + <param> + <key>ant13</key> + <value></value> + </param> + <param> + <key>bw13</key> + <value>0</value> + </param> + <param> + <key>center_freq13</key> + <value>0</value> + </param> + <param> + <key>norm_gain13</key> + <value>False</value> + </param> + <param> + <key>gain13</key> + <value>0</value> + </param> + <param> + <key>ant14</key> + <value></value> + </param> + <param> + <key>bw14</key> + <value>0</value> + </param> + <param> + <key>center_freq14</key> + <value>0</value> + </param> + <param> + <key>norm_gain14</key> + <value>False</value> + </param> + <param> + <key>gain14</key> + <value>0</value> + </param> + <param> + <key>ant15</key> + <value></value> + </param> + <param> + <key>bw15</key> + <value>0</value> + </param> + <param> + <key>center_freq15</key> + <value>0</value> + </param> + <param> + <key>norm_gain15</key> + <value>False</value> + </param> + <param> + <key>gain15</key> + <value>0</value> + </param> + <param> + <key>ant16</key> + <value></value> + </param> + <param> + <key>bw16</key> + <value>0</value> + </param> + <param> + <key>center_freq16</key> + <value>0</value> + </param> + <param> + <key>norm_gain16</key> + <value>False</value> + </param> + <param> + <key>gain16</key> + <value>0</value> + </param> + <param> + <key>ant17</key> + <value></value> + </param> + <param> + <key>bw17</key> + <value>0</value> + </param> + <param> + <key>center_freq17</key> + <value>0</value> + </param> + <param> + <key>norm_gain17</key> + <value>False</value> + </param> + <param> + <key>gain17</key> + <value>0</value> + </param> + <param> + <key>ant18</key> + <value></value> + </param> + <param> + <key>bw18</key> + <value>0</value> + </param> + <param> + <key>center_freq18</key> + <value>0</value> + </param> + <param> + <key>norm_gain18</key> + <value>False</value> + </param> + <param> + <key>gain18</key> + <value>0</value> + </param> + <param> + <key>ant19</key> + <value></value> + </param> + <param> + <key>bw19</key> + <value>0</value> + </param> + <param> + <key>center_freq19</key> + <value>0</value> + </param> + <param> + <key>norm_gain19</key> + <value>False</value> + </param> + <param> + <key>gain19</key> + <value>0</value> + </param> + <param> + <key>ant1</key> + <value></value> + </param> + <param> + <key>bw1</key> + <value>0</value> + </param> + <param> + <key>center_freq1</key> + <value>0</value> + </param> + <param> + <key>norm_gain1</key> + <value>False</value> + </param> + <param> + <key>gain1</key> + <value>0</value> + </param> + <param> + <key>ant20</key> + <value></value> + </param> + <param> + <key>bw20</key> + <value>0</value> + </param> + <param> + <key>center_freq20</key> + <value>0</value> + </param> + <param> + <key>norm_gain20</key> + <value>False</value> + </param> + <param> + <key>gain20</key> + <value>0</value> + </param> + <param> + <key>ant21</key> + <value></value> + </param> + <param> + <key>bw21</key> + <value>0</value> + </param> + <param> + <key>center_freq21</key> + <value>0</value> + </param> + <param> + <key>norm_gain21</key> + <value>False</value> + </param> + <param> + <key>gain21</key> + <value>0</value> + </param> + <param> + <key>ant22</key> + <value></value> + </param> + <param> + <key>bw22</key> + <value>0</value> + </param> + <param> + <key>center_freq22</key> + <value>0</value> + </param> + <param> + <key>norm_gain22</key> + <value>False</value> + </param> + <param> + <key>gain22</key> + <value>0</value> + </param> + <param> + <key>ant23</key> + <value></value> + </param> + <param> + <key>bw23</key> + <value>0</value> + </param> + <param> + <key>center_freq23</key> + <value>0</value> + </param> + <param> + <key>norm_gain23</key> + <value>False</value> + </param> + <param> + <key>gain23</key> + <value>0</value> + </param> + <param> + <key>ant24</key> + <value></value> + </param> + <param> + <key>bw24</key> + <value>0</value> + </param> + <param> + <key>center_freq24</key> + <value>0</value> + </param> + <param> + <key>norm_gain24</key> + <value>False</value> + </param> + <param> + <key>gain24</key> + <value>0</value> + </param> + <param> + <key>ant25</key> + <value></value> + </param> + <param> + <key>bw25</key> + <value>0</value> + </param> + <param> + <key>center_freq25</key> + <value>0</value> + </param> + <param> + <key>norm_gain25</key> + <value>False</value> + </param> + <param> + <key>gain25</key> + <value>0</value> + </param> + <param> + <key>ant26</key> + <value></value> + </param> + <param> + <key>bw26</key> + <value>0</value> + </param> + <param> + <key>center_freq26</key> + <value>0</value> + </param> + <param> + <key>norm_gain26</key> + <value>False</value> + </param> + <param> + <key>gain26</key> + <value>0</value> + </param> + <param> + <key>ant27</key> + <value></value> + </param> + <param> + <key>bw27</key> + <value>0</value> + </param> + <param> + <key>center_freq27</key> + <value>0</value> + </param> + <param> + <key>norm_gain27</key> + <value>False</value> + </param> + <param> + <key>gain27</key> + <value>0</value> + </param> + <param> + <key>ant28</key> + <value></value> + </param> + <param> + <key>bw28</key> + <value>0</value> + </param> + <param> + <key>center_freq28</key> + <value>0</value> + </param> + <param> + <key>norm_gain28</key> + <value>False</value> + </param> + <param> + <key>gain28</key> + <value>0</value> + </param> + <param> + <key>ant29</key> + <value></value> + </param> + <param> + <key>bw29</key> + <value>0</value> + </param> + <param> + <key>center_freq29</key> + <value>0</value> + </param> + <param> + <key>norm_gain29</key> + <value>False</value> + </param> + <param> + <key>gain29</key> + <value>0</value> + </param> + <param> + <key>ant2</key> + <value></value> + </param> + <param> + <key>bw2</key> + <value>0</value> + </param> + <param> + <key>center_freq2</key> + <value>0</value> + </param> + <param> + <key>norm_gain2</key> + <value>False</value> + </param> + <param> + <key>gain2</key> + <value>0</value> + </param> + <param> + <key>ant30</key> + <value></value> + </param> + <param> + <key>bw30</key> + <value>0</value> + </param> + <param> + <key>center_freq30</key> + <value>0</value> + </param> + <param> + <key>norm_gain30</key> + <value>False</value> + </param> + <param> + <key>gain30</key> + <value>0</value> + </param> + <param> + <key>ant31</key> + <value></value> + </param> + <param> + <key>bw31</key> + <value>0</value> + </param> + <param> + <key>center_freq31</key> + <value>0</value> + </param> + <param> + <key>norm_gain31</key> + <value>False</value> + </param> + <param> + <key>gain31</key> + <value>0</value> + </param> + <param> + <key>ant3</key> + <value></value> + </param> + <param> + <key>bw3</key> + <value>0</value> + </param> + <param> + <key>center_freq3</key> + <value>0</value> + </param> + <param> + <key>norm_gain3</key> + <value>False</value> + </param> + <param> + <key>gain3</key> + <value>0</value> + </param> + <param> + <key>ant4</key> + <value></value> + </param> + <param> + <key>bw4</key> + <value>0</value> + </param> + <param> + <key>center_freq4</key> + <value>0</value> + </param> + <param> + <key>norm_gain4</key> + <value>False</value> + </param> + <param> + <key>gain4</key> + <value>0</value> + </param> + <param> + <key>ant5</key> + <value></value> + </param> + <param> + <key>bw5</key> + <value>0</value> + </param> + <param> + <key>center_freq5</key> + <value>0</value> + </param> + <param> + <key>norm_gain5</key> + <value>False</value> + </param> + <param> + <key>gain5</key> + <value>0</value> + </param> + <param> + <key>ant6</key> + <value></value> + </param> + <param> + <key>bw6</key> + <value>0</value> + </param> + <param> + <key>center_freq6</key> + <value>0</value> + </param> + <param> + <key>norm_gain6</key> + <value>False</value> + </param> + <param> + <key>gain6</key> + <value>0</value> + </param> + <param> + <key>ant7</key> + <value></value> + </param> + <param> + <key>bw7</key> + <value>0</value> + </param> + <param> + <key>center_freq7</key> + <value>0</value> + </param> + <param> + <key>norm_gain7</key> + <value>False</value> + </param> + <param> + <key>gain7</key> + <value>0</value> + </param> + <param> + <key>ant8</key> + <value></value> + </param> + <param> + <key>bw8</key> + <value>0</value> + </param> + <param> + <key>center_freq8</key> + <value>0</value> + </param> + <param> + <key>norm_gain8</key> + <value>False</value> + </param> + <param> + <key>gain8</key> + <value>0</value> + </param> + <param> + <key>ant9</key> + <value></value> + </param> + <param> + <key>bw9</key> + <value>0</value> + </param> + <param> + <key>center_freq9</key> + <value>0</value> + </param> + <param> + <key>norm_gain9</key> + <value>False</value> + </param> + <param> + <key>gain9</key> + <value>0</value> + </param> + <param> + <key>clock_rate</key> + <value>0.0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>dev_addr</key> + <value>"send_frame_size=65536,num_send_frames=256,master_clock_rate=" + str(samp_rate*2)</value> + </param> + <param> + <key>dev_args</key> + <value>""</value> + </param> + <param> + <key>_enabled</key> + <value>1</value> + </param> + <param> + <key>_coordinate</key> + <value>(1064, 403)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>uhd_usrp_sink_0_0</value> + </param> + <param> + <key>type</key> + <value>fc32</value> + </param> + <param> + <key>clock_source0</key> + <value></value> + </param> + <param> + <key>sd_spec0</key> + <value></value> + </param> + <param> + <key>time_source0</key> + <value></value> + </param> + <param> + <key>clock_source1</key> + <value></value> + </param> + <param> + <key>sd_spec1</key> + <value></value> + </param> + <param> + <key>time_source1</key> + <value></value> + </param> + <param> + <key>clock_source2</key> + <value></value> + </param> + <param> + <key>sd_spec2</key> + <value></value> + </param> + <param> + <key>time_source2</key> + <value></value> + </param> + <param> + <key>clock_source3</key> + <value></value> + </param> + <param> + <key>sd_spec3</key> + <value></value> + </param> + <param> + <key>time_source3</key> + <value></value> + </param> + <param> + <key>clock_source4</key> + <value></value> + </param> + <param> + <key>sd_spec4</key> + <value></value> + </param> + <param> + <key>time_source4</key> + <value></value> + </param> + <param> + <key>clock_source5</key> + <value></value> + </param> + <param> + <key>sd_spec5</key> + <value></value> + </param> + <param> + <key>time_source5</key> + <value></value> + </param> + <param> + <key>clock_source6</key> + <value></value> + </param> + <param> + <key>sd_spec6</key> + <value></value> + </param> + <param> + <key>time_source6</key> + <value></value> + </param> + <param> + <key>clock_source7</key> + <value></value> + </param> + <param> + <key>sd_spec7</key> + <value></value> + </param> + <param> + <key>time_source7</key> + <value></value> + </param> + <param> + <key>nchan</key> + <value>1</value> + </param> + <param> + <key>num_mboards</key> + <value>1</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>hide_cmd_port</key> + <value>False</value> + </param> + <param> + <key>stream_args</key> + <value></value> + </param> + <param> + <key>stream_chans</key> + <value>[]</value> + </param> + <param> + <key>sync</key> + <value></value> + </param> + <param> + <key>len_tag_name</key> + <value></value> + </param> + <param> + <key>otw</key> + <value></value> + </param> + </block> + <block> + <key>wxgui_fftsink2</key> + <param> + <key>avg_alpha</key> + <value>0.13333</value> + </param> + <param> + <key>average</key> + <value>True</value> + </param> + <param> + <key>baseband_freq</key> + <value>frequency</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>fft_size</key> + <value>1024</value> + </param> + <param> + <key>freqvar</key> + <value>None</value> + </param> + <param> + <key>_coordinate</key> + <value>(848, 195)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>wxgui_fftsink2_0</value> + </param> + <param> + <key>notebook</key> + <value></value> + </param> + <param> + <key>peak_hold</key> + <value>False</value> + </param> + <param> + <key>ref_level</key> + <value>0</value> + </param> + <param> + <key>ref_scale</key> + <value>2.0</value> + </param> + <param> + <key>fft_rate</key> + <value>15</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>title</key> + <value>FFT Plot</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>win_size</key> + <value></value> + </param> + <param> + <key>win</key> + <value>None</value> + </param> + <param> + <key>y_divs</key> + <value>10</value> + </param> + <param> + <key>y_per_div</key> + <value>10</value> + </param> + </block> + <connection> + <source_block_id>blocks_file_source_0</source_block_id> + <sink_block_id>dtv_dvbt_energy_dispersal_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_pack_k_bits_bb_0</source_block_id> + <sink_block_id>dtv_dvbs2_modulator_bc_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_packed_to_unpacked_xx_0</source_block_id> + <sink_block_id>trellis_encoder_xx_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>fec_puncture_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>dtv_dvbs2_modulator_bc_0</source_block_id> + <sink_block_id>fft_filter_xxx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>dtv_dvbt_convolutional_interleaver_0</source_block_id> + <sink_block_id>blocks_packed_to_unpacked_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>dtv_dvbt_energy_dispersal_0</source_block_id> + <sink_block_id>dtv_dvbt_reed_solomon_enc_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>dtv_dvbt_reed_solomon_enc_0</source_block_id> + <sink_block_id>dtv_dvbt_convolutional_interleaver_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>fec_puncture_xx_0</source_block_id> + <sink_block_id>blocks_pack_k_bits_bb_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>fft_filter_xxx_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>fft_filter_xxx_0</source_block_id> + <sink_block_id>osmosdr_sink_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>fft_filter_xxx_0</source_block_id> + <sink_block_id>uhd_usrp_sink_0_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>fft_filter_xxx_0</source_block_id> + <sink_block_id>wxgui_fftsink2_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>trellis_encoder_xx_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> +</flow_graph> diff --git a/gr-dtv/grc/CMakeLists.txt b/gr-dtv/grc/CMakeLists.txt index 2fed81ab12..a1386c5adf 100644 --- a/gr-dtv/grc/CMakeLists.txt +++ b/gr-dtv/grc/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2014,2015 Free Software Foundation, Inc. +# Copyright 2014,2015,2016 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -68,6 +68,11 @@ install(FILES dtv_dvbt_convolutional_deinterleaver.xml dtv_dvbt_reed_solomon_dec.xml dtv_dvbt_energy_descramble.xml + dtv_catv_transport_framing_enc_bb.xml + dtv_catv_reed_solomon_enc_bb.xml + dtv_catv_randomizer_bb.xml + dtv_catv_frame_sync_enc_bb.xml + dtv_catv_trellis_enc_bb.xml DESTINATION ${GRC_BLOCKS_DIR} COMPONENT "dtv_python" ) diff --git a/gr-dtv/grc/dtv_block_tree.xml b/gr-dtv/grc/dtv_block_tree.xml index 32c8974531..8dcbd18127 100644 --- a/gr-dtv/grc/dtv_block_tree.xml +++ b/gr-dtv/grc/dtv_block_tree.xml @@ -1,7 +1,7 @@ <?xml version="1.0"?> <!-- - Copyright 2014,2015 Free Software Foundation, Inc. + Copyright 2014,2015,2016 Free Software Foundation, Inc. This file is part of GNU Radio @@ -27,7 +27,7 @@ ################################################### --> <cat> - <name></name> <!-- Blank for Root Name --> + <name>[Core]</name> <cat> <name>Digital Television</name> <cat> @@ -94,5 +94,13 @@ <block>dtv_dvbt_reed_solomon_dec</block> <block>dtv_dvbt_energy_descramble</block> </cat> + <cat> + <name>ITU-T J.83B</name> + <block>dtv_catv_transport_framing_enc_bb</block> + <block>dtv_catv_reed_solomon_enc_bb</block> + <block>dtv_catv_randomizer_bb</block> + <block>dtv_catv_frame_sync_enc_bb</block> + <block>dtv_catv_trellis_enc_bb</block> + </cat> </cat> </cat> diff --git a/gr-dtv/grc/dtv_catv_frame_sync_enc_bb.xml b/gr-dtv/grc/dtv_catv_frame_sync_enc_bb.xml new file mode 100644 index 0000000000..e7c1392960 --- /dev/null +++ b/gr-dtv/grc/dtv_catv_frame_sync_enc_bb.xml @@ -0,0 +1,26 @@ +<?xml version="1.0"?> +<!-- +################################################### +## Frame Sync Encoder +################################################### + --> +<block> + <name>Frame Sync Encoder</name> + <key>dtv_catv_frame_sync_enc_bb</key> + <import>from gnuradio import dtv</import> + <make>dtv.catv_frame_sync_enc_bb($ctrlword)</make> + <param> + <name>Control Word</name> + <key>ctrlword</key> + <value>6</value> + <type>int</type> + </param> + <sink> + <name>in</name> + <type>byte</type> + </sink> + <source> + <name>out</name> + <type>byte</type> + </source> +</block> diff --git a/gr-dtv/grc/dtv_catv_randomizer_bb.xml b/gr-dtv/grc/dtv_catv_randomizer_bb.xml new file mode 100644 index 0000000000..ca4787d786 --- /dev/null +++ b/gr-dtv/grc/dtv_catv_randomizer_bb.xml @@ -0,0 +1,20 @@ +<?xml version="1.0"?> +<!-- +################################################### +## Randomizer +################################################### + --> +<block> + <name>Randomizer</name> + <key>dtv_catv_randomizer_bb</key> + <import>from gnuradio import dtv</import> + <make>dtv.catv_randomizer_bb()</make> + <sink> + <name>in</name> + <type>byte</type> + </sink> + <source> + <name>out</name> + <type>byte</type> + </source> +</block> diff --git a/gr-dtv/grc/dtv_catv_reed_solomon_enc_bb.xml b/gr-dtv/grc/dtv_catv_reed_solomon_enc_bb.xml new file mode 100644 index 0000000000..a5dfdc2a5a --- /dev/null +++ b/gr-dtv/grc/dtv_catv_reed_solomon_enc_bb.xml @@ -0,0 +1,20 @@ +<?xml version="1.0"?> +<!-- +################################################### +## Reed Solomon Encoder +################################################### + --> +<block> + <name>Reed-Solomon Encoder</name> + <key>dtv_catv_reed_solomon_enc_bb</key> + <import>from gnuradio import dtv</import> + <make>dtv.catv_reed_solomon_enc_bb()</make> + <sink> + <name>in</name> + <type>byte</type> + </sink> + <source> + <name>out</name> + <type>byte</type> + </source> +</block> diff --git a/gr-dtv/grc/dtv_catv_transport_framing_enc_bb.xml b/gr-dtv/grc/dtv_catv_transport_framing_enc_bb.xml new file mode 100644 index 0000000000..d0b6bf382b --- /dev/null +++ b/gr-dtv/grc/dtv_catv_transport_framing_enc_bb.xml @@ -0,0 +1,20 @@ +<?xml version="1.0"?> +<!-- +################################################### +## Transport Framing Encoder +################################################### + --> +<block> + <name>Transport Framing Encoder</name> + <key>dtv_catv_transport_framing_enc_bb</key> + <import>from gnuradio import dtv</import> + <make>dtv.catv_transport_framing_enc_bb()</make> + <sink> + <name>in</name> + <type>byte</type> + </sink> + <source> + <name>out</name> + <type>byte</type> + </source> +</block> diff --git a/gr-dtv/grc/dtv_catv_trellis_enc_bb.xml b/gr-dtv/grc/dtv_catv_trellis_enc_bb.xml new file mode 100644 index 0000000000..a96d60c895 --- /dev/null +++ b/gr-dtv/grc/dtv_catv_trellis_enc_bb.xml @@ -0,0 +1,20 @@ +<?xml version="1.0"?> +<!-- +################################################### +## Trellis Encoder +################################################### + --> +<block> + <name>Trellis Encoder</name> + <key>dtv_catv_trellis_enc_bb</key> + <import>from gnuradio import dtv</import> + <make>dtv.catv_trellis_enc_bb()</make> + <sink> + <name>in</name> + <type>byte</type> + </sink> + <source> + <name>out</name> + <type>byte</type> + </source> +</block> diff --git a/gr-dtv/grc/dtv_dvbs2_modulator_bc.xml b/gr-dtv/grc/dtv_dvbs2_modulator_bc.xml index 1f7fef09a3..8de645d585 100644 --- a/gr-dtv/grc/dtv_dvbs2_modulator_bc.xml +++ b/gr-dtv/grc/dtv_dvbs2_modulator_bc.xml @@ -8,7 +8,7 @@ <name>DVB-S2X Modulator</name> <key>dtv_dvbs2_modulator_bc</key> <import>from gnuradio import dtv</import> - <make>dtv.dvbs2_modulator_bc($framesize.val, $rate.val, $constellation.val)</make> + <make>dtv.dvbs2_modulator_bc($framesize.val, $rate.val, $constellation.val, $interpolation.val)</make> <param> <name>FECFRAME size</name> <key>framesize</key> @@ -253,6 +253,26 @@ <key>MOD_256APSK</key> <opt>val:dtv.MOD_256APSK</opt> </option> + <option> + <name>64QAM</name> + <key>MOD_64QAM</key> + <opt>val:dtv.MOD_64QAM</opt> + </option> + </param> + <param> + <name>2X Interpolation</name> + <key>interpolation</key> + <type>enum</type> + <option> + <name>Off</name> + <key>INTERPOLATION_OFF</key> + <opt>val:dtv.INTERPOLATION_OFF</opt> + </option> + <option> + <name>On</name> + <key>INTERPOLATION_ON</key> + <opt>val:dtv.INTERPOLATION_ON</opt> + </option> </param> <sink> <name>in</name> diff --git a/gr-dtv/include/gnuradio/dtv/CMakeLists.txt b/gr-dtv/include/gnuradio/dtv/CMakeLists.txt index 4232ff7065..620dd55879 100644 --- a/gr-dtv/include/gnuradio/dtv/CMakeLists.txt +++ b/gr-dtv/include/gnuradio/dtv/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2014,2015 Free Software Foundation, Inc. +# Copyright 2014,2015,2016 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -74,6 +74,11 @@ install(FILES dvbt_convolutional_deinterleaver.h dvbt_reed_solomon_dec.h dvbt_energy_descramble.h + catv_transport_framing_enc_bb.h + catv_reed_solomon_enc_bb.h + catv_randomizer_bb.h + catv_frame_sync_enc_bb.h + catv_trellis_enc_bb.h DESTINATION ${GR_INCLUDE_DIR}/gnuradio/dtv COMPONENT "dtv_devel" diff --git a/gr-dtv/include/gnuradio/dtv/atsc_equalizer.h b/gr-dtv/include/gnuradio/dtv/atsc_equalizer.h index 3fe101ac25..17f2e8f768 100644 --- a/gr-dtv/include/gnuradio/dtv/atsc_equalizer.h +++ b/gr-dtv/include/gnuradio/dtv/atsc_equalizer.h @@ -45,6 +45,9 @@ namespace gr { * \brief Make a new instance of gr::dtv::atsc_equalizer. */ static sptr make(); + + virtual std::vector<float> taps() const = 0; + virtual std::vector<float> data() const = 0; }; } /* namespace dtv */ diff --git a/gr-dtv/include/gnuradio/dtv/atsc_rs_decoder.h b/gr-dtv/include/gnuradio/dtv/atsc_rs_decoder.h index 92eb263e5d..c2b7d0defa 100644 --- a/gr-dtv/include/gnuradio/dtv/atsc_rs_decoder.h +++ b/gr-dtv/include/gnuradio/dtv/atsc_rs_decoder.h @@ -42,6 +42,21 @@ namespace gr { typedef boost::shared_ptr<atsc_rs_decoder> sptr; /*! + * Returns the number of errors corrected by the decoder. + */ + virtual int num_errors_corrected() const = 0; + + /*! + * Returns the number of bad packets rejected by the decoder. + */ + virtual int num_bad_packets() const = 0; + + /*! + * Returns the total number of packets seen by the decoder. + */ + virtual int num_packets() const = 0; + + /*! * \brief Make a new instance of gr::dtv::atsc_rs_decoder. */ static sptr make(); diff --git a/gr-dtv/include/gnuradio/dtv/atsc_viterbi_decoder.h b/gr-dtv/include/gnuradio/dtv/atsc_viterbi_decoder.h index a4c18c39da..8efece4c43 100644 --- a/gr-dtv/include/gnuradio/dtv/atsc_viterbi_decoder.h +++ b/gr-dtv/include/gnuradio/dtv/atsc_viterbi_decoder.h @@ -45,6 +45,12 @@ namespace gr { * \brief Make a new instance of gr::dtv::atsc_viterbi_decoder. */ static sptr make(); + + /*! + * For each decoder, returns the current best state of the + * decoding metrics. + */ + virtual std::vector<float> decoder_metrics() const = 0; }; } /* namespace dtv */ diff --git a/gr-dtv/include/gnuradio/dtv/catv_frame_sync_enc_bb.h b/gr-dtv/include/gnuradio/dtv/catv_frame_sync_enc_bb.h new file mode 100644 index 0000000000..799a2d3c55 --- /dev/null +++ b/gr-dtv/include/gnuradio/dtv/catv_frame_sync_enc_bb.h @@ -0,0 +1,54 @@ +/* -*- c++ -*- */ +/* + * Copyright 2016 Free Software Foundation, Inc. + * + * This 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. + * + * This software 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 software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DTV_CATV_FRAME_SYNC_ENC_BB_H +#define INCLUDED_DTV_CATV_FRAME_SYNC_ENC_BB_H + +#include <gnuradio/dtv/api.h> +#include <gnuradio/block.h> + +namespace gr { + namespace dtv { + + /*! + * \brief Frame Sync Encoder. Adds a 42-bit frame sync pattern with control word. + * \ingroup dtv + * + * Input: Scrambled FEC Frame packets of 60 * 128 7-bit symbols.\n + * Output: Scrambled FEC Frame packets of 60 * 128 7-bit symbols with 42-bit FSYNC word. + */ + class DTV_API catv_frame_sync_enc_bb : virtual public gr::block + { + public: + typedef boost::shared_ptr<catv_frame_sync_enc_bb> sptr; + + /*! + * \brief Create an ITU-T J.83B Frame Sync Encoder. + * + * \param ctrlword convolutional interleaver control word. + */ + static sptr make(int ctrlword); + }; + + } // namespace dtv +} // namespace gr + +#endif /* INCLUDED_DTV_CATV_FRAME_SYNC_ENC_BB_H */ + diff --git a/gr-dtv/include/gnuradio/dtv/catv_randomizer_bb.h b/gr-dtv/include/gnuradio/dtv/catv_randomizer_bb.h new file mode 100644 index 0000000000..83230b8167 --- /dev/null +++ b/gr-dtv/include/gnuradio/dtv/catv_randomizer_bb.h @@ -0,0 +1,53 @@ +/* -*- c++ -*- */ +/* + * Copyright 2016 Free Software Foundation, Inc. + * + * This 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. + * + * This software 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 software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DTV_CATV_RANDOMIZER_BB_H +#define INCLUDED_DTV_CATV_RANDOMIZER_BB_H + +#include <gnuradio/dtv/api.h> +#include <gnuradio/sync_block.h> + +namespace gr { + namespace dtv { + + /*! + * \brief Randomizer, x^3 + x + alpha^3, 7-bit symbols. + * \ingroup dtv + * + * Input: Interleaved MPEG-2 + RS parity bitstream packets of 128 7-bit symbols.\n + * Output: Scrambled FEC Frame packets of 60 * 128 7-bit symbols. + */ + class DTV_API catv_randomizer_bb : virtual public gr::sync_block + { + public: + typedef boost::shared_ptr<catv_randomizer_bb> sptr; + + /*! + * \brief Create an ITU-T J.83B randomizer. + * + */ + static sptr make(); + }; + + } // namespace dtv +} // namespace gr + +#endif /* INCLUDED_DTV_CATV_RANDOMIZER_BB_H */ + diff --git a/gr-dtv/include/gnuradio/dtv/catv_reed_solomon_enc_bb.h b/gr-dtv/include/gnuradio/dtv/catv_reed_solomon_enc_bb.h new file mode 100644 index 0000000000..b661f0da7f --- /dev/null +++ b/gr-dtv/include/gnuradio/dtv/catv_reed_solomon_enc_bb.h @@ -0,0 +1,53 @@ +/* -*- c++ -*- */ +/* + * Copyright 2016 Free Software Foundation, Inc. + * + * This 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. + * + * This software 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 software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DTV_CATV_REED_SOLOMON_ENC_BB_H +#define INCLUDED_DTV_CATV_REED_SOLOMON_ENC_BB_H + +#include <gnuradio/dtv/api.h> +#include <gnuradio/block.h> + +namespace gr { + namespace dtv { + + /*! + * \brief Reed Solomon Encoder, t=3, (128,122), 7-bit symbols. + * \ingroup dtv + * + * Input: MPEG-2 bitstream packets of 122 7-bit symbols.\n + * Output: MPEG-2 + RS parity bitstream packets of 128 7-bit symbols. + */ + class DTV_API catv_reed_solomon_enc_bb : virtual public gr::block + { + public: + typedef boost::shared_ptr<catv_reed_solomon_enc_bb> sptr; + + /*! + * \brief Create an ITU-T J.83B Reed Solomon encoder. + * + */ + static sptr make(); + }; + + } // namespace dtv +} // namespace gr + +#endif /* INCLUDED_DTV_CATV_REED_SOLOMON_ENC_BB_H */ + diff --git a/gr-dtv/include/gnuradio/dtv/catv_transport_framing_enc_bb.h b/gr-dtv/include/gnuradio/dtv/catv_transport_framing_enc_bb.h new file mode 100644 index 0000000000..7df100d990 --- /dev/null +++ b/gr-dtv/include/gnuradio/dtv/catv_transport_framing_enc_bb.h @@ -0,0 +1,53 @@ +/* -*- c++ -*- */ +/* + * Copyright 2016 Free Software Foundation, Inc. + * + * This 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. + * + * This software 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 software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DTV_CATV_TRANSPORT_FRAMING_ENC_BB_H +#define INCLUDED_DTV_CATV_TRANSPORT_FRAMING_ENC_BB_H + +#include <gnuradio/dtv/api.h> +#include <gnuradio/sync_block.h> + +namespace gr { + namespace dtv { + + /*! + * \brief Transport Framing Encoder. Adds a parity checksum to MPEG-2 packets. + * \ingroup dtv + * + * Input: MPEG-2 Transport Stream.\n + * Output: MPEG-2 Transport Stream with parity checksum byte. + */ + class DTV_API catv_transport_framing_enc_bb : virtual public gr::sync_block + { + public: + typedef boost::shared_ptr<catv_transport_framing_enc_bb> sptr; + + /*! + * \brief Create an ITU-T J.83B Transport Framing Encoder. + * + */ + static sptr make(); + }; + + } // namespace dtv +} // namespace gr + +#endif /* INCLUDED_DTV_CATV_TRANSPORT_FRAMING_ENC_BB_H */ + diff --git a/gr-dtv/include/gnuradio/dtv/catv_trellis_enc_bb.h b/gr-dtv/include/gnuradio/dtv/catv_trellis_enc_bb.h new file mode 100644 index 0000000000..aa6cec1703 --- /dev/null +++ b/gr-dtv/include/gnuradio/dtv/catv_trellis_enc_bb.h @@ -0,0 +1,53 @@ +/* -*- c++ -*- */ +/* + * Copyright 2016 Free Software Foundation, Inc. + * + * This 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. + * + * This software 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 software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DTV_CATV_TRELLIS_ENC_BB_H +#define INCLUDED_DTV_CATV_TRELLIS_ENC_BB_H + +#include <gnuradio/dtv/api.h> +#include <gnuradio/block.h> + +namespace gr { + namespace dtv { + + /*! + * \brief Trellis Encoder. 14/15 code rate. + * \ingroup dtv + * + * Input: Scrambled FEC Frame packets of 60 * 128 7-bit symbols with 42-bit FSYNC word.\n + * Output: Four 7-bit symbols (28 bits) Trellis encoded to 30 bits (14/15 code rate). + */ + class DTV_API catv_trellis_enc_bb : virtual public gr::block + { + public: + typedef boost::shared_ptr<catv_trellis_enc_bb> sptr; + + /*! + * \brief Create an ITU-T J.83B Trellis Encoder. + * + */ + static sptr make(); + }; + + } // namespace dtv +} // namespace gr + +#endif /* INCLUDED_DTV_CATV_TRELLIS_ENC_BB_H */ + diff --git a/gr-dtv/include/gnuradio/dtv/dvbs2_config.h b/gr-dtv/include/gnuradio/dtv/dvbs2_config.h index 919c7de155..079857a8cf 100644 --- a/gr-dtv/include/gnuradio/dtv/dvbs2_config.h +++ b/gr-dtv/include/gnuradio/dtv/dvbs2_config.h @@ -38,11 +38,17 @@ namespace gr { PILOTS_ON, }; + enum dvbs2_interpolation_t { + INTERPOLATION_OFF = 0, + INTERPOLATION_ON, + }; + } // namespace dtv } // namespace gr typedef gr::dtv::dvbs2_rolloff_factor_t dvbs2_rolloff_factor_t; typedef gr::dtv::dvbs2_pilots_t dvbs2_pilots_t; +typedef gr::dtv::dvbs2_interpolation_t dvbs2_interpolation_t; #endif /* INCLUDED_DTV_DVBS2_CONFIG_H */ diff --git a/gr-dtv/include/gnuradio/dtv/dvbs2_modulator_bc.h b/gr-dtv/include/gnuradio/dtv/dvbs2_modulator_bc.h index 68e5403e05..a1f2c9e6fe 100644 --- a/gr-dtv/include/gnuradio/dtv/dvbs2_modulator_bc.h +++ b/gr-dtv/include/gnuradio/dtv/dvbs2_modulator_bc.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2015 Free Software Foundation, Inc. + * Copyright 2015,2016 Free Software Foundation, Inc. * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,6 +23,7 @@ #include <gnuradio/dtv/api.h> #include <gnuradio/dtv/dvb_config.h> +#include <gnuradio/dtv/dvbs2_config.h> #include <gnuradio/block.h> namespace gr { @@ -46,8 +47,9 @@ namespace gr { * \param framesize FEC frame size (normal or short). * \param rate FEC code rate. * \param constellation DVB-S2 constellation. + * \param interpolation 2X zero stuffing interpolation (on/off). */ - static sptr make(dvb_framesize_t framesize, dvb_code_rate_t rate, dvb_constellation_t constellation); + static sptr make(dvb_framesize_t framesize, dvb_code_rate_t rate, dvb_constellation_t constellation, dvbs2_interpolation_t interpolation); }; } // namespace dtv diff --git a/gr-dtv/lib/CMakeLists.txt b/gr-dtv/lib/CMakeLists.txt index fc78cccf9e..fec48990a2 100644 --- a/gr-dtv/lib/CMakeLists.txt +++ b/gr-dtv/lib/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2014,2015 Free Software Foundation, Inc. +# Copyright 2014,2015,2016 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -112,6 +112,11 @@ list(APPEND dtv_sources dvbt/dvbt_convolutional_deinterleaver_impl.cc dvbt/dvbt_reed_solomon_dec_impl.cc dvbt/dvbt_energy_descramble_impl.cc + catv/catv_transport_framing_enc_bb_impl.cc + catv/catv_reed_solomon_enc_bb_impl.cc + catv/catv_randomizer_bb_impl.cc + catv/catv_frame_sync_enc_bb_impl.cc + catv/catv_trellis_enc_bb_impl.cc ) if(ENABLE_GR_CTRLPORT) diff --git a/gr-dtv/lib/atsc/atsc_equalizer_impl.cc b/gr-dtv/lib/atsc/atsc_equalizer_impl.cc index c804be6dc7..de01cea6f2 100644 --- a/gr-dtv/lib/atsc/atsc_equalizer_impl.cc +++ b/gr-dtv/lib/atsc/atsc_equalizer_impl.cc @@ -28,6 +28,7 @@ #include "atsc_types.h" #include "atsc_pnXXX_impl.h" #include <gnuradio/io_signature.h> +#include <volk/volk.h> namespace gr { namespace dtv { @@ -76,23 +77,42 @@ namespace gr { init_field_sync_common(training_sequence1, 0); init_field_sync_common(training_sequence2, 1); - for (int i = 0; i < NTAPS; i++) - d_taps[i] = 0.0; + d_taps.resize(NTAPS, 0.0f); d_buff_not_filled = true; + + const int alignment_multiple = + volk_get_alignment() / sizeof(float); + set_alignment(std::max(1, alignment_multiple)); } atsc_equalizer_impl::~atsc_equalizer_impl() { } + std::vector<float> + atsc_equalizer_impl::taps() const + { + return d_taps; + } + + std::vector<float> + atsc_equalizer_impl::data() const + { + std::vector<float> ret(&data_mem2[0], &data_mem2[ATSC_DATA_SEGMENT_LENGTH-1]); + return ret; + } + void - atsc_equalizer_impl::filterN(const float *input_samples, float *output_samples, int nsamples) + atsc_equalizer_impl::filterN(const float *input_samples, + float *output_samples, + int nsamples) { for (int j = 0; j < nsamples; j++) { output_samples[j] = 0; - for(int i = 0; i < NTAPS; i++) - output_samples[j] += d_taps[i] * input_samples[j + i]; + volk_32f_x2_dot_prod_32f(&output_samples[j], + &input_samples[j], + &d_taps[0], NTAPS); } } @@ -107,14 +127,16 @@ namespace gr { for(int j = 0; j < nsamples; j++) { output_samples[j] = 0; - for( int i = 0; i < NTAPS; i++ ) - output_samples[j] += d_taps[i] * input_samples[j + i]; + volk_32f_x2_dot_prod_32f(&output_samples[j], + &input_samples[j], + &d_taps[0], NTAPS); - double e = output_samples[j] - training_pattern[j]; + float e = output_samples[j] - training_pattern[j]; // update taps... - for( int i = 0; i < NTAPS; i++ ) - d_taps[i] -= BETA * e * (double)(input_samples[j + i]); + float tmp_taps[NTAPS]; + volk_32f_s32f_multiply_32f(tmp_taps, &input_samples[j], BETA*e, NTAPS); + volk_32f_x2_subtract_32f(&d_taps[0], &d_taps[0], tmp_taps, NTAPS); } } @@ -131,8 +153,8 @@ namespace gr { int i = 0; if(d_buff_not_filled) { - for(int j = 0; j < ATSC_DATA_SEGMENT_LENGTH; j++) - data_mem[NPRETAPS + j] = in[i].data[j]; + memcpy(&data_mem[NPRETAPS], in[i].data, + ATSC_DATA_SEGMENT_LENGTH*sizeof(float)); d_flags = in[i].pli._flags; d_segno = in[i].pli._segno; d_buff_not_filled = false; @@ -141,8 +163,8 @@ namespace gr { for (; i < noutput_items; i++) { - for(int j = 0; j < NTAPS - NPRETAPS; j++) - data_mem[ATSC_DATA_SEGMENT_LENGTH + NPRETAPS + j] = in[i].data[j]; + memcpy(&data_mem[ATSC_DATA_SEGMENT_LENGTH + NPRETAPS], in[i].data, + (NTAPS - NPRETAPS)*sizeof(float)); if(d_segno == -1) { if(d_flags & 0x0010) { @@ -157,19 +179,18 @@ namespace gr { else { filterN(data_mem, data_mem2, ATSC_DATA_SEGMENT_LENGTH); - for(int j = 0; j < ATSC_DATA_SEGMENT_LENGTH; j++) - out[output_produced].data[j] = data_mem2[j]; + memcpy(out[output_produced].data, data_mem2, + ATSC_DATA_SEGMENT_LENGTH*sizeof(float)); out[output_produced].pli._flags = d_flags; out[output_produced].pli._segno = d_segno; output_produced++; } - for( int j = 0; j < NPRETAPS; j++ ) - data_mem[j] = data_mem[ATSC_DATA_SEGMENT_LENGTH + j]; - - for(int j = 0; j < ATSC_DATA_SEGMENT_LENGTH; j++) - data_mem[NPRETAPS + j] = in[i].data[j]; + memcpy(data_mem, &data_mem[ATSC_DATA_SEGMENT_LENGTH], + NPRETAPS*sizeof(float)); + memcpy(&data_mem[NPRETAPS], in[i].data, + ATSC_DATA_SEGMENT_LENGTH*sizeof(float)); d_flags = in[i].pli._flags; d_segno = in[i].pli._segno; @@ -179,5 +200,31 @@ namespace gr { return output_produced; } + void + atsc_equalizer_impl::setup_rpc() + { +#ifdef GR_CTRLPORT + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_get<atsc_equalizer, std::vector<float> >( + alias(), "taps", + &atsc_equalizer::taps, + pmt::make_f32vector(1,-10), + pmt::make_f32vector(1,10), + pmt::make_f32vector(1,0), + "", "Equalizer Taps", RPC_PRIVLVL_MIN, + DISPTIME))); + + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_get<atsc_equalizer, std::vector<float> >( + alias(), "data", + &atsc_equalizer::data, + pmt::make_f32vector(1,-10), + pmt::make_f32vector(1,10), + pmt::make_f32vector(1,0), + "", "Post-equalizer Data", RPC_PRIVLVL_MIN, + DISPTIME))); +#endif /* GR_CTRLPORT */ + } + } /* namespace dtv */ } /* namespace gr */ diff --git a/gr-dtv/lib/atsc/atsc_equalizer_impl.h b/gr-dtv/lib/atsc/atsc_equalizer_impl.h index 6ff89ca646..75862f6408 100644 --- a/gr-dtv/lib/atsc/atsc_equalizer_impl.h +++ b/gr-dtv/lib/atsc/atsc_equalizer_impl.h @@ -46,7 +46,7 @@ namespace gr { void adaptN(const float *input_samples, const float *training_pattern, float *output_samples, int nsamples); - float d_taps[NTAPS]; + std::vector<float> d_taps; float data_mem[ATSC_DATA_SEGMENT_LENGTH + NTAPS]; // Buffer for previous data packet float data_mem2[ATSC_DATA_SEGMENT_LENGTH]; @@ -59,6 +59,11 @@ namespace gr { atsc_equalizer_impl(); ~atsc_equalizer_impl(); + void setup_rpc(); + + std::vector<float> taps() const; + std::vector<float> data() const; + virtual int general_work(int noutput_items, gr_vector_int &ninput_items, gr_vector_const_void_star &input_items, diff --git a/gr-dtv/lib/atsc/atsc_rs_decoder_impl.cc b/gr-dtv/lib/atsc/atsc_rs_decoder_impl.cc index 7a950e716e..b8ee91a52d 100644 --- a/gr-dtv/lib/atsc/atsc_rs_decoder_impl.cc +++ b/gr-dtv/lib/atsc/atsc_rs_decoder_impl.cc @@ -56,9 +56,9 @@ namespace gr { { d_rs = init_rs_char(rs_init_symsize, rs_init_gfpoly, rs_init_fcr, rs_init_prim, rs_init_nroots); assert (d_rs != 0); - nerrors_corrrected_count = 0; - bad_packet_count = 0; - total_packets = 0; + d_nerrors_corrrected_count = 0; + d_bad_packet_count = 0; + d_total_packets = 0; } int atsc_rs_decoder_impl::decode (atsc_mpeg_packet_no_sync &out, const atsc_mpeg_packet_rs_encoded &in) @@ -89,6 +89,24 @@ namespace gr { } int + atsc_rs_decoder_impl::num_errors_corrected() const + { + return d_nerrors_corrrected_count; + } + + int + atsc_rs_decoder_impl::num_bad_packets() const + { + return d_bad_packet_count; + } + + int + atsc_rs_decoder_impl::num_packets() const + { + return d_total_packets; + } + + int atsc_rs_decoder_impl::work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) @@ -102,24 +120,20 @@ namespace gr { int nerrors_corrrected = decode(out[i], in[i]); out[i].pli.set_transport_error(nerrors_corrrected == -1); - if (nerrors_corrrected == -1) - bad_packet_count++; - else - nerrors_corrrected_count += nerrors_corrrected; + if (nerrors_corrrected == -1) { + d_bad_packet_count++; + d_nerrors_corrrected_count += 10; // lower bound estimate; most this RS can fix + } + else { + d_nerrors_corrrected_count += nerrors_corrrected; + } - total_packets++; + d_total_packets++; #if 0 - if (total_packets > 1000) { - // FIXME: convert to logger - std::cout << "Error rate: " - << (float)nerrors_corrrected_count/total_packets - << "\tPacket error rate: " - << (float)bad_packet_count/total_packets - << std::endl; - - nerrors_corrrected_count = 0; - bad_packet_count = 0; - total_packets = 0; + if (d_total_packets > 1000) { + GR_LOG_INFO(d_logger, boost::format("Error rate: %1%\tPacket error rate: %2%") \ + % ((float)d_nerrors_corrrected_count/(ATSC_MPEG_DATA_LENGTH*d_total_packets)) + % ((float)d_bad_packet_count/d_total_packets)); } #endif } @@ -127,5 +141,42 @@ namespace gr { return noutput_items; } + + void + atsc_rs_decoder_impl::setup_rpc() + { +#ifdef GR_CTRLPORT + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_get<atsc_rs_decoder, int >( + alias(), "num_errors_corrected", + &atsc_rs_decoder::num_errors_corrected, + pmt::from_long(0), + pmt::from_long(10000000), + pmt::from_long(0), + "", "Number of errors corrected", RPC_PRIVLVL_MIN, + DISPTIME))); + + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_get<atsc_rs_decoder, int >( + alias(), "num_bad_packets", + &atsc_rs_decoder::num_bad_packets, + pmt::from_long(0), + pmt::from_long(10000000), + pmt::from_long(0), + "", "Number of bad packets", RPC_PRIVLVL_MIN, + DISPTIME))); + + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_get<atsc_rs_decoder, int >( + alias(), "num_packets", + &atsc_rs_decoder::num_packets, + pmt::from_long(0), + pmt::from_long(10000000), + pmt::from_long(0), + "", "Number of packets", RPC_PRIVLVL_MIN, + DISPTIME))); +#endif /* GR_CTRLPORT */ + } + } /* namespace dtv */ } /* namespace gr */ diff --git a/gr-dtv/lib/atsc/atsc_rs_decoder_impl.h b/gr-dtv/lib/atsc/atsc_rs_decoder_impl.h index 57460128dc..adbc4879a9 100644 --- a/gr-dtv/lib/atsc/atsc_rs_decoder_impl.h +++ b/gr-dtv/lib/atsc/atsc_rs_decoder_impl.h @@ -36,15 +36,22 @@ namespace gr { class atsc_rs_decoder_impl : public atsc_rs_decoder { private: - int nerrors_corrrected_count; - int bad_packet_count; - int total_packets; + int d_nerrors_corrrected_count; + int d_bad_packet_count; + int d_total_packets; + int d_total_bits; void *d_rs; public: atsc_rs_decoder_impl(); ~atsc_rs_decoder_impl(); + void setup_rpc(); + + int num_errors_corrected() const; + int num_bad_packets() const; + int num_packets() const; + /*! * Decode RS encoded packet. * \returns a count of corrected symbols, or -1 if the block was uncorrectible. diff --git a/gr-dtv/lib/atsc/atsc_single_viterbi.cc b/gr-dtv/lib/atsc/atsc_single_viterbi.cc index 385940e453..011cc0fe96 100644 --- a/gr-dtv/lib/atsc/atsc_single_viterbi.cc +++ b/gr-dtv/lib/atsc/atsc_single_viterbi.cc @@ -28,7 +28,7 @@ namespace gr { /* was_sent is a table of what symbol we get given what bit pair was sent and what state we where in [state][pair] */ - const int atsc_single_viterbi::was_sent[4][4] = { + const int atsc_single_viterbi::d_was_sent[4][4] = { {0,2,4,6}, {0,2,4,6}, {1,3,5,7}, @@ -37,7 +37,7 @@ namespace gr { /* transition_table is a table of what state we were in given current state and bit pair sent [state][pair] */ - const int atsc_single_viterbi::transition_table[4][4] = { + const int atsc_single_viterbi::d_transition_table[4][4] = { {0,2,0,2}, {2,0,2,0}, {1,3,1,3}, @@ -49,11 +49,12 @@ namespace gr { { for (unsigned int i = 0; i<2; i++) for (unsigned int j = 0; j<4; j++) { - path_metrics[i][j] = 0; - traceback[i][j] = 0; + d_path_metrics[i][j] = 0; + d_traceback[i][j] = 0; } - post_coder_state = 0; - phase = 0; + d_post_coder_state = 0; + d_phase = 0; + d_best_state_metric = 100000; } atsc_single_viterbi::atsc_single_viterbi() @@ -61,17 +62,24 @@ namespace gr { reset(); } + float + atsc_single_viterbi::best_state_metric() const + { + return d_best_state_metric; + } + char atsc_single_viterbi::decode(float input) { unsigned int best_state = 0; - float best_state_metric = 100000; + //float best_state_metric = 100000; + d_best_state_metric = 100000; /* Precompute distances from input to each possible symbol */ - float distances[8] = { (float)fabs( input + 7 ), (float)fabs( input + 5 ), - (float)fabs( input + 3 ), (float)fabs( input + 1 ), - (float)fabs( input - 1 ), (float)fabs( input - 3 ), - (float)fabs( input - 5 ), (float)fabs( input - 7 ) }; + float distances[8] = { fabsf( input + 7 ), fabsf( input + 5 ), + fabsf( input + 3 ), fabsf( input + 1 ), + fabsf( input - 1 ), fabsf( input - 3 ), + fabsf( input - 5 ), fabsf( input - 7 ) }; /* We start by iterating over all possible states */ for (unsigned int state = 0; state < 4; state++) { @@ -79,15 +87,20 @@ namespace gr { states to the state we are testing, we only need to look at the 4 paths that can be taken given the 2-bit input */ int min_metric_symb = 0; - float min_metric = distances[was_sent[state][0]] + path_metrics[phase][transition_table[state][0]]; + float min_metric = distances[d_was_sent[state][0]] + + d_path_metrics[d_phase][d_transition_table[state][0]]; + for (unsigned int symbol_sent = 1; symbol_sent < 4; symbol_sent++) - if( (distances[was_sent[state][symbol_sent]] + path_metrics[phase][transition_table[state][symbol_sent]]) < min_metric) { - min_metric = distances[was_sent[state][symbol_sent]] + path_metrics[phase][transition_table[state][symbol_sent]]; + if( (distances[d_was_sent[state][symbol_sent]] + + d_path_metrics[d_phase][d_transition_table[state][symbol_sent]]) < min_metric) { + min_metric = distances[d_was_sent[state][symbol_sent]] + + d_path_metrics[d_phase][d_transition_table[state][symbol_sent]]; min_metric_symb = symbol_sent; } - path_metrics[phase^1][state] = min_metric; - traceback[phase^1][state] = (((unsigned long long)min_metric_symb) << 62) | (traceback[phase][transition_table[state][min_metric_symb]] >> 2); + d_path_metrics[d_phase^1][state] = min_metric; + d_traceback[d_phase^1][state] = (((unsigned long long)min_metric_symb) << 62) | + (d_traceback[d_phase][d_transition_table[state][min_metric_symb]] >> 2); /* If this is the most probable state so far remember it, this only needs to be checked when we are about to output a path @@ -98,26 +111,24 @@ namespace gr { head state path will tend towards the optimal path with a probability approaching 1 in just 8 or so transitions */ - if(min_metric <= best_state_metric) { - best_state_metric = min_metric; + if(min_metric <= d_best_state_metric) { + d_best_state_metric = min_metric; best_state = state; } } - if(best_state_metric > 10000) { + if(d_best_state_metric > 10000) { for(unsigned int state = 0; state < 4; state++) - path_metrics[phase^1][state] -= best_state_metric; + d_path_metrics[d_phase^1][state] -= d_best_state_metric; } - phase ^= 1; + d_phase ^= 1; - int y2 = (0x2 & traceback[phase][best_state]) >> 1; - int x2 = y2 ^ post_coder_state; - post_coder_state = y2; + int y2 = (0x2 & d_traceback[d_phase][best_state]) >> 1; + int x2 = y2 ^ d_post_coder_state; + d_post_coder_state = y2; - return ( x2 << 1 ) | (0x1 & traceback[phase][best_state]); + return ( x2 << 1 ) | (0x1 & d_traceback[d_phase][best_state]); } } /* namespace dtv */ } /* namespace gr */ - - diff --git a/gr-dtv/lib/atsc/atsc_single_viterbi.h b/gr-dtv/lib/atsc/atsc_single_viterbi.h index 3c756c7690..9522e2eb1b 100644 --- a/gr-dtv/lib/atsc/atsc_single_viterbi.h +++ b/gr-dtv/lib/atsc/atsc_single_viterbi.h @@ -44,14 +44,17 @@ namespace gr { //! internal delay of decoder static int delay () { return TB_LEN - 1; } - protected: - static const int transition_table[4][4]; - static const int was_sent[4][4]; + float best_state_metric() const; - float path_metrics [2][4]; - unsigned long long traceback [2][4]; - unsigned char phase; - int post_coder_state; + protected: + static const int d_transition_table[4][4]; + static const int d_was_sent[4][4]; + + float d_path_metrics [2][4]; + unsigned long long d_traceback [2][4]; + unsigned char d_phase; + int d_post_coder_state; + float d_best_state_metric; }; } /* namespace dtv */ diff --git a/gr-dtv/lib/atsc/atsc_viterbi_decoder_impl.cc b/gr-dtv/lib/atsc/atsc_viterbi_decoder_impl.cc index 430eb55a17..2284372dfb 100644 --- a/gr-dtv/lib/atsc/atsc_viterbi_decoder_impl.cc +++ b/gr-dtv/lib/atsc/atsc_viterbi_decoder_impl.cc @@ -77,6 +77,15 @@ namespace gr { fifo[i]->reset(); } + std::vector<float> + atsc_viterbi_decoder_impl::decoder_metrics() const + { + std::vector<float> metrics(NCODERS); + for (int i = 0; i < NCODERS; i++) + metrics[i] = viterbi[i].best_state_metric(); + return metrics; + } + int atsc_viterbi_decoder_impl::work(int noutput_items, gr_vector_const_void_star &input_items, @@ -135,5 +144,21 @@ namespace gr { return noutput_items; } + void + atsc_viterbi_decoder_impl::setup_rpc() + { +#ifdef GR_CTRLPORT + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_get<atsc_viterbi_decoder, std::vector<float> >( + alias(), "decoder_metrics", + &atsc_viterbi_decoder::decoder_metrics, + pmt::make_f32vector(1,0), + pmt::make_f32vector(1,100000), + pmt::make_f32vector(1,0), + "", "Viterbi decoder metrics", RPC_PRIVLVL_MIN, + DISPTIME))); +#endif /* GR_CTRLPORT */ + } + } /* namespace dtv */ } /* namespace gr */ diff --git a/gr-dtv/lib/atsc/atsc_viterbi_decoder_impl.h b/gr-dtv/lib/atsc/atsc_viterbi_decoder_impl.h index b4fbbd1033..ef4faab313 100644 --- a/gr-dtv/lib/atsc/atsc_viterbi_decoder_impl.h +++ b/gr-dtv/lib/atsc/atsc_viterbi_decoder_impl.h @@ -63,8 +63,12 @@ namespace gr { atsc_viterbi_decoder_impl(); ~atsc_viterbi_decoder_impl(); + void setup_rpc(); + void reset(); + std::vector<float> decoder_metrics() const; + virtual int work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); diff --git a/gr-dtv/lib/catv/catv_frame_sync_enc_bb_impl.cc b/gr-dtv/lib/catv/catv_frame_sync_enc_bb_impl.cc new file mode 100644 index 0000000000..87c706b445 --- /dev/null +++ b/gr-dtv/lib/catv/catv_frame_sync_enc_bb_impl.cc @@ -0,0 +1,96 @@ +/* -*- c++ -*- */ +/* + * Copyright 2016 Free Software Foundation, Inc. + * + * This 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. + * + * This software 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 software; 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 "catv_frame_sync_enc_bb_impl.h" + +namespace gr { + namespace dtv { + + catv_frame_sync_enc_bb::sptr + catv_frame_sync_enc_bb::make(int ctrlword) + { + return gnuradio::get_initial_sptr + (new catv_frame_sync_enc_bb_impl(ctrlword)); + } + + /* + * The private constructor + */ + catv_frame_sync_enc_bb_impl::catv_frame_sync_enc_bb_impl(int ctrlword) + : gr::block("catv_frame_sync_enc_bb", + gr::io_signature::make(1, 1, sizeof(unsigned char)), + gr::io_signature::make(1, 1, sizeof(unsigned char))) + { + set_output_multiple(60 * 128 + 6); + control_word = ctrlword; + } + + /* + * Our virtual destructor. + */ + catv_frame_sync_enc_bb_impl::~catv_frame_sync_enc_bb_impl() + { + } + + void + catv_frame_sync_enc_bb_impl::forecast (int noutput_items, gr_vector_int &ninput_items_required) + { + ninput_items_required[0] = noutput_items / (60 * 128 + 6) * (60 * 128); + } + + int + catv_frame_sync_enc_bb_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 unsigned char *in = (const unsigned char *) input_items[0]; + unsigned char *out = (unsigned char *) output_items[0]; + + int i = 0, j = 0; + while (i < noutput_items) { + memcpy(out + i, in + j, 60 * 128); + i += 60 * 128; + j += 60 * 128; + + out[i++] = 0x75; + out[i++] = 0x2C; + out[i++] = 0x0D; + out[i++] = 0x6C; + out[i++] = control_word << 3; + out[i++] = 0x00; + } + + // Tell runtime system how many input items we consumed on + // each input stream. + consume_each (j); + + // Tell runtime system how many output items we produced. + return noutput_items; + } + + } /* namespace dtv */ +} /* namespace gr */ + diff --git a/gr-dtv/lib/catv/catv_frame_sync_enc_bb_impl.h b/gr-dtv/lib/catv/catv_frame_sync_enc_bb_impl.h new file mode 100644 index 0000000000..fb924f7a14 --- /dev/null +++ b/gr-dtv/lib/catv/catv_frame_sync_enc_bb_impl.h @@ -0,0 +1,50 @@ +/* -*- c++ -*- */ +/* + * Copyright 2016 Free Software Foundation, Inc. + * + * This 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. + * + * This software 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 software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DTV_CATV_FRAME_SYNC_ENC_BB_IMPL_H +#define INCLUDED_DTV_CATV_FRAME_SYNC_ENC_BB_IMPL_H + +#include <gnuradio/dtv/catv_frame_sync_enc_bb.h> + +namespace gr { + namespace dtv { + + class catv_frame_sync_enc_bb_impl : public catv_frame_sync_enc_bb + { + private: + int control_word; + + public: + catv_frame_sync_enc_bb_impl(int ctrlword); + ~catv_frame_sync_enc_bb_impl(); + + 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); + }; + + } // namespace dtv +} // namespace gr + +#endif /* INCLUDED_DTV_CATV_FRAME_SYNC_ENC_BB_IMPL_H */ + diff --git a/gr-dtv/lib/catv/catv_randomizer_bb_impl.cc b/gr-dtv/lib/catv/catv_randomizer_bb_impl.cc new file mode 100644 index 0000000000..698288dcd3 --- /dev/null +++ b/gr-dtv/lib/catv/catv_randomizer_bb_impl.cc @@ -0,0 +1,104 @@ +/* -*- c++ -*- */ +/* + * Copyright 2016 Free Software Foundation, Inc. + * + * This 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. + * + * This software 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 software; 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 "catv_randomizer_bb_impl.h" + +namespace gr { + namespace dtv { + + catv_randomizer_bb::sptr + catv_randomizer_bb::make() + { + return gnuradio::get_initial_sptr + (new catv_randomizer_bb_impl()); + } + + /* + * The private constructor + */ + catv_randomizer_bb_impl::catv_randomizer_bb_impl() + : gr::sync_block("catv_randomizer_bb", + gr::io_signature::make(1, 1, sizeof(unsigned char)), + gr::io_signature::make(1, 1, sizeof(unsigned char))) + { + init_rand(); + + offset = 0; + max_offset = 60 * 128; + } + + /* + * Our virtual destructor. + */ + catv_randomizer_bb_impl::~catv_randomizer_bb_impl() + { + } + + void + catv_randomizer_bb_impl::init_rand() + { + unsigned char c2 = 0x7F, c1 = 0x7F, c0 = 0x7F; + unsigned char c2_new, c1_new, c0_new; + int n, i; + + for (n = 0; n < 60 * 128; n++) { + rseq[n] = c2; + c2_new = c1; + c1_new = c0 ^ c2; + c0_new = c2; + for (i = 0; i < 3; i++) { + c0_new <<= 1; + if (c0_new & 0x80) { + c0_new = (c0_new & 0x7F) ^ 0x09; + } + } + c2 = c2_new; + c1 = c1_new; + c0 = c0_new; + } + } + + int + catv_randomizer_bb_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + const unsigned char *in = (const unsigned char *) input_items[0]; + unsigned char *out = (unsigned char *) output_items[0]; + + for (int i = 0; i < noutput_items; i++) { + out[i] = in[i] ^ rseq[offset++]; + if (offset == max_offset) { + offset = 0; + } + } + + // Tell runtime system how many output items we produced. + return noutput_items; + } + + } /* namespace dtv */ +} /* namespace gr */ + diff --git a/gr-dtv/lib/catv/catv_randomizer_bb_impl.h b/gr-dtv/lib/catv/catv_randomizer_bb_impl.h new file mode 100644 index 0000000000..e4e40f8f6e --- /dev/null +++ b/gr-dtv/lib/catv/catv_randomizer_bb_impl.h @@ -0,0 +1,49 @@ +/* -*- c++ -*- */ +/* + * Copyright 2016 Free Software Foundation, Inc. + * + * This 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. + * + * This software 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 software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DTV_CATV_RANDOMIZER_BB_IMPL_H +#define INCLUDED_DTV_CATV_RANDOMIZER_BB_IMPL_H + +#include <gnuradio/dtv/catv_randomizer_bb.h> + +namespace gr { + namespace dtv { + + class catv_randomizer_bb_impl : public catv_randomizer_bb + { + private: + unsigned char rseq[60 * 128]; + int offset, max_offset; + void init_rand(); + + public: + catv_randomizer_bb_impl(); + ~catv_randomizer_bb_impl(); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } // namespace dtv +} // namespace gr + +#endif /* INCLUDED_DTV_CATV_RANDOMIZER_BB_IMPL_H */ + diff --git a/gr-dtv/lib/catv/catv_reed_solomon_enc_bb_impl.cc b/gr-dtv/lib/catv/catv_reed_solomon_enc_bb_impl.cc new file mode 100644 index 0000000000..06a1f12c3e --- /dev/null +++ b/gr-dtv/lib/catv/catv_reed_solomon_enc_bb_impl.cc @@ -0,0 +1,153 @@ +/* -*- c++ -*- */ +/* + * Copyright 2016 Free Software Foundation, Inc. + * + * This 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. + * + * This software 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 software; 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 "catv_reed_solomon_enc_bb_impl.h" + +namespace gr { + namespace dtv { + + catv_reed_solomon_enc_bb::sptr + catv_reed_solomon_enc_bb::make() + { + return gnuradio::get_initial_sptr + (new catv_reed_solomon_enc_bb_impl()); + } + + /* + * The private constructor + */ + catv_reed_solomon_enc_bb_impl::catv_reed_solomon_enc_bb_impl() + : gr::block("catv_reed_solomon_enc_bb", + gr::io_signature::make(1, 1, sizeof(unsigned char)), + gr::io_signature::make(1, 1, sizeof(unsigned char))) + { + set_output_multiple(128); + init_rs(); + } + + /* + * Our virtual destructor. + */ + catv_reed_solomon_enc_bb_impl::~catv_reed_solomon_enc_bb_impl() + { + } + + void + catv_reed_solomon_enc_bb_impl::init_rs() + { + unsigned char x; + int i, j; + + gf_exp[0] = 1; + gf_log[1] = 0; + + x = 1; + for (i = 1; i < 127; i++) { + x <<= 1; + if (x & 0x80) { + x = (x & 0x7F) ^ 0x09; + } + gf_exp[i] = x; + gf_log[x] = i; + } + for (; i < 256; i++) { + gf_exp[i] = gf_exp[i - 127]; + } + + for (i = 0; i < 128; i++) { + for (j = 0; j < 128; j++) { + if ((i == 0) || (j == 0)) { + gf_mul_table[i][j] = 0; + } else { + gf_mul_table[i][j] = gf_exp[gf_log[i] + gf_log[j]]; + } + } + } + } + + unsigned char + catv_reed_solomon_enc_bb_impl::gf_poly_eval(unsigned char *p, int len, unsigned char x) + { + unsigned char y = p[0]; + int i; + + for (i = 1; i < len; i++) { + y = gf_mul_table[y][x] ^ p[i]; + } + return y; + } + + void + catv_reed_solomon_enc_bb_impl::reed_solomon_enc(const unsigned char *message, unsigned char *output) + { + // Generator polynomial from p.7 of ANSI/SCTE 07 2013 + unsigned char g[] = {1, gf_exp[52], gf_exp[116], gf_exp[119], gf_exp[61], gf_exp[15]}; + int i, j; + + memcpy(output, message, 122); + memset(output + 122, 0, 6); + + for (i = 0; i < 122; i++) { + for (j = 1; j < 6; j++) { + output[i + j] ^= gf_mul_table[output[i]][g[j]]; + } + output[i] = message[i]; + } + + output[127] = gf_poly_eval(output, 128, gf_exp[6]); + } + + void + catv_reed_solomon_enc_bb_impl::forecast (int noutput_items, gr_vector_int &ninput_items_required) + { + ninput_items_required[0] = (noutput_items / 128) * 122; + } + + int + catv_reed_solomon_enc_bb_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 unsigned char *in = (const unsigned char *) input_items[0]; + unsigned char *out = (unsigned char *) output_items[0]; + int j = 0; + + for (int i = 0; i < noutput_items; i += 128) { + reed_solomon_enc(in + j, out + i); + j += 122; + } + + // Tell runtime system how many input items we consumed on + // each input stream. + consume_each (j); + + // Tell runtime system how many output items we produced. + return noutput_items; + } + + } /* namespace dtv */ +} /* namespace gr */ + diff --git a/gr-dtv/lib/catv/catv_reed_solomon_enc_bb_impl.h b/gr-dtv/lib/catv/catv_reed_solomon_enc_bb_impl.h new file mode 100644 index 0000000000..84e4cb500e --- /dev/null +++ b/gr-dtv/lib/catv/catv_reed_solomon_enc_bb_impl.h @@ -0,0 +1,56 @@ +/* -*- c++ -*- */ +/* + * Copyright 2016 Free Software Foundation, Inc. + * + * This 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. + * + * This software 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 software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DTV_CATV_REED_SOLOMON_ENC_BB_IMPL_H +#define INCLUDED_DTV_CATV_REED_SOLOMON_ENC_BB_IMPL_H + +#include <gnuradio/dtv/catv_reed_solomon_enc_bb.h> + +namespace gr { + namespace dtv { + + class catv_reed_solomon_enc_bb_impl : public catv_reed_solomon_enc_bb + { + private: + unsigned char gf_mul_table[128][128]; + unsigned char gf_exp[256]; + unsigned char gf_log[128]; + + void init_rs(); + unsigned char gf_poly_eval(unsigned char *p, int len, unsigned char x); + void reed_solomon_enc(const unsigned char *message, unsigned char *output); + + public: + catv_reed_solomon_enc_bb_impl(); + ~catv_reed_solomon_enc_bb_impl(); + + 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); + }; + + } // namespace dtv +} // namespace gr + +#endif /* INCLUDED_DTV_CATV_REED_SOLOMON_ENC_BB_IMPL_H */ + diff --git a/gr-dtv/lib/catv/catv_transport_framing_enc_bb_impl.cc b/gr-dtv/lib/catv/catv_transport_framing_enc_bb_impl.cc new file mode 100644 index 0000000000..4082e8265c --- /dev/null +++ b/gr-dtv/lib/catv/catv_transport_framing_enc_bb_impl.cc @@ -0,0 +1,131 @@ +/* -*- c++ -*- */ +/* + * Copyright 2016 Free Software Foundation, Inc. + * + * This 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. + * + * This software 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 software; 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 "catv_transport_framing_enc_bb_impl.h" + +namespace gr { + namespace dtv { + + catv_transport_framing_enc_bb::sptr + catv_transport_framing_enc_bb::make() + { + return gnuradio::get_initial_sptr + (new catv_transport_framing_enc_bb_impl()); + } + + /* + * The private constructor + */ + catv_transport_framing_enc_bb_impl::catv_transport_framing_enc_bb_impl() + : gr::sync_block("catv_transport_framing_enc_bb", + gr::io_signature::make(1, 1, sizeof(unsigned char)), + gr::io_signature::make(1, 1, sizeof(unsigned char))) + { + set_output_multiple(188); + } + + /* + * Our virtual destructor. + */ + catv_transport_framing_enc_bb_impl::~catv_transport_framing_enc_bb_impl() + { + } + + unsigned char catv_transport_framing_enc_bb_impl::compute_sum(const unsigned char *bytes) + { + unsigned char i, bit, out, out1, out2, out3; + + unsigned char tapsG = 0xB1; // 10110001 + unsigned char tapsB = 0x45; // 1000101 + + unsigned char register1 = 0; + unsigned char register2 = 0; + unsigned char register3 = 0; + + unsigned char result = 0x67; + + unsigned char first7[] = {0, 0, 0, 0, 0, 0, 0, 0}; + + for (i = 0; i < 8; i++) { + bit = (bytes[0] >> (7-i)) & 1; + out = (register1 & 1) ^ bit; + if (i < 7) { + first7[i+1] = out; + } + register1 >>= 1; + if (out == 1) { + register1 ^= tapsG; + } + } + + for (i = 1; i < 187; i++) { + register1 = crctable[((register1) ^ BitReverseTable[bytes[i]]) & 0xff]; + } + + for (i = 0; i < 8; i++) { + out1 = register1 & 1; + register1 >>= 1; + if (out1 == 1) { + register1 ^= tapsG; + } + + out2 = (register2 & 1) ^ first7[i]; + register2 >>= 1; + if (first7[i] == 1) { + register2 ^= tapsB; + } + + out3 = (register3 & 1) ^ out1 ^ out2; + register3 >>= 1; + if ((out1 ^ out2) == 1) { + register3 ^= tapsG; + } + + result ^= (out3 << (7-i)); + } + + return result; + } + + int + catv_transport_framing_enc_bb_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + const unsigned char *in = (const unsigned char *) input_items[0]; + unsigned char *out = (unsigned char *) output_items[0]; + + for (int i = 0; i < noutput_items; i += 188) { + memcpy(out + i, in + i + 1, 187); + out[i + 187] = compute_sum(in + i + 1); + } + + // Tell runtime system how many output items we produced. + return noutput_items; + } + + } /* namespace dtv */ +} /* namespace gr */ + diff --git a/gr-dtv/lib/catv/catv_transport_framing_enc_bb_impl.h b/gr-dtv/lib/catv/catv_transport_framing_enc_bb_impl.h new file mode 100644 index 0000000000..fe69bc01f6 --- /dev/null +++ b/gr-dtv/lib/catv/catv_transport_framing_enc_bb_impl.h @@ -0,0 +1,84 @@ +/* -*- c++ -*- */ +/* + * Copyright 2016 Free Software Foundation, Inc. + * + * This 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. + * + * This software 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 software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DTV_CATV_TRANSPORT_FRAMING_ENC_BB_IMPL_H +#define INCLUDED_DTV_CATV_TRANSPORT_FRAMING_ENC_BB_IMPL_H + +#include <gnuradio/dtv/catv_transport_framing_enc_bb.h> + +namespace gr { + namespace dtv { + static const unsigned char crctable[] = { + 0x00,0x1b,0x36,0x2d,0x6c,0x77,0x5a,0x41,0xd8,0xc3,0xee,0xf5,0xb4,0xaf,0x82,0x99, + 0xd3,0xc8,0xe5,0xfe,0xbf,0xa4,0x89,0x92,0x0b,0x10,0x3d,0x26,0x67,0x7c,0x51,0x4a, + 0xc5,0xde,0xf3,0xe8,0xa9,0xb2,0x9f,0x84,0x1d,0x06,0x2b,0x30,0x71,0x6a,0x47,0x5c, + 0x16,0x0d,0x20,0x3b,0x7a,0x61,0x4c,0x57,0xce,0xd5,0xf8,0xe3,0xa2,0xb9,0x94,0x8f, + 0xe9,0xf2,0xdf,0xc4,0x85,0x9e,0xb3,0xa8,0x31,0x2a,0x07,0x1c,0x5d,0x46,0x6b,0x70, + 0x3a,0x21,0x0c,0x17,0x56,0x4d,0x60,0x7b,0xe2,0xf9,0xd4,0xcf,0x8e,0x95,0xb8,0xa3, + 0x2c,0x37,0x1a,0x01,0x40,0x5b,0x76,0x6d,0xf4,0xef,0xc2,0xd9,0x98,0x83,0xae,0xb5, + 0xff,0xe4,0xc9,0xd2,0x93,0x88,0xa5,0xbe,0x27,0x3c,0x11,0x0a,0x4b,0x50,0x7d,0x66, + 0xb1,0xaa,0x87,0x9c,0xdd,0xc6,0xeb,0xf0,0x69,0x72,0x5f,0x44,0x05,0x1e,0x33,0x28, + 0x62,0x79,0x54,0x4f,0x0e,0x15,0x38,0x23,0xba,0xa1,0x8c,0x97,0xd6,0xcd,0xe0,0xfb, + 0x74,0x6f,0x42,0x59,0x18,0x03,0x2e,0x35,0xac,0xb7,0x9a,0x81,0xc0,0xdb,0xf6,0xed, + 0xa7,0xbc,0x91,0x8a,0xcb,0xd0,0xfd,0xe6,0x7f,0x64,0x49,0x52,0x13,0x08,0x25,0x3e, + 0x58,0x43,0x6e,0x75,0x34,0x2f,0x02,0x19,0x80,0x9b,0xb6,0xad,0xec,0xf7,0xda,0xc1, + 0x8b,0x90,0xbd,0xa6,0xe7,0xfc,0xd1,0xca,0x53,0x48,0x65,0x7e,0x3f,0x24,0x09,0x12, + 0x9d,0x86,0xab,0xb0,0xf1,0xea,0xc7,0xdc,0x45,0x5e,0x73,0x68,0x29,0x32,0x1f,0x04, + 0x4e,0x55,0x78,0x63,0x22,0x39,0x14,0x0f,0x96,0x8d,0xa0,0xbb,0xfa,0xe1,0xcc,0xd7 + }; + + static const unsigned char BitReverseTable[] = { + 0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0,0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0, + 0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8,0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8, + 0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4,0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4, + 0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC,0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC, + 0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2,0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2, + 0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA,0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA, + 0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6,0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6, + 0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE,0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE, + 0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1,0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1, + 0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9,0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9, + 0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5,0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5, + 0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED,0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD, + 0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3,0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3, + 0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB,0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB, + 0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7,0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7, + 0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF,0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF + }; + + class catv_transport_framing_enc_bb_impl : public catv_transport_framing_enc_bb + { + private: + unsigned char compute_sum(const unsigned char *bytes); + + public: + catv_transport_framing_enc_bb_impl(); + ~catv_transport_framing_enc_bb_impl(); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } // namespace dtv +} // namespace gr + +#endif /* INCLUDED_DTV_CATV_TRANSPORT_FRAMING_ENC_BB_IMPL_H */ + diff --git a/gr-dtv/lib/catv/catv_trellis_enc_bb_impl.cc b/gr-dtv/lib/catv/catv_trellis_enc_bb_impl.cc new file mode 100644 index 0000000000..8fdb8722b6 --- /dev/null +++ b/gr-dtv/lib/catv/catv_trellis_enc_bb_impl.cc @@ -0,0 +1,193 @@ +/* -*- c++ -*- */ +/* + * Copyright 2016 Free Software Foundation, Inc. + * + * This 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. + * + * This software 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 software; 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 "catv_trellis_enc_bb_impl.h" + +namespace gr { + namespace dtv { + + catv_trellis_enc_bb::sptr + catv_trellis_enc_bb::make() + { + return gnuradio::get_initial_sptr + (new catv_trellis_enc_bb_impl()); + } + + /* + * The private constructor + */ + catv_trellis_enc_bb_impl::catv_trellis_enc_bb_impl() + : gr::block("catv_trellis_enc_bb", + gr::io_signature::make(1, 1, sizeof(unsigned char)), + gr::io_signature::make(1, 1, sizeof(unsigned char))) + { + set_output_multiple(5); + + init_trellis(); + + Xq = 0; + Yq = 0; + XYp = 0; + } + + /* + * Our virtual destructor. + */ + catv_trellis_enc_bb_impl::~catv_trellis_enc_bb_impl() + { + } + + void + catv_trellis_enc_bb_impl::forecast (int noutput_items, gr_vector_int &ninput_items_required) + { + ninput_items_required[0] = noutput_items / 5 * 4; + } + + void + catv_trellis_enc_bb_impl::diff_precoder(unsigned char W, unsigned char Z, unsigned char *Xp, unsigned char *Yp) + { + unsigned char common, newX, newY; + + common = (Z & (*Xp ^ *Yp)); + newX = W ^ *Xp ^ common; + newY = Z ^ W ^ *Yp ^ common; + + *Xp = newX; + *Yp = newY; + } + + void + catv_trellis_enc_bb_impl::init_trellis() + { + unsigned char XYp, W, Z, X, Y, Xp, Yp, state, xy, Xq; + int i, n; + + for (XYp = 0; XYp < 4; XYp++) { + for (W = 0; W < 16; W++) { + for (Z = 0; Z < 16; Z++) { + X = 0; + Y = 0; + Xp = (XYp & 0b10) >> 1; + Yp = (XYp & 0b01); + for (i = 0; i < 4; i++) { + diff_precoder((W >> i) & 1, (Z >> i) & 1, &Xp, &Yp); + X |= (Xp << i); + Y |= (Yp << i); + } + diff_precoder_table[XYp][W][Z][0] = (Xp << 1) + Yp; + diff_precoder_table[XYp][W][Z][1] = X; + diff_precoder_table[XYp][W][Z][2] = Y; + } + } + } + + for (i = 0; i < 32; i++) { + G1table[i] = (i >> 4) ^ ((i & 0x04) >> 2) ^ (i & 1); + G2table[i] = (i >> 4) ^ ((i & 0x08) >> 3) ^ ((i & 0x04) >> 2) ^ ((i & 0x02) >> 1) ^ (i & 1); + } + + memset(trellis_table_x, 0, 16*16*6); + memset(trellis_table_y, 0, 16*16*6); + for (state = 0; state < 16; state++) { + for (xy = 0; xy < 16; xy++) { + i = 0; + Xq = state; + for (n = 0; n < 4; n++) { + Xq = (Xq << 1) + ((xy >> n) & 1); + + if (n == 3) { + trellis_table_x[state][xy][i+1] |= G1table[Xq] << 3; + trellis_table_y[state][xy][i+1] |= G1table[Xq]; + i += 1; + } + trellis_table_x[state][xy][i+1] |= G2table[Xq] << 3; + trellis_table_y[state][xy][i+1] |= G2table[Xq]; + i += 1; + + Xq &= 0x0F; + } + + trellis_table_x[state][xy][0] = Xq; + trellis_table_y[state][xy][0] = Xq; + } + } + } + + void + catv_trellis_enc_bb_impl::trellis_code(const unsigned char *rs, unsigned char *qs) + { + unsigned char X, Y; + int A, B, n; + + A = (rs[1] << 7) | rs[0]; + B = (rs[3] << 7) | rs[2]; + + memset(qs, 0, 5); + + for (n = 0; n < 5; n++) { + qs[n] |= ((A >> (2*n)) & 3) << 4; + qs[n] |= ((B >> (2*n)) & 3) << 1; + } + + X = diff_precoder_table[XYp][A >> 10][B >> 10][1]; + Y = diff_precoder_table[XYp][A >> 10][B >> 10][2]; + XYp = diff_precoder_table[XYp][A >> 10][B >> 10][0]; + + for (n = 0; n < 5; n++) { + qs[n] |= trellis_table_x[Xq][X][1+n]; + qs[n] |= trellis_table_y[Yq][Y][1+n]; + } + Xq = trellis_table_x[Xq][X][0]; + Yq = trellis_table_y[Yq][Y][0]; + } + + int + catv_trellis_enc_bb_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 unsigned char *in = (const unsigned char *) input_items[0]; + unsigned char *out = (unsigned char *) output_items[0]; + + int i = 0, j = 0; + + while (i < noutput_items) { + trellis_code(in + j, out + i); + i += 5; + j += 4; + } + + // Tell runtime system how many input items we consumed on + // each input stream. + consume_each (j); + + // Tell runtime system how many output items we produced. + return noutput_items; + } + + } /* namespace dtv */ +} /* namespace gr */ + diff --git a/gr-dtv/lib/catv/catv_trellis_enc_bb_impl.h b/gr-dtv/lib/catv/catv_trellis_enc_bb_impl.h new file mode 100644 index 0000000000..aa7874d929 --- /dev/null +++ b/gr-dtv/lib/catv/catv_trellis_enc_bb_impl.h @@ -0,0 +1,60 @@ +/* -*- c++ -*- */ +/* + * Copyright 2016 Free Software Foundation, Inc. + * + * This 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. + * + * This software 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 software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_DTV_CATV_TRELLIS_ENC_BB_IMPL_H +#define INCLUDED_DTV_CATV_TRELLIS_ENC_BB_IMPL_H + +#include <gnuradio/dtv/catv_trellis_enc_bb.h> + +namespace gr { + namespace dtv { + + class catv_trellis_enc_bb_impl : public catv_trellis_enc_bb + { + private: + unsigned char diff_precoder_table[4][16][16][3]; + unsigned char G1table[32]; + unsigned char G2table[32]; + unsigned char trellis_table_x[16][16][6]; + unsigned char trellis_table_y[16][16][6]; + unsigned char Xq, Yq, XYp; + + void diff_precoder(unsigned char W, unsigned char Z, unsigned char *Xp, unsigned char *Yp); + void init_trellis(); + void trellis_code(const unsigned char *rs, unsigned char *qs); + + public: + catv_trellis_enc_bb_impl(); + ~catv_trellis_enc_bb_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); + }; + + } // namespace dtv +} // namespace gr + +#endif /* INCLUDED_DTV_CATV_TRELLIS_ENC_BB_IMPL_H */ + diff --git a/gr-dtv/lib/dvbs2/dvbs2_modulator_bc_impl.cc b/gr-dtv/lib/dvbs2/dvbs2_modulator_bc_impl.cc index c8579d1c1e..d1f3b4aa27 100644 --- a/gr-dtv/lib/dvbs2/dvbs2_modulator_bc_impl.cc +++ b/gr-dtv/lib/dvbs2/dvbs2_modulator_bc_impl.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2015 Free Software Foundation, Inc. + * Copyright 2015,2016 Free Software Foundation, Inc. * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,16 +29,16 @@ namespace gr { namespace dtv { dvbs2_modulator_bc::sptr - dvbs2_modulator_bc::make(dvb_framesize_t framesize, dvb_code_rate_t rate, dvb_constellation_t constellation) + dvbs2_modulator_bc::make(dvb_framesize_t framesize, dvb_code_rate_t rate, dvb_constellation_t constellation, dvbs2_interpolation_t interpolation) { return gnuradio::get_initial_sptr - (new dvbs2_modulator_bc_impl(framesize, rate, constellation)); + (new dvbs2_modulator_bc_impl(framesize, rate, constellation, interpolation)); } /* * The private constructor */ - dvbs2_modulator_bc_impl::dvbs2_modulator_bc_impl(dvb_framesize_t framesize, dvb_code_rate_t rate, dvb_constellation_t constellation) + dvbs2_modulator_bc_impl::dvbs2_modulator_bc_impl(dvb_framesize_t framesize, dvb_code_rate_t rate, dvb_constellation_t constellation, dvbs2_interpolation_t interpolation) : gr::block("dvbs2_modulator_bc", gr::io_signature::make(1, 1, sizeof(unsigned char)), gr::io_signature::make(1, 1, sizeof(gr_complex))) @@ -1660,6 +1660,72 @@ namespace gr { m_256apsk[255] = gr_complex((r6 * cos(43 * M_PI / 32.0)), (r6 * sin(43 * M_PI / 32.0))); } break; + case MOD_64QAM: + m_64apsk[0] = gr_complex( 1.0, 1.0); + m_64apsk[1] = gr_complex( 1.0, -1.0); + m_64apsk[2] = gr_complex( 1.0, -3.0); + m_64apsk[3] = gr_complex( -3.0, -1.0); + m_64apsk[4] = gr_complex( -3.0, 1.0); + m_64apsk[5] = gr_complex( 1.0, 3.0); + m_64apsk[6] = gr_complex( -3.0, -3.0); + m_64apsk[7] = gr_complex( -3.0, 3.0); + m_64apsk[8] = gr_complex( -1.0, 1.0); + m_64apsk[9] = gr_complex( -1.0, -1.0); + m_64apsk[10] = gr_complex( 3.0, 1.0); + m_64apsk[11] = gr_complex(-1.0, 3.0); + m_64apsk[12] = gr_complex(-1.0, -3.0); + m_64apsk[13] = gr_complex( 3.0, -1.0); + m_64apsk[14] = gr_complex( 3.0, -3.0); + m_64apsk[15] = gr_complex( 3.0, 3.0); + m_64apsk[16] = gr_complex( 5.0, 1.0); + m_64apsk[17] = gr_complex( 1.0, -5.0); + m_64apsk[18] = gr_complex( 1.0, -7.0); + m_64apsk[19] = gr_complex(-7.0, -1.0); + m_64apsk[20] = gr_complex(-3.0, 5.0); + m_64apsk[21] = gr_complex( 5.0, 3.0); + m_64apsk[22] = gr_complex(-7.0, -3.0); + m_64apsk[23] = gr_complex(-3.0, 7.0); + m_64apsk[24] = gr_complex(-1.0, 5.0); + m_64apsk[25] = gr_complex(-5.0, -1.0); + m_64apsk[26] = gr_complex( 7.0, 1.0); + m_64apsk[27] = gr_complex(-1.0, 7.0); + m_64apsk[28] = gr_complex(-5.0, -3.0); + m_64apsk[29] = gr_complex( 3.0, -5.0); + m_64apsk[30] = gr_complex( 3.0, -7.0); + m_64apsk[31] = gr_complex( 7.0, 3.0); + m_64apsk[32] = gr_complex( 1.0, 5.0); + m_64apsk[33] = gr_complex( 5.0, -1.0); + m_64apsk[34] = gr_complex( 5.0, -3.0); + m_64apsk[35] = gr_complex(-3.0, -5.0); + m_64apsk[36] = gr_complex(-7.0, 1.0); + m_64apsk[37] = gr_complex( 1.0, 7.0); + m_64apsk[38] = gr_complex(-3.0, -7.0); + m_64apsk[39] = gr_complex(-7.0, 3.0); + m_64apsk[40] = gr_complex(-5.0, 1.0); + m_64apsk[41] = gr_complex(-1.0, -5.0); + m_64apsk[42] = gr_complex( 3.0, 5.0); + m_64apsk[43] = gr_complex(-5.0, 3.0); + m_64apsk[44] = gr_complex(-1.0, -7.0); + m_64apsk[45] = gr_complex( 7.0, -1.0); + m_64apsk[46] = gr_complex( 7.0, -3.0); + m_64apsk[47] = gr_complex( 3.0, 7.0); + m_64apsk[48] = gr_complex( 5.0, 5.0); + m_64apsk[49] = gr_complex( 5.0, -5.0); + m_64apsk[50] = gr_complex( 5.0, -7.0); + m_64apsk[51] = gr_complex(-7.0, -5.0); + m_64apsk[52] = gr_complex(-7.0, 5.0); + m_64apsk[53] = gr_complex( 5.0, 7.0); + m_64apsk[54] = gr_complex(-7.0, -7.0); + m_64apsk[55] = gr_complex(-7.0, 7.0); + m_64apsk[56] = gr_complex(-5.0, 5.0); + m_64apsk[57] = gr_complex(-5.0, -5.0); + m_64apsk[58] = gr_complex( 7.0, 5.0); + m_64apsk[59] = gr_complex(-5.0, 7.0); + m_64apsk[60] = gr_complex(-5.0, -7.0); + m_64apsk[61] = gr_complex( 7.0, -5.0); + m_64apsk[62] = gr_complex( 7.0, -7.0); + m_64apsk[63] = gr_complex( 7.0, 7.0); + break; default: m_qpsk[0] = gr_complex((r1 * cos(M_PI / 4.0)), (r1 * sin(M_PI / 4.0))); m_qpsk[1] = gr_complex((r1 * cos(7 * M_PI / 4.0)), (r1 * sin(7 * M_PI / 4.0))); @@ -1668,6 +1734,7 @@ namespace gr { break; } signal_constellation = constellation; + signal_interpolation = interpolation; set_output_multiple(2); } @@ -1681,7 +1748,12 @@ namespace gr { void dvbs2_modulator_bc_impl::forecast (int noutput_items, gr_vector_int &ninput_items_required) { - ninput_items_required[0] = noutput_items; + if (signal_interpolation == INTERPOLATION_OFF) { + ninput_items_required[0] = noutput_items; + } + else { + ninput_items_required[0] = noutput_items / 2; + } } int @@ -1693,67 +1765,145 @@ namespace gr { const unsigned char *in = (const unsigned char *) input_items[0]; gr_complex *out = (gr_complex *) output_items[0]; int index; + gr_complex zero; - switch (signal_constellation) { - case MOD_QPSK: - for (int i = 0; i < noutput_items; i++) { - index = *in++; - *out++ = m_qpsk[index & 0x3]; - } - break; - case MOD_8PSK: - case MOD_8APSK: - for (int i = 0; i < noutput_items; i++) { - index = *in++; - *out++ = m_8psk[index & 0x7]; - } - break; - case MOD_16APSK: - case MOD_8_8APSK: - for (int i = 0; i < noutput_items; i++) { - index = *in++; - *out++ = m_16apsk[index & 0xf]; - } - break; - case MOD_32APSK: - case MOD_4_12_16APSK: - case MOD_4_8_4_16APSK: - for (int i = 0; i < noutput_items; i++) { - index = *in++; - *out++ = m_32apsk[index & 0x1f]; - } - break; - case MOD_64APSK: - case MOD_8_16_20_20APSK: - case MOD_4_12_20_28APSK: - for (int i = 0; i < noutput_items; i++) { - index = *in++; - *out++ = m_64apsk[index & 0x3f]; - } - break; - case MOD_128APSK: - for (int i = 0; i < noutput_items; i++) { - index = *in++; - *out++ = m_128apsk[index & 0x7f]; - } - break; - case MOD_256APSK: - for (int i = 0; i < noutput_items; i++) { - index = *in++; - *out++ = m_256apsk[index & 0xff]; - } - break; - default: - for (int i = 0; i < noutput_items; i++) { - index = *in++; - *out++ = m_qpsk[index & 0x3]; - } - break; + zero = gr_complex(0.0, 0.0); + + if (signal_interpolation == INTERPOLATION_OFF) { + switch (signal_constellation) { + case MOD_QPSK: + for (int i = 0; i < noutput_items; i++) { + index = *in++; + *out++ = m_qpsk[index & 0x3]; + } + break; + case MOD_8PSK: + case MOD_8APSK: + for (int i = 0; i < noutput_items; i++) { + index = *in++; + *out++ = m_8psk[index & 0x7]; + } + break; + case MOD_16APSK: + case MOD_8_8APSK: + for (int i = 0; i < noutput_items; i++) { + index = *in++; + *out++ = m_16apsk[index & 0xf]; + } + break; + case MOD_32APSK: + case MOD_4_12_16APSK: + case MOD_4_8_4_16APSK: + for (int i = 0; i < noutput_items; i++) { + index = *in++; + *out++ = m_32apsk[index & 0x1f]; + } + break; + case MOD_64APSK: + case MOD_64QAM: + case MOD_8_16_20_20APSK: + case MOD_4_12_20_28APSK: + for (int i = 0; i < noutput_items; i++) { + index = *in++; + *out++ = m_64apsk[index & 0x3f]; + } + break; + case MOD_128APSK: + for (int i = 0; i < noutput_items; i++) { + index = *in++; + *out++ = m_128apsk[index & 0x7f]; + } + break; + case MOD_256APSK: + for (int i = 0; i < noutput_items; i++) { + index = *in++; + *out++ = m_256apsk[index & 0xff]; + } + break; + default: + for (int i = 0; i < noutput_items; i++) { + index = *in++; + *out++ = m_qpsk[index & 0x3]; + } + break; + } + } + else { + switch (signal_constellation) { + case MOD_QPSK: + for (int i = 0; i < noutput_items / 2; i++) { + index = *in++; + *out++ = m_qpsk[index & 0x3]; + *out++ = zero; + } + break; + case MOD_8PSK: + case MOD_8APSK: + for (int i = 0; i < noutput_items / 2; i++) { + index = *in++; + *out++ = m_8psk[index & 0x7]; + *out++ = zero; + } + break; + case MOD_16APSK: + case MOD_8_8APSK: + for (int i = 0; i < noutput_items / 2; i++) { + index = *in++; + *out++ = m_16apsk[index & 0xf]; + *out++ = zero; + } + break; + case MOD_32APSK: + case MOD_4_12_16APSK: + case MOD_4_8_4_16APSK: + for (int i = 0; i < noutput_items / 2; i++) { + index = *in++; + *out++ = m_32apsk[index & 0x1f]; + *out++ = zero; + } + break; + case MOD_64APSK: + case MOD_64QAM: + case MOD_8_16_20_20APSK: + case MOD_4_12_20_28APSK: + for (int i = 0; i < noutput_items / 2; i++) { + index = *in++; + *out++ = m_64apsk[index & 0x3f]; + *out++ = zero; + } + break; + case MOD_128APSK: + for (int i = 0; i < noutput_items / 2; i++) { + index = *in++; + *out++ = m_128apsk[index & 0x7f]; + *out++ = zero; + } + break; + case MOD_256APSK: + for (int i = 0; i < noutput_items / 2; i++) { + index = *in++; + *out++ = m_256apsk[index & 0xff]; + *out++ = zero; + } + break; + default: + for (int i = 0; i < noutput_items / 2; i++) { + index = *in++; + *out++ = m_qpsk[index & 0x3]; + *out++ = zero; + } + break; + } } // Tell runtime system how many input items we consumed on // each input stream. - consume_each (noutput_items); + if (signal_interpolation == INTERPOLATION_OFF) { + consume_each (noutput_items); + } + else { + consume_each (noutput_items / 2); + } // Tell runtime system how many output items we produced. return noutput_items; diff --git a/gr-dtv/lib/dvbs2/dvbs2_modulator_bc_impl.h b/gr-dtv/lib/dvbs2/dvbs2_modulator_bc_impl.h index 8507878b0e..4fdbb149df 100644 --- a/gr-dtv/lib/dvbs2/dvbs2_modulator_bc_impl.h +++ b/gr-dtv/lib/dvbs2/dvbs2_modulator_bc_impl.h @@ -31,6 +31,7 @@ namespace gr { { private: int signal_constellation; + int signal_interpolation; gr_complex m_qpsk[4]; gr_complex m_8psk[8]; gr_complex m_16apsk[16]; @@ -40,7 +41,7 @@ namespace gr { gr_complex m_256apsk[256]; public: - dvbs2_modulator_bc_impl(dvb_framesize_t framesize, dvb_code_rate_t rate, dvb_constellation_t constellation); + dvbs2_modulator_bc_impl(dvb_framesize_t framesize, dvb_code_rate_t rate, dvb_constellation_t constellation, dvbs2_interpolation_t interpolation); ~dvbs2_modulator_bc_impl(); void forecast (int noutput_items, gr_vector_int &ninput_items_required); diff --git a/gr-dtv/lib/dvbt/dvbt_ofdm_sym_acquisition_impl.cc b/gr-dtv/lib/dvbt/dvbt_ofdm_sym_acquisition_impl.cc index 6617c2c11f..ee8dce0f15 100644 --- a/gr-dtv/lib/dvbt/dvbt_ofdm_sym_acquisition_impl.cc +++ b/gr-dtv/lib/dvbt/dvbt_ofdm_sym_acquisition_impl.cc @@ -34,12 +34,12 @@ namespace gr { namespace dtv { int - dvbt_ofdm_sym_acquisition_impl::peak_detect_init(float threshold_factor_rise, float threshold_factor_fall, int look_ahead, float alpha) + dvbt_ofdm_sym_acquisition_impl::peak_detect_init(float threshold_factor_rise, float alpha) { d_avg_alpha = alpha; d_threshold_factor_rise = threshold_factor_rise; - d_threshold_factor_fall = threshold_factor_fall; - d_avg = 0; + d_avg_max = - (float)INFINITY; + d_avg_min = (float)INFINITY; return (0); } @@ -47,56 +47,50 @@ namespace gr { int dvbt_ofdm_sym_acquisition_impl::peak_detect_process(const float * datain, const int datain_length, int * peak_pos, int * peak_max) { - int state = 0; - float peak_val = -(float)INFINITY; int peak_index = 0; int peak_pos_length = 0; + uint16_t peak_index = 0; + int peak_pos_length = 0; - int i = 0; + volk_32f_index_max_16u(&peak_index, &datain[0], datain_length); - while(i < datain_length) { - if (state == 0) { - if (datain[i] > d_avg * d_threshold_factor_rise) { - state = 1; - } - else { - d_avg = d_avg_alpha * datain[i] + (1 - d_avg_alpha) * d_avg; - i++; - } + peak_pos_length = 1; + if (datain_length >= d_fft_length) { + float min = datain[(peak_index + d_fft_length / 2) % d_fft_length]; + if (d_avg_min == (float)INFINITY) { + d_avg_min = min; } - else if (state == 1) { - if (datain[i] > peak_val) { - peak_val = datain[i]; - peak_index = i; - d_avg = d_avg_alpha * datain[i] + (1 - d_avg_alpha) * d_avg; - i++; - } - else if (datain[i] > d_avg * d_threshold_factor_fall) { - d_avg = (d_avg_alpha) * datain[i] + (1 - d_avg_alpha) * d_avg; - i++; - } - else { - peak_pos[peak_pos_length] = peak_index; - peak_pos_length++; - state = 0; - peak_val = - (float)INFINITY; - } + else { + d_avg_min = d_avg_alpha * min + (1 - d_avg_alpha) * d_avg_min; } } - // Find peak of peaks - if (peak_pos_length) { - float max = datain[peak_pos[0]]; - int maxi = 0; + if (d_avg_max == -(float)INFINITY) { + // Initialize d_avg_max with the first value. + d_avg_max = datain[peak_index]; + } + else if (datain[peak_index] > d_avg_max - d_threshold_factor_rise * (d_avg_max-d_avg_min)) { + d_avg_max = d_avg_alpha * datain[peak_index] + (1 - d_avg_alpha) * d_avg_max; + } + else { + peak_pos_length = 0; + } - for (int i = 1; i < peak_pos_length; i++) { - if (datain[peak_pos[i]] > max) { - max = datain[peak_pos[i]]; - maxi = i; - } + // We now check whether the peak is in the border of the search interval. This would mean that + // the search interval is not correct, and it should be re-set. This happens for instance when the + // hardware dropped some samples. + // Our definition of "border of the search interval" depends if the search interval is "big" or not. + if (datain_length < d_fft_length) { + if ((peak_index == 0) || (peak_index == (unsigned int)datain_length - 1)) { + peak_pos_length = 0; + } + } + else { + if ((peak_index < 5) || (peak_index > (unsigned int)datain_length - 5)) { + peak_pos_length = 0; } - - *peak_max = maxi; } + peak_pos[0] = peak_index; + *peak_max = 0; return (peak_pos_length); } @@ -119,7 +113,8 @@ namespace gr { volk_32fc_magnitude_squared_32f(&d_norm[low], &in[low], size); // Calculate gamma on each point - low = lookup_stop - d_cp_length + 1; + // TODO check these boundaries!!!!!!! + low = lookup_stop - (d_cp_length - 1); size = lookup_start - low + 1; volk_32fc_x2_multiply_conjugate_32fc(&d_corr[low - d_fft_length], &in[low], &in[low - d_fft_length], size); @@ -220,6 +215,12 @@ namespace gr { this->add_item_tag(0, offset, key, value); } + // Derotates the signal + void dvbt_ofdm_sym_acquisition_impl::derotate(const gr_complex * in, gr_complex * out) + { + volk_32fc_x2_multiply_32fc(&out[0], &d_derot[0], &in[0], d_fft_length); + } + dvbt_ofdm_sym_acquisition::sptr dvbt_ofdm_sym_acquisition::make(int blocks, int fft_length, int occupied_tones, int cp_length, float snr) { @@ -234,11 +235,10 @@ namespace gr { : block("dvbt_ofdm_sym_acquisition", io_signature::make(1, 1, sizeof (gr_complex) * blocks), io_signature::make(1, 1, sizeof (gr_complex) * blocks * fft_length)), - d_blocks(blocks), d_fft_length(fft_length), d_cp_length(cp_length), d_snr(snr), - d_index(0), d_phase(0.0), d_phaseinc(0.0), d_cp_found(0), d_count(0), d_nextphaseinc(0), d_nextpos(0), \ - d_sym_acq_count(0),d_sym_acq_timeout(100), d_initial_acquisition(0), \ - d_freq_correction_count(0), d_freq_correction_timeout(0), d_cp_start(0), \ - d_to_consume(0), d_to_out(0) + d_blocks(blocks), d_fft_length(fft_length), d_cp_length(cp_length), d_snr(snr), \ + d_phase(0.0), d_phaseinc(0.0), d_cp_found(0), d_nextphaseinc(0), d_nextpos(0), \ + d_initial_acquisition(0), d_cp_start(0), \ + d_to_consume(0), d_to_out(0), d_consumed(0), d_out(0) { set_relative_rate(1.0 / (double) (d_cp_length + d_fft_length)); @@ -296,7 +296,7 @@ namespace gr { exit(1); } - peak_detect_init(0.8, 0.9, 30, 0.9); + peak_detect_init(0.3, 0.9); } /* @@ -319,7 +319,7 @@ namespace gr { // make sure we receive at least (symbol_length + fft_length) for (int i = 0; i < ninputs; i++) { - ninput_items_required[i] = (2 * d_fft_length + d_cp_length) * noutput_items; + ninput_items_required[i] = (d_cp_length + d_fft_length) * (noutput_items + 1); } } @@ -336,45 +336,52 @@ namespace gr { { const gr_complex *in = (const gr_complex *) input_items[0]; gr_complex *out = (gr_complex *) output_items[0]; - - int low, size; - - // This is initial acquisition of symbol start - // TODO - make a FSM - if (!d_initial_acquisition) { - d_initial_acquisition = ml_sync(in, 2 * d_fft_length + d_cp_length - 1, d_fft_length + d_cp_length - 1, \ - &d_cp_start, &d_derot[0], &d_to_consume, &d_to_out); - - // Send sync_start downstream - send_sync_start(); - } - - // This is fractional frequency correction (pre FFT) - // It is also called coarse frequency correction - if (d_initial_acquisition) { - d_cp_found = ml_sync(in, d_cp_start + 16, d_cp_start, \ - &d_cp_start, &d_derot[0], &d_to_consume, &d_to_out); + int low; + + d_consumed = 0; + d_out = 0; + + for (int i = 0; i < noutput_items; i++) { + // This is initial acquisition of symbol start + // TODO - make a FSM + if (!d_initial_acquisition) { + d_initial_acquisition = ml_sync(&in[d_consumed], 2 * d_fft_length + d_cp_length - 1, d_fft_length + d_cp_length - 1, \ + &d_cp_start, &d_derot[0], &d_to_consume, &d_to_out); + d_cp_found = d_initial_acquisition; + } + else { + // If we are here it means that in the previous iteration we found the CP. We + // now thus only search near it. + d_cp_found = ml_sync(&in[d_consumed], d_cp_start + 8, std::max(d_cp_start - 8, d_cp_length+d_fft_length - 1), \ + &d_cp_start, &d_derot[0], &d_to_consume, &d_to_out); + if (!d_cp_found) { + // We may have not found the CP because the smaller search range was too small (rare, but possible). + // We re-try with the whole search range. + d_cp_found = ml_sync(&in[d_consumed], 2 * d_fft_length + d_cp_length - 1, d_fft_length + d_cp_length - 1, \ + &d_cp_start, &d_derot[0], &d_to_consume, &d_to_out ); + } + } if (d_cp_found) { - d_freq_correction_count = 0; - - // Derotate the signal and out - low = d_cp_start - d_fft_length + 1; - size = d_cp_start - (d_cp_start - d_fft_length + 1) + 1; - - volk_32fc_x2_multiply_32fc(&out[0], &d_derot[0], &in[low], size); + low = d_consumed + d_cp_start - d_fft_length + 1; + derotate(&in[low], &out[i * d_fft_length]); } else { - // If we have a number of consecutive misses then we restart acquisition - if (++d_freq_correction_count > d_freq_correction_timeout) { - d_initial_acquisition = 0; - d_freq_correction_count = 0; - - // Restart with a half number so that we'll not endup with the same situation - // This will prevent peak_detect to not detect anything - d_to_consume = d_to_consume / 2; - } + // Send sync_start downstream + send_sync_start(); + d_initial_acquisition = 0; + + // Restart wit a half number so that we'll not end up with the same situation + // This will prevent peak_detect to not detect anything + d_to_consume = d_to_consume / 2; + d_consumed += d_to_consume; + consume_each(d_consumed); + + // Tell runtime system how many output items we produced. + return (d_out); } + d_consumed += d_to_consume; + d_out += d_to_out; } // Tell runtime system how many input items we consumed on @@ -386,3 +393,4 @@ namespace gr { } } /* namespace dtv */ } /* namespace gr */ + diff --git a/gr-dtv/lib/dvbt/dvbt_ofdm_sym_acquisition_impl.h b/gr-dtv/lib/dvbt/dvbt_ofdm_sym_acquisition_impl.h index e7b92cbd41..0964361b1e 100644 --- a/gr-dtv/lib/dvbt/dvbt_ofdm_sym_acquisition_impl.h +++ b/gr-dtv/lib/dvbt/dvbt_ofdm_sym_acquisition_impl.h @@ -35,8 +35,6 @@ namespace gr { float d_snr; float d_rho; - int d_index; - gr_complex * d_conj; float * d_norm; gr_complex * d_corr; @@ -45,37 +43,35 @@ namespace gr { // For peak detector float d_threshold_factor_rise; - float d_threshold_factor_fall; float d_avg_alpha; - float d_avg; + float d_avg_min; + float d_avg_max; float d_phase; double d_phaseinc; int d_cp_found; - int d_count; double d_nextphaseinc; int d_nextpos; - int d_sym_acq_count; - int d_sym_acq_timeout; - int d_initial_acquisition; - int d_freq_correction_count; - int d_freq_correction_timeout; - int d_cp_start; gr_complex * d_derot; int d_to_consume; int d_to_out; + int d_consumed; + int d_out; int ml_sync(const gr_complex * in, int lookup_start, int lookup_stop, int * cp_pos, gr_complex * derot, int * to_consume, int * to_out); - int peak_detect_init(float threshold_factor_rise, float threshold_factor_fall, int look_ahead, float alpha); + int peak_detect_init(float threshold_factor_rise, float alpha); int peak_detect_process(const float * datain, const int datain_length, int * peak_pos, int * peak_max); void send_sync_start(); + + void derotate(const gr_complex * in, gr_complex *out); + public: dvbt_ofdm_sym_acquisition_impl(int blocks, int fft_length, int occupied_tones, int cp_length, float snr); ~dvbt_ofdm_sym_acquisition_impl(); @@ -92,3 +88,4 @@ namespace gr { } // namespace gr #endif /* INCLUDED_DTV_DVBT_OFDM_SYM_ACQUISITION_IMPL_H */ + diff --git a/gr-dtv/swig/dtv_swig.i b/gr-dtv/swig/dtv_swig.i index c2fa312e05..24960bd2be 100644 --- a/gr-dtv/swig/dtv_swig.i +++ b/gr-dtv/swig/dtv_swig.i @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2014,2015 Free Software Foundation, Inc. + * Copyright 2014,2015,2016 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -79,6 +79,11 @@ #include "gnuradio/dtv/dvbt_convolutional_deinterleaver.h" #include "gnuradio/dtv/dvbt_reed_solomon_dec.h" #include "gnuradio/dtv/dvbt_energy_descramble.h" +#include "gnuradio/dtv/catv_transport_framing_enc_bb.h" +#include "gnuradio/dtv/catv_reed_solomon_enc_bb.h" +#include "gnuradio/dtv/catv_randomizer_bb.h" +#include "gnuradio/dtv/catv_frame_sync_enc_bb.h" +#include "gnuradio/dtv/catv_trellis_enc_bb.h" %} %include "gnuradio/dtv/atsc_deinterleaver.h" @@ -132,6 +137,11 @@ %include "gnuradio/dtv/dvbt_convolutional_deinterleaver.h" %include "gnuradio/dtv/dvbt_reed_solomon_dec.h" %include "gnuradio/dtv/dvbt_energy_descramble.h" +%include "gnuradio/dtv/catv_transport_framing_enc_bb.h" +%include "gnuradio/dtv/catv_reed_solomon_enc_bb.h" +%include "gnuradio/dtv/catv_randomizer_bb.h" +%include "gnuradio/dtv/catv_frame_sync_enc_bb.h" +%include "gnuradio/dtv/catv_trellis_enc_bb.h" GR_SWIG_BLOCK_MAGIC2(dtv, atsc_deinterleaver); GR_SWIG_BLOCK_MAGIC2(dtv, atsc_depad); @@ -180,3 +190,8 @@ GR_SWIG_BLOCK_MAGIC2(dtv, dvbt_viterbi_decoder); GR_SWIG_BLOCK_MAGIC2(dtv, dvbt_convolutional_deinterleaver); GR_SWIG_BLOCK_MAGIC2(dtv, dvbt_reed_solomon_dec); GR_SWIG_BLOCK_MAGIC2(dtv, dvbt_energy_descramble); +GR_SWIG_BLOCK_MAGIC2(dtv, catv_transport_framing_enc_bb); +GR_SWIG_BLOCK_MAGIC2(dtv, catv_reed_solomon_enc_bb); +GR_SWIG_BLOCK_MAGIC2(dtv, catv_randomizer_bb); +GR_SWIG_BLOCK_MAGIC2(dtv, catv_frame_sync_enc_bb); +GR_SWIG_BLOCK_MAGIC2(dtv, catv_trellis_enc_bb); diff --git a/gr-fcd/grc/fcd_source_c.xml b/gr-fcd/grc/fcd_source_c.xml index 01ea26d81c..b8975e4070 100644 --- a/gr-fcd/grc/fcd_source_c.xml +++ b/gr-fcd/grc/fcd_source_c.xml @@ -2,7 +2,7 @@ <block> <name>Funcube Dongle Source</name> <key>fcd_source_c</key> - <category>FCD</category> + <category>[Core]/FCD</category> <flags>throttle</flags> <import>from gnuradio import fcd</import> <make>fcd.source_c($device_name) diff --git a/gr-fec/grc/fec_block_tree.xml b/gr-fec/grc/fec_block_tree.xml index 5efc144cbd..b1535e3c42 100644 --- a/gr-fec/grc/fec_block_tree.xml +++ b/gr-fec/grc/fec_block_tree.xml @@ -5,7 +5,7 @@ ################################################### --> <cat> - <name></name> <!-- Blank for Root Name --> + <name>[Core]</name> <cat> <name>Error Coding</name> <cat> diff --git a/gr-fft/grc/fft_block_tree.xml b/gr-fft/grc/fft_block_tree.xml index 7028ed1049..e844d837b4 100644 --- a/gr-fft/grc/fft_block_tree.xml +++ b/gr-fft/grc/fft_block_tree.xml @@ -27,7 +27,7 @@ ################################################### --> <cat> - <name></name> <!-- Blank for Root Name --> + <name>[Core]</name> <!-- Blank for Root Name --> <cat> <name>Fourier Analysis</name> <block>fft_vxx</block> diff --git a/gr-filter/examples/synth_to_chan.py b/gr-filter/examples/synth_to_chan.py index 9e682021b7..88fb080a65 100755 --- a/gr-filter/examples/synth_to_chan.py +++ b/gr-filter/examples/synth_to_chan.py @@ -54,7 +54,7 @@ def main(): fmtx = list() for fi in freqs: s = analog.sig_source_f(fs, analog.GR_SIN_WAVE, fi, 1) - fm = analog.nbfm_tx(fs, 4*fs, max_dev=10000, tau=75e-6) + fm = analog.nbfm_tx(fs, 4*fs, max_dev=10000, tau=75e-6, fh=0.925*(4*fs)/2.0) sigs.append(s) fmtx.append(fm) diff --git a/gr-filter/grc/filter_block_tree.xml b/gr-filter/grc/filter_block_tree.xml index ea869a75bd..3613df8202 100644 --- a/gr-filter/grc/filter_block_tree.xml +++ b/gr-filter/grc/filter_block_tree.xml @@ -27,7 +27,7 @@ ################################################### --> <cat> - <name></name> <!-- Blank for Root Name --> + <name>[Core]</name> <cat> <name>Filters</name> <!-- FIR filter tap generators --> diff --git a/gr-filter/lib/pfb_decimator_ccf_impl.cc b/gr-filter/lib/pfb_decimator_ccf_impl.cc index e282b484fd..9d1d6f6139 100644 --- a/gr-filter/lib/pfb_decimator_ccf_impl.cc +++ b/gr-filter/lib/pfb_decimator_ccf_impl.cc @@ -73,6 +73,26 @@ namespace gr { else { set_history(d_taps_per_filter); } + + d_tmp = NULL; + } + + bool pfb_decimator_ccf_impl::start() + { + if(d_use_fft_filters) { + d_tmp = fft::malloc_complex(max_noutput_items()*d_rate); + } + + return block::start(); + } + + bool pfb_decimator_ccf_impl::stop() + { + if((d_use_fft_filters) && (d_tmp)) { + fft::free(d_tmp); + } + + return block::stop(); } pfb_decimator_ccf_impl::~pfb_decimator_ccf_impl() @@ -198,14 +218,13 @@ namespace gr { gr_complex *out = (gr_complex *)output_items[0]; int i; - gr_complex *tmp = fft::malloc_complex(noutput_items*d_rate); // Filter each input stream by the FFT filters; do all // noutput_items at once to avoid repeated calls to the FFT // setup and operation. for(unsigned int j = 0; j < d_rate; j++) { in = (gr_complex*)input_items[d_rate-j-1]; - d_fft_filters[j]->filter(noutput_items, in, &(tmp[j*noutput_items])); + d_fft_filters[j]->filter(noutput_items, in, &(d_tmp[j*noutput_items])); } // Rotate and add filter outputs (k=channel number; M=number of @@ -214,11 +233,10 @@ namespace gr { for(i = 0; i < noutput_items; i++) { out[i] = 0; for(unsigned int j = 0; j < d_rate; j++) { - out[i] += tmp[j*noutput_items+i]*d_rotator[j]; + out[i] += d_tmp[j*noutput_items+i]*d_rotator[j]; } } - fft::free(tmp); return noutput_items; } @@ -231,18 +249,17 @@ namespace gr { gr_complex *out = (gr_complex *)output_items[0]; int i; - gr_complex *tmp = fft::malloc_complex(noutput_items*d_rate); for(unsigned int j = 0; j < d_rate; j++) { in = (gr_complex*)input_items[d_rate-j-1]; - d_fft_filters[j]->filter(noutput_items, in, &tmp[j*noutput_items]); + d_fft_filters[j]->filter(noutput_items, in, &d_tmp[j*noutput_items]); } // Performs the rotate and add operations by implementing it as // an FFT. for(i = 0; i < noutput_items; i++) { for(unsigned int j = 0; j < d_rate; j++) { - d_fft->get_inbuf()[j] = tmp[j*noutput_items + i]; + d_fft->get_inbuf()[j] = d_tmp[j*noutput_items + i]; } // Perform the FFT to do the complex multiply despinning for all channels @@ -252,7 +269,6 @@ namespace gr { out[i] = d_fft->get_outbuf()[d_chan]; } - fft::free(tmp); return noutput_items; } diff --git a/gr-filter/lib/pfb_decimator_ccf_impl.h b/gr-filter/lib/pfb_decimator_ccf_impl.h index 3397701cf9..5e0b70177d 100644 --- a/gr-filter/lib/pfb_decimator_ccf_impl.h +++ b/gr-filter/lib/pfb_decimator_ccf_impl.h @@ -40,6 +40,7 @@ namespace gr { bool d_use_fft_rotator; bool d_use_fft_filters; gr_complex *d_rotator; + gr_complex *d_tmp; // used for fft filters gr::thread::mutex d_mutex; // mutex to protect set/work access inline int work_fir_exp(int noutput_items, @@ -55,7 +56,6 @@ namespace gr { gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); - public: pfb_decimator_ccf_impl(unsigned int decim, const std::vector<float> &taps, @@ -70,6 +70,10 @@ namespace gr { std::vector<std::vector<float> > taps() const; void set_channel(const unsigned int channel); + // Overload to create/destroy d_tmp based on max_noutput_items. + bool start(); + bool stop(); + int work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); diff --git a/gr-noaa/grc/noaa_hrpt_decoder.xml b/gr-noaa/grc/noaa_hrpt_decoder.xml index 2d6e98c531..c5874159f2 100644 --- a/gr-noaa/grc/noaa_hrpt_decoder.xml +++ b/gr-noaa/grc/noaa_hrpt_decoder.xml @@ -2,7 +2,7 @@ <block> <name>HRPT Decoder</name> <key>noaa_hrpt_decoder</key> - <category>NOAA</category> + <category>[Core]/NOAA</category> <import>from gnuradio import noaa</import> <make>noaa.hrpt_decoder($verbose,$output)</make> diff --git a/gr-noaa/grc/noaa_hrpt_deframer.xml b/gr-noaa/grc/noaa_hrpt_deframer.xml index af36abf2a2..fbb52f4e44 100644 --- a/gr-noaa/grc/noaa_hrpt_deframer.xml +++ b/gr-noaa/grc/noaa_hrpt_deframer.xml @@ -2,7 +2,7 @@ <block> <name>HRPT Deframer</name> <key>noaa_hrpt_deframer</key> - <category>NOAA</category> + <category>[Core]/NOAA</category> <import>from gnuradio import noaa</import> <make>noaa.hrpt_deframer()</make> <sink> diff --git a/gr-noaa/grc/noaa_hrpt_pll_cf.xml b/gr-noaa/grc/noaa_hrpt_pll_cf.xml index bbe15e8c32..a57d12ed71 100644 --- a/gr-noaa/grc/noaa_hrpt_pll_cf.xml +++ b/gr-noaa/grc/noaa_hrpt_pll_cf.xml @@ -2,7 +2,7 @@ <block> <name>HRPT PLL</name> <key>noaa_hrpt_pll_cf</key> - <category>NOAA</category> + <category>[Core]/NOAA</category> <import>from gnuradio import noaa</import> <make>noaa.hrpt_pll_cf($alpha, $beta, $max_offset)</make> <callback>set_alpha($alpha)</callback> diff --git a/gr-pager/grc/pager_flex_deinterleave.xml b/gr-pager/grc/pager_flex_deinterleave.xml index 14e5782da1..a006023212 100644 --- a/gr-pager/grc/pager_flex_deinterleave.xml +++ b/gr-pager/grc/pager_flex_deinterleave.xml @@ -7,7 +7,7 @@ <block> <name>FLEX Deinterleave</name> <key>pager_flex_deinterleave</key> - <category>Pager</category> + <category>[Core]/Pager</category> <import>from gnuradio import pager</import> <make>pager.flex_deinterleave()</make> diff --git a/gr-pager/grc/pager_flex_sync.xml b/gr-pager/grc/pager_flex_sync.xml index ec22321aa5..d37a4fb7a9 100644 --- a/gr-pager/grc/pager_flex_sync.xml +++ b/gr-pager/grc/pager_flex_sync.xml @@ -7,7 +7,7 @@ <block> <name>FLEX Synchronizer</name> <key>pager_flex_sync</key> - <category>Pager</category> + <category>[Core]/Pager</category> <import>from gnuradio import pager</import> <make>pager.flex_sync()</make> diff --git a/gr-pager/grc/pager_slicer_fb.xml b/gr-pager/grc/pager_slicer_fb.xml index 25642cb48f..f3e95e0748 100644 --- a/gr-pager/grc/pager_slicer_fb.xml +++ b/gr-pager/grc/pager_slicer_fb.xml @@ -7,7 +7,7 @@ <block> <name>4-Level Slicer/DCR</name> <key>pager_slicer_fb</key> - <category>Pager</category> + <category>[Core]/Pager</category> <import>from gnuradio import pager</import> <make>pager.slicer_fb($alpha)</make> diff --git a/gr-qtgui/CMakeLists.txt b/gr-qtgui/CMakeLists.txt index 8fc49a0f5e..ad10363c1e 100644 --- a/gr-qtgui/CMakeLists.txt +++ b/gr-qtgui/CMakeLists.txt @@ -109,6 +109,7 @@ CPACK_COMPONENT("qtgui_swig" add_subdirectory(include/gnuradio/qtgui) add_subdirectory(lib) add_subdirectory(doc) +add_subdirectory(examples/c++) if(ENABLE_PYTHON) add_subdirectory(grc) add_subdirectory(swig) diff --git a/gr-qtgui/doc/qtgui.dox b/gr-qtgui/doc/qtgui.dox index d95ab19f72..d0d7345f73 100644 --- a/gr-qtgui/doc/qtgui.dox +++ b/gr-qtgui/doc/qtgui.dox @@ -232,6 +232,54 @@ tb.msg_connect((msg_block, 'msg'), (tsnk, 'in')) \endcode +\section qtgui_widgets QTGUI Widgets + +The QTGUI component also includes a number of widgets that can be used +to perform live updates of variables through standard QT input +widgets. Most of the widgets are implemented directly in Python +through PyQT. However, GNU Radio is introducing more widgets, written +and therefore available in C++ that also produce messsages. The +Python-based widgets only act as variables and so as they are changed, +any block using those widgets to set paramters has the callback (i.e., +set_value()) function's called. + +\subsection qtgui_widgets_python Python widgets: + +\li Range: creates a slider and/or combo box to change to set/change +the value of a parameter. This widget can set either float of int +values. +\li Entry: An edit box that allows a user to directly set a +new value for the parameter. +\li Chooser: Creates a drop-down menu of pre-set values. +\li Check Box: Creates a check box. The user sets what the value of +the check means when enabled or disabled. +\li Push Button: Adds a button that changes state when pushed versus +released (no sticky). The user sets up what the value is when pressed +versus when released. +\li Label: Adds a Label widget to annotate the GUI. Generally not used +as a variable. +\li Tab Widget: Adds a tab widget that can house other GUI widgets to +format the interface. Use the GUI hint of the other QT widgets and +instruments to specify if and where they exist in the tab widget using +the format "tag widget name@index: row, col, row span, col +span". Simply using "tab widget name@index" will put that widget into +the specific index (starting at 0) of the tab widget while adding the +"row, col, row span, col span" will allow the user to place them in +the tab grid. +\li + + +\subsection qtgui_widgets_cpp C++ and Message-Passing Widgets + +\li \ref gr::qtgui::edit_box_msg "Message Edit Box": A QT edit box +that emits a message when editing is done (e.g., user presses enter, +tabs out of the widget, or mouse-clicks out of the widget). The +message type is settable as are the contents. Messages can be sent as +key:value pairs when Pair Mode is enabled. When Static Mode is +enabled, the data type and the pair key (if in Pair Mode) are set at +the start and cannot be changed at runtime. + + \section qtgui_configuration Configuration There is currently a single configuration option in the preferences diff --git a/gr-qtgui/examples/CMakeLists.txt b/gr-qtgui/examples/CMakeLists.txt index 3581bdaec1..5662dfa9c8 100644 --- a/gr-qtgui/examples/CMakeLists.txt +++ b/gr-qtgui/examples/CMakeLists.txt @@ -41,6 +41,7 @@ install( qtgui_tags_viewing.grc qtgui_vector_sink_example.grc qtgui_message_inputs.grc + test_qtgui_msg.grc DESTINATION ${GR_PKG_QTGUI_EXAMPLES_DIR} COMPONENT "qtgui_python" ) diff --git a/gr-qtgui/examples/c++/CMakeLists.txt b/gr-qtgui/examples/c++/CMakeLists.txt new file mode 100644 index 0000000000..ad84287367 --- /dev/null +++ b/gr-qtgui/examples/c++/CMakeLists.txt @@ -0,0 +1,49 @@ +# Copyright 2016 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_directories( + ${GR_QTGUI_INCLUDE_DIRS} + ${GR_ANALOG_INCLUDE_DIRS} + ${GR_FILTER_INCLUDE_DIRS} + ${GR_BLOCKS_INCLUDE_DIRS} + ${GR_FFT_INCLUDE_DIRS} + ${GNURADIO_RUNTIME_INCLUDE_DIRS} + ${QT_INCLUDE_DIRS} + ${Boost_INCLUDE_DIRS} +) + +list(APPEND QTGUI_LIBRARIES + gnuradio-qtgui + gnuradio-analog + gnuradio-filter + gnuradio-blocks + gnuradio-fft + gnuradio-runtime + ${QWT_LIBRARY_DIRS} +) + +QT4_WRAP_CPP(qtgui_moc_sources display_qt.h) +add_executable(display_qt display_qt.cc ${qtgui_moc_sources}) +target_link_libraries(display_qt ${QTGUI_LIBRARIES}) + +INSTALL(TARGETS + display_qt + DESTINATION ${GR_PKG_QTGUI_EXAMPLES_DIR} + COMPONENT "qtgui_examples" +) diff --git a/gr-qtgui/examples/c++/display_qt.cc b/gr-qtgui/examples/c++/display_qt.cc new file mode 100644 index 0000000000..9d990b25d6 --- /dev/null +++ b/gr-qtgui/examples/c++/display_qt.cc @@ -0,0 +1,126 @@ +/* + * Copyright 2016 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 "display_qt.h" + +mywindow::mywindow() + : QWidget() +{ + // We'll use a horizontal layout of two QTabWidgets + layout = new QHBoxLayout(); + + // Create the tab widgets + tab0 = new QTabWidget(); + tab1 = new QTabWidget(); + + // Add the tab widgets to the layou + layout->addWidget(tab0); + layout->addWidget(tab1); + + // Set the layout as the main widget's layou + setLayout(layout); + + // Simple resizing of the app + resize(2000,800); + + // sample rate + int rate = 48000; + + // Create the GNU Radio top block + tb = make_top_block("display_qt"); + + // Source will be sine wave in noise + src0 = analog::sig_source_f::make(rate, analog::GR_SIN_WAVE, 1500, 1); + src1 = analog::noise_source_f::make(analog::GR_GAUSSIAN, 0.1); + + // Combine signal and noise; add throttle + src = blocks::add_ff::make(); + thr = blocks::throttle::make(sizeof(float), rate); + + // Create the QTGUI sinks + // Time, Freq, Waterfall, and Histogram sinks + tsnk = qtgui::time_sink_f::make(1024, rate, "", 1); + fsnk = qtgui::freq_sink_f::make(1024, fft::window::WIN_HANN, + 0, rate, "", 1); + wsnk = qtgui::waterfall_sink_f::make(1024, fft::window::WIN_HANN, + 0, rate, "", 1); + hsnk = qtgui::histogram_sink_f::make(1024, 100, -2, 2, "", 1); + + // Turn off the legend on these plots + tsnk->disable_legend(); + fsnk->disable_legend(); + hsnk->disable_legend(); + + // Connect the graph + tb->connect(src0, 0, src, 0); + tb->connect(src1, 0, src, 1); + tb->connect(src, 0, thr, 0); + tb->connect(thr, 0, tsnk, 0); + tb->connect(thr, 0, fsnk, 0); + tb->connect(thr, 0, wsnk, 0); + tb->connect(thr, 0, hsnk, 0); + + // Get the raw QWidget objects from the GNU Radio blocks + qtgui_time_sink_win = tsnk->qwidget(); + qtgui_freq_sink_win = fsnk->qwidget(); + qtgui_waterfall_sink_win = wsnk->qwidget(); + qtgui_histogram_sink_win = hsnk->qwidget(); + + // Plug the widgets into the tabs + tab0->addTab(qtgui_time_sink_win, "Time"); + tab0->addTab(qtgui_histogram_sink_win, "Hist"); + tab1->addTab(qtgui_freq_sink_win, "Freq"); + tab1->addTab(qtgui_waterfall_sink_win, "Waterfall"); +} + +mywindow::~mywindow() +{ +} + +void +mywindow::start() +{ + tb->start(); +} + +void +mywindow::quitting() +{ + tb->stop(); + tb->wait(); +} + +int main(int argc, char **argv) +{ + // The global QT application + QApplication app(argc, argv); + + mywindow *w = new mywindow(); + + QObject::connect(&app, SIGNAL(aboutToQuit()), + w, SLOT(quitting())); + + w->start(); // Start the flowgraph + w->show(); // show the window + app.exec(); // run the QT executor loop + + return 0; +} diff --git a/gr-qtgui/examples/c++/display_qt.h b/gr-qtgui/examples/c++/display_qt.h new file mode 100644 index 0000000000..97c46c1faa --- /dev/null +++ b/gr-qtgui/examples/c++/display_qt.h @@ -0,0 +1,77 @@ +/* + * Copyright 2016 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. + */ + +// Q_MOC_RUN is a workaround for a QT4 + Boost version issue +#ifndef Q_MOC_RUN +#include <gnuradio/top_block.h> +#include <gnuradio/analog/sig_source_f.h> +#include <gnuradio/analog/noise_source_f.h> +#include <gnuradio/blocks/add_ff.h> +#include <gnuradio/blocks/throttle.h> +#include <gnuradio/qtgui/time_sink_f.h> +#include <gnuradio/qtgui/freq_sink_f.h> +#include <gnuradio/qtgui/waterfall_sink_f.h> +#include <gnuradio/qtgui/histogram_sink_f.h> +#include <gnuradio/fft/window.h> +#endif + +#include <QWidget> +#include <QHBoxLayout> +#include <QTabWidget> + +using namespace gr; + +class mywindow : public QWidget +{ + Q_OBJECT + +private: + QHBoxLayout *layout; + QTabWidget *tab0; + QTabWidget *tab1; + QWidget* qtgui_time_sink_win; + QWidget* qtgui_freq_sink_win; + QWidget* qtgui_waterfall_sink_win; + QWidget* qtgui_histogram_sink_win; + +#ifndef Q_MOC_RUN + top_block_sptr tb; + analog::sig_source_f::sptr src0; + analog::noise_source_f::sptr src1; + blocks::add_ff::sptr src; + blocks::throttle::sptr thr; + qtgui::time_sink_f::sptr tsnk; + qtgui::freq_sink_f::sptr fsnk; + qtgui::waterfall_sink_f::sptr wsnk; + qtgui::histogram_sink_f::sptr hsnk; +#endif + +public slots: + // Stop the topblock before shutting down the window + void quitting(); + +public: + mywindow(); + virtual ~mywindow(); + + // call start() on the topblock + void start(); +}; diff --git a/grc/examples/xmlrpc/xmlrpc_server.grc b/gr-qtgui/examples/test_qtgui_msg.grc index d210b2694e..12ca0e34c9 100644 --- a/grc/examples/xmlrpc/xmlrpc_server.grc +++ b/gr-qtgui/examples/test_qtgui_msg.grc @@ -1,39 +1,68 @@ -<?xml version='1.0' encoding='ASCII'?> +<?xml version='1.0' encoding='utf-8'?> +<?grc format='1' created='3.7.10'?> <flow_graph> - <timestamp>Sat Jul 12 17:11:40 2014</timestamp> + <timestamp>Wed Feb 10 14:28:57 2016</timestamp> <block> <key>options</key> <param> - <key>id</key> - <value>server_block</value> + <key>author</key> + <value></value> </param> <param> - <key>_enabled</key> - <value>True</value> + <key>window_size</key> + <value></value> </param> <param> - <key>title</key> - <value>XMLRPC Server</value> + <key>category</key> + <value>Custom</value> </param> <param> - <key>author</key> - <value>Example</value> + <key>comment</key> + <value></value> </param> <param> <key>description</key> - <value>example flow graph</value> + <value></value> </param> <param> - <key>window_size</key> - <value>1280, 1024</value> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(8, 11)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> </param> <param> <key>generate_options</key> <value>qt_gui</value> </param> <param> - <key>category</key> - <value>Custom</value> + <key>hier_block_src_path</key> + <value>.:</value> + </param> + <param> + <key>id</key> + <value>test_qtgui_msg</value> + </param> + <param> + <key>max_nouts</key> + <value>0</value> + </param> + <param> + <key>qt_qss_theme</key> + <value></value> + </param> + <param> + <key>realtime_scheduling</key> + <value></value> + </param> + <param> + <key>run_command</key> + <value>{python} -u {filename}</value> </param> <param> <key>run_options</key> @@ -44,38 +73,45 @@ <value>True</value> </param> <param> - <key>max_nouts</key> - <value>0</value> + <key>thread_safe_setters</key> + <value></value> </param> <param> - <key>realtime_scheduling</key> + <key>title</key> <value></value> </param> + </block> + <block> + <key>variable</key> <param> - <key>alias</key> + <key>comment</key> <value></value> </param> <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> <key>_coordinate</key> - <value>(0, -1)</value> + <value>(168, 11)</value> </param> <param> <key>_rotation</key> <value>0</value> </param> - </block> - <block> - <key>variable</key> <param> <key>id</key> - <value>ampl</value> + <value>samp_rate</value> </param> <param> - <key>_enabled</key> - <value>True</value> + <key>value</key> + <value>32000</value> </param> + </block> + <block> + <key>analog_sig_source_x</key> <param> - <key>value</key> + <key>amp</key> <value>1</value> </param> <param> @@ -83,363 +119,413 @@ <value></value> </param> <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>1</value> + </param> + <param> + <key>freq</key> + <value>1000</value> + </param> + <param> <key>_coordinate</key> - <value>(4, 291)</value> + <value>(80, 83)</value> </param> <param> <key>_rotation</key> <value>0</value> </param> - </block> - <block> - <key>variable</key> <param> <key>id</key> - <value>freq</value> + <value>analog_sig_source_x_0</value> </param> <param> - <key>_enabled</key> - <value>True</value> + <key>maxoutbuf</key> + <value>0</value> </param> <param> - <key>value</key> - <value>1000</value> + <key>minoutbuf</key> + <value>0</value> </param> <param> - <key>alias</key> - <value></value> + <key>offset</key> + <value>0</value> </param> <param> - <key>_coordinate</key> - <value>(2, 213)</value> + <key>type</key> + <value>complex</value> </param> <param> - <key>_rotation</key> - <value>0</value> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>waveform</key> + <value>analog.GR_COS_WAVE</value> </param> </block> <block> - <key>variable</key> + <key>blocks_message_debug</key> <param> - <key>id</key> - <value>samp_rate</value> + <key>alias</key> + <value></value> </param> <param> - <key>_enabled</key> - <value>True</value> + <key>comment</key> + <value></value> </param> <param> - <key>value</key> - <value>32000</value> + <key>affinity</key> + <value></value> </param> <param> - <key>alias</key> - <value></value> + <key>_enabled</key> + <value>1</value> </param> <param> <key>_coordinate</key> - <value>(2, 136)</value> + <value>(600, 481)</value> </param> <param> <key>_rotation</key> <value>0</value> </param> - </block> - <block> - <key>variable</key> <param> <key>id</key> - <value>offset</value> + <value>blocks_message_debug_0</value> </param> + </block> + <block> + <key>qtgui_edit_box_msg</key> <param> - <key>_enabled</key> - <value>True</value> + <key>alias</key> + <value></value> </param> <param> - <key>value</key> - <value>0</value> + <key>comment</key> + <value></value> </param> <param> - <key>alias</key> + <key>affinity</key> <value></value> </param> <param> + <key>_enabled</key> + <value>1</value> + </param> + <param> <key>_coordinate</key> - <value>(3, 366)</value> + <value>(104, 219)</value> + </param> + <param> + <key>gui_hint</key> + <value>0,0,1,1</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> + <value>qtgui_edit_box_msg_0</value> </param> <param> - <key>_enabled</key> - <value>True</value> + <key>key</key> + <value>freq</value> </param> <param> - <key>type</key> - <value>float</value> + <key>label</key> + <value>Frequency</value> </param> <param> - <key>samp_rate</key> - <value>samp_rate</value> + <key>maxoutbuf</key> + <value>0</value> </param> <param> - <key>waveform</key> - <value>analog.GR_COS_WAVE</value> + <key>minoutbuf</key> + <value>0</value> </param> <param> - <key>freq</key> - <value>freq</value> + <key>is_pair</key> + <value>True</value> </param> <param> - <key>amp</key> - <value>ampl</value> + <key>is_static</key> + <value>True</value> </param> <param> - <key>offset</key> - <value>offset</value> + <key>type</key> + <value>float</value> </param> <param> + <key>value</key> + <value></value> + </param> + </block> + <block> + <key>qtgui_edit_box_msg</key> + <param> <key>alias</key> <value></value> </param> <param> - <key>affinity</key> + <key>comment</key> <value></value> </param> <param> - <key>minoutbuf</key> - <value>0</value> + <key>affinity</key> + <value></value> </param> <param> - <key>maxoutbuf</key> - <value>0</value> + <key>_enabled</key> + <value>1</value> </param> <param> <key>_coordinate</key> - <value>(175, 0)</value> + <value>(88, 371)</value> + </param> + <param> + <key>gui_hint</key> + <value>3,1,1,1</value> </param> <param> <key>_rotation</key> <value>0</value> </param> - </block> - <block> - <key>blocks_throttle</key> <param> <key>id</key> - <value>blocks_throttle</value> + <value>qtgui_edit_box_msg_0_0</value> </param> <param> - <key>_enabled</key> - <value>True</value> + <key>key</key> + <value></value> </param> <param> - <key>type</key> - <value>float</value> + <key>label</key> + <value></value> </param> <param> - <key>samples_per_second</key> - <value>samp_rate</value> + <key>maxoutbuf</key> + <value>0</value> </param> <param> - <key>vlen</key> - <value>1</value> + <key>minoutbuf</key> + <value>0</value> </param> <param> - <key>ignoretag</key> - <value>True</value> + <key>is_pair</key> + <value>False</value> + </param> + <param> + <key>is_static</key> + <value>False</value> + </param> + <param> + <key>type</key> + <value>int_vec</value> </param> <param> + <key>value</key> + <value></value> + </param> + </block> + <block> + <key>qtgui_edit_box_msg</key> + <param> <key>alias</key> <value></value> </param> <param> - <key>affinity</key> + <key>comment</key> <value></value> </param> <param> - <key>minoutbuf</key> - <value>0</value> + <key>affinity</key> + <value></value> </param> <param> - <key>maxoutbuf</key> - <value>0</value> + <key>_enabled</key> + <value>1</value> </param> <param> <key>_coordinate</key> - <value>(399, 35)</value> + <value>(272, 499)</value> + </param> + <param> + <key>gui_hint</key> + <value>3,0,1,1</value> </param> <param> <key>_rotation</key> <value>0</value> </param> - </block> - <block> - <key>xmlrpc_server</key> <param> <key>id</key> - <value>xmlrpc_server</value> - </param> - <param> - <key>_enabled</key> - <value>True</value> + <value>qtgui_edit_box_msg_0_0_0</value> </param> <param> - <key>addr</key> - <value>localhost</value> - </param> - <param> - <key>port</key> - <value>1234</value> + <key>key</key> + <value></value> </param> <param> - <key>alias</key> + <key>label</key> <value></value> </param> <param> - <key>_coordinate</key> - <value>(129, 137)</value> + <key>maxoutbuf</key> + <value>0</value> </param> <param> - <key>_rotation</key> + <key>minoutbuf</key> <value>0</value> </param> - </block> - <block> - <key>qtgui_time_sink_x</key> <param> - <key>id</key> - <value>qtgui_time_sink_x_0</value> + <key>is_pair</key> + <value>False</value> </param> <param> - <key>_enabled</key> - <value>True</value> + <key>is_static</key> + <value>False</value> </param> <param> <key>type</key> - <value>float</value> + <value>int_vec</value> </param> <param> - <key>name</key> - <value>Scope Plot</value> + <key>value</key> + <value></value> </param> + </block> + <block> + <key>qtgui_freq_sink_x</key> <param> - <key>size</key> - <value>1024</value> + <key>autoscale</key> + <value>False</value> </param> <param> - <key>srate</key> - <value>samp_rate</value> + <key>average</key> + <value>1.0</value> </param> <param> - <key>autoscale</key> - <value>False</value> + <key>bw</key> + <value>samp_rate</value> </param> <param> - <key>ymin</key> - <value>-1</value> + <key>alias</key> + <value></value> </param> <param> - <key>ymax</key> - <value>1</value> + <key>fc</key> + <value>0</value> </param> <param> - <key>nconnections</key> - <value>1</value> + <key>comment</key> + <value></value> </param> <param> - <key>update_time</key> - <value>0.10</value> + <key>ctrlpanel</key> + <value>False</value> </param> <param> - <key>entags</key> - <value>True</value> + <key>affinity</key> + <value></value> </param> <param> - <key>gui_hint</key> - <value>0, 0, 2, 4</value> + <key>_enabled</key> + <value>1</value> </param> <param> - <key>tr_mode</key> - <value>qtgui.TRIG_MODE_FREE</value> + <key>fftsize</key> + <value>1024</value> </param> <param> - <key>tr_slope</key> - <value>qtgui.TRIG_SLOPE_POS</value> + <key>_coordinate</key> + <value>(512, 195)</value> </param> <param> - <key>tr_level</key> - <value>0.0</value> + <key>gui_hint</key> + <value>1,0,1,2</value> </param> <param> - <key>tr_delay</key> + <key>_rotation</key> <value>0</value> </param> <param> - <key>tr_chan</key> - <value>0</value> + <key>grid</key> + <value>False</value> </param> <param> - <key>tr_tag</key> - <value>""</value> + <key>id</key> + <value>qtgui_freq_sink_x_0</value> </param> <param> - <key>label1</key> - <value></value> + <key>legend</key> + <value>False</value> </param> <param> - <key>width1</key> - <value>1</value> + <key>alpha1</key> + <value>1.0</value> </param> <param> <key>color1</key> <value>"blue"</value> </param> <param> - <key>style1</key> - <value>1</value> + <key>label1</key> + <value></value> </param> <param> - <key>marker1</key> - <value>-1</value> + <key>width1</key> + <value>1</value> </param> <param> - <key>alpha1</key> + <key>alpha10</key> <value>1.0</value> </param> <param> - <key>label2</key> + <key>color10</key> + <value>"dark blue"</value> + </param> + <param> + <key>label10</key> <value></value> </param> <param> - <key>width2</key> + <key>width10</key> <value>1</value> </param> <param> + <key>alpha2</key> + <value>1.0</value> + </param> + <param> <key>color2</key> <value>"red"</value> </param> <param> - <key>style2</key> - <value>1</value> + <key>label2</key> + <value></value> </param> <param> - <key>marker2</key> - <value>-1</value> + <key>width2</key> + <value>1</value> </param> <param> - <key>alpha2</key> + <key>alpha3</key> <value>1.0</value> </param> <param> + <key>color3</key> + <value>"green"</value> + </param> + <param> <key>label3</key> <value></value> </param> @@ -448,20 +534,12 @@ <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> + <key>alpha4</key> + <value>1.0</value> </param> <param> - <key>alpha3</key> - <value>1.0</value> + <key>color4</key> + <value>"black"</value> </param> <param> <key>label4</key> @@ -472,20 +550,12 @@ <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> + <key>alpha5</key> + <value>1.0</value> </param> <param> - <key>alpha4</key> - <value>1.0</value> + <key>color5</key> + <value>"cyan"</value> </param> <param> <key>label5</key> @@ -496,20 +566,12 @@ <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> + <key>alpha6</key> + <value>1.0</value> </param> <param> - <key>alpha5</key> - <value>1.0</value> + <key>color6</key> + <value>"magenta"</value> </param> <param> <key>label6</key> @@ -520,20 +582,12 @@ <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> + <key>alpha7</key> + <value>1.0</value> </param> <param> - <key>alpha6</key> - <value>1.0</value> + <key>color7</key> + <value>"yellow"</value> </param> <param> <key>label7</key> @@ -544,20 +598,12 @@ <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> + <key>alpha8</key> + <value>1.0</value> </param> <param> - <key>alpha7</key> - <value>1.0</value> + <key>color8</key> + <value>"dark red"</value> </param> <param> <key>label8</key> @@ -568,20 +614,12 @@ <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> + <key>alpha9</key> + <value>1.0</value> </param> <param> - <key>alpha8</key> - <value>1.0</value> + <key>color9</key> + <value>"dark green"</value> </param> <param> <key>label9</key> @@ -592,317 +630,349 @@ <value>1</value> </param> <param> - <key>color9</key> - <value>"dark green"</value> + <key>maxoutbuf</key> + <value>0</value> </param> <param> - <key>style9</key> - <value>1</value> + <key>minoutbuf</key> + <value>0</value> </param> <param> - <key>marker9</key> - <value>-1</value> + <key>name</key> + <value>""</value> </param> <param> - <key>alpha9</key> - <value>1.0</value> + <key>nconnections</key> + <value>1</value> </param> <param> - <key>label10</key> - <value></value> + <key>showports</key> + <value>False</value> </param> <param> - <key>width10</key> - <value>1</value> + <key>freqhalf</key> + <value>True</value> </param> <param> - <key>color10</key> - <value>"blue"</value> + <key>tr_chan</key> + <value>0</value> </param> <param> - <key>style10</key> - <value>1</value> + <key>tr_level</key> + <value>0.0</value> </param> <param> - <key>marker10</key> - <value>-1</value> + <key>tr_mode</key> + <value>qtgui.TRIG_MODE_FREE</value> </param> <param> - <key>alpha10</key> - <value>1.0</value> + <key>tr_tag</key> + <value>""</value> </param> <param> - <key>alias</key> - <value></value> + <key>type</key> + <value>complex</value> </param> <param> - <key>affinity</key> - <value></value> + <key>update_time</key> + <value>0.10</value> </param> <param> - <key>_coordinate</key> - <value>(644, 13)</value> + <key>wintype</key> + <value>firdes.WIN_BLACKMAN_hARRIS</value> </param> <param> - <key>_rotation</key> - <value>0</value> + <key>ymax</key> + <value>10</value> + </param> + <param> + <key>ymin</key> + <value>-140</value> </param> </block> <block> - <key>qtgui_freq_sink_x</key> + <key>qtgui_waterfall_sink_x</key> <param> - <key>id</key> - <value>qtgui_freq_sink_x_0</value> + <key>bw</key> + <value>samp_rate</value> </param> <param> - <key>_enabled</key> - <value>True</value> + <key>alias</key> + <value></value> </param> <param> - <key>type</key> - <value>float</value> + <key>fc</key> + <value>0</value> </param> <param> - <key>name</key> - <value>Spectrum Plot</value> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>1</value> </param> <param> <key>fftsize</key> <value>1024</value> </param> <param> - <key>wintype</key> - <value>firdes.WIN_BLACKMAN_hARRIS</value> + <key>_coordinate</key> + <value>(512, 115)</value> </param> <param> - <key>fc</key> - <value>0</value> + <key>gui_hint</key> + <value>2,0,1,2</value> </param> <param> - <key>bw</key> - <value>samp_rate</value> + <key>_rotation</key> + <value>0</value> </param> <param> - <key>autoscale</key> + <key>grid</key> <value>False</value> </param> <param> - <key>average</key> - <value>1.0</value> + <key>id</key> + <value>qtgui_waterfall_sink_x_0</value> </param> <param> - <key>ymin</key> - <value>-140</value> + <key>int_max</key> + <value>10</value> </param> <param> - <key>ymax</key> - <value>10</value> + <key>int_min</key> + <value>-140</value> </param> <param> - <key>nconnections</key> - <value>1</value> + <key>legend</key> + <value>True</value> </param> <param> - <key>update_time</key> - <value>0.10</value> + <key>alpha1</key> + <value>1.0</value> </param> <param> - <key>gui_hint</key> - <value>2, 0, 2, 4</value> + <key>color1</key> + <value>0</value> </param> <param> <key>label1</key> <value></value> </param> <param> - <key>width1</key> - <value>1</value> - </param> - <param> - <key>color1</key> - <value>"blue"</value> + <key>alpha10</key> + <value>1.0</value> </param> <param> - <key>alpha1</key> - <value>1.0</value> + <key>color10</key> + <value>0</value> </param> <param> - <key>label2</key> + <key>label10</key> <value></value> </param> <param> - <key>width2</key> - <value>1</value> + <key>alpha2</key> + <value>1.0</value> </param> <param> <key>color2</key> - <value>"red"</value> - </param> - <param> - <key>alpha2</key> - <value>1.0</value> + <value>0</value> </param> <param> - <key>label3</key> + <key>label2</key> <value></value> </param> <param> - <key>width3</key> - <value>1</value> + <key>alpha3</key> + <value>1.0</value> </param> <param> <key>color3</key> - <value>"green"</value> - </param> - <param> - <key>alpha3</key> - <value>1.0</value> + <value>0</value> </param> <param> - <key>label4</key> + <key>label3</key> <value></value> </param> <param> - <key>width4</key> - <value>1</value> + <key>alpha4</key> + <value>1.0</value> </param> <param> <key>color4</key> - <value>"black"</value> - </param> - <param> - <key>alpha4</key> - <value>1.0</value> + <value>0</value> </param> <param> - <key>label5</key> + <key>label4</key> <value></value> </param> <param> - <key>width5</key> - <value>1</value> + <key>alpha5</key> + <value>1.0</value> </param> <param> <key>color5</key> - <value>"cyan"</value> - </param> - <param> - <key>alpha5</key> - <value>1.0</value> + <value>0</value> </param> <param> - <key>label6</key> + <key>label5</key> <value></value> </param> <param> - <key>width6</key> - <value>1</value> + <key>alpha6</key> + <value>1.0</value> </param> <param> <key>color6</key> - <value>"magenta"</value> - </param> - <param> - <key>alpha6</key> - <value>1.0</value> + <value>0</value> </param> <param> - <key>label7</key> + <key>label6</key> <value></value> </param> <param> - <key>width7</key> - <value>1</value> + <key>alpha7</key> + <value>1.0</value> </param> <param> <key>color7</key> - <value>"yellow"</value> - </param> - <param> - <key>alpha7</key> - <value>1.0</value> + <value>0</value> </param> <param> - <key>label8</key> + <key>label7</key> <value></value> </param> <param> - <key>width8</key> - <value>1</value> + <key>alpha8</key> + <value>1.0</value> </param> <param> <key>color8</key> - <value>"dark red"</value> - </param> - <param> - <key>alpha8</key> - <value>1.0</value> + <value>0</value> </param> <param> - <key>label9</key> + <key>label8</key> <value></value> </param> <param> - <key>width9</key> - <value>1</value> + <key>alpha9</key> + <value>1.0</value> </param> <param> <key>color9</key> - <value>"dark green"</value> + <value>0</value> </param> <param> - <key>alpha9</key> - <value>1.0</value> + <key>label9</key> + <value></value> </param> <param> - <key>label10</key> - <value></value> + <key>maxoutbuf</key> + <value>0</value> </param> <param> - <key>width10</key> - <value>1</value> + <key>minoutbuf</key> + <value>0</value> </param> <param> - <key>color10</key> - <value>"dark blue"</value> + <key>name</key> + <value>""</value> </param> <param> - <key>alpha10</key> - <value>1.0</value> + <key>nconnections</key> + <value>1</value> </param> <param> - <key>alias</key> - <value></value> + <key>showports</key> + <value>False</value> </param> <param> - <key>affinity</key> - <value></value> + <key>freqhalf</key> + <value>True</value> </param> <param> - <key>_coordinate</key> - <value>(644, 126)</value> + <key>type</key> + <value>complex</value> </param> <param> - <key>_rotation</key> - <value>0</value> + <key>update_time</key> + <value>0.10</value> + </param> + <param> + <key>wintype</key> + <value>firdes.WIN_BLACKMAN_hARRIS</value> </param> </block> <connection> <source_block_id>analog_sig_source_x_0</source_block_id> - <sink_block_id>blocks_throttle</sink_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>blocks_throttle</source_block_id> - <sink_block_id>qtgui_time_sink_x_0</sink_block_id> + <source_block_id>analog_sig_source_x_0</source_block_id> + <sink_block_id>qtgui_waterfall_sink_x_0</sink_block_id> <source_key>0</source_key> <sink_key>0</sink_key> </connection> <connection> - <source_block_id>blocks_throttle</source_block_id> + <source_block_id>qtgui_edit_box_msg_0</source_block_id> <sink_block_id>qtgui_freq_sink_x_0</sink_block_id> - <source_key>0</source_key> - <sink_key>0</sink_key> + <source_key>msg</source_key> + <sink_key>freq</sink_key> + </connection> + <connection> + <source_block_id>qtgui_edit_box_msg_0</source_block_id> + <sink_block_id>qtgui_waterfall_sink_x_0</sink_block_id> + <source_key>msg</source_key> + <sink_key>freq</sink_key> + </connection> + <connection> + <source_block_id>qtgui_edit_box_msg_0_0</source_block_id> + <sink_block_id>qtgui_edit_box_msg_0_0_0</sink_block_id> + <source_key>msg</source_key> + <sink_key>val</sink_key> + </connection> + <connection> + <source_block_id>qtgui_edit_box_msg_0_0_0</source_block_id> + <sink_block_id>blocks_message_debug_0</sink_block_id> + <source_key>msg</source_key> + <sink_key>print</sink_key> + </connection> + <connection> + <source_block_id>qtgui_edit_box_msg_0_0_0</source_block_id> + <sink_block_id>qtgui_edit_box_msg_0_0</sink_block_id> + <source_key>msg</source_key> + <sink_key>val</sink_key> + </connection> + <connection> + <source_block_id>qtgui_freq_sink_x_0</source_block_id> + <sink_block_id>qtgui_edit_box_msg_0</sink_block_id> + <source_key>freq</source_key> + <sink_key>val</sink_key> + </connection> + <connection> + <source_block_id>qtgui_freq_sink_x_0</source_block_id> + <sink_block_id>qtgui_freq_sink_x_0</sink_block_id> + <source_key>freq</source_key> + <sink_key>freq</sink_key> + </connection> + <connection> + <source_block_id>qtgui_waterfall_sink_x_0</source_block_id> + <sink_block_id>qtgui_edit_box_msg_0</sink_block_id> + <source_key>freq</source_key> + <sink_key>val</sink_key> + </connection> + <connection> + <source_block_id>qtgui_waterfall_sink_x_0</source_block_id> + <sink_block_id>qtgui_waterfall_sink_x_0</sink_block_id> + <source_key>freq</source_key> + <sink_key>freq</sink_key> </connection> </flow_graph> diff --git a/gr-qtgui/grc/qtgui_block_tree.xml b/gr-qtgui/grc/qtgui_block_tree.xml index 03db638d61..33fb12a7b5 100644 --- a/gr-qtgui/grc/qtgui_block_tree.xml +++ b/gr-qtgui/grc/qtgui_block_tree.xml @@ -27,7 +27,7 @@ ################################################### --> <cat> - <name></name> <!-- Blank for Root Name --> + <name>[Core]</name> <cat> <name>Instrumentation</name> <cat> @@ -55,6 +55,7 @@ <block>variable_qtgui_push_button</block> <block>variable_qtgui_entry</block> <block>variable_qtgui_label</block> + <block>qtgui_edit_box_msg</block> </cat> </cat> </cat> diff --git a/gr-qtgui/grc/qtgui_const_sink_x.xml b/gr-qtgui/grc/qtgui_const_sink_x.xml index 956a1694ec..755f12f964 100644 --- a/gr-qtgui/grc/qtgui_const_sink_x.xml +++ b/gr-qtgui/grc/qtgui_const_sink_x.xml @@ -22,6 +22,7 @@ self.$(id).set_x_axis($xmin, $xmax) self.$(id).set_trigger_mode($tr_mode, $tr_slope, $tr_level, $tr_chan, $tr_tag) self.$(id).enable_autoscale($autoscale) self.$(id).enable_grid($grid) +self.$(id).enable_axis_labels($axislabels) if not $legend: self.$(id).disable_legend() @@ -276,6 +277,23 @@ $(gui_hint()($win))</make> </param> <param> + <name>Axis Labels</name> + <key>axislabels</key> + <value>True</value> + <type>enum</type> + <hide>part</hide> + <option> + <name>Yes</name> + <key>True</key> + </option> + <option> + <name>No</name> + <key>False</key> + </option> + <tab>Config</tab> + </param> + + <param> <name>Line 1 Label</name> <key>label1</key> <type>string</type> diff --git a/gr-qtgui/grc/qtgui_edit_box_msg.xml b/gr-qtgui/grc/qtgui_edit_box_msg.xml new file mode 100644 index 0000000000..c7c758a612 --- /dev/null +++ b/gr-qtgui/grc/qtgui_edit_box_msg.xml @@ -0,0 +1,152 @@ +<?xml version="1.0"?> +<!-- +################################################### +##QT GUI Edit Box that sends messages +################################################### + --> +<block> + <name>QT GUI Message Edit Box</name> + <key>qtgui_edit_box_msg</key> + <import>from PyQt4 import Qt</import> + <import>from gnuradio import qtgui</import> + <import>import sip</import> + <make>#set $win = 'self._%s_win'%$id +qtgui.edit_box_msg($(type.t), $value, $label, $is_pair, $is_static, $key) +self._$(id)_win = sip.wrapinstance(self.$(id).pyqwidget(), Qt.QWidget) +$(gui_hint()($win))</make> + + <param> + <name>Type</name> + <key>type</key> + <value>string</value> + <type>enum</type> + <option> + <name>String</name> + <key>string</key> + <opt>t:qtgui.STRING</opt> + </option> + <option> + <name>Int</name> + <key>int</key> + <opt>t:qtgui.INT</opt> + </option> + <option> + <name>Float</name> + <key>float</key> + <opt>t:qtgui.FLOAT</opt> + </option> + <option> + <name>Double</name> + <key>double</key> + <opt>t:qtgui.DOUBLE</opt> + </option> + <option> + <name>Complex</name> + <key>complex</key> + <opt>t:qtgui.COMPLEX</opt> + </option> + <option> + <name>Int (Vec)</name> + <key>int_vec</key> + <opt>t:qtgui.INT_VEC</opt> + </option> + <option> + <name>Float (Vec)</name> + <key>flt_vec</key> + <opt>t:qtgui.FLOAT_VEC</opt> + </option> + <option> + <name>Double (Vec)</name> + <key>dbl_vec</key> + <opt>t:qtgui.DOUBLE_VEC</opt> + </option> + <option> + <name>Complex (Vec)</name> + <key>cpx_vec</key> + <opt>t:qtgui.COMPLEX_VEC</opt> + </option> + </param> + + <param> + <name>Value</name> + <key>value</key> + <value></value> + <type>string</type> + </param> + + <param> + <name>Label</name> + <key>label</key> + <value></value> + <type>string</type> + </param> + + <param> + <name>Pair Mode</name> + <key>is_pair</key> + <value>True</value> + <type>enum</type> + <option> + <name>False</name> + <key>False</key> + </option> + <option> + <name>True</name> + <key>True</key> + </option> + </param> + + <param> + <name>Static Mode</name> + <key>is_static</key> + <value>True</value> + <type>enum</type> + <option> + <name>False</name> + <key>False</key> + </option> + <option> + <name>True</name> + <key>True</key> + </option> + </param> + + <param> + <name>Key</name> + <key>key</key> + <value></value> + <type>string</type> + <hide>#if $is_pair() == 'True' then 'none' else 'all'#</hide> + </param> + + <param> + <name>GUI Hint</name> + <key>gui_hint</key> + <value></value> + <type>gui_hint</type> + <hide>part</hide> + </param> + + <!-- If both is_pair and is_static are True, we require a default key. + If either or both are false, we don't care about a default key. + --> + <check>($is_pair and $is_static and len($key) > 0) or not ($is_pair and $is_static)</check> + + <sink> + <name>val</name> + <type>message</type> + <optional>1</optional> + </sink> + + <source> + <name>msg</name> + <type>message</type> + <optional>1</optional> + </source> + + <doc> +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/grc/qtgui_freq_sink_x.xml b/gr-qtgui/grc/qtgui_freq_sink_x.xml index d7c3139849..009a184327 100644 --- a/gr-qtgui/grc/qtgui_freq_sink_x.xml +++ b/gr-qtgui/grc/qtgui_freq_sink_x.xml @@ -22,10 +22,12 @@ qtgui.$(type.fcn)( ) self.$(id).set_update_time($update_time) self.$(id).set_y_axis($ymin, $ymax) +self.$(id).set_y_label($label, $units) self.$(id).set_trigger_mode($tr_mode, $tr_level, $tr_chan, $tr_tag) self.$(id).enable_autoscale($autoscale) self.$(id).enable_grid($grid) self.$(id).set_fft_average($average) +self.$(id).enable_axis_labels($axislabels) self.$(id).enable_control_panel($ctrlpanel) if not $legend: @@ -252,6 +254,22 @@ $(gui_hint()($win))</make> </param> <param> + <name>Y label</name> + <key>label</key> + <value>Relative Gain</value> + <type>string</type> + <hide>part</hide> + </param> + + <param> + <name>Y units</name> + <key>units</key> + <value>dB</value> + <type>string</type> + <hide>part</hide> + </param> + + <param> <name>Number of Inputs</name> <key>nconnections</key> <value>1</value> @@ -383,6 +401,23 @@ $(gui_hint()($win))</make> </param> <param> + <name>Axis Labels</name> + <key>axislabels</key> + <value>True</value> + <type>enum</type> + <hide>part</hide> + <option> + <name>Yes</name> + <key>True</key> + </option> + <option> + <name>No</name> + <key>False</key> + </option> + <tab>Config</tab> + </param> + + <param> <name>Line 1 Label</name> <key>label1</key> <type>string</type> diff --git a/gr-qtgui/grc/qtgui_histogram_sink_x.xml b/gr-qtgui/grc/qtgui_histogram_sink_x.xml index a9966fd7ec..a789d2e4fa 100644 --- a/gr-qtgui/grc/qtgui_histogram_sink_x.xml +++ b/gr-qtgui/grc/qtgui_histogram_sink_x.xml @@ -24,6 +24,7 @@ self.$(id).set_update_time($update_time) self.$(id).enable_autoscale($autoscale) self.$(id).enable_accumulate($accum) self.$(id).enable_grid($grid) +self.$(id).enable_axis_labels($axislabels) if not $legend: self.$(id).disable_legend() @@ -211,6 +212,23 @@ $(gui_hint()($win)) </param> <param> + <name>Axis Labels</name> + <key>axislabels</key> + <value>True</value> + <type>enum</type> + <hide>part</hide> + <option> + <name>Yes</name> + <key>True</key> + </option> + <option> + <name>No</name> + <key>False</key> + </option> + <tab>Config</tab> + </param> + + <param> <name>Line 1 Label</name> <key>label1</key> <type>string</type> diff --git a/gr-qtgui/grc/qtgui_time_raster_x.xml b/gr-qtgui/grc/qtgui_time_raster_x.xml index b36944c1e5..0359dc3b71 100644 --- a/gr-qtgui/grc/qtgui_time_raster_x.xml +++ b/gr-qtgui/grc/qtgui_time_raster_x.xml @@ -24,6 +24,7 @@ qtgui.$(type.fcn)( self.$(id).set_update_time($update_time) self.$(id).set_intensity_range($zmin, $zmax) self.$(id).enable_grid($grid) +self.$(id).enable_axis_labels($axislabels) labels = [$label1, $label2, $label3, $label4, $label5, $label6, $label7, $label8, $label9, $label10] @@ -185,6 +186,22 @@ $(gui_hint()($win))</make> <hide>part</hide> </param> + <param> + <name>Axis Labels</name> + <key>axislabels</key> + <value>True</value> + <type>enum</type> + <hide>part</hide> + <option> + <name>Yes</name> + <key>True</key> + </option> + <option> + <name>No</name> + <key>False</key> + </option> + <tab>Config</tab> + </param> <param> <name>Line 1 Label</name> diff --git a/gr-qtgui/grc/qtgui_time_sink_x.xml b/gr-qtgui/grc/qtgui_time_sink_x.xml index b17b55fc48..c0584b6d34 100644 --- a/gr-qtgui/grc/qtgui_time_sink_x.xml +++ b/gr-qtgui/grc/qtgui_time_sink_x.xml @@ -27,6 +27,7 @@ self.$(id).enable_tags(-1, $entags) self.$(id).set_trigger_mode($tr_mode, $tr_slope, $tr_level, $tr_delay, $tr_chan, $tr_tag) self.$(id).enable_autoscale($autoscale) self.$(id).enable_grid($grid) +self.$(id).enable_axis_labels($axislabels) self.$(id).enable_control_panel($ctrlpanel) if not $legend: @@ -363,6 +364,23 @@ $(gui_hint()($win))</make> </param> <param> + <name>Axis Labels</name> + <key>axislabels</key> + <value>True</value> + <type>enum</type> + <hide>part</hide> + <option> + <name>Yes</name> + <key>True</key> + </option> + <option> + <name>No</name> + <key>False</key> + </option> + <tab>Config</tab> + </param> + + <param> <name>Line 1 Label</name> <key>label1</key> <type>string</type> diff --git a/gr-qtgui/grc/qtgui_waterfall_sink_x.xml b/gr-qtgui/grc/qtgui_waterfall_sink_x.xml index 1d8461b8c4..cdecd5cce1 100644 --- a/gr-qtgui/grc/qtgui_waterfall_sink_x.xml +++ b/gr-qtgui/grc/qtgui_waterfall_sink_x.xml @@ -22,6 +22,7 @@ qtgui.$(type.fcn)( ) self.$(id).set_update_time($update_time) self.$(id).enable_grid($grid) +self.$(id).enable_axis_labels($axislabels) if not $legend: self.$(id).disable_legend() @@ -271,6 +272,23 @@ $(gui_hint()($win))</make> </param> <param> + <name>Axis Labels</name> + <key>axislabels</key> + <value>True</value> + <type>enum</type> + <hide>part</hide> + <option> + <name>Yes</name> + <key>True</key> + </option> + <option> + <name>No</name> + <key>False</key> + </option> + <tab>Config</tab> + </param> + + <param> <name>Line 1 Color</name> <key>color1</key> <type>enum</type> diff --git a/gr-qtgui/include/gnuradio/qtgui/CMakeLists.txt b/gr-qtgui/include/gnuradio/qtgui/CMakeLists.txt index 1e0c9f35f2..5d22f112af 100644 --- a/gr-qtgui/include/gnuradio/qtgui/CMakeLists.txt +++ b/gr-qtgui/include/gnuradio/qtgui/CMakeLists.txt @@ -38,6 +38,7 @@ install(FILES histogram_sink_f.h histogramdisplayform.h HistogramDisplayPlot.h + edit_box_msg.h number_sink.h numberdisplayform.h plot_raster.h diff --git a/gr-qtgui/include/gnuradio/qtgui/DisplayPlot.h b/gr-qtgui/include/gnuradio/qtgui/DisplayPlot.h index 9edff7b183..eba12e2635 100644 --- a/gr-qtgui/include/gnuradio/qtgui/DisplayPlot.h +++ b/gr-qtgui/include/gnuradio/qtgui/DisplayPlot.h @@ -190,6 +190,7 @@ public: public slots: virtual void disableLegend(); + virtual void setAxisLabels(bool en); virtual void setYaxis(double min, double max); virtual void setXaxis(double min, double max); virtual void setLineLabel(int which, QString label); diff --git a/gr-qtgui/include/gnuradio/qtgui/FrequencyDisplayPlot.h b/gr-qtgui/include/gnuradio/qtgui/FrequencyDisplayPlot.h index 8b2ef457fb..5c6b2c71b0 100644 --- a/gr-qtgui/include/gnuradio/qtgui/FrequencyDisplayPlot.h +++ b/gr-qtgui/include/gnuradio/qtgui/FrequencyDisplayPlot.h @@ -118,6 +118,9 @@ public slots: void setPlotPosHalf(bool half); + void setYLabel(const std::string &label, + const std::string &unit); + void clearMaxData(); void clearMinData(); diff --git a/gr-qtgui/include/gnuradio/qtgui/const_sink_c.h b/gr-qtgui/include/gnuradio/qtgui/const_sink_c.h index 851f3be7ca..9905eb7690 100644 --- a/gr-qtgui/include/gnuradio/qtgui/const_sink_c.h +++ b/gr-qtgui/include/gnuradio/qtgui/const_sink_c.h @@ -144,6 +144,7 @@ namespace gr { virtual void enable_menu(bool en=true) = 0; virtual void enable_autoscale(bool en) = 0; virtual void enable_grid(bool en) = 0; + virtual void enable_axis_labels(bool en=true) = 0; virtual void disable_legend() = 0; virtual int nsamps() const = 0; virtual void reset() = 0; diff --git a/gr-qtgui/include/gnuradio/qtgui/displayform.h b/gr-qtgui/include/gnuradio/qtgui/displayform.h index 2ef4b801cf..1da1383370 100644 --- a/gr-qtgui/include/gnuradio/qtgui/displayform.h +++ b/gr-qtgui/include/gnuradio/qtgui/displayform.h @@ -83,6 +83,7 @@ public slots: void setStop(); void setGrid(bool on); + void setAxisLabels(bool en); void saveFigure(); @@ -117,6 +118,8 @@ protected: bool d_stop_state; QAction *d_grid_act; bool d_grid_state; + QAction *d_axislabelsmenu; + bool d_axislabels; QAction *d_autoscale_act; bool d_autoscale_state; diff --git a/gr-qtgui/include/gnuradio/qtgui/edit_box_msg.h b/gr-qtgui/include/gnuradio/qtgui/edit_box_msg.h new file mode 100644 index 0000000000..2976cfb2be --- /dev/null +++ b/gr-qtgui/include/gnuradio/qtgui/edit_box_msg.h @@ -0,0 +1,151 @@ +/* -*- c++ -*- */ +/* + * Copyright 2016 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_QTGUI_EDIT_BOX_MSG_H +#define INCLUDED_QTGUI_EDIT_BOX_MSG_H + +#ifdef ENABLE_PYTHON +#include <Python.h> +#endif + +#include <gnuradio/qtgui/qtgui_types.h> +#include <gnuradio/qtgui/api.h> +#include <gnuradio/block.h> +#include <qapplication.h> + +namespace gr { + namespace qtgui { + + /*! + * \brief Create a QT Edit Box widget where the value is posted as a message. + * \ingroup qtgui_blk + * + * \details + * This block creates a QT Edit Box widget that manages data + * through message passing interfaces. The 'msg' output port + * produces messages based on the text in the edit box and the + * data type set by the \p type argument (see + * gr::qtgui::data_type_t). The data types are checked, and WARN + * log messages are produced when the data is in the wrong + * format. Not all errors are explicitly checked for, only that + * conversions happen correctly. Failures are either produces as + * log messages or the action is simply silently dropped. + * + * The value of the edit boxes can be programmatically updated + * through the 'val' input message port. It is also checked for + * the correct data type. + * + * The \p is_pair argument to the constructor determines if the + * edit box handles a key:value pair. If set to True, two edit + * boxes are created with the left for the key and right for the + * value. The key is always assumed to be a string and the value + * is restricted by the data type setting as above. + * + * The block can take a default value. Because the block is + * capable of handling multiple different data types, we enter the + * default value as a string in the same way we expect the user to + * enter it into the Value edit box of the widget. We convert this + * default exactly the same way we convert the user-entered text + * in the edit box. See the next paragraph for an explanation for + * how to handle complex numbers. + * + * Complex numbers are currently handled a bit differently than + * expected. Because we use the Boost lexical_cast function, + * complex numbers MUST be in the form "(a,b)" to represent "a + + * jb". Note that you cannot even have a space after the comma, so + * "(1.23,10.56)" is correct while "(1.23, 10.56)" will not parse. + * + * The 'static' mode prevents the user from changing the data type + * or the key used in the widget. If also in 'pair' mode, the key + * is not displayed and so must be set in the constructor. It is + * an error if using static and pair modes with no default key + * set. + * + * Message Ports: + * + * - msg (output): + * Produces a PMT message from the data in the edit box. It + * is packaged in the PMT container determined by the \p + * type argument to the ctor. If the data in the box is not of + * the correct type and the conversion fails, the block + * produces a log WARN message but does nothing else with + * the data. If the \p is_pair flag is set on this block, it + * will produce a PMT pair object where the key (car) is + * assumed to be a string and the value (cdr) is determined + * by \p type. + * + * - val (input): + * Accepts messages to update the value in the edit + * boxes. The messages, as PMTs, are first checked to make + * sure that they are the correct type (integer, float, + * string, or complex), and unpacks them and converts them + * to QStrings to display in the edit box. When using \p + * is_pair, the PMT is checked to make sure it is a PMT + * pair. Then the key (car) is extracted as a string before + * the value (cdr) is processed based on the set data type + * of the box. + */ + class QTGUI_API edit_box_msg : virtual public block + { + public: + // gr::qtgui::edit_box_msg::sptr + typedef boost::shared_ptr<edit_box_msg> sptr; + + /*! + * \brief Constructs the Edit box block. + * + * \param type the data type of data in the value box. + * \param value the default value of the message. This is + * entered as a string regardless of the type and + * converted internally -- much like how the block + * extracts the value from the edit box and converts it. + * \param label a label to identify the box on screen. + * \param is_pair if we are using a key:value pair. + * \param is_static sets the key edit box as a static text box + * (cannot be edited live). + * \param key Set the key used in a key:value pair message. + * \param parent a QWidget parent in the QT app. + */ + static sptr make(gr::qtgui::data_type_t type, + const std::string &value="", + const std::string &label="", + bool is_pair=true, + bool is_static=true, + const std::string &key="", + QWidget *parent=NULL); + + virtual void exec_() = 0; + virtual QWidget* qwidget() = 0; + +#ifdef ENABLE_PYTHON + virtual PyObject* pyqwidget() = 0; +#else + virtual void* pyqwidget() = 0; +#endif + + QApplication *d_qApplication; + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_EDIT_BOX_MSG_H */ diff --git a/gr-qtgui/include/gnuradio/qtgui/freq_sink_c.h b/gr-qtgui/include/gnuradio/qtgui/freq_sink_c.h index afb836ccf3..f2fc571acb 100644 --- a/gr-qtgui/include/gnuradio/qtgui/freq_sink_c.h +++ b/gr-qtgui/include/gnuradio/qtgui/freq_sink_c.h @@ -128,6 +128,7 @@ namespace gr { virtual void set_update_time(double t) = 0; virtual void set_title(const std::string &title) = 0; + virtual void set_y_label(const std::string &label, const std::string &unit) = 0; virtual void set_line_label(int which, const std::string &label) = 0; virtual void set_line_color(int which, const std::string &color) = 0; virtual void set_line_width(int which, int width) = 0; @@ -182,6 +183,7 @@ namespace gr { virtual void clear_min_hold() = 0; virtual void disable_legend() = 0; virtual void reset() = 0; + virtual void enable_axis_labels(bool en=true) = 0; QApplication *d_qApplication; }; diff --git a/gr-qtgui/include/gnuradio/qtgui/freq_sink_f.h b/gr-qtgui/include/gnuradio/qtgui/freq_sink_f.h index 01f3cc4ae8..f13653731d 100644 --- a/gr-qtgui/include/gnuradio/qtgui/freq_sink_f.h +++ b/gr-qtgui/include/gnuradio/qtgui/freq_sink_f.h @@ -126,7 +126,9 @@ namespace gr { virtual void set_y_axis(double min, double max) = 0; virtual void set_update_time(double t) = 0; + virtual void set_title(const std::string &title) = 0; + virtual void set_y_label(const std::string &label, const std::string &unit) = 0; virtual void set_line_label(int which, const std::string &label) = 0; virtual void set_line_color(int which, const std::string &color) = 0; virtual void set_line_width(int which, int width) = 0; @@ -188,6 +190,7 @@ namespace gr { virtual void clear_min_hold() = 0; virtual void disable_legend() = 0; virtual void reset() = 0; + virtual void enable_axis_labels(bool en=true) = 0; QApplication *d_qApplication; }; diff --git a/gr-qtgui/include/gnuradio/qtgui/freqcontrolpanel.h b/gr-qtgui/include/gnuradio/qtgui/freqcontrolpanel.h index ce0aeedf9f..ec3ef6cfd1 100644 --- a/gr-qtgui/include/gnuradio/qtgui/freqcontrolpanel.h +++ b/gr-qtgui/include/gnuradio/qtgui/freqcontrolpanel.h @@ -76,6 +76,7 @@ private: QVBoxLayout *d_axes_layout; QCheckBox *d_grid_check; + QCheckBox *d_axislabels_check; QHBoxLayout *d_yrange_layout; QLabel *d_yrange_label; QPushButton *d_yrange_plus; diff --git a/gr-qtgui/include/gnuradio/qtgui/freqdisplayform.h b/gr-qtgui/include/gnuradio/qtgui/freqdisplayform.h index e7b95cb30e..4d8ed3e2a1 100644 --- a/gr-qtgui/include/gnuradio/qtgui/freqdisplayform.h +++ b/gr-qtgui/include/gnuradio/qtgui/freqdisplayform.h @@ -75,6 +75,8 @@ public slots: void setFrequencyRange(const double centerfreq, const double bandwidth); void setYaxis(double min, double max); + void setYLabel(const std::string &label, + const std::string &unit=""); void setYMax(const QString &m); void setYMin(const QString &m); void autoScale(bool en); @@ -108,7 +110,6 @@ public slots: void notifyTriggerLevelPlus(); void notifyTriggerLevelMinus(); - signals: void signalFFTSize(int size); void signalFFTWindow(gr::filter::firdes::win_type win); diff --git a/gr-qtgui/include/gnuradio/qtgui/histogram_sink_f.h b/gr-qtgui/include/gnuradio/qtgui/histogram_sink_f.h index 431941a414..6e7dee005d 100644 --- a/gr-qtgui/include/gnuradio/qtgui/histogram_sink_f.h +++ b/gr-qtgui/include/gnuradio/qtgui/histogram_sink_f.h @@ -121,6 +121,7 @@ namespace gr { virtual void enable_semilogx(bool en=true) = 0; virtual void enable_semilogy(bool en=true) = 0; virtual void enable_accumulate(bool en=true) = 0; + virtual void enable_axis_labels(bool en=true) = 0; virtual void autoscalex() = 0; virtual int nsamps() const = 0; virtual int bins() const = 0; diff --git a/gr-qtgui/include/gnuradio/qtgui/qtgui_types.h b/gr-qtgui/include/gnuradio/qtgui/qtgui_types.h index e55746c48c..ae7d081013 100644 --- a/gr-qtgui/include/gnuradio/qtgui/qtgui_types.h +++ b/gr-qtgui/include/gnuradio/qtgui/qtgui_types.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2012 Free Software Foundation, Inc. + * Copyright 2012,2016 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -27,6 +27,24 @@ #include <qwt_scale_draw.h> #include <gnuradio/high_res_timer.h> +namespace gr { + namespace qtgui { + + enum data_type_t { + INT = 0, + FLOAT, + DOUBLE, + COMPLEX, + STRING, + INT_VEC, + FLOAT_VEC, + DOUBLE_VEC, + COMPLEX_VEC, + }; + + } /* namespace qtgui */ +} /* namespace gr */ + class FreqOffsetAndPrecisionClass { public: diff --git a/gr-qtgui/include/gnuradio/qtgui/time_raster_sink_b.h b/gr-qtgui/include/gnuradio/qtgui/time_raster_sink_b.h index ed55e2ed50..efaef1cc4b 100644 --- a/gr-qtgui/include/gnuradio/qtgui/time_raster_sink_b.h +++ b/gr-qtgui/include/gnuradio/qtgui/time_raster_sink_b.h @@ -131,6 +131,7 @@ namespace gr { virtual void enable_menu(bool en) = 0; virtual void enable_grid(bool en) = 0; virtual void enable_autoscale(bool en) = 0; + virtual void enable_axis_labels(bool en=true) = 0; virtual void reset() = 0; QApplication *d_qApplication; diff --git a/gr-qtgui/include/gnuradio/qtgui/time_raster_sink_f.h b/gr-qtgui/include/gnuradio/qtgui/time_raster_sink_f.h index 5610dabdda..ae2ec8d11b 100644 --- a/gr-qtgui/include/gnuradio/qtgui/time_raster_sink_f.h +++ b/gr-qtgui/include/gnuradio/qtgui/time_raster_sink_f.h @@ -127,6 +127,7 @@ namespace gr { virtual void enable_menu(bool en) = 0; virtual void enable_grid(bool en) = 0; virtual void enable_autoscale(bool en) = 0; + virtual void enable_axis_labels(bool en=true) = 0; virtual void reset() = 0; QApplication *d_qApplication; diff --git a/gr-qtgui/include/gnuradio/qtgui/time_sink_c.h b/gr-qtgui/include/gnuradio/qtgui/time_sink_c.h index 10c87c8bf2..af921afc1b 100644 --- a/gr-qtgui/include/gnuradio/qtgui/time_sink_c.h +++ b/gr-qtgui/include/gnuradio/qtgui/time_sink_c.h @@ -170,6 +170,7 @@ namespace gr { virtual void enable_semilogy(bool en=true) = 0; virtual void enable_control_panel(bool en=true) = 0; virtual void enable_tags(int which, bool en) = 0; + virtual void enable_axis_labels(bool en=true) = 0; virtual void disable_legend() = 0; virtual int nsamps() const = 0; diff --git a/gr-qtgui/include/gnuradio/qtgui/time_sink_f.h b/gr-qtgui/include/gnuradio/qtgui/time_sink_f.h index d96383c0e8..7ddca46297 100644 --- a/gr-qtgui/include/gnuradio/qtgui/time_sink_f.h +++ b/gr-qtgui/include/gnuradio/qtgui/time_sink_f.h @@ -160,6 +160,7 @@ namespace gr { virtual void enable_semilogy(bool en=true) = 0; virtual void enable_control_panel(bool en=true) = 0; virtual void enable_tags(int which, bool en) = 0; + virtual void enable_axis_labels(bool en=true) = 0; virtual void disable_legend() = 0; virtual int nsamps() const = 0; diff --git a/gr-qtgui/include/gnuradio/qtgui/timecontrolpanel.h b/gr-qtgui/include/gnuradio/qtgui/timecontrolpanel.h index aabd890e6e..d7cbf5431b 100644 --- a/gr-qtgui/include/gnuradio/qtgui/timecontrolpanel.h +++ b/gr-qtgui/include/gnuradio/qtgui/timecontrolpanel.h @@ -75,6 +75,8 @@ private: QCheckBox *d_autoscale_check; QCheckBox *d_grid_check; + QCheckBox *d_axislabels_check; + QPushButton *d_yoff_plus, *d_yoff_minus; QPushButton *d_yrange_plus, *d_yrange_minus; QPushButton *d_xmax_plus, *d_xmax_minus; diff --git a/gr-qtgui/include/gnuradio/qtgui/waterfall_sink_c.h b/gr-qtgui/include/gnuradio/qtgui/waterfall_sink_c.h index 16fa9c5c2d..c0bdf1623b 100644 --- a/gr-qtgui/include/gnuradio/qtgui/waterfall_sink_c.h +++ b/gr-qtgui/include/gnuradio/qtgui/waterfall_sink_c.h @@ -156,6 +156,7 @@ namespace gr { virtual void enable_menu(bool en=true) = 0; virtual void enable_grid(bool en=true) = 0; virtual void disable_legend() = 0; + virtual void enable_axis_labels(bool en=true) = 0; QApplication *d_qApplication; }; diff --git a/gr-qtgui/include/gnuradio/qtgui/waterfall_sink_f.h b/gr-qtgui/include/gnuradio/qtgui/waterfall_sink_f.h index dff2b29d6e..d38cb2cae7 100644 --- a/gr-qtgui/include/gnuradio/qtgui/waterfall_sink_f.h +++ b/gr-qtgui/include/gnuradio/qtgui/waterfall_sink_f.h @@ -164,6 +164,7 @@ namespace gr { virtual void enable_menu(bool en=true) = 0; virtual void enable_grid(bool en=true) = 0; virtual void disable_legend() = 0; + virtual void enable_axis_labels(bool en=true) = 0; QApplication *d_qApplication; }; diff --git a/gr-qtgui/lib/CMakeLists.txt b/gr-qtgui/lib/CMakeLists.txt index 0ac5a599d0..b1ee580398 100644 --- a/gr-qtgui/lib/CMakeLists.txt +++ b/gr-qtgui/lib/CMakeLists.txt @@ -43,6 +43,7 @@ set(qtgui_moc_hdrs ${qtgui_mod_includedir}/ConstellationDisplayPlot.h ${qtgui_mod_includedir}/HistogramDisplayPlot.h ${qtgui_mod_includedir}/VectorDisplayPlot.h + edit_box_msg_impl.h ) QT4_WRAP_CPP(qtgui_moc_sources ${qtgui_moc_hdrs}) QT4_WRAP_UI(qtgui_ui_hdrs spectrumdisplayform.ui) @@ -99,6 +100,7 @@ set(qtgui_sources ber_sink_b_impl.cc vectordisplayform.cc vector_sink_f_impl.cc + edit_box_msg_impl.cc ) #Add Windows DLL resource file if using MSVC diff --git a/gr-qtgui/lib/DisplayPlot.cc b/gr-qtgui/lib/DisplayPlot.cc index 30fd837772..59384c88f6 100644 --- a/gr-qtgui/lib/DisplayPlot.cc +++ b/gr-qtgui/lib/DisplayPlot.cc @@ -443,3 +443,12 @@ DisplayPlot::onPickerPointSelected6(const QPointF & p) //fprintf(stderr,"onPickerPointSelected %f %f\n", point.x(), point.y()); emit plotPointSelected(point); } + +void +DisplayPlot::setAxisLabels(bool en) +{ + enableAxis(0,en); + enableAxis(2,en); +} + + diff --git a/gr-qtgui/lib/FrequencyDisplayPlot.cc b/gr-qtgui/lib/FrequencyDisplayPlot.cc index aef975d332..233c786d0a 100644 --- a/gr-qtgui/lib/FrequencyDisplayPlot.cc +++ b/gr-qtgui/lib/FrequencyDisplayPlot.cc @@ -97,7 +97,7 @@ FrequencyDisplayPlot::FrequencyDisplayPlot(int nplots, QWidget* parent) d_ymax = 10; setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine); setAxisScale(QwtPlot::yLeft, d_ymin, d_ymax); - setAxisTitle(QwtPlot::yLeft, "Power (dB)"); + setAxisTitle(QwtPlot::yLeft, "Relative Gain (dB)"); QList<QColor> default_colors; default_colors << QColor(Qt::blue) << QColor(Qt::red) << QColor(Qt::green) @@ -600,6 +600,17 @@ FrequencyDisplayPlot::onPickerPointSelected6(const QPointF & p) } void +FrequencyDisplayPlot::setYLabel(const std::string &label, + const std::string &unit) +{ + std::string l = label; + if(unit.length() > 0) + l += " (" + unit + ")"; + setAxisTitle(QwtPlot::yLeft, QString(l.c_str())); + ((FreqDisplayZoomer*)d_zoomer)->setUnitType(unit); +} + +void FrequencyDisplayPlot::setMinFFTColor (QColor c) { d_min_fft_color = c; diff --git a/gr-qtgui/lib/WaterfallDisplayPlot.cc b/gr-qtgui/lib/WaterfallDisplayPlot.cc index 676e4077db..b16c32bf9d 100644 --- a/gr-qtgui/lib/WaterfallDisplayPlot.cc +++ b/gr-qtgui/lib/WaterfallDisplayPlot.cc @@ -657,8 +657,6 @@ WaterfallDisplayPlot::_updateIntensityRangeDisplay() setAxisScale(QwtPlot::yRight, intv.minValue(), intv.maxValue()); #endif - enableAxis(d_legend_enabled); - plotLayout()->setAlignCanvasToScales(true); // Tell the display to redraw everything diff --git a/gr-qtgui/lib/const_sink_c_impl.cc b/gr-qtgui/lib/const_sink_c_impl.cc index 7ef8db393d..eaaf5f570a 100644 --- a/gr-qtgui/lib/const_sink_c_impl.cc +++ b/gr-qtgui/lib/const_sink_c_impl.cc @@ -375,6 +375,12 @@ namespace gr { } void + const_sink_c_impl::enable_axis_labels(bool en) + { + d_main_gui->setAxisLabels(en); + } + + void const_sink_c_impl::disable_legend() { d_main_gui->disableLegend(); diff --git a/gr-qtgui/lib/const_sink_c_impl.h b/gr-qtgui/lib/const_sink_c_impl.h index 7d52c3ae15..f2dcb6e73e 100644 --- a/gr-qtgui/lib/const_sink_c_impl.h +++ b/gr-qtgui/lib/const_sink_c_impl.h @@ -118,6 +118,7 @@ namespace gr { void enable_menu(bool en); void enable_autoscale(bool en); void enable_grid(bool en); + void enable_axis_labels(bool en); void disable_legend(); void reset(); diff --git a/gr-qtgui/lib/displayform.cc b/gr-qtgui/lib/displayform.cc index 27a09512fc..13c2d8a193 100644 --- a/gr-qtgui/lib/displayform.cc +++ b/gr-qtgui/lib/displayform.cc @@ -29,6 +29,7 @@ DisplayForm::DisplayForm(int nplots, QWidget* parent) : QWidget(parent), d_nplots(nplots), d_system_specified_flag(false) { d_isclosed = false; + d_axislabels = true; // Set the initial plot size resize(QSize(800, 600)); @@ -53,11 +54,18 @@ DisplayForm::DisplayForm(int nplots, QWidget* parent) this, SLOT(setGrid(bool))); d_grid_state = false; + d_axislabelsmenu = new QAction("Axis Labels", this); + d_axislabelsmenu->setCheckable(true); + d_axislabelsmenu->setStatusTip(tr("Toggle Axis Labels on/off")); + connect(d_axislabelsmenu, SIGNAL(triggered(bool)), + this, SLOT(setAxisLabels(bool))); + // Create a pop-up menu for manipulating the figure d_menu_on = true; d_menu = new QMenu(this); d_menu->addAction(d_stop_act); d_menu->addAction(d_grid_act); + d_menu->addAction(d_axislabelsmenu); for(int i = 0; i < d_nplots; i++) { d_line_title_act.push_back(new LineTitleAction(i, this)); @@ -339,6 +347,14 @@ DisplayForm::setGrid(bool on) } void +DisplayForm::setAxisLabels(bool en) +{ + d_axislabels = en; + d_axislabelsmenu->setChecked(en); + getPlot()->setAxisLabels(d_axislabels); +} + +void DisplayForm::saveFigure() { QPixmap qpix = QPixmap::grabWidget(this); diff --git a/gr-qtgui/lib/edit_box_msg_impl.cc b/gr-qtgui/lib/edit_box_msg_impl.cc new file mode 100644 index 0000000000..e0c5f64b3d --- /dev/null +++ b/gr-qtgui/lib/edit_box_msg_impl.cc @@ -0,0 +1,571 @@ +/* -*- c++ -*- */ +/* + * Copyright 2016 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 "edit_box_msg_impl.h" +#include <gnuradio/io_signature.h> +#include <gnuradio/prefs.h> +#include <gnuradio/qtgui/utils.h> +#include <boost/lexical_cast.hpp> + +namespace gr { + namespace qtgui { + + edit_box_msg::sptr + edit_box_msg::make(data_type_t type, const std::string &label, + const std::string &value, + bool is_pair, bool is_static, + const std::string &key, QWidget* parent) + { + return gnuradio::get_initial_sptr + (new edit_box_msg_impl(type, value, label, is_pair, + is_static, key, parent)); + } + + edit_box_msg_impl::edit_box_msg_impl(data_type_t type, const std::string &label, + const std::string &value, + bool is_pair, bool is_static, + const std::string &key, QWidget* parent) + : block("edit_box_msg", + io_signature::make(0, 0, 0), + io_signature::make(0, 0, 0)), + QObject(parent) + { + // Required now for Qt; argc must be greater than 0 and argv + // must have at least one valid character. Must be valid through + // life of the qApplication: + // http://harmattan-dev.nokia.com/docs/library/html/qt4/qapplication.html + d_argc = 1; + d_argv = new char; + d_argv[0] = '\0'; + + if(qApp != NULL) { + d_qApplication = qApp; + } + else { +#if QT_VERSION >= 0x040500 + std::string style = prefs::singleton()->get_string("qtgui", "style", "raster"); + QApplication::setGraphicsSystem(QString(style.c_str())); +#endif + d_qApplication = new QApplication(d_argc, &d_argv); + } + + // If a style sheet is set in the prefs file, enable it here. + std::string qssfile = prefs::singleton()->get_string("qtgui","qss",""); + if(qssfile.size() > 0) { + QString sstext = get_qt_style_sheet(QString(qssfile.c_str())); + d_qApplication->setStyleSheet(sstext); + } + + d_is_pair = is_pair; + d_is_static = is_static; + + d_val = new QLineEdit(); + d_val->setObjectName("qtgui_editboxmsg_val"); // used to set background color + d_val->setText(QString(value.c_str())); + + set_type(type); + + d_group = new QGroupBox(); + d_vlayout = new QVBoxLayout(parent); + d_hlayout = new QHBoxLayout(parent); + + if(d_is_pair) { + d_key = new QLineEdit(); + + QString key_text = QString(key.c_str()); + d_key->setText(key_text); + + // If static, we create the d_key object, which we use later + // to be consistent about getting the key string. But we do + // not add it to the layout. + if(d_is_static) { + d_key->setEnabled(false); + + QFontMetrics fm = d_key->fontMetrics(); + int width = 15 + fm.width(key_text); + + d_key->setFixedWidth(width); + + // Verify that a default key has been set or emit an error + if(key.size() == 0) { + throw std::runtime_error("When using static + pair mode, please set a default key."); + } + } + else { + // Adding it to the layout if in non-static mode so users + // can see and update the key. + d_hlayout->addWidget(d_key); + } + } + + d_label = NULL; + if(label != "") { + d_label = new QLabel(QString(label.c_str())); + d_vlayout->addWidget(d_label); + } + + d_hlayout->addWidget(d_val); + + if(!d_is_static) { + // If not static, we can change the key and the data type of + // the value box. + d_type_box = new QComboBox(); + d_type_box->setEditable(false); + + // Items listed in order of enum data_type_t + d_type_box->addItem("Int"); + d_type_box->addItem("Float"); + d_type_box->addItem("Double"); + d_type_box->addItem("Complex"); + d_type_box->addItem("String"); + d_type_box->addItem("Int (vec)"); + d_type_box->addItem("Float (vec)"); + d_type_box->addItem("Double (vec)"); + d_type_box->addItem("Complex (vec)"); + d_type_box->setCurrentIndex(d_type); + d_hlayout->addWidget(d_type_box); + + QObject::connect(d_type_box, SIGNAL(currentIndexChanged(int)), + this, SLOT(set_type(int))); + } + + d_vlayout->addItem(d_hlayout); + d_group->setLayout(d_vlayout); + + QObject::connect(d_val, SIGNAL(editingFinished()), + this, SLOT(edit_finished())); + + d_msg = pmt::PMT_NIL; + + message_port_register_out(pmt::mp("msg")); + message_port_register_in(pmt::mp("val")); + + set_msg_handler(pmt::mp("val"), + boost::bind(&edit_box_msg_impl::set_value, this, _1)); + } + + edit_box_msg_impl::~edit_box_msg_impl() + { + delete d_argv; + delete d_group; + delete d_hlayout; + delete d_vlayout; + delete d_val; + if(d_is_pair) + delete d_key; + if(d_label) + delete d_label; + } + + bool + edit_box_msg_impl::start() + { + QString text = d_val->text(); + if(!text.isEmpty()) { + edit_finished(); + } + + return block::start(); + } + + void + edit_box_msg_impl::exec_() + { + d_qApplication->exec(); + } + + QWidget* + edit_box_msg_impl::qwidget() + { + return (QWidget*)d_group; + } + +#ifdef ENABLE_PYTHON + PyObject* + edit_box_msg_impl::pyqwidget() + { + PyObject *w = PyLong_FromVoidPtr((void*)d_group); + PyObject *retarg = Py_BuildValue("N", w); + return retarg; + } +#else + void * + edit_box_msg_impl::pyqwidget() + { + return NULL; + } +#endif + + void + edit_box_msg_impl::set_type(int type) + { + set_type(static_cast<data_type_t>(type)); + } + + void + edit_box_msg_impl::set_type(gr::qtgui::data_type_t type) + { + d_type = type; + + switch(d_type) { + case INT: + case INT_VEC: + d_val->setStyleSheet("QLineEdit#qtgui_editboxmsg_val {background-color: #4CAF50;}"); + break; + case FLOAT: + case FLOAT_VEC: + d_val->setStyleSheet("QLineEdit#qtgui_editboxmsg_val {background-color: #F57C00;}"); + break; + case DOUBLE: + case DOUBLE_VEC: + d_val->setStyleSheet("QLineEdit#qtgui_editboxmsg_val {background-color: #00BCD4;}"); + break; + case COMPLEX: + case COMPLEX_VEC: + d_val->setStyleSheet("QLineEdit#qtgui_editboxmsg_val {background-color: #2196F3;}"); + break; + case STRING: + d_val->setStyleSheet("QLineEdit#qtgui_editboxmsg_val {background-color: #FFFFFF; color: #000000;}"); + break; + } + } + + void + edit_box_msg_impl::set_value(pmt::pmt_t val) + { + // If the contents of the new value are the same as we already + // had, don't update anything, just exit and move on. + if(pmt::eqv(val, d_msg)) { + return; + } + + int xi; + float xf; + double xd; + std::string xs; + gr_complex xc; + + d_msg = val; + + // Only update key if we're expecting a pair + if(d_is_pair) { + // If we are, make sure that the PMT is actually a pair + if(pmt::is_pair(val)) { + pmt::pmt_t key = pmt::car(val); + std::string skey = pmt::symbol_to_string(key); + + // If static, check to make sure that the key of the + // incoming message matches our key. If it doesn't, emit a + // warning and exit without changing anything. + if(d_is_static) { + std::string cur_key = d_key->text().toStdString(); + if(skey != cur_key) { + GR_LOG_WARN(d_logger, boost::format("Got key '%1%' but expected '%2%'") \ + % skey % cur_key); + return; + } + } + val = pmt::cdr(val); + d_key->setText(QString(skey.c_str())); + } + else { + GR_LOG_WARN(d_logger, "Did not find PMT pair"); + return; + } + } + + switch(d_type) { + case INT: + if(pmt::is_integer(val)) { + xi = pmt::to_long(val); + d_val->setText(QString::number(xi)); + } + else { + GR_LOG_WARN(d_logger, "Conversion from integer failed"); + return; + } + break; + case INT_VEC: + if(pmt::is_s32vector(val)) { + QStringList text_list; + const std::vector<int32_t> xv = pmt::s32vector_elements(val); + for(size_t i = 0; i < xv.size(); i++) { + text_list.append(QString::number(xv[i])); + } + d_val->setText(text_list.join(", ")); + } + else { + GR_LOG_WARN(d_logger, "Conversion from integer vector failed"); + return; + } + break; + case FLOAT: + if(pmt::is_real(val)) { + xf = pmt::to_float(val); + d_val->setText(QString::number(xf)); + } + else { + GR_LOG_WARN(d_logger, "Conversion from float failed"); + return; + } + break; + case FLOAT_VEC: + if(pmt::is_f32vector(val)) { + QStringList text_list; + const std::vector<float> xv = pmt::f32vector_elements(val); + for(size_t i = 0; i < xv.size(); i++) { + text_list.append(QString::number(xv[i])); + } + d_val->setText(text_list.join(", ")); + } + else { + GR_LOG_WARN(d_logger, "Conversion from float vector failed"); + return; + } + break; + case DOUBLE: + if(pmt::is_real(val)) { + xd = pmt::to_double(val); + d_val->setText(QString::number(xd)); + } + else { + GR_LOG_WARN(d_logger, "Conversion from double failed"); + return; + } + break; + case DOUBLE_VEC: + if(pmt::is_f64vector(val)) { + QStringList text_list; + const std::vector<double> xv = pmt::f64vector_elements(val); + for(size_t i = 0; i < xv.size(); i++) { + text_list.append(QString::number(xv[i])); + } + d_val->setText(text_list.join(", ")); + } + else { + GR_LOG_WARN(d_logger, "Conversion from double vector failed"); + return; + } + break; + case COMPLEX: + if(pmt::is_complex(val)) { + xc = pmt::to_complex(val); + d_val->setText(QString("(%1,%2)").arg(xc.real()).arg(xc.imag())); + } + else { + GR_LOG_WARN(d_logger, "Conversion from complex failed"); + return; + } + break; + case COMPLEX_VEC: + if(pmt::is_c32vector(val)) { + QStringList text_list; + const std::vector<gr_complex> xv = pmt::c32vector_elements(val); + for(size_t i = 0; i < xv.size(); i++) { + text_list.append(QString("(%1,%2)").arg(xv[i].real()).arg(xv[i].imag())); + } + d_val->setText(text_list.join(", ")); + } + else { + GR_LOG_WARN(d_logger, "Conversion from complex vector failed"); + return; + } + break; + case STRING: + if(pmt::is_symbol(val)) { + xs = pmt::symbol_to_string(val); + d_val->setText(QString(xs.c_str())); + } + else { + GR_LOG_WARN(d_logger, "Conversion from string failed"); + return; + } + break; + } + + // Emit the new message to pass updates downstream. + // Loops are prevented by the early exit if d_msg == val. + message_port_pub(pmt::mp("msg"), d_msg); + } + + void + edit_box_msg_impl::edit_finished() + { + QString text = d_val->text(); + bool conv_ok = true; + int xi; + float xf; + double xd; + std::string xs; + gr_complex xc; + + switch(d_type) { + case INT: + xi = text.toInt(&conv_ok); + if(conv_ok) { + d_msg = pmt::from_long(xi); + } + else { + GR_LOG_WARN(d_logger, "Conversion to integer failed"); + return; + } + break; + case INT_VEC: + { + std::vector<int32_t> xv; + QStringList text_list = text.split(","); + for(int i = 0; i < text_list.size(); ++i) { + QString s = text_list.at(i); + s = s.remove(QChar(' ')); + int t = s.toInt(&conv_ok); + if(conv_ok) { + xv.push_back(t); + } + else { + GR_LOG_WARN(d_logger, "Conversion to integer vector failed"); + return; + } + } + d_msg = pmt::init_s32vector(xv.size(), xv); + } + break; + case FLOAT: + xf = text.toFloat(&conv_ok); + if(conv_ok) { + d_msg = pmt::from_float(xf); + } + else { + GR_LOG_WARN(d_logger, "Conversion to float failed"); + return; + } + break; + case FLOAT_VEC: + { + std::vector<float> xv; + QStringList text_list = text.split(","); + for(int i = 0; i < text_list.size(); ++i) { + QString s = text_list.at(i); + s = s.remove(QChar(' ')); + float t = s.toFloat(&conv_ok); + if(conv_ok) { + xv.push_back(t); + } + else { + GR_LOG_WARN(d_logger, "Conversion to float vector failed"); + return; + } + } + d_msg = pmt::init_f32vector(xv.size(), xv); + } + break; + case DOUBLE: + xd = text.toDouble(&conv_ok); + if(conv_ok) { + d_msg = pmt::from_double(xd); + } + else { + GR_LOG_WARN(d_logger, "Conversion to double failed"); + return; + } + break; + case DOUBLE_VEC: + { + std::vector<double> xv; + QStringList text_list = text.split(","); + for(int i = 0; i < text_list.size(); ++i) { + QString s = text_list.at(i); + s = s.remove(QChar(' ')); + double t = s.toDouble(&conv_ok); + if(conv_ok) { + xv.push_back(t); + } + else { + GR_LOG_WARN(d_logger, "Conversion to double vector failed"); + return; + } + } + d_msg = pmt::init_f64vector(xv.size(), xv); + } + break; + case COMPLEX: + try { + xc = boost::lexical_cast<gr_complex>(text.toStdString()); + } + catch(boost::bad_lexical_cast const & e) { + GR_LOG_WARN(d_logger, boost::format("Conversion to complex failed (%1%)") \ + % e.what()); + return; + } + d_msg = pmt::from_complex(xc.real(), xc.imag()); + break; + case COMPLEX_VEC: + { + std::vector<gr_complex> xv; + QStringList text_list = text.split(","); + bool even = false; + gr_complex c; + float re, im; + for(int i = 0; i < text_list.size(); ++i) { + QString s = text_list.at(i); + s = s.remove(QChar(' ')); + s = s.remove(QChar(')')); + s = s.remove(QChar('(')); + float t = s.toFloat(&conv_ok); + if(conv_ok) { + if(even) { + im = t; + xv.push_back(gr_complex(re, im)); + even = false; + } + else { + re = t; + even = true; + } + } + else { + GR_LOG_WARN(d_logger, "Conversion to complex vector failed"); + return; + } + } + d_msg = pmt::init_c32vector(xv.size(), xv); + } + break; + case STRING: + xs = text.toStdString(); + d_msg = pmt::intern(xs); + break; + } + + if(d_is_pair) { + std::string key = d_key->text().toStdString(); + d_msg = pmt::cons(pmt::intern(key), d_msg); + } + + message_port_pub(pmt::mp("msg"), d_msg); + } + + } /* namespace qtgui */ +} /* namespace gr */ diff --git a/gr-qtgui/lib/edit_box_msg_impl.h b/gr-qtgui/lib/edit_box_msg_impl.h new file mode 100644 index 0000000000..c60b3de9b0 --- /dev/null +++ b/gr-qtgui/lib/edit_box_msg_impl.h @@ -0,0 +1,93 @@ +/* -*- c++ -*- */ +/* + * Copyright 2016 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_QTGUI_EDIT_BOX_MSG_IMPL_H +#define INCLUDED_QTGUI_EDIT_BOX_MSG_IMPL_H + +#include <gnuradio/qtgui/edit_box_msg.h> +#include <QGroupBox> +#include <QVBoxLayout> +#include <QHBoxLayout> +#include <QLineEdit> +#include <QComboBox> +#include <QLabel> + +namespace gr { + namespace qtgui { + + class QTGUI_API edit_box_msg_impl + : public QObject, public edit_box_msg + { + Q_OBJECT + + private: + int d_argc; + char *d_argv; + data_type_t d_type; + bool d_is_pair; + bool d_is_static; + + QGroupBox *d_group; + QVBoxLayout *d_vlayout; + QHBoxLayout *d_hlayout; + QLabel *d_label; + QLineEdit *d_val; + QLineEdit *d_key; + QComboBox *d_type_box; + + pmt::pmt_t d_msg; + + public: + edit_box_msg_impl(gr::qtgui::data_type_t type, + const std::string &value="", + const std::string &label="", + bool is_pair=false, + bool is_static=true, + const std::string &key="", + QWidget* parent=0); + ~edit_box_msg_impl(); + + // Overload the start method of gr::block to emit a message if a + // default value is provided. + bool start(); + + void exec_(); + QWidget* qwidget(); + +#ifdef ENABLE_PYTHON + PyObject* pyqwidget(); +#else + void* pyqwidget(); +#endif + + void set_value(pmt::pmt_t val); + + public slots: + void edit_finished(); + void set_type(int); + void set_type(gr::qtgui::data_type_t type); + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_EDIT_BOX_MSG_IMPL_H */ diff --git a/gr-qtgui/lib/freq_sink_c_impl.cc b/gr-qtgui/lib/freq_sink_c_impl.cc index 7dc0b2cf89..517786d58d 100644 --- a/gr-qtgui/lib/freq_sink_c_impl.cc +++ b/gr-qtgui/lib/freq_sink_c_impl.cc @@ -266,6 +266,13 @@ namespace gr { } void + freq_sink_c_impl::set_y_label(const std::string &label, + const std::string &unit) + { + d_main_gui->setYLabel(label, unit); + } + + void freq_sink_c_impl::set_update_time(double t) { //convert update time to ticks @@ -407,6 +414,12 @@ namespace gr { } void + freq_sink_c_impl::enable_axis_labels(bool en) + { + d_main_gui->setAxisLabels(en); + } + + void freq_sink_c_impl::enable_control_panel(bool en) { if(en) diff --git a/gr-qtgui/lib/freq_sink_c_impl.h b/gr-qtgui/lib/freq_sink_c_impl.h index 8da193bf29..b102209359 100644 --- a/gr-qtgui/lib/freq_sink_c_impl.h +++ b/gr-qtgui/lib/freq_sink_c_impl.h @@ -123,6 +123,7 @@ namespace gr { void set_update_time(double t); void set_title(const std::string &title); + void set_y_label(const std::string &label, const std::string &unit); void set_line_label(int which, const std::string &label); void set_line_color(int which, const std::string &color); void set_line_width(int which, int width); @@ -153,6 +154,7 @@ namespace gr { void clear_min_hold(); void disable_legend(); void reset(); + void enable_axis_labels(bool en); int work(int noutput_items, gr_vector_const_void_star &input_items, diff --git a/gr-qtgui/lib/freq_sink_f_impl.cc b/gr-qtgui/lib/freq_sink_f_impl.cc index 5418eca9ec..90624e78eb 100644 --- a/gr-qtgui/lib/freq_sink_f_impl.cc +++ b/gr-qtgui/lib/freq_sink_f_impl.cc @@ -265,6 +265,13 @@ namespace gr { } void + freq_sink_f_impl::set_y_label(const std::string &label, + const std::string &unit) + { + d_main_gui->setYLabel(label, unit); + } + + void freq_sink_f_impl::set_update_time(double t) { //convert update time to ticks @@ -412,6 +419,12 @@ namespace gr { } void + freq_sink_f_impl::enable_axis_labels(bool en) + { + d_main_gui->setAxisLabels(en); + } + + void freq_sink_f_impl::enable_control_panel(bool en) { if(en) diff --git a/gr-qtgui/lib/freq_sink_f_impl.h b/gr-qtgui/lib/freq_sink_f_impl.h index 39e5c92eac..85d45b3f4c 100644 --- a/gr-qtgui/lib/freq_sink_f_impl.h +++ b/gr-qtgui/lib/freq_sink_f_impl.h @@ -122,6 +122,7 @@ namespace gr { void set_update_time(double t); void set_title(const std::string &title); + void set_y_label(const std::string &label, const std::string &unit); void set_line_label(int which, const std::string &label); void set_line_color(int which, const std::string &color); void set_line_width(int which, int width); @@ -153,6 +154,7 @@ namespace gr { void clear_min_hold(); void disable_legend(); void reset(); + void enable_axis_labels(bool en); int work(int noutput_items, gr_vector_const_void_star &input_items, diff --git a/gr-qtgui/lib/freqcontrolpanel.cc b/gr-qtgui/lib/freqcontrolpanel.cc index 5840989949..9729005f50 100644 --- a/gr-qtgui/lib/freqcontrolpanel.cc +++ b/gr-qtgui/lib/freqcontrolpanel.cc @@ -50,6 +50,7 @@ FreqControlPanel::FreqControlPanel(FreqDisplayForm *form) d_axes_layout = new QVBoxLayout; d_grid_check = new QCheckBox("Grid"); + d_axislabels_check = new QCheckBox("Axis Labels"); d_yrange_layout = new QHBoxLayout; d_yrange_label = new QLabel("Y Range:"); d_yrange_plus = new QPushButton("+"); @@ -133,6 +134,7 @@ FreqControlPanel::FreqControlPanel(FreqDisplayForm *form) d_trace_box->setLayout(d_trace_layout); d_axes_layout->addWidget(d_grid_check); + d_axes_layout->addWidget(d_axislabels_check); d_axes_layout->addLayout(d_yrange_layout); d_axes_layout->addLayout(d_ymin_layout); d_axes_layout->addWidget(d_autoscale_button); @@ -171,6 +173,9 @@ FreqControlPanel::FreqControlPanel(FreqDisplayForm *form) connect(d_grid_check, SIGNAL(clicked(bool)), d_parent, SLOT(setGrid(bool))); + connect(d_axislabels_check, SIGNAL(clicked(bool)), + d_parent, SLOT(setAxisLabels(bool))); + connect(d_ymin_plus, SIGNAL(pressed(void)), d_parent, SLOT(notifyYAxisPlus(void))); connect(d_ymin_minus, SIGNAL(pressed(void)), diff --git a/gr-qtgui/lib/freqdisplayform.cc b/gr-qtgui/lib/freqdisplayform.cc index a51da3f35d..141df5475c 100644 --- a/gr-qtgui/lib/freqdisplayform.cc +++ b/gr-qtgui/lib/freqdisplayform.cc @@ -323,6 +323,12 @@ FreqDisplayForm::setYaxis(double min, double max) getPlot()->setYaxis(min, max); } +void FreqDisplayForm::setYLabel(const std::string &label, + const std::string &unit) +{ + getPlot()->setYLabel(label, unit); +} + void FreqDisplayForm::setYMax(const QString &m) { diff --git a/gr-qtgui/lib/histogram_sink_f_impl.cc b/gr-qtgui/lib/histogram_sink_f_impl.cc index 0ac494d751..4a1267c129 100644 --- a/gr-qtgui/lib/histogram_sink_f_impl.cc +++ b/gr-qtgui/lib/histogram_sink_f_impl.cc @@ -346,6 +346,12 @@ namespace gr { } void + histogram_sink_f_impl::enable_axis_labels(bool en) + { + d_main_gui->setAxisLabels(en); + } + + void histogram_sink_f_impl::enable_autoscale(bool en) { d_main_gui->autoScale(en); diff --git a/gr-qtgui/lib/histogram_sink_f_impl.h b/gr-qtgui/lib/histogram_sink_f_impl.h index acacf1a745..d397beeb6b 100644 --- a/gr-qtgui/lib/histogram_sink_f_impl.h +++ b/gr-qtgui/lib/histogram_sink_f_impl.h @@ -88,6 +88,7 @@ namespace gr { void set_line_alpha(int which, double alpha); void set_nsamps(const int newsize); void set_bins(const int bins); + void enable_axis_labels(bool en); std::string title(); std::string line_label(int which); diff --git a/gr-qtgui/lib/time_raster_sink_b_impl.cc b/gr-qtgui/lib/time_raster_sink_b_impl.cc index 26fed4b062..c0990a9890 100644 --- a/gr-qtgui/lib/time_raster_sink_b_impl.cc +++ b/gr-qtgui/lib/time_raster_sink_b_impl.cc @@ -418,6 +418,12 @@ namespace gr { } void + time_raster_sink_b_impl::enable_axis_labels(bool en) + { + d_main_gui->setAxisLabels(en); + } + + void time_raster_sink_b_impl::enable_autoscale(bool en) { d_main_gui->autoScale(en); diff --git a/gr-qtgui/lib/time_raster_sink_b_impl.h b/gr-qtgui/lib/time_raster_sink_b_impl.h index 4da6990a18..48037b5e5e 100644 --- a/gr-qtgui/lib/time_raster_sink_b_impl.h +++ b/gr-qtgui/lib/time_raster_sink_b_impl.h @@ -122,6 +122,7 @@ namespace gr { void enable_menu(bool en); void enable_grid(bool en); void enable_autoscale(bool en); + void enable_axis_labels(bool en); void reset(); int work(int noutput_items, diff --git a/gr-qtgui/lib/time_raster_sink_f_impl.cc b/gr-qtgui/lib/time_raster_sink_f_impl.cc index 419d52cad6..2fb3e7db68 100644 --- a/gr-qtgui/lib/time_raster_sink_f_impl.cc +++ b/gr-qtgui/lib/time_raster_sink_f_impl.cc @@ -408,6 +408,12 @@ namespace gr { } void + time_raster_sink_f_impl::enable_axis_labels(bool en) + { + d_main_gui->setAxisLabels(en); + } + + void time_raster_sink_f_impl::enable_autoscale(bool en) { d_main_gui->autoScale(en); diff --git a/gr-qtgui/lib/time_raster_sink_f_impl.h b/gr-qtgui/lib/time_raster_sink_f_impl.h index ad63e4d777..1f25dc5534 100644 --- a/gr-qtgui/lib/time_raster_sink_f_impl.h +++ b/gr-qtgui/lib/time_raster_sink_f_impl.h @@ -121,6 +121,7 @@ namespace gr { void enable_menu(bool en); void enable_grid(bool en); void enable_autoscale(bool en); + void enable_axis_labels(bool en); void reset(); int work(int noutput_items, diff --git a/gr-qtgui/lib/time_sink_c_impl.cc b/gr-qtgui/lib/time_sink_c_impl.cc index fff12b070a..98f603a8b0 100644 --- a/gr-qtgui/lib/time_sink_c_impl.cc +++ b/gr-qtgui/lib/time_sink_c_impl.cc @@ -441,10 +441,16 @@ namespace gr { d_main_gui->setTagMenu(which, en); } + void + time_sink_c_impl::enable_axis_labels(bool en) + { + d_main_gui->setAxisLabels(en); + } + void time_sink_c_impl::disable_legend() { - d_main_gui->disableLegend(); + d_main_gui->disableLegend(); } void diff --git a/gr-qtgui/lib/time_sink_c_impl.h b/gr-qtgui/lib/time_sink_c_impl.h index ce6bd94d51..ff938f8ea1 100644 --- a/gr-qtgui/lib/time_sink_c_impl.h +++ b/gr-qtgui/lib/time_sink_c_impl.h @@ -130,6 +130,7 @@ namespace gr { void enable_semilogy(bool en); void enable_control_panel(bool en); void enable_tags(int which, bool en); + void enable_axis_labels(bool en); void disable_legend(); void reset(); diff --git a/gr-qtgui/lib/time_sink_f_impl.cc b/gr-qtgui/lib/time_sink_f_impl.cc index 579dfd9906..50a86d7dff 100644 --- a/gr-qtgui/lib/time_sink_f_impl.cc +++ b/gr-qtgui/lib/time_sink_f_impl.cc @@ -437,6 +437,12 @@ namespace gr { } void + time_sink_f_impl::enable_axis_labels(bool en) + { + d_main_gui->setAxisLabels(en); + } + + void time_sink_f_impl::disable_legend() { d_main_gui->disableLegend(); diff --git a/gr-qtgui/lib/time_sink_f_impl.h b/gr-qtgui/lib/time_sink_f_impl.h index 25d920c0da..a7a304aee1 100644 --- a/gr-qtgui/lib/time_sink_f_impl.h +++ b/gr-qtgui/lib/time_sink_f_impl.h @@ -130,6 +130,7 @@ namespace gr { void enable_semilogy(bool en); void enable_control_panel(bool en); void enable_tags(int which, bool en); + void enable_axis_labels(bool en); void disable_legend(); void reset(); diff --git a/gr-qtgui/lib/timecontrolpanel.cc b/gr-qtgui/lib/timecontrolpanel.cc index 4c5a718f97..95d2cb7a1c 100644 --- a/gr-qtgui/lib/timecontrolpanel.cc +++ b/gr-qtgui/lib/timecontrolpanel.cc @@ -31,6 +31,8 @@ TimeControlPanel::TimeControlPanel(TimeDisplayForm *form) d_axes_layout = new QVBoxLayout; d_autoscale_check = new QCheckBox("Autoscale"); d_grid_check = new QCheckBox("Grid"); + d_axislabels_check = new QCheckBox("Axis Labels"); + d_axislabels_check->setChecked(true); d_yoff_layout = new QHBoxLayout; d_yoff_label = new QLabel("Y Offset:"); @@ -106,6 +108,7 @@ TimeControlPanel::TimeControlPanel(TimeDisplayForm *form) // Set up the boxes into the layout d_axes_layout->addWidget(d_autoscale_check); d_axes_layout->addWidget(d_grid_check); + d_axes_layout->addWidget(d_axislabels_check); d_axes_layout->addLayout(d_yoff_layout); d_axes_layout->addLayout(d_yrange_layout); d_axes_layout->addLayout(d_xmax_layout); @@ -132,6 +135,8 @@ TimeControlPanel::TimeControlPanel(TimeDisplayForm *form) d_parent, SLOT(autoScale(bool))); connect(d_grid_check, SIGNAL(clicked(bool)), d_parent, SLOT(setGrid(bool))); + connect(d_axislabels_check, SIGNAL(clicked(bool)), + d_parent, SLOT(setAxisLabels(bool))); connect(d_yoff_plus, SIGNAL(pressed(void)), d_parent, SLOT(notifyYAxisPlus(void))); connect(d_yoff_minus, SIGNAL(pressed(void)), diff --git a/gr-qtgui/lib/waterfall_sink_c_impl.cc b/gr-qtgui/lib/waterfall_sink_c_impl.cc index 694ef4dac7..7395aaf2a9 100644 --- a/gr-qtgui/lib/waterfall_sink_c_impl.cc +++ b/gr-qtgui/lib/waterfall_sink_c_impl.cc @@ -368,6 +368,12 @@ namespace gr { } void + waterfall_sink_c_impl::enable_axis_labels(bool en) + { + d_main_gui->setAxisLabels(en); + } + + void waterfall_sink_c_impl::disable_legend() { d_main_gui->disableLegend(); diff --git a/gr-qtgui/lib/waterfall_sink_c_impl.h b/gr-qtgui/lib/waterfall_sink_c_impl.h index 3e7f9ddd2d..49766cd3dc 100644 --- a/gr-qtgui/lib/waterfall_sink_c_impl.h +++ b/gr-qtgui/lib/waterfall_sink_c_impl.h @@ -132,6 +132,7 @@ namespace gr { void enable_menu(bool en); void enable_grid(bool en); void disable_legend(); + void enable_axis_labels(bool en); int work(int noutput_items, gr_vector_const_void_star &input_items, diff --git a/gr-qtgui/lib/waterfall_sink_f_impl.cc b/gr-qtgui/lib/waterfall_sink_f_impl.cc index 56c2061f25..b6f50de31d 100644 --- a/gr-qtgui/lib/waterfall_sink_f_impl.cc +++ b/gr-qtgui/lib/waterfall_sink_f_impl.cc @@ -374,6 +374,12 @@ namespace gr { } void + waterfall_sink_f_impl::enable_axis_labels(bool en) + { + d_main_gui->setAxisLabels(en); + } + + void waterfall_sink_f_impl::disable_legend() { d_main_gui->disableLegend(); diff --git a/gr-qtgui/lib/waterfall_sink_f_impl.h b/gr-qtgui/lib/waterfall_sink_f_impl.h index e4f855c9f5..db0f4239bc 100644 --- a/gr-qtgui/lib/waterfall_sink_f_impl.h +++ b/gr-qtgui/lib/waterfall_sink_f_impl.h @@ -133,6 +133,7 @@ namespace gr { void enable_menu(bool en); void enable_grid(bool en); void disable_legend(); + void enable_axis_labels(bool en); int work(int noutput_items, gr_vector_const_void_star &input_items, diff --git a/gr-qtgui/swig/qtgui_swig.i b/gr-qtgui/swig/qtgui_swig.i index 9664a71786..d9595e4d66 100644 --- a/gr-qtgui/swig/qtgui_swig.i +++ b/gr-qtgui/swig/qtgui_swig.i @@ -37,6 +37,18 @@ namespace gr { NUM_GRAPH_VERT, }; + enum data_type_t { + INT = 0, + FLOAT, + DOUBLE, + COMPLEX, + STRING, + INT_VEC, + FLOAT_VEC, + DOUBLE_VEC, + COMPLEX_VEC, + }; + } /* namespace qtgui */ } /* namespace gr */ @@ -75,6 +87,7 @@ enum{ #include "gnuradio/qtgui/number_sink.h" #include "gnuradio/qtgui/ber_sink_b.h" #include "gnuradio/qtgui/vector_sink_f.h" +#include "gnuradio/qtgui/edit_box_msg.h" %} %include "gnuradio/qtgui/sink_c.h" @@ -92,6 +105,7 @@ enum{ %include "gnuradio/qtgui/number_sink.h" %include "gnuradio/qtgui/ber_sink_b.h" %include "gnuradio/qtgui/vector_sink_f.h" +%include "gnuradio/qtgui/edit_box_msg.h" GR_SWIG_BLOCK_MAGIC2(qtgui, sink_c); GR_SWIG_BLOCK_MAGIC2(qtgui, sink_f); @@ -108,3 +122,4 @@ GR_SWIG_BLOCK_MAGIC2(qtgui, histogram_sink_f); GR_SWIG_BLOCK_MAGIC2(qtgui, number_sink); GR_SWIG_BLOCK_MAGIC2(qtgui, ber_sink_b); GR_SWIG_BLOCK_MAGIC2(qtgui, vector_sink_f); +GR_SWIG_BLOCK_MAGIC2(qtgui, edit_box_msg); diff --git a/gr-trellis/grc/trellis_encoder_xx.xml b/gr-trellis/grc/trellis_encoder_xx.xml index d59903ca2d..ab7d5226e9 100644 --- a/gr-trellis/grc/trellis_encoder_xx.xml +++ b/gr-trellis/grc/trellis_encoder_xx.xml @@ -8,7 +8,7 @@ <block> <name>Trellis Encoder</name> <key>trellis_encoder_xx</key> - <category>Trellis Coding</category> + <category>[Core]/Trellis Coding</category> <import>from gnuradio import trellis</import> <make> trellis.encoder_$(type)(trellis.fsm($fsm_args), $init_state, $blocklength) if $blockwise else trellis.encoder_$(type)(trellis.fsm($fsm_args), $init_state) </make> <callback>set_FSM(trellis.fsm($fsm_args))</callback> diff --git a/gr-trellis/grc/trellis_metrics_x.xml b/gr-trellis/grc/trellis_metrics_x.xml index 8bb978f275..15e7338098 100644 --- a/gr-trellis/grc/trellis_metrics_x.xml +++ b/gr-trellis/grc/trellis_metrics_x.xml @@ -9,7 +9,7 @@ <block> <name>Trellis Metrics</name> <key>trellis_metrics_x</key> - <category>Trellis Coding</category> + <category>[Core]/Trellis Coding</category> <import>from gnuradio import trellis, digital</import> <make>trellis.metrics_$(type)($card, $dim, $table, $metric_type)</make> <callback>set_O($card)</callback> diff --git a/gr-trellis/grc/trellis_pccc_decoder_combined_xx.xml b/gr-trellis/grc/trellis_pccc_decoder_combined_xx.xml index ff727fbf8b..7bb38db893 100644 --- a/gr-trellis/grc/trellis_pccc_decoder_combined_xx.xml +++ b/gr-trellis/grc/trellis_pccc_decoder_combined_xx.xml @@ -9,7 +9,7 @@ <block> <name>PCCC Decoder Combo</name> <key>trellis_pccc_decoder_combined_xx</key> - <category>Trellis Coding</category> + <category>[Core]/Trellis Coding</category> <import>from gnuradio import trellis, digital</import> <make>trellis.pccc_decoder_combined_$(type)$(out_type)( trellis.fsm($o_fsm_args), $o_init_state, $o_final_state, diff --git a/gr-trellis/grc/trellis_pccc_decoder_x.xml b/gr-trellis/grc/trellis_pccc_decoder_x.xml index 81f4282728..08d1f526af 100644 --- a/gr-trellis/grc/trellis_pccc_decoder_x.xml +++ b/gr-trellis/grc/trellis_pccc_decoder_x.xml @@ -9,7 +9,7 @@ <block> <name>PCCC Decoder</name> <key>trellis_pccc_decoder_x</key> - <category>Trellis Coding</category> + <category>[Core]/Trellis Coding</category> <import>from gnuradio import trellis</import> <make>trellis.pccc_decoder_$(out_type)( trellis.fsm($o_fsm_args), $o_init_state, $o_final_state, diff --git a/gr-trellis/grc/trellis_pccc_encoder_xx.xml b/gr-trellis/grc/trellis_pccc_encoder_xx.xml index faaa0c1233..85348e6e9b 100644 --- a/gr-trellis/grc/trellis_pccc_encoder_xx.xml +++ b/gr-trellis/grc/trellis_pccc_encoder_xx.xml @@ -8,7 +8,7 @@ <block> <name>PCCC Encoder</name> <key>trellis_pccc_encoder_xx</key> - <category>Trellis Coding</category> + <category>[Core]/Trellis Coding</category> <import>from gnuradio import trellis</import> <make>trellis.pccc_encoder_$(type)(trellis.fsm($o_fsm_args), $o_init_state, trellis.fsm($i_fsm_args), $i_init_state, trellis.interleaver($interleaver_args), $bl)</make> <param> diff --git a/gr-trellis/grc/trellis_permutation.xml b/gr-trellis/grc/trellis_permutation.xml index 0f118e889f..99eb2d291c 100644 --- a/gr-trellis/grc/trellis_permutation.xml +++ b/gr-trellis/grc/trellis_permutation.xml @@ -9,7 +9,7 @@ <block> <name>Trellis Permutation</name> <key>trellis_permutation</key> - <category>Trellis Coding</category> + <category>[Core]/Trellis Coding</category> <import>from gnuradio import trellis</import> <make>trellis.permutation($interleaver_size, $table, $syms_per_block, $type.size*$vlen)</make> <callback>set_K($interleaver_size)</callback> diff --git a/gr-trellis/grc/trellis_sccc_decoder_combined_xx.xml b/gr-trellis/grc/trellis_sccc_decoder_combined_xx.xml index 4947376d94..79be0b8b5d 100644 --- a/gr-trellis/grc/trellis_sccc_decoder_combined_xx.xml +++ b/gr-trellis/grc/trellis_sccc_decoder_combined_xx.xml @@ -9,7 +9,7 @@ <block> <name>SCCC Decoder Combo</name> <key>trellis_sccc_decoder_combined_xx</key> - <category>Trellis Coding</category> + <category>[Core]/Trellis Coding</category> <import>from gnuradio import trellis, digital</import> <make>trellis.sccc_decoder_combined_$(type)$(out_type)( trellis.fsm($o_fsm_args), $o_init_state, $o_final_state, diff --git a/gr-trellis/grc/trellis_sccc_decoder_x.xml b/gr-trellis/grc/trellis_sccc_decoder_x.xml index 38348cbb51..584ff0b0af 100644 --- a/gr-trellis/grc/trellis_sccc_decoder_x.xml +++ b/gr-trellis/grc/trellis_sccc_decoder_x.xml @@ -9,7 +9,7 @@ <block> <name>SCCC Decoder</name> <key>trellis_sccc_decoder_x</key> - <category>Trellis Coding</category> + <category>[Core]/Trellis Coding</category> <import>from gnuradio import trellis</import> <make>trellis.sccc_decoder_$(out_type)( trellis.fsm($o_fsm_args), $o_init_state, $o_final_state, diff --git a/gr-trellis/grc/trellis_sccc_encoder_xx.xml b/gr-trellis/grc/trellis_sccc_encoder_xx.xml index a87db79ac2..7b4ab30c75 100644 --- a/gr-trellis/grc/trellis_sccc_encoder_xx.xml +++ b/gr-trellis/grc/trellis_sccc_encoder_xx.xml @@ -8,7 +8,7 @@ <block> <name>SCCC Encoder</name> <key>trellis_sccc_encoder_xx</key> - <category>Trellis Coding</category> + <category>[Core]/Trellis Coding</category> <import>from gnuradio import trellis</import> <make>trellis.sccc_encoder_$(type)(trellis.fsm($o_fsm_args), $o_init_state, trellis.fsm($i_fsm_args), $i_init_state, trellis.interleaver($interleaver_args), $bl)</make> <param> diff --git a/gr-trellis/grc/trellis_siso_combined_f.xml b/gr-trellis/grc/trellis_siso_combined_f.xml index 542bba6d8b..f8ec9c2d21 100644 --- a/gr-trellis/grc/trellis_siso_combined_f.xml +++ b/gr-trellis/grc/trellis_siso_combined_f.xml @@ -9,7 +9,7 @@ <block> <name>SISO Combo</name> <key>trellis_siso_combined_f</key> - <category>Trellis Coding</category> + <category>[Core]/Trellis Coding</category> <import>from gnuradio import trellis</import> <make>trellis.siso_combined_f(trellis.fsm($fsm_args), $block_size, $init_state, $final_state, $a_post_in, $a_post_out, $siso_type, $dim, $table, $metric_type)</make> <callback>set_FSM(trellis.fsm($fsm_args))</callback> diff --git a/gr-trellis/grc/trellis_siso_f.xml b/gr-trellis/grc/trellis_siso_f.xml index 30849d2723..e121643e3f 100644 --- a/gr-trellis/grc/trellis_siso_f.xml +++ b/gr-trellis/grc/trellis_siso_f.xml @@ -9,7 +9,7 @@ <block> <name>SISO</name> <key>trellis_siso_f</key> - <category>Trellis Coding</category> + <category>[Core]/Trellis Coding</category> <import>from gnuradio import trellis</import> <make>trellis.siso_f(trellis.fsm($fsm_args), $block_size, $init_state, $final_state, $a_post_in, $a_post_out, $siso_type)</make> <callback>set_FSM(trellis.fsm($fsm_args))</callback> diff --git a/gr-trellis/grc/trellis_viterbi_combined_xx.xml b/gr-trellis/grc/trellis_viterbi_combined_xx.xml index 9c755dc524..a52e5ed2b5 100644 --- a/gr-trellis/grc/trellis_viterbi_combined_xx.xml +++ b/gr-trellis/grc/trellis_viterbi_combined_xx.xml @@ -9,7 +9,7 @@ <block> <name>Viterbi Combo</name> <key>trellis_viterbi_combined_xx</key> - <category>Trellis Coding</category> + <category>[Core]/Trellis Coding</category> <import>from gnuradio import trellis, digital</import> <make>trellis.viterbi_combined_$(type)$(out_type)(trellis.fsm($fsm_args), $block_size, $init_state, $final_state, $dim, $table, $metric_type)</make> <callback>set_FSM(trellis.fsm($fsm_args))</callback> diff --git a/gr-trellis/grc/trellis_viterbi_x.xml b/gr-trellis/grc/trellis_viterbi_x.xml index 9083061e6b..21611ee51d 100644 --- a/gr-trellis/grc/trellis_viterbi_x.xml +++ b/gr-trellis/grc/trellis_viterbi_x.xml @@ -9,7 +9,7 @@ <block> <name>Viterbi</name> <key>trellis_viterbi_x</key> - <category>Trellis Coding</category> + <category>[Core]/Trellis Coding</category> <import>from gnuradio import trellis</import> <make>trellis.viterbi_$(type)(trellis.fsm($fsm_args), $block_size, $init_state, $final_state)</make> <callback>set_FSM(trellis.fsm($fsm_args))</callback> diff --git a/gr-uhd/apps/uhd_app.py b/gr-uhd/apps/uhd_app.py index 59bebe24ec..ff4412a140 100644 --- a/gr-uhd/apps/uhd_app.py +++ b/gr-uhd/apps/uhd_app.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2015 Free Software Foundation, Inc. +# Copyright 2015-2016 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -141,6 +141,9 @@ class UHDApp(object): if args.spec: for mb_idx in xrange(self.usrp.get_num_mboards()): self.usrp.set_subdev_spec(args.spec, mb_idx) + # Set the clock source: + if args.clock_source is not None: + self.usrp.set_clock_source(args.clock_source) # Sampling rate: self.usrp.set_samp_rate(args.samp_rate) self.samp_rate = self.usrp.get_samp_rate() @@ -305,5 +308,7 @@ class UHDApp(object): help="Show asynchronous message notifications from UHD") group.add_argument("--sync", choices=('default', 'pps', 'auto'), default='auto', help="Set to 'pps' to sync devices to PPS") + group.add_argument("--clock-source", + help="Set the clock source; typically 'internal', 'external' or 'gpsdo'") return parser diff --git a/gr-uhd/examples/python/fm_tx4.py b/gr-uhd/examples/python/fm_tx4.py index fefa67861b..516033dae1 100755 --- a/gr-uhd/examples/python/fm_tx4.py +++ b/gr-uhd/examples/python/fm_tx4.py @@ -63,7 +63,8 @@ class pipeline(gr.hier_block2): sys.exit(1) print audio_rate, if_rate - fmtx = analog.nbfm_tx(audio_rate, if_rate, max_dev=5e3, tau=75e-6) + fmtx = analog.nbfm_tx(audio_rate, if_rate, max_dev=5e3, + tau=75e-6, fh=0.925*if_rate/2.0) # Local oscillator lo = analog.sig_source_c(if_rate, # sample rate diff --git a/gr-uhd/grc/gen_uhd_usrp_blocks.py b/gr-uhd/grc/gen_uhd_usrp_blocks.py index fd2e77707e..e99de0d7d0 100644 --- a/gr-uhd/grc/gen_uhd_usrp_blocks.py +++ b/gr-uhd/grc/gen_uhd_usrp_blocks.py @@ -82,6 +82,12 @@ self.\$(id).set_antenna(\$ant$(n), $n) self.\$(id).set_bandwidth(\$bw$(n), $n) \#end if #if $sourk == 'source' + \#if \$lo_export$(n)() and not \$hide_lo_controls() +self.\$(id).set_lo_export_enabled(\$lo_export$(n), uhd.ALL_LOS, $n) + \#end if + \#if \$lo_source$(n)() and not \$hide_lo_controls() +self.\$(id).set_lo_source(\$lo_source$(n), uhd.ALL_LOS, $n) + \#end if \#if \$dc_offs_enb$(n)() self.\$(id).set_auto_dc_offset(\$dc_offs_enb$(n), $n) \#end if @@ -101,6 +107,14 @@ self.\$(id).set_normalized_gain(\$gain$(n), $n) self.\$(id).set_gain(\$gain$(n), $n) \#end if </callback> + <callback>\#if not \$hide_lo_controls() +set_lo_source(\$lo_source$(n), uhd.ALL_LOS, $n) +\#end if + </callback> + <callback>\#if not \$hide_lo_controls() +set_lo_export_enabled(\$lo_export$(n), uhd.ALL_LOS, $n) +\#end if + </callback> <callback>set_antenna(\$ant$(n), $n)</callback> <callback>set_bandwidth(\$bw$(n), $n)</callback> #end for @@ -524,6 +538,62 @@ PARAMS_TMPL = """ <param> </param> #if $sourk == 'source' <param> + <name>Ch$(n): LO Source</name> + <key>lo_source$(n)</key> + <value>internal</value> + <type>string</type> + <hide> + \#if not \$nchan() > $n + all + \#elif \$hide_lo_controls() + all + \#else + none + \#end if + </hide> + <option> + <name>Internal</name> + <key>internal</key> + </option> + <option> + <name>External</name> + <key>external</key> + </option> + <option> + <name>Companion</name> + <key>companion</key> + </option> + <tab>RF Options</tab> + </param> +#end if +#if $sourk == 'source' + <param> + <name>Ch$(n): LO Export</name> + <key>lo_export$(n)</key> + <value>False</value> + <type>bool</type> + <hide> + \#if not \$nchan() > $n + all + \#elif \$hide_lo_controls() + all + \#else + none + \#end if + </hide> + <option> + <name>True</name> + <key>True</key> + </option> + <option> + <name>False</name> + <key>False</key> + </option> + <tab>RF Options</tab> + </param> +#end if +#if $sourk == 'source' + <param> <name>Ch$(n): Enable DC Offset Correction</name> <key>dc_offs_enb$(n)</key> <value>""</value> @@ -573,6 +643,25 @@ SHOW_CMD_PORT_PARAM = """ </param> """ +SHOW_LO_CONTROLS_PARAM = """ + <param> + <name>Show LO Controls</name> + <key>hide_lo_controls</key> + <value>True</value> + <type>bool</type> + <hide>part</hide> + <option> + <name>Yes</name> + <key>False</key> + </option> + <option> + <name>No</name> + <key>True</key> + </option> + <tab>Advanced</tab> + </param> +""" + TSBTAG_PARAM = """ <param> <name>TSB tag name</name> <key>len_tag_name</key> @@ -605,7 +694,8 @@ if __name__ == '__main__': else: raise Exception, 'is %s a source or sink?'%file params = ''.join([parse_tmpl(PARAMS_TMPL, n=n, sourk=sourk) for n in range(max_num_channels)]) - params += SHOW_CMD_PORT_PARAM + params += SHOW_CMD_PORT_PARAM + params += SHOW_LO_CONTROLS_PARAM if sourk == 'sink': params += TSBTAG_PARAM lentag_arg = TSBTAG_ARG diff --git a/gr-uhd/grc/uhd_block_tree.xml b/gr-uhd/grc/uhd_block_tree.xml index 5d3b493936..c598645526 100644 --- a/gr-uhd/grc/uhd_block_tree.xml +++ b/gr-uhd/grc/uhd_block_tree.xml @@ -5,7 +5,7 @@ ################################################### --> <cat> - <name></name> <!-- Blank for Root Name --> + <name>[Core]</name> <!-- Blank for Root Name --> <cat> <name>UHD</name> <block>uhd_usrp_source</block> diff --git a/gr-uhd/include/gnuradio/uhd/usrp_source.h b/gr-uhd/include/gnuradio/uhd/usrp_source.h index 19b3feb61f..a0022b3876 100644 --- a/gr-uhd/include/gnuradio/uhd/usrp_source.h +++ b/gr-uhd/include/gnuradio/uhd/usrp_source.h @@ -96,10 +96,13 @@ namespace gr { /*! * \param device_addr the address to identify the hardware * \param stream_args the IO format and channel specification + * \param issue_stream_cmd_on_start enable or disable continuous streaming when flowgraph + * starts (default true) * \return a new USRP source block object */ static sptr make(const ::uhd::device_addr_t &device_addr, - const ::uhd::stream_args_t &stream_args); + const ::uhd::stream_args_t &stream_args, + const bool issue_stream_cmd_on_start = true); /*! * Set the start time for incoming samples. @@ -137,6 +140,83 @@ namespace gr { */ virtual ::uhd::dict<std::string, std::string> get_usrp_info(size_t chan = 0) = 0; + + /*! + * Get a list of possible LO stage names + * \param chan the channel index 0 to N-1 + * \return a vector of strings for possible LO names + */ + virtual std::vector<std::string> get_lo_names(size_t chan = 0) = 0; + + /*! + * Set the LO source for the usrp device. + * For usrps that support selectable LOs, this function + * allows switching between them. + * Typical options for source: internal, external. + * \param src a string representing the LO source + * \param name the name of the LO stage to update + * \param chan the channel index 0 to N-1 + */ + virtual void set_lo_source(const std::string &src, const std::string &name, size_t chan = 0) = 0; + + /*! + * Get the currently set LO source. + * \param name the name of the LO stage to query + * \param chan the channel index 0 to N-1 + * \return the configured LO source + */ + virtual const std::string get_lo_source(const std::string &name, size_t chan = 0) = 0; + + /*! + * Get a list of possible LO sources. + * \param name the name of the LO stage to query + * \param chan the channel index 0 to N-1 + * \return a vector of strings for possible settings + */ + virtual std::vector<std::string> get_lo_sources(const std::string &name, size_t chan = 0) = 0; + + /*! + * Set whether the LO used by the usrp device is exported + * For usrps that support exportable LOs, this function + * configures if the LO used by chan is exported or not. + * \param enabled if true then export the LO + * \param name the name of the LO stage to update + * \param chan the channel index 0 to N-1 for the source channel + */ + virtual void set_lo_export_enabled(bool enabled, const std::string &name, size_t chan = 0) = 0; + + /*! + * Returns true if the currently selected LO is being exported. + * \param name the name of the LO stage to query + * \param chan the channel index 0 to N-1 + */ + virtual bool get_lo_export_enabled(const std::string &name, size_t chan = 0) = 0; + + /*! + * Set the RX LO frequency (Advanced). + * \param freq the frequency to set the LO to + * \param name the name of the LO stage to update + * \param chan the channel index 0 to N-1 + * \return a coerced LO frequency + */ + virtual double set_lo_freq(double freq, const std::string &name, size_t chan = 0) = 0; + + /*! + * Get the current RX LO frequency (Advanced). + * \param name the name of the LO stage to query + * \param chan the channel index 0 to N-1 + * \return the configured LO frequency + */ + virtual double get_lo_freq(const std::string &name, size_t chan = 0) = 0; + + /*! + * Get the LO frequency range of the RX LO. + * \param name the name of the LO stage to query + * \param chan the channel index 0 to N-1 + * \return a frequency range object + */ + virtual ::uhd::freq_range_t get_lo_freq_range(const std::string &name, size_t chan = 0) = 0; + /*! * Enable/disable the automatic DC offset correction. * The automatic correction subtracts out the long-run average. diff --git a/gr-uhd/lib/usrp_block_impl.h b/gr-uhd/lib/usrp_block_impl.h index cb07fb356d..a1bbf97826 100644 --- a/gr-uhd/lib/usrp_block_impl.h +++ b/gr-uhd/lib/usrp_block_impl.h @@ -40,6 +40,14 @@ namespace gr { namespace uhd { + static const size_t ALL_MBOARDS = ::uhd::usrp::multi_usrp::ALL_MBOARDS; + static const size_t ALL_CHANS = ::uhd::usrp::multi_usrp::ALL_CHANS; + static const std::string ALL_GAINS = ::uhd::usrp::multi_usrp::ALL_GAINS; +#ifdef UHD_USRP_MULTI_USRP_LO_CONFIG_API + static const std::string ALL_LOS = ::uhd::usrp::multi_usrp::ALL_LOS; +#else + static const std::string ALL_LOS; +#endif class usrp_block_impl : virtual public usrp_block { public: diff --git a/gr-uhd/lib/usrp_sink_impl.cc b/gr-uhd/lib/usrp_sink_impl.cc index 666f09cbf5..8b3412b025 100644 --- a/gr-uhd/lib/usrp_sink_impl.cc +++ b/gr-uhd/lib/usrp_sink_impl.cc @@ -29,8 +29,6 @@ namespace gr { namespace uhd { - static const size_t ALL_CHANS = ::uhd::usrp::multi_usrp::ALL_CHANS; - usrp_sink::sptr usrp_sink::make(const ::uhd::device_addr_t &device_addr, const ::uhd::io_type_t &io_type, diff --git a/gr-uhd/lib/usrp_source_impl.cc b/gr-uhd/lib/usrp_source_impl.cc index eeb9521a5a..f0450a604d 100644 --- a/gr-uhd/lib/usrp_source_impl.cc +++ b/gr-uhd/lib/usrp_source_impl.cc @@ -31,6 +31,8 @@ namespace gr { namespace uhd { + const pmt::pmt_t CMD_TAG_KEY = pmt::mp("tag"); + usrp_source::sptr usrp_source::make(const ::uhd::device_addr_t &device_addr, const ::uhd::io_type_t &io_type, @@ -53,20 +55,23 @@ namespace gr { usrp_source::sptr usrp_source::make(const ::uhd::device_addr_t &device_addr, - const ::uhd::stream_args_t &stream_args) + const ::uhd::stream_args_t &stream_args, + const bool issue_stream_cmd_on_start) { check_abi(); return usrp_source::sptr - (new usrp_source_impl(device_addr, stream_args_ensure(stream_args))); + (new usrp_source_impl(device_addr, stream_args_ensure(stream_args), issue_stream_cmd_on_start)); } usrp_source_impl::usrp_source_impl(const ::uhd::device_addr_t &device_addr, - const ::uhd::stream_args_t &stream_args): + const ::uhd::stream_args_t &stream_args, + const bool issue_stream_cmd_on_start): usrp_block("gr uhd usrp source", io_signature::make(0, 0, 0), args_to_io_sig(stream_args)), usrp_block_impl(device_addr, stream_args, ""), - _tag_now(false) + _tag_now(false), + _issue_stream_cmd_on_start(issue_stream_cmd_on_start) { std::stringstream str; str << name() << unique_id(); @@ -77,6 +82,7 @@ namespace gr { #ifdef GR_UHD_USE_STREAM_API _samps_per_packet = 1; #endif + register_msg_cmd_handler(CMD_TAG_KEY, boost::bind(&usrp_source_impl::_cmd_handler_tag, this, _1)); } usrp_source_impl::~usrp_source_impl() @@ -275,6 +281,104 @@ namespace gr { return _dev->get_rx_bandwidth_range(chan); } + std::vector<std::string> + usrp_source_impl::get_lo_names(size_t chan) + { +#ifdef UHD_USRP_MULTI_USRP_LO_CONFIG_API + chan = _stream_args.channels[chan]; + return _dev->get_rx_lo_names(chan); +#else + throw std::runtime_error("not implemented in this version"); +#endif + } + + const std::string + usrp_source_impl::get_lo_source(const std::string &name, size_t chan) + { +#ifdef UHD_USRP_MULTI_USRP_LO_CONFIG_API + chan = _stream_args.channels[chan]; + return _dev->get_rx_lo_source(name, chan); +#else + throw std::runtime_error("not implemented in this version"); +#endif + } + + std::vector<std::string> + usrp_source_impl::get_lo_sources(const std::string &name, size_t chan) + { +#ifdef UHD_USRP_MULTI_USRP_LO_CONFIG_API + chan = _stream_args.channels[chan]; + return _dev->get_rx_lo_sources(name, chan); +#else + throw std::runtime_error("not implemented in this version"); +#endif + } + + void + usrp_source_impl::set_lo_source(const std::string &src, const std::string &name, size_t chan) + { +#ifdef UHD_USRP_MULTI_USRP_LO_CONFIG_API + chan = _stream_args.channels[chan]; + return _dev->set_rx_lo_source(src, name, chan); +#else + throw std::runtime_error("not implemented in this version"); +#endif + } + + bool + usrp_source_impl::get_lo_export_enabled(const std::string &name, size_t chan) + { +#ifdef UHD_USRP_MULTI_USRP_LO_CONFIG_API + chan = _stream_args.channels[chan]; + return _dev->get_rx_lo_export_enabled(name, chan); +#else + throw std::runtime_error("not implemented in this version"); +#endif + } + + void + usrp_source_impl::set_lo_export_enabled(bool enabled, const std::string &name, size_t chan) + { +#ifdef UHD_USRP_MULTI_USRP_LO_CONFIG_API + chan = _stream_args.channels[chan]; + return _dev->set_rx_lo_export_enabled(enabled, name, chan); +#else + throw std::runtime_error("not implemented in this version"); +#endif + } + + ::uhd::freq_range_t + usrp_source_impl::get_lo_freq_range(const std::string &name, size_t chan) + { +#ifdef UHD_USRP_MULTI_USRP_LO_CONFIG_API + chan = _stream_args.channels[chan]; + return _dev->get_rx_lo_freq_range(name, chan); +#else + throw std::runtime_error("not implemented in this version"); +#endif + } + + double + usrp_source_impl::get_lo_freq(const std::string &name, size_t chan) + { +#ifdef UHD_USRP_MULTI_USRP_LO_CONFIG_API + chan = _stream_args.channels[chan]; + return _dev->get_rx_lo_freq(name, chan); +#else + throw std::runtime_error("not implemented in this version"); +#endif + } + + double + usrp_source_impl::set_lo_freq(double freq, const std::string &name, size_t chan) { +#ifdef UHD_USRP_MULTI_USRP_LO_CONFIG_API + chan = _stream_args.channels[chan]; + return _dev->set_rx_lo_freq(freq, name, chan); +#else + throw std::runtime_error("not implemented in this version"); +#endif + } + void usrp_source_impl::set_auto_dc_offset(const bool enable, size_t chan) { @@ -356,6 +460,12 @@ namespace gr { } void + usrp_source_impl::_cmd_handler_tag(const pmt::pmt_t &tag) + { + _tag_now = true; + } + + void usrp_source_impl::set_start_time(const ::uhd::time_spec_t &time) { _start_time = time; @@ -382,18 +492,20 @@ namespace gr { _samps_per_packet = _rx_stream->get_max_num_samps(); } #endif - //setup a stream command that starts streaming slightly in the future - static const double reasonable_delay = 0.1; //order of magnitude over RTT - ::uhd::stream_cmd_t stream_cmd(::uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); - stream_cmd.stream_now = _stream_now; - if(_start_time_set) { - _start_time_set = false; //cleared for next run - stream_cmd.time_spec = _start_time; - } - else { - stream_cmd.time_spec = get_time_now() + ::uhd::time_spec_t(reasonable_delay); + if(_issue_stream_cmd_on_start){ + //setup a stream command that starts streaming slightly in the future + static const double reasonable_delay = 0.1; //order of magnitude over RTT + ::uhd::stream_cmd_t stream_cmd(::uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + stream_cmd.stream_now = _stream_now; + if(_start_time_set) { + _start_time_set = false; //cleared for next run + stream_cmd.time_spec = _start_time; + } + else { + stream_cmd.time_spec = get_time_now() + ::uhd::time_spec_t(reasonable_delay); + } + this->issue_stream_cmd(stream_cmd); } - this->issue_stream_cmd(stream_cmd); _tag_now = true; return true; } diff --git a/gr-uhd/lib/usrp_source_impl.h b/gr-uhd/lib/usrp_source_impl.h index 98af816c02..f6225a7e35 100644 --- a/gr-uhd/lib/usrp_source_impl.h +++ b/gr-uhd/lib/usrp_source_impl.h @@ -55,7 +55,8 @@ namespace gr { { public: usrp_source_impl(const ::uhd::device_addr_t &device_addr, - const ::uhd::stream_args_t &stream_args); + const ::uhd::stream_args_t &stream_args, + const bool issue_stream_cmd_on_start = true); ~usrp_source_impl(); // Get Commands @@ -76,6 +77,12 @@ namespace gr { ::uhd::sensor_value_t get_sensor(const std::string &name, size_t chan); std::vector<std::string> get_sensor_names(size_t chan); ::uhd::usrp::dboard_iface::sptr get_dboard_iface(size_t chan); + std::vector<std::string> get_lo_names(size_t chan); + const std::string get_lo_source(const std::string &name, size_t chan); + std::vector<std::string> get_lo_sources(const std::string &name, size_t chan); + bool get_lo_export_enabled(const std::string &name, size_t chan); + double get_lo_freq(const std::string &name, size_t chan); + ::uhd::freq_range_t get_lo_freq_range(const std::string &name, size_t chan); // Set Commands void set_subdev_spec(const std::string &spec, size_t mboard); @@ -95,6 +102,9 @@ namespace gr { void set_iq_balance(const std::complex<double> &correction, size_t chan); void set_stream_args(const ::uhd::stream_args_t &stream_args); void set_start_time(const ::uhd::time_spec_t &time); + void set_lo_source(const std::string &src, const std::string &name = ALL_LOS, size_t chan = 0); + void set_lo_export_enabled(bool enabled, const std::string &name = ALL_LOS, size_t chan = 0); + double set_lo_freq(double freq, const std::string &name, size_t chan); void issue_stream_cmd(const ::uhd::stream_cmd_t &cmd); void flush(void); @@ -111,6 +121,7 @@ namespace gr { private: //! Like set_center_freq(), but uses _curr_freq and _curr_lo_offset ::uhd::tune_result_t _set_center_freq_from_internals(size_t chan); + void _cmd_handler_tag(const pmt::pmt_t &tag); #ifdef GR_UHD_USE_STREAM_API ::uhd::rx_streamer::sptr _rx_stream; @@ -119,6 +130,7 @@ namespace gr { bool _tag_now; ::uhd::rx_metadata_t _metadata; pmt::pmt_t _id; + bool _issue_stream_cmd_on_start; //tag shadows double _samp_rate; diff --git a/gr-uhd/swig/uhd_swig.i b/gr-uhd/swig/uhd_swig.i index 108f544da3..b82d0fdae9 100644 --- a/gr-uhd/swig/uhd_swig.i +++ b/gr-uhd/swig/uhd_swig.i @@ -155,8 +155,20 @@ static uhd::device_addrs_t find_devices_raw(const uhd::device_addr_t &dev_addr = //////////////////////////////////////////////////////////////////////// %{ static const size_t ALL_MBOARDS = uhd::usrp::multi_usrp::ALL_MBOARDS; +static const size_t ALL_CHANS = uhd::usrp::multi_usrp::ALL_CHANS; +static const std::string ALL_GAINS = uhd::usrp::multi_usrp::ALL_GAINS; + +#ifdef UHD_USRP_MULTI_USRP_LO_CONFIG_API +static const std::string ALL_LOS = uhd::usrp::multi_usrp::ALL_LOS; +#else +static const std::string ALL_LOS; +#endif %} + static const size_t ALL_MBOARDS; +static const size_t ALL_CHANS; +static const std::string ALL_GAINS; +static const std::string ALL_LOS; %{ #include <uhd/version.hpp> diff --git a/gr-utils/python/modtool/gr-newmod/CMakeLists.txt b/gr-utils/python/modtool/gr-newmod/CMakeLists.txt index 9b41b99f14..bde9dc0c0f 100644 --- a/gr-utils/python/modtool/gr-newmod/CMakeLists.txt +++ b/gr-utils/python/modtool/gr-newmod/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2011,2012,2014 Free Software Foundation, Inc. +# Copyright 2011,2012,2014,2016 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -24,6 +24,12 @@ cmake_minimum_required(VERSION 2.6) project(gr-howto CXX C) enable_testing() +#install to PyBOMBS target prefix if defined +if(DEFINED ENV{PYBOMBS_PREFIX}) + set(CMAKE_INSTALL_PREFIX $ENV{PYBOMBS_PREFIX}) + message(STATUS "PyBOMBS installed GNU Radio. Setting CMAKE_INSTALL_PREFIX to $ENV{PYBOMBS_PREFIX}") +endif() + #select the release build type by default to get optimization flags if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release") @@ -34,6 +40,12 @@ set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "") #make sure our local CMake Modules path comes first list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_SOURCE_DIR}/cmake/Modules) +# Set the version information here +set(VERSION_INFO_MAJOR_VERSION 1) +set(VERSION_INFO_API_COMPAT 0) +set(VERSION_INFO_MINOR_VERSION 0) +set(VERSION_INFO_MAINT_VERSION git) + ######################################################################## # Compiler specific setup ######################################################################## @@ -113,6 +125,7 @@ find_package(Doxygen) set(GR_REQUIRED_COMPONENTS RUNTIME) find_package(Gnuradio "3.7.2" REQUIRED) list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_SOURCE_DIR}/cmake/Modules) +include(GrVersion) if(NOT CPPUNIT_FOUND) message(FATAL_ERROR "CppUnit required to compile howto") diff --git a/gr-utils/python/modtool/gr-newmod/lib/CMakeLists.txt b/gr-utils/python/modtool/gr-newmod/lib/CMakeLists.txt index 6192c51f04..10a15b7dd9 100644 --- a/gr-utils/python/modtool/gr-newmod/lib/CMakeLists.txt +++ b/gr-utils/python/modtool/gr-newmod/lib/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2011,2012 Free Software Foundation, Inc. +# Copyright 2011,2012,2016 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -47,11 +47,8 @@ endif(APPLE) ######################################################################## # Install built library files ######################################################################## -install(TARGETS gnuradio-howto - LIBRARY DESTINATION lib${LIB_SUFFIX} # .so/.dylib file - ARCHIVE DESTINATION lib${LIB_SUFFIX} # .lib file - RUNTIME DESTINATION bin # .dll file -) +include(GrMiscUtils) +GR_LIBRARY_FOO(gnuradio-howto RUNTIME_COMPONENT "howto_runtime" DEVEL_COMPONENT "howto_devel") ######################################################################## # Build and register unit test @@ -76,3 +73,10 @@ target_link_libraries( ) GR_ADD_TEST(test_howto test-howto) + +######################################################################## +# Print summary +######################################################################## +message(STATUS "Using install prefix: ${CMAKE_INSTALL_PREFIX}") +message(STATUS "Building for version: ${VERSION} / ${LIBVER}") + diff --git a/gr-utils/python/modtool/modtool_add.py b/gr-utils/python/modtool/modtool_add.py index 77f82aaae9..b60474740c 100644 --- a/gr-utils/python/modtool/modtool_add.py +++ b/gr-utils/python/modtool/modtool_add.py @@ -53,6 +53,8 @@ class ModToolAdd(ModTool): choices=self._block_types, default=None, help="One of %s." % ', '.join(self._block_types)) ogroup.add_option("--license-file", type="string", default=None, help="File containing the license header for every source code file.") + ogroup.add_option("--copyright", type="string", default=None, + help="Name of the copyright holder (you or your company) MUST be a quoted string.") ogroup.add_option("--argument-list", type="string", default=None, help="The argument list for the constructor and make functions.") ogroup.add_option("--add-python-qa", action="store_true", default=None, @@ -77,7 +79,6 @@ class ModToolAdd(ModTool): self._info['blocktype'] = raw_input("Enter block type: ") if self._info['blocktype'] not in self._block_types: print 'Must be one of ' + str(self._block_types) - # Allow user to specify language interactively if not set self._info['lang'] = options.lang if self._info['lang'] is None: @@ -101,8 +102,14 @@ class ModToolAdd(ModTool): raise ModToolException('Invalid block name.') print "Block/code identifier: " + self._info['blockname'] self._info['fullblockname'] = self._info['modname'] + '_' + self._info['blockname'] + if not options.license_file: + self._info['copyrightholder'] = options.copyright + if self._info['copyrightholder'] is None: + self._info['copyrightholder'] = '<+YOU OR YOUR COMPANY+>' + elif self._info['is_component']: + print "For GNU Radio components the FSF is added as copyright holder" + self._license_file = options.license_file self._info['license'] = self.setup_choose_license() - if options.argument_list is not None: self._info['arglist'] = options.argument_list else: @@ -121,7 +128,6 @@ class ModToolAdd(ModTool): print "Warning: Autotools modules are not supported. ", print "Files will be created, but Makefiles will not be edited." self._skip_cmakefiles = True - self._license_file = options.license_file def setup_choose_license(self): """ Select a license by the following rules, in this order: @@ -139,7 +145,7 @@ class ModToolAdd(ModTool): elif self._info['is_component']: return Templates['grlicense'] else: - return Templates['defaultlicense'] + return get_template('defaultlicense', **self._info) def _write_tpl(self, tpl, path, fname): """ Shorthand for writing a substituted template to a file""" diff --git a/gr-utils/python/modtool/templates.py b/gr-utils/python/modtool/templates.py index dc840df392..9caf658194 100644 --- a/gr-utils/python/modtool/templates.py +++ b/gr-utils/python/modtool/templates.py @@ -26,7 +26,7 @@ Templates = {} # Default licence Templates['defaultlicense'] = ''' -Copyright %d <+YOU OR YOUR COMPANY+>. +Copyright %d ${copyrightholder}. This is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/gr-video-sdl/grc/video_block_tree.xml b/gr-video-sdl/grc/video_block_tree.xml index 442c68a787..f4f3e4199c 100644 --- a/gr-video-sdl/grc/video_block_tree.xml +++ b/gr-video-sdl/grc/video_block_tree.xml @@ -5,7 +5,7 @@ ################################################### --> <cat> - <name></name> <!-- Blank for Root Name --> + <name>[Core]</name> <cat> <name>Video</name> <block>video_sdl_sink</block> diff --git a/gr-vocoder/grc/vocoder_block_tree.xml b/gr-vocoder/grc/vocoder_block_tree.xml index c96a5cc687..07d8ba8fd1 100644 --- a/gr-vocoder/grc/vocoder_block_tree.xml +++ b/gr-vocoder/grc/vocoder_block_tree.xml @@ -27,7 +27,7 @@ ################################################### --> <cat> - <name></name> <!-- Blank for Root Name --> + <name>[Core]</name> <cat> <name>Audio</name> <block>vocoder_alaw_decode_bs</block> diff --git a/gr-wxgui/grc/notebook.xml b/gr-wxgui/grc/notebook.xml index 5f30c06201..1e4b0d0679 100644 --- a/gr-wxgui/grc/notebook.xml +++ b/gr-wxgui/grc/notebook.xml @@ -7,7 +7,7 @@ <block> <name>WX GUI Notebook</name> <key>notebook</key> - <category>GUI Widgets/WX</category> + <category>[Core]/GUI Widgets/WX</category> <import>from grc_gnuradio import wxgui as grc_wxgui</import> <make>#set $parent = $notebook() and 'self.%s.GetPage(%s)'%$notebook() or 'self' self.$(id) = wx.Notebook($(parent).GetWin(), style=$style) diff --git a/gr-wxgui/grc/variable_check_box.xml b/gr-wxgui/grc/variable_check_box.xml index 75dae78215..da8bb8b8d5 100644 --- a/gr-wxgui/grc/variable_check_box.xml +++ b/gr-wxgui/grc/variable_check_box.xml @@ -8,7 +8,7 @@ <block> <name>WX GUI Check Box</name> <key>variable_check_box</key> - <category>GUI Widgets/WX</category> + <category>[Core]/GUI Widgets/WX</category> <import>from gnuradio.wxgui import forms</import> <var_make>self.$(id) = $(id) = $value</var_make> <make>#set $parent = $notebook() and 'self.%s.GetPage(%s)'%$notebook() or 'self' diff --git a/gr-wxgui/grc/variable_chooser.xml b/gr-wxgui/grc/variable_chooser.xml index ef75a04337..216be01e08 100644 --- a/gr-wxgui/grc/variable_chooser.xml +++ b/gr-wxgui/grc/variable_chooser.xml @@ -9,7 +9,7 @@ <block> <name>WX GUI Chooser</name> <key>variable_chooser</key> - <category>GUI Widgets/WX</category> + <category>[Core]/GUI Widgets/WX</category> <import>from gnuradio.wxgui import forms</import> <var_make>self.$(id) = $(id) = $value</var_make> <make>#set $parent = $notebook() and 'self.%s.GetPage(%s)'%$notebook() or 'self' diff --git a/gr-wxgui/grc/variable_slider.xml b/gr-wxgui/grc/variable_slider.xml index 96c0b5ec70..29f3182d0a 100644 --- a/gr-wxgui/grc/variable_slider.xml +++ b/gr-wxgui/grc/variable_slider.xml @@ -8,7 +8,7 @@ <block> <name>WX GUI Slider</name> <key>variable_slider</key> - <category>GUI Widgets/WX</category> + <category>[Core]/GUI Widgets/WX</category> <import>from gnuradio.wxgui import forms</import> <var_make>self.$(id) = $(id) = $value</var_make> <make>#set $parent = $notebook() and 'self.%s.GetPage(%s)'%$notebook() or 'self' diff --git a/gr-wxgui/grc/variable_static_text.xml b/gr-wxgui/grc/variable_static_text.xml index d68b00a1dd..e47f5c3731 100644 --- a/gr-wxgui/grc/variable_static_text.xml +++ b/gr-wxgui/grc/variable_static_text.xml @@ -8,7 +8,7 @@ <block> <name>WX GUI Static Text</name> <key>variable_static_text</key> - <category>GUI Widgets/WX</category> + <category>[Core]/GUI Widgets/WX</category> <import>from gnuradio.wxgui import forms</import> <var_make>self.$(id) = $(id) = $value</var_make> <make>#set $parent = $notebook() and 'self.%s.GetPage(%s)'%$notebook() or 'self' diff --git a/gr-wxgui/grc/variable_text_box.xml b/gr-wxgui/grc/variable_text_box.xml index a2cf1a8220..8c8119433a 100644 --- a/gr-wxgui/grc/variable_text_box.xml +++ b/gr-wxgui/grc/variable_text_box.xml @@ -8,7 +8,7 @@ <block> <name>WX GUI Text Box</name> <key>variable_text_box</key> - <category>GUI Widgets/WX</category> + <category>[Core]/GUI Widgets/WX</category> <import>from gnuradio.wxgui import forms</import> <var_make>self.$(id) = $(id) = $value</var_make> <make>#set $parent = $notebook() and 'self.%s.GetPage(%s)'%$notebook() or 'self' diff --git a/gr-wxgui/grc/wxgui_constellationsink2.xml b/gr-wxgui/grc/wxgui_constellationsink2.xml index d0314ca547..999ebbe2cf 100644 --- a/gr-wxgui/grc/wxgui_constellationsink2.xml +++ b/gr-wxgui/grc/wxgui_constellationsink2.xml @@ -7,7 +7,7 @@ <block> <name>WX GUI Constellation Sink</name> <key>wxgui_constellationsink2</key> - <category>Instrumentation/WX</category> + <category>[Core]/Instrumentation/WX</category> <import>from gnuradio.wxgui import constsink_gl</import> <import>from gnuradio import wxgui</import> <make>#set $parent = $notebook() and 'self.%s.GetPage(%s)'%$notebook() or 'self' diff --git a/gr-wxgui/grc/wxgui_fftsink2.xml b/gr-wxgui/grc/wxgui_fftsink2.xml index 5f82670ac5..11e9c7255e 100644 --- a/gr-wxgui/grc/wxgui_fftsink2.xml +++ b/gr-wxgui/grc/wxgui_fftsink2.xml @@ -7,7 +7,7 @@ <block> <name>WX GUI FFT Sink</name> <key>wxgui_fftsink2</key> - <category>Instrumentation/WX</category> + <category>[Core]/Instrumentation/WX</category> <import>from gnuradio.fft import window</import> <import>from gnuradio.wxgui import fftsink2</import> <import>from gnuradio import wxgui</import> diff --git a/gr-wxgui/grc/wxgui_histosink2.xml b/gr-wxgui/grc/wxgui_histosink2.xml index 4fc331ab8e..63bfc000ae 100644 --- a/gr-wxgui/grc/wxgui_histosink2.xml +++ b/gr-wxgui/grc/wxgui_histosink2.xml @@ -7,7 +7,7 @@ <block> <name>WX GUI Histo Sink</name> <key>wxgui_histosink2</key> - <category>Instrumentation/WX</category> + <category>[Core]/Instrumentation/WX</category> <import>from gnuradio.wxgui import histosink_gl</import> <import>from gnuradio import wxgui</import> <make>#set $parent = $notebook() and 'self.%s.GetPage(%s)'%$notebook() or 'self' diff --git a/gr-wxgui/grc/wxgui_numbersink2.xml b/gr-wxgui/grc/wxgui_numbersink2.xml index 4697164088..6f4d4a574a 100644 --- a/gr-wxgui/grc/wxgui_numbersink2.xml +++ b/gr-wxgui/grc/wxgui_numbersink2.xml @@ -7,7 +7,7 @@ <block> <name>WX GUI Number Sink</name> <key>wxgui_numbersink2</key> - <category>Instrumentation/WX</category> + <category>[Core]/Instrumentation/WX</category> <import>from gnuradio.wxgui import numbersink2</import> <import>from gnuradio import wxgui</import> <make>#set $parent = $notebook() and 'self.%s.GetPage(%s)'%$notebook() or 'self' diff --git a/gr-wxgui/grc/wxgui_scopesink2.xml b/gr-wxgui/grc/wxgui_scopesink2.xml index ea43d3a690..7e896897d6 100644 --- a/gr-wxgui/grc/wxgui_scopesink2.xml +++ b/gr-wxgui/grc/wxgui_scopesink2.xml @@ -7,7 +7,7 @@ <block> <name>WX GUI Scope Sink</name> <key>wxgui_scopesink2</key> - <category>Instrumentation/WX</category> + <category>[Core]/Instrumentation/WX</category> <import>from gnuradio.wxgui import scopesink2</import> <import>from gnuradio import wxgui</import> <make>#set $parent = $notebook() and 'self.%s.GetPage(%s)'%$notebook() or 'self' diff --git a/gr-wxgui/grc/wxgui_termsink.xml b/gr-wxgui/grc/wxgui_termsink.xml index 1367f4af8a..54fedde081 100644 --- a/gr-wxgui/grc/wxgui_termsink.xml +++ b/gr-wxgui/grc/wxgui_termsink.xml @@ -7,7 +7,7 @@ <block> <name>WX GUI Terminal Sink</name> <key>wxgui_termsink</key> - <category>Instrumentation/WX</category> + <category>[Core]/Instrumentation/WX</category> <import>from gnuradio.wxgui import termsink</import> <import>from gnuradio import wxgui</import> diff --git a/gr-wxgui/grc/wxgui_waterfallsink2.xml b/gr-wxgui/grc/wxgui_waterfallsink2.xml index bee28f23fa..5b56f98ebe 100644 --- a/gr-wxgui/grc/wxgui_waterfallsink2.xml +++ b/gr-wxgui/grc/wxgui_waterfallsink2.xml @@ -7,7 +7,7 @@ <block> <name>WX GUI Waterfall Sink</name> <key>wxgui_waterfallsink2</key> - <category>Instrumentation/WX</category> + <category>[Core]/Instrumentation/WX</category> <import>from gnuradio.fft import window</import> <import>from gnuradio.wxgui import waterfallsink2</import> <import>from gnuradio import wxgui</import> diff --git a/gr-zeromq/grc/zeromq_pub_msg_sink.xml b/gr-zeromq/grc/zeromq_pub_msg_sink.xml index 8f541ce731..245ac37180 100644 --- a/gr-zeromq/grc/zeromq_pub_msg_sink.xml +++ b/gr-zeromq/grc/zeromq_pub_msg_sink.xml @@ -2,7 +2,7 @@ <block> <name>ZMQ PUB Message Sink</name> <key>zeromq_pub_msg_sink</key> - <category>ZeroMQ Interfaces</category> + <category>[Core]/ZeroMQ Interfaces</category> <import>from gnuradio import zeromq</import> <make>zeromq.pub_msg_sink($address, $timeout)</make> diff --git a/gr-zeromq/grc/zeromq_pub_sink.xml b/gr-zeromq/grc/zeromq_pub_sink.xml index 1b2f9ec52a..af130eeb21 100644 --- a/gr-zeromq/grc/zeromq_pub_sink.xml +++ b/gr-zeromq/grc/zeromq_pub_sink.xml @@ -2,7 +2,7 @@ <block> <name>ZMQ PUB Sink</name> <key>zeromq_pub_sink</key> - <category>ZeroMQ Interfaces</category> + <category>[Core]/ZeroMQ Interfaces</category> <import>from gnuradio import zeromq</import> <make>zeromq.pub_sink($type.itemsize, $vlen, $address, $timeout, $pass_tags, $hwm)</make> diff --git a/gr-zeromq/grc/zeromq_pull_msg_source.xml b/gr-zeromq/grc/zeromq_pull_msg_source.xml index c0a6ca5a17..c6004d987e 100644 --- a/gr-zeromq/grc/zeromq_pull_msg_source.xml +++ b/gr-zeromq/grc/zeromq_pull_msg_source.xml @@ -2,7 +2,7 @@ <block> <name>ZMQ PULL Message Source</name> <key>zeromq_pull_msg_source</key> - <category>ZeroMQ Interfaces</category> + <category>[Core]/ZeroMQ Interfaces</category> <import>from gnuradio import zeromq</import> <make>zeromq.pull_msg_source($address, $timeout)</make> diff --git a/gr-zeromq/grc/zeromq_pull_source.xml b/gr-zeromq/grc/zeromq_pull_source.xml index 8158b47361..f2c8b61d86 100644 --- a/gr-zeromq/grc/zeromq_pull_source.xml +++ b/gr-zeromq/grc/zeromq_pull_source.xml @@ -2,7 +2,7 @@ <block> <name>ZMQ PULL Source</name> <key>zeromq_pull_source</key> - <category>ZeroMQ Interfaces</category> + <category>[Core]/ZeroMQ Interfaces</category> <import>from gnuradio import zeromq</import> <make>zeromq.pull_source($type.itemsize, $vlen, $address, $timeout, $pass_tags, $hwm)</make> diff --git a/gr-zeromq/grc/zeromq_push_msg_sink.xml b/gr-zeromq/grc/zeromq_push_msg_sink.xml index 65626c0761..283a250064 100644 --- a/gr-zeromq/grc/zeromq_push_msg_sink.xml +++ b/gr-zeromq/grc/zeromq_push_msg_sink.xml @@ -2,7 +2,7 @@ <block> <name>ZMQ PUSH Message Sink</name> <key>zeromq_push_msg_sink</key> - <category>ZeroMQ Interfaces</category> + <category>[Core]/ZeroMQ Interfaces</category> <import>from gnuradio import zeromq</import> <make>zeromq.push_msg_sink($address, $timeout)</make> diff --git a/gr-zeromq/grc/zeromq_push_sink.xml b/gr-zeromq/grc/zeromq_push_sink.xml index 528da94ee6..8277c02c22 100644 --- a/gr-zeromq/grc/zeromq_push_sink.xml +++ b/gr-zeromq/grc/zeromq_push_sink.xml @@ -2,7 +2,7 @@ <block> <name>ZMQ PUSH Sink</name> <key>zeromq_push_sink</key> - <category>ZeroMQ Interfaces</category> + <category>[Core]/ZeroMQ Interfaces</category> <import>from gnuradio import zeromq</import> <make>zeromq.push_sink($type.itemsize, $vlen, $address, $timeout, $pass_tags, $hwm)</make> diff --git a/gr-zeromq/grc/zeromq_rep_msg_sink.xml b/gr-zeromq/grc/zeromq_rep_msg_sink.xml index f978f442a7..37515b39bc 100644 --- a/gr-zeromq/grc/zeromq_rep_msg_sink.xml +++ b/gr-zeromq/grc/zeromq_rep_msg_sink.xml @@ -2,7 +2,7 @@ <block> <name>ZMQ REP Message Sink</name> <key>zeromq_rep_msg_sink</key> - <category>ZeroMQ Interfaces</category> + <category>[Core]/ZeroMQ Interfaces</category> <import>from gnuradio import zeromq</import> <make>zeromq.rep_msg_sink($address, $timeout)</make> diff --git a/gr-zeromq/grc/zeromq_rep_sink.xml b/gr-zeromq/grc/zeromq_rep_sink.xml index db735a37bb..17c824156c 100644 --- a/gr-zeromq/grc/zeromq_rep_sink.xml +++ b/gr-zeromq/grc/zeromq_rep_sink.xml @@ -2,7 +2,7 @@ <block> <name>ZMQ REP Sink</name> <key>zeromq_rep_sink</key> - <category>ZeroMQ Interfaces</category> + <category>[Core]/ZeroMQ Interfaces</category> <import>from gnuradio import zeromq</import> <make>zeromq.rep_sink($type.itemsize, $vlen, $address, $timeout, $pass_tags, $hwm)</make> diff --git a/gr-zeromq/grc/zeromq_req_msg_source.xml b/gr-zeromq/grc/zeromq_req_msg_source.xml index 3ba7488223..ae34c30dd0 100644 --- a/gr-zeromq/grc/zeromq_req_msg_source.xml +++ b/gr-zeromq/grc/zeromq_req_msg_source.xml @@ -2,7 +2,7 @@ <block> <name>ZMQ REQ Message Source</name> <key>zeromq_req_msg_source</key> - <category>ZeroMQ Interfaces</category> + <category>[Core]/ZeroMQ Interfaces</category> <import>from gnuradio import zeromq</import> <make>zeromq.req_msg_source($address, $timeout)</make> diff --git a/gr-zeromq/grc/zeromq_req_source.xml b/gr-zeromq/grc/zeromq_req_source.xml index 2ef224399d..0fc1aa1488 100644 --- a/gr-zeromq/grc/zeromq_req_source.xml +++ b/gr-zeromq/grc/zeromq_req_source.xml @@ -2,7 +2,7 @@ <block> <name>ZMQ REQ Source</name> <key>zeromq_req_source</key> - <category>ZeroMQ Interfaces</category> + <category>[Core]/ZeroMQ Interfaces</category> <import>from gnuradio import zeromq</import> <make>zeromq.req_source($type.itemsize, $vlen, $address, $timeout, $pass_tags, $hwm)</make> diff --git a/gr-zeromq/grc/zeromq_sub_msg_source.xml b/gr-zeromq/grc/zeromq_sub_msg_source.xml index 32a1c9862b..475537f5fe 100644 --- a/gr-zeromq/grc/zeromq_sub_msg_source.xml +++ b/gr-zeromq/grc/zeromq_sub_msg_source.xml @@ -2,7 +2,7 @@ <block> <name>ZMQ SUB Message Source</name> <key>zeromq_sub_msg_source</key> - <category>ZeroMQ Interfaces</category> + <category>[Core]/ZeroMQ Interfaces</category> <import>from gnuradio import zeromq</import> <make>zeromq.sub_msg_source($address, $timeout)</make> diff --git a/gr-zeromq/grc/zeromq_sub_source.xml b/gr-zeromq/grc/zeromq_sub_source.xml index 268a8938d5..31e6170c6a 100644 --- a/gr-zeromq/grc/zeromq_sub_source.xml +++ b/gr-zeromq/grc/zeromq_sub_source.xml @@ -2,7 +2,7 @@ <block> <name>ZMQ SUB Source</name> <key>zeromq_sub_source</key> - <category>ZeroMQ Interfaces</category> + <category>[Core]/ZeroMQ Interfaces</category> <import>from gnuradio import zeromq</import> <make>zeromq.sub_source($type.itemsize, $vlen, $address, $timeout, $pass_tags, $hwm)</make> diff --git a/grc/CMakeLists.txt b/grc/CMakeLists.txt index 859b9e9045..4c782a7f7d 100644 --- a/grc/CMakeLists.txt +++ b/grc/CMakeLists.txt @@ -96,8 +96,10 @@ install( COMPONENT "grc" ) +file(GLOB py_files "*.py") + GR_PYTHON_INSTALL( - FILES __init__.py + FILES ${py_files} DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc COMPONENT "grc" ) @@ -133,13 +135,9 @@ endif(WIN32) ######################################################################## # Add subdirectories ######################################################################## -add_subdirectory(base) add_subdirectory(blocks) -add_subdirectory(freedesktop) -add_subdirectory(grc_gnuradio) add_subdirectory(gui) -add_subdirectory(python) +add_subdirectory(core) add_subdirectory(scripts) -add_subdirectory(examples) endif(ENABLE_GRC) diff --git a/grc/__main__.py b/grc/__main__.py new file mode 100644 index 0000000000..899d0195d1 --- /dev/null +++ b/grc/__main__.py @@ -0,0 +1,20 @@ +# Copyright 2016 Free Software Foundation, Inc. +# This file is part of GNU Radio +# +# GNU Radio Companion 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 2 +# of the License, or (at your option) any later version. +# +# GNU Radio Companion 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 + +from .main import main + +main() diff --git a/grc/base/Block.py b/grc/base/Block.py deleted file mode 100644 index 77c3145173..0000000000 --- a/grc/base/Block.py +++ /dev/null @@ -1,542 +0,0 @@ -""" -Copyright 2008-2011 Free Software Foundation, Inc. -This file is part of GNU Radio - -GNU Radio Companion 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 2 -of the License, or (at your option) any later version. - -GNU Radio Companion 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 -""" - -from . import odict -from . Constants import ADVANCED_PARAM_TAB, DEFAULT_PARAM_TAB -from . Constants import BLOCK_FLAG_THROTTLE, BLOCK_FLAG_DISABLE_BYPASS -from . Constants import BLOCK_ENABLED, BLOCK_BYPASSED, BLOCK_DISABLED -from Element import Element - -from Cheetah.Template import Template -from UserDict import UserDict -from itertools import imap - - -class TemplateArg(UserDict): - """ - A cheetah template argument created from a param. - The str of this class evaluates to the param's to code method. - The use of this class as a dictionary (enum only) will reveal the enum opts. - The __call__ or () method can return the param evaluated to a raw python data type. - """ - - def __init__(self, param): - UserDict.__init__(self) - self._param = param - if param.is_enum(): - for key in param.get_opt_keys(): - self[key] = str(param.get_opt(key)) - - def __str__(self): - return str(self._param.to_code()) - - def __call__(self): - return self._param.get_evaluated() - - -def _get_keys(lst): - return [elem.get_key() for elem in lst] - - -def _get_elem(lst, key): - try: return lst[_get_keys(lst).index(key)] - except ValueError: raise ValueError, 'Key "%s" not found in %s.'%(key, _get_keys(lst)) - - -class Block(Element): - - def __init__(self, flow_graph, n): - """ - Make a new block from nested data. - - Args: - flow: graph the parent element - n: the nested odict - - Returns: - block a new block - """ - #build the block - Element.__init__(self, flow_graph) - #grab the data - params = n.findall('param') - sources = n.findall('source') - sinks = n.findall('sink') - self._name = n.find('name') - self._key = n.find('key') - self._category = n.find('category') or '' - self._flags = n.find('flags') or '' - # Backwards compatibility - if n.find('throttle') and BLOCK_FLAG_THROTTLE not in self._flags: - self._flags += BLOCK_FLAG_THROTTLE - self._grc_source = n.find('grc_source') or '' - self._block_wrapper_path = n.find('block_wrapper_path') - self._bussify_sink = n.find('bus_sink') - self._bussify_source = n.find('bus_source') - self._var_value = n.find('var_value') or '$value' - - # get list of param tabs - n_tabs = n.find('param_tab_order') or None - self._param_tab_labels = n_tabs.findall('tab') if n_tabs is not None else [DEFAULT_PARAM_TAB] - - #create the param objects - self._params = list() - #add the id param - self.get_params().append(self.get_parent().get_parent().Param( - block=self, - n=odict({ - 'name': 'ID', - 'key': 'id', - 'type': 'id', - }) - )) - self.get_params().append(self.get_parent().get_parent().Param( - block=self, - n=odict({ - 'name': 'Enabled', - 'key': '_enabled', - 'type': 'raw', - 'value': 'True', - 'hide': 'all', - }) - )) - for param in imap(lambda n: self.get_parent().get_parent().Param(block=self, n=n), params): - key = param.get_key() - #test against repeated keys - if key in self.get_param_keys(): - raise Exception, 'Key "%s" already exists in params'%key - #store the param - self.get_params().append(param) - #create the source objects - self._sources = list() - for source in map(lambda n: self.get_parent().get_parent().Port(block=self, n=n, dir='source'), sources): - key = source.get_key() - #test against repeated keys - if key in self.get_source_keys(): - raise Exception, 'Key "%s" already exists in sources'%key - #store the port - self.get_sources().append(source) - self.back_ofthe_bus(self.get_sources()) - #create the sink objects - self._sinks = list() - for sink in map(lambda n: self.get_parent().get_parent().Port(block=self, n=n, dir='sink'), sinks): - key = sink.get_key() - #test against repeated keys - if key in self.get_sink_keys(): - raise Exception, 'Key "%s" already exists in sinks'%key - #store the port - self.get_sinks().append(sink) - self.back_ofthe_bus(self.get_sinks()) - self.current_bus_structure = {'source':'','sink':''}; - - # Virtual source/sink and pad source/sink blocks are - # indistinguishable from normal GR blocks. Make explicit - # checks for them here since they have no work function or - # buffers to manage. - is_virtual_or_pad = self._key in ( - "virtual_source", "virtual_sink", "pad_source", "pad_sink") - is_variable = self._key.startswith('variable') - - # Disable blocks that are virtual/pads or variables - if is_virtual_or_pad or is_variable: - self._flags += BLOCK_FLAG_DISABLE_BYPASS - - if not (is_virtual_or_pad or is_variable or self._key == 'options'): - self.get_params().append(self.get_parent().get_parent().Param( - block=self, - n=odict({'name': 'Block Alias', - 'key': 'alias', - 'type': 'string', - 'hide': 'part', - 'tab': ADVANCED_PARAM_TAB - }) - )) - - if (len(sources) or len(sinks)) and not is_virtual_or_pad: - self.get_params().append(self.get_parent().get_parent().Param( - block=self, - n=odict({'name': 'Core Affinity', - 'key': 'affinity', - 'type': 'int_vector', - 'hide': 'part', - 'tab': ADVANCED_PARAM_TAB - }) - )) - if len(sources) and not is_virtual_or_pad: - self.get_params().append(self.get_parent().get_parent().Param( - block=self, - n=odict({'name': 'Min Output Buffer', - 'key': 'minoutbuf', - 'type': 'int', - 'hide': 'part', - 'value': '0', - 'tab': ADVANCED_PARAM_TAB - }) - )) - self.get_params().append(self.get_parent().get_parent().Param( - block=self, - n=odict({'name': 'Max Output Buffer', - 'key': 'maxoutbuf', - 'type': 'int', - 'hide': 'part', - 'value': '0', - 'tab': ADVANCED_PARAM_TAB - }) - )) - - self.get_params().append(self.get_parent().get_parent().Param( - block=self, - n=odict({'name': 'Comment', - 'key': 'comment', - 'type': '_multiline', - 'hide': 'part', - 'value': '', - 'tab': ADVANCED_PARAM_TAB - }) - )) - - def back_ofthe_bus(self, portlist): - portlist.sort(key=lambda p: p._type == 'bus') - - def filter_bus_port(self, ports): - buslist = [p for p in ports if p._type == 'bus'] - return buslist or ports - - # Main functions to get and set the block state - # Also kept get_enabled and set_enabled to keep compatibility - def get_state(self): - """ - Gets the block's current state. - - Returns: - ENABLED - 0 - BYPASSED - 1 - DISABLED - 2 - """ - try: return int(eval(self.get_param('_enabled').get_value())) - except: return BLOCK_ENABLED - - def set_state(self, state): - """ - Sets the state for the block. - - Args: - ENABLED - 0 - BYPASSED - 1 - DISABLED - 2 - """ - if state in [BLOCK_ENABLED, BLOCK_BYPASSED, BLOCK_DISABLED]: - self.get_param('_enabled').set_value(str(state)) - else: - self.get_param('_enabled').set_value(str(BLOCK_ENABLED)) - - # Enable/Disable Aliases - def get_enabled(self): - """ - Get the enabled state of the block. - - Returns: - true for enabled - """ - return not (self.get_state() == BLOCK_DISABLED) - - def set_enabled(self, enabled): - """ - Set the enabled state of the block. - - Args: - enabled: true for enabled - - Returns: - True if block changed state - """ - old_state = self.get_state() - new_state = BLOCK_ENABLED if enabled else BLOCK_DISABLED - self.set_state(new_state) - return old_state != new_state - - # Block bypassing - def get_bypassed(self): - """ - Check if the block is bypassed - """ - return self.get_state() == BLOCK_BYPASSED - - def set_bypassed(self): - """ - Bypass the block - - Returns: - True if block chagnes state - """ - if self.get_state() != BLOCK_BYPASSED and self.can_bypass(): - self.set_state(BLOCK_BYPASSED) - return True - return False - - def can_bypass(self): - """ Check the number of sinks and sources and see if this block can be bypassed """ - # Check to make sure this is a single path block - # Could possibly support 1 to many blocks - if len(self.get_sources()) != 1 or len(self.get_sinks()) != 1: - return False - if not (self.get_sources()[0].get_type() == self.get_sinks()[0].get_type()): - return False - if self.bypass_disabled(): - return False - return True - - def __str__(self): return 'Block - %s - %s(%s)'%(self.get_id(), self.get_name(), self.get_key()) - - def get_id(self): return self.get_param('id').get_value() - def is_block(self): return True - def get_name(self): return self._name - def get_key(self): return self._key - def get_category(self): return self._category - def set_category(self, cat): self._category = cat - def get_doc(self): return '' - def get_ports(self): return self.get_sources() + self.get_sinks() - def get_ports_gui(self): return self.filter_bus_port(self.get_sources()) + self.filter_bus_port(self.get_sinks()); - def get_children(self): return self.get_ports() + self.get_params() - def get_children_gui(self): return self.get_ports_gui() + self.get_params() - def get_block_wrapper_path(self): return self._block_wrapper_path - def get_comment(self): return self.get_param('comment').get_value() - - def get_flags(self): return self._flags - def throtteling(self): return BLOCK_FLAG_THROTTLE in self._flags - def bypass_disabled(self): return BLOCK_FLAG_DISABLE_BYPASS in self._flags - - ############################################## - # Access Params - ############################################## - def get_param_tab_labels(self): return self._param_tab_labels - def get_param_keys(self): return _get_keys(self._params) - def get_param(self, key): return _get_elem(self._params, key) - def get_params(self): return self._params - def has_param(self, key): - try: - _get_elem(self._params, key); - return True; - except: - return False; - - ############################################## - # Access Sinks - ############################################## - def get_sink_keys(self): return _get_keys(self._sinks) - def get_sink(self, key): return _get_elem(self._sinks, key) - def get_sinks(self): return self._sinks - def get_sinks_gui(self): return self.filter_bus_port(self.get_sinks()) - - ############################################## - # Access Sources - ############################################## - def get_source_keys(self): return _get_keys(self._sources) - def get_source(self, key): return _get_elem(self._sources, key) - def get_sources(self): return self._sources - def get_sources_gui(self): return self.filter_bus_port(self.get_sources()); - - def get_connections(self): - return sum([port.get_connections() for port in self.get_ports()], []) - - def resolve_dependencies(self, tmpl): - """ - Resolve a paramater dependency with cheetah templates. - - Args: - tmpl: the string with dependencies - - Returns: - the resolved value - """ - tmpl = str(tmpl) - if '$' not in tmpl: return tmpl - n = dict((p.get_key(), TemplateArg(p)) for p in self.get_params()) - try: - return str(Template(tmpl, n)) - except Exception as err: - return "Template error: %s\n %s" % (tmpl, err) - - ############################################## - # Controller Modify - ############################################## - def type_controller_modify(self, direction): - """ - Change the type controller. - - Args: - direction: +1 or -1 - - Returns: - true for change - """ - changed = False - type_param = None - for param in filter(lambda p: p.is_enum(), self.get_params()): - children = self.get_ports() + self.get_params() - #priority to the type controller - if param.get_key() in ' '.join(map(lambda p: p._type, children)): type_param = param - #use param if type param is unset - if not type_param: type_param = param - if type_param: - #try to increment the enum by direction - try: - keys = type_param.get_option_keys() - old_index = keys.index(type_param.get_value()) - new_index = (old_index + direction + len(keys))%len(keys) - type_param.set_value(keys[new_index]) - changed = True - except: pass - return changed - - def port_controller_modify(self, direction): - """ - Change the port controller. - - Args: - direction: +1 or -1 - - Returns: - true for change - """ - return False - - def form_bus_structure(self, direc): - if direc == 'source': - get_p = self.get_sources; - get_p_gui = self.get_sources_gui; - bus_structure = self.get_bus_structure('source'); - else: - get_p = self.get_sinks; - get_p_gui = self.get_sinks_gui - bus_structure = self.get_bus_structure('sink'); - - struct = [range(len(get_p()))]; - if True in map(lambda a: isinstance(a.get_nports(), int), get_p()): - - - structlet = []; - last = 0; - for j in [i.get_nports() for i in get_p() if isinstance(i.get_nports(), int)]: - structlet.extend(map(lambda a: a+last, range(j))); - last = structlet[-1] + 1; - struct = [structlet]; - if bus_structure: - - struct = bus_structure - - self.current_bus_structure[direc] = struct; - return struct - - def bussify(self, n, direc): - if direc == 'source': - get_p = self.get_sources; - get_p_gui = self.get_sources_gui; - bus_structure = self.get_bus_structure('source'); - else: - get_p = self.get_sinks; - get_p_gui = self.get_sinks_gui - bus_structure = self.get_bus_structure('sink'); - - - for elt in get_p(): - for connect in elt.get_connections(): - self.get_parent().remove_element(connect); - - - - - - - if (not 'bus' in map(lambda a: a.get_type(), get_p())) and len(get_p()) > 0: - - struct = self.form_bus_structure(direc); - self.current_bus_structure[direc] = struct; - if get_p()[0].get_nports(): - n['nports'] = str(1); - - for i in range(len(struct)): - n['key'] = str(len(get_p())); - n = odict(n); - port = self.get_parent().get_parent().Port(block=self, n=n, dir=direc); - get_p().append(port); - - - - - elif 'bus' in map(lambda a: a.get_type(), get_p()): - for elt in get_p_gui(): - get_p().remove(elt); - self.current_bus_structure[direc] = '' - ############################################## - ## Import/Export Methods - ############################################## - def export_data(self): - """ - Export this block's params to nested data. - - Returns: - a nested data odict - """ - n = odict() - n['key'] = self.get_key() - n['param'] = map(lambda p: p.export_data(), sorted(self.get_params(), key=str)) - if 'bus' in map(lambda a: a.get_type(), self.get_sinks()): - n['bus_sink'] = str(1); - if 'bus' in map(lambda a: a.get_type(), self.get_sources()): - n['bus_source'] = str(1); - return n - - def import_data(self, n): - """ - Import this block's params from nested data. - Any param keys that do not exist will be ignored. - Since params can be dynamically created based another param, - call rewrite, and repeat the load until the params stick. - This call to rewrite will also create any dynamic ports - that are needed for the connections creation phase. - - Args: - n: the nested data odict - """ - get_hash = lambda: hash(tuple(map(hash, self.get_params()))) - my_hash = 0 - while get_hash() != my_hash: - params_n = n.findall('param') - for param_n in params_n: - key = param_n.find('key') - value = param_n.find('value') - #the key must exist in this block's params - if key in self.get_param_keys(): - self.get_param(key).set_value(value) - #store hash and call rewrite - my_hash = get_hash() - self.rewrite() - bussinks = n.findall('bus_sink'); - if len(bussinks) > 0 and not self._bussify_sink: - self.bussify({'name':'bus','type':'bus'}, 'sink') - elif len(bussinks) > 0: - self.bussify({'name':'bus','type':'bus'}, 'sink') - self.bussify({'name':'bus','type':'bus'}, 'sink') - bussrcs = n.findall('bus_source'); - if len(bussrcs) > 0 and not self._bussify_source: - self.bussify({'name':'bus','type':'bus'}, 'source') - elif len(bussrcs) > 0: - self.bussify({'name':'bus','type':'bus'}, 'source') - self.bussify({'name':'bus','type':'bus'}, 'source') diff --git a/grc/base/Constants.py b/grc/base/Constants.py deleted file mode 100644 index 1e83de63b5..0000000000 --- a/grc/base/Constants.py +++ /dev/null @@ -1,50 +0,0 @@ -""" -Copyright 2008, 2009 Free Software Foundation, Inc. -This file is part of GNU Radio - -GNU Radio Companion 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 2 -of the License, or (at your option) any later version. - -GNU Radio Companion 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 os - -#data files -DATA_DIR = os.path.dirname(__file__) -FLOW_GRAPH_DTD = os.path.join(DATA_DIR, 'flow_graph.dtd') -BLOCK_TREE_DTD = os.path.join(DATA_DIR, 'block_tree.dtd') - -# file format versions: -# 0: undefined / legacy -# 1: non-numeric message port keys (label is used instead) -FLOW_GRAPH_FILE_FORMAT_VERSION = 1 - -# Param tabs -DEFAULT_PARAM_TAB = "General" -ADVANCED_PARAM_TAB = "Advanced" - -# Port domains -DOMAIN_DTD = os.path.join(DATA_DIR, 'domain.dtd') -GR_STREAM_DOMAIN = "gr_stream" -GR_MESSAGE_DOMAIN = "gr_message" -DEFAULT_DOMAIN = GR_STREAM_DOMAIN - -BLOCK_FLAG_THROTTLE = 'throttle' -BLOCK_FLAG_DISABLE_BYPASS = 'disable_bypass' -BLOCK_FLAG_NEED_QT_GUI = 'need_qt_gui' -BLOCK_FLAG_NEED_WX_GUI = 'need_ex_gui' - -# Block States -BLOCK_DISABLED = 0 -BLOCK_ENABLED = 1 -BLOCK_BYPASSED = 2 diff --git a/grc/base/FlowGraph.py b/grc/base/FlowGraph.py deleted file mode 100644 index 0398dfd011..0000000000 --- a/grc/base/FlowGraph.py +++ /dev/null @@ -1,484 +0,0 @@ -""" -Copyright 2008-2011 Free Software Foundation, Inc. -This file is part of GNU Radio - -GNU Radio Companion 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 2 -of the License, or (at your option) any later version. - -GNU Radio Companion 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 time -from operator import methodcaller -from itertools import ifilter - -from .. gui import Messages - -from . import odict -from .Element import Element -from .Constants import FLOW_GRAPH_FILE_FORMAT_VERSION - - -class FlowGraph(Element): - - def __init__(self, platform): - """ - Make a flow graph from the arguments. - - Args: - platform: a platforms with blocks and contrcutors - - Returns: - the flow graph object - """ - #initialize - Element.__init__(self, platform) - self._elements = [] - self._timestamp = time.ctime() - #inital blank import - self.import_data() - - def _get_unique_id(self, base_id=''): - """ - Get a unique id starting with the base id. - - Args: - base_id: the id starts with this and appends a count - - Returns: - a unique id - """ - index = 0 - while True: - id = '%s_%d' % (base_id, index) - index += 1 - #make sure that the id is not used by another block - if not filter(lambda b: b.get_id() == id, self.get_blocks()): return id - - def __str__(self): - return 'FlowGraph - %s(%s)' % (self.get_option('title'), self.get_option('id')) - - def get_complexity(self): - """ - Determines the complexity of a flowgraph - """ - dbal = 0 - block_list = self.get_blocks() - for block in block_list: - # Skip options block - if block.get_key() == 'options': - continue - - # Don't worry about optional sinks? - sink_list = filter(lambda c: not c.get_optional(), block.get_sinks()) - source_list = filter(lambda c: not c.get_optional(), block.get_sources()) - sinks = float(len(sink_list)) - sources = float(len(source_list)) - base = max(min(sinks, sources), 1) - - # Port ratio multiplier - if min(sinks, sources) > 0: - multi = sinks / sources - multi = (1 / multi) if multi > 1 else multi - else: - multi = 1 - - # Connection ratio multiplier - sink_multi = max(float(sum(map(lambda c: len(c.get_connections()), sink_list)) / max(sinks, 1.0)), 1.0) - source_multi = max(float(sum(map(lambda c: len(c.get_connections()), source_list)) / max(sources, 1.0)), 1.0) - dbal = dbal + (base * multi * sink_multi * source_multi) - - elements = float(len(self.get_elements())) - connections = float(len(self.get_connections())) - disabled_connections = len(filter(lambda c: not c.get_enabled(), self.get_connections())) - blocks = float(len(block_list)) - variables = elements - blocks - connections - enabled = float(len(self.get_enabled_blocks())) - - # Disabled multiplier - if enabled > 0: - disabled_multi = 1 / (max(1 - ((blocks - enabled) / max(blocks, 1)), 0.05)) - else: - disabled_multi = 1 - - # Connection multiplier (How many connections ) - if (connections - disabled_connections) > 0: - conn_multi = 1 / (max(1 - (disabled_connections / max(connections, 1)), 0.05)) - else: - conn_multi = 1 - - final = round(max((dbal - 1) * disabled_multi * conn_multi * connections, 0.0) / 1000000, 6) - return final - - def rewrite(self): - def refactor_bus_structure(): - - for block in self.get_blocks(): - for direc in ['source', 'sink']: - if direc == 'source': - get_p = block.get_sources; - get_p_gui = block.get_sources_gui; - bus_structure = block.form_bus_structure('source'); - else: - get_p = block.get_sinks; - get_p_gui = block.get_sinks_gui - bus_structure = block.form_bus_structure('sink'); - - if 'bus' in map(lambda a: a.get_type(), get_p_gui()): - if len(get_p_gui()) > len(bus_structure): - times = range(len(bus_structure), len(get_p_gui())); - for i in times: - for connect in get_p_gui()[-1].get_connections(): - block.get_parent().remove_element(connect); - get_p().remove(get_p_gui()[-1]); - elif len(get_p_gui()) < len(bus_structure): - n = {'name':'bus','type':'bus'}; - if True in map(lambda a: isinstance(a.get_nports(), int), get_p()): - n['nports'] = str(1); - - times = range(len(get_p_gui()), len(bus_structure)); - - for i in times: - n['key'] = str(len(get_p())); - n = odict(n); - port = block.get_parent().get_parent().Port(block=block, n=n, dir=direc); - get_p().append(port); - - for child in self.get_children(): child.rewrite() - refactor_bus_structure() - - def get_option(self, key): - """ - Get the option for a given key. - The option comes from the special options block. - - Args: - key: the param key for the options block - - Returns: - the value held by that param - """ - return self._options_block.get_param(key).get_evaluated() - - def is_flow_graph(self): return True - - ############################################## - ## Access Elements - ############################################## - def get_block(self, id): - for block in self.iter_blocks(): - if block.get_id() == id: - return block - raise KeyError('No block with ID {0!r}'.format(id)) - - def iter_blocks(self): - return ifilter(methodcaller('is_block'), self.get_elements()) - - def get_blocks(self): - return list(self.iter_blocks()) - - def iter_connections(self): - return ifilter(methodcaller('is_connection'), self.get_elements()) - - def get_connections(self): - return list(self.iter_connections()) - - def get_elements(self): - """ - Get a list of all the elements. - Always ensure that the options block is in the list (only once). - - Returns: - the element list - """ - options_block_count = self._elements.count(self._options_block) - if not options_block_count: - self._elements.append(self._options_block) - for i in range(options_block_count-1): - self._elements.remove(self._options_block) - return self._elements - - get_children = get_elements - - def iter_enabled_blocks(self): - """ - Get an iterator of all blocks that are enabled and not bypassed. - """ - return ifilter(methodcaller('get_enabled'), self.iter_blocks()) - - def get_enabled_blocks(self): - """ - Get a list of all blocks that are enabled and not bypassed. - - Returns: - a list of blocks - """ - return list(self.iter_enabled_blocks()) - - def get_bypassed_blocks(self): - """ - Get a list of all blocks that are bypassed. - - Returns: - a list of blocks - """ - return filter(methodcaller('get_bypassed'), self.iter_blocks()) - - def get_enabled_connections(self): - """ - Get a list of all connections that are enabled. - - Returns: - a list of connections - """ - return filter(methodcaller('get_enabled'), self.get_connections()) - - def get_new_block(self, key): - """ - Get a new block of the specified key. - Add the block to the list of elements. - - Args: - key: the block key - - Returns: - the new block or None if not found - """ - if key not in self.get_parent().get_block_keys(): return None - block = self.get_parent().get_new_block(self, key) - self.get_elements().append(block); - if block._bussify_sink: - block.bussify({'name':'bus','type':'bus'}, 'sink') - if block._bussify_source: - block.bussify({'name':'bus','type':'bus'}, 'source') - return block; - - def connect(self, porta, portb): - """ - Create a connection between porta and portb. - - Args: - porta: a port - portb: another port - @throw Exception bad connection - - Returns: - the new connection - """ - connection = self.get_parent().Connection(flow_graph=self, porta=porta, portb=portb) - self.get_elements().append(connection) - return connection - - def remove_element(self, element): - """ - Remove the element from the list of elements. - If the element is a port, remove the whole block. - If the element is a block, remove its connections. - If the element is a connection, just remove the connection. - """ - if element not in self.get_elements(): return - #found a port, set to parent signal block - if element.is_port(): - element = element.get_parent() - #remove block, remove all involved connections - if element.is_block(): - for port in element.get_ports(): - map(self.remove_element, port.get_connections()) - if element.is_connection(): - if element.is_bus(): - cons_list = [] - for i in map(lambda a: a.get_connections(), element.get_source().get_associated_ports()): - cons_list.extend(i); - map(self.remove_element, cons_list); - self.get_elements().remove(element) - - def evaluate(self, expr): - """ - Evaluate the expression. - - Args: - expr: the string expression - @throw NotImplementedError - """ - raise NotImplementedError - - ############################################## - ## Import/Export Methods - ############################################## - def export_data(self): - """ - Export this flow graph to nested data. - Export all block and connection data. - - Returns: - a nested data odict - """ - # sort blocks and connections for nicer diffs - blocks = sorted(self.iter_blocks(), key=lambda b: ( - b.get_key() != 'options', # options to the front - not b.get_key().startswith('variable'), # then vars - str(b) - )) - connections = sorted(self.get_connections(), key=str) - n = odict() - n['timestamp'] = self._timestamp - n['block'] = [b.export_data() for b in blocks] - n['connection'] = [c.export_data() for c in connections] - instructions = odict({ - 'created': self.get_parent().get_version_short(), - 'format': FLOW_GRAPH_FILE_FORMAT_VERSION, - }) - return odict({'flow_graph': n, '_instructions': instructions}) - - def import_data(self, n=None): - """ - Import blocks and connections into this flow graph. - Clear this flowgraph of all previous blocks and connections. - Any blocks or connections in error will be ignored. - - Args: - n: the nested data odict - """ - errors = False - self._elements = list() # remove previous elements - # set file format - try: - instructions = n.find('_instructions') or {} - file_format = int(instructions.get('format', '0')) or _guess_file_format_1(n) - except: - file_format = 0 - - fg_n = n and n.find('flow_graph') or odict() # use blank data if none provided - self._timestamp = fg_n.find('timestamp') or time.ctime() - - # build the blocks - self._options_block = self.get_parent().get_new_block(self, 'options') - for block_n in fg_n.findall('block'): - key = block_n.find('key') - block = self._options_block if key == 'options' else self.get_new_block(key) - - if not block: # looks like this block key cannot be found - # create a dummy block instead - block = self.get_new_block('dummy_block') - # Ugly ugly ugly - _initialize_dummy_block(block, block_n) - Messages.send_error_msg_load('Block key "%s" not found' % key) - - block.import_data(block_n) - - self.rewrite() # evaluate stuff like nports before adding connections - - # build the connections - def verify_and_get_port(key, block, dir): - ports = block.get_sinks() if dir == 'sink' else block.get_sources() - for port in ports: - if key == port.get_key(): - break - if not key.isdigit() and port.get_type() == '' and key == port.get_name(): - break - else: - if block.is_dummy_block(): - port = _dummy_block_add_port(block, key, dir) - else: - raise LookupError('%s key %r not in %s block keys' % (dir, key, dir)) - return port - - for connection_n in fg_n.findall('connection'): - # get the block ids and port keys - source_block_id = connection_n.find('source_block_id') - sink_block_id = connection_n.find('sink_block_id') - source_key = connection_n.find('source_key') - sink_key = connection_n.find('sink_key') - try: - source_block = self.get_block(source_block_id) - sink_block = self.get_block(sink_block_id) - - # fix old, numeric message ports keys - if file_format < 1: - source_key, sink_key = _update_old_message_port_keys( - source_key, sink_key, source_block, sink_block) - - # build the connection - source_port = verify_and_get_port(source_key, source_block, 'source') - sink_port = verify_and_get_port(sink_key, sink_block, 'sink') - self.connect(source_port, sink_port) - except LookupError as e: - Messages.send_error_load( - 'Connection between %s(%s) and %s(%s) could not be made.\n\t%s' % ( - source_block_id, source_key, sink_block_id, sink_key, e)) - errors = True - - self.rewrite() # global rewrite - return errors - - -def _update_old_message_port_keys(source_key, sink_key, source_block, sink_block): - """Backward compatibility for message port keys - - Message ports use their names as key (like in the 'connect' method). - Flowgraph files from former versions still have numeric keys stored for - message connections. These have to be replaced by the name of the - respective port. The correct message port is deduced from the integer - value of the key (assuming the order has not changed). - - The connection ends are updated only if both ends translate into a - message port. - """ - try: - # get ports using the "old way" (assuming liner indexed keys) - source_port = source_block.get_sources()[int(source_key)] - sink_port = sink_block.get_sinks()[int(sink_key)] - if source_port.get_type() == "message" and sink_port.get_type() == "message": - source_key, sink_key = source_port.get_key(), sink_port.get_key() - except (ValueError, IndexError): - pass - return source_key, sink_key # do nothing - - -def _guess_file_format_1(n): - """Try to guess the file format for flow-graph files without version tag""" - try: - has_non_numeric_message_keys = any(not ( - connection_n.find('source_key').isdigit() and - connection_n.find('sink_key').isdigit() - ) for connection_n in n.find('flow_graph').findall('connection')) - if has_non_numeric_message_keys: - return 1 - except: - pass - return 0 - - -def _initialize_dummy_block(block, block_n): - """This is so ugly... dummy-fy a block - - Modify block object to get the behaviour for a missing block - """ - block._key = block_n.find('key') - block.is_dummy_block = lambda: True - block.is_valid = lambda: False - block.get_enabled = lambda: False - for param_n in block_n.findall('param'): - if param_n['key'] not in block.get_param_keys(): - new_param_n = odict({'key': param_n['key'], 'name': param_n['key'], 'type': 'string'}) - block.get_params().append(block.get_parent().get_parent().Param(block=block, n=new_param_n)) - - -def _dummy_block_add_port(block, key, dir): - """This is so ugly... Add a port to a dummy-field block""" - port_n = odict({'name': '?', 'key': key, 'type': ''}) - port = block.get_parent().get_parent().Port(block=block, n=port_n, dir=dir) - if port.is_source(): - block.get_sources().append(port) - else: - block.get_sinks().append(port) - return port diff --git a/grc/base/Param.py b/grc/base/Param.py deleted file mode 100644 index 34dd36e790..0000000000 --- a/grc/base/Param.py +++ /dev/null @@ -1,205 +0,0 @@ -""" -Copyright 2008-2011 Free Software Foundation, Inc. -This file is part of GNU Radio - -GNU Radio Companion 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 2 -of the License, or (at your option) any later version. - -GNU Radio Companion 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 -""" - -from . import odict -from Element import Element - -def _get_keys(lst): return [elem.get_key() for elem in lst] -def _get_elem(lst, key): - try: return lst[_get_keys(lst).index(key)] - except ValueError: raise ValueError, 'Key "%s" not found in %s.'%(key, _get_keys(lst)) - -class Option(Element): - - def __init__(self, param, n): - Element.__init__(self, param) - self._name = n.find('name') - self._key = n.find('key') - self._opts = dict() - opts = n.findall('opt') - #test against opts when non enum - if not self.get_parent().is_enum() and opts: - raise Exception, 'Options for non-enum types cannot have sub-options' - #extract opts - for opt in opts: - #separate the key:value - try: key, value = opt.split(':') - except: raise Exception, 'Error separating "%s" into key:value'%opt - #test against repeated keys - if self._opts.has_key(key): - raise Exception, 'Key "%s" already exists in option'%key - #store the option - self._opts[key] = value - - def __str__(self): return 'Option %s(%s)'%(self.get_name(), self.get_key()) - def get_name(self): return self._name - def get_key(self): return self._key - - ############################################## - # Access Opts - ############################################## - def get_opt_keys(self): return self._opts.keys() - def get_opt(self, key): return self._opts[key] - def get_opts(self): return self._opts.values() - -class Param(Element): - - def __init__(self, block, n): - """ - Make a new param from nested data. - - Args: - block: the parent element - n: the nested odict - """ - # if the base key is a valid param key, copy its data and overlay this params data - base_key = n.find('base_key') - if base_key and base_key in block.get_param_keys(): - n_expanded = block.get_param(base_key)._n.copy() - n_expanded.update(n) - n = n_expanded - # save odict in case this param will be base for another - self._n = n - # parse the data - self._name = n.find('name') - self._key = n.find('key') - value = n.find('value') or '' - self._type = n.find('type') or 'raw' - self._hide = n.find('hide') or '' - self._tab_label = n.find('tab') or block.get_param_tab_labels()[0] - if not self._tab_label in block.get_param_tab_labels(): - block.get_param_tab_labels().append(self._tab_label) - #build the param - Element.__init__(self, block) - #create the Option objects from the n data - self._options = list() - for option in map(lambda o: Option(param=self, n=o), n.findall('option')): - key = option.get_key() - #test against repeated keys - if key in self.get_option_keys(): - raise Exception, 'Key "%s" already exists in options'%key - #store the option - self.get_options().append(option) - #test the enum options - if self.is_enum(): - #test against options with identical keys - if len(set(self.get_option_keys())) != len(self.get_options()): - raise Exception, 'Options keys "%s" are not unique.'%self.get_option_keys() - #test against inconsistent keys in options - opt_keys = self.get_options()[0].get_opt_keys() - for option in self.get_options(): - if set(opt_keys) != set(option.get_opt_keys()): - raise Exception, 'Opt keys "%s" are not identical across all options.'%opt_keys - #if a value is specified, it must be in the options keys - self._value = value if value or value in self.get_option_keys() else self.get_option_keys()[0] - if self.get_value() not in self.get_option_keys(): - raise Exception, 'The value "%s" is not in the possible values of "%s".'%(self.get_value(), self.get_option_keys()) - else: self._value = value or '' - self._default = value - - def validate(self): - """ - Validate the param. - The value must be evaluated and type must a possible type. - """ - Element.validate(self) - if self.get_type() not in self.get_types(): - self.add_error_message('Type "%s" is not a possible type.'%self.get_type()) - - def get_evaluated(self): raise NotImplementedError - - def to_code(self): - """ - Convert the value to code. - @throw NotImplementedError - """ - raise NotImplementedError - - def get_types(self): - """ - Get a list of all possible param types. - @throw NotImplementedError - """ - raise NotImplementedError - - def get_color(self): return '#FFFFFF' - def __str__(self): return 'Param - %s(%s)'%(self.get_name(), self.get_key()) - def is_param(self): return True - def get_name(self): return self.get_parent().resolve_dependencies(self._name).strip() - def get_key(self): return self._key - def get_hide(self): return self.get_parent().resolve_dependencies(self._hide).strip() - - def get_value(self): - value = self._value - if self.is_enum() and value not in self.get_option_keys(): - value = self.get_option_keys()[0] - self.set_value(value) - return value - - def set_value(self, value): self._value = str(value) #must be a string - - def set_default(self, value): - if self._default == self._value: - self.set_value(value) - self._default = str(value) - - def get_type(self): return self.get_parent().resolve_dependencies(self._type) - def get_tab_label(self): return self._tab_label - def is_enum(self): return self._type == 'enum' - - def __repr__(self): - """ - Get the repr (nice string format) for this param. - Just return the value (special case enum). - Derived classes can handle complex formatting. - - Returns: - the string representation - """ - if self.is_enum(): return self.get_option(self.get_value()).get_name() - return self.get_value() - - ############################################## - # Access Options - ############################################## - def get_option_keys(self): return _get_keys(self.get_options()) - def get_option(self, key): return _get_elem(self.get_options(), key) - def get_options(self): return self._options - - ############################################## - # Access Opts - ############################################## - def get_opt_keys(self): return self.get_option(self.get_value()).get_opt_keys() - def get_opt(self, key): return self.get_option(self.get_value()).get_opt(key) - def get_opts(self): return self.get_option(self.get_value()).get_opts() - - ############################################## - ## Import/Export Methods - ############################################## - def export_data(self): - """ - Export this param's key/value. - - Returns: - a nested data odict - """ - n = odict() - n['key'] = self.get_key() - n['value'] = self.get_value() - return n diff --git a/grc/base/Platform.py b/grc/base/Platform.py deleted file mode 100644 index 0cc3fcf1dd..0000000000 --- a/grc/base/Platform.py +++ /dev/null @@ -1,271 +0,0 @@ -""" -Copyright 2008-2011 Free Software Foundation, Inc. -This file is part of GNU Radio - -GNU Radio Companion 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 2 -of the License, or (at your option) any later version. - -GNU Radio Companion 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 os -import sys -from .. base import ParseXML, odict -from Element import Element as _Element -from FlowGraph import FlowGraph as _FlowGraph -from Connection import Connection as _Connection -from Block import Block as _Block -from Port import Port as _Port -from Param import Param as _Param -from Constants import BLOCK_TREE_DTD, FLOW_GRAPH_DTD, DOMAIN_DTD - - -class Platform(_Element): - def __init__(self, name, version, key, - block_paths, block_dtd, default_flow_graph, generator, - license='', website=None, colors=None): - """ - Make a platform from the arguments. - - Args: - name: the platform name - version: the version string - key: the unique platform key - block_paths: the file paths to blocks in this platform - block_dtd: the dtd validator for xml block wrappers - default_flow_graph: the default flow graph file path - generator: the generator class for this platform - colors: a list of title, color_spec tuples - license: a multi-line license (first line is copyright) - website: the website url for this platform - - Returns: - a platform object - """ - _Element.__init__(self) - self._name = name - # Save the verion string to the first - self._version = version[0] - self._version_major = version[1] - self._version_api = version[2] - self._version_minor = version[3] - self._version_short = version[1] + "." + version[2] + "." + version[3] - - self._key = key - self._license = license - self._website = website - self._block_paths = list(set(block_paths)) - self._block_dtd = block_dtd - self._default_flow_graph = default_flow_graph - self._generator = generator - self._colors = colors or [] - #create a dummy flow graph for the blocks - self._flow_graph = _Element(self) - - self._blocks = None - self._blocks_n = None - self._category_trees_n = None - self._domains = dict() - self._connection_templates = dict() - self.load_blocks() - - def load_blocks(self): - """load the blocks and block tree from the search paths""" - # reset - self._blocks = odict() - self._blocks_n = odict() - self._category_trees_n = list() - self._domains.clear() - self._connection_templates.clear() - ParseXML.xml_failures.clear() - # try to parse and load blocks - for xml_file in self.iter_xml_files(): - try: - if xml_file.endswith("block_tree.xml"): - self.load_category_tree_xml(xml_file) - elif xml_file.endswith('domain.xml'): - self.load_domain_xml(xml_file) - else: - self.load_block_xml(xml_file) - except ParseXML.XMLSyntaxError as e: - # print >> sys.stderr, 'Warning: Block validation failed:\n\t%s\n\tIgnoring: %s' % (e, xml_file) - pass - except Exception as e: - print >> sys.stderr, 'Warning: XML parsing failed:\n\t%s\n\tIgnoring: %s' % (e, xml_file) - - def iter_xml_files(self): - """Iterator for block descriptions and category trees""" - get_path = lambda x: os.path.abspath(os.path.expanduser(x)) - for block_path in map(get_path, self._block_paths): - if os.path.isfile(block_path): - yield block_path - elif os.path.isdir(block_path): - for dirpath, dirnames, filenames in os.walk(block_path): - for filename in sorted(filter(lambda f: f.endswith('.xml'), filenames)): - yield os.path.join(dirpath, filename) - - def load_block_xml(self, xml_file): - """Load block description from xml file""" - # validate and import - ParseXML.validate_dtd(xml_file, self._block_dtd) - n = ParseXML.from_file(xml_file).find('block') - n['block_wrapper_path'] = xml_file # inject block wrapper path - # get block instance and add it to the list of blocks - block = self.Block(self._flow_graph, n) - key = block.get_key() - if key in self._blocks: - print >> sys.stderr, 'Warning: Block with key "%s" already exists.\n\tIgnoring: %s' % (key, xml_file) - else: # store the block - self._blocks[key] = block - self._blocks_n[key] = n - return block - - def load_category_tree_xml(self, xml_file): - """Validate and parse category tree file and add it to list""" - ParseXML.validate_dtd(xml_file, BLOCK_TREE_DTD) - n = ParseXML.from_file(xml_file).find('cat') - self._category_trees_n.append(n) - - def load_domain_xml(self, xml_file): - """Load a domain properties and connection templates from XML""" - ParseXML.validate_dtd(xml_file, DOMAIN_DTD) - n = ParseXML.from_file(xml_file).find('domain') - - key = n.find('key') - if not key: - print >> sys.stderr, 'Warning: Domain with emtpy key.\n\tIgnoring: %s' % xml_file - return - if key in self.get_domains(): # test against repeated keys - print >> sys.stderr, 'Warning: Domain with key "%s" already exists.\n\tIgnoring: %s' % (key, xml_file) - return - - to_bool = lambda s, d: d if s is None else \ - s.lower() not in ('false', 'off', '0', '') - - color = n.find('color') or '' - try: - import gtk # ugly but handy - gtk.gdk.color_parse(color) - except (ValueError, ImportError): - if color: # no color is okay, default set in GUI - print >> sys.stderr, 'Warning: Can\'t parse color code "%s" for domain "%s" ' % (color, key) - color = None - - self._domains[key] = dict( - name=n.find('name') or key, - multiple_sinks=to_bool(n.find('multiple_sinks'), True), - multiple_sources=to_bool(n.find('multiple_sources'), False), - color=color - ) - for connection_n in n.findall('connection'): - key = (connection_n.find('source_domain'), connection_n.find('sink_domain')) - if not all(key): - print >> sys.stderr, 'Warning: Empty domain key(s) in connection template.\n\t%s' % xml_file - elif key in self._connection_templates: - print >> sys.stderr, 'Warning: Connection template "%s" already exists.\n\t%s' % (key, xml_file) - else: - self._connection_templates[key] = connection_n.find('make') or '' - - def parse_flow_graph(self, flow_graph_file): - """ - Parse a saved flow graph file. - Ensure that the file exists, and passes the dtd check. - - Args: - flow_graph_file: the flow graph file - - Returns: - nested data - @throws exception if the validation fails - """ - flow_graph_file = flow_graph_file or self._default_flow_graph - open(flow_graph_file, 'r') # test open - ParseXML.validate_dtd(flow_graph_file, FLOW_GRAPH_DTD) - return ParseXML.from_file(flow_graph_file) - - def load_block_tree(self, block_tree): - """ - Load a block tree with categories and blocks. - Step 1: Load all blocks from the xml specification. - Step 2: Load blocks with builtin category specifications. - - Args: - block_tree: the block tree object - """ - #recursive function to load categories and blocks - def load_category(cat_n, parent=None): - #add this category - parent = (parent or []) + [cat_n.find('name')] - block_tree.add_block(parent) - #recursive call to load sub categories - map(lambda c: load_category(c, parent), cat_n.findall('cat')) - #add blocks in this category - for block_key in cat_n.findall('block'): - if block_key not in self.get_block_keys(): - print >> sys.stderr, 'Warning: Block key "%s" not found when loading category tree.' % (block_key) - continue - block = self.get_block(block_key) - #if it exists, the block's category shall not be overridden by the xml tree - if not block.get_category(): - block.set_category(parent) - - # recursively load the category trees and update the categories for each block - for category_tree_n in self._category_trees_n: - load_category(category_tree_n) - - #add blocks to block tree - for block in self.get_blocks(): - #blocks with empty categories are hidden - if not block.get_category(): continue - block_tree.add_block(block.get_category(), block) - - def __str__(self): return 'Platform - %s(%s)'%(self.get_key(), self.get_name()) - - def is_platform(self): return True - - def get_new_flow_graph(self): return self.FlowGraph(platform=self) - - def get_generator(self): return self._generator - - ############################################## - # Access Blocks - ############################################## - def get_block_keys(self): return self._blocks.keys() - def get_block(self, key): return self._blocks[key] - def get_blocks(self): return self._blocks.values() - def get_new_block(self, flow_graph, key): return self.Block(flow_graph, n=self._blocks_n[key]) - - def get_domains(self): return self._domains - def get_domain(self, key): return self._domains.get(key) - def get_connection_templates(self): return self._connection_templates - - def get_name(self): return self._name - def get_version(self): return self._version - def get_version_major(self): return self._version_major - def get_version_api(self): return self._version_api - def get_version_minor(self): return self._version_minor - def get_version_short(self): return self._version_short - - def get_key(self): return self._key - def get_license(self): return self._license - def get_website(self): return self._website - def get_colors(self): return self._colors - def get_block_paths(self): return self._block_paths - - ############################################## - # Constructors - ############################################## - FlowGraph = _FlowGraph - Connection = _Connection - Block = _Block - Port = _Port - Param = _Param diff --git a/grc/base/Port.py b/grc/base/Port.py deleted file mode 100644 index d1c35163f5..0000000000 --- a/grc/base/Port.py +++ /dev/null @@ -1,136 +0,0 @@ -""" -Copyright 2008-2011 Free Software Foundation, Inc. -This file is part of GNU Radio - -GNU Radio Companion 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 2 -of the License, or (at your option) any later version. - -GNU Radio Companion 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 -""" - -from Element import Element -from . Constants import GR_STREAM_DOMAIN, GR_MESSAGE_DOMAIN - -class Port(Element): - - def __init__(self, block, n, dir): - """ - Make a new port from nested data. - - Args: - block: the parent element - n: the nested odict - dir: the direction source or sink - """ - #build the port - Element.__init__(self, block) - #grab the data - self._name = n['name'] - self._key = n['key'] - self._type = n['type'] or '' - self._domain = n['domain'] - self._hide = n.find('hide') or '' - self._dir = dir - self._hide_evaluated = False # updated on rewrite() - - def validate(self): - """ - Validate the port. - The port must be non-empty and type must a possible type. - """ - Element.validate(self) - if self.get_type() not in self.get_types(): - self.add_error_message('Type "%s" is not a possible type.' % self.get_type()) - platform = self.get_parent().get_parent().get_parent() - if self.get_domain() not in platform.get_domains(): - self.add_error_message('Domain key "%s" is not registered.' % self.get_domain()) - - def rewrite(self): - """resolve dependencies in for type and hide""" - Element.rewrite(self) - hide = self.get_parent().resolve_dependencies(self._hide).strip().lower() - self._hide_evaluated = False if hide in ('false', 'off', '0') else bool(hide) - # update domain if was deduced from (dynamic) port type - type_ = self.get_type() - if self._domain == GR_STREAM_DOMAIN and type_ == "message": - self._domain = GR_MESSAGE_DOMAIN - self._key = self._name - if self._domain == GR_MESSAGE_DOMAIN and type_ != "message": - self._domain = GR_STREAM_DOMAIN - self._key = '0' # is rectified in rewrite() - - def __str__(self): - if self.is_source(): - return 'Source - %s(%s)'%(self.get_name(), self.get_key()) - if self.is_sink(): - return 'Sink - %s(%s)'%(self.get_name(), self.get_key()) - - def get_types(self): - """ - Get a list of all possible port types. - @throw NotImplementedError - """ - raise NotImplementedError - - def is_port(self): return True - def get_color(self): return '#FFFFFF' - def get_name(self): - number = '' - if self.get_type() == 'bus': - busses = filter(lambda a: a._dir == self._dir, self.get_parent().get_ports_gui()) - number = str(busses.index(self)) + '#' + str(len(self.get_associated_ports())) - return self._name + number - - def get_key(self): return self._key - def is_sink(self): return self._dir == 'sink' - def is_source(self): return self._dir == 'source' - def get_type(self): return self.get_parent().resolve_dependencies(self._type) - def get_domain(self): return self._domain - def get_hide(self): return self._hide_evaluated - - def get_connections(self): - """ - Get all connections that use this port. - - Returns: - a list of connection objects - """ - connections = self.get_parent().get_parent().get_connections() - connections = filter(lambda c: c.get_source() is self or c.get_sink() is self, connections) - return connections - - def get_enabled_connections(self): - """ - Get all enabled connections that use this port. - - Returns: - a list of connection objects - """ - return filter(lambda c: c.get_enabled(), self.get_connections()) - - def get_associated_ports(self): - if not self.get_type() == 'bus': - return [self] - else: - if self.is_source(): - get_ports = self.get_parent().get_sources - bus_structure = self.get_parent().current_bus_structure['source'] - else: - get_ports = self.get_parent().get_sinks - bus_structure = self.get_parent().current_bus_structure['sink'] - - ports = [i for i in get_ports() if not i.get_type() == 'bus'] - if bus_structure: - busses = [i for i in get_ports() if i.get_type() == 'bus'] - bus_index = busses.index(self) - ports = filter(lambda a: ports.index(a) in bus_structure[bus_index], ports) - return ports diff --git a/grc/base/__init__.py b/grc/base/__init__.py deleted file mode 100644 index 2682db8125..0000000000 --- a/grc/base/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -""" -Copyright 2009 Free Software Foundation, Inc. -This file is part of GNU Radio - -GNU Radio Companion 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 2 -of the License, or (at your option) any later version. - -GNU Radio Companion 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 -""" - -from odict import odict diff --git a/grc/blocks/block_tree.xml b/grc/blocks/block_tree.xml index d07c52e9c5..3125864d4d 100644 --- a/grc/blocks/block_tree.xml +++ b/grc/blocks/block_tree.xml @@ -1,11 +1,6 @@ <?xml version="1.0"?> -<!-- -################################################### -##Block Tree for platform gnuradio python. -################################################### - --> <cat> - <name></name> <!-- Blank for Root Name --> + <name>[Core]</name> <cat> <name>Misc</name> <block>pad_source</block> @@ -18,29 +13,16 @@ <block>bus_structure_sink</block> <block>bus_structure_source</block> + <block>epy_block</block> + <block>epy_module</block> + <block>note</block> <block>import</block> - - <block>blks2_selector</block> - <block>blks2_valve</block> - <block>blks2_error_rate</block> - - <block>xmlrpc_server</block> - <block>xmlrpc_client</block> - </cat> - <cat> - <name>Networking Tools</name> - <block>blks2_tcp_source</block> - <block>blks2_tcp_sink</block> - </cat> - <cat> - <name>Packet Operators</name> - <block>blks2_packet_decoder</block> - <block>blks2_packet_encoder</block> </cat> <cat> <name>Variables</name> <block>variable</block> + <block>variable_struct</block> <block>variable_config</block> <block>variable_function_probe</block> <block>parameter</block> diff --git a/grc/blocks/epy_block.xml b/grc/blocks/epy_block.xml index d443d29c31..65e78c4062 100644 --- a/grc/blocks/epy_block.xml +++ b/grc/blocks/epy_block.xml @@ -2,7 +2,6 @@ <block> <name>Python Block</name> <key>epy_block</key> - <category>Misc</category> <import></import> <make></make> <param><!-- Cache the last working block IO to keep FG sane --> @@ -25,7 +24,8 @@ be the parameters. All of them are required to have default values! import numpy as np from gnuradio import gr -class blk(gr.sync_block): + +class blk(gr.sync_block): # other base classes are basic_block, decim_block, interp_block """Embedded Python Block example - a simple multiply const""" def __init__(self, example_param=1.0): # only default arguments here @@ -36,11 +36,13 @@ class blk(gr.sync_block): in_sig=[np.complex64], out_sig=[np.complex64] ) - self.factor = example_param + # if an attribute with the same name as a parameter is found, + # a callback is registered (properties work, too). + self.example_param = example_param def work(self, input_items, output_items): """example: multiply with constant""" - output_items[0][:] = input_items[0] * self.factor + output_items[0][:] = input_items[0] * self.example_param return len(output_items[0]) </value> <type>_multiline_python_external</type> diff --git a/grc/blocks/epy_module.xml b/grc/blocks/epy_module.xml index 6d6d71804c..fa3e5f91f4 100644 --- a/grc/blocks/epy_module.xml +++ b/grc/blocks/epy_module.xml @@ -2,7 +2,6 @@ <block> <name>Python Module</name> <key>epy_module</key> - <category>Misc</category> <import>import $id # embedded python module</import> <make></make> <param> diff --git a/grc/blocks/options.xml b/grc/blocks/options.xml index 937cfe82ea..1dee986c5c 100644 --- a/grc/blocks/options.xml +++ b/grc/blocks/options.xml @@ -84,7 +84,7 @@ else: self.stop(); self.wait()</callback> <param> <name>Category</name> <key>category</key> - <value>Custom</value> + <value>[GRC Hier Blocks]</value> <type>string</type> <hide>#if $generate_options().startswith('hb') then 'none' else 'all'#</hide> </param> diff --git a/grc/blocks/pad_sink.xml b/grc/blocks/pad_sink.xml index b022fa3d0d..8ea8871d2e 100644 --- a/grc/blocks/pad_sink.xml +++ b/grc/blocks/pad_sink.xml @@ -44,6 +44,11 @@ <opt>size:gr.sizeof_char</opt> </option> <option> + <name>Bits</name> + <key>bit</key> + <opt>size:gr.sizeof_char</opt> + </option> + <option> <name>Message</name> <key>message</key> <opt>size:0</opt> diff --git a/grc/blocks/pad_source.xml b/grc/blocks/pad_source.xml index c0fb19eee7..3d8ccbed6a 100644 --- a/grc/blocks/pad_source.xml +++ b/grc/blocks/pad_source.xml @@ -44,6 +44,11 @@ <opt>size:gr.sizeof_char</opt> </option> <option> + <name>Bits</name> + <key>bit</key> + <opt>size:gr.sizeof_char</opt> + </option> + <option> <name>Message</name> <key>message</key> <opt>size:0</opt> diff --git a/grc/blocks/variable_function_probe.xml b/grc/blocks/variable_function_probe.xml index baa996c0ec..47c11b29fe 100644 --- a/grc/blocks/variable_function_probe.xml +++ b/grc/blocks/variable_function_probe.xml @@ -10,7 +10,7 @@ <import>import time</import> <import>import threading</import> <var_make>self.$(id) = $(id) = $value</var_make> - <make>#slurp + <make> def _$(id)_probe(): while True: #set $obj = 'self' + ('.' + $block_id() if $block_id() else '') @@ -22,15 +22,10 @@ def _$(id)_probe(): time.sleep(1.0 / ($poll_rate)) _$(id)_thread = threading.Thread(target=_$(id)_probe) _$(id)_thread.daemon = True -_$(id)_thread.start()</make> +_$(id)_thread.start() + </make> <callback>self.set_$(id)($value)</callback> <param> - <name>Value</name> - <key>value</key> - <value>0</value> - <type>raw</type> - </param> - <param> <name>Block ID</name> <key>block_id</key> <value>my_block_0</value> @@ -55,6 +50,13 @@ _$(id)_thread.start()</make> <value>10</value> <type>real</type> </param> + <param> + <name>Initial Value</name> + <key>value</key> + <value>0</value> + <type>raw</type> + <hide>part</hide> + </param> <doc> Periodically probe a function and set its value to this variable. diff --git a/grc/blocks/variable_struct.xml.py b/grc/blocks/variable_struct.xml.py index e43200828b..de4411e975 100644 --- a/grc/blocks/variable_struct.xml.py +++ b/grc/blocks/variable_struct.xml.py @@ -6,7 +6,6 @@ HEADER = """\ <block> <name>Struct Variable</name> <key>variable_struct</key> - <category>Variables</category> <import>def struct(data): return type('Struct', (object,), data)()</import> <var_make>self.$id = $id = struct({{#slurp #for $i in range({0}): diff --git a/grc/checks.py b/grc/checks.py new file mode 100755 index 0000000000..fd0e5de06a --- /dev/null +++ b/grc/checks.py @@ -0,0 +1,80 @@ +# Copyright 2009-2016 Free Software Foundation, Inc. +# This file is part of GNU Radio +# +# GNU Radio Companion 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 2 +# of the License, or (at your option) any later version. +# +# GNU Radio Companion 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 os +import warnings + + +GR_IMPORT_ERROR_MESSAGE = """\ +Cannot import gnuradio. + +Is the model path environment variable set correctly? + All OS: PYTHONPATH + +Is the library path environment variable set correctly? + Linux: LD_LIBRARY_PATH + Windows: PATH + MacOSX: DYLD_LIBRARY_PATH +""" + + +def die(error, message): + msg = "{0}\n\n({1})".format(message, error) + try: + import gtk + d = gtk.MessageDialog( + type=gtk.MESSAGE_ERROR, + buttons=gtk.BUTTONS_CLOSE, + message_format=msg, + ) + d.set_title(type(error).__name__) + d.run() + exit(1) + except ImportError: + exit(type(error).__name__ + '\n\n' + msg) + + +def check_gtk(): + try: + warnings.filterwarnings("error") + import pygtk + pygtk.require('2.0') + import gtk + gtk.init_check() + warnings.filterwarnings("always") + except Exception as err: + die(err, "Failed to initialize GTK. If you are running over ssh, " + "did you enable X forwarding and start ssh with -X?") + + +def check_gnuradio_import(): + try: + from gnuradio import gr + except ImportError as err: + die(err, GR_IMPORT_ERROR_MESSAGE) + + +def check_blocks_path(): + if 'GR_DONT_LOAD_PREFS' in os.environ and not os.environ.get('GRC_BLOCKS_PATH', ''): + die(EnvironmentError("No block definitions available"), + "Can't find block definitions. Use config.conf or GRC_BLOCKS_PATH.") + + +def do_all(): + check_gnuradio_import() + check_gtk() + check_blocks_path() diff --git a/grc/core/Block.py b/grc/core/Block.py new file mode 100644 index 0000000000..8a683a2b6b --- /dev/null +++ b/grc/core/Block.py @@ -0,0 +1,850 @@ +""" +Copyright 2008-2015 Free Software Foundation, Inc. +This file is part of GNU Radio + +GNU Radio Companion 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 2 +of the License, or (at your option) any later version. + +GNU Radio Companion 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 collections +import itertools + +from Cheetah.Template import Template + +from .utils import epy_block_io, odict +from . Constants import ( + BLOCK_FLAG_NEED_QT_GUI, BLOCK_FLAG_NEED_WX_GUI, + ADVANCED_PARAM_TAB, DEFAULT_PARAM_TAB, + BLOCK_FLAG_THROTTLE, BLOCK_FLAG_DISABLE_BYPASS, + BLOCK_FLAG_DEPRECATED, + BLOCK_ENABLED, BLOCK_BYPASSED, BLOCK_DISABLED +) +from . Element import Element + + +def _get_keys(lst): + return [elem.get_key() for elem in lst] + + +def _get_elem(lst, key): + try: + return lst[_get_keys(lst).index(key)] + except ValueError: + raise ValueError('Key "{}" not found in {}.'.format(key, _get_keys(lst))) + + +class Block(Element): + + is_block = True + + def __init__(self, flow_graph, n): + """ + Make a new block from nested data. + + Args: + flow: graph the parent element + n: the nested odict + + Returns: + block a new block + """ + # Grab the data + self._doc = (n.find('doc') or '').strip('\n').replace('\\\n', '') + self._imports = map(lambda i: i.strip(), n.findall('import')) + self._make = n.find('make') + self._var_make = n.find('var_make') + self._checks = n.findall('check') + self._callbacks = n.findall('callback') + self._bus_structure_source = n.find('bus_structure_source') or '' + self._bus_structure_sink = n.find('bus_structure_sink') or '' + self.port_counters = [itertools.count(), itertools.count()] + + # Build the block + Element.__init__(self, flow_graph) + + # Grab the data + params = n.findall('param') + sources = n.findall('source') + sinks = n.findall('sink') + self._name = n.find('name') + self._key = n.find('key') + category = (n.find('category') or '').split('/') + self.category = [cat.strip() for cat in category if cat.strip()] + self._flags = n.find('flags') or '' + # Backwards compatibility + if n.find('throttle') and BLOCK_FLAG_THROTTLE not in self._flags: + self._flags += BLOCK_FLAG_THROTTLE + self._grc_source = n.find('grc_source') or '' + self._block_wrapper_path = n.find('block_wrapper_path') + self._bussify_sink = n.find('bus_sink') + self._bussify_source = n.find('bus_source') + self._var_value = n.find('var_value') or '$value' + + # Get list of param tabs + n_tabs = n.find('param_tab_order') or None + self._param_tab_labels = n_tabs.findall('tab') if n_tabs is not None else [DEFAULT_PARAM_TAB] + + # Create the param objects + self._params = list() + + # Add the id param + self.get_params().append(self.get_parent().get_parent().Param( + block=self, + n=odict({ + 'name': 'ID', + 'key': 'id', + 'type': 'id', + }) + )) + self.get_params().append(self.get_parent().get_parent().Param( + block=self, + n=odict({ + 'name': 'Enabled', + 'key': '_enabled', + 'type': 'raw', + 'value': 'True', + 'hide': 'all', + }) + )) + for param in itertools.imap(lambda n: self.get_parent().get_parent().Param(block=self, n=n), params): + key = param.get_key() + # Test against repeated keys + if key in self.get_param_keys(): + raise Exception('Key "{}" already exists in params'.format(key)) + # Store the param + self.get_params().append(param) + # Create the source objects + self._sources = list() + for source in map(lambda n: self.get_parent().get_parent().Port(block=self, n=n, dir='source'), sources): + key = source.get_key() + # Test against repeated keys + if key in self.get_source_keys(): + raise Exception('Key "{}" already exists in sources'.format(key)) + # Store the port + self.get_sources().append(source) + self.back_ofthe_bus(self.get_sources()) + # Create the sink objects + self._sinks = list() + for sink in map(lambda n: self.get_parent().get_parent().Port(block=self, n=n, dir='sink'), sinks): + key = sink.get_key() + # Test against repeated keys + if key in self.get_sink_keys(): + raise Exception('Key "{}" already exists in sinks'.format(key)) + # Store the port + self.get_sinks().append(sink) + self.back_ofthe_bus(self.get_sinks()) + self.current_bus_structure = {'source': '', 'sink': ''} + + # Virtual source/sink and pad source/sink blocks are + # indistinguishable from normal GR blocks. Make explicit + # checks for them here since they have no work function or + # buffers to manage. + self.is_virtual_or_pad = self._key in ( + "virtual_source", "virtual_sink", "pad_source", "pad_sink") + self.is_variable = self._key.startswith('variable') + self.is_import = (self._key == 'import') + + # Disable blocks that are virtual/pads or variables + if self.is_virtual_or_pad or self.is_variable: + self._flags += BLOCK_FLAG_DISABLE_BYPASS + + if not (self.is_virtual_or_pad or self.is_variable or self._key == 'options'): + self.get_params().append(self.get_parent().get_parent().Param( + block=self, + n=odict({'name': 'Block Alias', + 'key': 'alias', + 'type': 'string', + 'hide': 'part', + 'tab': ADVANCED_PARAM_TAB + }) + )) + + if (len(sources) or len(sinks)) and not self.is_virtual_or_pad: + self.get_params().append(self.get_parent().get_parent().Param( + block=self, + n=odict({'name': 'Core Affinity', + 'key': 'affinity', + 'type': 'int_vector', + 'hide': 'part', + 'tab': ADVANCED_PARAM_TAB + }) + )) + if len(sources) and not self.is_virtual_or_pad: + self.get_params().append(self.get_parent().get_parent().Param( + block=self, + n=odict({'name': 'Min Output Buffer', + 'key': 'minoutbuf', + 'type': 'int', + 'hide': 'part', + 'value': '0', + 'tab': ADVANCED_PARAM_TAB + }) + )) + self.get_params().append(self.get_parent().get_parent().Param( + block=self, + n=odict({'name': 'Max Output Buffer', + 'key': 'maxoutbuf', + 'type': 'int', + 'hide': 'part', + 'value': '0', + 'tab': ADVANCED_PARAM_TAB + }) + )) + + self.get_params().append(self.get_parent().get_parent().Param( + block=self, + n=odict({'name': 'Comment', + 'key': 'comment', + 'type': '_multiline', + 'hide': 'part', + 'value': '', + 'tab': ADVANCED_PARAM_TAB + }) + )) + + self._epy_source_hash = -1 # for epy blocks + self._epy_reload_error = None + + if self._bussify_sink: + self.bussify({'name': 'bus', 'type': 'bus'}, 'sink') + if self._bussify_source: + self.bussify({'name': 'bus', 'type': 'bus'}, 'source') + + def get_bus_structure(self, direction): + if direction == 'source': + bus_structure = self._bus_structure_source + else: + bus_structure = self._bus_structure_sink + + bus_structure = self.resolve_dependencies(bus_structure) + + if not bus_structure: + return '' # TODO: Don't like empty strings. should change this to None eventually + + try: + clean_bus_structure = self.get_parent().evaluate(bus_structure) + return clean_bus_structure + except: + return '' + + def validate(self): + """ + Validate this block. + Call the base class validate. + Evaluate the checks: each check must evaluate to True. + """ + Element.validate(self) + # Evaluate the checks + for check in self._checks: + check_res = self.resolve_dependencies(check) + try: + if not self.get_parent().evaluate(check_res): + self.add_error_message('Check "{}" failed.'.format(check)) + except: + self.add_error_message('Check "{}" did not evaluate.'.format(check)) + + # For variables check the value (only if var_value is used + if self.is_variable and self._var_value != '$value': + value = self._var_value + try: + value = self.get_var_value() + self.get_parent().evaluate(value) + except Exception as err: + self.add_error_message('Value "{}" cannot be evaluated:\n{}'.format(value, err)) + + # check if this is a GUI block and matches the selected generate option + current_generate_option = self.get_parent().get_option('generate_options') + + def check_generate_mode(label, flag, valid_options): + block_requires_mode = ( + flag in self.get_flags() or + self.get_name().upper().startswith(label) + ) + if block_requires_mode and current_generate_option not in valid_options: + self.add_error_message("Can't generate this block in mode: {} ".format( + repr(current_generate_option))) + + check_generate_mode('WX GUI', BLOCK_FLAG_NEED_WX_GUI, ('wx_gui',)) + check_generate_mode('QT GUI', BLOCK_FLAG_NEED_QT_GUI, ('qt_gui', 'hb_qt_gui')) + if self._epy_reload_error: + self.get_param('_source_code').add_error_message(str(self._epy_reload_error)) + + def rewrite(self): + """ + Add and remove ports to adjust for the nports. + """ + Element.rewrite(self) + # Check and run any custom rewrite function for this block + getattr(self, 'rewrite_' + self._key, lambda: None)() + + # Adjust nports, disconnect hidden ports + for ports in (self.get_sources(), self.get_sinks()): + for i, master_port in enumerate(ports): + nports = master_port.get_nports() or 1 + num_ports = 1 + len(master_port.get_clones()) + if master_port.get_hide(): + for connection in master_port.get_connections(): + self.get_parent().remove_element(connection) + if not nports and num_ports == 1: # Not a master port and no left-over clones + continue + # Remove excess cloned ports + for port in master_port.get_clones()[nports-1:]: + # Remove excess connections + for connection in port.get_connections(): + self.get_parent().remove_element(connection) + master_port.remove_clone(port) + ports.remove(port) + # Add more cloned ports + for j in range(num_ports, nports): + port = master_port.add_clone() + ports.insert(ports.index(master_port) + j, port) + + self.back_ofthe_bus(ports) + # Renumber non-message/message ports + domain_specific_port_index = collections.defaultdict(int) + for port in filter(lambda p: p.get_key().isdigit(), ports): + domain = port.get_domain() + port._key = str(domain_specific_port_index[domain]) + domain_specific_port_index[domain] += 1 + + def port_controller_modify(self, direction): + """ + Change the port controller. + + Args: + direction: +1 or -1 + + Returns: + true for change + """ + changed = False + # Concat the nports string from the private nports settings of all ports + nports_str = ' '.join([port._nports for port in self.get_ports()]) + # Modify all params whose keys appear in the nports string + for param in self.get_params(): + if param.is_enum() or param.get_key() not in nports_str: + continue + # Try to increment the port controller by direction + try: + value = param.get_evaluated() + value = value + direction + if 0 < value: + param.set_value(value) + changed = True + except: + pass + return changed + + def get_doc(self): + platform = self.get_parent().get_parent() + documentation = platform.block_docstrings.get(self._key, {}) + from_xml = self._doc.strip() + if from_xml: + documentation[''] = from_xml + return documentation + + def get_imports(self, raw=False): + """ + Resolve all import statements. + Split each import statement at newlines. + Combine all import statements into a list. + Filter empty imports. + + Returns: + a list of import statements + """ + if raw: + return self._imports + return filter(lambda i: i, sum(map(lambda i: self.resolve_dependencies(i).split('\n'), self._imports), [])) + + def get_make(self, raw=False): + if raw: + return self._make + return self.resolve_dependencies(self._make) + + def get_var_make(self): + return self.resolve_dependencies(self._var_make) + + def get_var_value(self): + return self.resolve_dependencies(self._var_value) + + def get_callbacks(self): + """ + Get a list of function callbacks for this block. + + Returns: + a list of strings + """ + def make_callback(callback): + callback = self.resolve_dependencies(callback) + if 'self.' in callback: + return callback + return 'self.{}.{}'.format(self.get_id(), callback) + return map(make_callback, self._callbacks) + + def is_virtual_sink(self): + return self.get_key() == 'virtual_sink' + + def is_virtual_source(self): + return self.get_key() == 'virtual_source' + + ########################################################################### + # Custom rewrite functions + ########################################################################### + + def rewrite_epy_block(self): + flowgraph = self.get_parent() + platform = flowgraph.get_parent() + param_blk = self.get_param('_io_cache') + param_src = self.get_param('_source_code') + + src = param_src.get_value() + src_hash = hash((self.get_id(), src)) + if src_hash == self._epy_source_hash: + return + + try: + blk_io = epy_block_io.extract(src) + + except Exception as e: + self._epy_reload_error = ValueError(str(e)) + try: # Load last working block io + blk_io_args = eval(param_blk.get_value()) + if len(blk_io_args) == 6: + blk_io_args += ([],) # add empty callbacks + blk_io = epy_block_io.BlockIO(*blk_io_args) + except Exception: + return + else: + self._epy_reload_error = None # Clear previous errors + param_blk.set_value(repr(tuple(blk_io))) + + # print "Rewriting embedded python block {!r}".format(self.get_id()) + + self._epy_source_hash = src_hash + self._name = blk_io.name or blk_io.cls + self._doc = blk_io.doc + self._imports[0] = 'import ' + self.get_id() + self._make = '{0}.{1}({2})'.format(self.get_id(), blk_io.cls, ', '.join( + '{0}=${{ {0} }}'.format(key) for key, _ in blk_io.params)) + self._callbacks = ['{0} = ${{ {0} }}'.format(attr) for attr in blk_io.callbacks] + + params = {} + for param in list(self._params): + if hasattr(param, '__epy_param__'): + params[param.get_key()] = param + self._params.remove(param) + + for key, value in blk_io.params: + try: + param = params[key] + param.set_default(value) + except KeyError: # need to make a new param + name = key.replace('_', ' ').title() + n = odict(dict(name=name, key=key, type='raw', value=value)) + param = platform.Param(block=self, n=n) + setattr(param, '__epy_param__', True) + self._params.append(param) + + def update_ports(label, ports, port_specs, direction): + ports_to_remove = list(ports) + iter_ports = iter(ports) + ports_new = [] + port_current = next(iter_ports, None) + for key, port_type in port_specs: + reuse_port = ( + port_current is not None and + port_current.get_type() == port_type and + (key.isdigit() or port_current.get_key() == key) + ) + if reuse_port: + ports_to_remove.remove(port_current) + port, port_current = port_current, next(iter_ports, None) + else: + n = odict(dict(name=label + str(key), type=port_type, key=key)) + if port_type == 'message': + n['name'] = key + n['optional'] = '1' + port = platform.Port(block=self, n=n, dir=direction) + ports_new.append(port) + # replace old port list with new one + del ports[:] + ports.extend(ports_new) + # remove excess port connections + for port in ports_to_remove: + for connection in port.get_connections(): + flowgraph.remove_element(connection) + + update_ports('in', self.get_sinks(), blk_io.sinks, 'sink') + update_ports('out', self.get_sources(), blk_io.sources, 'source') + self.rewrite() + + def back_ofthe_bus(self, portlist): + portlist.sort(key=lambda p: p._type == 'bus') + + def filter_bus_port(self, ports): + buslist = [p for p in ports if p._type == 'bus'] + return buslist or ports + + # Main functions to get and set the block state + # Also kept get_enabled and set_enabled to keep compatibility + def get_state(self): + """ + Gets the block's current state. + + Returns: + ENABLED - 0 + BYPASSED - 1 + DISABLED - 2 + """ + try: + return int(eval(self.get_param('_enabled').get_value())) + except: + return BLOCK_ENABLED + + def set_state(self, state): + """ + Sets the state for the block. + + Args: + ENABLED - 0 + BYPASSED - 1 + DISABLED - 2 + """ + if state in [BLOCK_ENABLED, BLOCK_BYPASSED, BLOCK_DISABLED]: + self.get_param('_enabled').set_value(str(state)) + else: + self.get_param('_enabled').set_value(str(BLOCK_ENABLED)) + + # Enable/Disable Aliases + def get_enabled(self): + """ + Get the enabled state of the block. + + Returns: + true for enabled + """ + return not (self.get_state() == BLOCK_DISABLED) + + def set_enabled(self, enabled): + """ + Set the enabled state of the block. + + Args: + enabled: true for enabled + + Returns: + True if block changed state + """ + old_state = self.get_state() + new_state = BLOCK_ENABLED if enabled else BLOCK_DISABLED + self.set_state(new_state) + return old_state != new_state + + # Block bypassing + def get_bypassed(self): + """ + Check if the block is bypassed + """ + return self.get_state() == BLOCK_BYPASSED + + def set_bypassed(self): + """ + Bypass the block + + Returns: + True if block chagnes state + """ + if self.get_state() != BLOCK_BYPASSED and self.can_bypass(): + self.set_state(BLOCK_BYPASSED) + return True + return False + + def can_bypass(self): + """ Check the number of sinks and sources and see if this block can be bypassed """ + # Check to make sure this is a single path block + # Could possibly support 1 to many blocks + if len(self.get_sources()) != 1 or len(self.get_sinks()) != 1: + return False + if not (self.get_sources()[0].get_type() == self.get_sinks()[0].get_type()): + return False + if self.bypass_disabled(): + return False + return True + + def __str__(self): + return 'Block - {} - {}({})'.format(self.get_id(), self.get_name(), self.get_key()) + + def get_id(self): + return self.get_param('id').get_value() + + def get_name(self): + return self._name + + def get_key(self): + return self._key + + def get_ports(self): + return self.get_sources() + self.get_sinks() + + def get_ports_gui(self): + return self.filter_bus_port(self.get_sources()) + self.filter_bus_port(self.get_sinks()) + + def get_children(self): + return self.get_ports() + self.get_params() + + def get_children_gui(self): + return self.get_ports_gui() + self.get_params() + + def get_block_wrapper_path(self): + return self._block_wrapper_path + + def get_comment(self): + return self.get_param('comment').get_value() + + def get_flags(self): + return self._flags + + def throtteling(self): + return BLOCK_FLAG_THROTTLE in self._flags + + def bypass_disabled(self): + return BLOCK_FLAG_DISABLE_BYPASS in self._flags + + @property + def is_deprecated(self): + return BLOCK_FLAG_DEPRECATED in self._flags + + ############################################## + # Access Params + ############################################## + def get_param_tab_labels(self): + return self._param_tab_labels + + def get_param_keys(self): + return _get_keys(self._params) + + def get_param(self, key): + return _get_elem(self._params, key) + + def get_params(self): + return self._params + + def has_param(self, key): + try: + _get_elem(self._params, key) + return True + except: + return False + + ############################################## + # Access Sinks + ############################################## + def get_sink_keys(self): + return _get_keys(self._sinks) + + def get_sink(self, key): + return _get_elem(self._sinks, key) + + def get_sinks(self): + return self._sinks + + def get_sinks_gui(self): + return self.filter_bus_port(self.get_sinks()) + + ############################################## + # Access Sources + ############################################## + def get_source_keys(self): + return _get_keys(self._sources) + + def get_source(self, key): + return _get_elem(self._sources, key) + + def get_sources(self): + return self._sources + + def get_sources_gui(self): + return self.filter_bus_port(self.get_sources()) + + def get_connections(self): + return sum([port.get_connections() for port in self.get_ports()], []) + + def resolve_dependencies(self, tmpl): + """ + Resolve a paramater dependency with cheetah templates. + + Args: + tmpl: the string with dependencies + + Returns: + the resolved value + """ + tmpl = str(tmpl) + if '$' not in tmpl: + return tmpl + n = dict((param.get_key(), param.template_arg) + for param in self.get_params()) # TODO: cache that + try: + return str(Template(tmpl, n)) + except Exception as err: + return "Template error: {}\n {}".format(tmpl, err) + + ############################################## + # Controller Modify + ############################################## + def type_controller_modify(self, direction): + """ + Change the type controller. + + Args: + direction: +1 or -1 + + Returns: + true for change + """ + changed = False + type_param = None + for param in filter(lambda p: p.is_enum(), self.get_params()): + children = self.get_ports() + self.get_params() + # Priority to the type controller + if param.get_key() in ' '.join(map(lambda p: p._type, children)): type_param = param + # Use param if type param is unset + if not type_param: + type_param = param + if type_param: + # Try to increment the enum by direction + try: + keys = type_param.get_option_keys() + old_index = keys.index(type_param.get_value()) + new_index = (old_index + direction + len(keys)) % len(keys) + type_param.set_value(keys[new_index]) + changed = True + except: + pass + return changed + + def form_bus_structure(self, direc): + if direc == 'source': + get_p = self.get_sources + get_p_gui = self.get_sources_gui + bus_structure = self.get_bus_structure('source') + else: + get_p = self.get_sinks + get_p_gui = self.get_sinks_gui + bus_structure = self.get_bus_structure('sink') + + struct = [range(len(get_p()))] + if True in map(lambda a: isinstance(a.get_nports(), int), get_p()): + structlet = [] + last = 0 + for j in [i.get_nports() for i in get_p() if isinstance(i.get_nports(), int)]: + structlet.extend(map(lambda a: a+last, range(j))) + last = structlet[-1] + 1 + struct = [structlet] + if bus_structure: + + struct = bus_structure + + self.current_bus_structure[direc] = struct + return struct + + def bussify(self, n, direc): + if direc == 'source': + get_p = self.get_sources + get_p_gui = self.get_sources_gui + bus_structure = self.get_bus_structure('source') + else: + get_p = self.get_sinks + get_p_gui = self.get_sinks_gui + bus_structure = self.get_bus_structure('sink') + + for elt in get_p(): + for connect in elt.get_connections(): + self.get_parent().remove_element(connect) + + if ('bus' not in map(lambda a: a.get_type(), get_p())) and len(get_p()) > 0: + struct = self.form_bus_structure(direc) + self.current_bus_structure[direc] = struct + if get_p()[0].get_nports(): + n['nports'] = str(1) + + for i in range(len(struct)): + n['key'] = str(len(get_p())) + n = odict(n) + port = self.get_parent().get_parent().Port(block=self, n=n, dir=direc) + get_p().append(port) + elif 'bus' in map(lambda a: a.get_type(), get_p()): + for elt in get_p_gui(): + get_p().remove(elt) + self.current_bus_structure[direc] = '' + + ############################################## + # Import/Export Methods + ############################################## + def export_data(self): + """ + Export this block's params to nested data. + + Returns: + a nested data odict + """ + n = odict() + n['key'] = self.get_key() + n['param'] = map(lambda p: p.export_data(), sorted(self.get_params(), key=str)) + if 'bus' in map(lambda a: a.get_type(), self.get_sinks()): + n['bus_sink'] = str(1) + if 'bus' in map(lambda a: a.get_type(), self.get_sources()): + n['bus_source'] = str(1) + return n + + def get_hash(self): + return hash(tuple(map(hash, self.get_params()))) + + def import_data(self, n): + """ + Import this block's params from nested data. + Any param keys that do not exist will be ignored. + Since params can be dynamically created based another param, + call rewrite, and repeat the load until the params stick. + This call to rewrite will also create any dynamic ports + that are needed for the connections creation phase. + + Args: + n: the nested data odict + """ + my_hash = 0 + while self.get_hash() != my_hash: + params_n = n.findall('param') + for param_n in params_n: + key = param_n.find('key') + value = param_n.find('value') + # The key must exist in this block's params + if key in self.get_param_keys(): + self.get_param(key).set_value(value) + # Store hash and call rewrite + my_hash = self.get_hash() + self.rewrite() + bussinks = n.findall('bus_sink') + if len(bussinks) > 0 and not self._bussify_sink: + self.bussify({'name': 'bus', 'type': 'bus'}, 'sink') + elif len(bussinks) > 0: + self.bussify({'name': 'bus', 'type': 'bus'}, 'sink') + self.bussify({'name': 'bus', 'type': 'bus'}, 'sink') + bussrcs = n.findall('bus_source') + if len(bussrcs) > 0 and not self._bussify_source: + self.bussify({'name': 'bus', 'type': 'bus'}, 'source') + elif len(bussrcs) > 0: + self.bussify({'name': 'bus', 'type': 'bus'}, 'source') + self.bussify({'name': 'bus', 'type': 'bus'}, 'source') diff --git a/grc/examples/CMakeLists.txt b/grc/core/CMakeLists.txt index a218dbe500..51b0dacba6 100644 --- a/grc/examples/CMakeLists.txt +++ b/grc/core/CMakeLists.txt @@ -17,21 +17,21 @@ # the Free Software Foundation, Inc., 51 Franklin Street, # Boston, MA 02110-1301, USA. -# SIMPLE -install( - FILES - simple/variable_config.grc - DESTINATION ${GR_PKG_DATA_DIR}/examples/grc/simple +file(GLOB py_files "*.py") + +GR_PYTHON_INSTALL( + FILES ${py_files} + DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc/core COMPONENT "grc" ) -# XMLRPC +file(GLOB dtd_files "*.dtd") + install( - FILES - xmlrpc/readme.txt - xmlrpc/xmlrpc_client.grc - xmlrpc/xmlrpc_client_script.py - xmlrpc/xmlrpc_server.grc - DESTINATION ${GR_PKG_DATA_DIR}/examples/grc/xmlrpc + FILES ${dtd_files} default_flow_graph.grc + DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc/core COMPONENT "grc" ) + +add_subdirectory(generator) +add_subdirectory(utils) diff --git a/grc/core/Config.py b/grc/core/Config.py new file mode 100644 index 0000000000..ac38d9978c --- /dev/null +++ b/grc/core/Config.py @@ -0,0 +1,55 @@ +""" +Copyright 2016 Free Software Foundation, Inc. +This file is part of GNU Radio + +GNU Radio Companion 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 2 +of the License, or (at your option) any later version. + +GNU Radio Companion 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 os +from os.path import expanduser, normpath, expandvars, exists + + +class Config(object): + + key = 'grc' + name = 'GNU Radio Companion (no gui)' + license = __doc__.strip() + website = 'http://gnuradio.org' + + hier_block_lib_dir = os.environ.get('GRC_HIER_PATH', expanduser('~/.grc_gnuradio')) + + def __init__(self, prefs_file, version, version_parts=None): + self.prefs = prefs_file + self.version = version + self.version_parts = version_parts or version[1:].split('-', 1)[0].split('.')[:3] + + @property + def block_paths(self): + path_list_sep = {'/': ':', '\\': ';'}[os.path.sep] + + paths_sources = ( + self.hier_block_lib_dir, + os.environ.get('GRC_BLOCKS_PATH', ''), + self.prefs.get_string('grc', 'local_blocks_path', ''), + self.prefs.get_string('grc', 'global_blocks_path', ''), + ) + + collected_paths = sum((paths.split(path_list_sep) + for paths in paths_sources), []) + + valid_paths = [normpath(expanduser(expandvars(path))) + for path in collected_paths if exists(path)] + + return valid_paths diff --git a/grc/base/Connection.py b/grc/core/Connection.py index bf3c75277c..3aa32ef183 100644 --- a/grc/base/Connection.py +++ b/grc/core/Connection.py @@ -1,5 +1,5 @@ """ -Copyright 2008-2011 Free Software Foundation, Inc. +Copyright 2008-2015 Free Software Foundation, Inc. This file is part of GNU Radio GNU Radio Companion is free software; you can redistribute it and/or @@ -17,11 +17,15 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ -from Element import Element -from . import odict +from . import Constants +from .Element import Element +from .utils import odict + class Connection(Element): + is_connection = True + def __init__(self, flow_graph, porta, portb): """ Make a new connection given the parent and 2 ports. @@ -37,72 +41,88 @@ class Connection(Element): """ Element.__init__(self, flow_graph) source = sink = None - #separate the source and sink + # Separate the source and sink for port in (porta, portb): - if port.is_source(): source = port - if port.is_sink(): sink = port - if not source: raise ValueError('Connection could not isolate source') - if not sink: raise ValueError('Connection could not isolate sink') - busses = len(filter(lambda a: a.get_type() == 'bus', [source, sink]))%2 - if not busses == 0: raise ValueError('busses must get with busses') + if port.is_source: + source = port + else: + sink = port + if not source: + raise ValueError('Connection could not isolate source') + if not sink: + raise ValueError('Connection could not isolate sink') + busses = len(filter(lambda a: a.get_type() == 'bus', [source, sink])) % 2 + if not busses == 0: + raise ValueError('busses must get with busses') if not len(source.get_associated_ports()) == len(sink.get_associated_ports()): - raise ValueError('port connections must have same cardinality'); - #ensure that this connection (source -> sink) is unique - for connection in self.get_parent().get_connections(): + raise ValueError('port connections must have same cardinality') + # Ensure that this connection (source -> sink) is unique + for connection in flow_graph.connections: if connection.get_source() is source and connection.get_sink() is sink: raise LookupError('This connection between source and sink is not unique.') self._source = source self._sink = sink if source.get_type() == 'bus': - sources = source.get_associated_ports(); - sinks = sink.get_associated_ports(); + sources = source.get_associated_ports() + sinks = sink.get_associated_ports() for i in range(len(sources)): try: - flow_graph.connect(sources[i], sinks[i]); + flow_graph.connect(sources[i], sinks[i]) except: pass def __str__(self): - return 'Connection (\n\t%s\n\t\t%s\n\t%s\n\t\t%s\n)'%( + return 'Connection (\n\t{}\n\t\t{}\n\t{}\n\t\t{}\n)'.format( self.get_source().get_parent(), self.get_source(), self.get_sink().get_parent(), self.get_sink(), ) - def is_connection(self): return True + def is_msg(self): + return self.get_source().get_type() == self.get_sink().get_type() == 'msg' + + def is_bus(self): + return self.get_source().get_type() == self.get_sink().get_type() == 'bus' def validate(self): """ Validate the connections. + The ports must match in io size. + """ + """ + Validate the connections. The ports must match in type. """ Element.validate(self) platform = self.get_parent().get_parent() source_domain = self.get_source().get_domain() sink_domain = self.get_sink().get_domain() - if (source_domain, sink_domain) not in platform.get_connection_templates(): - self.add_error_message('No connection known for domains "%s", "%s"' - % (source_domain, sink_domain)) + if (source_domain, sink_domain) not in platform.connection_templates: + self.add_error_message('No connection known for domains "{}", "{}"'.format( + source_domain, sink_domain)) too_many_other_sinks = ( - source_domain in platform.get_domains() and - not platform.get_domain(key=source_domain)['multiple_sinks'] and + not platform.domains.get(source_domain, []).get('multiple_sinks', False) and len(self.get_source().get_enabled_connections()) > 1 ) too_many_other_sources = ( - sink_domain in platform.get_domains() and - not platform.get_domain(key=sink_domain)['multiple_sources'] and + not platform.domains.get(sink_domain, []).get('multiple_sources', False) and len(self.get_sink().get_enabled_connections()) > 1 ) if too_many_other_sinks: self.add_error_message( - 'Domain "%s" can have only one downstream block' % source_domain) + 'Domain "{}" can have only one downstream block'.format(source_domain)) if too_many_other_sources: self.add_error_message( - 'Domain "%s" can have only one upstream block' % sink_domain) + 'Domain "{}" can have only one upstream block'.format(sink_domain)) + + source_size = Constants.TYPE_TO_SIZEOF[self.get_source().get_type()] * self.get_source().get_vlen() + sink_size = Constants.TYPE_TO_SIZEOF[self.get_sink().get_type()] * self.get_sink().get_vlen() + if source_size != sink_size: + self.add_error_message('Source IO size "{}" does not match sink IO size "{}".'.format(source_size, sink_size)) def get_enabled(self): """ @@ -117,11 +137,14 @@ class Connection(Element): ############################# # Access Ports ############################# - def get_sink(self): return self._sink - def get_source(self): return self._source + def get_sink(self): + return self._sink + + def get_source(self): + return self._source ############################################## - ## Import/Export Methods + # Import/Export Methods ############################################## def export_data(self): """ diff --git a/grc/python/Constants.py b/grc/core/Constants.py index b7a370cad7..eeb1d7f848 100644 --- a/grc/python/Constants.py +++ b/grc/core/Constants.py @@ -1,5 +1,5 @@ """ -Copyright 2008-2011 Free Software Foundation, Inc. +Copyright 2008-2016 Free Software Foundation, Inc. This file is part of GNU Radio GNU Radio Companion is free software; you can redistribute it and/or @@ -18,49 +18,55 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ import os -from os.path import expanduser import numpy import stat -from gnuradio import gr -_gr_prefs = gr.prefs() - -# setup paths -PATH_SEP = {'/': ':', '\\': ';'}[os.path.sep] - -HIER_BLOCKS_LIB_DIR = os.environ.get('GRC_HIER_PATH', expanduser('~/.grc_gnuradio')) - -PREFS_FILE = os.environ.get('GRC_PREFS_PATH', expanduser('~/.gnuradio/grc.conf')) -PREFS_FILE_OLD = os.environ.get('GRC_PREFS_PATH', expanduser('~/.grc')) - -BLOCKS_DIRS = filter( # filter blank strings - lambda x: x, PATH_SEP.join([ - os.environ.get('GRC_BLOCKS_PATH', ''), - _gr_prefs.get_string('grc', 'local_blocks_path', ''), - _gr_prefs.get_string('grc', 'global_blocks_path', ''), - ]).split(PATH_SEP), -) + [HIER_BLOCKS_LIB_DIR] - -# user settings -XTERM_EXECUTABLE = _gr_prefs.get_string('grc', 'xterm_executable', 'xterm') - -# file creation modes -TOP_BLOCK_FILE_MODE = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP | stat.S_IROTH -HIER_BLOCK_FILE_MODE = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH - -# data files +# Data files DATA_DIR = os.path.dirname(__file__) -FLOW_GRAPH_TEMPLATE = os.path.join(DATA_DIR, 'flow_graph.tmpl') +FLOW_GRAPH_DTD = os.path.join(DATA_DIR, 'flow_graph.dtd') +BLOCK_TREE_DTD = os.path.join(DATA_DIR, 'block_tree.dtd') BLOCK_DTD = os.path.join(DATA_DIR, 'block.dtd') DEFAULT_FLOW_GRAPH = os.path.join(DATA_DIR, 'default_flow_graph.grc') +DOMAIN_DTD = os.path.join(DATA_DIR, 'domain.dtd') + +# File format versions: +# 0: undefined / legacy +# 1: non-numeric message port keys (label is used instead) +FLOW_GRAPH_FILE_FORMAT_VERSION = 1 + +# Param tabs +DEFAULT_PARAM_TAB = "General" +ADVANCED_PARAM_TAB = "Advanced" +DEFAULT_BLOCK_MODULE_NAME = '(no module specified)' + +# Port domains +GR_STREAM_DOMAIN = "gr_stream" +GR_MESSAGE_DOMAIN = "gr_message" +DEFAULT_DOMAIN = GR_STREAM_DOMAIN + +BLOCK_FLAG_THROTTLE = 'throttle' +BLOCK_FLAG_DISABLE_BYPASS = 'disable_bypass' +BLOCK_FLAG_NEED_QT_GUI = 'need_qt_gui' +BLOCK_FLAG_NEED_WX_GUI = 'need_wx_gui' +BLOCK_FLAG_DEPRECATED = 'deprecated' + +# Block States +BLOCK_DISABLED = 0 +BLOCK_ENABLED = 1 +BLOCK_BYPASSED = 2 + +# File creation modes +TOP_BLOCK_FILE_MODE = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | \ + stat.S_IWGRP | stat.S_IXGRP | stat.S_IROTH +HIER_BLOCK_FILE_MODE = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH -#define types, native python + numpy +# Define types, native python + numpy VECTOR_TYPES = (tuple, list, set, numpy.ndarray) COMPLEX_TYPES = [complex, numpy.complex, numpy.complex64, numpy.complex128] REAL_TYPES = [float, numpy.float, numpy.float32, numpy.float64] INT_TYPES = [int, long, numpy.int, numpy.int8, numpy.int16, numpy.int32, numpy.uint64, - numpy.uint, numpy.uint8, numpy.uint16, numpy.uint32, numpy.uint64] -#cast to tuple for isinstance, concat subtypes + numpy.uint, numpy.uint8, numpy.uint16, numpy.uint32, numpy.uint64] +# Cast to tuple for isinstance, concat subtypes COMPLEX_TYPES = tuple(COMPLEX_TYPES + REAL_TYPES + INT_TYPES) REAL_TYPES = tuple(REAL_TYPES + INT_TYPES) INT_TYPES = tuple(INT_TYPES) @@ -82,12 +88,12 @@ GRC_COLOR_LIME = '#CDDC39' GRC_COLOR_TEAL = '#009688' GRC_COLOR_YELLOW = '#FFEB3B' GRC_COLOR_PINK = '#F50057' -GRC_COLOR_LIGHT_PURPLE = '#E040FB' +GRC_COLOR_PURPLE_A100 = '#EA80FC' +GRC_COLOR_PURPLE_A400 = '#D500F9' GRC_COLOR_DARK_GREY = '#72706F' GRC_COLOR_GREY = '#BDBDBD' GRC_COLOR_WHITE = '#FFFFFF' - CORE_TYPES = ( # name, key, sizeof, color ('Complex Float 64', 'fc64', 16, GRC_COLOR_BROWN), ('Complex Float 32', 'fc32', 8, GRC_COLOR_BLUE), @@ -100,7 +106,8 @@ CORE_TYPES = ( # name, key, sizeof, color ('Integer 64', 's64', 8, GRC_COLOR_LIME), ('Integer 32', 's32', 4, GRC_COLOR_TEAL), ('Integer 16', 's16', 2, GRC_COLOR_YELLOW), - ('Integer 8', 's8', 1, GRC_COLOR_LIGHT_PURPLE), + ('Integer 8', 's8', 1, GRC_COLOR_PURPLE_A400), + ('Bits (unpacked byte)', 'bit', 1, GRC_COLOR_PURPLE_A100), ('Message Queue', 'msg', 0, GRC_COLOR_DARK_GREY), ('Async Message', 'message', 0, GRC_COLOR_GREY), ('Bus Connection', 'bus', 0, GRC_COLOR_WHITE), @@ -108,23 +115,26 @@ CORE_TYPES = ( # name, key, sizeof, color ) ALIAS_TYPES = { - 'complex' : (8, GRC_COLOR_BLUE), - 'float' : (4, GRC_COLOR_ORANGE), - 'int' : (4, GRC_COLOR_TEAL), - 'short' : (2, GRC_COLOR_YELLOW), - 'byte' : (1, GRC_COLOR_LIGHT_PURPLE), + 'complex': (8, GRC_COLOR_BLUE), + 'float': (4, GRC_COLOR_ORANGE), + 'int': (4, GRC_COLOR_TEAL), + 'short': (2, GRC_COLOR_YELLOW), + 'byte': (1, GRC_COLOR_PURPLE_A400), + 'bits': (1, GRC_COLOR_PURPLE_A100), } TYPE_TO_COLOR = dict() TYPE_TO_SIZEOF = dict() + for name, key, sizeof, color in CORE_TYPES: TYPE_TO_COLOR[key] = color TYPE_TO_SIZEOF[key] = sizeof + for key, (sizeof, color) in ALIAS_TYPES.iteritems(): TYPE_TO_COLOR[key] = color TYPE_TO_SIZEOF[key] = sizeof -#coloring +# Coloring COMPLEX_COLOR_SPEC = '#3399FF' FLOAT_COLOR_SPEC = '#FF8C69' INT_COLOR_SPEC = '#00FF99' diff --git a/grc/base/Element.py b/grc/core/Element.py index b0f94d0183..67c36e12b4 100644 --- a/grc/base/Element.py +++ b/grc/core/Element.py @@ -1,5 +1,5 @@ """ -Copyright 2008, 2009 Free Software Foundation, Inc. +Copyright 2008, 2009, 2015 Free Software Foundation, Inc. This file is part of GNU Radio GNU Radio Companion is free software; you can redistribute it and/or @@ -17,6 +17,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ + class Element(object): def __init__(self, parent=None): @@ -32,7 +33,8 @@ class Element(object): Call this base method before adding error messages in the subclass. """ del self._error_messages[:] - for child in self.get_children(): child.validate() + for child in self.get_children(): + child.validate() def is_valid(self): """ @@ -61,10 +63,10 @@ class Element(object): Returns: a list of error message strings """ - error_messages = list(self._error_messages) #make a copy + error_messages = list(self._error_messages) # Make a copy for child in filter(lambda c: c.get_enabled() and not c.get_bypassed(), self.get_children()): for msg in child.get_error_messages(): - error_messages.append("%s:\n\t%s"%(child, msg.replace("\n", "\n\t"))) + error_messages.append("{}:\n\t{}".format(child, msg.replace("\n", "\n\t"))) return error_messages def rewrite(self): @@ -72,27 +74,41 @@ class Element(object): Rewrite this element and call rewrite on all children. Call this base method before rewriting the element. """ - for child in self.get_children(): child.rewrite() + for child in self.get_children(): + child.rewrite() + + def get_enabled(self): + return True - def get_enabled(self): return True - def get_bypassed(self): return False + def get_bypassed(self): + return False ############################################## - ## Tree-like API + # Tree-like API ############################################## - def get_parent(self): return self._parent - def get_children(self): return list() + def get_parent(self): + return self._parent + + def get_children(self): + return list() ############################################## - ## Type testing methods + # Type testing ############################################## - def is_element(self): return True - def is_platform(self): return False - def is_flow_graph(self): return False - def is_connection(self): return False - def is_block(self): return False - def is_dummy_block(self): return False - def is_source(self): return False - def is_sink(self): return False - def is_port(self): return False - def is_param(self): return False + is_platform = False + + is_flow_graph = False + + is_block = False + + is_dummy_block = False + + is_connection = False + + is_port = False + + is_param = False + + is_variable = False + + is_import = False diff --git a/grc/core/FlowGraph.py b/grc/core/FlowGraph.py new file mode 100644 index 0000000000..949eecaa71 --- /dev/null +++ b/grc/core/FlowGraph.py @@ -0,0 +1,592 @@ +# Copyright 2008-2015 Free Software Foundation, Inc. +# This file is part of GNU Radio +# +# GNU Radio Companion 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 2 +# of the License, or (at your option) any later version. +# +# GNU Radio Companion 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 imp +import time +from itertools import ifilter, chain +from operator import methodcaller, attrgetter + +import re + +from . import Messages +from .Constants import FLOW_GRAPH_FILE_FORMAT_VERSION +from .Element import Element +from .utils import odict, expr_utils + +_parameter_matcher = re.compile('^(parameter)$') +_monitors_searcher = re.compile('(ctrlport_monitor)') +_bussink_searcher = re.compile('^(bus_sink)$') +_bussrc_searcher = re.compile('^(bus_source)$') +_bus_struct_sink_searcher = re.compile('^(bus_structure_sink)$') +_bus_struct_src_searcher = re.compile('^(bus_structure_source)$') + + +class FlowGraph(Element): + + is_flow_graph = True + + def __init__(self, platform): + """ + Make a flow graph from the arguments. + + Args: + platform: a platforms with blocks and contrcutors + + Returns: + the flow graph object + """ + Element.__init__(self, platform) + self._elements = [] + self._timestamp = time.ctime() + + self.platform = platform # todo: make this a lazy prop + self.blocks = [] + self.connections = [] + + self._eval_cache = {} + self.namespace = {} + + self.grc_file_path = '' + self._options_block = self.new_block('options') + + def __str__(self): + return 'FlowGraph - {}({})'.format(self.get_option('title'), self.get_option('id')) + + ############################################## + # TODO: Move these to new generator package + ############################################## + def get_imports(self): + """ + Get a set of all import statements in this flow graph namespace. + + Returns: + a set of import statements + """ + imports = sum([block.get_imports() for block in self.iter_enabled_blocks()], []) + return sorted(set(imports)) + + def get_variables(self): + """ + Get a list of all variables in this flow graph namespace. + Exclude parameterized variables. + + Returns: + a sorted list of variable blocks in order of dependency (indep -> dep) + """ + variables = filter(attrgetter('is_variable'), self.iter_enabled_blocks()) + return expr_utils.sort_objects(variables, methodcaller('get_id'), methodcaller('get_var_make')) + + def get_parameters(self): + """ + Get a list of all parameterized variables in this flow graph namespace. + + Returns: + a list of parameterized variables + """ + parameters = filter(lambda b: _parameter_matcher.match(b.get_key()), self.iter_enabled_blocks()) + return parameters + + def get_monitors(self): + """ + Get a list of all ControlPort monitors + """ + monitors = filter(lambda b: _monitors_searcher.search(b.get_key()), + self.iter_enabled_blocks()) + return monitors + + def get_python_modules(self): + """Iterate over custom code block ID and Source""" + for block in self.iter_enabled_blocks(): + if block.get_key() == 'epy_module': + yield block.get_id(), block.get_param('source_code').get_value() + + def get_bussink(self): + bussink = filter(lambda b: _bussink_searcher.search(b.get_key()), self.get_enabled_blocks()) + + for i in bussink: + for j in i.get_params(): + if j.get_name() == 'On/Off' and j.get_value() == 'on': + return True + return False + + def get_bussrc(self): + bussrc = filter(lambda b: _bussrc_searcher.search(b.get_key()), self.get_enabled_blocks()) + + for i in bussrc: + for j in i.get_params(): + if j.get_name() == 'On/Off' and j.get_value() == 'on': + return True + return False + + def get_bus_structure_sink(self): + bussink = filter(lambda b: _bus_struct_sink_searcher.search(b.get_key()), self.get_enabled_blocks()) + return bussink + + def get_bus_structure_src(self): + bussrc = filter(lambda b: _bus_struct_src_searcher.search(b.get_key()), self.get_enabled_blocks()) + return bussrc + + def iter_enabled_blocks(self): + """ + Get an iterator of all blocks that are enabled and not bypassed. + """ + return ifilter(methodcaller('get_enabled'), self.blocks) + + def get_enabled_blocks(self): + """ + Get a list of all blocks that are enabled and not bypassed. + + Returns: + a list of blocks + """ + return list(self.iter_enabled_blocks()) + + def get_bypassed_blocks(self): + """ + Get a list of all blocks that are bypassed. + + Returns: + a list of blocks + """ + return filter(methodcaller('get_bypassed'), self.blocks) + + def get_enabled_connections(self): + """ + Get a list of all connections that are enabled. + + Returns: + a list of connections + """ + return filter(methodcaller('get_enabled'), self.connections) + + def get_option(self, key): + """ + Get the option for a given key. + The option comes from the special options block. + + Args: + key: the param key for the options block + + Returns: + the value held by that param + """ + return self._options_block.get_param(key).get_evaluated() + + ############################################## + # Access Elements + ############################################## + def get_block(self, id): + for block in self.blocks: + if block.get_id() == id: + return block + raise KeyError('No block with ID {!r}'.format(id)) + + def get_elements(self): + """ + Get a list of all the elements. + Always ensure that the options block is in the list (only once). + + Returns: + the element list + """ + options_block_count = self.blocks.count(self._options_block) + if not options_block_count: + self.blocks.append(self._options_block) + for i in range(options_block_count-1): + self.blocks.remove(self._options_block) + + return self.blocks + self.connections + + get_children = get_elements + + def rewrite(self): + """ + Flag the namespace to be renewed. + """ + + self.renew_namespace() + for child in chain(self.blocks, self.connections): + child.rewrite() + + self.bus_ports_rewrite() + + def renew_namespace(self): + namespace = {} + # Load imports + for expr in self.get_imports(): + try: + exec expr in namespace + except: + pass + + for id, expr in self.get_python_modules(): + try: + module = imp.new_module(id) + exec expr in module.__dict__ + namespace[id] = module + except: + pass + + # Load parameters + np = {} # params don't know each other + for parameter in self.get_parameters(): + try: + value = eval(parameter.get_param('value').to_code(), namespace) + np[parameter.get_id()] = value + except: + pass + namespace.update(np) # Merge param namespace + + # Load variables + for variable in self.get_variables(): + try: + value = eval(variable.get_var_value(), namespace) + namespace[variable.get_id()] = value + except: + pass + + self.namespace.clear() + self._eval_cache.clear() + self.namespace.update(namespace) + + def evaluate(self, expr): + """ + Evaluate the expression. + + Args: + expr: the string expression + @throw Exception bad expression + + Returns: + the evaluated data + """ + # Evaluate + if not expr: + raise Exception('Cannot evaluate empty statement.') + return self._eval_cache.setdefault(expr, eval(expr, self.namespace)) + + ############################################## + # Add/remove stuff + ############################################## + + def new_block(self, key): + """ + Get a new block of the specified key. + Add the block to the list of elements. + + Args: + key: the block key + + Returns: + the new block or None if not found + """ + try: + block = self.platform.get_new_block(self, key) + self.blocks.append(block) + except KeyError: + block = None + return block + + def connect(self, porta, portb): + """ + Create a connection between porta and portb. + + Args: + porta: a port + portb: another port + @throw Exception bad connection + + Returns: + the new connection + """ + + connection = self.platform.Connection( + flow_graph=self, porta=porta, portb=portb) + self.connections.append(connection) + return connection + + def remove_element(self, element): + """ + Remove the element from the list of elements. + If the element is a port, remove the whole block. + If the element is a block, remove its connections. + If the element is a connection, just remove the connection. + """ + if element.is_port: + # Found a port, set to parent signal block + element = element.get_parent() + + if element in self.blocks: + # Remove block, remove all involved connections + for port in element.get_ports(): + map(self.remove_element, port.get_connections()) + self.blocks.remove(element) + + elif element in self.connections: + if element.is_bus(): + cons_list = [] + for i in map(lambda a: a.get_connections(), element.get_source().get_associated_ports()): + cons_list.extend(i) + map(self.remove_element, cons_list) + self.connections.remove(element) + + ############################################## + # Import/Export Methods + ############################################## + def export_data(self): + """ + Export this flow graph to nested data. + Export all block and connection data. + + Returns: + a nested data odict + """ + # sort blocks and connections for nicer diffs + blocks = sorted(self.blocks, key=lambda b: ( + b.get_key() != 'options', # options to the front + not b.get_key().startswith('variable'), # then vars + str(b) + )) + connections = sorted(self.connections, key=str) + n = odict() + n['timestamp'] = self._timestamp + n['block'] = [b.export_data() for b in blocks] + n['connection'] = [c.export_data() for c in connections] + instructions = odict({ + 'created': '.'.join(self.get_parent().config.version_parts), + 'format': FLOW_GRAPH_FILE_FORMAT_VERSION, + }) + return odict({'flow_graph': n, '_instructions': instructions}) + + def import_data(self, n): + """ + Import blocks and connections into this flow graph. + Clear this flowgraph of all previous blocks and connections. + Any blocks or connections in error will be ignored. + + Args: + n: the nested data odict + """ + # Remove previous elements + del self.blocks[:] + del self.connections[:] + # set file format + try: + instructions = n.find('_instructions') or {} + file_format = int(instructions.get('format', '0')) or _guess_file_format_1(n) + except: + file_format = 0 + + fg_n = n and n.find('flow_graph') or odict() # use blank data if none provided + self._timestamp = fg_n.find('timestamp') or time.ctime() + + # build the blocks + self._options_block = self.new_block('options') + for block_n in fg_n.findall('block'): + key = block_n.find('key') + block = self._options_block if key == 'options' else self.new_block(key) + + if not block: + # we're before the initial fg update(), so no evaluated values! + # --> use raw value instead + path_param = self._options_block.get_param('hier_block_src_path') + file_path = self.platform.find_file_in_paths( + filename=key + '.grc', + paths=path_param.get_value(), + cwd=self.grc_file_path + ) + if file_path: # grc file found. load and get block + self.platform.load_and_generate_flow_graph(file_path) + block = self.new_block(key) # can be None + + if not block: # looks like this block key cannot be found + # create a dummy block instead + block = self.new_block('dummy_block') + # Ugly ugly ugly + _initialize_dummy_block(block, block_n) + print('Block key "%s" not found' % key) + + block.import_data(block_n) + + self.rewrite() # evaluate stuff like nports before adding connections + + # build the connections + def verify_and_get_port(key, block, dir): + ports = block.get_sinks() if dir == 'sink' else block.get_sources() + for port in ports: + if key == port.get_key(): + break + if not key.isdigit() and port.get_type() == '' and key == port.get_name(): + break + else: + if block.is_dummy_block: + port = _dummy_block_add_port(block, key, dir) + else: + raise LookupError('%s key %r not in %s block keys' % (dir, key, dir)) + return port + + errors = False + for connection_n in fg_n.findall('connection'): + # get the block ids and port keys + source_block_id = connection_n.find('source_block_id') + sink_block_id = connection_n.find('sink_block_id') + source_key = connection_n.find('source_key') + sink_key = connection_n.find('sink_key') + try: + source_block = self.get_block(source_block_id) + sink_block = self.get_block(sink_block_id) + + # fix old, numeric message ports keys + if file_format < 1: + source_key, sink_key = _update_old_message_port_keys( + source_key, sink_key, source_block, sink_block) + + # build the connection + source_port = verify_and_get_port(source_key, source_block, 'source') + sink_port = verify_and_get_port(sink_key, sink_block, 'sink') + self.connect(source_port, sink_port) + except LookupError as e: + Messages.send_error_load( + 'Connection between {}({}) and {}({}) could not be made.\n\t{}'.format( + source_block_id, source_key, sink_block_id, sink_key, e)) + errors = True + + self.rewrite() # global rewrite + return errors + + ############################################## + # Needs to go + ############################################## + def bus_ports_rewrite(self): + # todo: move to block.rewrite() + for block in self.blocks: + for direc in ['source', 'sink']: + if direc == 'source': + get_p = block.get_sources + get_p_gui = block.get_sources_gui + bus_structure = block.form_bus_structure('source') + else: + get_p = block.get_sinks + get_p_gui = block.get_sinks_gui + bus_structure = block.form_bus_structure('sink') + + if 'bus' in map(lambda a: a.get_type(), get_p_gui()): + if len(get_p_gui()) > len(bus_structure): + times = range(len(bus_structure), len(get_p_gui())) + for i in times: + for connect in get_p_gui()[-1].get_connections(): + block.get_parent().remove_element(connect) + get_p().remove(get_p_gui()[-1]) + elif len(get_p_gui()) < len(bus_structure): + n = {'name': 'bus', 'type': 'bus'} + if True in map( + lambda a: isinstance(a.get_nports(), int), + get_p()): + n['nports'] = str(1) + + times = range(len(get_p_gui()), len(bus_structure)) + + for i in times: + n['key'] = str(len(get_p())) + n = odict(n) + port = block.get_parent().get_parent().Port( + block=block, n=n, dir=direc) + get_p().append(port) + + if 'bus' in map(lambda a: a.get_type(), + block.get_sources_gui()): + for i in range(len(block.get_sources_gui())): + if len(block.get_sources_gui()[ + i].get_connections()) > 0: + source = block.get_sources_gui()[i] + sink = [] + + for j in range(len(source.get_connections())): + sink.append( + source.get_connections()[j].get_sink()) + for elt in source.get_connections(): + self.remove_element(elt) + for j in sink: + self.connect(source, j) + + +def _update_old_message_port_keys(source_key, sink_key, source_block, sink_block): + """ + Backward compatibility for message port keys + + Message ports use their names as key (like in the 'connect' method). + Flowgraph files from former versions still have numeric keys stored for + message connections. These have to be replaced by the name of the + respective port. The correct message port is deduced from the integer + value of the key (assuming the order has not changed). + + The connection ends are updated only if both ends translate into a + message port. + """ + try: + # get ports using the "old way" (assuming liner indexed keys) + source_port = source_block.get_sources()[int(source_key)] + sink_port = sink_block.get_sinks()[int(sink_key)] + if source_port.get_type() == "message" and sink_port.get_type() == "message": + source_key, sink_key = source_port.get_key(), sink_port.get_key() + except (ValueError, IndexError): + pass + return source_key, sink_key # do nothing + + +def _guess_file_format_1(n): + """ + Try to guess the file format for flow-graph files without version tag + """ + try: + has_non_numeric_message_keys = any(not ( + connection_n.find('source_key').isdigit() and + connection_n.find('sink_key').isdigit() + ) for connection_n in n.find('flow_graph').findall('connection')) + if has_non_numeric_message_keys: + return 1 + except: + pass + return 0 + + +def _initialize_dummy_block(block, block_n): + """ + This is so ugly... dummy-fy a block + Modify block object to get the behaviour for a missing block + """ + + block._key = block_n.find('key') + block.is_dummy_block = lambda: True + block.is_valid = lambda: False + block.get_enabled = lambda: False + for param_n in block_n.findall('param'): + if param_n['key'] not in block.get_param_keys(): + new_param_n = odict({'key': param_n['key'], 'name': param_n['key'], 'type': 'string'}) + params = block.get_parent().get_parent().Param(block=block, n=new_param_n) + block.get_params().append(params) + + +def _dummy_block_add_port(block, key, dir): + """ This is so ugly... Add a port to a dummy-field block """ + port_n = odict({'name': '?', 'key': key, 'type': ''}) + port = block.get_parent().get_parent().Port(block=block, n=port_n, dir=dir) + if port.is_source: + block.get_sources().append(port) + else: + block.get_sinks().append(port) + return port diff --git a/grc/gui/Messages.py b/grc/core/Messages.py index 551a8ce753..8daa12c33f 100644 --- a/grc/gui/Messages.py +++ b/grc/core/Messages.py @@ -1,21 +1,20 @@ -""" -Copyright 2007 Free Software Foundation, Inc. -This file is part of GNU Radio +# Copyright 2007, 2015 Free Software Foundation, Inc. +# This file is part of GNU Radio +# +# GNU Radio Companion 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 2 +# of the License, or (at your option) any later version. +# +# GNU Radio Companion 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 -GNU Radio Companion 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 2 -of the License, or (at your option) any later version. - -GNU Radio Companion 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 traceback import sys @@ -59,24 +58,12 @@ register_messenger(sys.stdout.write) # Special functions for specific program functionalities ########################################################################### def send_init(platform): - p = platform - - def get_paths(x): - return os.path.abspath(os.path.expanduser(x)), x - - send('\n'.join([ - "<<< Welcome to %s %s >>>" % (p.get_name(), p.get_version()), - "", - "Preferences file: " + p.get_prefs_file(), - "Block paths:" - ] + [ - "\t%s" % path + (" (%s)" % opath if opath != path else "") - for path, opath in map(get_paths, p.get_block_paths()) - ]) + "\n") - - -def send_page_switch(file_path): - send('\nShowing: "%s"\n' % file_path) + msg = "<<< Welcome to {config.name} {config.version} >>>\n\n" \ + "Block paths:\n\t{paths}\n" + send(msg.format( + config=platform.config, + paths="\n\t".join(platform.config.block_paths)) + ) def send_xml_errors_if_any(xml_failures): diff --git a/grc/core/Param.py b/grc/core/Param.py new file mode 100644 index 0000000000..d155800c43 --- /dev/null +++ b/grc/core/Param.py @@ -0,0 +1,740 @@ +""" +Copyright 2008-2015 Free Software Foundation, Inc. +This file is part of GNU Radio + +GNU Radio Companion 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 2 +of the License, or (at your option) any later version. + +GNU Radio Companion 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 ast +import weakref +import re + +from . import Constants +from .Constants import VECTOR_TYPES, COMPLEX_TYPES, REAL_TYPES, INT_TYPES +from .Element import Element +from .utils import odict + +# Blacklist certain ids, its not complete, but should help +import __builtin__ + + +ID_BLACKLIST = ['self', 'options', 'gr', 'blks2', 'wxgui', 'wx', 'math', 'forms', 'firdes'] + dir(__builtin__) +try: + from gnuradio import gr + ID_BLACKLIST.extend(attr for attr in dir(gr.top_block()) if not attr.startswith('_')) +except ImportError: + pass + +_check_id_matcher = re.compile('^[a-z|A-Z]\w*$') +_show_id_matcher = re.compile('^(variable\w*|parameter|options|notebook)$') + + +def _get_keys(lst): + return [elem.get_key() for elem in lst] + + +def _get_elem(lst, key): + try: + return lst[_get_keys(lst).index(key)] + except ValueError: + raise ValueError('Key "{}" not found in {}.'.format(key, _get_keys(lst))) + + +def num_to_str(num): + """ Display logic for numbers """ + def eng_notation(value, fmt='g'): + """Convert a number to a string in engineering notation. E.g., 5e-9 -> 5n""" + template = '{:' + fmt + '}{}' + magnitude = abs(value) + for exp, symbol in zip(range(9, -15-1, -3), 'GMk munpf'): + factor = 10 ** exp + if magnitude >= factor: + return template.format(value / factor, symbol.strip()) + return template.format(value, '') + + if isinstance(num, COMPLEX_TYPES): + num = complex(num) # Cast to python complex + if num == 0: + return '0' + output = eng_notation(num.real) if num.real else '' + output += eng_notation(num.imag, '+g' if output else 'g') + 'j' if num.imag else '' + return output + else: + return str(num) + + +class Option(Element): + + def __init__(self, param, n): + Element.__init__(self, param) + self._name = n.find('name') + self._key = n.find('key') + self._opts = dict() + opts = n.findall('opt') + # Test against opts when non enum + if not self.get_parent().is_enum() and opts: + raise Exception('Options for non-enum types cannot have sub-options') + # Extract opts + for opt in opts: + # Separate the key:value + try: + key, value = opt.split(':') + except: + raise Exception('Error separating "{}" into key:value'.format(opt)) + # Test against repeated keys + if key in self._opts: + raise Exception('Key "{}" already exists in option'.format(key)) + # Store the option + self._opts[key] = value + + def __str__(self): + return 'Option {}({})'.format(self.get_name(), self.get_key()) + + def get_name(self): + return self._name + + def get_key(self): + return self._key + + ############################################## + # Access Opts + ############################################## + def get_opt_keys(self): + return self._opts.keys() + + def get_opt(self, key): + return self._opts[key] + + def get_opts(self): + return self._opts.values() + + +class TemplateArg(object): + """ + A cheetah template argument created from a param. + The str of this class evaluates to the param's to code method. + The use of this class as a dictionary (enum only) will reveal the enum opts. + The __call__ or () method can return the param evaluated to a raw python data type. + """ + + def __init__(self, param): + self._param = weakref.proxy(param) + + def __getitem__(self, item): + return str(self._param.get_opt(item)) if self._param.is_enum() else NotImplemented + + def __str__(self): + return str(self._param.to_code()) + + def __call__(self): + return self._param.get_evaluated() + + +class Param(Element): + + is_param = True + + def __init__(self, block, n): + """ + Make a new param from nested data. + + Args: + block: the parent element + n: the nested odict + """ + # If the base key is a valid param key, copy its data and overlay this params data + base_key = n.find('base_key') + if base_key and base_key in block.get_param_keys(): + n_expanded = block.get_param(base_key)._n.copy() + n_expanded.update(n) + n = n_expanded + # Save odict in case this param will be base for another + self._n = n + # Parse the data + self._name = n.find('name') + self._key = n.find('key') + value = n.find('value') or '' + self._type = n.find('type') or 'raw' + self._hide = n.find('hide') or '' + self._tab_label = n.find('tab') or block.get_param_tab_labels()[0] + if self._tab_label not in block.get_param_tab_labels(): + block.get_param_tab_labels().append(self._tab_label) + # Build the param + Element.__init__(self, block) + # Create the Option objects from the n data + self._options = list() + self._evaluated = None + for option in map(lambda o: Option(param=self, n=o), n.findall('option')): + key = option.get_key() + # Test against repeated keys + if key in self.get_option_keys(): + raise Exception('Key "{}" already exists in options'.format(key)) + # Store the option + self.get_options().append(option) + # Test the enum options + if self.is_enum(): + # Test against options with identical keys + if len(set(self.get_option_keys())) != len(self.get_options()): + raise Exception('Options keys "{}" are not unique.'.format(self.get_option_keys())) + # Test against inconsistent keys in options + opt_keys = self.get_options()[0].get_opt_keys() + for option in self.get_options(): + if set(opt_keys) != set(option.get_opt_keys()): + raise Exception('Opt keys "{}" are not identical across all options.'.format(opt_keys)) + # If a value is specified, it must be in the options keys + if value or value in self.get_option_keys(): + self._value = value + else: + self._value = self.get_option_keys()[0] + if self.get_value() not in self.get_option_keys(): + raise Exception('The value "{}" is not in the possible values of "{}".'.format(self.get_value(), self.get_option_keys())) + else: + self._value = value or '' + self._default = value + self._init = False + self._hostage_cells = list() + self.template_arg = TemplateArg(self) + + def get_types(self): + return ( + 'raw', 'enum', + 'complex', 'real', 'float', 'int', + 'complex_vector', 'real_vector', 'float_vector', 'int_vector', + 'hex', 'string', 'bool', + 'file_open', 'file_save', '_multiline', '_multiline_python_external', + 'id', 'stream_id', + 'grid_pos', 'notebook', 'gui_hint', + 'import', + ) + + def __repr__(self): + """ + Get the repr (nice string format) for this param. + + Returns: + the string representation + """ + ################################################## + # Truncate helper method + ################################################## + def _truncate(string, style=0): + max_len = max(27 - len(self.get_name()), 3) + if len(string) > max_len: + if style < 0: # Front truncate + string = '...' + string[3-max_len:] + elif style == 0: # Center truncate + string = string[:max_len/2 - 3] + '...' + string[-max_len/2:] + elif style > 0: # Rear truncate + string = string[:max_len-3] + '...' + return string + + ################################################## + # Simple conditions + ################################################## + if not self.is_valid(): + return _truncate(self.get_value()) + if self.get_value() in self.get_option_keys(): + return self.get_option(self.get_value()).get_name() + + ################################################## + # Split up formatting by type + ################################################## + # Default center truncate + truncate = 0 + e = self.get_evaluated() + t = self.get_type() + if isinstance(e, bool): + return str(e) + elif isinstance(e, COMPLEX_TYPES): + dt_str = num_to_str(e) + elif isinstance(e, VECTOR_TYPES): + # Vector types + if len(e) > 8: + # Large vectors use code + dt_str = self.get_value() + truncate = 1 + else: + # Small vectors use eval + dt_str = ', '.join(map(num_to_str, e)) + elif t in ('file_open', 'file_save'): + dt_str = self.get_value() + truncate = -1 + else: + # Other types + dt_str = str(e) + + # Done + return _truncate(dt_str, truncate) + + def __repr2__(self): + """ + Get the repr (nice string format) for this param. + + Returns: + the string representation + """ + if self.is_enum(): + return self.get_option(self.get_value()).get_name() + return self.get_value() + + def __str__(self): + return 'Param - {}({})'.format(self.get_name(), self.get_key()) + + def get_color(self): + """ + Get the color that represents this param's type. + + Returns: + a hex color code. + """ + try: + return { + # Number types + 'complex': Constants.COMPLEX_COLOR_SPEC, + 'real': Constants.FLOAT_COLOR_SPEC, + 'float': Constants.FLOAT_COLOR_SPEC, + 'int': Constants.INT_COLOR_SPEC, + # Vector types + 'complex_vector': Constants.COMPLEX_VECTOR_COLOR_SPEC, + 'real_vector': Constants.FLOAT_VECTOR_COLOR_SPEC, + 'float_vector': Constants.FLOAT_VECTOR_COLOR_SPEC, + 'int_vector': Constants.INT_VECTOR_COLOR_SPEC, + # Special + 'bool': Constants.INT_COLOR_SPEC, + 'hex': Constants.INT_COLOR_SPEC, + 'string': Constants.BYTE_VECTOR_COLOR_SPEC, + 'id': Constants.ID_COLOR_SPEC, + 'stream_id': Constants.ID_COLOR_SPEC, + 'grid_pos': Constants.INT_VECTOR_COLOR_SPEC, + 'notebook': Constants.INT_VECTOR_COLOR_SPEC, + 'raw': Constants.WILDCARD_COLOR_SPEC, + }[self.get_type()] + except: + return '#FFFFFF' + + def get_hide(self): + """ + Get the hide value from the base class. + Hide the ID parameter for most blocks. Exceptions below. + If the parameter controls a port type, vlen, or nports, return part. + If the parameter is an empty grid position, return part. + These parameters are redundant to display in the flow graph view. + + Returns: + hide the hide property string + """ + hide = self.get_parent().resolve_dependencies(self._hide).strip() + if hide: + return hide + # Hide ID in non variable blocks + if self.get_key() == 'id' and not _show_id_matcher.match(self.get_parent().get_key()): + return 'part' + # Hide port controllers for type and nports + if self.get_key() in ' '.join(map(lambda p: ' '.join([p._type, p._nports]), + self.get_parent().get_ports())): + return 'part' + # Hide port controllers for vlen, when == 1 + if self.get_key() in ' '.join(map( + lambda p: p._vlen, self.get_parent().get_ports()) + ): + try: + if int(self.get_evaluated()) == 1: + return 'part' + except: + pass + # Hide empty grid positions + if self.get_key() in ('grid_pos', 'notebook') and not self.get_value(): + return 'part' + return hide + + def validate(self): + """ + Validate the param. + The value must be evaluated and type must a possible type. + """ + Element.validate(self) + if self.get_type() not in self.get_types(): + self.add_error_message('Type "{}" is not a possible type.'.format(self.get_type())) + + self._evaluated = None + try: + self._evaluated = self.evaluate() + except Exception, e: + self.add_error_message(str(e)) + + def get_evaluated(self): + return self._evaluated + + def evaluate(self): + """ + Evaluate the value. + + Returns: + evaluated type + """ + self._init = True + self._lisitify_flag = False + self._stringify_flag = False + self._hostage_cells = list() + t = self.get_type() + v = self.get_value() + + ######################### + # Enum Type + ######################### + if self.is_enum(): + return v + + ######################### + # Numeric Types + ######################### + elif t in ('raw', 'complex', 'real', 'float', 'int', 'hex', 'bool'): + # Raise exception if python cannot evaluate this value + try: + e = self.get_parent().get_parent().evaluate(v) + except Exception, e: + raise Exception('Value "{}" cannot be evaluated:\n{}'.format(v, e)) + # Raise an exception if the data is invalid + if t == 'raw': + return e + elif t == 'complex': + if not isinstance(e, COMPLEX_TYPES): + raise Exception('Expression "{}" is invalid for type complex.'.format(str(e))) + return e + elif t == 'real' or t == 'float': + if not isinstance(e, REAL_TYPES): + raise Exception('Expression "{}" is invalid for type float.'.format(str(e))) + return e + elif t == 'int': + if not isinstance(e, INT_TYPES): + raise Exception('Expression "{}" is invalid for type integer.'.format(str(e))) + return e + elif t == 'hex': + return hex(e) + elif t == 'bool': + if not isinstance(e, bool): + raise Exception('Expression "{}" is invalid for type bool.'.format(str(e))) + return e + else: + raise TypeError('Type "{}" not handled'.format(t)) + ######################### + # Numeric Vector Types + ######################### + elif t in ('complex_vector', 'real_vector', 'float_vector', 'int_vector'): + if not v: + # Turn a blank string into an empty list, so it will eval + v = '()' + # Raise exception if python cannot evaluate this value + try: + e = self.get_parent().get_parent().evaluate(v) + except Exception, e: + raise Exception('Value "{}" cannot be evaluated:\n{}'.format(v, e)) + # Raise an exception if the data is invalid + if t == 'complex_vector': + if not isinstance(e, VECTOR_TYPES): + self._lisitify_flag = True + e = [e] + if not all([isinstance(ei, COMPLEX_TYPES) for ei in e]): + raise Exception('Expression "{}" is invalid for type complex vector.'.format(str(e))) + return e + elif t == 'real_vector' or t == 'float_vector': + if not isinstance(e, VECTOR_TYPES): + self._lisitify_flag = True + e = [e] + if not all([isinstance(ei, REAL_TYPES) for ei in e]): + raise Exception('Expression "{}" is invalid for type float vector.'.format(str(e))) + return e + elif t == 'int_vector': + if not isinstance(e, VECTOR_TYPES): + self._lisitify_flag = True + e = [e] + if not all([isinstance(ei, INT_TYPES) for ei in e]): + raise Exception('Expression "{}" is invalid for type integer vector.'.format(str(e))) + return e + ######################### + # String Types + ######################### + elif t in ('string', 'file_open', 'file_save', '_multiline', '_multiline_python_external'): + # Do not check if file/directory exists, that is a runtime issue + try: + e = self.get_parent().get_parent().evaluate(v) + if not isinstance(e, str): + raise Exception() + except: + self._stringify_flag = True + e = str(v) + if t == '_multiline_python_external': + ast.parse(e) # Raises SyntaxError + return e + ######################### + # Unique ID Type + ######################### + elif t == 'id': + # Can python use this as a variable? + if not _check_id_matcher.match(v): + raise Exception('ID "{}" must begin with a letter and may contain letters, numbers, and underscores.'.format(v)) + ids = [param.get_value() for param in self.get_all_params(t)] + + # Id should only appear once, or zero times if block is disabled + if ids.count(v) > 1: + raise Exception('ID "{}" is not unique.'.format(v)) + if v in ID_BLACKLIST: + raise Exception('ID "{}" is blacklisted.'.format(v)) + return v + + ######################### + # Stream ID Type + ######################### + elif t == 'stream_id': + # Get a list of all stream ids used in the virtual sinks + ids = [param.get_value() for param in filter( + lambda p: p.get_parent().is_virtual_sink(), + self.get_all_params(t), + )] + # Check that the virtual sink's stream id is unique + if self.get_parent().is_virtual_sink(): + # Id should only appear once, or zero times if block is disabled + if ids.count(v) > 1: + raise Exception('Stream ID "{}" is not unique.'.format(v)) + # Check that the virtual source's steam id is found + if self.get_parent().is_virtual_source(): + if v not in ids: + raise Exception('Stream ID "{}" is not found.'.format(v)) + return v + + ######################### + # GUI Position/Hint + ######################### + elif t == 'gui_hint': + if ':' in v: + tab, pos = v.split(':') + elif '@' in v: + tab, pos = v, '' + else: + tab, pos = '', v + + if '@' in tab: + tab, index = tab.split('@') + else: + index = '?' + + # TODO: Problem with this code. Produces bad tabs + widget_str = ({ + (True, True): 'self.%(tab)s_grid_layout_%(index)s.addWidget(%(widget)s, %(pos)s)', + (True, False): 'self.%(tab)s_layout_%(index)s.addWidget(%(widget)s)', + (False, True): 'self.top_grid_layout.addWidget(%(widget)s, %(pos)s)', + (False, False): 'self.top_layout.addWidget(%(widget)s)', + }[bool(tab), bool(pos)]) % {'tab': tab, 'index': index, 'widget': '%s', 'pos': pos} + + # FIXME: Move replace(...) into the make template of the qtgui blocks + # Return a string here + class GuiHint(object): + def __init__(self, ws): + self._ws = ws + + def __call__(self, w): + return (self._ws.replace('addWidget', 'addLayout') if 'layout' in w else self._ws) % w + + def __str__(self): + return self._ws + return GuiHint(widget_str) + ######################### + # Grid Position Type + ######################### + elif t == 'grid_pos': + if not v: + # Allow for empty grid pos + return '' + e = self.get_parent().get_parent().evaluate(v) + if not isinstance(e, (list, tuple)) or len(e) != 4 or not all([isinstance(ei, int) for ei in e]): + raise Exception('A grid position must be a list of 4 integers.') + row, col, row_span, col_span = e + # Check row, col + if row < 0 or col < 0: + raise Exception('Row and column must be non-negative.') + # Check row span, col span + if row_span <= 0 or col_span <= 0: + raise Exception('Row and column span must be greater than zero.') + # Get hostage cell parent + try: + my_parent = self.get_parent().get_param('notebook').evaluate() + except: + my_parent = '' + # Calculate hostage cells + for r in range(row_span): + for c in range(col_span): + self._hostage_cells.append((my_parent, (row+r, col+c))) + # Avoid collisions + params = filter(lambda p: p is not self, self.get_all_params('grid_pos')) + for param in params: + for parent, cell in param._hostage_cells: + if (parent, cell) in self._hostage_cells: + raise Exception('Another graphical element is using parent "{}", cell "{}".'.format(str(parent), str(cell))) + return e + ######################### + # Notebook Page Type + ######################### + elif t == 'notebook': + if not v: + # Allow for empty notebook + return '' + + # Get a list of all notebooks + notebook_blocks = filter(lambda b: b.get_key() == 'notebook', self.get_parent().get_parent().get_enabled_blocks()) + # Check for notebook param syntax + try: + notebook_id, page_index = map(str.strip, v.split(',')) + except: + raise Exception('Bad notebook page format.') + # Check that the notebook id is valid + try: + notebook_block = filter(lambda b: b.get_id() == notebook_id, notebook_blocks)[0] + except: + raise Exception('Notebook id "{}" is not an existing notebook id.'.format(notebook_id)) + + # Check that page index exists + if int(page_index) not in range(len(notebook_block.get_param('labels').evaluate())): + raise Exception('Page index "{}" is not a valid index number.'.format(page_index)) + return notebook_id, page_index + + ######################### + # Import Type + ######################### + elif t == 'import': + # New namespace + n = dict() + try: + exec v in n + except ImportError: + raise Exception('Import "{}" failed.'.format(v)) + except Exception: + raise Exception('Bad import syntax: "{}".'.format(v)) + return filter(lambda k: str(k) != '__builtins__', n.keys()) + + ######################### + else: + raise TypeError('Type "{}" not handled'.format(t)) + + def to_code(self): + """ + Convert the value to code. + For string and list types, check the init flag, call evaluate(). + This ensures that evaluate() was called to set the xxxify_flags. + + Returns: + a string representing the code + """ + v = self.get_value() + t = self.get_type() + # String types + if t in ('string', 'file_open', 'file_save', '_multiline', '_multiline_python_external'): + if not self._init: + self.evaluate() + if self._stringify_flag: + return '"%s"' % v.replace('"', '\"') + else: + return v + # Vector types + elif t in ('complex_vector', 'real_vector', 'float_vector', 'int_vector'): + if not self._init: + self.evaluate() + if self._lisitify_flag: + return '(%s, )' % v + else: + return '(%s)' % v + else: + return v + + def get_all_params(self, type): + """ + Get all the params from the flowgraph that have the given type. + + Args: + type: the specified type + + Returns: + a list of params + """ + return sum([filter(lambda p: p.get_type() == type, block.get_params()) for block in self.get_parent().get_parent().get_enabled_blocks()], []) + + def is_enum(self): + return self._type == 'enum' + + def get_value(self): + value = self._value + if self.is_enum() and value not in self.get_option_keys(): + value = self.get_option_keys()[0] + self.set_value(value) + return value + + def set_value(self, value): + # Must be a string + self._value = str(value) + + def set_default(self, value): + if self._default == self._value: + self.set_value(value) + self._default = str(value) + + def get_type(self): + return self.get_parent().resolve_dependencies(self._type) + + def get_tab_label(self): + return self._tab_label + + def get_name(self): + return self.get_parent().resolve_dependencies(self._name).strip() + + def get_key(self): + return self._key + + ############################################## + # Access Options + ############################################## + def get_option_keys(self): + return _get_keys(self.get_options()) + + def get_option(self, key): + return _get_elem(self.get_options(), key) + + def get_options(self): + return self._options + + ############################################## + # Access Opts + ############################################## + def get_opt_keys(self): + return self.get_option(self.get_value()).get_opt_keys() + + def get_opt(self, key): + return self.get_option(self.get_value()).get_opt(key) + + def get_opts(self): + return self.get_option(self.get_value()).get_opts() + + ############################################## + # Import/Export Methods + ############################################## + def export_data(self): + """ + Export this param's key/value. + + Returns: + a nested data odict + """ + n = odict() + n['key'] = self.get_key() + n['value'] = self.get_value() + return n diff --git a/grc/base/ParseXML.py b/grc/core/ParseXML.py index e05fc1428d..c9f6541ee7 100644 --- a/grc/base/ParseXML.py +++ b/grc/core/ParseXML.py @@ -1,5 +1,5 @@ """ -Copyright 2008 Free Software Foundation, Inc. +Copyright 2008, 2015 Free Software Foundation, Inc. This file is part of GNU Radio GNU Radio Companion is free software; you can redistribute it and/or @@ -18,7 +18,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ from lxml import etree -from . import odict + +from .utils import odict xml_failures = {} etree.set_default_parser(etree.XMLParser(remove_comments=True)) @@ -42,7 +43,7 @@ def validate_dtd(xml_file, dtd_file=None): dtd_file: the optional dtd file @throws Exception validation fails """ - # perform parsing, use dtd validation if dtd file is not specified + # Perform parsing, use dtd validation if dtd file is not specified parser = etree.XMLParser(dtd_validation=not dtd_file) try: xml = etree.parse(xml_file, parser=parser) @@ -51,7 +52,7 @@ def validate_dtd(xml_file, dtd_file=None): if parser.error_log: raise XMLSyntaxError(parser.error_log) - # perform dtd validation if the dtd file is specified + # Perform dtd validation if the dtd file is specified if not dtd_file: return try: @@ -100,9 +101,11 @@ def _from_file(xml): nested_data = odict() for elem in xml: key, value = _from_file(elem).items()[0] - if nested_data.has_key(key): nested_data[key].append(value) - else: nested_data[key] = [value] - # delistify if the length of values is 1 + if key in nested_data: + nested_data[key].append(value) + else: + nested_data[key] = [value] + # Delistify if the length of values is 1 for key, values in nested_data.iteritems(): if len(values) == 1: nested_data[key] = values[0] @@ -120,7 +123,8 @@ def to_file(nested_data, xml_file): """ xml_data = "" instructions = nested_data.pop('_instructions', None) - if instructions: # create the processing instruction from the array + # Create the processing instruction from the array + if instructions: xml_data += etree.tostring(etree.ProcessingInstruction( 'grc', ' '.join( "{0}='{1}'".format(*item) for item in instructions.iteritems()) @@ -143,7 +147,7 @@ def _to_file(nested_data): """ nodes = list() for key, values in nested_data.iteritems(): - # listify the values if not a list + # Listify the values if not a list if not isinstance(values, (list, set, tuple)): values = [values] for value in values: diff --git a/grc/core/Platform.py b/grc/core/Platform.py new file mode 100644 index 0000000000..9b25e67d65 --- /dev/null +++ b/grc/core/Platform.py @@ -0,0 +1,309 @@ +""" +Copyright 2008-2016 Free Software Foundation, Inc. +This file is part of GNU Radio + +GNU Radio Companion 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 2 +of the License, or (at your option) any later version. + +GNU Radio Companion 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 os +import sys + +from . import ParseXML, Messages, Constants + +from .Config import Config +from .Element import Element +from .generator import Generator +from .FlowGraph import FlowGraph +from .Connection import Connection +from .Block import Block +from .Port import Port +from .Param import Param + +from .utils import odict, extract_docs + + +class Platform(Element): + + Config = Config + Generator = Generator + FlowGraph = FlowGraph + Connection = Connection + Block = Block + Port = Port + Param = Param + + is_platform = True + + def __init__(self, *args, **kwargs): + """ Make a platform for GNU Radio """ + Element.__init__(self) + + self.config = self.Config(*args, **kwargs) + + self.block_docstrings = {} + self.block_docstrings_loaded_callback = lambda: None # dummy to be replaced by BlockTreeWindow + + self._docstring_extractor = extract_docs.SubprocessLoader( + callback_query_result=self._save_docstring_extraction_result, + callback_finished=lambda: self.block_docstrings_loaded_callback() + ) + + self._block_dtd = Constants.BLOCK_DTD + self._default_flow_graph = Constants.DEFAULT_FLOW_GRAPH + + # Create a dummy flow graph for the blocks + self._flow_graph = Element(self) + self._flow_graph.connections = [] + + self.blocks = odict() + self._blocks_n = odict() + self._block_categories = {} + self.domains = {} + self.connection_templates = {} + + self._auto_hier_block_generate_chain = set() + + self.build_block_library() + + def __str__(self): + return 'Platform - {}({})'.format(self.config.key, self.config.name) + + @staticmethod + def find_file_in_paths(filename, paths, cwd): + """Checks the provided paths relative to cwd for a certain filename""" + if not os.path.isdir(cwd): + cwd = os.path.dirname(cwd) + if isinstance(paths, str): + paths = (p for p in paths.split(':') if p) + + for path in paths: + path = os.path.expanduser(path) + if not os.path.isabs(path): + path = os.path.normpath(os.path.join(cwd, path)) + file_path = os.path.join(path, filename) + if os.path.exists(os.path.normpath(file_path)): + return file_path + + def load_and_generate_flow_graph(self, file_path): + """Loads a flow graph from file and generates it""" + Messages.set_indent(len(self._auto_hier_block_generate_chain)) + Messages.send('>>> Loading: %r\n' % file_path) + if file_path in self._auto_hier_block_generate_chain: + Messages.send(' >>> Warning: cyclic hier_block dependency\n') + return False + self._auto_hier_block_generate_chain.add(file_path) + try: + flow_graph = self.get_new_flow_graph() + flow_graph.grc_file_path = file_path + # Other, nested higiter_blocks might be auto-loaded here + flow_graph.import_data(self.parse_flow_graph(file_path)) + flow_graph.rewrite() + flow_graph.validate() + if not flow_graph.is_valid(): + raise Exception('Flowgraph invalid') + if not flow_graph.get_option('generate_options').startswith('hb'): + raise Exception('Not a hier block') + except Exception as e: + Messages.send('>>> Load Error: {}: {}\n'.format(file_path, str(e))) + return False + finally: + self._auto_hier_block_generate_chain.discard(file_path) + Messages.set_indent(len(self._auto_hier_block_generate_chain)) + + try: + Messages.send('>>> Generating: {}\n'.format(file_path)) + generator = self.Generator(flow_graph, file_path) + generator.write() + except Exception as e: + Messages.send('>>> Generate Error: {}: {}\n'.format(file_path, str(e))) + return False + + self.load_block_xml(generator.get_file_path_xml()) + return True + + def build_block_library(self): + """load the blocks and block tree from the search paths""" + self._docstring_extractor.start() + # Reset + self.blocks.clear() + self._blocks_n.clear() + self._block_categories.clear() + self.domains.clear() + self.connection_templates.clear() + ParseXML.xml_failures.clear() + + # Try to parse and load blocks + for xml_file in self.iter_xml_files(): + try: + if xml_file.endswith("block_tree.xml"): + self.load_category_tree_xml(xml_file) + elif xml_file.endswith('domain.xml'): + self.load_domain_xml(xml_file) + else: + self.load_block_xml(xml_file) + except ParseXML.XMLSyntaxError as e: + # print >> sys.stderr, 'Warning: Block validation failed:\n\t%s\n\tIgnoring: %s' % (e, xml_file) + pass + except Exception as e: + print >> sys.stderr, 'Warning: XML parsing failed:\n\t%r\n\tIgnoring: %s' % (e, xml_file) + + # Add blocks to block tree + for key, block in self.blocks.iteritems(): + category = self._block_categories.get(key, block.category) + # Blocks with empty categories are hidden + if not category: + continue + root = category[0] + if root.startswith('[') and root.endswith(']'): + category[0] = root[1:-1] + else: + category.insert(0, Constants.DEFAULT_BLOCK_MODULE_NAME) + block.category = category + + self._docstring_extractor.finish() + # self._docstring_extractor.wait() + + def iter_xml_files(self): + """Iterator for block descriptions and category trees""" + for block_path in self.config.block_paths: + if os.path.isfile(block_path): + yield block_path + elif os.path.isdir(block_path): + for dirpath, dirnames, filenames in os.walk(block_path): + for filename in sorted(filter(lambda f: f.endswith('.xml'), filenames)): + yield os.path.join(dirpath, filename) + + def load_block_xml(self, xml_file): + """Load block description from xml file""" + # Validate and import + ParseXML.validate_dtd(xml_file, self._block_dtd) + n = ParseXML.from_file(xml_file).find('block') + n['block_wrapper_path'] = xml_file # inject block wrapper path + # Get block instance and add it to the list of blocks + block = self.Block(self._flow_graph, n) + key = block.get_key() + if key in self.blocks: + print >> sys.stderr, 'Warning: Block with key "{}" already exists.\n\tIgnoring: {}'.format(key, xml_file) + else: # Store the block + self.blocks[key] = block + self._blocks_n[key] = n + + self._docstring_extractor.query( + block.get_key(), + block.get_imports(raw=True), + block.get_make(raw=True) + ) + + def load_category_tree_xml(self, xml_file): + """Validate and parse category tree file and add it to list""" + ParseXML.validate_dtd(xml_file, Constants.BLOCK_TREE_DTD) + xml = ParseXML.from_file(xml_file) + path = [] + + def load_category(cat_n): + path.append(cat_n.find('name').strip()) + for block_key in cat_n.findall('block'): + if block_key not in self._block_categories: + self._block_categories[block_key] = list(path) + for sub_cat_n in cat_n.findall('cat'): + load_category(sub_cat_n) + path.pop() + + load_category(xml.find('cat')) + + def load_domain_xml(self, xml_file): + """Load a domain properties and connection templates from XML""" + ParseXML.validate_dtd(xml_file, Constants.DOMAIN_DTD) + n = ParseXML.from_file(xml_file).find('domain') + + key = n.find('key') + if not key: + print >> sys.stderr, 'Warning: Domain with emtpy key.\n\tIgnoring: {}'.format(xml_file) + return + if key in self.domains: # test against repeated keys + print >> sys.stderr, 'Warning: Domain with key "{}" already exists.\n\tIgnoring: {}'.format(key, xml_file) + return + + #to_bool = lambda s, d: d if s is None else s.lower() not in ('false', 'off', '0', '') + def to_bool(s, d): + if s is not None: + return s.lower() not in ('false', 'off', '0', '') + return d + + color = n.find('color') or '' + try: + import gtk # ugly but handy + gtk.gdk.color_parse(color) + except (ValueError, ImportError): + if color: # no color is okay, default set in GUI + print >> sys.stderr, 'Warning: Can\'t parse color code "{}" for domain "{}" '.format(color, key) + color = None + + self.domains[key] = dict( + name=n.find('name') or key, + multiple_sinks=to_bool(n.find('multiple_sinks'), True), + multiple_sources=to_bool(n.find('multiple_sources'), False), + color=color + ) + for connection_n in n.findall('connection'): + key = (connection_n.find('source_domain'), connection_n.find('sink_domain')) + if not all(key): + print >> sys.stderr, 'Warning: Empty domain key(s) in connection template.\n\t{}'.format(xml_file) + elif key in self.connection_templates: + print >> sys.stderr, 'Warning: Connection template "{}" already exists.\n\t{}'.format(key, xml_file) + else: + self.connection_templates[key] = connection_n.find('make') or '' + + def _save_docstring_extraction_result(self, key, docstrings): + docs = {} + for match, docstring in docstrings.iteritems(): + if not docstring or match.endswith('_sptr'): + continue + docstring = docstring.replace('\n\n', '\n').strip() + docs[match] = docstring + self.block_docstrings[key] = docs + + ############################################## + # Access + ############################################## + + def parse_flow_graph(self, flow_graph_file): + """ + Parse a saved flow graph file. + Ensure that the file exists, and passes the dtd check. + + Args: + flow_graph_file: the flow graph file + + Returns: + nested data + @throws exception if the validation fails + """ + flow_graph_file = flow_graph_file or self._default_flow_graph + open(flow_graph_file, 'r') # Test open + ParseXML.validate_dtd(flow_graph_file, Constants.FLOW_GRAPH_DTD) + return ParseXML.from_file(flow_graph_file) + + def get_new_flow_graph(self): + return self.FlowGraph(platform=self) + + def get_blocks(self): + return self.blocks.values() + + def get_new_block(self, flow_graph, key): + return self.Block(flow_graph, n=self._blocks_n[key]) + + def get_colors(self): + return [(name, color) for name, key, sizeof, color in Constants.CORE_TYPES] diff --git a/grc/core/Port.py b/grc/core/Port.py new file mode 100644 index 0000000000..6a8f484082 --- /dev/null +++ b/grc/core/Port.py @@ -0,0 +1,404 @@ +""" +Copyright 2008-2015 Free Software Foundation, Inc. +This file is part of GNU Radio + +GNU Radio Companion 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 2 +of the License, or (at your option) any later version. + +GNU Radio Companion 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 +""" + +from .Constants import DEFAULT_DOMAIN, GR_STREAM_DOMAIN, GR_MESSAGE_DOMAIN +from .Element import Element + +from . import Constants + + +def _get_source_from_virtual_sink_port(vsp): + """ + Resolve the source port that is connected to the given virtual sink port. + Use the get source from virtual source to recursively resolve subsequent ports. + """ + try: + return _get_source_from_virtual_source_port( + vsp.get_enabled_connections()[0].get_source()) + except: + raise Exception('Could not resolve source for virtual sink port {}'.format(vsp)) + + +def _get_source_from_virtual_source_port(vsp, traversed=[]): + """ + Recursively resolve source ports over the virtual connections. + Keep track of traversed sources to avoid recursive loops. + """ + if not vsp.get_parent().is_virtual_source(): + return vsp + if vsp in traversed: + raise Exception('Loop found when resolving virtual source {}'.format(vsp)) + try: + return _get_source_from_virtual_source_port( + _get_source_from_virtual_sink_port( + filter( # Get all virtual sinks with a matching stream id + lambda vs: vs.get_param('stream_id').get_value() == vsp.get_parent().get_param('stream_id').get_value(), + filter( # Get all enabled blocks that are also virtual sinks + lambda b: b.is_virtual_sink(), + vsp.get_parent().get_parent().get_enabled_blocks(), + ), + )[0].get_sinks()[0] + ), traversed + [vsp], + ) + except: + raise Exception('Could not resolve source for virtual source port {}'.format(vsp)) + + +def _get_sink_from_virtual_source_port(vsp): + """ + Resolve the sink port that is connected to the given virtual source port. + Use the get sink from virtual sink to recursively resolve subsequent ports. + """ + try: + # Could have many connections, but use first + return _get_sink_from_virtual_sink_port( + vsp.get_enabled_connections()[0].get_sink()) + except: + raise Exception('Could not resolve source for virtual source port {}'.format(vsp)) + + +def _get_sink_from_virtual_sink_port(vsp, traversed=[]): + """ + Recursively resolve sink ports over the virtual connections. + Keep track of traversed sinks to avoid recursive loops. + """ + if not vsp.get_parent().is_virtual_sink(): + return vsp + if vsp in traversed: + raise Exception('Loop found when resolving virtual sink {}'.format(vsp)) + try: + return _get_sink_from_virtual_sink_port( + _get_sink_from_virtual_source_port( + filter( # Get all virtual source with a matching stream id + lambda vs: vs.get_param('stream_id').get_value() == vsp.get_parent().get_param('stream_id').get_value(), + filter( # Get all enabled blocks that are also virtual sinks + lambda b: b.is_virtual_source(), + vsp.get_parent().get_parent().get_enabled_blocks(), + ), + )[0].get_sources()[0] + ), traversed + [vsp], + ) + except: + raise Exception('Could not resolve source for virtual sink port {}'.format(vsp)) + + +class Port(Element): + + is_port = True + + def __init__(self, block, n, dir): + """ + Make a new port from nested data. + + Args: + block: the parent element + n: the nested odict + dir: the direction + """ + self._n = n + if n['type'] == 'message': + n['domain'] = GR_MESSAGE_DOMAIN + if 'domain' not in n: + n['domain'] = DEFAULT_DOMAIN + elif n['domain'] == GR_MESSAGE_DOMAIN: + n['key'] = n['name'] + n['type'] = 'message' # For port color + if n['type'] == 'msg': + n['key'] = 'msg' + if not n.find('key'): + n['key'] = str(next(block.port_counters[dir == 'source'])) + + # Build the port + Element.__init__(self, block) + # Grab the data + self._name = n['name'] + self._key = n['key'] + self._type = n['type'] or '' + self._domain = n['domain'] + self._hide = n.find('hide') or '' + self._dir = dir + self._hide_evaluated = False # Updated on rewrite() + + self._nports = n.find('nports') or '' + self._vlen = n.find('vlen') or '' + self._optional = bool(n.find('optional')) + self._clones = [] # References to cloned ports (for nports > 1) + + def __str__(self): + if self.is_source: + return 'Source - {}({})'.format(self.get_name(), self.get_key()) + if self.is_sink: + return 'Sink - {}({})'.format(self.get_name(), self.get_key()) + + def get_types(self): + return Constants.TYPE_TO_SIZEOF.keys() + + def is_type_empty(self): + return not self._n['type'] + + def validate(self): + Element.validate(self) + if self.get_type() not in self.get_types(): + self.add_error_message('Type "{}" is not a possible type.'.format(self.get_type())) + platform = self.get_parent().get_parent().get_parent() + if self.get_domain() not in platform.domains: + self.add_error_message('Domain key "{}" is not registered.'.format(self.get_domain())) + if not self.get_enabled_connections() and not self.get_optional(): + self.add_error_message('Port is not connected.') + # Message port logic + if self.get_type() == 'msg': + if self.get_nports(): + self.add_error_message('A port of type "msg" cannot have "nports" set.') + if self.get_vlen() != 1: + self.add_error_message('A port of type "msg" must have a "vlen" of 1.') + + def rewrite(self): + """ + Handle the port cloning for virtual blocks. + """ + if self.is_type_empty(): + try: + # Clone type and vlen + source = self.resolve_empty_type() + self._type = str(source.get_type()) + self._vlen = str(source.get_vlen()) + except: + # Reset type and vlen + self._type = '' + self._vlen = '' + + Element.rewrite(self) + hide = self.get_parent().resolve_dependencies(self._hide).strip().lower() + self._hide_evaluated = False if hide in ('false', 'off', '0') else bool(hide) + + # Update domain if was deduced from (dynamic) port type + type_ = self.get_type() + if self._domain == GR_STREAM_DOMAIN and type_ == "message": + self._domain = GR_MESSAGE_DOMAIN + self._key = self._name + if self._domain == GR_MESSAGE_DOMAIN and type_ != "message": + self._domain = GR_STREAM_DOMAIN + self._key = '0' # Is rectified in rewrite() + + def resolve_virtual_source(self): + if self.get_parent().is_virtual_sink(): + return _get_source_from_virtual_sink_port(self) + if self.get_parent().is_virtual_source(): + return _get_source_from_virtual_source_port(self) + + def resolve_empty_type(self): + if self.is_sink: + try: + src = _get_source_from_virtual_sink_port(self) + if not src.is_type_empty(): + return src + except: + pass + sink = _get_sink_from_virtual_sink_port(self) + if not sink.is_type_empty(): + return sink + if self.is_source: + try: + src = _get_source_from_virtual_source_port(self) + if not src.is_type_empty(): + return src + except: + pass + sink = _get_sink_from_virtual_source_port(self) + if not sink.is_type_empty(): + return sink + + def get_vlen(self): + """ + Get the vector length. + If the evaluation of vlen cannot be cast to an integer, return 1. + + Returns: + the vector length or 1 + """ + vlen = self.get_parent().resolve_dependencies(self._vlen) + try: + return int(self.get_parent().get_parent().evaluate(vlen)) + except: + return 1 + + def get_nports(self): + """ + Get the number of ports. + If already blank, return a blank + If the evaluation of nports cannot be cast to a positive integer, return 1. + + Returns: + the number of ports or 1 + """ + if self._nports == '': + return '' + + nports = self.get_parent().resolve_dependencies(self._nports) + try: + return max(1, int(self.get_parent().get_parent().evaluate(nports))) + except: + return 1 + + def get_optional(self): + return bool(self._optional) + + def get_color(self): + """ + Get the color that represents this port's type. + Codes differ for ports where the vec length is 1 or greater than 1. + + Returns: + a hex color code. + """ + try: + color = Constants.TYPE_TO_COLOR[self.get_type()] + vlen = self.get_vlen() + if vlen == 1: + return color + color_val = int(color[1:], 16) + r = (color_val >> 16) & 0xff + g = (color_val >> 8) & 0xff + b = (color_val >> 0) & 0xff + dark = (0, 0, 30, 50, 70)[min(4, vlen)] + r = max(r-dark, 0) + g = max(g-dark, 0) + b = max(b-dark, 0) + # TODO: Change this to .format() + return '#%.2x%.2x%.2x' % (r, g, b) + except: + return '#FFFFFF' + + def get_clones(self): + """ + Get the clones of this master port (nports > 1) + + Returns: + a list of ports + """ + return self._clones + + def add_clone(self): + """ + Create a clone of this (master) port and store a reference in self._clones. + + The new port name (and key for message ports) will have index 1... appended. + If this is the first clone, this (master) port will get a 0 appended to its name (and key) + + Returns: + the cloned port + """ + # Add index to master port name if there are no clones yet + if not self._clones: + self._name = self._n['name'] + '0' + # Also update key for none stream ports + if not self._key.isdigit(): + self._key = self._name + + # Prepare a copy of the odict for the clone + n = self._n.copy() + # Remove nports from the key so the copy cannot be a duplicator + if 'nports' in n: + n.pop('nports') + n['name'] = self._n['name'] + str(len(self._clones) + 1) + # Dummy value 99999 will be fixed later + n['key'] = '99999' if self._key.isdigit() else n['name'] + + # Clone + port = self.__class__(self.get_parent(), n, self._dir) + self._clones.append(port) + return port + + def remove_clone(self, port): + """ + Remove a cloned port (from the list of clones only) + Remove the index 0 of the master port name (and key9 if there are no more clones left + """ + self._clones.remove(port) + # Remove index from master port name if there are no more clones + if not self._clones: + self._name = self._n['name'] + # Also update key for none stream ports + if not self._key.isdigit(): + self._key = self._name + + def get_name(self): + number = '' + if self.get_type() == 'bus': + busses = filter(lambda a: a._dir == self._dir, self.get_parent().get_ports_gui()) + number = str(busses.index(self)) + '#' + str(len(self.get_associated_ports())) + return self._name + number + + def get_key(self): + return self._key + + @property + def is_sink(self): + return self._dir == 'sink' + + @property + def is_source(self): + return self._dir == 'source' + + def get_type(self): + return self.get_parent().resolve_dependencies(self._type) + + def get_domain(self): + return self._domain + + def get_hide(self): + return self._hide_evaluated + + def get_connections(self): + """ + Get all connections that use this port. + + Returns: + a list of connection objects + """ + connections = self.get_parent().get_parent().connections + connections = filter(lambda c: c.get_source() is self or c.get_sink() is self, connections) + return connections + + def get_enabled_connections(self): + """ + Get all enabled connections that use this port. + + Returns: + a list of connection objects + """ + return filter(lambda c: c.get_enabled(), self.get_connections()) + + def get_associated_ports(self): + if not self.get_type() == 'bus': + return [self] + else: + if self.is_source: + get_ports = self.get_parent().get_sources + bus_structure = self.get_parent().current_bus_structure['source'] + else: + get_ports = self.get_parent().get_sinks + bus_structure = self.get_parent().current_bus_structure['sink'] + + ports = [i for i in get_ports() if not i.get_type() == 'bus'] + if bus_structure: + busses = [i for i in get_ports() if i.get_type() == 'bus'] + bus_index = busses.index(self) + ports = filter(lambda a: ports.index(a) in bus_structure[bus_index], ports) + return ports diff --git a/grc/python/__init__.py b/grc/core/__init__.py index 8b13789179..8b13789179 100644 --- a/grc/python/__init__.py +++ b/grc/core/__init__.py diff --git a/grc/python/block.dtd b/grc/core/block.dtd index 145f4d8610..145f4d8610 100644 --- a/grc/python/block.dtd +++ b/grc/core/block.dtd diff --git a/grc/base/block_tree.dtd b/grc/core/block_tree.dtd index 9e23576477..9e23576477 100644 --- a/grc/base/block_tree.dtd +++ b/grc/core/block_tree.dtd diff --git a/grc/python/default_flow_graph.grc b/grc/core/default_flow_graph.grc index 059509d34b..059509d34b 100644 --- a/grc/python/default_flow_graph.grc +++ b/grc/core/default_flow_graph.grc diff --git a/grc/base/domain.dtd b/grc/core/domain.dtd index b5b0b8bf39..b5b0b8bf39 100644 --- a/grc/base/domain.dtd +++ b/grc/core/domain.dtd diff --git a/grc/base/flow_graph.dtd b/grc/core/flow_graph.dtd index bdfe1dc059..bdfe1dc059 100644 --- a/grc/base/flow_graph.dtd +++ b/grc/core/flow_graph.dtd diff --git a/grc/python/CMakeLists.txt b/grc/core/generator/CMakeLists.txt index 3f9e273146..4bdd59a7a2 100644 --- a/grc/python/CMakeLists.txt +++ b/grc/core/generator/CMakeLists.txt @@ -17,28 +17,16 @@ # the Free Software Foundation, Inc., 51 Franklin Street, # Boston, MA 02110-1301, USA. -######################################################################## -GR_PYTHON_INSTALL(FILES - expr_utils.py - extract_docs.py - epy_block_io.py - Block.py - Connection.py - Constants.py - FlowGraph.py - Generator.py - Param.py - Platform.py - Port.py - __init__.py - DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc/python +file(GLOB py_files "*.py") + +GR_PYTHON_INSTALL( + FILES ${py_files} + DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc/core/generator COMPONENT "grc" ) install(FILES - block.dtd - default_flow_graph.grc flow_graph.tmpl - DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc/python + DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc/core/generator COMPONENT "grc" ) diff --git a/grc/core/generator/FlowGraphProxy.py b/grc/core/generator/FlowGraphProxy.py new file mode 100644 index 0000000000..3723005576 --- /dev/null +++ b/grc/core/generator/FlowGraphProxy.py @@ -0,0 +1,126 @@ +# Copyright 2016 Free Software Foundation, Inc. +# This file is part of GNU Radio +# +# GNU Radio Companion 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 2 +# of the License, or (at your option) any later version. +# +# GNU Radio Companion 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 + + +class FlowGraphProxy(object): + + def __init__(self, fg): + self._fg = fg + + def __getattr__(self, item): + return getattr(self._fg, item) + + def get_hier_block_stream_io(self, direction): + """ + Get a list of stream io signatures for this flow graph. + + Args: + direction: a string of 'in' or 'out' + + Returns: + a list of dicts with: type, label, vlen, size, optional + """ + return filter(lambda p: p['type'] != "message", self.get_hier_block_io(direction)) + + def get_hier_block_message_io(self, direction): + """ + Get a list of message io signatures for this flow graph. + + Args: + direction: a string of 'in' or 'out' + + Returns: + a list of dicts with: type, label, vlen, size, optional + """ + return filter(lambda p: p['type'] == "message", self.get_hier_block_io(direction)) + + def get_hier_block_io(self, direction): + """ + Get a list of io ports for this flow graph. + + Args: + direction: a string of 'in' or 'out' + + Returns: + a list of dicts with: type, label, vlen, size, optional + """ + pads = self.get_pad_sources() if direction in ('sink', 'in') else \ + self.get_pad_sinks() if direction in ('source', 'out') else [] + ports = [] + for pad in pads: + master = { + 'label': str(pad.get_param('label').get_evaluated()), + 'type': str(pad.get_param('type').get_evaluated()), + 'vlen': str(pad.get_param('vlen').get_value()), + 'size': pad.get_param('type').get_opt('size'), + 'optional': bool(pad.get_param('optional').get_evaluated()), + } + num_ports = pad.get_param('num_streams').get_evaluated() + if num_ports > 1: + for i in xrange(num_ports): + clone = master.copy() + clone['label'] += str(i) + ports.append(clone) + else: + ports.append(master) + return ports + + def get_pad_sources(self): + """ + Get a list of pad source blocks sorted by id order. + + Returns: + a list of pad source blocks in this flow graph + """ + pads = filter(lambda b: b.get_key() == 'pad_source', self.get_enabled_blocks()) + return sorted(pads, lambda x, y: cmp(x.get_id(), y.get_id())) + + def get_pad_sinks(self): + """ + Get a list of pad sink blocks sorted by id order. + + Returns: + a list of pad sink blocks in this flow graph + """ + pads = filter(lambda b: b.get_key() == 'pad_sink', self.get_enabled_blocks()) + return sorted(pads, lambda x, y: cmp(x.get_id(), y.get_id())) + + def get_pad_port_global_key(self, port): + """ + Get the key for a port of a pad source/sink to use in connect() + This takes into account that pad blocks may have multiple ports + + Returns: + the key (str) + """ + key_offset = 0 + pads = self.get_pad_sources() if port.is_source else self.get_pad_sinks() + for pad in pads: + # using the block param 'type' instead of the port domain here + # to emphasize that hier block generation is domain agnostic + is_message_pad = pad.get_param('type').get_evaluated() == "message" + if port.get_parent() == pad: + if is_message_pad: + key = pad.get_param('label').get_value() + else: + key = str(key_offset + int(port.get_key())) + return key + else: + # assuming we have either only sources or sinks + if not is_message_pad: + key_offset += len(pad.get_ports()) + return -1
\ No newline at end of file diff --git a/grc/python/Generator.py b/grc/core/generator/Generator.py index 56e3a6e78f..fb7a3afb99 100644 --- a/grc/python/Generator.py +++ b/grc/core/generator/Generator.py @@ -1,41 +1,37 @@ -""" -Copyright 2008-2011 Free Software Foundation, Inc. -This file is part of GNU Radio +# Copyright 2008-2016 Free Software Foundation, Inc. +# This file is part of GNU Radio +# +# GNU Radio Companion 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 2 +# of the License, or (at your option) any later version. +# +# GNU Radio Companion 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 -GNU Radio Companion 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 2 -of the License, or (at your option) any later version. - -GNU Radio Companion 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 codecs import os -import sys -import subprocess import tempfile -import shlex -import codecs -import re # for shlex_quote -from distutils.spawn import find_executable from Cheetah.Template import Template -from .. gui import Messages -from .. base import ParseXML -from .. base import odict -from .. base.Constants import BLOCK_FLAG_NEED_QT_GUI +from .FlowGraphProxy import FlowGraphProxy +from .. import ParseXML, Messages +from ..Constants import ( + TOP_BLOCK_FILE_MODE, BLOCK_FLAG_NEED_QT_GUI, + HIER_BLOCK_FILE_MODE, BLOCK_DTD +) +from ..utils import expr_utils, odict -from . Constants import TOP_BLOCK_FILE_MODE, FLOW_GRAPH_TEMPLATE, \ - XTERM_EXECUTABLE, HIER_BLOCK_FILE_MODE, HIER_BLOCKS_LIB_DIR, BLOCK_DTD -from . import expr_utils +DATA_DIR = os.path.dirname(__file__) +FLOW_GRAPH_TEMPLATE = os.path.join(DATA_DIR, 'flow_graph.tmpl') class Generator(object): @@ -50,19 +46,16 @@ class Generator(object): flow_graph: the flow graph object file_path: the path to the grc file """ - self._generate_options = flow_graph.get_option('generate_options') - if self._generate_options == 'hb': + self.generate_options = flow_graph.get_option('generate_options') + if self.generate_options == 'hb': generator_cls = HierBlockGenerator - elif self._generate_options == 'hb_qt_gui': + elif self.generate_options == 'hb_qt_gui': generator_cls = QtHierBlockGenerator else: generator_cls = TopBlockGenerator self._generator = generator_cls(flow_graph, file_path) - def get_generate_options(self): - return self._generate_options - def __getattr__(self, item): """get all other attrib from actual generator object""" return getattr(self._generator, item) @@ -78,23 +71,23 @@ class TopBlockGenerator(object): flow_graph: the flow graph object file_path: the path to write the file to """ - self._flow_graph = flow_graph + self._flow_graph = FlowGraphProxy(flow_graph) self._generate_options = self._flow_graph.get_option('generate_options') self._mode = TOP_BLOCK_FILE_MODE dirname = self._dirname = os.path.dirname(file_path) - # handle the case where the directory is read-only - # in this case, use the system's temp directory + # Handle the case where the directory is read-only + # In this case, use the system's temp directory if not os.access(dirname, os.W_OK): dirname = tempfile.gettempdir() filename = self._flow_graph.get_option('id') + '.py' - self._file_path = os.path.join(dirname, filename) + self.file_path = os.path.join(dirname, filename) def get_file_path(self): - return self._file_path + return self.file_path def write(self): """generate output and write it to files""" - # do throttle warning + # Do throttle warning throttling_blocks = filter(lambda b: b.throtteling(), self._flow_graph.get_enabled_blocks()) if not throttling_blocks and not self._generate_options.startswith('hb'): Messages.send_warning("This flow graph may not have flow control: " @@ -109,48 +102,16 @@ class TopBlockGenerator(object): "e.g. a hardware source or sink. " "This is usually undesired. Consider " "removing the throttle block.") - # generate + # Generate for filename, data in self._build_python_code_from_template(): with codecs.open(filename, 'w', encoding='utf-8') as fp: fp.write(data) - if filename == self.get_file_path(): + if filename == self.file_path: try: os.chmod(filename, self._mode) except: pass - def get_popen(self): - """ - Execute this python flow graph. - - Returns: - a popen object - """ - run_command = self._flow_graph.get_option('run_command') - try: - run_command = run_command.format( - python=shlex_quote(sys.executable), - filename=shlex_quote(self.get_file_path())) - run_command_args = shlex.split(run_command) - except Exception as e: - raise ValueError("Can't parse run command {!r}: {}".format(run_command, e)) - - # when in no gui mode on linux, use a graphical terminal (looks nice) - xterm_executable = find_executable(XTERM_EXECUTABLE) - if self._generate_options == 'no_gui' and xterm_executable: - run_command_args = [xterm_executable, '-e', run_command] - - # this does not reproduce a shell executable command string, if a graphical - # terminal is used. Passing run_command though shlex_quote would do it but - # it looks really ugly and confusing in the console panel. - Messages.send_start_exec(' '.join(run_command_args)) - - return subprocess.Popen( - args=run_command_args, - stdout=subprocess.PIPE, stderr=subprocess.STDOUT, - shell=False, universal_newlines=True - ) - def _build_python_code_from_template(self): """ Convert the flow graph to python code. @@ -167,25 +128,29 @@ class TopBlockGenerator(object): parameters = fg.get_parameters() monitors = fg.get_monitors() - # list of blocks not including variables and imports and parameters and disabled + # List of blocks not including variables and imports and parameters and disabled def _get_block_sort_text(block): code = block.get_make().replace(block.get_id(), ' ') try: - code += block.get_param('notebook').get_value() # older gui markup w/ wxgui + code += block.get_param('notebook').get_value() # Older gui markup w/ wxgui except: pass try: - code += block.get_param('gui_hint').get_value() # newer gui markup w/ qtgui + code += block.get_param('gui_hint').get_value() # Newer gui markup w/ qtgui except: pass return code - blocks = expr_utils.sort_objects( - filter(lambda b: b.get_enabled() and not b.get_bypassed(), fg.iter_blocks()), + blocks_all = expr_utils.sort_objects( + filter(lambda b: b.get_enabled() and not b.get_bypassed(), fg.blocks), lambda b: b.get_id(), _get_block_sort_text ) + deprecated_block_keys = set(block.get_name() for block in blocks_all if block.is_deprecated) + for key in deprecated_block_keys: + Messages.send_warning("The block {!r} is deprecated.".format(key)) + # List of regular blocks (all blocks minus the special ones) - blocks = filter(lambda b: b not in (imports + parameters), blocks) + blocks = filter(lambda b: b not in (imports + parameters), blocks_all) for block in blocks: key = block.get_key() @@ -198,7 +163,8 @@ class TopBlockGenerator(object): output.append((file_path, src)) # Filter out virtual sink connections - cf = lambda c: not (c.is_bus() or c.is_msg() or c.get_sink().get_parent().is_virtual_sink()) + def cf(c): + return not (c.is_bus() or c.is_msg() or c.get_sink().get_parent().is_virtual_sink()) connections = filter(cf, fg.get_enabled_connections()) # Get the virtual blocks and resolve their connections @@ -220,8 +186,7 @@ class TopBlockGenerator(object): for block in bypassed_blocks: # Get the upstream connection (off of the sink ports) # Use *connections* not get_connections() - get_source_connection = lambda c: c.get_sink() == block.get_sinks()[0] - source_connection = filter(get_source_connection, connections) + source_connection = filter(lambda c: c.get_sink() == block.get_sinks()[0], connections) # The source connection should never have more than one element. assert (len(source_connection) == 1) @@ -229,8 +194,7 @@ class TopBlockGenerator(object): source_port = source_connection[0].get_source() # Loop through all the downstream connections - get_sink_connections = lambda c: c.get_source() == block.get_sources()[0] - for sink in filter(get_sink_connections, connections): + for sink in filter(lambda c: c.get_source() == block.get_sources()[0], connections): if not sink.get_enabled(): # Ignore disabled connections continue @@ -248,23 +212,26 @@ class TopBlockGenerator(object): c.get_source().get_parent().get_id(), c.get_sink().get_parent().get_id() )) - connection_templates = fg.get_parent().get_connection_templates() + connection_templates = fg.get_parent().connection_templates msgs = filter(lambda c: c.is_msg(), fg.get_enabled_connections()) - # list of variable names + + # List of variable names var_ids = [var.get_id() for var in parameters + variables] - # prepend self. - replace_dict = dict([(var_id, 'self.%s' % var_id) for var_id in var_ids]) - # list of callbacks - callbacks = [ - expr_utils.expr_replace(cb, replace_dict) - for cb in sum([block.get_callbacks() for block in fg.get_enabled_blocks()], []) - ] - # map var id to callbacks - var_id2cbs = dict([ - (var_id, filter(lambda c: expr_utils.get_variable_dependencies(c, [var_id]), callbacks)) - for var_id in var_ids - ]) - # load the namespace + replace_dict = dict((var_id, 'self.' + var_id) for var_id in var_ids) + callbacks_all = [] + for block in blocks_all: + callbacks_all.extend(expr_utils.expr_replace(cb, replace_dict) for cb in block.get_callbacks()) + + # Map var id to callbacks + def uses_var_id(): + used = expr_utils.get_variable_dependencies(callback, [var_id]) + return used and 'self.' + var_id in callback # callback might contain var_id itself + + callbacks = {} + for var_id in var_ids: + callbacks[var_id] = [callback for callback in callbacks_all if uses_var_id()] + + # Load the namespace namespace = { 'title': title, 'imports': imports, @@ -277,11 +244,11 @@ class TopBlockGenerator(object): 'connection_templates': connection_templates, 'msgs': msgs, 'generate_options': self._generate_options, - 'var_id2cbs': var_id2cbs, + 'callbacks': callbacks, } - # build the template + # Build the template t = Template(open(FLOW_GRAPH_TEMPLATE, 'r').read(), namespace) - output.append((self.get_file_path(), str(t))) + output.append((self.file_path, str(t))) return output @@ -297,10 +264,15 @@ class HierBlockGenerator(TopBlockGenerator): file_path: where to write the py file (the xml goes into HIER_BLOCK_LIB_DIR) """ TopBlockGenerator.__init__(self, flow_graph, file_path) + platform = flow_graph.get_parent() + + hier_block_lib_dir = platform.config.hier_block_lib_dir + if not os.path.exists(hier_block_lib_dir): + os.mkdir(hier_block_lib_dir) + self._mode = HIER_BLOCK_FILE_MODE - self._file_path = os.path.join(HIER_BLOCKS_LIB_DIR, - self._flow_graph.get_option('id') + '.py') - self._file_path_xml = self._file_path + '.xml' + self.file_path = os.path.join(hier_block_lib_dir, self._flow_graph.get_option('id') + '.py') + self._file_path_xml = self.file_path + '.xml' def get_file_path_xml(self): return self._file_path_xml @@ -322,7 +294,7 @@ class HierBlockGenerator(TopBlockGenerator): Returns: a xml node tree """ - # extract info from the flow graph + # Extract info from the flow graph block_key = self._flow_graph.get_option('id') parameters = self._flow_graph.get_parameters() @@ -331,7 +303,7 @@ class HierBlockGenerator(TopBlockGenerator): return "$"+name return name - # build the nested data + # Build the nested data block_n = odict() block_n['name'] = self._flow_graph.get_option('title') or \ self._flow_graph.get_option('id').replace('_', ' ').title() @@ -339,7 +311,7 @@ class HierBlockGenerator(TopBlockGenerator): block_n['category'] = self._flow_graph.get_option('category') block_n['import'] = "from {0} import {0} # grc-generated hier_block".format( self._flow_graph.get_option('id')) - # make data + # Make data if parameters: block_n['make'] = '{cls}(\n {kwargs},\n)'.format( cls=block_key, @@ -349,7 +321,7 @@ class HierBlockGenerator(TopBlockGenerator): ) else: block_n['make'] = '{cls}()'.format(cls=block_key) - # callback data + # Callback data block_n['callback'] = [ 'set_{key}(${key})'.format(key=param.get_id()) for param in parameters ] @@ -364,13 +336,13 @@ class HierBlockGenerator(TopBlockGenerator): param_n['type'] = 'raw' block_n['param'].append(param_n) - # bus stuff + # Bus stuff if self._flow_graph.get_bussink(): block_n['bus_sink'] = '1' if self._flow_graph.get_bussrc(): block_n['bus_source'] = '1' - # sink/source ports + # Sink/source ports for direction in ('sink', 'source'): block_n[direction] = list() for port in self._flow_graph.get_hier_block_io(direction): @@ -383,7 +355,7 @@ class HierBlockGenerator(TopBlockGenerator): port_n['optional'] = '1' block_n[direction].append(port_n) - # more bus stuff + # More bus stuff bus_struct_sink = self._flow_graph.get_bus_structure_sink() if bus_struct_sink: block_n['bus_structure_sink'] = bus_struct_sink[0].get_param('struct').get_value() @@ -391,11 +363,11 @@ class HierBlockGenerator(TopBlockGenerator): if bus_struct_src: block_n['bus_structure_source'] = bus_struct_src[0].get_param('struct').get_value() - # documentation + # Documentation block_n['doc'] = "\n".join(field for field in ( self._flow_graph.get_option('author'), self._flow_graph.get_option('description'), - self.get_file_path() + self.file_path ) if field) block_n['grc_source'] = str(self._flow_graph.grc_file_path) @@ -427,21 +399,3 @@ class QtHierBlockGenerator(HierBlockGenerator): "\n${gui_hint()($win)}" ) return n - - -########################################################### -# back-port from python3 -########################################################### -_find_unsafe = re.compile(r'[^\w@%+=:,./-]').search - - -def shlex_quote(s): - """Return a shell-escaped version of the string *s*.""" - if not s: - return "''" - if _find_unsafe(s) is None: - return s - - # use single quotes, and put single quotes into double quotes - # the string $'b is then quoted as '$'"'"'b' - return "'" + s.replace("'", "'\"'\"'") + "'" diff --git a/grc/core/generator/__init__.py b/grc/core/generator/__init__.py new file mode 100644 index 0000000000..f44b94a85d --- /dev/null +++ b/grc/core/generator/__init__.py @@ -0,0 +1,18 @@ +# Copyright 2008-2015 Free Software Foundation, Inc. +# This file is part of GNU Radio +# +# GNU Radio Companion 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 2 +# of the License, or (at your option) any later version. +# +# GNU Radio Companion 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 + +from Generator import Generator diff --git a/grc/python/flow_graph.tmpl b/grc/core/generator/flow_graph.tmpl index bd8025b676..07c4169525 100644 --- a/grc/python/flow_graph.tmpl +++ b/grc/core/generator/flow_graph.tmpl @@ -13,7 +13,7 @@ ##@param connections the connections ##@param msgs the msg type connections ##@param generate_options the type of flow graph -##@param var_id2cbs variable id map to callback strings +##@param callbacks variable id map to callback strings ######################################################## #def indent($code) #set $code = '\n '.join(str($code).splitlines()) @@ -274,8 +274,8 @@ gr.io_signaturev($(len($io_sigs)), $(len($io_sigs)), [$(', '.join($size_strs))]) self.settings = Qt.QSettings("GNU Radio", "$class_name") self.settings.setValue("geometry", self.saveGeometry()) event.accept() - #if $flow_graph.get_option('qt_qss_theme') + def setStyleSheetFromFile(self, filename): try: if not os.path.exists(filename): @@ -301,12 +301,12 @@ gr.io_signaturev($(len($io_sigs)), $(len($io_sigs)), [$(', '.join($size_strs))]) #if $flow_graph.get_option('thread_safe_setters') with self._lock: self.$id = $id - #for $callback in $var_id2cbs[$id] + #for $callback in $callbacks[$id] $indent($callback) #end for #else self.$id = $id - #for $callback in $var_id2cbs[$id] + #for $callback in $callbacks[$id] $indent($callback) #end for #end if @@ -336,7 +336,12 @@ $short_id#slurp def argument_parser(): - parser = OptionParser(option_class=eng_option, usage="%prog: [options]") + #set $desc_args = 'usage="%prog: [options]", option_class=eng_option' + #if $flow_graph.get_option('description') + #set $desc_args += ', description=description' + description = $repr($flow_graph.get_option('description')) + #end if + parser = OptionParser($desc_args) #for $param in $parameters #set $type = $param.get_param('type').get_value() #if $type diff --git a/grc/core/utils/CMakeLists.txt b/grc/core/utils/CMakeLists.txt new file mode 100644 index 0000000000..2528fbc43c --- /dev/null +++ b/grc/core/utils/CMakeLists.txt @@ -0,0 +1,26 @@ +# 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. + +file(GLOB py_files "*.py") + +GR_PYTHON_INSTALL( + FILES ${py_files} + DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc/core/utils + COMPONENT "grc" +) diff --git a/grc/core/utils/__init__.py b/grc/core/utils/__init__.py new file mode 100644 index 0000000000..6b23da2723 --- /dev/null +++ b/grc/core/utils/__init__.py @@ -0,0 +1,22 @@ +# Copyright 2008-2015 Free Software Foundation, Inc. +# This file is part of GNU Radio +# +# GNU Radio Companion 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 2 +# of the License, or (at your option) any later version. +# +# GNU Radio Companion 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 expr_utils +import epy_block_io +import extract_docs + +from odict import odict diff --git a/grc/core/utils/complexity.py b/grc/core/utils/complexity.py new file mode 100644 index 0000000000..baa8040db4 --- /dev/null +++ b/grc/core/utils/complexity.py @@ -0,0 +1,49 @@ + +def calculate_flowgraph_complexity(flowgraph): + """ Determines the complexity of a flowgraph """ + dbal = 0 + for block in flowgraph.blocks: + # Skip options block + if block.get_key() == 'options': + continue + + # Don't worry about optional sinks? + sink_list = filter(lambda c: not c.get_optional(), block.get_sinks()) + source_list = filter(lambda c: not c.get_optional(), block.get_sources()) + sinks = float(len(sink_list)) + sources = float(len(source_list)) + base = max(min(sinks, sources), 1) + + # Port ratio multiplier + if min(sinks, sources) > 0: + multi = sinks / sources + multi = (1 / multi) if multi > 1 else multi + else: + multi = 1 + + # Connection ratio multiplier + sink_multi = max(float(sum(map(lambda c: len(c.get_connections()), sink_list)) / max(sinks, 1.0)), 1.0) + source_multi = max(float(sum(map(lambda c: len(c.get_connections()), source_list)) / max(sources, 1.0)), 1.0) + dbal = dbal + (base * multi * sink_multi * source_multi) + + blocks = float(len(flowgraph.blocks)) + connections = float(len(flowgraph.connections)) + elements = blocks + connections + disabled_connections = len(filter(lambda c: not c.get_enabled(), flowgraph.connections)) + variables = elements - blocks - connections + enabled = float(len(flowgraph.get_enabled_blocks())) + + # Disabled multiplier + if enabled > 0: + disabled_multi = 1 / (max(1 - ((blocks - enabled) / max(blocks, 1)), 0.05)) + else: + disabled_multi = 1 + + # Connection multiplier (How many connections ) + if (connections - disabled_connections) > 0: + conn_multi = 1 / (max(1 - (disabled_connections / max(connections, 1)), 0.05)) + else: + conn_multi = 1 + + final = round(max((dbal - 1) * disabled_multi * conn_multi * connections, 0.0) / 1000000, 6) + return final diff --git a/grc/python/epy_block_io.py b/grc/core/utils/epy_block_io.py index e089908a01..76b50051db 100644 --- a/grc/python/epy_block_io.py +++ b/grc/core/utils/epy_block_io.py @@ -2,9 +2,6 @@ import inspect import collections -from gnuradio import gr -import pmt - TYPE_MAP = { 'complex64': 'complex', 'complex': 'complex', @@ -14,7 +11,7 @@ TYPE_MAP = { 'int8': 'byte', 'uint8': 'byte', } -BlockIO = collections.namedtuple('BlockIO', 'name cls params sinks sources doc') +BlockIO = collections.namedtuple('BlockIO', 'name cls params sinks sources doc callbacks') def _ports(sigs, msgs): @@ -31,23 +28,30 @@ def _ports(sigs, msgs): return ports -def _blk_class(source_code): +def _find_block_class(source_code, cls): ns = {} try: exec source_code in ns except Exception as e: raise ValueError("Can't interpret source code: " + str(e)) for var in ns.itervalues(): - if inspect.isclass(var)and issubclass(var, gr.gateway.gateway_block): + if inspect.isclass(var) and issubclass(var, cls): return var raise ValueError('No python block class found in code') def extract(cls): + try: + from gnuradio import gr + import pmt + except ImportError: + raise EnvironmentError("Can't import GNU Radio") + if not inspect.isclass(cls): - cls = _blk_class(cls) + cls = _find_block_class(cls, gr.gateway.gateway_block) spec = inspect.getargspec(cls.__init__) + init_args = spec.args[1:] defaults = map(repr, spec.defaults or ()) doc = cls.__doc__ or cls.__init__.__doc__ or '' cls_name = cls.__name__ @@ -61,14 +65,23 @@ def extract(cls): raise RuntimeError("Can't create an instance of your block: " + str(e)) name = instance.name() - params = list(zip(spec.args[1:], defaults)) + + params = list(zip(init_args, defaults)) + + def settable(attr): + try: + return callable(getattr(cls, attr).fset) # check for a property with setter + except AttributeError: + return attr in instance.__dict__ # not dir() - only the instance attribs + + callbacks = [attr for attr in dir(instance) if attr in init_args and settable(attr)] sinks = _ports(instance.in_sig(), pmt.to_python(instance.message_ports_in())) sources = _ports(instance.out_sig(), pmt.to_python(instance.message_ports_out())) - return BlockIO(name, cls_name, params, sinks, sources, doc) + return BlockIO(name, cls_name, params, sinks, sources, doc, callbacks) if __name__ == '__main__': @@ -78,7 +91,7 @@ from gnuradio import gr import pmt class blk(gr.sync_block): - def __init__(self, param1=None, param2=None): + def __init__(self, param1=None, param2=None, param3=None): "Test Docu" gr.sync_block.__init__( self, @@ -88,8 +101,25 @@ class blk(gr.sync_block): ) self.message_port_register_in(pmt.intern('msg_in')) self.message_port_register_out(pmt.intern('msg_out')) + self.param1 = param1 + self._param2 = param2 + self._param3 = param3 + + @property + def param2(self): + return self._param2 + + @property + def param3(self): + return self._param3 + + @param3.setter + def param3(self, value): + self._param3 = value def work(self, inputs_items, output_items): return 10 """ - print extract(blk_code) + from pprint import pprint + pprint(dict(extract(blk_code)._asdict())) + diff --git a/grc/python/expr_utils.py b/grc/core/utils/expr_utils.py index 9e0b2a4a0a..2059ceff9f 100644 --- a/grc/python/expr_utils.py +++ b/grc/core/utils/expr_utils.py @@ -20,6 +20,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA import string VAR_CHARS = string.letters + string.digits + '_' + class graph(object): """ Simple graph structure held in a dictionary. @@ -30,13 +31,16 @@ class graph(object): def __str__(self): return str(self._graph) def add_node(self, node_key): - if self._graph.has_key(node_key): return + if node_key in self._graph: + return self._graph[node_key] = set() def remove_node(self, node_key): - if not self._graph.has_key(node_key): return + if node_key not in self._graph: + return for edges in self._graph.values(): - if node_key in edges: edges.remove(node_key) + if node_key in edges: + edges.remove(node_key) self._graph.pop(node_key) def add_edge(self, src_node_key, dest_node_key): @@ -45,11 +49,14 @@ class graph(object): def remove_edge(self, src_node_key, dest_node_key): self._graph[src_node_key].remove(dest_node_key) - def get_nodes(self): return self._graph.keys() + def get_nodes(self): + return self._graph.keys() + + def get_edges(self, node_key): + return self._graph[node_key] - def get_edges(self, node_key): return self._graph[node_key] -def expr_split(expr): +def expr_split(expr, var_chars=VAR_CHARS): """ Split up an expression by non alphanumeric characters, including underscore. Leave strings in-tact. @@ -65,8 +72,9 @@ def expr_split(expr): tok = '' quote = '' for char in expr: - if quote or char in VAR_CHARS: - if char == quote: quote = '' + if quote or char in var_chars: + if char == quote: + quote = '' tok += char elif char in ("'", '"'): toks.append(tok) @@ -79,6 +87,7 @@ def expr_split(expr): toks.append(tok) return filter(lambda t: t, toks) + def expr_replace(expr, replace_dict): """ Search for vars in the expression and add the prepend. @@ -90,12 +99,13 @@ def expr_replace(expr, replace_dict): Returns: a new expression with the prepend """ - expr_splits = expr_split(expr) + expr_splits = expr_split(expr, var_chars=VAR_CHARS + '.') for i, es in enumerate(expr_splits): if es in replace_dict.keys(): expr_splits[i] = replace_dict[es] return ''.join(expr_splits) + def get_variable_dependencies(expr, vars): """ Return a set of variables used in this expression. @@ -108,7 +118,8 @@ def get_variable_dependencies(expr, vars): a subset of vars used in the expression """ expr_toks = expr_split(expr) - return set(filter(lambda v: v in expr_toks, vars)) + return set(var for var in vars if var in expr_toks) + def get_graph(exprs): """ @@ -121,14 +132,17 @@ def get_graph(exprs): a graph of variable deps """ vars = exprs.keys() - #get dependencies for each expression, load into graph + # Get dependencies for each expression, load into graph var_graph = graph() - for var in vars: var_graph.add_node(var) + for var in vars: + var_graph.add_node(var) for var, expr in exprs.iteritems(): for dep in get_variable_dependencies(expr, vars): - if dep != var: var_graph.add_edge(dep, var) + if dep != var: + var_graph.add_edge(dep, var) return var_graph + def sort_variables(exprs): """ Get a list of variables in order of dependencies. @@ -142,17 +156,20 @@ def sort_variables(exprs): """ var_graph = get_graph(exprs) sorted_vars = list() - #determine dependency order + # Determine dependency order while var_graph.get_nodes(): - #get a list of nodes with no edges + # Get a list of nodes with no edges indep_vars = filter(lambda var: not var_graph.get_edges(var), var_graph.get_nodes()) - if not indep_vars: raise Exception('circular dependency caught in sort_variables') - #add the indep vars to the end of the list + if not indep_vars: + raise Exception('circular dependency caught in sort_variables') + # Add the indep vars to the end of the list sorted_vars.extend(sorted(indep_vars)) - #remove each edge-less node from the graph - for var in indep_vars: var_graph.remove_node(var) + # Remove each edge-less node from the graph + for var in indep_vars: + var_graph.remove_node(var) return reversed(sorted_vars) + def sort_objects(objects, get_id, get_expr): """ Sort a list of objects according to their expressions. @@ -166,12 +183,9 @@ def sort_objects(objects, get_id, get_expr): a list of sorted objects """ id2obj = dict([(get_id(obj), obj) for obj in objects]) - #map obj id to expression code + # Map obj id to expression code id2expr = dict([(get_id(obj), get_expr(obj)) for obj in objects]) - #sort according to dependency + # Sort according to dependency sorted_ids = sort_variables(id2expr) - #return list of sorted objects + # Return list of sorted objects return [id2obj[id] for id in sorted_ids] - -if __name__ == '__main__': - for i in sort_variables({'x':'1', 'y':'x+1', 'a':'x+y', 'b':'y+1', 'c':'a+b+x+y'}): print i diff --git a/grc/python/extract_docs.py b/grc/core/utils/extract_docs.py index d8dc4f4e8f..a6e0bc971e 100644 --- a/grc/python/extract_docs.py +++ b/grc/core/utils/extract_docs.py @@ -1,5 +1,5 @@ """ -Copyright 2008-2011 Free Software Foundation, Inc. +Copyright 2008-2015 Free Software Foundation, Inc. This file is part of GNU Radio GNU Radio Companion is free software; you can redistribute it and/or @@ -32,8 +32,8 @@ import itertools ############################################################################### def docstring_guess_from_key(key): - """Extract the documentation from the python __doc__ strings - + """ + Extract the documentation from the python __doc__ strings By guessing module and constructor names from key Args: @@ -65,12 +65,10 @@ def docstring_guess_from_key(key): else: return doc_strings - pattern = re.compile( - '^' + init_name.replace('_', '_*').replace('x', r'\w') + r'\w*$' - ) + pattern = re.compile('^' + init_name.replace('_', '_*').replace('x', r'\w') + r'\w*$') for match in filter(pattern.match, dir(module)): try: - doc_strings[match] = getattr(module, match).__doc__.strip() + doc_strings[match] = getattr(module, match).__doc__ except AttributeError: continue @@ -78,8 +76,8 @@ def docstring_guess_from_key(key): def docstring_from_make(key, imports, make): - """Extract the documentation from the python __doc__ strings - + """ + Extract the documentation from the python __doc__ strings By importing it and checking a truncated make Args: @@ -95,12 +93,10 @@ def docstring_from_make(key, imports, make): blk_cls = make.partition('(')[0].strip() if '$' in blk_cls: raise ValueError('Not an identifier') - ns = dict() for _import in imports: exec(_import.strip(), ns) blk = eval(blk_cls, ns) - doc_strings = {key: blk.__doc__} except (ImportError, AttributeError, SyntaxError, ValueError): @@ -114,10 +110,11 @@ def docstring_from_make(key, imports, make): ############################################################################### class SubprocessLoader(object): - """Start and manage docstring extraction process - + """ + Start and manage docstring extraction process Manages subprocess and handles RPC. """ + BOOTSTRAP = "import runpy; runpy.run_path({!r}, run_name='__worker__')" AUTH_CODE = random.random() # sort out unwanted output of worker process RESTART = 5 # number of worker restarts before giving up @@ -134,7 +131,7 @@ class SubprocessLoader(object): self._last_cmd = None def start(self): - """Start the worker process handler thread""" + """ Start the worker process handler thread """ if self._thread is not None: return self._shutdown.clear() @@ -143,7 +140,7 @@ class SubprocessLoader(object): thread.start() def run_worker(self): - """Read docstring back from worker stdout and execute callback.""" + """ Read docstring back from worker stdout and execute callback. """ for _ in range(self.RESTART): if self._shutdown.is_set(): break @@ -173,7 +170,7 @@ class SubprocessLoader(object): self.callback_finished() def _handle_worker(self): - """Send commands and responses back from worker.""" + """ Send commands and responses back from worker. """ assert '1' == self._worker.stdout.read(1) for cmd, args in iter(self._queue.get, self.DONE): self._last_cmd = cmd, args @@ -182,13 +179,13 @@ class SubprocessLoader(object): self._handle_response(cmd, args) def _send(self, cmd, args): - """send a command to worker's stdin""" + """ Send a command to worker's stdin """ fd = self._worker.stdin json.dump((self.AUTH_CODE, cmd, args), fd) fd.write('\n'.encode()) def _receive(self): - """receive response from worker's stdout""" + """ Receive response from worker's stdout """ for line in iter(self._worker.stdout.readline, ''): try: key, cmd, args = json.loads(line, encoding='utf-8') @@ -201,7 +198,7 @@ class SubprocessLoader(object): raise IOError("Can't read worker response") def _handle_response(self, cmd, args): - """Handle response from worker, call the callback""" + """ Handle response from worker, call the callback """ if cmd == 'result': key, docs = args self.callback_query_result(key, docs) @@ -211,7 +208,7 @@ class SubprocessLoader(object): print >> sys.stderr, "Unknown response:", cmd, args def query(self, key, imports=None, make=None): - """request docstring extraction for a certain key""" + """ Request docstring extraction for a certain key """ if self._thread is None: self.start() if imports and make: @@ -220,16 +217,16 @@ class SubprocessLoader(object): self._queue.put(('query_key_only', (key,))) def finish(self): - """signal end of requests""" + """ Signal end of requests """ self._queue.put(self.DONE) def wait(self): - """Wait for the handler thread to die""" + """ Wait for the handler thread to die """ if self._thread: self._thread.join() def terminate(self): - """Terminate the worker and wait""" + """ Terminate the worker and wait """ self._shutdown.set() try: self._worker.terminate() @@ -243,8 +240,8 @@ class SubprocessLoader(object): ############################################################################### def worker_main(): - """Main entry point for the docstring extraction process. - + """ + Main entry point for the docstring extraction process. Manages RPC with main process through. Runs a docstring extraction for each key it read on stdin. """ diff --git a/grc/base/odict.py b/grc/core/utils/odict.py index 70ab67d053..85927e869f 100644 --- a/grc/base/odict.py +++ b/grc/core/utils/odict.py @@ -1,5 +1,5 @@ """ -Copyright 2008-2011 Free Software Foundation, Inc. +Copyright 2008-2015 Free Software Foundation, Inc. This file is part of GNU Radio GNU Radio Companion is free software; you can redistribute it and/or @@ -19,6 +19,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA from UserDict import DictMixin + class odict(DictMixin): def __init__(self, d={}): @@ -57,7 +58,8 @@ class odict(DictMixin): val: the value for the new entry """ index = (pos_key is None) and len(self._keys) or self._keys.index(pos_key) - if key in self._keys: raise KeyError('Cannot insert, key "%s" already exists'%str(key)) + if key in self._keys: + raise KeyError('Cannot insert, key "{}" already exists'.format(str(key))) self._keys.insert(index+1, key) self._data[key] = val @@ -72,7 +74,8 @@ class odict(DictMixin): val: the value for the new entry """ index = (pos_key is not None) and self._keys.index(pos_key) or 0 - if key in self._keys: raise KeyError('Cannot insert, key "%s" already exists'%str(key)) + if key in self._keys: + raise KeyError('Cannot insert, key "{}" already exists'.format(str(key))) self._keys.insert(index, key) self._data[key] = val @@ -86,7 +89,8 @@ class odict(DictMixin): Returns: the value or None """ - if self.has_key(key): return self[key] + if key in self: + return self[key] return None def findall(self, key): @@ -100,6 +104,12 @@ class odict(DictMixin): a list of values or empty list """ obj = self.find(key) - if obj is None: obj = list() - if isinstance(obj, list): return obj + if obj is None: + obj = list() + if isinstance(obj, list): + return obj return [obj] + + def clear(self): + self._data.clear() + del self._keys[:]
\ No newline at end of file diff --git a/grc/cpp/README b/grc/cpp/README deleted file mode 100644 index 3eccc5dbf7..0000000000 --- a/grc/cpp/README +++ /dev/null @@ -1,5 +0,0 @@ -GRC could be used to generate c++ based flowgraphs: - -* A few base and gui classes would be overridden. -* Block info could be extracted from the doxygen xml. -* New flowgraph templates would be designed. diff --git a/grc/examples/simple/variable_config.grc b/grc/examples/simple/variable_config.grc deleted file mode 100644 index 0b60abc813..0000000000 --- a/grc/examples/simple/variable_config.grc +++ /dev/null @@ -1,561 +0,0 @@ -<?xml version='1.0' encoding='ASCII'?> -<flow_graph> - <timestamp>Sat Jul 12 16:15:51 2014</timestamp> - <block> - <key>options</key> - <param> - <key>id</key> - <value>variable_config_demo</value> - </param> - <param> - <key>_enabled</key> - <value>True</value> - </param> - <param> - <key>title</key> - <value>Variable Config Block Demonstration</value> - </param> - <param> - <key>author</key> - <value>Example</value> - </param> - <param> - <key>description</key> - <value>Save/Load freq from a config file.</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>alias</key> - <value></value> - </param> - <param> - <key>_coordinate</key> - <value>(-1, 2)</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>_coordinate</key> - <value>(-1, 125)</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>complex</value> - </param> - <param> - <key>samp_rate</key> - <value>samp_rate</value> - </param> - <param> - <key>waveform</key> - <value>analog.GR_COS_WAVE</value> - </param> - <param> - <key>freq</key> - <value>freq</value> - </param> - <param> - <key>amp</key> - <value>1</value> - </param> - <param> - <key>offset</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>_coordinate</key> - <value>(173, 201)</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>_coordinate</key> - <value>(392, 233)</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>complex</value> - </param> - <param> - <key>name</key> - <value>QT GUI Plot</value> - </param> - <param> - <key>fftsize</key> - <value>1024</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>autoscale</key> - <value>False</value> - </param> - <param> - <key>average</key> - <value>1.0</value> - </param> - <param> - <key>ymin</key> - <value>-140</value> - </param> - <param> - <key>ymax</key> - <value>10</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></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>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>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>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>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>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>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>_coordinate</key> - <value>(643, 221)</value> - </param> - <param> - <key>_rotation</key> - <value>0</value> - </param> - </block> - <block> - <key>variable_qtgui_range</key> - <param> - <key>id</key> - <value>freq</value> - </param> - <param> - <key>_enabled</key> - <value>True</value> - </param> - <param> - <key>label</key> - <value>Frequency (Hz)</value> - </param> - <param> - <key>value</key> - <value>1e3</value> - </param> - <param> - <key>start</key> - <value>-samp_rate/2</value> - </param> - <param> - <key>stop</key> - <value>samp_rate/2</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></value> - </param> - <param> - <key>alias</key> - <value></value> - </param> - <param> - <key>_coordinate</key> - <value>(339, 9)</value> - </param> - <param> - <key>_rotation</key> - <value>0</value> - </param> - </block> - <block> - <key>variable_config</key> - <param> - <key>id</key> - <value>freq_init</value> - </param> - <param> - <key>_enabled</key> - <value>True</value> - </param> - <param> - <key>value</key> - <value>1000</value> - </param> - <param> - <key>type</key> - <value>real</value> - </param> - <param> - <key>config_file</key> - <value>/home/mbant/.gnuradio/config.conf</value> - </param> - <param> - <key>section</key> - <value>main</value> - </param> - <param> - <key>option</key> - <value>freq</value> - </param> - <param> - <key>writeback</key> - <value>freq</value> - </param> - <param> - <key>alias</key> - <value></value> - </param> - <param> - <key>_coordinate</key> - <value>(168, 0)</value> - </param> - <param> - <key>_rotation</key> - <value>0</value> - </param> - </block> - <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> - <connection> - <source_block_id>blocks_throttle_0</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> -</flow_graph> diff --git a/grc/examples/xmlrpc/readme.txt b/grc/examples/xmlrpc/readme.txt deleted file mode 100644 index 056ad1e823..0000000000 --- a/grc/examples/xmlrpc/readme.txt +++ /dev/null @@ -1,18 +0,0 @@ -################################################## -# XMLRPC example -################################################## - -XMLRPC allows software to make remote function calls over http. -In the case of GRC, one can use XMLRPC to modify variables in a running flow graph. -See http://www.xmlrpc.com/ - ---- Server Example --- -Place an "XMLRPC Server" block inside of any flow graph. -The server will provide set functions for every variable in the flow graph. -If a variable is called "freq", the server will provide a function set_freq(new_freq). -Run the server example and experiment with the example client script. - --- Client Example -- -The "XMLRPC Client" block will give a variable control over one remove function. -In the example client, there is one client block and gui control per variable. -This technique can be used to remotely control a flow graph, perhaps running on a non-gui machine. diff --git a/grc/examples/xmlrpc/xmlrpc_client.grc b/grc/examples/xmlrpc/xmlrpc_client.grc deleted file mode 100644 index 45d8af2824..0000000000 --- a/grc/examples/xmlrpc/xmlrpc_client.grc +++ /dev/null @@ -1,428 +0,0 @@ -<?xml version='1.0' encoding='ASCII'?> -<flow_graph> - <timestamp>Sat Jul 12 17:10:55 2014</timestamp> - <block> - <key>options</key> - <param> - <key>id</key> - <value>client_block</value> - </param> - <param> - <key>_enabled</key> - <value>True</value> - </param> - <param> - <key>title</key> - <value>XMLRPC Client</value> - </param> - <param> - <key>author</key> - <value>Example</value> - </param> - <param> - <key>description</key> - <value>example flow graph</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>alias</key> - <value></value> - </param> - <param> - <key>_coordinate</key> - <value>(-2, 0)</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>_coordinate</key> - <value>(13, 172)</value> - </param> - <param> - <key>_rotation</key> - <value>0</value> - </param> - </block> - <block> - <key>xmlrpc_client</key> - <param> - <key>id</key> - <value>xmlrpc_client</value> - </param> - <param> - <key>_enabled</key> - <value>True</value> - </param> - <param> - <key>addr</key> - <value>localhost</value> - </param> - <param> - <key>port</key> - <value>1234</value> - </param> - <param> - <key>callback</key> - <value>set_freq</value> - </param> - <param> - <key>variable</key> - <value>freq</value> - </param> - <param> - <key>alias</key> - <value></value> - </param> - <param> - <key>_coordinate</key> - <value>(177, 0)</value> - </param> - <param> - <key>_rotation</key> - <value>0</value> - </param> - </block> - <block> - <key>xmlrpc_client</key> - <param> - <key>id</key> - <value>xmlrpc_client0</value> - </param> - <param> - <key>_enabled</key> - <value>True</value> - </param> - <param> - <key>addr</key> - <value>localhost</value> - </param> - <param> - <key>port</key> - <value>1234</value> - </param> - <param> - <key>callback</key> - <value>set_ampl</value> - </param> - <param> - <key>variable</key> - <value>ampl</value> - </param> - <param> - <key>alias</key> - <value></value> - </param> - <param> - <key>_coordinate</key> - <value>(308, 0)</value> - </param> - <param> - <key>_rotation</key> - <value>0</value> - </param> - </block> - <block> - <key>xmlrpc_client</key> - <param> - <key>id</key> - <value>xmlrpc_client1</value> - </param> - <param> - <key>_enabled</key> - <value>True</value> - </param> - <param> - <key>addr</key> - <value>localhost</value> - </param> - <param> - <key>port</key> - <value>1234</value> - </param> - <param> - <key>callback</key> - <value>set_offset</value> - </param> - <param> - <key>variable</key> - <value>offset*ampl</value> - </param> - <param> - <key>alias</key> - <value></value> - </param> - <param> - <key>_coordinate</key> - <value>(440, 4)</value> - </param> - <param> - <key>_rotation</key> - <value>0</value> - </param> - </block> - <block> - <key>variable_qtgui_range</key> - <param> - <key>id</key> - <value>freq</value> - </param> - <param> - <key>_enabled</key> - <value>True</value> - </param> - <param> - <key>label</key> - <value>Frequency (Hz)</value> - </param> - <param> - <key>value</key> - <value>1000</value> - </param> - <param> - <key>start</key> - <value>0</value> - </param> - <param> - <key>stop</key> - <value>5e3</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>0,0,1,2</value> - </param> - <param> - <key>alias</key> - <value></value> - </param> - <param> - <key>_coordinate</key> - <value>(209, 165)</value> - </param> - <param> - <key>_rotation</key> - <value>0</value> - </param> - </block> - <block> - <key>variable_qtgui_range</key> - <param> - <key>id</key> - <value>ampl</value> - </param> - <param> - <key>_enabled</key> - <value>True</value> - </param> - <param> - <key>label</key> - <value>Amplitude</value> - </param> - <param> - <key>value</key> - <value>1</value> - </param> - <param> - <key>start</key> - <value>0</value> - </param> - <param> - <key>stop</key> - <value>2</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>1,0,1,2</value> - </param> - <param> - <key>alias</key> - <value></value> - </param> - <param> - <key>_coordinate</key> - <value>(367, 158)</value> - </param> - <param> - <key>_rotation</key> - <value>0</value> - </param> - </block> - <block> - <key>variable_qtgui_chooser</key> - <param> - <key>id</key> - <value>offset</value> - </param> - <param> - <key>_enabled</key> - <value>True</value> - </param> - <param> - <key>label</key> - <value>Offset</value> - </param> - <param> - <key>type</key> - <value>int</value> - </param> - <param> - <key>num_opts</key> - <value>3</value> - </param> - <param> - <key>value</key> - <value>0</value> - </param> - <param> - <key>options</key> - <value>[0, 1, 2]</value> - </param> - <param> - <key>labels</key> - <value>[]</value> - </param> - <param> - <key>option0</key> - <value>-1</value> - </param> - <param> - <key>label0</key> - <value>neg</value> - </param> - <param> - <key>option1</key> - <value>0</value> - </param> - <param> - <key>label1</key> - <value>zero</value> - </param> - <param> - <key>option2</key> - <value>1</value> - </param> - <param> - <key>label2</key> - <value>pos</value> - </param> - <param> - <key>option3</key> - <value>3</value> - </param> - <param> - <key>label3</key> - <value></value> - </param> - <param> - <key>option4</key> - <value>4</value> - </param> - <param> - <key>label4</key> - <value></value> - </param> - <param> - <key>widget</key> - <value>combo_box</value> - </param> - <param> - <key>orient</key> - <value>Qt.QVBoxLayout</value> - </param> - <param> - <key>gui_hint</key> - <value>2,0,1,2</value> - </param> - <param> - <key>alias</key> - <value></value> - </param> - <param> - <key>_coordinate</key> - <value>(531, 145)</value> - </param> - <param> - <key>_rotation</key> - <value>0</value> - </param> - </block> -</flow_graph> diff --git a/grc/examples/xmlrpc/xmlrpc_client_script.py b/grc/examples/xmlrpc/xmlrpc_client_script.py deleted file mode 100644 index e96c4cbf83..0000000000 --- a/grc/examples/xmlrpc/xmlrpc_client_script.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python - -import time -import random -import xmlrpclib - -#create server object -s = xmlrpclib.Server("http://localhost:1234") - -#randomly change parameters of the sinusoid -for i in range(10): - #generate random values - new_freq = random.uniform(0, 5000) - new_ampl = random.uniform(0, 2) - new_offset = random.uniform(-1, 1) - #set new values - time.sleep(1) - s.set_freq(new_freq) - time.sleep(1) - s.set_ampl(new_ampl) - time.sleep(1) - s.set_offset(new_offset) - diff --git a/grc/gui/ActionHandler.py b/grc/gui/ActionHandler.py index 7766a0a853..11e81c4613 100644 --- a/grc/gui/ActionHandler.py +++ b/grc/gui/ActionHandler.py @@ -17,26 +17,21 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ -import os -import subprocess -from threading import Thread -import pygtk -pygtk.require('2.0') -import gtk import gobject +import gtk +import os +import subprocess -from .. base import ParseXML, Constants -from .. python.Constants import XTERM_EXECUTABLE - -from . import Dialogs, Messages, Preferences, Actions -from .ParserErrorsDialog import ParserErrorsDialog -from .MainWindow import MainWindow -from .PropsDialog import PropsDialog +from . import Dialogs, Preferences, Actions, Executor, Constants from .FileDialogs import (OpenFlowGraphFileDialog, SaveFlowGraphFileDialog, - SaveReportsFileDialog, SaveImageFileDialog, + SaveConsoleFileDialog, SaveScreenShotDialog, OpenQSSFileDialog) -from .Constants import DEFAULT_CANVAS_SIZE, IMAGE_FILE_EXTENSION, GR_PREFIX +from .MainWindow import MainWindow +from .ParserErrorsDialog import ParserErrorsDialog +from .PropsDialog import PropsDialog + +from ..core import ParseXML, Messages gobject.threads_init() @@ -66,16 +61,14 @@ class ActionHandler: self.main_window.connect('delete-event', self._quit) self.main_window.connect('key-press-event', self._handle_key_press) self.get_page = self.main_window.get_page - self.get_flow_graph = self.main_window.get_flow_graph self.get_focus_flag = self.main_window.get_focus_flag #setup the messages - Messages.register_messenger(self.main_window.add_report_line) + Messages.register_messenger(self.main_window.add_console_line) Messages.send_init(platform) #initialize self.init_file_paths = file_paths + self.init = False Actions.APPLICATION_INITIALIZE() - #enter the mainloop - gtk.main() def _handle_key_press(self, widget, event): """ @@ -111,6 +104,14 @@ class ActionHandler: def _handle_action(self, action, *args): #print action + main = self.main_window + page = main.get_page() + flow_graph = page.get_flow_graph() if page else None + + def flow_graph_update(fg=flow_graph): + main.vars.update_gui() + fg.update() + ################################################## # Initialize/Quit ################################################## @@ -119,12 +120,13 @@ class ActionHandler: self.init_file_paths = filter(os.path.exists, Preferences.get_open_files()) if not self.init_file_paths: self.init_file_paths = [''] for file_path in self.init_file_paths: - if file_path: self.main_window.new_page(file_path) #load pages from file paths + if file_path: main.new_page(file_path) #load pages from file paths if Preferences.file_open() in self.init_file_paths: - self.main_window.new_page(Preferences.file_open(), show=True) - if not self.get_page(): self.main_window.new_page() #ensure that at least a blank page exists + main.new_page(Preferences.file_open(), show=True) + if not self.get_page(): + main.new_page() # ensure that at least a blank page exists - self.main_window.btwin.search_entry.hide() + main.btwin.search_entry.hide() # Disable all actions, then re-enable a few for action in Actions.get_all_actions(): @@ -135,14 +137,18 @@ class ActionHandler: Actions.FLOW_GRAPH_CLOSE, Actions.ABOUT_WINDOW_DISPLAY, Actions.FLOW_GRAPH_SCREEN_CAPTURE, Actions.HELP_WINDOW_DISPLAY, Actions.TYPES_WINDOW_DISPLAY, Actions.TOGGLE_BLOCKS_WINDOW, - Actions.TOGGLE_REPORTS_WINDOW, Actions.TOGGLE_HIDE_DISABLED_BLOCKS, + Actions.TOGGLE_CONSOLE_WINDOW, Actions.TOGGLE_HIDE_DISABLED_BLOCKS, Actions.TOOLS_RUN_FDESIGN, Actions.TOGGLE_SCROLL_LOCK, - Actions.CLEAR_REPORTS, Actions.SAVE_REPORTS, + Actions.CLEAR_CONSOLE, Actions.SAVE_CONSOLE, Actions.TOGGLE_AUTO_HIDE_PORT_LABELS, Actions.TOGGLE_SNAP_TO_GRID, Actions.TOGGLE_SHOW_BLOCK_COMMENTS, Actions.TOGGLE_SHOW_CODE_PREVIEW_TAB, Actions.TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY, Actions.FLOW_GRAPH_OPEN_QSS_THEME, + Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR, + Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR, + Actions.TOGGLE_HIDE_VARIABLES, + Actions.SELECT_ALL, ): action.set_sensitive(True) if hasattr(action, 'load_from_preferences'): @@ -150,9 +156,9 @@ class ActionHandler: if ParseXML.xml_failures: Messages.send_xml_errors_if_any(ParseXML.xml_failures) Actions.XML_PARSER_ERRORS_DISPLAY.set_sensitive(True) - + self.init = True elif action == Actions.APPLICATION_QUIT: - if self.main_window.close_pages(): + if main.close_pages(): gtk.main_quit() exit(0) ################################################## @@ -161,25 +167,27 @@ class ActionHandler: elif action == Actions.ELEMENT_SELECT: pass #do nothing, update routines below elif action == Actions.NOTHING_SELECT: - self.get_flow_graph().unselect() + flow_graph.unselect() + elif action == Actions.SELECT_ALL: + flow_graph.select_all() ################################################## # Enable/Disable ################################################## elif action == Actions.BLOCK_ENABLE: - if self.get_flow_graph().enable_selected(True): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) + if flow_graph.enable_selected(True): + flow_graph_update() + page.get_state_cache().save_new_state(flow_graph.export_data()) + page.set_saved(False) elif action == Actions.BLOCK_DISABLE: - if self.get_flow_graph().enable_selected(False): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) + if flow_graph.enable_selected(False): + flow_graph_update() + page.get_state_cache().save_new_state(flow_graph.export_data()) + page.set_saved(False) elif action == Actions.BLOCK_BYPASS: - if self.get_flow_graph().bypass_selected(): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) + if flow_graph.bypass_selected(): + flow_graph_update() + page.get_state_cache().save_new_state(flow_graph.export_data()) + page.set_saved(False) ################################################## # Cut/Copy/Paste ################################################## @@ -187,20 +195,20 @@ class ActionHandler: Actions.BLOCK_COPY() Actions.ELEMENT_DELETE() elif action == Actions.BLOCK_COPY: - self.clipboard = self.get_flow_graph().copy_to_clipboard() + self.clipboard = flow_graph.copy_to_clipboard() elif action == Actions.BLOCK_PASTE: if self.clipboard: - self.get_flow_graph().paste_from_clipboard(self.clipboard) - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) + flow_graph.paste_from_clipboard(self.clipboard) + flow_graph_update() + page.get_state_cache().save_new_state(flow_graph.export_data()) + page.set_saved(False) ################################################## # Create heir block ################################################## elif action == Actions.BLOCK_CREATE_HIER: # keeping track of coordinates for pasting later - coords = self.get_flow_graph().get_selected_blocks()[0].get_coordinate() + coords = flow_graph.get_selected_blocks()[0].get_coordinate() x,y = coords x_min = x y_min = y @@ -209,15 +217,15 @@ class ActionHandler: params = []; # Save the state of the leaf blocks - for block in self.get_flow_graph().get_selected_blocks(): + for block in flow_graph.get_selected_blocks(): # Check for string variables within the blocks for param in block.get_params(): - for variable in self.get_flow_graph().get_variables(): + for variable in flow_graph.get_variables(): # If a block parameter exists that is a variable, create a parameter for it if param.get_value() == variable.get_id(): params.append(param.get_value()) - for flow_param in self.get_flow_graph().get_parameters(): + for flow_param in flow_graph.get_parameters(): # If a block parameter exists that is a parameter, create a parameter for it if param.get_value() == flow_param.get_id(): params.append(param.get_value()) @@ -230,46 +238,46 @@ class ActionHandler: if y < y_min: y_min = y - for connection in block.get_connections(): + for connection in block.connections: # Get id of connected blocks source_id = connection.get_source().get_parent().get_id() sink_id = connection.get_sink().get_parent().get_id() # If connected block is not in the list of selected blocks create a pad for it - if self.get_flow_graph().get_block(source_id) not in self.get_flow_graph().get_selected_blocks(): + if flow_graph.get_block(source_id) not in flow_graph.get_selected_blocks(): pads.append({'key': connection.get_sink().get_key(), 'coord': connection.get_source().get_coordinate(), 'block_id' : block.get_id(), 'direction': 'source'}) - if self.get_flow_graph().get_block(sink_id) not in self.get_flow_graph().get_selected_blocks(): + if flow_graph.get_block(sink_id) not in flow_graph.get_selected_blocks(): pads.append({'key': connection.get_source().get_key(), 'coord': connection.get_sink().get_coordinate(), 'block_id' : block.get_id(), 'direction': 'sink'}) # Copy the selected blocks and paste them into a new page # then move the flowgraph to a reasonable position Actions.BLOCK_COPY() - self.main_window.new_page() + main.new_page() Actions.BLOCK_PASTE() coords = (x_min,y_min) - self.get_flow_graph().move_selected(coords) + flow_graph.move_selected(coords) # Set flow graph to heir block type - top_block = self.get_flow_graph().get_block("top_block") + top_block = flow_graph.get_block("top_block") top_block.get_param('generate_options').set_value('hb') # this needs to be a unique name top_block.get_param('id').set_value('new_heir') # Remove the default samp_rate variable block that is created - remove_me = self.get_flow_graph().get_block("samp_rate") - self.get_flow_graph().remove_element(remove_me) + remove_me = flow_graph.get_block("samp_rate") + flow_graph.remove_element(remove_me) # Add the param blocks along the top of the window x_pos = 150 for param in params: - param_id = self.get_flow_graph().add_new_block('parameter',(x_pos,10)) - param_block = self.get_flow_graph().get_block(param_id) + param_id = flow_graph.add_new_block('parameter',(x_pos,10)) + param_block = flow_graph.get_block(param_id) param_block.get_param('id').set_value(param) x_pos = x_pos + 100 @@ -278,13 +286,13 @@ class ActionHandler: if pad['direction'] == 'sink': # Add new PAD_SINK block to the canvas - pad_id = self.get_flow_graph().add_new_block('pad_sink', pad['coord']) + pad_id = flow_graph.add_new_block('pad_sink', pad['coord']) # setup the references to the sink and source - pad_block = self.get_flow_graph().get_block(pad_id) + pad_block = flow_graph.get_block(pad_id) pad_sink = pad_block.get_sinks()[0] - source_block = self.get_flow_graph().get_block(pad['block_id']) + source_block = flow_graph.get_block(pad['block_id']) source = source_block.get_source(pad['key']) # Ensure the port types match @@ -296,16 +304,16 @@ class ActionHandler: pad_block.type_controller_modify(1) # Connect the pad to the proper sinks - new_connection = self.get_flow_graph().connect(source,pad_sink) + new_connection = flow_graph.connect(source,pad_sink) elif pad['direction'] == 'source': - pad_id = self.get_flow_graph().add_new_block('pad_source', pad['coord']) + pad_id = flow_graph.add_new_block('pad_source', pad['coord']) # setup the references to the sink and source - pad_block = self.get_flow_graph().get_block(pad_id) + pad_block = flow_graph.get_block(pad_id) pad_source = pad_block.get_sources()[0] - sink_block = self.get_flow_graph().get_block(pad['block_id']) + sink_block = flow_graph.get_block(pad['block_id']) sink = sink_block.get_sink(pad['key']) # Ensure the port types match @@ -316,139 +324,172 @@ class ActionHandler: pad_block.type_controller_modify(1) # Connect the pad to the proper sinks - new_connection = self.get_flow_graph().connect(pad_source,sink) + new_connection = flow_graph.connect(pad_source,sink) # update the new heir block flow graph - self.get_flow_graph().update() + flow_graph_update() ################################################## # Move/Rotate/Delete/Create ################################################## elif action == Actions.BLOCK_MOVE: - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) + page.get_state_cache().save_new_state(flow_graph.export_data()) + page.set_saved(False) + elif action in Actions.BLOCK_ALIGNMENTS: + if flow_graph.align_selected(action): + page.get_state_cache().save_new_state(flow_graph.export_data()) + page.set_saved(False) elif action == Actions.BLOCK_ROTATE_CCW: - if self.get_flow_graph().rotate_selected(90): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) + if flow_graph.rotate_selected(90): + flow_graph_update() + page.get_state_cache().save_new_state(flow_graph.export_data()) + page.set_saved(False) elif action == Actions.BLOCK_ROTATE_CW: - if self.get_flow_graph().rotate_selected(-90): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) + if flow_graph.rotate_selected(-90): + flow_graph_update() + page.get_state_cache().save_new_state(flow_graph.export_data()) + page.set_saved(False) elif action == Actions.ELEMENT_DELETE: - if self.get_flow_graph().remove_selected(): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + if flow_graph.remove_selected(): + flow_graph_update() + page.get_state_cache().save_new_state(flow_graph.export_data()) Actions.NOTHING_SELECT() - self.get_page().set_saved(False) + page.set_saved(False) elif action == Actions.ELEMENT_CREATE: - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + flow_graph_update() + page.get_state_cache().save_new_state(flow_graph.export_data()) Actions.NOTHING_SELECT() - self.get_page().set_saved(False) + page.set_saved(False) elif action == Actions.BLOCK_INC_TYPE: - if self.get_flow_graph().type_controller_modify_selected(1): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) + if flow_graph.type_controller_modify_selected(1): + flow_graph_update() + page.get_state_cache().save_new_state(flow_graph.export_data()) + page.set_saved(False) elif action == Actions.BLOCK_DEC_TYPE: - if self.get_flow_graph().type_controller_modify_selected(-1): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) + if flow_graph.type_controller_modify_selected(-1): + flow_graph_update() + page.get_state_cache().save_new_state(flow_graph.export_data()) + page.set_saved(False) elif action == Actions.PORT_CONTROLLER_INC: - if self.get_flow_graph().port_controller_modify_selected(1): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) + if flow_graph.port_controller_modify_selected(1): + flow_graph_update() + page.get_state_cache().save_new_state(flow_graph.export_data()) + page.set_saved(False) elif action == Actions.PORT_CONTROLLER_DEC: - if self.get_flow_graph().port_controller_modify_selected(-1): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) + if flow_graph.port_controller_modify_selected(-1): + flow_graph_update() + page.get_state_cache().save_new_state(flow_graph.export_data()) + page.set_saved(False) ################################################## # Window stuff ################################################## elif action == Actions.ABOUT_WINDOW_DISPLAY: - Dialogs.AboutDialog(self.get_flow_graph().get_parent()) + platform = flow_graph.get_parent() + Dialogs.AboutDialog(platform.config) elif action == Actions.HELP_WINDOW_DISPLAY: Dialogs.HelpDialog() elif action == Actions.TYPES_WINDOW_DISPLAY: - Dialogs.TypesDialog(self.get_flow_graph().get_parent()) + Dialogs.TypesDialog(flow_graph.get_parent()) elif action == Actions.ERRORS_WINDOW_DISPLAY: - Dialogs.ErrorsDialog(self.get_flow_graph()) - elif action == Actions.TOGGLE_REPORTS_WINDOW: - if action.get_active(): - self.main_window.reports_scrolled_window.show() - else: - self.main_window.reports_scrolled_window.hide() + Dialogs.ErrorsDialog(flow_graph) + elif action == Actions.TOGGLE_CONSOLE_WINDOW: + main.update_panel_visibility(main.CONSOLE, action.get_active()) action.save_to_preferences() elif action == Actions.TOGGLE_BLOCKS_WINDOW: - if action.get_active(): - self.main_window.btwin.show() - else: - self.main_window.btwin.hide() + main.update_panel_visibility(main.BLOCKS, action.get_active()) action.save_to_preferences() elif action == Actions.TOGGLE_SCROLL_LOCK: active = action.get_active() - self.main_window.text_display.scroll_lock = active + main.text_display.scroll_lock = active if active: - self.main_window.text_display.scroll_to_end() + main.text_display.scroll_to_end() action.save_to_preferences() - elif action == Actions.CLEAR_REPORTS: - self.main_window.text_display.clear() - elif action == Actions.SAVE_REPORTS: - file_path = SaveReportsFileDialog(self.get_page().get_file_path()).run() + elif action == Actions.CLEAR_CONSOLE: + main.text_display.clear() + elif action == Actions.SAVE_CONSOLE: + file_path = SaveConsoleFileDialog(page.get_file_path()).run() if file_path is not None: - self.main_window.text_display.save(file_path) + main.text_display.save(file_path) elif action == Actions.TOGGLE_HIDE_DISABLED_BLOCKS: Actions.NOTHING_SELECT() elif action == Actions.TOGGLE_AUTO_HIDE_PORT_LABELS: action.save_to_preferences() - for page in self.main_window.get_pages(): + for page in main.get_pages(): page.get_flow_graph().create_shapes() - elif action == Actions.TOGGLE_SNAP_TO_GRID: + elif action in (Actions.TOGGLE_SNAP_TO_GRID, + Actions.TOGGLE_SHOW_BLOCK_COMMENTS, + Actions.TOGGLE_SHOW_CODE_PREVIEW_TAB): action.save_to_preferences() - elif action == Actions.TOGGLE_SHOW_BLOCK_COMMENTS: + elif action == Actions.TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY: action.save_to_preferences() - elif action == Actions.TOGGLE_SHOW_CODE_PREVIEW_TAB: + for page in main.get_pages(): + flow_graph_update(page.get_flow_graph()) + elif action == Actions.TOGGLE_HIDE_VARIABLES: + # Call the variable editor TOGGLE in case it needs to be showing + Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR() + Actions.NOTHING_SELECT() action.save_to_preferences() - elif action == Actions.TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY: + elif action == Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR: + # See if the variables are hidden + if Actions.TOGGLE_HIDE_VARIABLES.get_active(): + # Force this to be shown + main.update_panel_visibility(main.VARIABLES, True) + action.set_active(True) + action.set_sensitive(False) + else: + if action.get_sensitive(): + main.update_panel_visibility(main.VARIABLES, action.get_active()) + else: # This is occurring after variables are un-hidden + # Leave it enabled + action.set_sensitive(True) + action.set_active(True) + #Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR.set_sensitive(action.get_active()) action.save_to_preferences() - for page in self.main_window.get_pages(): - page.get_flow_graph().update() + elif action == Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR: + if self.init: + md = gtk.MessageDialog(main, + gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_INFO, + gtk.BUTTONS_CLOSE, "Moving the variable editor requires a restart of GRC.") + md.run() + md.destroy() + action.save_to_preferences() ################################################## # Param Modifications ################################################## elif action == Actions.BLOCK_PARAM_MODIFY: - selected_block = self.get_flow_graph().get_selected_block() + if action.args: + selected_block = action.args[0] + else: + selected_block = flow_graph.get_selected_block() if selected_block: self.dialog = PropsDialog(selected_block) response = gtk.RESPONSE_APPLY while response == gtk.RESPONSE_APPLY: # rerun the dialog if Apply was hit response = self.dialog.run() if response in (gtk.RESPONSE_APPLY, gtk.RESPONSE_ACCEPT): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) + flow_graph_update() + page.get_state_cache().save_new_state(flow_graph.export_data()) + page.set_saved(False) else: # restore the current state - n = self.get_page().get_state_cache().get_current_state() - self.get_flow_graph().import_data(n) - self.get_flow_graph().update() + n = page.get_state_cache().get_current_state() + flow_graph.import_data(n) + flow_graph_update() if response == gtk.RESPONSE_APPLY: # null action, that updates the main window Actions.ELEMENT_SELECT() self.dialog.destroy() self.dialog = None elif action == Actions.EXTERNAL_UPDATE: - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_flow_graph().update() + page.get_state_cache().save_new_state(flow_graph.export_data()) + flow_graph_update() if self.dialog is not None: self.dialog.update_gui(force=True) - self.get_page().set_saved(False) + page.set_saved(False) + elif action == Actions.VARIABLE_EDITOR_UPDATE: + page.get_state_cache().save_new_state(flow_graph.export_data()) + flow_graph_update() + page.set_saved(False) ################################################## # View Parser Errors ################################################## @@ -458,135 +499,144 @@ class ActionHandler: # Undo/Redo ################################################## elif action == Actions.FLOW_GRAPH_UNDO: - n = self.get_page().get_state_cache().get_prev_state() + n = page.get_state_cache().get_prev_state() if n: - self.get_flow_graph().unselect() - self.get_flow_graph().import_data(n) - self.get_flow_graph().update() - self.get_page().set_saved(False) + flow_graph.unselect() + flow_graph.import_data(n) + flow_graph_update() + page.set_saved(False) elif action == Actions.FLOW_GRAPH_REDO: - n = self.get_page().get_state_cache().get_next_state() + n = page.get_state_cache().get_next_state() if n: - self.get_flow_graph().unselect() - self.get_flow_graph().import_data(n) - self.get_flow_graph().update() - self.get_page().set_saved(False) + flow_graph.unselect() + flow_graph.import_data(n) + flow_graph_update() + page.set_saved(False) ################################################## # New/Open/Save/Close ################################################## elif action == Actions.FLOW_GRAPH_NEW: - self.main_window.new_page() + main.new_page() if args: - self.get_flow_graph()._options_block.get_param('generate_options').set_value(args[0]) - self.get_flow_graph().update() + flow_graph._options_block.get_param('generate_options').set_value(args[0]) + flow_graph_update() elif action == Actions.FLOW_GRAPH_OPEN: - file_paths = args if args else OpenFlowGraphFileDialog(self.get_page().get_file_path()).run() + file_paths = args if args else OpenFlowGraphFileDialog(page.get_file_path()).run() if file_paths: #open a new page for each file, show only the first for i,file_path in enumerate(file_paths): - self.main_window.new_page(file_path, show=(i==0)) + main.new_page(file_path, show=(i==0)) Preferences.add_recent_file(file_path) - self.main_window.tool_bar.refresh_submenus() - self.main_window.menu_bar.refresh_submenus() + main.tool_bar.refresh_submenus() + main.menu_bar.refresh_submenus() + main.vars.update_gui() elif action == Actions.FLOW_GRAPH_OPEN_QSS_THEME: - file_paths = OpenQSSFileDialog(GR_PREFIX + '/share/gnuradio/themes/').run() + file_paths = OpenQSSFileDialog(self.platform.config.install_prefix + + '/share/gnuradio/themes/').run() if file_paths: try: - from gnuradio import gr - gr.prefs().set_string("qtgui", "qss", file_paths[0]) - gr.prefs().save() + prefs = self.platform.config.prefs + prefs.set_string("qtgui", "qss", file_paths[0]) + prefs.save() except Exception as e: Messages.send("Failed to save QSS preference: " + str(e)) elif action == Actions.FLOW_GRAPH_CLOSE: - self.main_window.close_page() + main.close_page() elif action == Actions.FLOW_GRAPH_SAVE: #read-only or undefined file path, do save-as - if self.get_page().get_read_only() or not self.get_page().get_file_path(): + if page.get_read_only() or not page.get_file_path(): Actions.FLOW_GRAPH_SAVE_AS() #otherwise try to save else: try: - ParseXML.to_file(self.get_flow_graph().export_data(), self.get_page().get_file_path()) - self.get_flow_graph().grc_file_path = self.get_page().get_file_path() - self.get_page().set_saved(True) + ParseXML.to_file(flow_graph.export_data(), page.get_file_path()) + flow_graph.grc_file_path = page.get_file_path() + page.set_saved(True) except IOError: - Messages.send_fail_save(self.get_page().get_file_path()) - self.get_page().set_saved(False) + Messages.send_fail_save(page.get_file_path()) + page.set_saved(False) elif action == Actions.FLOW_GRAPH_SAVE_AS: - file_path = SaveFlowGraphFileDialog(self.get_page().get_file_path()).run() + file_path = SaveFlowGraphFileDialog(page.get_file_path()).run() if file_path is not None: - self.get_page().set_file_path(file_path) + page.set_file_path(file_path) Actions.FLOW_GRAPH_SAVE() Preferences.add_recent_file(file_path) - self.main_window.tool_bar.refresh_submenus() - self.main_window.menu_bar.refresh_submenus() + main.tool_bar.refresh_submenus() + main.menu_bar.refresh_submenus() elif action == Actions.FLOW_GRAPH_SCREEN_CAPTURE: - file_path = SaveImageFileDialog(self.get_page().get_file_path()).run() + file_path, background_transparent = SaveScreenShotDialog(page.get_file_path()).run() if file_path is not None: - pixbuf = self.get_flow_graph().get_drawing_area().get_pixbuf() - pixbuf.save(file_path, IMAGE_FILE_EXTENSION[1:]) + pixbuf = flow_graph.get_drawing_area().get_screenshot(background_transparent) + pixbuf.save(file_path, Constants.IMAGE_FILE_EXTENSION[1:]) ################################################## # Gen/Exec/Stop ################################################## elif action == Actions.FLOW_GRAPH_GEN: - if not self.get_page().get_proc(): - if not self.get_page().get_saved() or not self.get_page().get_file_path(): + if not page.get_proc(): + if not page.get_saved() or not page.get_file_path(): Actions.FLOW_GRAPH_SAVE() #only save if file path missing or not saved - if self.get_page().get_saved() and self.get_page().get_file_path(): - generator = self.get_page().get_generator() + if page.get_saved() and page.get_file_path(): + generator = page.get_generator() try: Messages.send_start_gen(generator.get_file_path()) generator.write() - except Exception,e: Messages.send_fail_gen(e) - else: self.generator = None + except Exception as e: + Messages.send_fail_gen(e) + else: + self.generator = None elif action == Actions.FLOW_GRAPH_EXEC: - if not self.get_page().get_proc(): + if not page.get_proc(): Actions.FLOW_GRAPH_GEN() - if Preferences.xterm_missing() != XTERM_EXECUTABLE: - if not os.path.exists(XTERM_EXECUTABLE): - Dialogs.MissingXTermDialog(XTERM_EXECUTABLE) - Preferences.xterm_missing(XTERM_EXECUTABLE) - if self.get_page().get_saved() and self.get_page().get_file_path(): - ExecFlowGraphThread(self) + xterm = self.platform.config.xterm_executable + if Preferences.xterm_missing() != xterm: + if not os.path.exists(xterm): + Dialogs.MissingXTermDialog(xterm) + Preferences.xterm_missing(xterm) + if page.get_saved() and page.get_file_path(): + Executor.ExecFlowGraphThread( + flow_graph_page=page, + xterm_executable=xterm, + callback=self.update_exec_stop + ) elif action == Actions.FLOW_GRAPH_KILL: - if self.get_page().get_proc(): + if page.get_proc(): try: - self.get_page().get_proc().kill() + page.get_proc().kill() except: - print "could not kill process: %d" % self.get_page().get_proc().pid + print "could not kill process: %d" % page.get_proc().pid elif action == Actions.PAGE_CHANGE: # pass and run the global actions pass elif action == Actions.RELOAD_BLOCKS: - self.platform.load_blocks() - self.main_window.btwin.clear() - self.platform.load_block_tree(self.main_window.btwin) - Actions.XML_PARSER_ERRORS_DISPLAY.set_sensitive(bool(ParseXML.xml_failures)) + self.platform.build_block_library() + main.btwin.repopulate() + Actions.XML_PARSER_ERRORS_DISPLAY.set_sensitive(bool( + ParseXML.xml_failures)) Messages.send_xml_errors_if_any(ParseXML.xml_failures) # Force a redraw of the graph, by getting the current state and re-importing it - self.main_window.update_pages() + main.update_pages() elif action == Actions.FIND_BLOCKS: - self.main_window.btwin.show() - self.main_window.btwin.search_entry.show() - self.main_window.btwin.search_entry.grab_focus() + main.update_panel_visibility(main.BLOCKS, True) + main.btwin.search_entry.show() + main.btwin.search_entry.grab_focus() elif action == Actions.OPEN_HIER: - for b in self.get_flow_graph().get_selected_blocks(): + for b in flow_graph.get_selected_blocks(): if b._grc_source: - self.main_window.new_page(b._grc_source, show=True) + main.new_page(b._grc_source, show=True) elif action == Actions.BUSSIFY_SOURCES: n = {'name':'bus', 'type':'bus'} - for b in self.get_flow_graph().get_selected_blocks(): + for b in flow_graph.get_selected_blocks(): b.bussify(n, 'source') - self.get_flow_graph()._old_selected_port = None - self.get_flow_graph()._new_selected_port = None + flow_graph._old_selected_port = None + flow_graph._new_selected_port = None Actions.ELEMENT_CREATE() elif action == Actions.BUSSIFY_SINKS: n = {'name':'bus', 'type':'bus'} - for b in self.get_flow_graph().get_selected_blocks(): + for b in flow_graph.get_selected_blocks(): b.bussify(n, 'sink') - self.get_flow_graph()._old_selected_port = None - self.get_flow_graph()._new_selected_port = None + flow_graph._old_selected_port = None + flow_graph._new_selected_port = None Actions.ELEMENT_CREATE() elif action == Actions.TOOLS_RUN_FDESIGN: @@ -598,15 +648,22 @@ class ActionHandler: ################################################## # Global Actions for all States ################################################## - selected_block = self.get_flow_graph().get_selected_block() - selected_blocks = self.get_flow_graph().get_selected_blocks() + page = main.get_page() # page and flowgraph might have changed + flow_graph = page.get_flow_graph() if page else None + + selected_blocks = flow_graph.get_selected_blocks() + selected_block = selected_blocks[0] if selected_blocks else None #update general buttons - Actions.ERRORS_WINDOW_DISPLAY.set_sensitive(not self.get_flow_graph().is_valid()) - Actions.ELEMENT_DELETE.set_sensitive(bool(self.get_flow_graph().get_selected_elements())) + Actions.ERRORS_WINDOW_DISPLAY.set_sensitive(not flow_graph.is_valid()) + Actions.ELEMENT_DELETE.set_sensitive(bool(flow_graph.get_selected_elements())) Actions.BLOCK_PARAM_MODIFY.set_sensitive(bool(selected_block)) Actions.BLOCK_ROTATE_CCW.set_sensitive(bool(selected_blocks)) Actions.BLOCK_ROTATE_CW.set_sensitive(bool(selected_blocks)) + #update alignment options + for act in Actions.BLOCK_ALIGNMENTS: + if act: + act.set_sensitive(len(selected_blocks) > 1) #update cut/copy/paste Actions.BLOCK_CUT.set_sensitive(bool(selected_blocks)) Actions.BLOCK_COPY.set_sensitive(bool(selected_blocks)) @@ -631,17 +688,17 @@ class ActionHandler: #set the exec and stop buttons self.update_exec_stop() #saved status - Actions.FLOW_GRAPH_SAVE.set_sensitive(not self.get_page().get_saved()) - self.main_window.update() + Actions.FLOW_GRAPH_SAVE.set_sensitive(not page.get_saved()) + main.update() try: #set the size of the flow graph area (if changed) - new_size = (self.get_flow_graph().get_option('window_size') or - DEFAULT_CANVAS_SIZE) - if self.get_flow_graph().get_size() != tuple(new_size): - self.get_flow_graph().set_size(*new_size) + new_size = (flow_graph.get_option('window_size') or + self.platform.config.default_canvas_size) + if flow_graph.get_size() != tuple(new_size): + flow_graph.set_size(*new_size) except: pass #draw the flow graph - self.get_flow_graph().update_selected() - self.get_flow_graph().queue_draw() + flow_graph.update_selected() + flow_graph.queue_draw() return True #action was handled def update_exec_stop(self): @@ -649,52 +706,7 @@ class ActionHandler: Update the exec and stop buttons. Lock and unlock the mutex for race conditions with exec flow graph threads. """ - sensitive = self.get_flow_graph().is_valid() and not self.get_page().get_proc() + sensitive = self.main_window.get_flow_graph().is_valid() and not self.get_page().get_proc() Actions.FLOW_GRAPH_GEN.set_sensitive(sensitive) Actions.FLOW_GRAPH_EXEC.set_sensitive(sensitive) Actions.FLOW_GRAPH_KILL.set_sensitive(self.get_page().get_proc() is not None) - -class ExecFlowGraphThread(Thread): - """Execute the flow graph as a new process and wait on it to finish.""" - - def __init__ (self, action_handler): - """ - ExecFlowGraphThread constructor. - - Args: - action_handler: an instance of an ActionHandler - """ - Thread.__init__(self) - self.update_exec_stop = action_handler.update_exec_stop - self.flow_graph = action_handler.get_flow_graph() - #store page and dont use main window calls in run - self.page = action_handler.get_page() - #get the popen - try: - self.p = self.page.get_generator().get_popen() - self.page.set_proc(self.p) - #update - self.update_exec_stop() - self.start() - except Exception, e: - Messages.send_verbose_exec(str(e)) - Messages.send_end_exec() - - def run(self): - """ - Wait on the executing process by reading from its stdout. - Use gobject.idle_add when calling functions that modify gtk objects. - """ - #handle completion - r = "\n" - while r: - gobject.idle_add(Messages.send_verbose_exec, r) - r = os.read(self.p.stdout.fileno(), 1024) - self.p.poll() - gobject.idle_add(self.done) - - def done(self): - """Perform end of execution tasks.""" - Messages.send_end_exec(self.p.returncode) - self.page.set_proc(None) - self.update_exec_stop() diff --git a/grc/gui/Actions.py b/grc/gui/Actions.py index 9b32b3e601..9b2af36b93 100644 --- a/grc/gui/Actions.py +++ b/grc/gui/Actions.py @@ -92,6 +92,7 @@ class _ActionBase(object): self.set_accel_group(get_accel_group()) self.set_accel_path(accel_path) gtk.accel_map_add_entry(accel_path, keyval, mod_mask) + self.args = None def __str__(self): """ @@ -105,10 +106,11 @@ class _ActionBase(object): def __repr__(self): return str(self) - def __call__(self): + def __call__(self, *args): """ Emit the activate signal when called with (). """ + self.args = args self.emit('activate') @@ -171,6 +173,7 @@ class ToggleAction(gtk.ToggleAction, _ActionBase): ######################################################################## PAGE_CHANGE = Action() EXTERNAL_UPDATE = Action() +VARIABLE_EDITOR_UPDATE = Action() FLOW_GRAPH_NEW = Action( label='_New', tooltip='Create a new flow graph', @@ -226,6 +229,12 @@ FLOW_GRAPH_REDO = Action( keypresses=(gtk.keysyms.y, gtk.gdk.CONTROL_MASK), ) NOTHING_SELECT = Action() +SELECT_ALL = Action( + label='Select _All', + tooltip='Select all blocks and connections in the flow graph', + stock_id=gtk.STOCK_SELECT_ALL, + keypresses=(gtk.keysyms.a, gtk.gdk.CONTROL_MASK), +) ELEMENT_SELECT = Action() ELEMENT_CREATE = Action() ELEMENT_DELETE = Action( @@ -247,6 +256,45 @@ BLOCK_ROTATE_CW = Action( stock_id=gtk.STOCK_GO_FORWARD, keypresses=(gtk.keysyms.Right, NO_MODS_MASK), ) +BLOCK_VALIGN_TOP = Action( + label='Vertical Align Top', + tooltip='Align tops of selected blocks', + keypresses=(gtk.keysyms.t, gtk.gdk.SHIFT_MASK), +) +BLOCK_VALIGN_MIDDLE = Action( + label='Vertical Align Middle', + tooltip='Align centers of selected blocks vertically', + keypresses=(gtk.keysyms.m, gtk.gdk.SHIFT_MASK), +) +BLOCK_VALIGN_BOTTOM = Action( + label='Vertical Align Bottom', + tooltip='Align bottoms of selected blocks', + keypresses=(gtk.keysyms.b, gtk.gdk.SHIFT_MASK), +) +BLOCK_HALIGN_LEFT = Action( + label='Horizontal Align Left', + tooltip='Align left edges of blocks selected blocks', + keypresses=(gtk.keysyms.l, gtk.gdk.SHIFT_MASK), +) +BLOCK_HALIGN_CENTER = Action( + label='Horizontal Align Center', + tooltip='Align centers of selected blocks horizontally', + keypresses=(gtk.keysyms.c, gtk.gdk.SHIFT_MASK), +) +BLOCK_HALIGN_RIGHT = Action( + label='Horizontal Align Right', + tooltip='Align right edges of selected blocks', + keypresses=(gtk.keysyms.r, gtk.gdk.SHIFT_MASK), +) +BLOCK_ALIGNMENTS = [ + BLOCK_VALIGN_TOP, + BLOCK_VALIGN_MIDDLE, + BLOCK_VALIGN_BOTTOM, + None, + BLOCK_HALIGN_LEFT, + BLOCK_HALIGN_CENTER, + BLOCK_HALIGN_RIGHT, +] BLOCK_PARAM_MODIFY = Action( label='_Properties', tooltip='Modify params for the selected block', @@ -282,6 +330,26 @@ TOGGLE_HIDE_DISABLED_BLOCKS = ToggleAction( stock_id=gtk.STOCK_MISSING_IMAGE, keypresses=(gtk.keysyms.d, gtk.gdk.CONTROL_MASK), ) +TOGGLE_HIDE_VARIABLES = ToggleAction( + label='Hide Variables', + tooltip='Hide all variable blocks', + preference_name='hide_variables', + default=False, +) +TOGGLE_FLOW_GRAPH_VAR_EDITOR = ToggleAction( + label='Show _Variable Editor', + tooltip='Show the variable editor. Modify variables and imports in this flow graph', + stock_id=gtk.STOCK_EDIT, + default=True, + keypresses=(gtk.keysyms.e, gtk.gdk.CONTROL_MASK), + preference_name='variable_editor_visable', +) +TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR = ToggleAction( + label='Move the Variable Editor to the Sidebar', + tooltip='Move the variable editor to the sidebar', + default=False, + preference_name='variable_editor_sidebar', +) TOGGLE_AUTO_HIDE_PORT_LABELS = ToggleAction( label='Auto-Hide _Port Labels', tooltip='Automatically hide port labels', @@ -334,11 +402,11 @@ ERRORS_WINDOW_DISPLAY = Action( tooltip='View flow graph errors', stock_id=gtk.STOCK_DIALOG_ERROR, ) -TOGGLE_REPORTS_WINDOW = ToggleAction( - label='Show _Reports Panel', - tooltip='Toggle visibility of the Report widget', +TOGGLE_CONSOLE_WINDOW = ToggleAction( + label='Show _Console Panel', + tooltip='Toggle visibility of the console', keypresses=(gtk.keysyms.r, gtk.gdk.CONTROL_MASK), - preference_name='reports_window_visible' + preference_name='console_window_visible' ) TOGGLE_BLOCKS_WINDOW = ToggleAction( label='Show _Block Tree Panel', @@ -347,8 +415,8 @@ TOGGLE_BLOCKS_WINDOW = ToggleAction( preference_name='blocks_window_visible' ) TOGGLE_SCROLL_LOCK = ToggleAction( - label='Reports Scroll _Lock', - tooltip='Toggle scroll lock for the report window', + label='Console Scroll _Lock', + tooltip='Toggle scroll lock for the console window', preference_name='scroll_lock' ) ABOUT_WINDOW_DISPLAY = Action( @@ -386,7 +454,7 @@ FLOW_GRAPH_KILL = Action( keypresses=(gtk.keysyms.F7, NO_MODS_MASK), ) FLOW_GRAPH_SCREEN_CAPTURE = Action( - label='Sc_reen Capture', + label='Screen Ca_pture', tooltip='Create a screen capture of the flow graph', stock_id=gtk.STOCK_PRINT, keypresses=(gtk.keysyms.Print, NO_MODS_MASK), @@ -415,14 +483,14 @@ FIND_BLOCKS = Action( keypresses=(gtk.keysyms.f, gtk.gdk.CONTROL_MASK, gtk.keysyms.slash, NO_MODS_MASK), ) -CLEAR_REPORTS = Action( - label='_Clear Reports', - tooltip='Clear Reports', +CLEAR_CONSOLE = Action( + label='_Clear Console', + tooltip='Clear Console', stock_id=gtk.STOCK_CLEAR, ) -SAVE_REPORTS = Action( - label='_Save Reports', - tooltip='Save Reports', +SAVE_CONSOLE = Action( + label='_Save Console', + tooltip='Save Console', stock_id=gtk.STOCK_SAVE, ) OPEN_HIER = Action( diff --git a/grc/gui/Bars.py b/grc/gui/Bars.py index 8b389a797c..a4819b973c 100644 --- a/grc/gui/Bars.py +++ b/grc/gui/Bars.py @@ -31,6 +31,7 @@ TOOLBAR_LIST = ( Actions.FLOW_GRAPH_SAVE, Actions.FLOW_GRAPH_CLOSE, None, + Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR, Actions.FLOW_GRAPH_SCREEN_CAPTURE, None, Actions.BLOCK_CUT, @@ -82,9 +83,11 @@ MENU_BAR_LIST = ( Actions.BLOCK_COPY, Actions.BLOCK_PASTE, Actions.ELEMENT_DELETE, + Actions.SELECT_ALL, None, Actions.BLOCK_ROTATE_CCW, Actions.BLOCK_ROTATE_CW, + (gtk.Action('Align', '_Align', None, None), Actions.BLOCK_ALIGNMENTS), None, Actions.BLOCK_ENABLE, Actions.BLOCK_DISABLE, @@ -95,10 +98,14 @@ MENU_BAR_LIST = ( (gtk.Action('View', '_View', None, None), [ Actions.TOGGLE_BLOCKS_WINDOW, None, - Actions.TOGGLE_REPORTS_WINDOW, + Actions.TOGGLE_CONSOLE_WINDOW, Actions.TOGGLE_SCROLL_LOCK, - Actions.SAVE_REPORTS, - Actions.CLEAR_REPORTS, + Actions.SAVE_CONSOLE, + Actions.CLEAR_CONSOLE, + None, + Actions.TOGGLE_HIDE_VARIABLES, + Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR, + Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR, None, Actions.TOGGLE_HIDE_DISABLED_BLOCKS, Actions.TOGGLE_AUTO_HIDE_PORT_LABELS, diff --git a/grc/gui/Block.py b/grc/gui/Block.py index 67b80695fa..55c8805fae 100644 --- a/grc/gui/Block.py +++ b/grc/gui/Block.py @@ -17,22 +17,19 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ -from Element import Element -import Utils -import Colors -from .. base import odict -from .. python.Param import num_to_str -from Constants import BORDER_PROXIMITY_SENSITIVITY -from Constants import ( - BLOCK_LABEL_PADDING, PORT_SPACING, PORT_SEPARATION, LABEL_SEPARATION, - PORT_BORDER_SEPARATION, POSSIBLE_ROTATIONS, BLOCK_FONT, PARAM_FONT -) -import Actions import pygtk pygtk.require('2.0') import gtk import pango +from . import Actions, Colors, Utils, Constants + +from . Element import Element +from ..core.Param import num_to_str +from ..core.utils import odict +from ..core.utils.complexity import calculate_flowgraph_complexity +from ..core.Block import Block as _Block + BLOCK_MARKUP_TMPL="""\ #set $foreground = $block.is_valid() and 'black' or 'red' <span foreground="$foreground" font_desc="$font"><b>$encode($block.get_name())</b></span>""" @@ -52,15 +49,16 @@ COMMENT_COMPLEXITY_MARKUP_TMPL="""\ """ - -class Block(Element): +class Block(Element, _Block): """The graphical signal block.""" - def __init__(self): + def __init__(self, flow_graph, n): """ Block contructor. Add graphics related params to the block. """ + _Block.__init__(self, flow_graph, n) + self.W = 0 self.H = 0 #add the position param @@ -95,18 +93,19 @@ class Block(Element): Returns: the coordinate tuple (x, y) or (0, 0) if failure """ + proximity = Constants.BORDER_PROXIMITY_SENSITIVITY try: #should evaluate to tuple coor = eval(self.get_param('_coordinate').get_value()) x, y = map(int, coor) fgW,fgH = self.get_parent().get_size() if x <= 0: x = 0 - elif x >= fgW - BORDER_PROXIMITY_SENSITIVITY: - x = fgW - BORDER_PROXIMITY_SENSITIVITY + elif x >= fgW - proximity: + x = fgW - proximity if y <= 0: y = 0 - elif y >= fgH - BORDER_PROXIMITY_SENSITIVITY: - y = fgH - BORDER_PROXIMITY_SENSITIVITY + elif y >= fgH - proximity: + y = fgH - proximity return (x, y) except: self.set_coordinate((0, 0)) @@ -135,10 +134,10 @@ class Block(Element): delta_coor: requested delta coordinate (dX, dY) to move Returns: - The delta coordinate possible to move while keeping the block on the canvas + The delta coordinate possible to move while keeping the block on the canvas or the input (dX, dY) on failure """ - dX, dY = delta_coor + dX, dY = delta_coor try: fgW, fgH = self.get_parent().get_size() @@ -147,7 +146,7 @@ class Block(Element): sW, sH = self.W, self.H else: sW, sH = self.H, self.W - + if x + dX < 0: dX = -x elif dX + x + sW >= fgW: @@ -159,7 +158,7 @@ class Block(Element): except: pass - return ( dX, dY ) + return ( dX, dY ) def get_rotation(self): """ @@ -172,8 +171,8 @@ class Block(Element): rotation = eval(self.get_param('_rotation').get_value()) return int(rotation) except: - self.set_rotation(POSSIBLE_ROTATIONS[0]) - return POSSIBLE_ROTATIONS[0] + self.set_rotation(Constants.POSSIBLE_ROTATIONS[0]) + return Constants.POSSIBLE_ROTATIONS[0] def set_rotation(self, rot): """ @@ -193,7 +192,7 @@ class Block(Element): def create_labels(self): """Create the labels for the signal block.""" Element.create_labels(self) - self._bg_color = self.is_dummy_block() and Colors.MISSING_BLOCK_BACKGROUND_COLOR or \ + self._bg_color = self.is_dummy_block and Colors.MISSING_BLOCK_BACKGROUND_COLOR or \ self.get_bypassed() and Colors.BLOCK_BYPASSED_COLOR or \ self.get_enabled() and Colors.BLOCK_ENABLED_COLOR or Colors.BLOCK_DISABLED_COLOR @@ -201,23 +200,24 @@ class Block(Element): #create the main layout layout = gtk.DrawingArea().create_pango_layout('') layouts.append(layout) - layout.set_markup(Utils.parse_template(BLOCK_MARKUP_TMPL, block=self, font=BLOCK_FONT)) + layout.set_markup(Utils.parse_template(BLOCK_MARKUP_TMPL, block=self, font=Constants.BLOCK_FONT)) self.label_width, self.label_height = layout.get_pixel_size() #display the params - if self.is_dummy_block(): + if self.is_dummy_block: markups = [ - '<span foreground="black" font_desc="{font}"><b>key: </b>{key}</span>'.format(font=PARAM_FONT, key=self._key) + '<span foreground="black" font_desc="{font}"><b>key: </b>{key}</span>' + ''.format(font=Constants.PARAM_FONT, key=self._key) ] else: markups = [param.get_markup() for param in self.get_params() if param.get_hide() not in ('all', 'part')] if markups: layout = gtk.DrawingArea().create_pango_layout('') - layout.set_spacing(LABEL_SEPARATION*pango.SCALE) + layout.set_spacing(Constants.LABEL_SEPARATION * pango.SCALE) layout.set_markup('\n'.join(markups)) layouts.append(layout) w, h = layout.get_pixel_size() self.label_width = max(w, self.label_width) - self.label_height += h + LABEL_SEPARATION + self.label_height += h + Constants.LABEL_SEPARATION width = self.label_width height = self.label_height #setup the pixmap @@ -232,33 +232,35 @@ class Block(Element): if i == 0: w_off = (width-w)/2 else: w_off = 0 pixmap.draw_layout(gc, w_off, h_off, layout) - h_off = h + h_off + LABEL_SEPARATION + h_off = h + h_off + Constants.LABEL_SEPARATION #create vertical and horizontal pixmaps self.horizontal_label = pixmap if self.is_vertical(): self.vertical_label = self.get_parent().new_pixmap(height, width) Utils.rotate_pixmap(gc, self.horizontal_label, self.vertical_label) #calculate width and height needed - self.W = self.label_width + 2 * BLOCK_LABEL_PADDING + W = self.label_width + 2 * Constants.BLOCK_LABEL_PADDING def get_min_height_for_ports(): visible_ports = filter(lambda p: not p.get_hide(), ports) - H = 2*PORT_BORDER_SEPARATION + len(visible_ports) * PORT_SEPARATION - if visible_ports: H -= ports[0].H - return H - self.H = max( + min_height = 2*Constants.PORT_BORDER_SEPARATION + len(visible_ports) * Constants.PORT_SEPARATION + if visible_ports: + min_height -= ports[0].H + return min_height + H = max( [ # labels - self.label_height + 2 * BLOCK_LABEL_PADDING + self.label_height + 2 * Constants.BLOCK_LABEL_PADDING ] + [ # ports get_min_height_for_ports() for ports in (self.get_sources_gui(), self.get_sinks_gui()) ] + [ # bus ports only - 2 * PORT_BORDER_SEPARATION + - sum([port.H + PORT_SPACING for port in ports if port.get_type() == 'bus']) - PORT_SPACING + 2 * Constants.PORT_BORDER_SEPARATION + + sum([port.H + Constants.PORT_SPACING for port in ports if port.get_type() == 'bus']) - Constants.PORT_SPACING for ports in (self.get_sources_gui(), self.get_sinks_gui()) ] ) + self.W, self.H = Utils.align_to_grid((W, H)) self.has_busses = [ any(port.get_type() == 'bus' for port in ports) for ports in (self.get_sources_gui(), self.get_sinks_gui()) @@ -271,19 +273,20 @@ class Block(Element): # Show the flowgraph complexity on the top block if enabled if Actions.TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY.get_active() and self.get_key() == "options": - complexity = "Complexity: {}bal".format(num_to_str(self.get_parent().get_complexity())) + complexity = calculate_flowgraph_complexity(self.get_parent()) + complexity = "Complexity: {}bal".format(num_to_str(complexity)) layout = gtk.DrawingArea().create_pango_layout('') layout.set_markup(Utils.parse_template(COMMENT_COMPLEXITY_MARKUP_TMPL, block=self, comment=comment, complexity=complexity, - font=BLOCK_FONT)) + font=Constants.BLOCK_FONT)) # Setup the pixel map. Make sure that layout not empty width, height = layout.get_pixel_size() if width and height: - padding = BLOCK_LABEL_PADDING + padding = Constants.BLOCK_LABEL_PADDING pixmap = self.get_parent().new_pixmap(width + 2 * padding, height + 2 * padding) gc = pixmap.new_gc() @@ -311,13 +314,13 @@ class Block(Element): Element.draw( self, gc, window, bg_color=self._bg_color, border_color=self.is_highlighted() and Colors.HIGHLIGHT_COLOR or - self.is_dummy_block() and Colors.MISSING_BLOCK_BORDER_COLOR or Colors.BORDER_COLOR, + self.is_dummy_block and Colors.MISSING_BLOCK_BORDER_COLOR or Colors.BORDER_COLOR, ) #draw label image if self.is_horizontal(): - window.draw_drawable(gc, self.horizontal_label, 0, 0, x+BLOCK_LABEL_PADDING, y+(self.H-self.label_height)/2, -1, -1) + window.draw_drawable(gc, self.horizontal_label, 0, 0, x+Constants.BLOCK_LABEL_PADDING, y+(self.H-self.label_height)/2, -1, -1) elif self.is_vertical(): - window.draw_drawable(gc, self.vertical_label, 0, 0, x+(self.H-self.label_height)/2, y+BLOCK_LABEL_PADDING, -1, -1) + window.draw_drawable(gc, self.vertical_label, 0, 0, x+(self.H-self.label_height)/2, y+Constants.BLOCK_LABEL_PADDING, -1, -1) def what_is_selected(self, coor, coor_m=None): """ @@ -339,6 +342,10 @@ class Block(Element): if not self._comment_pixmap: return x, y = self.get_coordinate() - y += self.H if self.is_horizontal() else self.W - window.draw_drawable(gc, self._comment_pixmap, 0, 0, x, - y + BLOCK_LABEL_PADDING, -1, -1) + + if self.is_horizontal(): + y += self.H + Constants.BLOCK_LABEL_PADDING + else: + x += self.H + Constants.BLOCK_LABEL_PADDING + + window.draw_drawable(gc, self._comment_pixmap, 0, 0, x, y, -1, -1) diff --git a/grc/gui/BlockTreeWindow.py b/grc/gui/BlockTreeWindow.py index 6b3ebf7807..f49eb6c4fe 100644 --- a/grc/gui/BlockTreeWindow.py +++ b/grc/gui/BlockTreeWindow.py @@ -23,23 +23,59 @@ import gtk import gobject from . import Actions, Utils -from .Constants import DEFAULT_BLOCKS_WINDOW_WIDTH, DND_TARGETS +from . import Constants + NAME_INDEX = 0 KEY_INDEX = 1 DOC_INDEX = 2 DOC_MARKUP_TMPL = """\ -#if $doc -#if len($doc) > 1000 -#set $doc = $doc[:1000] + '...' +#set $docs = [] +#if $doc.get('') + #set $docs += $doc.pop('').splitlines() + [''] #end if -$encode($doc)#slurp -#else +#for b, d in $doc.iteritems() + #set $docs += ['--- {0} ---'.format(b)] + d.splitlines() + [''] +#end for +#set $len_out = 0 +#for $n, $line in $enumerate($docs[:-1]) +#if $n + +#end if +$encode($line)#slurp +#set $len_out += $len($line) +#if $n > 10 or $len_out > 500 + +...#slurp +#break +#end if +#end for +#if $len_out == 0 undocumented#slurp #end if""" -CAT_MARKUP_TMPL = """Category: $cat""" +CAT_MARKUP_TMPL = """ +#set $name = $cat[-1] +#if len($cat) > 1 +Category: $cat[-1] +## +#elif $name == 'Core' +Module: Core + +This subtree is meant for blocks included with GNU Radio (in-tree). +## +#elif $name == $default_module +This subtree holds all blocks (from OOT modules) that specify no module name. \ +The module name is the root category enclosed in square brackets. + +Please consider contacting OOT module maintainer for any block in here \ +and kindly ask to update their GRC Block Descriptions or Block Tree to include a module name. +#else +Module: $name +## +#end if +""".strip() class BlockTreeWindow(gtk.VBox): @@ -98,57 +134,69 @@ class BlockTreeWindow(gtk.VBox): column.set_sort_column_id(0) self.treestore.set_sort_column_id(0, gtk.SORT_ASCENDING) # setup drag and drop - self.treeview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, DND_TARGETS, gtk.gdk.ACTION_COPY) + self.treeview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, Constants.DND_TARGETS, gtk.gdk.ACTION_COPY) self.treeview.connect('drag-data-get', self._handle_drag_get_data) # make the scrolled window to hold the tree view scrolled_window = gtk.ScrolledWindow() scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) scrolled_window.add_with_viewport(self.treeview) - scrolled_window.set_size_request(DEFAULT_BLOCKS_WINDOW_WIDTH, -1) + scrolled_window.set_size_request(Constants.DEFAULT_BLOCKS_WINDOW_WIDTH, -1) self.pack_start(scrolled_window) # map categories to iters, automatic mapping for root self._categories = {tuple(): None} self._categories_search = {tuple(): None} - # add blocks and categories - self.platform.load_block_tree(self) self.platform.block_docstrings_loaded_callback = self.update_docs + self.repopulate() def clear(self): - self.treestore.clear(); - self._categories = {tuple(): None} + self.treestore.clear() + self._categories = {(): None} + + def repopulate(self): + self.clear() + for block in self.platform.blocks.itervalues(): + if block.category: + self.add_block(block) + self.expand_module_in_tree() + + def expand_module_in_tree(self, module_name='Core'): + self.treeview.collapse_all() + core_module_iter = self._categories.get((module_name,)) + if core_module_iter: + self.treeview.expand_row(self.treestore.get_path(core_module_iter), False) ############################################################ ## Block Tree Methods ############################################################ - def add_block(self, category, block=None, treestore=None, categories=None): + def add_block(self, block, treestore=None, categories=None): """ Add a block with category to this selection window. Add only the category when block is None. Args: - category: the category list or path string block: the block object or None """ - if treestore is None: treestore = self.treestore - if categories is None: categories = self._categories + treestore = treestore or self.treestore + categories = categories or self._categories + + category = tuple(filter(str, block.category)) # tuple is hashable, remove empty cats - if isinstance(category, (str, unicode)): category = category.split('/') - category = tuple(filter(lambda x: x, category)) # tuple is hashable # add category and all sub categories - for i, cat_name in enumerate(category): - sub_category = category[:i+1] - if sub_category not in categories: - iter = treestore.insert_before(categories[sub_category[:-1]], None) - treestore.set_value(iter, NAME_INDEX, '[ %s ]'%cat_name) - treestore.set_value(iter, KEY_INDEX, '') - treestore.set_value(iter, DOC_INDEX, Utils.parse_template(CAT_MARKUP_TMPL, cat=cat_name)) - categories[sub_category] = iter + for level, parent_cat_name in enumerate(category, 1): + parent_category = category[:level] + if parent_category not in categories: + iter_ = treestore.insert_before(categories[parent_category[:-1]], None) + treestore.set_value(iter_, NAME_INDEX, parent_cat_name) + treestore.set_value(iter_, KEY_INDEX, '') + treestore.set_value(iter_, DOC_INDEX, Utils.parse_template( + CAT_MARKUP_TMPL, cat=parent_category, default_module=Constants.DEFAULT_BLOCK_MODULE_NAME)) + categories[parent_category] = iter_ + # add block - if block is None: return - iter = treestore.insert_before(categories[category], None) - treestore.set_value(iter, NAME_INDEX, block.get_name()) - treestore.set_value(iter, KEY_INDEX, block.get_key()) - treestore.set_value(iter, DOC_INDEX, Utils.parse_template(DOC_MARKUP_TMPL, doc=block.get_doc())) + iter_ = treestore.insert_before(categories[category], None) + treestore.set_value(iter_, NAME_INDEX, block.get_name()) + treestore.set_value(iter_, KEY_INDEX, block.get_key()) + treestore.set_value(iter_, DOC_INDEX, Utils.parse_template(DOC_MARKUP_TMPL, doc=block.get_doc())) def update_docs(self): """Update the documentation column of every block""" @@ -157,7 +205,7 @@ class BlockTreeWindow(gtk.VBox): if model.iter_has_child(iter_): return # category node, no doc string key = model.get_value(iter_, KEY_INDEX) - block = self.platform.get_block(key) + block = self.platform.blocks[key] doc = Utils.parse_template(DOC_MARKUP_TMPL, doc=block.get_doc()) model.set_value(iter_, DOC_INDEX, doc) @@ -208,16 +256,15 @@ class BlockTreeWindow(gtk.VBox): key = widget.get_text().lower() if not key: self.treeview.set_model(self.treestore) - self.treeview.collapse_all() + self.expand_module_in_tree() else: - blocks = self.get_flow_graph().get_parent().get_blocks() - matching_blocks = filter(lambda b: key in b.get_key().lower() or key in b.get_name().lower(), blocks) + matching_blocks = filter(lambda b: key in b.get_key().lower() or key in b.get_name().lower(), + self.platform.blocks.values()) self.treestore_search.clear() self._categories_search = {tuple(): None} for block in matching_blocks: - self.add_block(block.get_category() or 'None', block, - self.treestore_search, self._categories_search) + self.add_block(block, self.treestore_search, self._categories_search) self.treeview.set_model(self.treestore_search) self.treeview.expand_all() diff --git a/grc/gui/CMakeLists.txt b/grc/gui/CMakeLists.txt index 99140df7c4..aa9592b351 100644 --- a/grc/gui/CMakeLists.txt +++ b/grc/gui/CMakeLists.txt @@ -17,34 +17,10 @@ # the Free Software Foundation, Inc., 51 Franklin Street, # Boston, MA 02110-1301, USA. -######################################################################## -GR_PYTHON_INSTALL(FILES - external_editor.py - Block.py - Colors.py - Constants.py - Connection.py - Element.py - FlowGraph.py - Param.py - Platform.py - Port.py - Utils.py - ActionHandler.py - Actions.py - Bars.py - BlockTreeWindow.py - Dialogs.py - DrawingArea.py - FileDialogs.py - MainWindow.py - Messages.py - NotebookPage.py - ParserErrorsDialog.py - PropsDialog.py - Preferences.py - StateCache.py - __init__.py +file(GLOB py_files "*.py") + +GR_PYTHON_INSTALL( + FILES ${py_files} DESTINATION ${GR_PYTHON_DIR}/gnuradio/grc/gui COMPONENT "grc" ) diff --git a/grc/gui/Colors.py b/grc/gui/Colors.py index 52c95e8edf..d322afa410 100644 --- a/grc/gui/Colors.py +++ b/grc/gui/Colors.py @@ -33,12 +33,13 @@ try: PARAM_ENTRY_TEXT_COLOR = get_color('black') ENTRYENUM_CUSTOM_COLOR = get_color('#EEEEEE') #flow graph color constants - FLOWGRAPH_BACKGROUND_COLOR = get_color('#FFF9FF') + FLOWGRAPH_BACKGROUND_COLOR = get_color('#FFFFFF') COMMENT_BACKGROUND_COLOR = get_color('#F3F3F3') + FLOWGRAPH_EDGE_COLOR = COMMENT_BACKGROUND_COLOR #block color constants BLOCK_ENABLED_COLOR = get_color('#F1ECFF') BLOCK_DISABLED_COLOR = get_color('#CCCCCC') - BLOCK_BYPASSED_COLOR = get_color('#FFFFE6') + BLOCK_BYPASSED_COLOR = get_color('#F4FF81') #connection color constants CONNECTION_ENABLED_COLOR = get_color('black') CONNECTION_DISABLED_COLOR = get_color('#BBBBBB') diff --git a/grc/gui/Config.py b/grc/gui/Config.py new file mode 100644 index 0000000000..9b0c5d4afe --- /dev/null +++ b/grc/gui/Config.py @@ -0,0 +1,74 @@ +""" +Copyright 2016 Free Software Foundation, Inc. +This file is part of GNU Radio + +GNU Radio Companion 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 2 +of the License, or (at your option) any later version. + +GNU Radio Companion 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 sys +import os +from ..core.Config import Config as _Config +from . import Constants + + +class Config(_Config): + + name = 'GNU Radio Companion' + + gui_prefs_file = os.environ.get( + 'GRC_PREFS_PATH', os.path.expanduser('~/.gnuradio/grc.conf')) + + def __init__(self, install_prefix, *args, **kwargs): + _Config.__init__(self, *args, **kwargs) + self.install_prefix = install_prefix + Constants.update_font_size(self.font_size) + + @property + def editor(self): + return self.prefs.get_string('grc', 'editor', '') + + @editor.setter + def editor(self, value): + self.prefs.get_string('grc', 'editor', value) + self.prefs.save() + + @property + def xterm_executable(self): + return self.prefs.get_string('grc', 'xterm_executable', 'xterm') + + @property + def default_canvas_size(self): + try: # ugly, but matches current code style + raw = self.prefs.get_string('grc', 'canvas_default_size', '1280, 1024') + value = tuple(int(x.strip('() ')) for x in raw.split(',')) + if len(value) != 2 or not all(300 < x < 4096 for x in value): + raise Exception() + return value + except: + print >> sys.stderr, "Error: invalid 'canvas_default_size' setting." + return Constants.DEFAULT_CANVAS_SIZE_DEFAULT + + @property + def font_size(self): + try: # ugly, but matches current code style + font_size = self.prefs.get_long('grc', 'canvas_font_size', + Constants.DEFAULT_FONT_SIZE) + if font_size <= 0: + raise Exception() + except: + font_size = Constants.DEFAULT_FONT_SIZE + print >> sys.stderr, "Error: invalid 'canvas_font_size' setting." + + return font_size diff --git a/grc/gui/Connection.py b/grc/gui/Connection.py index badf8e8a82..50361c19d0 100644 --- a/grc/gui/Connection.py +++ b/grc/gui/Connection.py @@ -17,15 +17,18 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ -import Utils -from Element import Element +import gtk + import Colors +import Utils from Constants import CONNECTOR_ARROW_BASE, CONNECTOR_ARROW_HEIGHT -import gtk +from Element import Element + +from ..core.Constants import GR_MESSAGE_DOMAIN +from ..core.Connection import Connection as _Connection -from .. base.Constants import GR_MESSAGE_DOMAIN -class Connection(Element): +class Connection(Element, _Connection): """ A graphical connection for ports. The connection has 2 parts, the arrow and the wire. @@ -35,8 +38,9 @@ class Connection(Element): The arrow coloring exposes the enabled and valid states. """ - def __init__(self): + def __init__(self, **kwargs): Element.__init__(self) + _Connection.__init__(self, **kwargs) # can't use Colors.CONNECTION_ENABLED_COLOR here, might not be defined (grcc) self._bg_color = self._arrow_color = self._color = None @@ -88,7 +92,7 @@ class Connection(Element): if not source_domain == sink_domain == GR_MESSAGE_DOMAIN \ else gtk.gdk.LINE_ON_OFF_DASH get_domain_color = lambda d: Colors.get_color(( - self.get_parent().get_parent().get_domain(d) or {} + self.get_parent().get_parent().domains.get(d, {}) ).get('color') or Colors.DEFAULT_DOMAIN_COLOR_CODE) self._color = get_domain_color(source_domain) self._bg_color = get_domain_color(sink_domain) diff --git a/grc/gui/Constants.py b/grc/gui/Constants.py index 741c6fda95..022564cd77 100644 --- a/grc/gui/Constants.py +++ b/grc/gui/Constants.py @@ -17,18 +17,10 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ -import os -import sys - -import pygtk -pygtk.require('2.0') import gtk -from gnuradio import gr +from ..core.Constants import * -prefs = gr.prefs() -GR_PREFIX = gr.prefix() -EDITOR = prefs.get_string('grc', 'editor', '') # default path for the open/save dialogs DEFAULT_FILE_PATH = os.getcwd() @@ -48,30 +40,14 @@ MIN_DIALOG_WIDTH = 500 MIN_DIALOG_HEIGHT = 500 # default sizes DEFAULT_BLOCKS_WINDOW_WIDTH = 100 -DEFAULT_REPORTS_WINDOW_WIDTH = 100 - -try: # ugly, but matches current code style - raw = prefs.get_string('grc', 'canvas_default_size', '1280, 1024') - DEFAULT_CANVAS_SIZE = tuple(int(x.strip('() ')) for x in raw.split(',')) - if len(DEFAULT_CANVAS_SIZE) != 2 or not all(300 < x < 4096 for x in DEFAULT_CANVAS_SIZE): - raise Exception() -except: - DEFAULT_CANVAS_SIZE = 1280, 1024 - print >> sys.stderr, "Error: invalid 'canvas_default_size' setting." - -# flow-graph canvas fonts -try: # ugly, but matches current code style - FONT_SIZE = prefs.get_long('grc', 'canvas_font_size', 8) - if FONT_SIZE <= 0: - raise Exception() -except: - FONT_SIZE = 8 - print >> sys.stderr, "Error: invalid 'canvas_font_size' setting." -FONT_FAMILY = "Sans" -BLOCK_FONT = "%s %f" % (FONT_FAMILY, FONT_SIZE) -PORT_FONT = BLOCK_FONT -PARAM_FONT = "%s %f" % (FONT_FAMILY, FONT_SIZE - 0.5) +DEFAULT_CONSOLE_WINDOW_WIDTH = 100 +DEFAULT_CANVAS_SIZE_DEFAULT = 1280, 1024 + +FONT_SIZE = DEFAULT_FONT_SIZE = 8 +FONT_FAMILY = "Sans" +BLOCK_FONT = PORT_FONT = "Sans 8" +PARAM_FONT = "Sans 7.5" # size of the state saving cache in the flow graph (undo/redo functionality) STATE_CACHE_SIZE = 42 @@ -90,8 +66,7 @@ CANVAS_GRID_SIZE = 8 # port constraint dimensions PORT_BORDER_SEPARATION = 8 PORT_SPACING = 2 * PORT_BORDER_SEPARATION -PORT_SEPARATION = PORT_SPACING + 2 * PORT_LABEL_PADDING + int(1.5 * FONT_SIZE) -PORT_SEPARATION += -PORT_SEPARATION % (2 * CANVAS_GRID_SIZE) # even multiple +PORT_SEPARATION = 32 PORT_MIN_WIDTH = 20 PORT_LABEL_HIDDEN_WIDTH = 10 @@ -120,3 +95,17 @@ SCROLL_DISTANCE = 15 # How close the mouse click can be to a line and register a connection select. LINE_SELECT_SENSITIVITY = 5 + + +def update_font_size(font_size): + global PORT_SEPARATION, BLOCK_FONT, PORT_FONT, PARAM_FONT, FONT_SIZE + + FONT_SIZE = font_size + BLOCK_FONT = "%s %f" % (FONT_FAMILY, font_size) + PORT_FONT = BLOCK_FONT + PARAM_FONT = "%s %f" % (FONT_FAMILY, font_size - 0.5) + + PORT_SEPARATION = PORT_SPACING + 2 * PORT_LABEL_PADDING + int(1.5 * font_size) + PORT_SEPARATION += -PORT_SEPARATION % (2 * CANVAS_GRID_SIZE) # even multiple + +update_font_size(DEFAULT_FONT_SIZE) diff --git a/grc/gui/Dialogs.py b/grc/gui/Dialogs.py index f2941250a8..1d114356c8 100644 --- a/grc/gui/Dialogs.py +++ b/grc/gui/Dialogs.py @@ -17,15 +17,13 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ -import pygtk -pygtk.require('2.0') import gtk import sys from distutils.spawn import find_executable - -from . import Utils, Actions, Constants, Messages +from . import Utils, Actions +from ..core import Messages class SimpleTextDisplay(gtk.TextView): @@ -94,21 +92,21 @@ class TextDisplay(SimpleTextDisplay): buffer.delete(buffer.get_start_iter(), buffer.get_end_iter()) def save(self, file_path): - report_file = open(file_path, 'w') + console_file = open(file_path, 'w') buffer = self.get_buffer() - report_file.write(buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True)) - report_file.close() + console_file.write(buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True)) + console_file.close() # Callback functions to handle the scrolling lock and clear context menus options # Action functions are set by the ActionHandler's init function def clear_cb(self, menu_item, web_view): - Actions.CLEAR_REPORTS() + Actions.CLEAR_CONSOLE() def scroll_back_cb(self, menu_item, web_view): Actions.TOGGLE_SCROLL_LOCK() def save_cb(self, menu_item, web_view): - Actions.SAVE_REPORTS() + Actions.SAVE_CONSOLE() def populate_popup(self, view, menu): """Create a popup menu for the scroll lock and clear functions""" @@ -177,14 +175,14 @@ def ErrorsDialog(flowgraph): MessageDialogHelper( class AboutDialog(gtk.AboutDialog): """A cute little about dialog.""" - def __init__(self, platform): + def __init__(self, config): """AboutDialog constructor.""" gtk.AboutDialog.__init__(self) - self.set_name(platform.get_name()) - self.set_version(platform.get_version()) - self.set_license(platform.get_license()) - self.set_copyright(platform.get_license().splitlines()[0]) - self.set_website(platform.get_website()) + self.set_name(config.name) + self.set_version(config.version) + self.set_license(config.license) + self.set_copyright(config.license.splitlines()[0]) + self.set_website(config.website) self.run() self.destroy() @@ -240,7 +238,7 @@ def MissingXTermDialog(xterm): ) -def ChooseEditorDialog(): +def ChooseEditorDialog(config): # Give the option to either choose an editor or use the default # Always return true/false so the caller knows it was successful buttons = ( @@ -266,10 +264,7 @@ def ChooseEditorDialog(): file_dialog.set_current_folder('/usr/bin') try: if file_dialog.run() == gtk.RESPONSE_OK: - file_path = file_dialog.get_filename() - Constants.prefs.set_string('grc', 'editor', file_path) - Constants.prefs.save() - Constants.EDITOR = file_path + config.editor = file_path = file_dialog.get_filename() file_dialog.destroy() return file_path finally: @@ -287,16 +282,12 @@ def ChooseEditorDialog(): if process is None: raise ValueError("Can't find default editor executable") # Save - Constants.prefs.set_string('grc', 'editor', process) - Constants.prefs.save() - Constants.EDITOR = process + config.editor = process return process except Exception: Messages.send('>>> Unable to load the default editor. Please choose an editor.\n') # Just reset of the constant and force the user to select an editor the next time - Constants.prefs.set_string('grc', 'editor', '') - Constants.prefs.save() - Constants.EDITOR = "" + config.editor = '' return Messages.send('>>> No editor selected.\n') diff --git a/grc/gui/DrawingArea.py b/grc/gui/DrawingArea.py index 4412129809..6a1df27a8c 100644 --- a/grc/gui/DrawingArea.py +++ b/grc/gui/DrawingArea.py @@ -20,7 +20,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA import pygtk pygtk.require('2.0') import gtk + from Constants import MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT, DND_TARGETS +import Colors + class DrawingArea(gtk.DrawingArea): """ @@ -68,13 +71,21 @@ class DrawingArea(gtk.DrawingArea): self.set_flags(gtk.CAN_FOCUS) # self.set_can_focus(True) self.connect('focus-out-event', self._handle_focus_lost_event) - def new_pixmap(self, width, height): return gtk.gdk.Pixmap(self.window, width, height, -1) - def get_pixbuf(self): - width, height = self._pixmap.get_size() - pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, 0, 8, width, height) - pixbuf.get_from_drawable(self._pixmap, self._pixmap.get_colormap(), 0, 0, 0, 0, width, height) + def new_pixmap(self, width, height): + return gtk.gdk.Pixmap(self.window, width, height, -1) + + def get_screenshot(self, transparent_bg=False): + pixmap = self._pixmap + W, H = pixmap.get_size() + pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, 0, 8, W, H) + pixbuf.fill(0xFF + Colors.FLOWGRAPH_BACKGROUND_COLOR.pixel << 8) + pixbuf.get_from_drawable(pixmap, pixmap.get_colormap(), 0, 0, 0, 0, W-1, H-1) + if transparent_bg: + bgc = Colors.FLOWGRAPH_BACKGROUND_COLOR + pixbuf = pixbuf.add_alpha(True, bgc.red, bgc.green, bgc.blue) return pixbuf + ########################################################################## ## Handlers ########################################################################## @@ -149,6 +160,12 @@ class DrawingArea(gtk.DrawingArea): gc = self.window.new_gc() self._flow_graph.draw(gc, self._pixmap) self.window.draw_drawable(gc, self._pixmap, 0, 0, 0, 0, -1, -1) + # draw a light grey line on the bottom and right end of the canvas. + # this is useful when the theme uses the same panel bg color as the canvas + W, H = self._pixmap.get_size() + gc.set_foreground(Colors.FLOWGRAPH_EDGE_COLOR) + self.window.draw_line(gc, 0, H-1, W, H-1) + self.window.draw_line(gc, W-1, 0, W-1, H) def _handle_focus_lost_event(self, widget, event): # don't clear selection while context menu is active diff --git a/grc/gui/Element.py b/grc/gui/Element.py index 18fb321929..9385424772 100644 --- a/grc/gui/Element.py +++ b/grc/gui/Element.py @@ -132,14 +132,14 @@ class Element(object): """ self.coor = coor - def get_parent(self): - """ - Get the parent of this element. - - Returns: - the parent - """ - return self.parent + # def get_parent(self): + # """ + # Get the parent of this element. + # + # Returns: + # the parent + # """ + # return self.parent def set_highlighted(self, highlighted): """ diff --git a/grc/gui/Executor.py b/grc/gui/Executor.py new file mode 100644 index 0000000000..bf9eecb9a8 --- /dev/null +++ b/grc/gui/Executor.py @@ -0,0 +1,121 @@ +# Copyright 2016 Free Software Foundation, Inc. +# This file is part of GNU Radio +# +# GNU Radio Companion 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 2 +# of the License, or (at your option) any later version. +# +# GNU Radio Companion 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 gobject +import os +import threading +import shlex +import subprocess +import sys +import re +from distutils.spawn import find_executable + +from ..core import Messages + + +class ExecFlowGraphThread(threading.Thread): + """Execute the flow graph as a new process and wait on it to finish.""" + + def __init__(self, flow_graph_page, xterm_executable, callback): + """ + ExecFlowGraphThread constructor. + + Args: + action_handler: an instance of an ActionHandler + """ + threading.Thread.__init__(self) + + self.page = flow_graph_page # store page and dont use main window calls in run + self.xterm_executable = xterm_executable + self.update_callback = callback + + try: + self.process = self._popen() + self.page.set_proc(self.process) + self.update_callback() + self.start() + except Exception as e: + Messages.send_verbose_exec(str(e)) + Messages.send_end_exec() + + def _popen(self): + """ + Execute this python flow graph. + """ + run_command = self.page.get_flow_graph().get_option('run_command') + generator = self.page.get_generator() + + try: + run_command = run_command.format( + python=shlex_quote(sys.executable), + filename=shlex_quote(generator.file_path)) + run_command_args = shlex.split(run_command) + except Exception as e: + raise ValueError("Can't parse run command {!r}: {}".format(run_command, e)) + + # When in no gui mode on linux, use a graphical terminal (looks nice) + xterm_executable = find_executable(self.xterm_executable) + if generator.generate_options == 'no_gui' and xterm_executable: + run_command_args = [xterm_executable, '-e', run_command] + + # this does not reproduce a shell executable command string, if a graphical + # terminal is used. Passing run_command though shlex_quote would do it but + # it looks really ugly and confusing in the console panel. + Messages.send_start_exec(' '.join(run_command_args)) + + return subprocess.Popen( + args=run_command_args, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + shell=False, universal_newlines=True + ) + + def run(self): + """ + Wait on the executing process by reading from its stdout. + Use gobject.idle_add when calling functions that modify gtk objects. + """ + # handle completion + r = "\n" + while r: + gobject.idle_add(Messages.send_verbose_exec, r) + r = os.read(self.process.stdout.fileno(), 1024) + self.process.poll() + gobject.idle_add(self.done) + + def done(self): + """Perform end of execution tasks.""" + Messages.send_end_exec(self.process.returncode) + self.page.set_proc(None) + self.update_callback() + + +########################################################### +# back-port from python3 +########################################################### +_find_unsafe = re.compile(r'[^\w@%+=:,./-]').search + + +def shlex_quote(s): + """Return a shell-escaped version of the string *s*.""" + if not s: + return "''" + if _find_unsafe(s) is None: + return s + + # use single quotes, and put single quotes into double quotes + # the string $'b is then quoted as '$'"'"'b' + return "'" + s.replace("'", "'\"'\"'") + "'" diff --git a/grc/gui/FileDialogs.py b/grc/gui/FileDialogs.py index 730ac6fba0..e9430b1f88 100644 --- a/grc/gui/FileDialogs.py +++ b/grc/gui/FileDialogs.py @@ -33,7 +33,7 @@ import Utils ################################################## OPEN_FLOW_GRAPH = 'open flow graph' SAVE_FLOW_GRAPH = 'save flow graph' -SAVE_REPORTS = 'save reports' +SAVE_CONSOLE = 'save console' SAVE_IMAGE = 'save image' OPEN_QSS_THEME = 'open qss theme' @@ -43,46 +43,45 @@ File <b>$encode($filename)</b> Exists!\nWould you like to overwrite the existing FILE_DNE_MARKUP_TMPL="""\ File <b>$encode($filename)</b> Does not Exist!""" -################################################## + + # File Filters -################################################## -##the filter for flow graph files def get_flow_graph_files_filter(): filter = gtk.FileFilter() filter.set_name('Flow Graph Files') filter.add_pattern('*'+Preferences.file_extension()) return filter + def get_text_files_filter(): filter = gtk.FileFilter() filter.set_name('Text Files') filter.add_pattern('*'+TEXT_FILE_EXTENSION) return filter -##the filter for image files + def get_image_files_filter(): filter = gtk.FileFilter() filter.set_name('Image Files') filter.add_pattern('*'+IMAGE_FILE_EXTENSION) return filter -##the filter for all files + def get_all_files_filter(): filter = gtk.FileFilter() filter.set_name('All Files') filter.add_pattern('*') return filter -##the filter for qss files + def get_qss_themes_filter(): filter = gtk.FileFilter() filter.set_name('QSS Themes') filter.add_pattern('*.qss') return filter -################################################## + # File Dialogs -################################################## class FileDialogHelper(gtk.FileChooserDialog): """ A wrapper class for the gtk file chooser dialog. @@ -105,6 +104,7 @@ class FileDialogHelper(gtk.FileChooserDialog): self.set_local_only(True) self.add_filter(get_all_files_filter()) + class FileDialog(FileDialogHelper): """A dialog box to save or open flow graph files. This is a base class, do not use.""" @@ -124,8 +124,8 @@ class FileDialog(FileDialogHelper): FileDialogHelper.__init__(self, gtk.FILE_CHOOSER_ACTION_SAVE, 'Save a Flow Graph to a File...') self.add_and_set_filter(get_flow_graph_files_filter()) self.set_current_name(path.basename(current_file_path)) - elif self.type == SAVE_REPORTS: - FileDialogHelper.__init__(self, gtk.FILE_CHOOSER_ACTION_SAVE, 'Save Reports to a File...') + elif self.type == SAVE_CONSOLE: + FileDialogHelper.__init__(self, gtk.FILE_CHOOSER_ACTION_SAVE, 'Save Console to a File...') self.add_and_set_filter(get_text_files_filter()) file_path = path.splitext(path.basename(current_file_path))[0] self.set_current_name(file_path) #show the current filename @@ -164,11 +164,11 @@ class FileDialog(FileDialogHelper): ############################################# # Handle Save Dialogs ############################################# - if self.type in (SAVE_FLOW_GRAPH, SAVE_REPORTS, SAVE_IMAGE): + if self.type in (SAVE_FLOW_GRAPH, SAVE_CONSOLE, SAVE_IMAGE): filename = self.get_filename() extension = { SAVE_FLOW_GRAPH: Preferences.file_extension(), - SAVE_REPORTS: TEXT_FILE_EXTENSION, + SAVE_CONSOLE: TEXT_FILE_EXTENSION, SAVE_IMAGE: IMAGE_FILE_EXTENSION, }[self.type] #append the missing file extension if the filter matches @@ -205,8 +205,37 @@ class FileDialog(FileDialogHelper): self.destroy() return filename -class OpenFlowGraphFileDialog(FileDialog): type = OPEN_FLOW_GRAPH -class SaveFlowGraphFileDialog(FileDialog): type = SAVE_FLOW_GRAPH -class OpenQSSFileDialog(FileDialog): type = OPEN_QSS_THEME -class SaveReportsFileDialog(FileDialog): type = SAVE_REPORTS -class SaveImageFileDialog(FileDialog): type = SAVE_IMAGE + +class OpenFlowGraphFileDialog(FileDialog): + type = OPEN_FLOW_GRAPH + + +class SaveFlowGraphFileDialog(FileDialog): + type = SAVE_FLOW_GRAPH + + +class OpenQSSFileDialog(FileDialog): + type = OPEN_QSS_THEME + + +class SaveConsoleFileDialog(FileDialog): + type = SAVE_CONSOLE + + +class SaveImageFileDialog(FileDialog): + type = SAVE_IMAGE + + +class SaveScreenShotDialog(SaveImageFileDialog): + + def __init__(self, current_file_path=''): + SaveImageFileDialog.__init__(self, current_file_path) + self._button = button = gtk.CheckButton('_Background transparent') + self._button.set_active(Preferences.screen_shot_background_transparent()) + self.set_extra_widget(button) + + def run(self): + filename = SaveImageFileDialog.run(self) + bg_transparent = self._button.get_active() + Preferences.screen_shot_background_transparent(bg_transparent) + return filename, bg_transparent diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py index 2053e86454..6eb05f9ac9 100644 --- a/grc/gui/FlowGraph.py +++ b/grc/gui/FlowGraph.py @@ -17,39 +17,43 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ -import random import functools -from itertools import chain -from operator import methodcaller +import random from distutils.spawn import find_executable +from itertools import chain, count +from operator import methodcaller import gobject -from . import Actions, Colors, Constants, Utils, Messages, Bars, Dialogs -from . Element import Element -from . Constants import SCROLL_PROXIMITY_SENSITIVITY, SCROLL_DISTANCE -from . external_editor import ExternalEditor +from . import Actions, Colors, Constants, Utils, Bars, Dialogs +from .Constants import SCROLL_PROXIMITY_SENSITIVITY, SCROLL_DISTANCE +from .Element import Element +from .external_editor import ExternalEditor + +from ..core.FlowGraph import FlowGraph as _Flowgraph +from ..core import Messages -class FlowGraph(Element): +class FlowGraph(Element, _Flowgraph): """ FlowGraph is the data structure to store graphical signal blocks, graphical inputs and outputs, and the connections between inputs and outputs. """ - def __init__(self): + def __init__(self, **kwargs): """ FlowGraph constructor. Create a list for signal blocks and connections. Connect mouse handlers. """ Element.__init__(self) + _Flowgraph.__init__(self, **kwargs) #when is the flow graph selected? (used by keyboard event handler) self.is_selected = lambda: bool(self.get_selected_elements()) #important vars dealing with mouse event tracking self.element_moved = False self.mouse_pressed = False - self.unselect() + self._selected_elements = [] self.press_coor = (0, 0) #selected ports self._old_selected_port = None @@ -62,14 +66,31 @@ class FlowGraph(Element): self._external_updaters = {} + def _get_unique_id(self, base_id=''): + """ + Get a unique id starting with the base id. + + Args: + base_id: the id starts with this and appends a count + + Returns: + a unique id + """ + for index in count(): + block_id = '{}_{}'.format(base_id, index) + if block_id not in (b.get_id() for b in self.blocks): + break + return block_id + def install_external_editor(self, param): target = (param.get_parent().get_id(), param.get_key()) if target in self._external_updaters: editor = self._external_updaters[target] else: - editor = (find_executable(Constants.EDITOR) or - Dialogs.ChooseEditorDialog()) + config = self.get_parent().config + editor = (find_executable(config.editor) or + Dialogs.ChooseEditorDialog(config)) if not editor: return updater = functools.partial( @@ -86,9 +107,7 @@ class FlowGraph(Element): # Problem launching the editor. Need to select a new editor. Messages.send('>>> Error opening an external editor. Please select a different editor.\n') # Reset the editor to force the user to select a new one. - Constants.prefs.set_string('grc', 'editor', '') - Constants.prefs.save() - Constants.EDITOR = "" + self.get_parent().config.editor = '' def handle_external_editor_change(self, new_value, target): try: @@ -131,7 +150,7 @@ class FlowGraph(Element): int(random.uniform(.25, .75)*v_adj.page_size + v_adj.get_value()), ) #get the new block - block = self.get_new_block(key) + block = self.new_block(key) block.set_coordinate(coor) block.set_rotation(0) block.get_param('id').set_value(id) @@ -160,7 +179,7 @@ class FlowGraph(Element): #get connections between selected blocks connections = filter( lambda c: c.get_source().get_parent() in blocks and c.get_sink().get_parent() in blocks, - self.get_connections(), + self.connections, ) clipboard = ( (x_min, y_min), @@ -190,7 +209,7 @@ class FlowGraph(Element): for block_n in blocks_n: block_key = block_n.find('key') if block_key == 'options': continue - block = self.get_new_block(block_key) + block = self.new_block(block_key) if not block: continue # unknown block was pasted (e.g. dummy block) selected.add(block) @@ -206,7 +225,7 @@ class FlowGraph(Element): if param_key == 'id': old_id2block[param_value] = block #if the block id is not unique, get a new block id - if param_value in [blk.get_id() for blk in self.get_blocks()]: + if param_value in (blk.get_id() for blk in self.blocks): param_value = self._get_unique_id(param_value) #set value to key block.get_param(param_key).set_value(param_value) @@ -293,6 +312,47 @@ class FlowGraph(Element): selected_block.move(delta_coordinate) self.element_moved = True + def align_selected(self, calling_action=None): + """ + Align the selected blocks. + + Args: + calling_action: the action initiating the alignment + + Returns: + True if changed, otherwise False + """ + blocks = self.get_selected_blocks() + if calling_action is None or not blocks: + return False + + # compute common boundary of selected objects + min_x, min_y = max_x, max_y = blocks[0].get_coordinate() + for selected_block in blocks: + x, y = selected_block.get_coordinate() + min_x, min_y = min(min_x, x), min(min_y, y) + x += selected_block.W + y += selected_block.H + max_x, max_y = max(max_x, x), max(max_y, y) + ctr_x, ctr_y = (max_x + min_x)/2, (max_y + min_y)/2 + + # align the blocks as requested + transform = { + Actions.BLOCK_VALIGN_TOP: lambda x, y, w, h: (x, min_y), + Actions.BLOCK_VALIGN_MIDDLE: lambda x, y, w, h: (x, ctr_y - h/2), + Actions.BLOCK_VALIGN_BOTTOM: lambda x, y, w, h: (x, max_y - h), + Actions.BLOCK_HALIGN_LEFT: lambda x, y, w, h: (min_x, y), + Actions.BLOCK_HALIGN_CENTER: lambda x, y, w, h: (ctr_x-w/2, y), + Actions.BLOCK_HALIGN_RIGHT: lambda x, y, w, h: (max_x - w, y), + }.get(calling_action, lambda *args: args) + + for selected_block in blocks: + x, y = selected_block.get_coordinate() + w, h = selected_block.W, selected_block.H + selected_block.set_coordinate(transform(x, y, w, h)) + + return True + def rotate_selected(self, rotation): """ Rotate the selected blocks by multiples of 90 degrees. @@ -303,7 +363,8 @@ class FlowGraph(Element): Returns: true if changed, otherwise false. """ - if not self.get_selected_blocks(): return False + if not self.get_selected_blocks(): + return False #initialize min and max coordinates min_x, min_y = self.get_selected_block().get_coordinate() max_x, max_y = self.get_selected_block().get_coordinate() @@ -344,13 +405,18 @@ class FlowGraph(Element): """ W,H = self.get_size() + hide_disabled_blocks = Actions.TOGGLE_HIDE_DISABLED_BLOCKS.get_active() + hide_variables = Actions.TOGGLE_HIDE_VARIABLES.get_active() + #draw the background gc.set_foreground(Colors.FLOWGRAPH_BACKGROUND_COLOR) window.draw_rectangle(gc, True, 0, 0, W, H) # draw comments first if Actions.TOGGLE_SHOW_BLOCK_COMMENTS.get_active(): - for block in self.iter_blocks(): + for block in self.blocks: + if hide_variables and (block.is_variable or block.is_import): + continue # skip hidden disabled blocks and connections if block.get_enabled(): block.draw_comment(gc, window) #draw multi select rectangle @@ -367,11 +433,12 @@ class FlowGraph(Element): gc.set_foreground(Colors.BORDER_COLOR) window.draw_rectangle(gc, False, x, y, w, h) #draw blocks on top of connections - hide_disabled_blocks = Actions.TOGGLE_HIDE_DISABLED_BLOCKS.get_active() - blocks = sorted(self.iter_blocks(), key=methodcaller('get_enabled')) - for element in chain(self.iter_connections(), blocks): + blocks = sorted(self.blocks, key=methodcaller('get_enabled')) + for element in chain(self.connections, blocks): if hide_disabled_blocks and not element.get_enabled(): continue # skip hidden disabled blocks and connections + if hide_variables and (element.is_variable or element.is_import): + continue # skip hidden disabled blocks and connections element.draw(gc, window) #draw selected blocks on top of selected connections for selected_element in self.get_selected_connections() + self.get_selected_blocks(): @@ -432,6 +499,10 @@ class FlowGraph(Element): """ self._selected_elements = [] + def select_all(self): + """Select all blocks in the flow graph""" + self._selected_elements = list(self.get_elements()) + def what_is_selected(self, coor, coor_m=None): """ What is selected? @@ -451,18 +522,18 @@ class FlowGraph(Element): selected_port = None selected = set() #check the elements + hide_disabled_blocks = Actions.TOGGLE_HIDE_DISABLED_BLOCKS.get_active() + hide_variables = Actions.TOGGLE_HIDE_VARIABLES.get_active() for element in reversed(self.get_elements()): + if hide_disabled_blocks and not element.get_enabled(): + continue # skip hidden disabled blocks and connections + if hide_variables and (element.is_variable or element.is_import): + continue # skip hidden disabled blocks and connections selected_element = element.what_is_selected(coor, coor_m) - if not selected_element: continue - # hidden disabled connections, blocks and their ports can not be selected - if Actions.TOGGLE_HIDE_DISABLED_BLOCKS.get_active() and ( - selected_element.is_block() and not selected_element.get_enabled() or - selected_element.is_connection() and not selected_element.get_enabled() or - selected_element.is_port() and not selected_element.get_parent().get_enabled() - ): + if not selected_element: continue #update the selected port information - if selected_element.is_port(): + if selected_element.is_port: if not coor_m: selected_port = selected_element selected_element = selected_element.get_parent() selected.add(selected_element) @@ -486,7 +557,8 @@ class FlowGraph(Element): """ selected = set() for selected_element in self.get_selected_elements(): - if selected_element.is_connection(): selected.add(selected_element) + if selected_element.is_connection: + selected.add(selected_element) return list(selected) def get_selected_blocks(self): @@ -498,7 +570,8 @@ class FlowGraph(Element): """ selected = set() for selected_element in self.get_selected_elements(): - if selected_element.is_block(): selected.add(selected_element) + if selected_element.is_block: + selected.add(selected_element) return list(selected) def get_selected_block(self): @@ -508,7 +581,8 @@ class FlowGraph(Element): Returns: a block or None """ - return self.get_selected_blocks() and self.get_selected_blocks()[0] or None + selected_blocks = self.get_selected_blocks() + return selected_blocks[0] if selected_blocks else None def get_selected_elements(self): """ @@ -526,7 +600,8 @@ class FlowGraph(Element): Returns: a block, port, or connection or None """ - return self.get_selected_elements() and self.get_selected_elements()[0] or None + selected_elements = self.get_selected_elements() + return selected_elements[0] if selected_elements else None def update_selected_elements(self): """ @@ -673,7 +748,7 @@ class FlowGraph(Element): adj.set_value(adj_val-SCROLL_DISTANCE) adj.emit('changed') #remove the connection if selected in drag event - if len(self.get_selected_elements()) == 1 and self.get_selected_element().is_connection(): + if len(self.get_selected_elements()) == 1 and self.get_selected_element().is_connection: Actions.ELEMENT_DELETE() #move the selected elements and record the new coordinate if not self.get_ctrl_mask(): diff --git a/grc/gui/MainWindow.py b/grc/gui/MainWindow.py index a340bcc817..1437391236 100644 --- a/grc/gui/MainWindow.py +++ b/grc/gui/MainWindow.py @@ -17,21 +17,20 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ -from Constants import \ - NEW_FLOGRAPH_TITLE, DEFAULT_REPORTS_WINDOW_WIDTH -import Actions -import pygtk -pygtk.require('2.0') -import gtk -import Bars -from BlockTreeWindow import BlockTreeWindow -from Dialogs import TextDisplay, MessageDialogHelper -from NotebookPage import NotebookPage -import Preferences -import Messages -import Utils import os +import gtk + +from . import Bars, Actions, Preferences, Utils +from .BlockTreeWindow import BlockTreeWindow +from .VariableEditor import VariableEditor +from .Constants import \ + NEW_FLOGRAPH_TITLE, DEFAULT_CONSOLE_WINDOW_WIDTH +from .Dialogs import TextDisplay, MessageDialogHelper +from .NotebookPage import NotebookPage + +from ..core import Messages + MAIN_WINDOW_TITLE_TMPL = """\ #if not $saved *#slurp @@ -58,6 +57,7 @@ PAGE_TITLE_MARKUP_TMPL = """\ #end if """ + ############################################################ # Main window ############################################################ @@ -65,59 +65,98 @@ PAGE_TITLE_MARKUP_TMPL = """\ class MainWindow(gtk.Window): """The topmost window with menus, the tool bar, and other major windows.""" + # Constants the action handler can use to indicate which panel visibility to change. + BLOCKS = 0 + CONSOLE = 1 + VARIABLES = 2 + def __init__(self, platform, action_handler_callback): """ - MainWindow contructor - Setup the menu, toolbar, flowgraph editor notebook, block selection window... + MainWindow constructor + Setup the menu, toolbar, flow graph editor notebook, block selection window... """ self._platform = platform - gen_opts = platform.get_block('options').get_param('generate_options') + + gen_opts = platform.blocks['options'].get_param('generate_options') generate_mode_default = gen_opts.get_value() generate_modes = [ (o.get_key(), o.get_name(), o.get_key() == generate_mode_default) for o in gen_opts.get_options()] - # load preferences + + # Load preferences Preferences.load(platform) - #setup window + + # Setup window gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL) vbox = gtk.VBox() - self.hpaned = gtk.HPaned() self.add(vbox) - #create the menu bar and toolbar + + # Create the menu bar and toolbar self.add_accel_group(Actions.get_accel_group()) self.menu_bar = Bars.MenuBar(generate_modes, action_handler_callback) vbox.pack_start(self.menu_bar, False) - self.tool_bar = Bars.Toolbar(generate_modes, action_handler_callback ) + self.tool_bar = Bars.Toolbar(generate_modes, action_handler_callback) vbox.pack_start(self.tool_bar, False) - vbox.pack_start(self.hpaned) - #create the notebook + + # Main parent container for the different panels + self.container = gtk.HPaned() + vbox.pack_start(self.container) + + # Create the notebook self.notebook = gtk.Notebook() self.page_to_be_closed = None self.current_page = None self.notebook.set_show_border(False) - self.notebook.set_scrollable(True) #scroll arrows for page tabs + self.notebook.set_scrollable(True) # scroll arrows for page tabs self.notebook.connect('switch-page', self._handle_page_change) - #setup containers - self.flow_graph_vpaned = gtk.VPaned() - #flow_graph_box.pack_start(self.scrolled_window) - self.flow_graph_vpaned.pack1(self.notebook) - self.hpaned.pack1(self.flow_graph_vpaned) - self.btwin = BlockTreeWindow(platform, self.get_flow_graph); - self.hpaned.pack2(self.btwin, False) #dont allow resize - #create the reports window + + # Create the console window self.text_display = TextDisplay() - #house the reports in a scrolled window - self.reports_scrolled_window = gtk.ScrolledWindow() - self.reports_scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - self.reports_scrolled_window.add(self.text_display) - self.reports_scrolled_window.set_size_request(-1, DEFAULT_REPORTS_WINDOW_WIDTH) - self.flow_graph_vpaned.pack2(self.reports_scrolled_window, False) #dont allow resize - #load preferences and show the main window + self.console_window = gtk.ScrolledWindow() + self.console_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + self.console_window.add(self.text_display) + self.console_window.set_size_request(-1, DEFAULT_CONSOLE_WINDOW_WIDTH) + + # Create the block tree and variable panels + self.btwin = BlockTreeWindow(platform, self.get_flow_graph) + self.vars = VariableEditor(platform, self.get_flow_graph) + + # Figure out which place to put the variable editor + self.left = gtk.VPaned() + self.right = gtk.VPaned() + self.left_subpanel = gtk.HPaned() + + self.variable_panel_sidebar = Preferences.variable_editor_sidebar() + if self.variable_panel_sidebar: + self.left.pack1(self.notebook) + self.left.pack2(self.console_window, False) + self.right.pack1(self.btwin) + self.right.pack2(self.vars, False) + else: + # Put the variable editor in a panel with the console + self.left.pack1(self.notebook) + self.left_subpanel.pack1(self.console_window, shrink=False) + self.left_subpanel.pack2(self.vars, resize=False, shrink=True) + self.left.pack2(self.left_subpanel, False) + + # Create the right panel + self.right.pack1(self.btwin) + + self.container.pack1(self.left) + self.container.pack2(self.right, False) + + # load preferences and show the main window self.resize(*Preferences.main_window_size()) - self.flow_graph_vpaned.set_position(Preferences.reports_window_position()) - self.hpaned.set_position(Preferences.blocks_window_position()) + self.container.set_position(Preferences.blocks_window_position()) + self.left.set_position(Preferences.console_window_position()) + if self.variable_panel_sidebar: + self.right.set_position(Preferences.variable_editor_position(sidebar=True)) + else: + self.left_subpanel.set_position(Preferences.variable_editor_position()) + self.show_all() - self.reports_scrolled_window.hide() + self.console_window.hide() + self.vars.hide() self.btwin.hide() ############################################################ @@ -148,14 +187,45 @@ class MainWindow(gtk.Window): page_num: new page number """ self.current_page = self.notebook.get_nth_page(page_num) - Messages.send_page_switch(self.current_page.get_file_path()) Actions.PAGE_CHANGE() + def update_panel_visibility(self, panel, visibility=True): + """ + Handles changing visibility of panels. + """ + # Set the visibility for the requested panel, then update the containers if they need + # to be hidden as well. + + if panel == self.BLOCKS: + self.btwin.set_visible(visibility) + elif panel == self.CONSOLE: + self.console_window.set_visible(visibility) + elif panel == self.VARIABLES: + self.vars.set_visible(visibility) + else: + return + + if self.variable_panel_sidebar: + # If both the variable editor and block panels are hidden, hide the right container + if not self.btwin.get_visible() and not self.vars.get_visible(): + self.right.hide() + else: + self.right.show() + else: + if not self.btwin.get_visible(): + self.right.hide() + else: + self.right.show() + if not self.vars.get_visible() and not self.console_window.get_visible(): + self.left_subpanel.hide() + else: + self.left_subpanel.show() + ############################################################ - # Report Window + # Console Window ############################################################ - def add_report_line(self, line): + def add_console_line(self, line): """ Place line at the end of the text buffer, then scroll its window all the way down. @@ -227,8 +297,12 @@ class MainWindow(gtk.Window): Preferences.set_open_files(open_files) Preferences.file_open(open_file) Preferences.main_window_size(self.get_size()) - Preferences.reports_window_position(self.flow_graph_vpaned.get_position()) - Preferences.blocks_window_position(self.hpaned.get_position()) + Preferences.console_window_position(self.left.get_position()) + Preferences.blocks_window_position(self.container.get_position()) + if self.variable_panel_sidebar: + Preferences.variable_editor_position(self.right.get_position(), sidebar=True) + else: + Preferences.variable_editor_position(self.left_subpanel.get_position()) Preferences.save() return True @@ -272,7 +346,7 @@ class MainWindow(gtk.Window): """ Set the title of the main window. Set the titles on the page tabs. - Show/hide the reports window. + Show/hide the console window. Args: title: the window title @@ -283,7 +357,7 @@ class MainWindow(gtk.Window): new_flowgraph_title=NEW_FLOGRAPH_TITLE, read_only=self.get_page().get_read_only(), saved=self.get_page().get_saved(), - platform_name=self._platform.get_name(), + platform_name=self._platform.config.name, ) ) #set tab titles @@ -298,6 +372,9 @@ class MainWindow(gtk.Window): #show/hide notebook tabs self.notebook.set_show_tabs(len(self.get_pages()) > 1) + # Need to update the variable window when changing + self.vars.update_gui() + def update_pages(self): """ Forces a reload of all the pages in this notebook. diff --git a/grc/gui/NotebookPage.py b/grc/gui/NotebookPage.py index 4c112154af..c9e8d0f186 100644 --- a/grc/gui/NotebookPage.py +++ b/grc/gui/NotebookPage.py @@ -39,13 +39,13 @@ class NotebookPage(gtk.HBox): file_path: path to a flow graph file """ self._flow_graph = flow_graph - self.set_proc(None) + self.process = None #import the file self.main_window = main_window - self.set_file_path(file_path) + self.file_path = file_path initial_state = flow_graph.get_parent().parse_flow_graph(file_path) self.state_cache = StateCache(initial_state) - self.set_saved(True) + self.saved = True #import the data to the flow graph self.get_flow_graph().import_data(initial_state) #initialize page gui @@ -102,10 +102,8 @@ class NotebookPage(gtk.HBox): Returns: generator """ - return self.get_flow_graph().get_parent().get_generator()( - self.get_flow_graph(), - self.get_file_path(), - ) + platform = self.get_flow_graph().get_parent() + return platform.Generator(self.get_flow_graph(), self.get_file_path()) def _handle_button(self, button): """ @@ -191,8 +189,7 @@ class NotebookPage(gtk.HBox): Args: file_path: file path string """ - if file_path: self.file_path = os.path.abspath(file_path) - else: self.file_path = '' + self.file_path = os.path.abspath(file_path) if file_path else '' def get_saved(self): """ diff --git a/grc/gui/Param.py b/grc/gui/Param.py index 6884d6530a..4b5a3c294a 100644 --- a/grc/gui/Param.py +++ b/grc/gui/Param.py @@ -23,8 +23,10 @@ import pygtk pygtk.require('2.0') import gtk -from . import Colors, Utils, Constants, Dialogs -from . Element import Element +from . import Colors, Utils, Constants +from .Element import Element + +from ..core.Param import Param as _Param class InputParam(gtk.HBox): @@ -82,7 +84,7 @@ class InputParam(gtk.HBox): self._have_pending_changes = True self._update_gui() if self._editing_callback: - self._editing_callback() + self._editing_callback(self, None) def _apply_change(self, *args): """ @@ -93,7 +95,7 @@ class InputParam(gtk.HBox): self.param.set_value(self.get_text()) #call the callback if self._changed_callback: - self._changed_callback(*args) + self._changed_callback(self, None) else: self.param.validate() #gui update @@ -127,8 +129,19 @@ class EntryParam(InputParam): return self._input.get_text() def set_color(self, color): - self._input.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(color)) - self._input.modify_text(gtk.STATE_NORMAL, Colors.PARAM_ENTRY_TEXT_COLOR) + need_status_color = self.label not in self.get_children() + text_color = ( + Colors.PARAM_ENTRY_TEXT_COLOR if not need_status_color else + gtk.gdk.color_parse('blue') if self._have_pending_changes else + gtk.gdk.color_parse('red') if not self.param.is_valid() else + Colors.PARAM_ENTRY_TEXT_COLOR) + base_color = ( + Colors.BLOCK_DISABLED_COLOR + if need_status_color and not self.param.get_parent().get_enabled() + else gtk.gdk.color_parse(color) + ) + self._input.modify_base(gtk.STATE_NORMAL, base_color) + self._input.modify_text(gtk.STATE_NORMAL, text_color) def set_tooltip_text(self, text): try: @@ -317,7 +330,9 @@ class FileParam(EntryParam): if self.param.get_key() == 'qt_qss_theme': dirname = os.path.dirname(dirname) # trim filename if not os.path.exists(dirname): - dirname = os.path.join(Constants.GR_PREFIX, '/share/gnuradio/themes') + platform = self.param.get_parent().get_parent().get_parent() + dirname = os.path.join(platform.config.install_prefix, + '/share/gnuradio/themes') if not os.path.exists(dirname): dirname = os.getcwd() # fix bad paths @@ -378,11 +393,12 @@ Error: #end if""" -class Param(Element): +class Param(Element, _Param): """The graphical parameter.""" - def __init__(self): + def __init__(self, **kwargs): Element.__init__(self) + _Param.__init__(self, **kwargs) def get_input(self, *args, **kwargs): """ diff --git a/grc/gui/Platform.py b/grc/gui/Platform.py index eda28a0e94..500df1cce4 100644 --- a/grc/gui/Platform.py +++ b/grc/gui/Platform.py @@ -17,12 +17,55 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ -from Element import Element +import os +import sys -class Platform(Element): - def __init__(self, prefs_file): +from ..core.Platform import Platform as _Platform + +from .Config import Config as _Config +from .Block import Block as _Block +from .Connection import Connection as _Connection +from .Element import Element +from .FlowGraph import FlowGraph as _FlowGraph +from .Param import Param as _Param +from .Port import Port as _Port + + +class Platform(Element, _Platform): + + def __init__(self, *args, **kwargs): Element.__init__(self) + _Platform.__init__(self, *args, **kwargs) + + # Ensure conf directories + gui_prefs_file = self.config.gui_prefs_file + if not os.path.exists(os.path.dirname(gui_prefs_file)): + os.mkdir(os.path.dirname(gui_prefs_file)) + + self._move_old_pref_file() + + def get_prefs_file(self): + return self.config.gui_prefs_file - self._prefs_file = prefs_file + def _move_old_pref_file(self): + gui_prefs_file = self.config.gui_prefs_file + old_gui_prefs_file = os.environ.get( + 'GRC_PREFS_PATH', os.path.expanduser('~/.grc')) + if gui_prefs_file == old_gui_prefs_file: + return # prefs file overridden with env var + if os.path.exists(old_gui_prefs_file) and not os.path.exists(gui_prefs_file): + try: + import shutil + shutil.move(old_gui_prefs_file, gui_prefs_file) + except Exception as e: + print >> sys.stderr, e - def get_prefs_file(self): return self._prefs_file + ############################################## + # Constructors + ############################################## + FlowGraph = _FlowGraph + Connection = _Connection + Block = _Block + Port = _Port + Param = _Param + Config = _Config diff --git a/grc/gui/Port.py b/grc/gui/Port.py index ae1a1d223f..6314b7ede8 100644 --- a/grc/gui/Port.py +++ b/grc/gui/Port.py @@ -17,32 +17,33 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ -from Element import Element -from Constants import ( - PORT_SEPARATION, PORT_SPACING, CONNECTOR_EXTENSION_MINIMAL, - CONNECTOR_EXTENSION_INCREMENT, CANVAS_GRID_SIZE, - PORT_LABEL_PADDING, PORT_MIN_WIDTH, PORT_LABEL_HIDDEN_WIDTH, PORT_FONT -) -from .. base.Constants import DEFAULT_DOMAIN, GR_MESSAGE_DOMAIN -import Utils -import Actions -import Colors import pygtk pygtk.require('2.0') import gtk +from . import Actions, Colors, Utils +from .Constants import ( + PORT_SEPARATION, PORT_SPACING, CONNECTOR_EXTENSION_MINIMAL, + CONNECTOR_EXTENSION_INCREMENT, PORT_LABEL_PADDING, PORT_MIN_WIDTH, PORT_LABEL_HIDDEN_WIDTH, PORT_FONT +) +from .Element import Element +from ..core.Constants import DEFAULT_DOMAIN, GR_MESSAGE_DOMAIN + +from ..core.Port import Port as _Port + PORT_MARKUP_TMPL="""\ <span foreground="black" font_desc="$font">$encode($port.get_name())</span>""" -class Port(Element): +class Port(_Port, Element): """The graphical port.""" - def __init__(self): + def __init__(self, block, n, dir): """ Port contructor. Create list of connector coordinates. """ + _Port.__init__(self, block, n, dir) Element.__init__(self) self.W = self.H = self.w = self.h = 0 self._connector_coordinate = (0, 0) @@ -63,7 +64,7 @@ class Port(Element): rotation = self.get_rotation() #get all sibling ports ports = self.get_parent().get_sources_gui() \ - if self.is_source() else self.get_parent().get_sinks_gui() + if self.is_source else self.get_parent().get_sinks_gui() ports = filter(lambda p: not p.get_hide(), ports) #get the max width self.W = max([port.W for port in ports] + [PORT_MIN_WIDTH]) @@ -81,27 +82,27 @@ class Port(Element): index = length-index-1 port_separation = PORT_SEPARATION \ - if not self.get_parent().has_busses[self.is_source()] \ + if not self.get_parent().has_busses[self.is_source] \ else max([port.H for port in ports]) + PORT_SPACING offset = (self.get_parent().H - (length-1)*port_separation - self.H)/2 #create areas and connector coordinates - if (self.is_sink() and rotation == 0) or (self.is_source() and rotation == 180): + if (self.is_sink and rotation == 0) or (self.is_source and rotation == 180): x = -W y = port_separation*index+offset self.add_area((x, y), (W, self.H)) self._connector_coordinate = (x-1, y+self.H/2) - elif (self.is_source() and rotation == 0) or (self.is_sink() and rotation == 180): + elif (self.is_source and rotation == 0) or (self.is_sink and rotation == 180): x = self.get_parent().W y = port_separation*index+offset self.add_area((x, y), (W, self.H)) self._connector_coordinate = (x+1+W, y+self.H/2) - elif (self.is_source() and rotation == 90) or (self.is_sink() and rotation == 270): + elif (self.is_source and rotation == 90) or (self.is_sink and rotation == 270): y = -W x = port_separation*index+offset self.add_area((x, y), (self.H, W)) self._connector_coordinate = (x+self.H/2, y-1) - elif (self.is_sink() and rotation == 90) or (self.is_source() and rotation == 270): + elif (self.is_sink and rotation == 90) or (self.is_source and rotation == 270): y = self.get_parent().W x = port_separation*index+offset self.add_area((x, y), (self.H, W)) @@ -144,7 +145,7 @@ class Port(Element): Element.draw( self, gc, window, bg_color=self._bg_color, border_color=self.is_highlighted() and Colors.HIGHLIGHT_COLOR or - self.get_parent().is_dummy_block() and Colors.MISSING_BLOCK_BORDER_COLOR or + self.get_parent().is_dummy_block and Colors.MISSING_BLOCK_BORDER_COLOR or Colors.BORDER_COLOR, ) if not self._areas_list or self._label_hidden(): @@ -176,8 +177,8 @@ class Port(Element): Returns: the direction in degrees """ - if self.is_source(): return self.get_rotation() - elif self.is_sink(): return (self.get_rotation() + 180)%360 + if self.is_source: return self.get_rotation() + elif self.is_sink: return (self.get_rotation() + 180)%360 def get_connector_length(self): """ diff --git a/grc/gui/Preferences.py b/grc/gui/Preferences.py index 3ebee24345..5fbdfe927a 100644 --- a/grc/gui/Preferences.py +++ b/grc/gui/Preferences.py @@ -35,7 +35,7 @@ _config_parser = ConfigParser.SafeConfigParser() def file_extension(): - return '.'+_platform.get_key() + return '.grc' def load(platform): @@ -140,13 +140,34 @@ def add_recent_file(file_name): set_recent_files(recent_files[:10]) # Keep up to 10 files -def reports_window_position(pos=None): - return entry('reports_window_position', pos, default=-1) or 1 +def console_window_position(pos=None): + return entry('console_window_position', pos, default=-1) or 1 def blocks_window_position(pos=None): return entry('blocks_window_position', pos, default=-1) or 1 +def variable_editor_position(pos=None, sidebar=False): + # Figure out default + if sidebar: + w, h = main_window_size() + return entry('variable_editor_sidebar_position', pos, default=int(h*0.7)) + else: + return entry('variable_editor_position', pos, default=int(blocks_window_position()*0.5)) + + +def variable_editor_sidebar(pos=None): + return entry('variable_editor_sidebar', pos, default=False) + + +def variable_editor_confirm_delete(pos=None): + return entry('variable_editor_confirm_delete', pos, default=True) + + def xterm_missing(cmd=None): return entry('xterm_missing', cmd, default='INVALID_XTERM_SETTING') + + +def screen_shot_background_transparent(transparent=None): + return entry('screen_shot_background_transparent', transparent, default=False) diff --git a/grc/gui/PropsDialog.py b/grc/gui/PropsDialog.py index bf7d31d391..7c66a77a54 100644 --- a/grc/gui/PropsDialog.py +++ b/grc/gui/PropsDialog.py @@ -97,7 +97,8 @@ class PropsDialog(gtk.Dialog): self._params_boxes.append((tab, label, vbox)) # Docs for the block - self._docs_text_display = SimpleTextDisplay() + self._docs_text_display = doc_view = SimpleTextDisplay() + doc_view.get_buffer().create_tag('b', weight=pango.WEIGHT_BOLD) self._docs_box = gtk.ScrolledWindow() self._docs_box.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self._docs_box.add_with_viewport(self._docs_text_display) @@ -200,10 +201,43 @@ class PropsDialog(gtk.Dialog): messages = '\n\n'.join(self._block.get_error_messages()) self._error_messages_text_display.set_text(messages) # update the docs box - self._docs_text_display.set_text(self._block.get_doc()) + self._update_docs_page() # update the generated code self._update_generated_code_page() + def _update_docs_page(self): + """Show documentation from XML and try to display best matching docstring""" + buffer = self._docs_text_display.get_buffer() + buffer.delete(buffer.get_start_iter(), buffer.get_end_iter()) + pos = buffer.get_end_iter() + + docstrings = self._block.get_doc() + if not docstrings: + return + + # show documentation string from block xml + from_xml = docstrings.pop('', '') + for line in from_xml.splitlines(): + if line.lstrip() == line and line.endswith(':'): + buffer.insert_with_tags_by_name(pos, line + '\n', 'b') + else: + buffer.insert(pos, line + '\n') + if from_xml: + buffer.insert(pos, '\n') + + # if given the current parameters an exact match can be made + block_constructor = self._block.get_make().rsplit('.', 2)[-1] + block_class = block_constructor.partition('(')[0].strip() + if block_class in docstrings: + docstrings = {block_class: docstrings[block_class]} + + # show docstring(s) extracted from python sources + for cls_name, docstring in docstrings.iteritems(): + buffer.insert_with_tags_by_name(pos, cls_name + '\n', 'b') + buffer.insert(pos, docstring + '\n\n') + pos.backward_chars(2) + buffer.delete(pos, buffer.get_end_iter()) + def _update_generated_code_page(self): if not self._code_text_display: return # user disabled code preview diff --git a/grc/gui/Utils.py b/grc/gui/Utils.py index f20e3c0fa6..51b9b19e9f 100644 --- a/grc/gui/Utils.py +++ b/grc/gui/Utils.py @@ -123,10 +123,11 @@ class TemplateParser(object): parse_template = TemplateParser() -def align_to_grid(coor): - _align = lambda: int(round(x / (1.0 * CANVAS_GRID_SIZE)) * CANVAS_GRID_SIZE) +def align_to_grid(coor, mode=round): + def align(value): + return int(mode(value / (1.0 * CANVAS_GRID_SIZE)) * CANVAS_GRID_SIZE) try: - return [_align() for x in coor] + return map(align, coor) except TypeError: x = coor - return _align() + return align(coor) diff --git a/grc/gui/VariableEditor.py b/grc/gui/VariableEditor.py new file mode 100644 index 0000000000..7721f3bda6 --- /dev/null +++ b/grc/gui/VariableEditor.py @@ -0,0 +1,354 @@ +""" +Copyright 2015 Free Software Foundation, Inc. +This file is part of GNU Radio + +GNU Radio Companion 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 2 +of the License, or (at your option) any later version. + +GNU Radio Companion 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 +""" + +from operator import attrgetter + +import pygtk +pygtk.require('2.0') +import gtk +import gobject + +from . import Actions +from . import Preferences +from .Constants import DEFAULT_BLOCKS_WINDOW_WIDTH + +BLOCK_INDEX = 0 +ID_INDEX = 1 + + +class VariableEditorContextMenu(gtk.Menu): + """ A simple context menu for our variable editor """ + def __init__(self, var_edit): + gtk.Menu.__init__(self) + + self.imports = gtk.MenuItem("Add _Import") + self.imports.connect('activate', var_edit.handle_action, var_edit.ADD_IMPORT) + self.add(self.imports) + + self.variables = gtk.MenuItem("Add _Variable") + self.variables.connect('activate', var_edit.handle_action, var_edit.ADD_VARIABLE) + self.add(self.variables) + self.add(gtk.SeparatorMenuItem()) + + self.enable = gtk.MenuItem("_Enable") + self.enable.connect('activate', var_edit.handle_action, var_edit.ENABLE_BLOCK) + self.disable = gtk.MenuItem("_Disable") + self.disable.connect('activate', var_edit.handle_action, var_edit.DISABLE_BLOCK) + self.add(self.enable) + self.add(self.disable) + self.add(gtk.SeparatorMenuItem()) + + self.delete = gtk.MenuItem("_Delete") + self.delete.connect('activate', var_edit.handle_action, var_edit.DELETE_BLOCK) + self.add(self.delete) + self.add(gtk.SeparatorMenuItem()) + + self.properties = gtk.MenuItem("_Properties...") + self.properties.connect('activate', var_edit.handle_action, var_edit.OPEN_PROPERTIES) + self.add(self.properties) + self.show_all() + + def update_sensitive(self, selected, enabled=False): + self.delete.set_sensitive(selected) + self.properties.set_sensitive(selected) + self.enable.set_sensitive(selected and not enabled) + self.disable.set_sensitive(selected and enabled) + + +class VariableEditor(gtk.VBox): + + # Actions that are handled by the editor + ADD_IMPORT = 0 + ADD_VARIABLE = 1 + OPEN_PROPERTIES = 2 + DELETE_BLOCK = 3 + DELETE_CONFIRM = 4 + ENABLE_BLOCK = 5 + DISABLE_BLOCK = 6 + + def __init__(self, platform, get_flow_graph): + gtk.VBox.__init__(self) + self.platform = platform + self.get_flow_graph = get_flow_graph + self._block = None + self._mouse_button_pressed = False + + # Only use the model to store the block reference and name. + # Generate everything else dynamically + self.treestore = gtk.TreeStore(gobject.TYPE_PYOBJECT, # Block reference + gobject.TYPE_STRING) # Category and block name + self.treeview = gtk.TreeView(self.treestore) + self.treeview.set_enable_search(False) + self.treeview.set_search_column(-1) + #self.treeview.set_enable_search(True) + #self.treeview.set_search_column(ID_INDEX) + self.treeview.get_selection().set_mode('single') + self.treeview.set_headers_visible(True) + self.treeview.connect('button-press-event', self._handle_mouse_button_press) + self.treeview.connect('button-release-event', self._handle_mouse_button_release) + self.treeview.connect('motion-notify-event', self._handle_motion_notify) + self.treeview.connect('key-press-event', self._handle_key_button_press) + + # Block Name or Category + self.id_cell = gtk.CellRendererText() + self.id_cell.connect('edited', self._handle_name_edited_cb) + id_column = gtk.TreeViewColumn("Id", self.id_cell, text=ID_INDEX) + id_column.set_name("id") + id_column.set_resizable(True) + id_column.set_max_width(300) + id_column.set_min_width(80) + id_column.set_fixed_width(100) + id_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) + id_column.set_cell_data_func(self.id_cell, self.set_properties) + self.id_column = id_column + self.treeview.append_column(id_column) + self.treestore.set_sort_column_id(ID_INDEX, gtk.SORT_ASCENDING) + # For forcing resize + self._col_width = 0 + + # Block Value + self.value_cell = gtk.CellRendererText() + self.value_cell.connect('edited', self._handle_value_edited_cb) + value_column = gtk.TreeViewColumn("Value", self.value_cell) + value_column.set_name("value") + value_column.set_resizable(False) + value_column.set_expand(True) + value_column.set_min_width(100) + value_column.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE) + value_column.set_cell_data_func(self.value_cell, self.set_value) + self.value_column = value_column + self.treeview.append_column(value_column) + + # Block Actions (Add, Remove) + self.action_cell = gtk.CellRendererPixbuf() + value_column.pack_start(self.action_cell, False) + value_column.set_cell_data_func(self.action_cell, self.set_icon) + + # Make the scrolled window to hold the tree view + scrolled_window = gtk.ScrolledWindow() + scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + scrolled_window.add_with_viewport(self.treeview) + scrolled_window.set_size_request(DEFAULT_BLOCKS_WINDOW_WIDTH, -1) + self.pack_start(scrolled_window) + + # Context menus + self._context_menu = VariableEditorContextMenu(self) + self._confirm_delete = Preferences.variable_editor_confirm_delete() + + # Sets cell contents + def set_icon(self, col, cell, model, iter): + block = model.get_value(iter, BLOCK_INDEX) + if block: + pb = self.treeview.render_icon(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU, None) + else: + pb = self.treeview.render_icon(gtk.STOCK_ADD, gtk.ICON_SIZE_MENU, None) + cell.set_property('pixbuf', pb) + + def set_value(self, col, cell, model, iter): + sp = cell.set_property + block = model.get_value(iter, BLOCK_INDEX) + + # Set the default properties for this column first. + # Some set in set_properties() may be overridden (editable for advanced variable blocks) + self.set_properties(col, cell, model, iter) + + # Set defaults + value = None + self.set_tooltip_text(None) + + # Block specific values + if block: + if block.get_key() == 'import': + value = block.get_param('import').get_value() + elif block.get_key() != "variable": + value = "<Open Properties>" + sp('editable', False) + sp('foreground', '#0D47A1') + else: + value = block.get_param('value').get_value() + + # Check if there are errors in the blocks. + # Show the block error as a tooltip + error_message = block.get_error_messages() + if len(error_message) > 0: + # Set the error message to the last error in the list. + # This should be the first message generated + self.set_tooltip_text(error_message[-1]) + else: + # Evaluate and show the value (if it is a variable) + if block.get_key() == "variable": + evaluated = str(block.get_param('value').evaluate()) + self.set_tooltip_text(evaluated) + # Always set the text value. + sp('text', value) + + def set_properties(self, col, cell, model, iter): + sp = cell.set_property + block = model.get_value(iter, BLOCK_INDEX) + # Set defaults + sp('sensitive', True) + sp('editable', False) + sp('foreground', None) + + # Block specific changes + if block: + if not block.get_enabled(): + # Disabled block. But, this should still be editable + sp('editable', True) + sp('foreground', 'gray') + else: + sp('editable', True) + if block.get_error_messages(): + sp('foreground', 'red') + + def update_gui(self): + if not self.get_flow_graph(): + return + self._update_blocks() + self._rebuild() + self.treeview.expand_all() + + def _update_blocks(self): + self._imports = filter(attrgetter('is_import'), + self.get_flow_graph().blocks) + self._variables = filter(attrgetter('is_variable'), + self.get_flow_graph().blocks) + + def _rebuild(self, *args): + self.treestore.clear() + imports = self.treestore.append(None, [None, 'Imports']) + variables = self.treestore.append(None, [None, 'Variables']) + for block in self._imports: + self.treestore.append(imports, [block, block.get_param('id').get_value()]) + for block in sorted(self._variables, key=lambda v: v.get_id()): + self.treestore.append(variables, [block, block.get_param('id').get_value()]) + + def _handle_name_edited_cb(self, cell, path, new_text): + block = self.treestore[path][BLOCK_INDEX] + block.get_param('id').set_value(new_text) + Actions.VARIABLE_EDITOR_UPDATE() + + def _handle_value_edited_cb(self, cell, path, new_text): + block = self.treestore[path][BLOCK_INDEX] + if block.is_import: + block.get_param('import').set_value(new_text) + else: + block.get_param('value').set_value(new_text) + Actions.VARIABLE_EDITOR_UPDATE() + + def handle_action(self, item, key, event=None): + """ + Single handler for the different actions that can be triggered by the context menu, + key presses or mouse clicks. Also triggers an update of the flow graph and editor. + """ + if key == self.ADD_IMPORT: + self.get_flow_graph().add_new_block('import') + elif key == self.ADD_VARIABLE: + self.get_flow_graph().add_new_block('variable') + elif key == self.OPEN_PROPERTIES: + Actions.BLOCK_PARAM_MODIFY(self._block) + elif key == self.DELETE_BLOCK: + self.get_flow_graph().remove_element(self._block) + elif key == self.DELETE_CONFIRM: + if self._confirm_delete: + # Create a context menu to confirm the delete operation + confirmation_menu = gtk.Menu() + block_id = self._block.get_param('id').get_value().replace("_", "__") + confirm = gtk.MenuItem("Delete {}".format(block_id)) + confirm.connect('activate', self.handle_action, self.DELETE_BLOCK) + confirmation_menu.add(confirm) + confirmation_menu.show_all() + confirmation_menu.popup(None, None, None, event.button, event.time) + else: + self.handle_action(None, self.DELETE_BLOCK, None) + elif key == self.ENABLE_BLOCK: + self._block.set_enabled(True) + elif key == self.DISABLE_BLOCK: + self._block.set_enabled(False) + Actions.VARIABLE_EDITOR_UPDATE() + + def _handle_mouse_button_press(self, widget, event): + """ + Handles mouse button for several different events: + - Double Click to open properties for advanced blocks + - Click to add/remove blocks + """ + # Save the column width to see if it changes on button_release + self._mouse_button_pressed = True + self._col_width = self.id_column.get_width() + + path = widget.get_path_at_pos(int(event.x), int(event.y)) + if path: + # If there is a valid path, then get the row, column and block selected. + row = self.treestore[path[0]] + col = path[1] + self._block = row[BLOCK_INDEX] + + if event.button == 1 and col.get_name() == "value": + # Make sure this has a block (not the import/variable rows) + if self._block and event.type == gtk.gdk._2BUTTON_PRESS: + # Open the advanced dialog if it is a gui variable + if self._block.get_key() not in ("variable", "import"): + self.handle_action(None, self.OPEN_PROPERTIES, event=event) + return True + if event.type == gtk.gdk.BUTTON_PRESS: + # User is adding/removing blocks + # Make sure this is the action cell (Add/Remove Icons) + if path[2] > col.cell_get_position(self.action_cell)[0]: + if row[1] == "Imports": + # Add a new import block. + self.handle_action(None, self.ADD_IMPORT, event=event) + elif row[1] == "Variables": + # Add a new variable block + self.handle_action(None, self.ADD_VARIABLE, event=event) + else: + self.handle_action(None, self.DELETE_CONFIRM, event=event) + return True + elif event.button == 3 and event.type == gtk.gdk.BUTTON_PRESS: + if self._block: + self._context_menu.update_sensitive(True, enabled=self._block.get_enabled()) + else: + self._context_menu.update_sensitive(False) + self._context_menu.popup(None, None, None, event.button, event.time) + + # Null handler. Stops the treeview from handling double click events. + if event.type == gtk.gdk._2BUTTON_PRESS: + return True + return False + + def _handle_mouse_button_release(self, widget, event): + self._mouse_button_pressed = False + return False + + def _handle_motion_notify(self, widget, event): + # Check to see if the column size has changed + if self._mouse_button_pressed and self.id_column.get_width() != self._col_width: + self.value_column.queue_resize() + return False + + def _handle_key_button_press(self, widget, event): + model, path = self.treeview.get_selection().get_selected_rows() + if path and self._block: + if self._block.get_enabled() and event.string == "d": + self.handle_action(None, self.DISABLE_BLOCK, None) + return True + elif not self._block.get_enabled() and event.string == "e": + self.handle_action(None, self.ENABLE_BLOCK, None) + return True + return False diff --git a/grc/main.py b/grc/main.py new file mode 100755 index 0000000000..ae7a0ce115 --- /dev/null +++ b/grc/main.py @@ -0,0 +1,55 @@ +# Copyright 2009-2016 Free Software Foundation, Inc. +# This file is part of GNU Radio +# +# GNU Radio Companion 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 2 +# of the License, or (at your option) any later version. +# +# GNU Radio Companion 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 optparse + +import gtk +from gnuradio import gr + +from .gui.Platform import Platform +from .gui.ActionHandler import ActionHandler + + +VERSION_AND_DISCLAIMER_TEMPLATE = """\ +GNU Radio Companion %s + +This program is part of GNU Radio +GRC comes with ABSOLUTELY NO WARRANTY. +This is free software, and you are welcome to redistribute it. +""" + + +def main(): + parser = optparse.OptionParser( + usage='usage: %prog [options] [saved flow graphs]', + version=VERSION_AND_DISCLAIMER_TEMPLATE % gr.version()) + options, args = parser.parse_args() + + try: + gtk.window_set_default_icon(gtk.IconTheme().load_icon('gnuradio-grc', 256, 0)) + except: + pass + + platform = Platform( + prefs_file=gr.prefs(), + version=gr.version(), + version_parts=(gr.major_version(), gr.api_version(), gr.minor_version()), + install_prefix=gr.prefix() + ) + ActionHandler(args, platform) + gtk.main() + diff --git a/grc/python/Block.py b/grc/python/Block.py deleted file mode 100644 index 782893fd8f..0000000000 --- a/grc/python/Block.py +++ /dev/null @@ -1,323 +0,0 @@ -""" -Copyright 2008-2011 Free Software Foundation, Inc. -This file is part of GNU Radio - -GNU Radio Companion 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 2 -of the License, or (at your option) any later version. - -GNU Radio Companion 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 itertools -import collections - -from .. base.Constants import BLOCK_FLAG_NEED_QT_GUI, BLOCK_FLAG_NEED_WX_GUI -from .. base.odict import odict - -from .. base.Block import Block as _Block -from .. gui.Block import Block as _GUIBlock - -from . FlowGraph import _variable_matcher -from . import epy_block_io - - -class Block(_Block, _GUIBlock): - - def __init__(self, flow_graph, n): - """ - Make a new block from nested data. - - Args: - flow: graph the parent element - n: the nested odict - - Returns: - block a new block - """ - #grab the data - self._doc = (n.find('doc') or '').strip('\n').replace('\\\n', '') - self._imports = map(lambda i: i.strip(), n.findall('import')) - self._make = n.find('make') - self._var_make = n.find('var_make') - self._checks = n.findall('check') - self._callbacks = n.findall('callback') - self._bus_structure_source = n.find('bus_structure_source') or '' - self._bus_structure_sink = n.find('bus_structure_sink') or '' - self.port_counters = [itertools.count(), itertools.count()] - #build the block - _Block.__init__( - self, - flow_graph=flow_graph, - n=n, - ) - _GUIBlock.__init__(self) - - self._epy_source_hash = -1 # for epy blocks - self._epy_reload_error = None - - def get_bus_structure(self, direction): - if direction == 'source': - bus_structure = self._bus_structure_source; - else: - bus_structure = self._bus_structure_sink; - - bus_structure = self.resolve_dependencies(bus_structure); - - if not bus_structure: return '' - try: - clean_bus_structure = self.get_parent().evaluate(bus_structure) - return clean_bus_structure - - except: return '' - - def validate(self): - """ - Validate this block. - Call the base class validate. - Evaluate the checks: each check must evaluate to True. - """ - _Block.validate(self) - #evaluate the checks - for check in self._checks: - check_res = self.resolve_dependencies(check) - try: - if not self.get_parent().evaluate(check_res): - self.add_error_message('Check "%s" failed.'%check) - except: self.add_error_message('Check "%s" did not evaluate.'%check) - # for variables check the value (only if var_value is used - if _variable_matcher.match(self.get_key()) and self._var_value != '$value': - value = self._var_value - try: - value = self.get_var_value() - self.get_parent().evaluate(value) - except Exception as err: - self.add_error_message('Value "%s" cannot be evaluated:\n%s' % (value, err)) - - # check if this is a GUI block and matches the selected generate option - current_generate_option = self.get_parent().get_option('generate_options') - - def check_generate_mode(label, flag, valid_options): - block_requires_mode = ( - flag in self.get_flags() or - self.get_name().upper().startswith(label) - ) - if block_requires_mode and current_generate_option not in valid_options: - self.add_error_message("Can't generate this block in mode " + - repr(current_generate_option)) - - check_generate_mode('WX GUI', BLOCK_FLAG_NEED_WX_GUI, ('wx_gui',)) - check_generate_mode('QT GUI', BLOCK_FLAG_NEED_QT_GUI, ('qt_gui', 'hb_qt_gui')) - if self._epy_reload_error: - self.get_param('_source_code').add_error_message(str(self._epy_reload_error)) - - def rewrite(self): - """ - Add and remove ports to adjust for the nports. - """ - _Block.rewrite(self) - # Check and run any custom rewrite function for this block - getattr(self, 'rewrite_' + self._key, lambda: None)() - - # adjust nports, disconnect hidden ports - for ports in (self.get_sources(), self.get_sinks()): - for i, master_port in enumerate(ports): - nports = master_port.get_nports() or 1 - num_ports = 1 + len(master_port.get_clones()) - if master_port.get_hide(): - for connection in master_port.get_connections(): - self.get_parent().remove_element(connection) - if not nports and num_ports == 1: # not a master port and no left-over clones - continue - # remove excess cloned ports - for port in master_port.get_clones()[nports-1:]: - # remove excess connections - for connection in port.get_connections(): - self.get_parent().remove_element(connection) - master_port.remove_clone(port) - ports.remove(port) - # add more cloned ports - for j in range(num_ports, nports): - port = master_port.add_clone() - ports.insert(ports.index(master_port) + j, port) - - self.back_ofthe_bus(ports) - # renumber non-message/-msg ports - domain_specific_port_index = collections.defaultdict(int) - for port in filter(lambda p: p.get_key().isdigit(), ports): - domain = port.get_domain() - port._key = str(domain_specific_port_index[domain]) - domain_specific_port_index[domain] += 1 - - def port_controller_modify(self, direction): - """ - Change the port controller. - - Args: - direction: +1 or -1 - - Returns: - true for change - """ - changed = False - #concat the nports string from the private nports settings of all ports - nports_str = ' '.join([port._nports for port in self.get_ports()]) - #modify all params whose keys appear in the nports string - for param in self.get_params(): - if param.is_enum() or param.get_key() not in nports_str: continue - #try to increment the port controller by direction - try: - value = param.get_evaluated() - value = value + direction - if 0 < value: - param.set_value(value) - changed = True - except: pass - return changed - - def get_doc(self): - platform = self.get_parent().get_parent() - extracted_docs = platform.block_docstrings.get(self._key, '') - return (self._doc + '\n\n' + extracted_docs).strip() - - def get_category(self): - return _Block.get_category(self) - - def get_imports(self, raw=False): - """ - Resolve all import statements. - Split each import statement at newlines. - Combine all import statments into a list. - Filter empty imports. - - Returns: - a list of import statements - """ - if raw: - return self._imports - return filter(lambda i: i, sum(map(lambda i: self.resolve_dependencies(i).split('\n'), self._imports), [])) - - def get_make(self, raw=False): - if raw: - return self._make - return self.resolve_dependencies(self._make) - - def get_var_make(self): - return self.resolve_dependencies(self._var_make) - - def get_var_value(self): - return self.resolve_dependencies(self._var_value) - - def get_callbacks(self): - """ - Get a list of function callbacks for this block. - - Returns: - a list of strings - """ - def make_callback(callback): - callback = self.resolve_dependencies(callback) - if 'self.' in callback: return callback - return 'self.%s.%s'%(self.get_id(), callback) - return map(make_callback, self._callbacks) - - def is_virtual_sink(self): - return self.get_key() == 'virtual_sink' - - def is_virtual_source(self): - return self.get_key() == 'virtual_source' - - ########################################################################### - # Custom rewrite functions - ########################################################################### - - def rewrite_epy_block(self): - flowgraph = self.get_parent() - platform = flowgraph.get_parent() - param_blk = self.get_param('_io_cache') - param_src = self.get_param('_source_code') - doc_end_tag = 'Block Documentation:' - - src = param_src.get_value() - src_hash = hash((self.get_id(), src)) - if src_hash == self._epy_source_hash: - return - - try: - blk_io = epy_block_io.extract(src) - - except Exception as e: - self._epy_reload_error = ValueError(str(e)) - try: # load last working block io - blk_io = epy_block_io.BlockIO(*eval(param_blk.get_value())) - except: - return - else: - self._epy_reload_error = None # clear previous errors - param_blk.set_value(repr(tuple(blk_io))) - - # print "Rewriting embedded python block {!r}".format(self.get_id()) - self._epy_source_hash = src_hash - self._name = blk_io.name or blk_io.cls - self._doc = self._doc.split(doc_end_tag)[0] + doc_end_tag + '\n' + blk_io.doc - self._imports[0] = 'import ' + self.get_id() - self._make = '{0}.{1}({2})'.format(self.get_id(), blk_io.cls, ', '.join( - '{0}=${0}'.format(key) for key, _ in blk_io.params)) - - params = {} - for param in list(self._params): - if hasattr(param, '__epy_param__'): - params[param.get_key()] = param - self._params.remove(param) - - for key, value in blk_io.params: - try: - param = params[key] - param.set_default(value) - except KeyError: # need to make a new param - name = key.replace('_', ' ').title() - n = odict(dict(name=name, key=key, type='raw', value=value)) - param = platform.Param(block=self, n=n) - setattr(param, '__epy_param__', True) - self._params.append(param) - - def update_ports(label, ports, port_specs, direction): - ports_to_remove = list(ports) - iter_ports = iter(ports) - ports_new = [] - port_current = next(iter_ports, None) - for key, port_type in port_specs: - reuse_port = ( - port_current is not None and - port_current.get_type() == port_type and - (key.isdigit() or port_current.get_key() == key) - ) - if reuse_port: - ports_to_remove.remove(port_current) - port, port_current = port_current, next(iter_ports, None) - else: - n = odict(dict(name=label + str(key), type=port_type, key=key)) - if port_type == 'message': - n['name'] = key - n['optional'] = '1' - port = platform.Port(block=self, n=n, dir=direction) - ports_new.append(port) - # replace old port list with new one - del ports[:] - ports.extend(ports_new) - # remove excess port connections - for port in ports_to_remove: - for connection in port.get_connections(): - flowgraph.remove_element(connection) - - update_ports('in', self.get_sinks(), blk_io.sinks, 'sink') - update_ports('out', self.get_sources(), blk_io.sources, 'source') - _Block.rewrite(self) diff --git a/grc/python/Connection.py b/grc/python/Connection.py deleted file mode 100644 index 822876a0ae..0000000000 --- a/grc/python/Connection.py +++ /dev/null @@ -1,45 +0,0 @@ -""" -Copyright 2008-2011 Free Software Foundation, Inc. -This file is part of GNU Radio - -GNU Radio Companion 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 2 -of the License, or (at your option) any later version. - -GNU Radio Companion 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 Constants -from .. base.Connection import Connection as _Connection -from .. gui.Connection import Connection as _GUIConnection - -class Connection(_Connection, _GUIConnection): - - def __init__(self, **kwargs): - _Connection.__init__(self, **kwargs) - _GUIConnection.__init__(self) - - def is_msg(self): - return self.get_source().get_type() == self.get_sink().get_type() == 'msg' - - def is_bus(self): - return self.get_source().get_type() == self.get_sink().get_type() == 'bus' - - def validate(self): - """ - Validate the connections. - The ports must match in io size. - """ - _Connection.validate(self) - source_size = Constants.TYPE_TO_SIZEOF[self.get_source().get_type()] * self.get_source().get_vlen() - sink_size = Constants.TYPE_TO_SIZEOF[self.get_sink().get_type()] * self.get_sink().get_vlen() - if source_size != sink_size: - self.add_error_message('Source IO size "%s" does not match sink IO size "%s".'%(source_size, sink_size)) diff --git a/grc/python/FlowGraph.py b/grc/python/FlowGraph.py deleted file mode 100644 index b2a1d27859..0000000000 --- a/grc/python/FlowGraph.py +++ /dev/null @@ -1,338 +0,0 @@ -""" -Copyright 2008-2011 Free Software Foundation, Inc. -This file is part of GNU Radio - -GNU Radio Companion 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 2 -of the License, or (at your option) any later version. - -GNU Radio Companion 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 re -import imp -from operator import methodcaller - -from . import expr_utils -from .. base.FlowGraph import FlowGraph as _FlowGraph -from .. gui.FlowGraph import FlowGraph as _GUIFlowGraph - -_variable_matcher = re.compile('^(variable\w*)$') -_parameter_matcher = re.compile('^(parameter)$') -_monitors_searcher = re.compile('(ctrlport_monitor)') -_bussink_searcher = re.compile('^(bus_sink)$') -_bussrc_searcher = re.compile('^(bus_source)$') -_bus_struct_sink_searcher = re.compile('^(bus_structure_sink)$') -_bus_struct_src_searcher = re.compile('^(bus_structure_source)$') - - -class FlowGraph(_FlowGraph, _GUIFlowGraph): - - def __init__(self, **kwargs): - self.grc_file_path = '' - _FlowGraph.__init__(self, **kwargs) - _GUIFlowGraph.__init__(self) - self.n = {} - self.n_hash = -1 - self._renew_eval_ns = True - self._eval_cache = {} - - def _eval(self, code, namespace, namespace_hash): - """ - Evaluate the code with the given namespace. - - Args: - code: a string with python code - namespace: a dict representing the namespace - namespace_hash: a unique hash for the namespace - - Returns: - the resultant object - """ - if not code: raise Exception, 'Cannot evaluate empty statement.' - my_hash = hash(code) ^ namespace_hash - #cache if does not exist - if not self._eval_cache.has_key(my_hash): - self._eval_cache[my_hash] = eval(code, namespace, namespace) - #return from cache - return self._eval_cache[my_hash] - - def get_hier_block_stream_io(self, direction): - """ - Get a list of stream io signatures for this flow graph. - - Args: - direction: a string of 'in' or 'out' - - Returns: - a list of dicts with: type, label, vlen, size, optional - """ - return filter(lambda p: p['type'] != "message", - self.get_hier_block_io(direction)) - - def get_hier_block_message_io(self, direction): - """ - Get a list of message io signatures for this flow graph. - - Args: - direction: a string of 'in' or 'out' - - Returns: - a list of dicts with: type, label, vlen, size, optional - """ - return filter(lambda p: p['type'] == "message", - self.get_hier_block_io(direction)) - - def get_hier_block_io(self, direction): - """ - Get a list of io ports for this flow graph. - - Args: - direction: a string of 'in' or 'out' - - Returns: - a list of dicts with: type, label, vlen, size, optional - """ - pads = self.get_pad_sources() if direction in ('sink', 'in') else \ - self.get_pad_sinks() if direction in ('source', 'out') else [] - ports = [] - for pad in pads: - master = { - 'label': str(pad.get_param('label').get_evaluated()), - 'type': str(pad.get_param('type').get_evaluated()), - 'vlen': str(pad.get_param('vlen').get_value()), - 'size': pad.get_param('type').get_opt('size'), - 'optional': bool(pad.get_param('optional').get_evaluated()), - } - num_ports = pad.get_param('num_streams').get_evaluated() - if num_ports > 1: - for i in xrange(num_ports): - clone = master.copy() - clone['label'] += str(i) - ports.append(clone) - else: - ports.append(master) - return ports - - def get_pad_sources(self): - """ - Get a list of pad source blocks sorted by id order. - - Returns: - a list of pad source blocks in this flow graph - """ - pads = filter(lambda b: b.get_key() == 'pad_source', self.get_enabled_blocks()) - return sorted(pads, lambda x, y: cmp(x.get_id(), y.get_id())) - - def get_pad_sinks(self): - """ - Get a list of pad sink blocks sorted by id order. - - Returns: - a list of pad sink blocks in this flow graph - """ - pads = filter(lambda b: b.get_key() == 'pad_sink', self.get_enabled_blocks()) - return sorted(pads, lambda x, y: cmp(x.get_id(), y.get_id())) - - def get_pad_port_global_key(self, port): - """ - Get the key for a port of a pad source/sink to use in connect() - This takes into account that pad blocks may have multiple ports - - Returns: - the key (str) - """ - key_offset = 0 - pads = self.get_pad_sources() if port.is_source() else self.get_pad_sinks() - for pad in pads: - # using the block param 'type' instead of the port domain here - # to emphasize that hier block generation is domain agnostic - is_message_pad = pad.get_param('type').get_evaluated() == "message" - if port.get_parent() == pad: - if is_message_pad: - key = pad.get_param('label').get_value() - else: - key = str(key_offset + int(port.get_key())) - return key - else: - # assuming we have either only sources or sinks - if not is_message_pad: - key_offset += len(pad.get_ports()) - return -1 - - def get_imports(self): - """ - Get a set of all import statments in this flow graph namespace. - - Returns: - a set of import statements - """ - imports = sum([block.get_imports() for block in self.get_enabled_blocks()], []) - imports = sorted(set(imports)) - return imports - - def get_variables(self): - """ - Get a list of all variables in this flow graph namespace. - Exclude paramterized variables. - - Returns: - a sorted list of variable blocks in order of dependency (indep -> dep) - """ - variables = filter(lambda b: _variable_matcher.match(b.get_key()), self.iter_enabled_blocks()) - return expr_utils.sort_objects(variables, methodcaller('get_id'), methodcaller('get_var_make')) - - def get_parameters(self): - """ - Get a list of all paramterized variables in this flow graph namespace. - - Returns: - a list of paramterized variables - """ - parameters = filter(lambda b: _parameter_matcher.match(b.get_key()), self.iter_enabled_blocks()) - return parameters - - def get_monitors(self): - """ - Get a list of all ControlPort monitors - """ - monitors = filter(lambda b: _monitors_searcher.search(b.get_key()), - self.iter_enabled_blocks()) - return monitors - - def get_python_modules(self): - """Iterate over custom code block ID and Source""" - for block in self.iter_enabled_blocks(): - if block.get_key() == 'epy_module': - yield block.get_id(), block.get_param('source_code').get_value() - - def get_bussink(self): - bussink = filter(lambda b: _bussink_searcher.search(b.get_key()), self.get_enabled_blocks()) - - for i in bussink: - for j in i.get_params(): - if j.get_name() == 'On/Off' and j.get_value() == 'on': - return True; - - return False - - def get_bussrc(self): - bussrc = filter(lambda b: _bussrc_searcher.search(b.get_key()), self.get_enabled_blocks()) - - for i in bussrc: - for j in i.get_params(): - if j.get_name() == 'On/Off' and j.get_value() == 'on': - return True; - - return False - - def get_bus_structure_sink(self): - bussink = filter(lambda b: _bus_struct_sink_searcher.search(b.get_key()), self.get_enabled_blocks()) - - return bussink - - def get_bus_structure_src(self): - bussrc = filter(lambda b: _bus_struct_src_searcher.search(b.get_key()), self.get_enabled_blocks()) - - return bussrc - - def rewrite(self): - """ - Flag the namespace to be renewed. - """ - def reconnect_bus_blocks(): - for block in self.get_blocks(): - - if 'bus' in map(lambda a: a.get_type(), block.get_sources_gui()): - - - for i in range(len(block.get_sources_gui())): - if len(block.get_sources_gui()[i].get_connections()) > 0: - source = block.get_sources_gui()[i] - sink = [] - - for j in range(len(source.get_connections())): - sink.append(source.get_connections()[j].get_sink()); - - - for elt in source.get_connections(): - self.remove_element(elt); - for j in sink: - self.connect(source, j); - self._renew_eval_ns = True - _FlowGraph.rewrite(self); - reconnect_bus_blocks(); - - def evaluate(self, expr): - """ - Evaluate the expression. - - Args: - expr: the string expression - @throw Exception bad expression - - Returns: - the evaluated data - """ - if self._renew_eval_ns: - self._renew_eval_ns = False - #reload namespace - n = dict() - #load imports - for code in self.get_imports(): - try: exec code in n - except: pass - - for id, code in self.get_python_modules(): - try: - module = imp.new_module(id) - exec code in module.__dict__ - n[id] = module - except: - pass - - #load parameters - np = dict() - for parameter in self.get_parameters(): - try: - e = eval(parameter.get_param('value').to_code(), n, n) - np[parameter.get_id()] = e - except: pass - n.update(np) #merge param namespace - #load variables - for variable in self.get_variables(): - try: - e = eval(variable.get_var_value(), n, n) - n[variable.get_id()] = e - except: pass - #make namespace public - self.n = n - self.n_hash = hash(str(n)) - #evaluate - e = self._eval(expr, self.n, self.n_hash) - return e - - def get_new_block(self, key): - """Try to auto-generate the block from file if missing""" - block = _FlowGraph.get_new_block(self, key) - if not block: - platform = self.get_parent() - # we're before the initial fg rewrite(), so no evaluated values! - # --> use raw value instead - path_param = self._options_block.get_param('hier_block_src_path') - file_path = platform.find_file_in_paths( - filename=key + '.' + platform.get_key(), - paths=path_param.get_value(), - cwd=self.grc_file_path - ) - if file_path: # grc file found. load and get block - platform.load_and_generate_flow_graph(file_path) - block = _FlowGraph.get_new_block(self, key) # can be None - return block diff --git a/grc/python/Param.py b/grc/python/Param.py deleted file mode 100644 index e60f613f00..0000000000 --- a/grc/python/Param.py +++ /dev/null @@ -1,433 +0,0 @@ -""" -Copyright 2008-2011 Free Software Foundation, Inc. -This file is part of GNU Radio - -GNU Radio Companion 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 2 -of the License, or (at your option) any later version. - -GNU Radio Companion 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 ast -import re - -from gnuradio import gr - -from .. base.Param import Param as _Param -from .. gui.Param import Param as _GUIParam - -import Constants -from Constants import VECTOR_TYPES, COMPLEX_TYPES, REAL_TYPES, INT_TYPES - -from gnuradio import eng_notation - -_check_id_matcher = re.compile('^[a-z|A-Z]\w*$') -_show_id_matcher = re.compile('^(variable\w*|parameter|options|notebook|epy_module)$') - - -#blacklist certain ids, its not complete, but should help -import __builtin__ -ID_BLACKLIST = ['self', 'options', 'gr', 'blks2', 'wxgui', 'wx', 'math', 'forms', 'firdes'] + \ - filter(lambda x: not x.startswith('_'), dir(gr.top_block())) + dir(__builtin__) - - -def num_to_str(num): - """ Display logic for numbers """ - if isinstance(num, COMPLEX_TYPES): - num = complex(num) #cast to python complex - if num == 0: return '0' #value is zero - elif num.imag == 0: return '%s'%eng_notation.num_to_str(num.real) #value is real - elif num.real == 0: return '%sj'%eng_notation.num_to_str(num.imag) #value is imaginary - elif num.imag < 0: return '%s-%sj'%(eng_notation.num_to_str(num.real), eng_notation.num_to_str(abs(num.imag))) - else: return '%s+%sj'%(eng_notation.num_to_str(num.real), eng_notation.num_to_str(num.imag)) - else: return str(num) - - -class Param(_Param, _GUIParam): - - def __init__(self, **kwargs): - _Param.__init__(self, **kwargs) - _GUIParam.__init__(self) - self._init = False - self._hostage_cells = list() - - def get_types(self): return ( - 'raw', 'enum', - 'complex', 'real', 'float', 'int', - 'complex_vector', 'real_vector', 'float_vector', 'int_vector', - 'hex', 'string', 'bool', - 'file_open', 'file_save', '_multiline', '_multiline_python_external', - 'id', 'stream_id', - 'grid_pos', 'notebook', 'gui_hint', - 'import', - ) - - def __repr__(self): - """ - Get the repr (nice string format) for this param. - - Returns: - the string representation - """ - ################################################## - # truncate helper method - ################################################## - def _truncate(string, style=0): - max_len = max(27 - len(self.get_name()), 3) - if len(string) > max_len: - if style < 0: #front truncate - string = '...' + string[3-max_len:] - elif style == 0: #center truncate - string = string[:max_len/2 -3] + '...' + string[-max_len/2:] - elif style > 0: #rear truncate - string = string[:max_len-3] + '...' - return string - ################################################## - # simple conditions - ################################################## - if not self.is_valid(): return _truncate(self.get_value()) - if self.get_value() in self.get_option_keys(): return self.get_option(self.get_value()).get_name() - - ################################################## - # split up formatting by type - ################################################## - truncate = 0 #default center truncate - e = self.get_evaluated() - t = self.get_type() - if isinstance(e, bool): return str(e) - elif isinstance(e, COMPLEX_TYPES): dt_str = num_to_str(e) - elif isinstance(e, VECTOR_TYPES): #vector types - if len(e) > 8: - dt_str = self.get_value() #large vectors use code - truncate = 1 - else: dt_str = ', '.join(map(num_to_str, e)) #small vectors use eval - elif t in ('file_open', 'file_save'): - dt_str = self.get_value() - truncate = -1 - else: dt_str = str(e) #other types - ################################################## - # done - ################################################## - return _truncate(dt_str, truncate) - - def get_color(self): - """ - Get the color that represents this param's type. - - Returns: - a hex color code. - """ - try: - return { - #number types - 'complex': Constants.COMPLEX_COLOR_SPEC, - 'real': Constants.FLOAT_COLOR_SPEC, - 'float': Constants.FLOAT_COLOR_SPEC, - 'int': Constants.INT_COLOR_SPEC, - #vector types - 'complex_vector': Constants.COMPLEX_VECTOR_COLOR_SPEC, - 'real_vector': Constants.FLOAT_VECTOR_COLOR_SPEC, - 'float_vector': Constants.FLOAT_VECTOR_COLOR_SPEC, - 'int_vector': Constants.INT_VECTOR_COLOR_SPEC, - #special - 'bool': Constants.INT_COLOR_SPEC, - 'hex': Constants.INT_COLOR_SPEC, - 'string': Constants.BYTE_VECTOR_COLOR_SPEC, - 'id': Constants.ID_COLOR_SPEC, - 'stream_id': Constants.ID_COLOR_SPEC, - 'grid_pos': Constants.INT_VECTOR_COLOR_SPEC, - 'notebook': Constants.INT_VECTOR_COLOR_SPEC, - 'raw': Constants.WILDCARD_COLOR_SPEC, - }[self.get_type()] - except: return _Param.get_color(self) - - def get_hide(self): - """ - Get the hide value from the base class. - Hide the ID parameter for most blocks. Exceptions below. - If the parameter controls a port type, vlen, or nports, return part. - If the parameter is an empty grid position, return part. - These parameters are redundant to display in the flow graph view. - - Returns: - hide the hide property string - """ - hide = _Param.get_hide(self) - if hide: return hide - #hide ID in non variable blocks - if self.get_key() == 'id' and not _show_id_matcher.match(self.get_parent().get_key()): return 'part' - #hide port controllers for type and nports - if self.get_key() in ' '.join(map( - lambda p: ' '.join([p._type, p._nports]), self.get_parent().get_ports()) - ): return 'part' - #hide port controllers for vlen, when == 1 - if self.get_key() in ' '.join(map( - lambda p: p._vlen, self.get_parent().get_ports()) - ): - try: - if int(self.get_evaluated()) == 1: return 'part' - except: pass - #hide empty grid positions - if self.get_key() in ('grid_pos', 'notebook') and not self.get_value(): return 'part' - return hide - - def validate(self): - """ - Validate the param. - A test evaluation is performed - """ - _Param.validate(self) #checks type - self._evaluated = None - try: self._evaluated = self.evaluate() - except Exception, e: self.add_error_message(str(e)) - - def get_evaluated(self): return self._evaluated - - def evaluate(self): - """ - Evaluate the value. - - Returns: - evaluated type - """ - self._init = True - self._lisitify_flag = False - self._stringify_flag = False - self._hostage_cells = list() - t = self.get_type() - v = self.get_value() - ######################### - # Enum Type - ######################### - if self.is_enum(): return v - ######################### - # Numeric Types - ######################### - elif t in ('raw', 'complex', 'real', 'float', 'int', 'hex', 'bool'): - #raise exception if python cannot evaluate this value - try: e = self.get_parent().get_parent().evaluate(v) - except Exception, e: raise Exception, 'Value "%s" cannot be evaluated:\n%s'%(v, e) - #raise an exception if the data is invalid - if t == 'raw': return e - elif t == 'complex': - if not isinstance(e, COMPLEX_TYPES): - raise Exception, 'Expression "%s" is invalid for type complex.'%str(e) - return e - elif t == 'real' or t == 'float': - if not isinstance(e, REAL_TYPES): - raise Exception, 'Expression "%s" is invalid for type float.'%str(e) - return e - elif t == 'int': - if not isinstance(e, INT_TYPES): - raise Exception, 'Expression "%s" is invalid for type integer.'%str(e) - return e - elif t == 'hex': return hex(e) - elif t == 'bool': - if not isinstance(e, bool): - raise Exception, 'Expression "%s" is invalid for type bool.'%str(e) - return e - else: raise TypeError, 'Type "%s" not handled'%t - ######################### - # Numeric Vector Types - ######################### - elif t in ('complex_vector', 'real_vector', 'float_vector', 'int_vector'): - if not v: v = '()' #turn a blank string into an empty list, so it will eval - #raise exception if python cannot evaluate this value - try: e = self.get_parent().get_parent().evaluate(v) - except Exception, e: raise Exception, 'Value "%s" cannot be evaluated:\n%s'%(v, e) - #raise an exception if the data is invalid - if t == 'complex_vector': - if not isinstance(e, VECTOR_TYPES): - self._lisitify_flag = True - e = [e] - if not all([isinstance(ei, COMPLEX_TYPES) for ei in e]): - raise Exception, 'Expression "%s" is invalid for type complex vector.'%str(e) - return e - elif t == 'real_vector' or t == 'float_vector': - if not isinstance(e, VECTOR_TYPES): - self._lisitify_flag = True - e = [e] - if not all([isinstance(ei, REAL_TYPES) for ei in e]): - raise Exception, 'Expression "%s" is invalid for type float vector.'%str(e) - return e - elif t == 'int_vector': - if not isinstance(e, VECTOR_TYPES): - self._lisitify_flag = True - e = [e] - if not all([isinstance(ei, INT_TYPES) for ei in e]): - raise Exception, 'Expression "%s" is invalid for type integer vector.'%str(e) - return e - ######################### - # String Types - ######################### - elif t in ('string', 'file_open', 'file_save', '_multiline', '_multiline_python_external'): - #do not check if file/directory exists, that is a runtime issue - try: - e = self.get_parent().get_parent().evaluate(v) - if not isinstance(e, str): - raise Exception() - except: - self._stringify_flag = True - e = str(v) - if t == '_multiline_python_external': - ast.parse(e) # raises SyntaxError - return e - ######################### - # Unique ID Type - ######################### - elif t == 'id': - #can python use this as a variable? - if not _check_id_matcher.match(v): - raise Exception, 'ID "%s" must begin with a letter and may contain letters, numbers, and underscores.'%v - ids = [param.get_value() for param in self.get_all_params(t)] - if ids.count(v) > 1: #id should only appear once, or zero times if block is disabled - raise Exception, 'ID "%s" is not unique.'%v - if v in ID_BLACKLIST: - raise Exception, 'ID "%s" is blacklisted.'%v - return v - ######################### - # Stream ID Type - ######################### - elif t == 'stream_id': - #get a list of all stream ids used in the virtual sinks - ids = [param.get_value() for param in filter( - lambda p: p.get_parent().is_virtual_sink(), - self.get_all_params(t), - )] - #check that the virtual sink's stream id is unique - if self.get_parent().is_virtual_sink(): - if ids.count(v) > 1: #id should only appear once, or zero times if block is disabled - raise Exception, 'Stream ID "%s" is not unique.'%v - #check that the virtual source's steam id is found - if self.get_parent().is_virtual_source(): - if v not in ids: - raise Exception, 'Stream ID "%s" is not found.'%v - return v - ######################### - # GUI Position/Hint - ######################### - elif t == 'gui_hint': - if ':' in v: tab, pos = v.split(':') - elif '@' in v: tab, pos = v, '' - else: tab, pos = '', v - - if '@' in tab: tab, index = tab.split('@') - else: index = '?' - - widget_str = ({ - (True, True): 'self.%(tab)s_grid_layout_%(index)s.addWidget(%(widget)s, %(pos)s)', - (True, False): 'self.%(tab)s_layout_%(index)s.addWidget(%(widget)s)', - (False, True): 'self.top_grid_layout.addWidget(%(widget)s, %(pos)s)', - (False, False): 'self.top_layout.addWidget(%(widget)s)', - }[bool(tab), bool(pos)])%{'tab': tab, 'index': index, 'widget': '%s', 'pos': pos} - - # FIXME: Move replace(...) into the make template of the qtgui blocks and return a string here - class GuiHint(object): - def __init__(self, ws): - self._ws = ws - - def __call__(self, w): - return (self._ws.replace('addWidget', 'addLayout') if 'layout' in w else self._ws) % w - - def __str__(self): - return self._ws - return GuiHint(widget_str) - ######################### - # Grid Position Type - ######################### - elif t == 'grid_pos': - if not v: return '' #allow for empty grid pos - e = self.get_parent().get_parent().evaluate(v) - if not isinstance(e, (list, tuple)) or len(e) != 4 or not all([isinstance(ei, int) for ei in e]): - raise Exception, 'A grid position must be a list of 4 integers.' - row, col, row_span, col_span = e - #check row, col - if row < 0 or col < 0: - raise Exception, 'Row and column must be non-negative.' - #check row span, col span - if row_span <= 0 or col_span <= 0: - raise Exception, 'Row and column span must be greater than zero.' - #get hostage cell parent - try: my_parent = self.get_parent().get_param('notebook').evaluate() - except: my_parent = '' - #calculate hostage cells - for r in range(row_span): - for c in range(col_span): - self._hostage_cells.append((my_parent, (row+r, col+c))) - #avoid collisions - params = filter(lambda p: p is not self, self.get_all_params('grid_pos')) - for param in params: - for parent, cell in param._hostage_cells: - if (parent, cell) in self._hostage_cells: - raise Exception, 'Another graphical element is using parent "%s", cell "%s".'%(str(parent), str(cell)) - return e - ######################### - # Notebook Page Type - ######################### - elif t == 'notebook': - if not v: return '' #allow for empty notebook - #get a list of all notebooks - notebook_blocks = filter(lambda b: b.get_key() == 'notebook', self.get_parent().get_parent().get_enabled_blocks()) - #check for notebook param syntax - try: notebook_id, page_index = map(str.strip, v.split(',')) - except: raise Exception, 'Bad notebook page format.' - #check that the notebook id is valid - try: notebook_block = filter(lambda b: b.get_id() == notebook_id, notebook_blocks)[0] - except: raise Exception, 'Notebook id "%s" is not an existing notebook id.'%notebook_id - #check that page index exists - if int(page_index) not in range(len(notebook_block.get_param('labels').evaluate())): - raise Exception, 'Page index "%s" is not a valid index number.'%page_index - return notebook_id, page_index - ######################### - # Import Type - ######################### - elif t == 'import': - n = dict() #new namespace - try: exec v in n - except ImportError: raise Exception, 'Import "%s" failed.'%v - except Exception: raise Exception, 'Bad import syntax: "%s".'%v - return filter(lambda k: str(k) != '__builtins__', n.keys()) - ######################### - else: raise TypeError, 'Type "%s" not handled'%t - - def to_code(self): - """ - Convert the value to code. - For string and list types, check the init flag, call evaluate(). - This ensures that evaluate() was called to set the xxxify_flags. - - Returns: - a string representing the code - """ - v = self.get_value() - t = self.get_type() - if t in ('string', 'file_open', 'file_save', '_multiline', '_multiline_python_external'): # string types - if not self._init: self.evaluate() - if self._stringify_flag: return '"%s"'%v.replace('"', '\"') - else: return v - elif t in ('complex_vector', 'real_vector', 'float_vector', 'int_vector'): #vector types - if not self._init: self.evaluate() - if self._lisitify_flag: return '(%s, )'%v - else: return '(%s)'%v - else: return v - - def get_all_params(self, type): - """ - Get all the params from the flowgraph that have the given type. - - Args: - type: the specified type - - Returns: - a list of params - """ - return sum([filter(lambda p: p.get_type() == type, block.get_params()) for block in self.get_parent().get_parent().get_enabled_blocks()], []) diff --git a/grc/python/Platform.py b/grc/python/Platform.py deleted file mode 100644 index 351f04cb95..0000000000 --- a/grc/python/Platform.py +++ /dev/null @@ -1,176 +0,0 @@ -""" -Copyright 2008-2016 Free Software Foundation, Inc. -This file is part of GNU Radio - -GNU Radio Companion 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 2 -of the License, or (at your option) any later version. - -GNU Radio Companion 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 os -import sys - -from gnuradio import gr - -from .. base.Platform import Platform as _Platform -from .. gui.Platform import Platform as _GUIPlatform -from .. gui import Messages - -from . import extract_docs -from .FlowGraph import FlowGraph as _FlowGraph -from .Connection import Connection as _Connection -from .Block import Block as _Block -from .Port import Port as _Port -from .Param import Param as _Param -from .Generator import Generator -from .Constants import ( - HIER_BLOCKS_LIB_DIR, BLOCK_DTD, DEFAULT_FLOW_GRAPH, BLOCKS_DIRS, - PREFS_FILE, PREFS_FILE_OLD, CORE_TYPES -) - -COLORS = [(name, color) for name, key, sizeof, color in CORE_TYPES] - - -class Platform(_Platform, _GUIPlatform): - def __init__(self): - """ - Make a platform for gnuradio. - """ - # ensure hier and conf directories - if not os.path.exists(HIER_BLOCKS_LIB_DIR): - os.mkdir(HIER_BLOCKS_LIB_DIR) - if not os.path.exists(os.path.dirname(PREFS_FILE)): - os.mkdir(os.path.dirname(PREFS_FILE)) - - self.block_docstrings = block_docstrings = dict() - self.block_docstrings_loaded_callback = lambda: None - - def setter(key, docs): - block_docstrings[key] = '\n\n'.join( - '--- {0} ---\n{1}\n'.format(b, d.replace('\n\n', '\n')) - for b, d in docs.iteritems() if d is not None - ) - - self._docstring_extractor = extract_docs.SubprocessLoader( - callback_query_result=setter, - callback_finished=lambda: self.block_docstrings_loaded_callback() - ) - - # init - _Platform.__init__( - self, - name='GNU Radio Companion', - version=(gr.version(), gr.major_version(), gr.api_version(), gr.minor_version()), - key='grc', - license=__doc__.strip(), - website='http://gnuradio.org/', - block_paths=BLOCKS_DIRS, - block_dtd=BLOCK_DTD, - default_flow_graph=DEFAULT_FLOW_GRAPH, - generator=Generator, - colors=COLORS, - ) - self._move_old_pref_file() - _GUIPlatform.__init__( - self, - prefs_file=PREFS_FILE - ) - self._auto_hier_block_generate_chain = set() - - @staticmethod - def _move_old_pref_file(): - if PREFS_FILE == PREFS_FILE_OLD: - return # prefs file overridden with env var - if os.path.exists(PREFS_FILE_OLD) and not os.path.exists(PREFS_FILE): - try: - import shutil - shutil.move(PREFS_FILE_OLD, PREFS_FILE) - except Exception as e: - print >> sys.stderr, e - - def load_blocks(self): - self._docstring_extractor.start() - _Platform.load_blocks(self) - self._docstring_extractor.finish() - # self._docstring_extractor.wait() - - def load_block_xml(self, xml_file): - block = _Platform.load_block_xml(self, xml_file) - self._docstring_extractor.query( - block.get_key(), - block.get_imports(raw=True), - block.get_make(raw=True) - ) - return block - - @staticmethod - def find_file_in_paths(filename, paths, cwd): - """Checks the provided paths relative to cwd for a certain filename""" - if not os.path.isdir(cwd): - cwd = os.path.dirname(cwd) - if isinstance(paths, str): - paths = (p for p in paths.split(':') if p) - - for path in paths: - path = os.path.expanduser(path) - if not os.path.isabs(path): - path = os.path.normpath(os.path.join(cwd, path)) - file_path = os.path.join(path, filename) - if os.path.exists(os.path.normpath(file_path)): - return file_path - - def load_and_generate_flow_graph(self, file_path): - """Loads a flowgraph from file and generates it""" - Messages.set_indent(len(self._auto_hier_block_generate_chain)) - Messages.send('>>> Loading: %r\n' % file_path) - if file_path in self._auto_hier_block_generate_chain: - Messages.send(' >>> Warning: cyclic hier_block dependency\n') - return False - self._auto_hier_block_generate_chain.add(file_path) - try: - flow_graph = self.get_new_flow_graph() - flow_graph.grc_file_path = file_path - # other, nested higiter_blocks might be auto-loaded here - flow_graph.import_data(self.parse_flow_graph(file_path)) - flow_graph.rewrite() - flow_graph.validate() - if not flow_graph.is_valid(): - raise Exception('Flowgraph invalid') - if not flow_graph.get_option('generate_options').startswith('hb'): - raise Exception('Not a hier block') - except Exception as e: - Messages.send('>>> Load Error: %r: %s\n' % (file_path, str(e))) - return False - finally: - self._auto_hier_block_generate_chain.discard(file_path) - Messages.set_indent(len(self._auto_hier_block_generate_chain)) - - try: - Messages.send('>>> Generating: %r\n' % file_path) - generator = self.get_generator()(flow_graph, file_path) - generator.write() - except Exception as e: - Messages.send('>>> Generate Error: %r: %s\n' % (file_path, str(e))) - return False - - self.load_block_xml(generator.get_file_path_xml()) - return True - - ############################################## - # Constructors - ############################################## - FlowGraph = _FlowGraph - Connection = _Connection - Block = _Block - Port = _Port - Param = _Param diff --git a/grc/python/Port.py b/grc/python/Port.py deleted file mode 100644 index 249d7aed71..0000000000 --- a/grc/python/Port.py +++ /dev/null @@ -1,268 +0,0 @@ -""" -Copyright 2008-2012 Free Software Foundation, Inc. -This file is part of GNU Radio - -GNU Radio Companion 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 2 -of the License, or (at your option) any later version. - -GNU Radio Companion 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 -""" - -from .. base.Port import Port as _Port -from .. base.Constants import DEFAULT_DOMAIN, GR_MESSAGE_DOMAIN -from .. gui.Port import Port as _GUIPort -import Constants - - -def _get_source_from_virtual_sink_port(vsp): - """ - Resolve the source port that is connected to the given virtual sink port. - Use the get source from virtual source to recursively resolve subsequent ports. - """ - try: return _get_source_from_virtual_source_port( - vsp.get_enabled_connections()[0].get_source()) - except: raise Exception, 'Could not resolve source for virtual sink port %s'%vsp - -def _get_source_from_virtual_source_port(vsp, traversed=[]): - """ - Recursively resolve source ports over the virtual connections. - Keep track of traversed sources to avoid recursive loops. - """ - if not vsp.get_parent().is_virtual_source(): return vsp - if vsp in traversed: raise Exception, 'Loop found when resolving virtual source %s'%vsp - try: return _get_source_from_virtual_source_port( - _get_source_from_virtual_sink_port( - filter(#get all virtual sinks with a matching stream id - lambda vs: vs.get_param('stream_id').get_value() == vsp.get_parent().get_param('stream_id').get_value(), - filter(#get all enabled blocks that are also virtual sinks - lambda b: b.is_virtual_sink(), - vsp.get_parent().get_parent().get_enabled_blocks(), - ), - )[0].get_sinks()[0] - ), traversed + [vsp], - ) - except: raise Exception, 'Could not resolve source for virtual source port %s'%vsp - -def _get_sink_from_virtual_source_port(vsp): - """ - Resolve the sink port that is connected to the given virtual source port. - Use the get sink from virtual sink to recursively resolve subsequent ports. - """ - try: return _get_sink_from_virtual_sink_port( - vsp.get_enabled_connections()[0].get_sink()) # Could have many connections, but use first - except: raise Exception, 'Could not resolve source for virtual source port %s'%vsp - -def _get_sink_from_virtual_sink_port(vsp, traversed=[]): - """ - Recursively resolve sink ports over the virtual connections. - Keep track of traversed sinks to avoid recursive loops. - """ - if not vsp.get_parent().is_virtual_sink(): return vsp - if vsp in traversed: raise Exception, 'Loop found when resolving virtual sink %s'%vsp - try: return _get_sink_from_virtual_sink_port( - _get_sink_from_virtual_source_port( - filter(#get all virtual source with a matching stream id - lambda vs: vs.get_param('stream_id').get_value() == vsp.get_parent().get_param('stream_id').get_value(), - filter(#get all enabled blocks that are also virtual sinks - lambda b: b.is_virtual_source(), - vsp.get_parent().get_parent().get_enabled_blocks(), - ), - )[0].get_sources()[0] - ), traversed + [vsp], - ) - except: raise Exception, 'Could not resolve source for virtual sink port %s'%vsp - -class Port(_Port, _GUIPort): - - def __init__(self, block, n, dir): - """ - Make a new port from nested data. - - Args: - block: the parent element - n: the nested odict - dir: the direction - """ - self._n = n - if n['type'] == 'message': - n['domain'] = GR_MESSAGE_DOMAIN - if 'domain' not in n: - n['domain'] = DEFAULT_DOMAIN - elif n['domain'] == GR_MESSAGE_DOMAIN: - n['key'] = n['name'] - n['type'] = 'message' # for port color - if n['type'] == 'msg': - n['key'] = 'msg' - if not n.find('key'): - n['key'] = str(next(block.port_counters[dir == 'source'])) - # build the port - _Port.__init__( - self, - block=block, - n=n, - dir=dir, - ) - _GUIPort.__init__(self) - self._nports = n.find('nports') or '' - self._vlen = n.find('vlen') or '' - self._optional = bool(n.find('optional')) - self._clones = [] # references to cloned ports (for nports > 1) - - def get_types(self): return Constants.TYPE_TO_SIZEOF.keys() - - def is_type_empty(self): return not self._n['type'] - - def validate(self): - _Port.validate(self) - if not self.get_enabled_connections() and not self.get_optional(): - self.add_error_message('Port is not connected.') - #message port logic - if self.get_type() == 'msg': - if self.get_nports(): - self.add_error_message('A port of type "msg" cannot have "nports" set.') - if self.get_vlen() != 1: - self.add_error_message('A port of type "msg" must have a "vlen" of 1.') - - def rewrite(self): - """ - Handle the port cloning for virtual blocks. - """ - if self.is_type_empty(): - try: #clone type and vlen - source = self.resolve_empty_type() - self._type = str(source.get_type()) - self._vlen = str(source.get_vlen()) - except: #reset type and vlen - self._type = '' - self._vlen = '' - _Port.rewrite(self) - - def resolve_virtual_source(self): - if self.get_parent().is_virtual_sink(): return _get_source_from_virtual_sink_port(self) - if self.get_parent().is_virtual_source(): return _get_source_from_virtual_source_port(self) - - def resolve_empty_type(self): - if self.is_sink(): - try: - src = _get_source_from_virtual_sink_port(self) - if not src.is_type_empty(): return src - except: pass - sink = _get_sink_from_virtual_sink_port(self) - if not sink.is_type_empty(): return sink - if self.is_source(): - try: - src = _get_source_from_virtual_source_port(self) - if not src.is_type_empty(): return src - except: pass - sink = _get_sink_from_virtual_source_port(self) - if not sink.is_type_empty(): return sink - - def get_vlen(self): - """ - Get the vector length. - If the evaluation of vlen cannot be cast to an integer, return 1. - - Returns: - the vector length or 1 - """ - vlen = self.get_parent().resolve_dependencies(self._vlen) - try: return int(self.get_parent().get_parent().evaluate(vlen)) - except: return 1 - - def get_nports(self): - """ - Get the number of ports. - If already blank, return a blank - If the evaluation of nports cannot be cast to a positive integer, return 1. - - Returns: - the number of ports or 1 - """ - if self._nports == '': return '' - - nports = self.get_parent().resolve_dependencies(self._nports) - try: - return max(1, int(self.get_parent().get_parent().evaluate(nports))) - except: - return 1 - - def get_optional(self): return bool(self._optional) - - def get_color(self): - """ - Get the color that represents this port's type. - Codes differ for ports where the vec length is 1 or greater than 1. - - Returns: - a hex color code. - """ - try: - color = Constants.TYPE_TO_COLOR[self.get_type()] - vlen = self.get_vlen() - if vlen == 1: return color - color_val = int(color[1:], 16) - r = (color_val >> 16) & 0xff - g = (color_val >> 8) & 0xff - b = (color_val >> 0) & 0xff - dark = (0, 0, 30, 50, 70)[min(4, vlen)] - r = max(r-dark, 0) - g = max(g-dark, 0) - b = max(b-dark, 0) - return '#%.2x%.2x%.2x'%(r, g, b) - except: return _Port.get_color(self) - - def get_clones(self): - """ - Get the clones of this master port (nports > 1) - - Returns: - a list of ports - """ - return self._clones - - def add_clone(self): - """ - Create a clone of this (master) port and store a reference in self._clones. - - The new port name (and key for message ports) will have index 1... appended. - If this is the first clone, this (master) port will get a 0 appended to its name (and key) - - Returns: - the cloned port - """ - # add index to master port name if there are no clones yet - if not self._clones: - self._name = self._n['name'] + '0' - if not self._key.isdigit(): # also update key for none stream ports - self._key = self._name - - # Prepare a copy of the odict for the clone - n = self._n.copy() - if 'nports' in n: n.pop('nports') # remove nports from the key so the copy cannot be a duplicator - n['name'] = self._n['name'] + str(len(self._clones) + 1) - n['key'] = '99999' if self._key.isdigit() else n['name'] # dummy value 99999 will be fixed later - - port = self.__class__(self.get_parent(), n, self._dir) # clone - self._clones.append(port) - return port - - def remove_clone(self, port): - """ - Remove a cloned port (from the list of clones only) - Remove the index 0 of the master port name (and key9 if there are no more clones left - """ - self._clones.remove(port) - # remove index from master port name if there are no more clones - if not self._clones: - self._name = self._n['name'] - if not self._key.isdigit(): # also update key for none stream ports - self._key = self._name diff --git a/grc/scripts/CMakeLists.txt b/grc/scripts/CMakeLists.txt index e905892308..6cc78c3cf3 100644 --- a/grc/scripts/CMakeLists.txt +++ b/grc/scripts/CMakeLists.txt @@ -23,3 +23,5 @@ GR_PYTHON_INSTALL( DESTINATION ${GR_RUNTIME_DIR} COMPONENT "grc" ) + +add_subdirectory(freedesktop) diff --git a/grc/freedesktop/CMakeLists.txt b/grc/scripts/freedesktop/CMakeLists.txt index 47e836f697..47e836f697 100644 --- a/grc/freedesktop/CMakeLists.txt +++ b/grc/scripts/freedesktop/CMakeLists.txt diff --git a/grc/freedesktop/README b/grc/scripts/freedesktop/README index 0857ecc224..0857ecc224 100644 --- a/grc/freedesktop/README +++ b/grc/scripts/freedesktop/README diff --git a/grc/freedesktop/convert.sh b/grc/scripts/freedesktop/convert.sh index e2cba264a6..e2cba264a6 100755 --- a/grc/freedesktop/convert.sh +++ b/grc/scripts/freedesktop/convert.sh diff --git a/grc/freedesktop/gnuradio-grc.desktop b/grc/scripts/freedesktop/gnuradio-grc.desktop index 39beeca1b8..39beeca1b8 100644 --- a/grc/freedesktop/gnuradio-grc.desktop +++ b/grc/scripts/freedesktop/gnuradio-grc.desktop diff --git a/grc/freedesktop/gnuradio-grc.xml b/grc/scripts/freedesktop/gnuradio-grc.xml index a5cb95d9fd..a5cb95d9fd 100644 --- a/grc/freedesktop/gnuradio-grc.xml +++ b/grc/scripts/freedesktop/gnuradio-grc.xml diff --git a/grc/freedesktop/gnuradio_logo_icon-square.svg b/grc/scripts/freedesktop/gnuradio_logo_icon-square.svg index 3b54bf4001..3b54bf4001 100644 --- a/grc/freedesktop/gnuradio_logo_icon-square.svg +++ b/grc/scripts/freedesktop/gnuradio_logo_icon-square.svg diff --git a/grc/freedesktop/grc-icon-128.png b/grc/scripts/freedesktop/grc-icon-128.png Binary files differindex 13efe806ba..13efe806ba 100644 --- a/grc/freedesktop/grc-icon-128.png +++ b/grc/scripts/freedesktop/grc-icon-128.png diff --git a/grc/freedesktop/grc-icon-16.png b/grc/scripts/freedesktop/grc-icon-16.png Binary files differindex bdd1823b3d..bdd1823b3d 100644 --- a/grc/freedesktop/grc-icon-16.png +++ b/grc/scripts/freedesktop/grc-icon-16.png diff --git a/grc/freedesktop/grc-icon-24.png b/grc/scripts/freedesktop/grc-icon-24.png Binary files differindex a124768125..a124768125 100644 --- a/grc/freedesktop/grc-icon-24.png +++ b/grc/scripts/freedesktop/grc-icon-24.png diff --git a/grc/freedesktop/grc-icon-256.png b/grc/scripts/freedesktop/grc-icon-256.png Binary files differindex 077688eac5..077688eac5 100644 --- a/grc/freedesktop/grc-icon-256.png +++ b/grc/scripts/freedesktop/grc-icon-256.png diff --git a/grc/freedesktop/grc-icon-32.png b/grc/scripts/freedesktop/grc-icon-32.png Binary files differindex a345aace3c..a345aace3c 100644 --- a/grc/freedesktop/grc-icon-32.png +++ b/grc/scripts/freedesktop/grc-icon-32.png diff --git a/grc/freedesktop/grc-icon-48.png b/grc/scripts/freedesktop/grc-icon-48.png Binary files differindex c522a5d0ec..c522a5d0ec 100644 --- a/grc/freedesktop/grc-icon-48.png +++ b/grc/scripts/freedesktop/grc-icon-48.png diff --git a/grc/freedesktop/grc-icon-64.png b/grc/scripts/freedesktop/grc-icon-64.png Binary files differindex df4f6dc07b..df4f6dc07b 100644 --- a/grc/freedesktop/grc-icon-64.png +++ b/grc/scripts/freedesktop/grc-icon-64.png diff --git a/grc/freedesktop/grc_setup_freedesktop.in b/grc/scripts/freedesktop/grc_setup_freedesktop.in index 87a388e2ec..87a388e2ec 100644 --- a/grc/freedesktop/grc_setup_freedesktop.in +++ b/grc/scripts/freedesktop/grc_setup_freedesktop.in diff --git a/grc/scripts/gnuradio-companion b/grc/scripts/gnuradio-companion index 203a8c773d..04a1cb44e7 100755 --- a/grc/scripts/gnuradio-companion +++ b/grc/scripts/gnuradio-companion @@ -1,6 +1,6 @@ #!/usr/bin/env python """ -Copyright 2009-2015 Free Software Foundation, Inc. +Copyright 2016 Free Software Foundation, Inc. This file is part of GNU Radio GNU Radio Companion is free software; you can redistribute it and/or @@ -20,111 +20,19 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA import os import sys -import optparse -import warnings - -GR_IMPORT_ERROR_MESSAGE = """\ -Cannot import gnuradio. - -Is the python path environment variable set correctly? - All OS: PYTHONPATH - -Is the library path environment variable set correctly? - Linux: LD_LIBRARY_PATH - Windows: PATH - MacOSX: DYLD_LIBRARY_PATH -""" - -VERSION_AND_DISCLAIMER_TEMPLATE = """\ -GNU Radio Companion %s - -This program is part of GNU Radio -GRC comes with ABSOLUTELY NO WARRANTY. -This is free software, and you are welcome to redistribute it. -""" - - -def die(error, message): - msg = "{0}\n\n({1})".format(message, error) - try: - import gtk - d = gtk.MessageDialog( - type=gtk.MESSAGE_ERROR, - buttons=gtk.BUTTONS_CLOSE, - message_format=msg, - ) - d.set_title(type(error).__name__) - d.run() - exit(1) - except ImportError: - exit(type(error).__name__ + '\n\n' + msg) - - -def check_gtk(): - try: - warnings.filterwarnings("error") - import pygtk - pygtk.require('2.0') - import gtk - gtk.init_check() - warnings.filterwarnings("always") - except Exception as err: - die(err, "Failed to initialize GTK. If you are running over ssh, " - "did you enable X forwarding and start ssh with -X?") - - -def check_gnuradio_import(): - try: - from gnuradio import gr - except ImportError as err: - die(err, GR_IMPORT_ERROR_MESSAGE) - - -def check_blocks_path(): - if 'GR_DONT_LOAD_PREFS' in os.environ and not os.environ.get('GRC_BLOCKS_PATH', ''): - die(EnvironmentError("No block definitions available"), - "Can't find block definitions. Use config.conf or GRC_BLOCKS_PATH.") - - -def get_source_tree_root(): - source_tree_subpath = "/grc/scripts" - script_path = os.path.dirname(os.path.abspath(__file__)) - if script_path.endswith(source_tree_subpath): - return script_path[:-len(source_tree_subpath)] - - -def main(): - check_gnuradio_import() - - from gnuradio import gr - parser = optparse.OptionParser( - usage='usage: %prog [options] [saved flow graphs]', - version=VERSION_AND_DISCLAIMER_TEMPLATE % gr.version()) - options, args = parser.parse_args() - - check_gtk() - check_blocks_path() - source_tree_root = get_source_tree_root() - if not source_tree_root: - # run the installed version - from gnuradio.grc.python.Platform import Platform - from gnuradio.grc.gui.ActionHandler import ActionHandler - - else: - print("Running from source tree") - sys.path.insert(1, source_tree_root) - from grc.python.Platform import Platform - from grc.gui.ActionHandler import ActionHandler - - try: - import gtk - gtk.window_set_default_icon(gtk.IconTheme().load_icon('gnuradio-grc', 256, 0)) - except: - pass - - ActionHandler(args, Platform()) - - -if __name__ == '__main__': - main() +script_path = os.path.dirname(os.path.abspath(__file__)) +source_tree_subpath = "/grc/scripts" + +if not script_path.endswith(source_tree_subpath): + # run the installed version + from gnuradio.grc.main import main + from gnuradio.grc import checks +else: + print("Running from source tree") + sys.path.insert(1, script_path[:-len(source_tree_subpath)]) + from grc.main import main + from grc import checks + +checks.do_all() +exit(main()) diff --git a/grc/todo.txt b/grc/todo.txt deleted file mode 100644 index cedea72aa3..0000000000 --- a/grc/todo.txt +++ /dev/null @@ -1,69 +0,0 @@ -################################################## -# Examples -################################################## -* Push-to-Talk example -* Start/Stop the flow graph - -################################################## -# Blocks -################################################## -* probe: also non-float outputs -* log slider gui control -* packet mod: whitening offset -* wx min window size in options block -* gr_adaptive_fir_ccf -* size params for the graphical sinks -* callbacks for set average on fft, waterfall, number sinks -* add units to params: Sps, Hz, dB... -* add bool type to command line option store_true or store_false -* messages for packet blocks and probe blocks - -################################################## -# Features -################################################## -* extract category from doxygen - * fix up block tree to mirror current doxygen group - * remove blocks in block tree covered by doxygen -* param editor, expand entry boxes in focus -* change param dialog to panel within main window -* gui grid editor for configuring grid params/placing wxgui plots and controls -* drag from one port to another to connect -* per parameter docs - * extract individual param docs from doxygen - * doc tag in param for handwritten notes -* separate generated code into top block and gui class - * use gui.py in gr-wxgui and remove custom top_block_gui -* configuration option for adding block paths -* orientations for ports (top, right, bottom, left) - * source defaults to right, sink defaults to left -* separation of variables and gui controls -* speedup w/ background layer and animation layer -* multiple doxygen directories (doc_dir becomes doc_path) -* use pango markup in tooltips for params -* use get_var_make to determine if it is a variable, not regexp -* concept of a project, or project flow graph - * collection of blocks, hier and top - * system-wide, default/work, and user created -* use templates/macros to generate the repetative stuff in the xml - -################################################## -# Problems -################################################## -* msg ports dont work with virtual connections - * dont fix this until pmts are used? -* hier block generation - * auto generate hier library on changes - * auto clean hier library when block removed - * add hier blocks to tree without restart -* dont generate py files in saved flowgraph dir -* save/restore cwd -* threads dont die on exit in probe and variable sink -* align param titles in properties dialog -* weird grid params misbehaving -* gr hier blocks have more diverse IO capabilities than we allow for - -################################################## -# Future -################################################## -* require pygtk 2.12 for treeview tooltips - * remove try/except in BlockTreeWindow.py diff --git a/volk b/volk -Subproject 78c8bc4a0e49487e1f787f0fe77d8c3dace7c7f +Subproject b930d7ffb9561be5d333301dea852fdbe104d36 |