diff options
-rwxr-xr-x | gr-blocks/examples/ctrlport/usrp_sink_controller.py | 42 | ||||
-rw-r--r-- | gr-blocks/examples/ctrlport/usrp_source_control.grc | 14 | ||||
-rwxr-xr-x | gr-blocks/examples/ctrlport/usrp_source_controller.py | 33 | ||||
-rw-r--r-- | gr-blocks/lib/tagged_stream_align_impl.cc | 2 | ||||
-rw-r--r-- | gr-uhd/lib/usrp_sink_impl.cc | 13 | ||||
-rw-r--r-- | gr-uhd/lib/usrp_sink_impl.h | 2 | ||||
-rw-r--r-- | grc/base/FlowGraph.py | 8 | ||||
-rw-r--r-- | grc/blocks/epy_block.xml | 2 | ||||
-rw-r--r-- | grc/blocks/epy_module.xml | 33 | ||||
-rw-r--r-- | grc/gui/Actions.py | 2 | ||||
-rw-r--r-- | grc/gui/Block.py | 4 | ||||
-rw-r--r-- | grc/gui/Param.py | 2 | ||||
-rw-r--r-- | grc/gui/PropsDialog.py | 12 | ||||
-rw-r--r-- | grc/python/FlowGraph.py | 35 | ||||
-rw-r--r-- | grc/python/Generator.py | 40 | ||||
-rw-r--r-- | grc/python/Param.py | 2 | ||||
-rw-r--r-- | grc/python/flow_graph.tmpl | 5 |
17 files changed, 203 insertions, 48 deletions
diff --git a/gr-blocks/examples/ctrlport/usrp_sink_controller.py b/gr-blocks/examples/ctrlport/usrp_sink_controller.py new file mode 100755 index 0000000000..d8c38e36f2 --- /dev/null +++ b/gr-blocks/examples/ctrlport/usrp_sink_controller.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python + +import sys +import pmt +from gnuradio.ctrlport.GNURadioControlPortClient import GNURadioControlPortClient +from optparse import OptionParser + +parser = OptionParser(usage="%prog: [options]") +parser.add_option("-H", "--host", type="string", default="localhost", + help="Hostname to connect to (default=%default)") +parser.add_option("-p", "--port", type="int", default=9090, + help="Port of Controlport instance on host (default=%default)") +parser.add_option("-a", "--alias", type="string", default="gr uhd usrp sink0", + help="The UHD block's alias to control (default=%default)") +options, args = parser.parse_args() + +if(len(args) < 2): + sys.stderr.write('Not enough arguments: usrp_source_controller.py [options] <command> <value>\n') + sys.stderr.write('See the "UHD Interface" section of the manual for available commands.\n\n') + sys.exit(1) + +port = 'command' +alias = options.alias +hostname = options.host +portnum = options.port +cmd = args[0] +val = args[1] + +if(cmd == "tune" or cmd == "time"): + sys.stderr.write("This application currently does not support the 'tune' or 'time' UHD " + "message commands.\n\n") + sys.exit(1) +elif(cmd == "antenna"): + val = pmt.intern(val) +else: + val = pmt.from_double(float(val)) + +argv = [None, hostname, portnum] +radiosys = GNURadioControlPortClient(argv=argv, rpcmethod='thrift') +radio = radiosys.client + +radio.postMessage(alias, port, pmt.cons(pmt.intern(cmd), val)) diff --git a/gr-blocks/examples/ctrlport/usrp_source_control.grc b/gr-blocks/examples/ctrlport/usrp_source_control.grc index b6683888b8..33dafdfa83 100644 --- a/gr-blocks/examples/ctrlport/usrp_source_control.grc +++ b/gr-blocks/examples/ctrlport/usrp_source_control.grc @@ -41,6 +41,10 @@ <value>qt_gui</value> </param> <param> + <key>hier_block_src_path</key> + <value>.:</value> + </param> + <param> <key>id</key> <value>usrp_source_control</value> </param> @@ -49,10 +53,18 @@ <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> @@ -746,7 +758,7 @@ <key>uhd_usrp_source</key> <param> <key>alias</key> - <value>usrp_source0</value> + <value></value> </param> <param> <key>ant0</key> diff --git a/gr-blocks/examples/ctrlport/usrp_source_controller.py b/gr-blocks/examples/ctrlport/usrp_source_controller.py index 77a6cb482b..02d30a9d37 100755 --- a/gr-blocks/examples/ctrlport/usrp_source_controller.py +++ b/gr-blocks/examples/ctrlport/usrp_source_controller.py @@ -3,24 +3,37 @@ import sys import pmt from gnuradio.ctrlport.GNURadioControlPortClient import GNURadioControlPortClient +from optparse import OptionParser -args = sys.argv -if(len(args) < 4): - sys.stderr.write('Not enough arguments: usrp_source_controller.py <host> <port> <command> <value>\n') +parser = OptionParser(usage="%prog: [options]") +parser.add_option("-H", "--host", type="string", default="localhost", + help="Hostname to connect to (default=%default)") +parser.add_option("-p", "--port", type="int", default=9090, + help="Port of Controlport instance on host (default=%default)") +parser.add_option("-a", "--alias", type="string", default="gr uhd usrp source0", + help="The UHD block's alias to control (default=%default)") +options, args = parser.parse_args() + +if(len(args) < 2): + sys.stderr.write('Not enough arguments: usrp_source_controller.py [options] <command> <value>\n') sys.stderr.write('See the "UHD Interface" section of the manual for available commands.\n\n') sys.exit(1) -alias = 'usrp_source0' port = 'command' +alias = options.alias +hostname = options.host +portnum = options.port +cmd = args[0] +val = args[1] -hostname = args[1] -portnum = int(args[2]) -cmd = args[3].lower() - +if(cmd == "tune" or cmd == "time"): + sys.stderr.write("This application currently does not support the 'tune' or 'time' UHD " + "message commands.\n\n") + sys.exit(1) if(cmd == "antenna"): - val = pmt.intern(args[4]) + val = pmt.intern(val) else: - val = pmt.from_double(float(args[4])) + val = pmt.from_double(float(val)) argv = [None, hostname, portnum] radiosys = GNURadioControlPortClient(argv=argv, rpcmethod='thrift') diff --git a/gr-blocks/lib/tagged_stream_align_impl.cc b/gr-blocks/lib/tagged_stream_align_impl.cc index e20c119bce..c71d038fcb 100644 --- a/gr-blocks/lib/tagged_stream_align_impl.cc +++ b/gr-blocks/lib/tagged_stream_align_impl.cc @@ -70,7 +70,7 @@ namespace gr { consume_each(ncp); return ncp; } else { - get_tags_in_range(tags, 0, nitems_read(0), nitems_read(0) + noutput_items, d_lengthtag); + get_tags_in_range(tags, 0, nitems_read(0), nitems_read(0) + ninput_items[0], d_lengthtag); if(tags.size() > 0){ d_have_sync = true; consume_each( tags[0].offset - nitems_read(0) ); diff --git a/gr-uhd/lib/usrp_sink_impl.cc b/gr-uhd/lib/usrp_sink_impl.cc index 0d26f7fcaa..53050051f9 100644 --- a/gr-uhd/lib/usrp_sink_impl.cc +++ b/gr-uhd/lib/usrp_sink_impl.cc @@ -618,5 +618,18 @@ namespace gr { return true; } + + void + usrp_sink_impl::setup_rpc() + { +#ifdef GR_CTRLPORT + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_handler<usrp_block>( + alias(), "command", + "", "UHD Commands", + RPC_PRIVLVL_MIN, DISPNULL))); +#endif /* GR_CTRLPORT */ + } + } /* namespace uhd */ } /* namespace gr */ diff --git a/gr-uhd/lib/usrp_sink_impl.h b/gr-uhd/lib/usrp_sink_impl.h index 1575378d21..d509baef90 100644 --- a/gr-uhd/lib/usrp_sink_impl.h +++ b/gr-uhd/lib/usrp_sink_impl.h @@ -103,6 +103,8 @@ namespace gr { inline void tag_work(int &ninput_items); + void setup_rpc(); + 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); diff --git a/grc/base/FlowGraph.py b/grc/base/FlowGraph.py index 5d600e27dd..b904a84697 100644 --- a/grc/base/FlowGraph.py +++ b/grc/base/FlowGraph.py @@ -209,6 +209,12 @@ class FlowGraph(Element): 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. @@ -216,7 +222,7 @@ class FlowGraph(Element): Returns: a list of blocks """ - return filter(methodcaller('get_enabled'), self.iter_blocks()) + return list(self.iter_enabled_blocks()) def get_bypassed_blocks(self): """ diff --git a/grc/blocks/epy_block.xml b/grc/blocks/epy_block.xml index 2cd1cb5c92..e56186ec65 100644 --- a/grc/blocks/epy_block.xml +++ b/grc/blocks/epy_block.xml @@ -1,6 +1,6 @@ <?xml version="1.0"?> <block> - <name>Embedded Python Block</name> + <name>Python Block</name> <key>epy_block</key> <category>Misc</category> <import></import> diff --git a/grc/blocks/epy_module.xml b/grc/blocks/epy_module.xml new file mode 100644 index 0000000000..6d6d71804c --- /dev/null +++ b/grc/blocks/epy_module.xml @@ -0,0 +1,33 @@ +<?xml version="1.0"?> +<block> + <name>Python Module</name> + <key>epy_module</key> + <category>Misc</category> + <import>import $id # embedded python module</import> + <make></make> + <param> + <name>Code</name> + <key>source_code</key> + <value># this module will be imported in the into your flowgraph</value> + <type>_multiline_python_external</type> + <hide>part</hide> + </param> + <doc>This block lets you embed a python module in your flowgraph. + +Code you put in this module is accessible in other blocks using the ID of this +block. Example: + +If you put + + a = 2 + + def double(arg): + return 2 * arg + +in a Python Module Block with the ID 'stuff' you can use code like + + stuff.a # evals to 2 + stuff.double(3) # evals to 6 + +to set parameters of other blocks in your flowgraph.</doc> +</block> diff --git a/grc/gui/Actions.py b/grc/gui/Actions.py index 1322dca874..9b32b3e601 100644 --- a/grc/gui/Actions.py +++ b/grc/gui/Actions.py @@ -442,7 +442,7 @@ BUSSIFY_SINKS = Action( ) XML_PARSER_ERRORS_DISPLAY = Action( label='_Parser Errors', - tooltip='View errors that occured while parsing XML files', + tooltip='View errors that occurred while parsing XML files', stock_id=gtk.STOCK_DIALOG_ERROR, ) FLOW_GRAPH_OPEN_QSS_THEME = Action( diff --git a/grc/gui/Block.py b/grc/gui/Block.py index 8c74fcb4a5..67b80695fa 100644 --- a/grc/gui/Block.py +++ b/grc/gui/Block.py @@ -206,7 +206,7 @@ class Block(Element): #display the params 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=PARAM_FONT, key=self._key) ] else: markups = [param.get_markup() for param in self.get_params() if param.get_hide() not in ('all', 'part')] @@ -215,7 +215,7 @@ class Block(Element): layout.set_spacing(LABEL_SEPARATION*pango.SCALE) layout.set_markup('\n'.join(markups)) layouts.append(layout) - w,h = layout.get_pixel_size() + w, h = layout.get_pixel_size() self.label_width = max(w, self.label_width) self.label_height += h + LABEL_SEPARATION width = self.label_width diff --git a/grc/gui/Param.py b/grc/gui/Param.py index 515c345a73..6884d6530a 100644 --- a/grc/gui/Param.py +++ b/grc/gui/Param.py @@ -345,7 +345,7 @@ class FileParam(EntryParam): PARAM_MARKUP_TMPL="""\ #set $foreground = $param.is_valid() and 'black' or 'red' -<span foreground="$foreground" font_desc="$font"><b>$encode($param.get_name()): </b>$encode(repr($param))</span>""" +<span foreground="$foreground" font_desc="$font"><b>$encode($param.get_name()): </b>$encode(repr($param).replace('\\n',' '))</span>""" PARAM_LABEL_MARKUP_TMPL="""\ #set $foreground = $modified and 'blue' or $param.is_valid() and 'black' or 'red' diff --git a/grc/gui/PropsDialog.py b/grc/gui/PropsDialog.py index f5a136e634..bf7d31d391 100644 --- a/grc/gui/PropsDialog.py +++ b/grc/gui/PropsDialog.py @@ -210,6 +210,14 @@ class PropsDialog(gtk.Dialog): buffer = self._code_text_display.get_buffer() block = self._block + key = block.get_key() + + if key == 'epy_block': + src = block.get_param('_source_code').get_value() + elif key == 'epy_module': + src = block.get_param('source_code').get_value() + else: + src = '' def insert(header, text): if not text: @@ -219,9 +227,11 @@ class PropsDialog(gtk.Dialog): buffer.delete(buffer.get_start_iter(), buffer.get_end_iter()) insert('# Imports\n', '\n'.join(block.get_imports())) - if block.get_key().startswith('variable'): + if key.startswith('variable'): insert('\n\n# Variables\n', block.get_var_make()) insert('\n\n# Blocks\n', block.get_make()) + if src: + insert('\n\n# External Code ({}.py)\n'.format(block.get_id()), src) def _handle_key_press(self, widget, event): """ diff --git a/grc/python/FlowGraph.py b/grc/python/FlowGraph.py index 9b55cb6d43..686dae70fa 100644 --- a/grc/python/FlowGraph.py +++ b/grc/python/FlowGraph.py @@ -16,11 +16,13 @@ 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 -import expr_utils +from . import expr_utils from .. base.FlowGraph import FlowGraph as _FlowGraph from .. gui.FlowGraph import FlowGraph as _GUIFlowGraph -import re _variable_matcher = re.compile('^(variable\w*)$') _parameter_matcher = re.compile('^(parameter)$') @@ -181,8 +183,8 @@ class FlowGraph(_FlowGraph, _GUIFlowGraph): Returns: a sorted list of variable blocks in order of dependency (indep -> dep) """ - variables = filter(lambda b: _variable_matcher.match(b.get_key()), self.get_enabled_blocks()) - return expr_utils.sort_objects(variables, lambda v: v.get_id(), lambda v: v.get_var_make()) + 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): """ @@ -191,16 +193,22 @@ class FlowGraph(_FlowGraph, _GUIFlowGraph): Returns: a list of paramterized variables """ - parameters = filter(lambda b: _parameter_matcher.match(b.get_key()), self.get_enabled_blocks()) + 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.get_enabled_blocks()) + 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()) @@ -212,8 +220,6 @@ class FlowGraph(_FlowGraph, _GUIFlowGraph): return False - - def get_bussrc(self): bussrc = filter(lambda b: _bussrc_searcher.search(b.get_key()), self.get_enabled_blocks()) @@ -277,9 +283,18 @@ class FlowGraph(_FlowGraph, _GUIFlowGraph): #reload namespace n = dict() #load imports - for imp in self.get_imports(): - try: exec imp in n + 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(): diff --git a/grc/python/Generator.py b/grc/python/Generator.py index f064c7528f..d688beba15 100644 --- a/grc/python/Generator.py +++ b/grc/python/Generator.py @@ -24,6 +24,7 @@ import tempfile import shlex import codecs from distutils.spawn import find_executable + from Cheetah.Template import Template from .. gui import Messages @@ -152,11 +153,12 @@ class TopBlockGenerator(object): """ output = list() - title = self._flow_graph.get_option('title') or self._flow_graph.get_option('id').replace('_', ' ').title() - imports = self._flow_graph.get_imports() - variables = self._flow_graph.get_variables() - parameters = self._flow_graph.get_parameters() - monitors = self._flow_graph.get_monitors() + fg = self._flow_graph + title = fg.get_option('title') or fg.get_option('id').replace('_', ' ').title() + imports = fg.get_imports() + variables = fg.get_variables() + parameters = fg.get_parameters() + monitors = fg.get_monitors() # list of blocks not including variables and imports and parameters and disabled def _get_block_sort_text(block): @@ -172,28 +174,32 @@ class TopBlockGenerator(object): return code blocks = expr_utils.sort_objects( - filter(lambda b: b.get_enabled() and not b.get_bypassed(), self._flow_graph.iter_blocks()), + filter(lambda b: b.get_enabled() and not b.get_bypassed(), fg.iter_blocks()), lambda b: b.get_id(), _get_block_sort_text ) # List of regular blocks (all blocks minus the special ones) blocks = filter(lambda b: b not in (imports + parameters), blocks) for block in blocks: - if block.get_key() == 'epy_block': - file_path = os.path.join(self._dirname, block.get_id() + '.py') + key = block.get_key() + file_path = os.path.join(self._dirname, block.get_id() + '.py') + if key == 'epy_block': src = block.get_param('_source_code').get_value() output.append((file_path, src)) + elif key == 'epy_module': + src = block.get_param('source_code').get_value() + 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()) - connections = filter(cf, self._flow_graph.get_enabled_connections()) + connections = filter(cf, fg.get_enabled_connections()) - # Get the virtual blocks and resolve their conenctions + # Get the virtual blocks and resolve their connections virtual = filter(lambda c: c.get_source().get_parent().is_virtual_source(), connections) for connection in virtual: source = connection.get_source().resolve_virtual_source() sink = connection.get_sink() - resolved = self._flow_graph.get_parent().Connection(flow_graph=self._flow_graph, porta=source, portb=sink) + resolved = fg.get_parent().Connection(flow_graph=fg, porta=source, portb=sink) connections.append(resolved) # Remove the virtual connection connections.remove(connection) @@ -203,7 +209,7 @@ class TopBlockGenerator(object): # that bypass the selected block and remove the existing ones. This allows adjacent # bypassed blocks to see the newly created connections to downstream blocks, # allowing them to correctly construct bypass connections. - bypassed_blocks = self._flow_graph.get_bypassed_blocks() + bypassed_blocks = fg.get_bypassed_blocks() for block in bypassed_blocks: # Get the upstream connection (off of the sink ports) # Use *connections* not get_connections() @@ -222,7 +228,7 @@ class TopBlockGenerator(object): # Ignore disabled connections continue sink_port = sink.get_sink() - connection = self._flow_graph.get_parent().Connection(flow_graph=self._flow_graph, porta=source_port, portb=sink_port) + connection = fg.get_parent().Connection(flow_graph=fg, porta=source_port, portb=sink_port) connections.append(connection) # Remove this sink connection connections.remove(sink) @@ -235,8 +241,8 @@ class TopBlockGenerator(object): c.get_source().get_parent().get_id(), c.get_sink().get_parent().get_id() )) - connection_templates = self._flow_graph.get_parent().get_connection_templates() - msgs = filter(lambda c: c.is_msg(), self._flow_graph.get_enabled_connections()) + connection_templates = fg.get_parent().get_connection_templates() + msgs = filter(lambda c: c.is_msg(), fg.get_enabled_connections()) # list of variable names var_ids = [var.get_id() for var in parameters + variables] # prepend self. @@ -244,7 +250,7 @@ class TopBlockGenerator(object): # list of callbacks callbacks = [ expr_utils.expr_replace(cb, replace_dict) - for cb in sum([block.get_callbacks() for block in self._flow_graph.get_enabled_blocks()], []) + for cb in sum([block.get_callbacks() for block in fg.get_enabled_blocks()], []) ] # map var id to callbacks var_id2cbs = dict([ @@ -255,7 +261,7 @@ class TopBlockGenerator(object): namespace = { 'title': title, 'imports': imports, - 'flow_graph': self._flow_graph, + 'flow_graph': fg, 'variables': variables, 'parameters': parameters, 'monitors': monitors, diff --git a/grc/python/Param.py b/grc/python/Param.py index 746f677e46..e60f613f00 100644 --- a/grc/python/Param.py +++ b/grc/python/Param.py @@ -31,7 +31,7 @@ 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)$') +_show_id_matcher = re.compile('^(variable\w*|parameter|options|notebook|epy_module)$') #blacklist certain ids, its not complete, but should help diff --git a/grc/python/flow_graph.tmpl b/grc/python/flow_graph.tmpl index 99390067fe..509c2d0612 100644 --- a/grc/python/flow_graph.tmpl +++ b/grc/python/flow_graph.tmpl @@ -68,7 +68,6 @@ sys.path.append(os.environ.get('GRC_HIER_PATH', os.path.expanduser('~/.grc_gnura ##$(imp.replace(" # grc-generated hier_block", "")) $imp #end for - ######################################################## ##Create Class ## Write the class declaration for a top or hier block. @@ -82,6 +81,7 @@ $imp #import gtk #set $icon = gtk.IconTheme().lookup_icon('gnuradio-grc', 32, 0) + class $(class_name)(grc_wxgui.top_block_gui): def __init__($param_str): @@ -92,6 +92,7 @@ class $(class_name)(grc_wxgui.top_block_gui): #end if #elif $generate_options == 'qt_gui' + class $(class_name)(gr.top_block, Qt.QWidget): def __init__($param_str): @@ -118,6 +119,7 @@ class $(class_name)(gr.top_block, Qt.QWidget): self.restoreGeometry(self.settings.value("geometry").toByteArray()) #elif $generate_options == 'no_gui' + class $(class_name)(gr.top_block): def __init__($param_str): @@ -126,6 +128,7 @@ class $(class_name)(gr.top_block): #set $in_sigs = $flow_graph.get_hier_block_stream_io('in') #set $out_sigs = $flow_graph.get_hier_block_stream_io('out') + #if $generate_options == 'hb_qt_gui' class $(class_name)(gr.hier_block2, Qt.QWidget): #else |