summaryrefslogtreecommitdiff
path: root/grc/src/grc_gnuradio
diff options
context:
space:
mode:
Diffstat (limited to 'grc/src/grc_gnuradio')
-rw-r--r--grc/src/grc_gnuradio/Block.py131
-rw-r--r--grc/src/grc_gnuradio/Connection.py42
-rw-r--r--grc/src/grc_gnuradio/Constants.py.in44
-rw-r--r--grc/src/grc_gnuradio/FlowGraph.py147
-rw-r--r--grc/src/grc_gnuradio/Generator.py132
-rw-r--r--grc/src/grc_gnuradio/Makefile.am53
-rw-r--r--grc/src/grc_gnuradio/Param.py255
-rw-r--r--grc/src/grc_gnuradio/Platform.py74
-rw-r--r--grc/src/grc_gnuradio/Port.py135
-rw-r--r--grc/src/grc_gnuradio/__init__.py21
-rw-r--r--grc/src/grc_gnuradio/blks2/Makefile.am31
-rw-r--r--grc/src/grc_gnuradio/blks2/__init__.py28
-rw-r--r--grc/src/grc_gnuradio/blks2/error_rate.py138
-rw-r--r--grc/src/grc_gnuradio/blks2/packet.py194
-rw-r--r--grc/src/grc_gnuradio/blks2/queue.py178
-rw-r--r--grc/src/grc_gnuradio/blks2/selector.py133
-rw-r--r--grc/src/grc_gnuradio/usrp/Makefile.am28
-rw-r--r--grc/src/grc_gnuradio/usrp/__init__.py25
-rw-r--r--grc/src/grc_gnuradio/usrp/simple_usrp.py379
-rw-r--r--grc/src/grc_gnuradio/utils/Makefile.am30
-rw-r--r--grc/src/grc_gnuradio/utils/__init__.py22
-rw-r--r--grc/src/grc_gnuradio/utils/convert_hier.py81
-rw-r--r--grc/src/grc_gnuradio/utils/expr_utils.py140
-rw-r--r--grc/src/grc_gnuradio/utils/extract_docs.py109
-rw-r--r--grc/src/grc_gnuradio/wxgui/Makefile.am29
-rw-r--r--grc/src/grc_gnuradio/wxgui/__init__.py30
-rw-r--r--grc/src/grc_gnuradio/wxgui/callback_controls.py281
-rw-r--r--grc/src/grc_gnuradio/wxgui/top_block_gui.py99
28 files changed, 2989 insertions, 0 deletions
diff --git a/grc/src/grc_gnuradio/Block.py b/grc/src/grc_gnuradio/Block.py
new file mode 100644
index 000000000..a14df8ec9
--- /dev/null
+++ b/grc/src/grc_gnuradio/Block.py
@@ -0,0 +1,131 @@
+"""
+Copyright 2008 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
+"""
+##@package grc_gnuradio.Block
+#Flow graph block.
+#@author Josh Blum
+
+from grc.elements.Block import Block as _Block
+from grc import Utils
+from utils import extract_docs
+
+class Block(_Block):
+
+ ##for make source to keep track of indexes
+ _source_count = 0
+ ##for make sink to keep track of indexes
+ _sink_count = 0
+
+ def __init__(self, flow_graph, n):
+ """
+ Make a new block from nested data.
+ @param flow graph the parent element
+ @param n the nested odict
+ @return block a new block
+ """
+ #grab the data
+ doc = Utils.exists_or_else(n, 'doc', '')
+ imports = map(lambda i: i.strip(), Utils.listify(n, 'import'))
+ make = n['make']
+ checks = Utils.listify(n, 'check')
+ callbacks = Utils.listify(n, 'callback')
+ #build the block
+ _Block.__init__(
+ self,
+ flow_graph=flow_graph,
+ n=n,
+ )
+ self._doc = doc
+ self._imports = imports
+ self._make = make
+ self._callbacks = callbacks
+ self._checks = checks
+
+ def validate(self):
+ """!
+ Validate this block.
+ Call the base class validate.
+ Evaluate the checks: each check must evaluate to True.
+ Adjust the nports.
+ """
+ _Block.validate(self)
+ #evaluate the checks
+ for check in self._checks:
+ check_res = self.resolve_dependencies(check)
+ try:
+ check_eval = self.get_parent().evaluate(check_res)
+ try: assert check_eval
+ except AssertionError: self._add_error_message('Check "%s" failed.'%check)
+ except: self._add_error_message('Check "%s" did not evaluate.'%check)
+ for ports, Port in (
+ (self._sources, self.get_parent().get_parent().Source),
+ (self._sinks, self.get_parent().get_parent().Sink),
+ ):
+ #how many ports?
+ num_ports = len(ports)
+ #do nothing for 0 ports
+ if not num_ports: continue
+ #get the nports setting
+ port0 = ports[str(0)]
+ nports = port0.get_nports()
+ #do nothing for no nports
+ if not nports: continue
+ #do nothing if nports is already num ports
+ if nports == num_ports: continue
+ #remove excess ports and connections
+ if nports < num_ports:
+ #remove the connections
+ for key in map(str, range(nports, num_ports)):
+ port = ports[key]
+ for connection in port.get_connections():
+ self.get_parent().remove_element(connection)
+ #remove the ports
+ for key in map(str, range(nports, num_ports)): ports.pop(key)
+ continue
+ #add more ports
+ if nports > num_ports:
+ for key in map(str, range(num_ports, nports)):
+ n = port0._n
+ n['key'] = key
+ port = Port(self, n)
+ ports[key] = port
+ continue
+
+ def get_doc(self):
+ doc = self._doc.strip('\n').replace('\\\n', '')
+ #merge custom doc with doxygen docs
+ return '\n'.join([doc, extract_docs.extract(self.get_key())]).strip('\n')
+
+ def get_imports(self):
+ """!
+ Resolve all import statements.
+ Split each import statement at newlines.
+ Combine all import statments into a list.
+ Filter empty imports.
+ @return a list of import statements
+ """
+ return filter(lambda i: i, sum(map(lambda i: self.resolve_dependencies(i).split('\n'), self._imports), []))
+
+ def get_make(self): return self.resolve_dependencies(self._make)
+
+ def get_callbacks(self):
+ """!
+ Get a list of function callbacks for this block.
+ @return a list of strings
+ """
+ return map(lambda c: self.get_id() + '.' + self.resolve_dependencies(c), self._callbacks)
diff --git a/grc/src/grc_gnuradio/Connection.py b/grc/src/grc_gnuradio/Connection.py
new file mode 100644
index 000000000..c7d6a74c7
--- /dev/null
+++ b/grc/src/grc_gnuradio/Connection.py
@@ -0,0 +1,42 @@
+"""
+Copyright 2008 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
+"""
+##@package grc_gnuradio.Connection
+#Flow graph connection.
+#A connection exists between 2 ports.
+#One port must be input, one output.
+#The port decided whether it can have the connection.
+#@author Josh Blum
+
+from grc.elements.Connection import Connection as _Connection
+
+class Connection(_Connection):
+
+ def validate(self):
+ """
+ Validate the connections.
+ The ports must match in type and vector length.
+ """
+ _Connection.validate(self) #checks type
+ #check vector length
+ source_vlen = self.get_source().get_vlen()
+ sink_vlen = self.get_sink().get_vlen()
+ try: assert(source_vlen == sink_vlen)
+ except AssertionError: self._add_error_message('Source vector length "%s" does not match sink vector length "%s".'%(source_vlen, sink_vlen))
+
+
diff --git a/grc/src/grc_gnuradio/Constants.py.in b/grc/src/grc_gnuradio/Constants.py.in
new file mode 100644
index 000000000..66b773e79
--- /dev/null
+++ b/grc/src/grc_gnuradio/Constants.py.in
@@ -0,0 +1,44 @@
+"""
+Copyright 2008 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
+"""
+##@package grc_gnuradio.Constants
+#Global constants for grc gnuradio package
+#@author Josh Blum
+
+import os
+import sys
+import stat
+
+PYEXEC = '@PYTHONW@'
+
+#setup paths
+DOCS_DIR = os.path.join('@docdir@', 'xml')
+DATA_DIR = '@datadir@'
+BLOCKS_DIR = '@blocksdir@'
+HIER_BLOCKS_LIB_DIR = os.path.join(os.path.expanduser('~'), '.grc_gnuradio')
+
+#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
+FLOW_GRAPH_TEMPLATE = os.path.join(DATA_DIR, 'flow_graph.tmpl')
+BLOCK_DTD = os.path.join(DATA_DIR, 'block.dtd')
+BLOCK_TREE = os.path.join(DATA_DIR, 'block_tree.xml')
+DEFAULT_FLOW_GRAPH = os.path.join(DATA_DIR, 'default_flow_graph.grc.xml')
+
diff --git a/grc/src/grc_gnuradio/FlowGraph.py b/grc/src/grc_gnuradio/FlowGraph.py
new file mode 100644
index 000000000..3e037305b
--- /dev/null
+++ b/grc/src/grc_gnuradio/FlowGraph.py
@@ -0,0 +1,147 @@
+"""
+Copyright 2008 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
+"""
+##@package grc_gnuradio.FlowGraph
+#Primative flow graph.
+#@author Josh Blum
+
+from utils import expr_utils
+from grc.elements.FlowGraph import FlowGraph as _FlowGraph
+from Block import Block
+from Connection import Connection
+
+class FlowGraph(_FlowGraph):
+
+ def _get_io_signature(self, pad_key):
+ """!
+ Get an io signature for this flow graph.
+ The pad key determines the directionality of the io signature.
+ @param pad_key a string of pad_source or pad_sink
+ @return a dict with: type, nports, vlen, size
+ """
+ pads = filter(lambda b: b.get_key() == pad_key, self.get_enabled_blocks())
+ if not pads: return {
+ 'nports': '0',
+ 'type': '',
+ 'vlen': '0',
+ 'size': '0',
+ }
+ pad = pads[0] #take only the first, user should not have more than 1
+ #load io signature
+ return {
+ 'nports': str(pad.get_param('nports').evaluate()),
+ 'type': str(pad.get_param('type').evaluate()),
+ 'vlen': str(pad.get_param('vlen').evaluate()),
+ 'size': pad.get_param('type').get_opt('size'),
+ }
+
+ def get_input_signature(self):
+ """!
+ Get the io signature for the input side of this flow graph.
+ The io signature with be "0", "0" if no pad source is present.
+ @return a string tuple of type, num_ports, port_size
+ """
+ return self._get_io_signature('pad_source')
+
+ def get_output_signature(self):
+ """!
+ Get the io signature for the output side of this flow graph.
+ The io signature with be "0", "0" if no pad sink is present.
+ @return a string tuple of type, num_ports, port_size
+ """
+ return self._get_io_signature('pad_sink')
+
+ def get_imports(self):
+ """!
+ Get a set of all import statments in this flow graph namespace.
+ @return 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.
+ @return a sorted list of variable blocks in order of dependency (indep -> dep)
+ """
+ variables = filter(lambda b: b.get_key() in (
+ 'variable', 'variable_slider', 'variable_chooser', 'variable_text_box'
+ ), self.get_enabled_blocks())
+ #map var id to variable block
+ id2var = dict([(var.get_id(), var) for var in variables])
+ #map var id to variable code
+ #variable code is a concatenation of all param code (without the id param)
+ id2expr = dict([(var.get_id(),
+ ' '.join([param.to_code() for param in filter(lambda p: p.get_key() != 'id',var.get_params())])
+ ) for var in variables])
+ #sort according to dependency
+ sorted_ids = expr_utils.sort_variables(id2expr)
+ #create list of sorted variable blocks
+ variables = [id2var[id] for id in sorted_ids]
+ return variables
+
+ def get_parameters(self):
+ """!
+ Get a list of all paramterized variables in this flow graph namespace.
+ @return a list of paramterized variables
+ """
+ parameters = filter(lambda b: b.get_key() == 'parameter', self.get_enabled_blocks())
+ return parameters
+
+ def evaluate(self, expr):
+ """!
+ Evaluate the expression.
+ @param expr the string expression
+ @throw Exception bad expression
+ @return the evaluated data
+ """
+ if self.is_flagged():
+ self.deflag()
+ #reload namespace
+ n = dict()
+ #load imports
+ for imp in self.get_imports():
+ try: exec imp in n
+ 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:
+ if variable.get_key() == 'variable_chooser':
+ choices = variable.get_param('choices').to_code()
+ value_index = variable.get_param('value_index').to_code()
+ e = eval("%s[%s]"%(choices, value_index), n, n)
+ else:
+ e = eval(variable.get_param('value').to_code(), n, n)
+ n[variable.get_id()] = e
+ except: pass
+ #make namespace public
+ self.n = n
+ #evaluate
+ e = eval(expr, self.n, self.n)
+ return e
+
diff --git a/grc/src/grc_gnuradio/Generator.py b/grc/src/grc_gnuradio/Generator.py
new file mode 100644
index 000000000..02c9de291
--- /dev/null
+++ b/grc/src/grc_gnuradio/Generator.py
@@ -0,0 +1,132 @@
+"""
+Copyright 2008 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
+"""
+##@package grc_gnuradio.Generator
+#Create python based flow graphs.
+#@author Josh Blum
+
+import os
+import subprocess
+from Cheetah.Template import Template
+from utils import expr_utils
+from Constants import *
+from utils import convert_hier
+
+class Generator(object):
+
+ def __init__(self, flow_graph, file_path):
+ """!
+ Initialize the generator object.
+ Determine the file to generate.
+ @param flow_graph the flow graph object
+ @param file_path the path to write the file to
+ """
+ self._flow_graph = flow_graph
+ self._generate_options = self._flow_graph.get_option('generate_options')
+ if self._generate_options == 'hb':
+ self._mode = HIER_BLOCK_FILE_MODE
+ dirname = HIER_BLOCKS_LIB_PATH
+ else:
+ self._mode = TOP_BLOCK_FILE_MODE
+ dirname = os.path.dirname(file_path)
+ filename = self._flow_graph.get_option('id') + '.py'
+ self._file_path = os.path.join(dirname, filename)
+
+ def get_file_path(self): return self._file_path
+
+ def write(self):
+ #generate
+ open(self.get_file_path(), 'w').write(str(self))
+ if self._generate_options == 'hb':
+ #convert hier block to xml wrapper
+ convert_hier.convert_hier(self._flow_graph, self.get_file_path())
+ os.chmod(self.get_file_path(), self._mode)
+
+ def get_popen(self):
+ """!
+ Execute this python flow graph.
+ @return a popen object
+ """
+ #execute
+ cmds = [PYEXEC, self.get_file_path()]
+ if self._generate_options == 'no_gui':
+ cmds = ['xterm', '-e'] + cmds
+ p = subprocess.Popen(args=cmds, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=False, universal_newlines=True)
+ return p
+
+ def __str__(self):
+ """!
+ Convert the flow graph to python code.
+ @return a string of python code
+ """
+ imports = self._flow_graph.get_imports()
+ variables = self._flow_graph.get_variables()
+ parameters = self._flow_graph.get_parameters()
+ #list of blocks not including variables and imports and parameters and disabled
+ blocks = sorted(self._flow_graph.get_enabled_blocks(), lambda x, y: cmp(x.get_id(), y.get_id()))
+ blocks = filter(lambda b: b not in (imports + parameters + variables), blocks)
+ #list of connections where each endpoint is enabled
+ connections = self._flow_graph.get_enabled_connections()
+ #list of variable names
+ var_ids = [var.get_id() for var in parameters + variables]
+ #list of callbacks (prepend self.)
+ callbacks = [
+ expr_utils.expr_prepend(cb, var_ids, 'self.')
+ for cb in sum([block.get_callbacks() for block in self._flow_graph.get_blocks()], [])
+ ]
+ #map var id to the expression (prepend self.)
+ var_id2expr = dict(
+ [(var.get_id(), expr_utils.expr_prepend(var.get_make().split('\n')[0], var_ids, 'self.'))
+ for var in parameters + variables]
+ )
+ #create graph structure for variables
+ variable_graph = expr_utils.get_graph(var_id2expr)
+ #map var id to direct dependents
+ #for each var id, make a list of all 2nd order edges
+ #use all edges of that id that are not also 2nd order edges
+ #meaning: list variables the ONLY depend directly on this variable
+ #and not variables that also depend indirectly on this variable
+ var_id2deps = dict(
+ [(var_id, filter(lambda e: e not in sum([list(variable_graph.get_edges(edge))
+ for edge in variable_graph.get_edges(var_id)], []), variable_graph.get_edges(var_id)
+ )
+ )
+ for var_id in var_ids]
+ )
+ #map var id to callbacks
+ var_id2cbs = dict(
+ [(var_id, filter(lambda c: var_id in expr_utils.expr_split(c), callbacks))
+ for var_id in var_ids]
+ )
+ #load the namespace
+ namespace = {
+ 'imports': imports,
+ 'flow_graph': self._flow_graph,
+ 'variables': variables,
+ 'parameters': parameters,
+ 'blocks': blocks,
+ 'connections': connections,
+ 'generate_options': self._generate_options,
+ 'var_id2expr': var_id2expr,
+ 'var_id2deps': var_id2deps,
+ 'var_id2cbs': var_id2cbs,
+ }
+ #build the template
+ t = Template(open(FLOW_GRAPH_TEMPLATE, 'r').read(), namespace)
+ return str(t)
+
diff --git a/grc/src/grc_gnuradio/Makefile.am b/grc/src/grc_gnuradio/Makefile.am
new file mode 100644
index 000000000..a2ef0f4cc
--- /dev/null
+++ b/grc/src/grc_gnuradio/Makefile.am
@@ -0,0 +1,53 @@
+#
+# Copyright 2008 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 $(top_srcdir)/grc/Makefile.common
+
+SUBDIRS = blks2 usrp utils wxgui
+
+ourpythondir = $(pythondir)/grc_gnuradio
+
+ourpython_PYTHON = \
+ __init__.py \
+ Constants.py \
+ Block.py \
+ Connection.py \
+ FlowGraph.py \
+ Generator.py \
+ Platform.py \
+ Param.py \
+ Port.py
+
+docdir = $(prefix)/share/doc/@PACKAGE@-@VERSION@
+
+BUILT_SOURCES = Constants.py
+
+Constants.py: Makefile $(srcdir)/Constants.py.in
+ sed \
+ -e 's|@PYTHONW[@]|$(PYTHONW)|g' \
+ -e 's|@datadir[@]|$(grc_gnuradio_data_dir)|g' \
+ -e 's|@blocksdir[@]|$(grc_gnuradio_blocks_dir)|g' \
+ -e 's|@docdir[@]|$(docdir)|g' \
+ $(srcdir)/Constants.py.in > $@
+
+EXTRA_DIST = Constants.py.in
+
+MOSTLYCLEANFILES = $(BUILT_SOURCES)
diff --git a/grc/src/grc_gnuradio/Param.py b/grc/src/grc_gnuradio/Param.py
new file mode 100644
index 000000000..207b29839
--- /dev/null
+++ b/grc/src/grc_gnuradio/Param.py
@@ -0,0 +1,255 @@
+"""
+Copyright 2008 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
+"""
+##@package grc_gnuradio.Param
+#Flow graph block parameters.
+#@author Josh Blum
+
+from utils import expr_utils
+from grc.elements.Param import Param as _Param
+import os
+
+class Param(_Param):
+
+ _init = False
+ _hostage_cells = list()
+
+ ##possible param types
+ TYPES = _Param.TYPES + [
+ 'complex', 'real', 'int',
+ 'complex_vector', 'real_vector', 'int_vector',
+ 'hex', 'string',
+ 'file_open', 'file_save',
+ 'id',
+ 'grid_pos', 'import',
+ ]
+
+ def get_hide(self):
+ """!
+ Get the hide value from the base class.
+ If hide was empty, and this is a type controller, set hide to part.
+ If hide was empty, and this is an id of a non variable, set hide to part.
+ @return hide the hide property string
+ """
+ hide = _Param.get_hide(self)
+ #hide IO controlling params
+ if not hide and self.get_key() in (
+ 'type', 'vlen', 'num_inputs', 'num_outputs'
+ ): hide = 'part'
+ #hide ID in non variable blocks
+ elif not hide and self.get_key() == 'id' and self.get_parent().get_key() not in (
+ 'variable', 'variable_slider', 'variable_chooser', 'variable_text_box', 'parameter', 'options'
+ ): hide = 'part'
+ return hide
+
+ def evaluate(self):
+ """!
+ Evaluate the value.
+ @return evaluated type
+ """
+ self._lisitify_flag = False
+ self._stringify_flag = False
+ self._hostage_cells = list()
+ def eval_string(v):
+ try:
+ e = self.get_parent().get_parent().evaluate(v)
+ assert(isinstance(e, str))
+ return e
+ except:
+ self._stringify_flag = True
+ return v
+ t = self.get_type()
+ v = self.get_value()
+ #########################
+ # Enum Type
+ #########################
+ if self.is_enum(): return self.get_value()
+ #########################
+ # Numeric Types
+ #########################
+ elif t in ('raw', 'complex', 'real', 'int', 'complex_vector', 'real_vector', 'int_vector', 'hex'):
+ #raise exception if python cannot evaluate this value
+ try: e = self.get_parent().get_parent().evaluate(v)
+ except:
+ self._add_error_message('Value "%s" cannot be evaluated.'%v)
+ raise Exception
+ #raise an exception if the data is invalid
+ if t == 'raw': return e
+ elif t == 'complex':
+ try: assert(isinstance(e, (complex, float, int, long)))
+ except AssertionError:
+ self._add_error_message('Expression "%s" is invalid for type complex.'%str(e))
+ raise Exception
+ return e
+ elif t == 'real':
+ try: assert(isinstance(e, (float, int, long)))
+ except AssertionError:
+ self._add_error_message('Expression "%s" is invalid for type real.'%str(e))
+ raise Exception
+ return e
+ elif t == 'int':
+ try: assert(isinstance(e, (int, long)))
+ except AssertionError:
+ self._add_error_message('Expression "%s" is invalid for type integer.'%str(e))
+ raise Exception
+ return e
+ elif t == 'complex_vector':
+ if not isinstance(e, (tuple, list, set)):
+ self._lisitify_flag = True
+ e = [e]
+ try:
+ for ei in e:
+ assert(isinstance(ei, (complex, float, int, long)))
+ except AssertionError:
+ self._add_error_message('Expression "%s" is invalid for type complex vector.'%str(e))
+ raise Exception
+ return e
+ elif t == 'real_vector':
+ if not isinstance(e, (tuple, list, set)):
+ self._lisitify_flag = True
+ e = [e]
+ try:
+ for ei in e:
+ assert(isinstance(ei, (float, int, long)))
+ except AssertionError:
+ self._add_error_message('Expression "%s" is invalid for type real vector.'%str(e))
+ raise Exception
+ return e
+ elif t == 'int_vector':
+ if not isinstance(e, (tuple, list, set)):
+ self._lisitify_flag = True
+ e = [e]
+ try:
+ for ei in e:
+ assert(isinstance(ei, (int, long)))
+ except AssertionError:
+ self._add_error_message('Expression "%s" is invalid for type integer vector.'%str(e))
+ raise Exception
+ return e
+ elif t == 'hex':
+ return hex(e)
+ else: raise TypeError, 'Type "%s" not handled'%t
+ #########################
+ # String Types
+ #########################
+ elif t in ('string', 'file_open', 'file_save'):
+ #do not check if file/directory exists, that is a runtime issue
+ e = eval_string(v)
+ return str(e)
+ #########################
+ # Unique ID Type
+ #########################
+ elif t == 'id':
+ #can python use this as a variable?
+ try:
+ assert(len(v) > 0)
+ assert(v[0].isalpha())
+ for c in v: assert(c.isalnum() or c in ('_',))
+ except AssertionError:
+ self._add_error_message('ID "%s" must be alpha-numeric or underscored, and begin with a letter.'%v)
+ raise Exception
+ params = self.get_all_params('id')
+ keys = [param.get_value() for param in params]
+ try: assert(len(keys) == len(set(keys)))
+ except:
+ self._add_error_message('ID "%s" is not unique.'%v)
+ raise Exception
+ return v
+ #########################
+ # Grid Position Type
+ #########################
+ elif t == 'grid_pos':
+ if not v: return '' #allow for empty grid pos
+ e = self.get_parent().get_parent().evaluate(v)
+ try:
+ assert(isinstance(e, (list, tuple)) and len(e) == 4)
+ for ei in e: assert(isinstance(ei, int))
+ except AssertionError:
+ self._add_error_message('A grid position must be a list of 4 integers.')
+ raise Exception
+ row, col, row_span, col_span = e
+ #check row, col
+ try: assert(row >= 0 and col >= 0)
+ except AssertionError:
+ self._add_error_message('Row and column must be non-negative.')
+ raise Exception
+ #check row span, col span
+ try: assert(row_span > 0 and col_span > 0)
+ except AssertionError:
+ self._add_error_message('Row and column span must be greater than zero.')
+ raise Exception
+ #calculate hostage cells
+ for r in range(row_span):
+ for c in range(col_span):
+ self._hostage_cells.append((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 cell in param._hostage_cells:
+ if cell in self._hostage_cells:
+ self._add_error_message('Another graphical element is using cell "%s".'%str(cell))
+ raise Exception
+ return e
+ #########################
+ # Import Type
+ #########################
+ elif t == 'import':
+ n = dict() #new namespace
+ try: exec v in n
+ except ImportError:
+ self._add_error_message('Import "%s" failed.'%v)
+ raise Exception
+ except Exception:
+ self._add_error_message('Bad import syntax: "%s".'%v)
+ raise Exception
+ 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.
+ @return a string representing the code
+ """
+ #run init tasks in evaluate
+ if not self._init:
+ self.evaluate()
+ self._init = True
+ v = self.get_value()
+ t = self.get_type()
+ if t in ('string', 'file_open', 'file_save'): #string types
+ if self._stringify_flag:
+ return '"%s"'%v.replace('"', '\"')
+ else:
+ return v
+ elif t in ('complex_vector', 'real_vector', 'int_vector'): #vector types
+ 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.
+ @param type the specified type
+ @return 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_blocks()], [])
+
diff --git a/grc/src/grc_gnuradio/Platform.py b/grc/src/grc_gnuradio/Platform.py
new file mode 100644
index 000000000..22a4c7ecd
--- /dev/null
+++ b/grc/src/grc_gnuradio/Platform.py
@@ -0,0 +1,74 @@
+"""
+Copyright 2008 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
+"""
+##@package grc_gnuradio.Platform
+#Gnuradio python specific platform.
+#@author Josh Blum
+
+import os
+from grc.Constants import FLOW_GRAPH_FILE_EXTENSION
+from grc.elements.Platform import Platform as _Platform
+from FlowGraph import FlowGraph as _FlowGraph
+from Connection import Connection as _Connection
+from Block import Block as _Block
+from Port import Source,Sink
+from Param import Param as _Param
+from Generator import Generator
+from Constants import *
+
+class Platform(_Platform):
+
+ def __init__(self, block_paths_internal_only=[], block_paths_external=[]):
+ """!
+ Make a platform for gnuradio.
+ The internal only list will replace the current block path.
+ @param block_paths_internal_only a list of blocks internal to this platform
+ @param block_paths_external a list of blocks to load in addition to the above blocks
+ """
+ #ensure hier dir
+ if not os.path.exists(HIER_BLOCKS_LIB_DIR): os.mkdir(HIER_BLOCKS_LIB_DIR)
+ #handle internal/only
+ if block_paths_internal_only:
+ block_paths = map(lambda b: os.path.join(BLOCKS_DIR, b), ['options.xml'] + block_paths_internal_only)
+ else: block_paths = [BLOCKS_DIR]
+ #handle external
+ block_paths.extend(block_paths_external)
+ #append custom hiers
+ block_paths.append(HIER_BLOCKS_LIB_DIR)
+ #init
+ _Platform.__init__(
+ self,
+ name='GNURadio Python',
+ key='gnuradio_python',
+ block_paths=block_paths,
+ block_dtd=BLOCK_DTD,
+ block_tree=BLOCK_TREE,
+ default_flow_graph=DEFAULT_FLOW_GRAPH,
+ generator=Generator,
+ )
+
+ ##############################################
+ # Constructors
+ ##############################################
+ FlowGraph = _FlowGraph
+ Connection = _Connection
+ Block = _Block
+ Source = Source
+ Sink = Sink
+ Param = _Param
+
diff --git a/grc/src/grc_gnuradio/Port.py b/grc/src/grc_gnuradio/Port.py
new file mode 100644
index 000000000..c8e899781
--- /dev/null
+++ b/grc/src/grc_gnuradio/Port.py
@@ -0,0 +1,135 @@
+"""
+Copyright 2008 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
+"""
+##@package grc_gnuradio.Port
+#Flow graph block port (source or sink).
+#@author Josh Blum
+
+from grc.elements.Port import Port as _Port
+from grc import Utils
+from grc.Constants import MAX_NUM_PORTS
+
+class Port(_Port):
+
+ ##possible port types
+ TYPES = ['complex', 'float', 'int', 'short', 'byte']
+
+ def __init__(self, block, n):
+ """
+ Make a new port from nested data.
+ @param block the parent element
+ @param n the nested odict
+ @return a new port
+ """
+ vlen = Utils.exists_or_else(n, 'vlen', '1')
+ nports = Utils.exists_or_else(n, 'nports', '')
+ optional = Utils.exists_or_else(n, 'optional', '')
+ #build the port
+ _Port.__init__(
+ self,
+ block=block,
+ n=n,
+ )
+ self._nports = nports
+ self._vlen = vlen
+ self._optional = bool(optional)
+
+ def get_vlen(self):
+ """
+ Get the vector length.
+ If the evaluation of vlen cannot be cast to an integer, return 1.
+ @return 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 an integer, return 1.
+ @return the number of ports or 1
+ """
+ nports = self.get_parent().resolve_dependencies(self._nports)
+ #return blank if nports is blank
+ if not nports: return ''
+ try:
+ nports = int(self.get_parent().get_parent().evaluate(nports))
+ assert(0 < nports <= MAX_NUM_PORTS)
+ return 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.
+ @return a hex color code.
+ """
+ try:
+ if self.get_vlen() == 1:
+ return {#vlen is 1
+ 'complex': '#3399FF',
+ 'float': '#FF8C69',
+ 'int': '#00FF99',
+ 'short': '#FFFF66',
+ 'byte': '#FF66FF',
+ }[self.get_type()]
+ return {#vlen is non 1
+ 'complex': '#3399AA',
+ 'float': '#CC8C69',
+ 'int': '#00CC99',
+ 'short': '#CCCC33',
+ 'byte': '#CC66CC',
+ }[self.get_type()]
+ except: return _Port.get_color(self)
+
+ def is_empty(self):
+ """!
+ Is this port empty?
+ An empty port has no connections.
+ Not empty of optional is set.
+ @return true if empty
+ """
+ return not self.get_optional() and not self.get_connections()
+
+class Source(Port):
+
+ def __init__(self, block, n):
+ self._n = n #save n
+ #key is port index
+ n['key'] = str(block._source_count)
+ block._source_count = block._source_count + 1
+ Port.__init__(self, block, n)
+
+ def __del__(self):
+ self.get_parent()._source_count = self.get_parent()._source_count - 1
+
+class Sink(Port):
+
+ def __init__(self, block, n):
+ self._n = n #save n
+ #key is port index
+ n['key'] = str(block._sink_count)
+ block._sink_count = block._sink_count + 1
+ Port.__init__(self, block, n)
+
+ def __del__(self):
+ self.get_parent()._sink_count = self.get_parent()._sink_count - 1
diff --git a/grc/src/grc_gnuradio/__init__.py b/grc/src/grc_gnuradio/__init__.py
new file mode 100644
index 000000000..fe09e0d4b
--- /dev/null
+++ b/grc/src/grc_gnuradio/__init__.py
@@ -0,0 +1,21 @@
+"""
+Copyright 2008 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
+"""
+##@package grc_gnuradio
+#gnuradio overloaded elements and supplemental python modules
+#@author Josh Blum
diff --git a/grc/src/grc_gnuradio/blks2/Makefile.am b/grc/src/grc_gnuradio/blks2/Makefile.am
new file mode 100644
index 000000000..307ec863f
--- /dev/null
+++ b/grc/src/grc_gnuradio/blks2/Makefile.am
@@ -0,0 +1,31 @@
+#
+# Copyright 2008 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 $(top_srcdir)/Makefile.common
+
+ourpythondir = $(pythondir)/grc_gnuradio/blks2
+
+ourpython_PYTHON = \
+ __init__.py \
+ error_rate.py \
+ packet.py \
+ queue.py \
+ selector.py
diff --git a/grc/src/grc_gnuradio/blks2/__init__.py b/grc/src/grc_gnuradio/blks2/__init__.py
new file mode 100644
index 000000000..cd1b793c4
--- /dev/null
+++ b/grc/src/grc_gnuradio/blks2/__init__.py
@@ -0,0 +1,28 @@
+# Copyright 2008 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 queue import queue_sink_thread
+from queue import queue_sink_c, queue_sink_f, queue_sink_i, queue_sink_s, queue_sink_b
+from queue import queue_source_c, queue_source_f, queue_source_i, queue_source_s, queue_source_b
+
+from selector import selector, valve
+from packet import packet_encoder, packet_decoder
+from error_rate import error_rate
+
diff --git a/grc/src/grc_gnuradio/blks2/error_rate.py b/grc/src/grc_gnuradio/blks2/error_rate.py
new file mode 100644
index 000000000..eb09940cb
--- /dev/null
+++ b/grc/src/grc_gnuradio/blks2/error_rate.py
@@ -0,0 +1,138 @@
+# Copyright 2008 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.
+#
+
+default_win_size = 1000
+
+from gnuradio import gr
+import gnuradio.gr.gr_threading as _threading
+import numpy
+
+#generate 1s counts array
+_1s_counts = [sum([1&(i>>j) for j in range(8)]) for i in range(2**8)]
+
+class input_watcher(_threading.Thread):
+ """
+ Read samples from the message queue and hand them to the callback.
+ """
+
+ def __init__(self, msgq, callback):
+ self._msgq = msgq
+ self._callback = callback
+ _threading.Thread.__init__(self)
+ self.setDaemon(1)
+ self.keep_running = True
+ self.start()
+
+ def run(self):
+ r = ''
+ while True:
+ msg = self._msgq.delete_head()
+ itemsize = int(msg.arg1())
+ nitems = int(msg.arg2())
+ s = r + msg.to_string()
+ i = (nitems-nitems%2)*itemsize
+ r = s[i:]
+ s = s[:i]
+ samples = numpy.fromstring(s, numpy.int8)
+ self._callback(samples)
+
+class error_rate(gr.hier_block2):
+ """
+ Sample the incoming data streams (byte) and calculate the bit or symbol error rate.
+ Write the running rate to the output data stream (float).
+ """
+
+ def __init__(self, type='BER', win_size=default_win_size, bits_per_symbol=2):
+ """!
+ Error rate constructor.
+ @param type a string 'BER' or 'SER'
+ @param win_size the number of samples to calculate over
+ @param bits_per_symbol the number of information bits per symbol (BER only)
+ """
+ #init
+ gr.hier_block2.__init__(
+ self, 'error_rate',
+ gr.io_signature(2, 2, gr.sizeof_char),
+ gr.io_signature(1, 1, gr.sizeof_float),
+ )
+ assert type in ('BER', 'SER')
+ self._max_samples = win_size
+ self._bits_per_symbol = bits_per_symbol
+ #setup message queue
+ msg_source = gr.message_source(gr.sizeof_float, 1)
+ self._msgq_source = msg_source.msgq()
+ msgq_sink = gr.msg_queue(2)
+ msg_sink = gr.message_sink(gr.sizeof_char, msgq_sink, False) #False -> blocking
+ inter = gr.interleave(gr.sizeof_char)
+ #start thread
+ self._num_errs = 0
+ self._err_index = 0
+ self._num_samps = 0
+ self._err_array = numpy.zeros(self._max_samples, numpy.int8)
+ if type == 'BER':
+ input_watcher(msgq_sink, self._handler_ber)
+ elif type == 'SER':
+ input_watcher(msgq_sink, self._handler_ser)
+ #connect
+ self.connect(msg_source, self)
+ self.connect((self, 0), (inter, 0))
+ self.connect((self, 1), (inter, 1))
+ self.connect(inter, msg_sink)
+
+ def _handler_ber(self, samples):
+ num = len(samples)/2
+ arr = numpy.zeros(num, numpy.float32)
+ for i in range(num):
+ old_err = self._err_array[self._err_index]
+ #record error
+ self._err_array[self._err_index] = _1s_counts[samples[i*2] ^ samples[i*2 + 1]]
+ self._num_errs = self._num_errs + self._err_array[self._err_index] - old_err
+ #increment index
+ self._err_index = (self._err_index + 1)%self._max_samples
+ self._num_samps = min(self._num_samps + 1, self._max_samples)
+ #write sample
+ arr[i] = float(self._num_errs)/float(self._num_samps*self._bits_per_symbol)
+ #write message
+ msg = gr.message_from_string(arr.tostring(), 0, gr.sizeof_float, num)
+ self._msgq_source.insert_tail(msg)
+
+ def _handler_ser(self, samples):
+ num = len(samples)/2
+ arr = numpy.zeros(num, numpy.float32)
+ for i in range(num):
+ old_err = self._err_array[self._err_index]
+ #record error
+ ref = samples[i*2]
+ res = samples[i*2 + 1]
+ if ref == res:
+ self._err_array[self._err_index] = 0
+ else:
+ self._err_array[self._err_index] = 1
+ #update number of errors
+ self._num_errs = self._num_errs + self._err_array[self._err_index] - old_err
+ #increment index
+ self._err_index = (self._err_index + 1)%self._max_samples
+ self._num_samps = min(self._num_samps + 1, self._max_samples)
+ #write sample
+ arr[i] = float(self._num_errs)/float(self._num_samps)
+ #write message
+ msg = gr.message_from_string(arr.tostring(), 0, gr.sizeof_float, num)
+ self._msgq_source.insert_tail(msg)
+
diff --git a/grc/src/grc_gnuradio/blks2/packet.py b/grc/src/grc_gnuradio/blks2/packet.py
new file mode 100644
index 000000000..5276de109
--- /dev/null
+++ b/grc/src/grc_gnuradio/blks2/packet.py
@@ -0,0 +1,194 @@
+# Copyright 2008 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, packet_utils
+import gnuradio.gr.gr_threading as _threading
+
+##payload length in bytes
+DEFAULT_PAYLOAD_LEN = 512
+
+##how many messages in a queue
+DEFAULT_MSGQ_LIMIT = 2
+
+##threshold for unmaking packets
+DEFAULT_THRESHOLD = 12
+
+#######################################################################################
+## Packet Encoder
+#######################################################################################
+
+class _packet_encoder_thread(_threading.Thread):
+
+ def __init__(self, msgq, payload_length, send):
+ self._msgq = msgq
+ self._payload_length = payload_length
+ self._send = send
+ _threading.Thread.__init__(self)
+ self.setDaemon(1)
+ self.keep_running = True
+ self.start()
+
+ def run(self):
+ sample = '' #residual sample
+ while self.keep_running:
+ msg = self._msgq.delete_head() #blocking read of message queue
+ sample = sample + msg.to_string() #get the body of the msg as a string
+ while len(sample) >= self._payload_length:
+ payload = sample[0:self._payload_length]
+ sample = sample[self._payload_length:]
+ self._send(payload)
+
+class packet_encoder(gr.hier_block2):
+ """
+ Hierarchical block for wrapping packet-based modulators.
+ """
+
+ def __init__(self, item_size_in, samples_per_symbol, bits_per_symbol, access_code='', pad_for_usrp=True, payload_length=-1):
+ """!
+ packet_mod constructor.
+ @param item_size_in the size of the input data stream in bytes
+ @param samples_per_symbol number of samples per symbol
+ @param bits_per_symbol number of bits per symbol
+ @param access_code AKA sync vector
+ @param pad_for_usrp If true, packets are padded such that they end up a multiple of 128 samples
+ @param payload_length number of bytes in a data-stream slice
+ """
+ #setup parameters
+ self._item_size_in = item_size_in
+ self._samples_per_symbol = samples_per_symbol
+ self._bits_per_symbol = bits_per_symbol
+ self._pad_for_usrp = pad_for_usrp
+ if not access_code: #get access code
+ access_code = packet_utils.default_access_code
+ if not packet_utils.is_1_0_string(access_code):
+ raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,)
+ self._access_code = access_code
+ self._pad_for_usrp = pad_for_usrp
+ if payload_length < 0: #get payload length
+ payload_length = DEFAULT_PAYLOAD_LEN
+ if payload_length%self._item_size_in != 0: #verify that packet length is a multiple of the stream size
+ raise ValueError, 'The packet length: "%d" is not a mutiple of the stream size: "%d".'%(payload_length, self._item_size_in)
+ self._payload_length = payload_length
+ #create blocks
+ msg_source = gr.message_source(gr.sizeof_char, DEFAULT_MSGQ_LIMIT)
+ self._msgq_out = msg_source.msgq()
+ self._msgq_in = gr.msg_queue(DEFAULT_MSGQ_LIMIT)
+ msg_sink = gr.message_sink(self._item_size_in, self._msgq_in, False) #False -> blocking
+ #initialize hier2
+ gr.hier_block2.__init__(
+ self,
+ "packet_encoder",
+ gr.io_signature(1, 1, self._item_size_in), # Input signature
+ gr.io_signature(1, 1, gr.sizeof_char) # Output signature
+ )
+ #connect
+ self.connect(self, msg_sink)
+ self.connect(msg_source, self)
+ #start thread
+ _packet_encoder_thread(self._msgq_in, self._payload_length, self._send_packet)
+
+ def _send_packet(self, payload):
+ """!
+ Wrap the payload in a packet and push onto the message queue.
+ @param payload string, data to send
+ """
+ packet = packet_utils.make_packet(
+ payload,
+ self._samples_per_symbol,
+ self._bits_per_symbol,
+ self._access_code,
+ self._pad_for_usrp
+ )
+ msg = gr.message_from_string(packet)
+ self._msgq_out.insert_tail(msg)
+
+#######################################################################################
+## Packet Decoder
+#######################################################################################
+
+class _packet_decoder_thread(_threading.Thread):
+
+ def __init__(self, msgq, callback):
+ _threading.Thread.__init__(self)
+ self.setDaemon(1)
+ self._msgq = msgq
+ self.callback = callback
+ self.keep_running = True
+ self.start()
+
+ def run(self):
+ while self.keep_running:
+ msg = self._msgq.delete_head()
+ ok, payload = packet_utils.unmake_packet(msg.to_string(), int(msg.arg1()))
+ if self.callback:
+ self.callback(ok, payload)
+
+class packet_decoder(gr.hier_block2):
+ """
+ Hierarchical block for wrapping packet-based demodulators.
+ """
+
+ def __init__(self, item_size_out, access_code='', threshold=-1):
+ """!
+ packet_demod constructor.
+ @param item_size_out the size of the output data stream in bytes
+ @param access_code AKA sync vector
+ @param threshold detect access_code with up to threshold bits wrong (-1 -> use default)
+ """
+ #setup
+ self._item_size_out = item_size_out
+ #access code
+ if not access_code: #get access code
+ access_code = packet_utils.default_access_code
+ if not packet_utils.is_1_0_string(access_code):
+ raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,)
+ self._access_code = access_code
+ #threshold
+ if threshold < 0: threshold = DEFAULT_THRESHOLD
+ self._threshold = threshold
+ #blocks
+ self._msgq_in = gr.msg_queue(DEFAULT_MSGQ_LIMIT) #holds packets from the PHY
+ correlator = gr.correlate_access_code_bb(self._access_code, self._threshold)
+ framer_sink = gr.framer_sink_1(self._msgq_in)
+ msg_source = gr.message_source(self._item_size_out, DEFAULT_MSGQ_LIMIT)
+ self._msgq_out = msg_source.msgq()
+ #initialize hier2
+ gr.hier_block2.__init__(
+ self,
+ "packet_decoder",
+ gr.io_signature(1, 1, gr.sizeof_char), # Input signature
+ gr.io_signature(1, 1, self._item_size_out) # Output signature
+ )
+ #connect
+ self.connect(self, correlator, framer_sink)
+ self.connect(msg_source, self)
+ #start thread
+ _packet_decoder_thread(self._msgq_in, self._recv_packet)
+
+ def _recv_packet(self, ok, payload):
+ """!
+ Extract the payload from the packet and push onto message queue.
+ @param ok boolean ok
+ @param payload data received
+ """
+ msg = gr.message_from_string(payload, 0, self._item_size_out, len(payload)/self._item_size_out)
+ if ok: self._msgq_out.insert_tail(msg)
+
+
diff --git a/grc/src/grc_gnuradio/blks2/queue.py b/grc/src/grc_gnuradio/blks2/queue.py
new file mode 100644
index 000000000..cec35e52a
--- /dev/null
+++ b/grc/src/grc_gnuradio/blks2/queue.py
@@ -0,0 +1,178 @@
+# Copyright 2008 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
+import gnuradio.gr.gr_threading as _threading
+import numpy
+
+#######################################################################################
+## Queue Sink Thread
+#######################################################################################
+class queue_sink_thread(_threading.Thread):
+ """!
+ Read samples from the queue sink and execute the callback.
+ """
+
+ def __init__(self, queue_sink, callback):
+ """!
+ Queue sink thread contructor.
+ @param queue_sink the queue to pop messages from
+ @param callback the function of one argument
+ """
+ self._queue_sink = queue_sink
+ self._callback = callback
+ _threading.Thread.__init__(self)
+ self.setDaemon(1)
+ self.keep_running = True
+ self.start()
+
+ def run(self):
+ while self.keep_running:
+ self._callback(self._queue_sink.pop())
+
+#######################################################################################
+## Queue Sink
+#######################################################################################
+class _queue_sink_base(gr.hier_block2):
+ """!
+ Queue sink base, a queue sink for any size queue.
+ Easy read access to a gnuradio data stream from python.
+ Call pop to read a sample from a gnuradio data stream.
+ Samples are cast as python data types, complex, float, or int.
+ """
+
+ def __init__(self, vlen=1):
+ """!
+ Queue sink base contructor.
+ @param vlen the vector length
+ """
+ self._vlen = vlen
+ #initialize hier2
+ gr.hier_block2.__init__(
+ self,
+ "queue_sink",
+ gr.io_signature(1, 1, self._item_size*self._vlen), # Input signature
+ gr.io_signature(0, 0, 0) # Output signature
+ )
+ #create message sink
+ self._msgq = gr.msg_queue(1)
+ message_sink = gr.message_sink(self._item_size*self._vlen, self._msgq, False) #False -> blocking
+ #connect
+ self.connect(self, message_sink)
+ self.arr = ''
+
+ def pop(self):
+ """!
+ Pop a new sample off the front of the queue.
+ @return a new sample
+ """
+ while len(self.arr) < self._item_size*self._vlen:
+ msg = self._msgq.delete_head()
+ self.arr = self.arr + msg.to_string()
+ sample = self.arr[:self._item_size*self._vlen]
+ self.arr = self.arr[self._item_size*self._vlen:]
+ sample = map(self._cast, numpy.fromstring(sample, self._numpy))
+ if self._vlen == 1: return sample[0]
+ return sample
+
+class queue_sink_c(_queue_sink_base):
+ _item_size = gr.sizeof_gr_complex
+ _numpy = numpy.complex64
+ def _cast(self, arg): return complex(arg.real, arg.imag)
+
+class queue_sink_f(_queue_sink_base):
+ _item_size = gr.sizeof_float
+ _numpy = numpy.float32
+ _cast = float
+
+class queue_sink_i(_queue_sink_base):
+ _item_size = gr.sizeof_int
+ _numpy = numpy.int32
+ _cast = int
+
+class queue_sink_s(_queue_sink_base):
+ _item_size = gr.sizeof_short
+ _numpy = numpy.int16
+ _cast = int
+
+class queue_sink_b(_queue_sink_base):
+ _item_size = gr.sizeof_char
+ _numpy = numpy.int8
+ _cast = int
+
+#######################################################################################
+## Queue Source
+#######################################################################################
+class _queue_source_base(gr.hier_block2):
+ """!
+ Queue source base, a queue source for any size queue.
+ Easy write access to a gnuradio data stream from python.
+ Call push to to write a sample into the gnuradio data stream.
+ """
+
+ def __init__(self, vlen=1):
+ """!
+ Queue source base contructor.
+ @param vlen the vector length
+ """
+ self._vlen = vlen
+ #initialize hier2
+ gr.hier_block2.__init__(
+ self,
+ "queue_source",
+ gr.io_signature(0, 0, 0), # Input signature
+ gr.io_signature(1, 1, self._item_size*self._vlen) # Output signature
+ )
+ #create message sink
+ message_source = gr.message_source(self._item_size*self._vlen, 1)
+ self._msgq = message_source.msgq()
+ #connect
+ self.connect(message_source, self)
+
+ def push(self, item):
+ """!
+ Push an item into the back of the queue.
+ @param item the item
+ """
+ if self._vlen == 1: item = [item]
+ arr = numpy.array(item, self._numpy)
+ msg = gr.message_from_string(arr.tostring(), 0, self._item_size, self._vlen)
+ self._msgq.insert_tail(msg)
+
+class queue_source_c(_queue_source_base):
+ _item_size = gr.sizeof_gr_complex
+ _numpy = numpy.complex64
+
+class queue_source_f(_queue_source_base):
+ _item_size = gr.sizeof_float
+ _numpy = numpy.float32
+
+class queue_source_i(_queue_source_base):
+ _item_size = gr.sizeof_int
+ _numpy = numpy.int32
+
+class queue_source_s(_queue_source_base):
+ _item_size = gr.sizeof_short
+ _numpy = numpy.int16
+
+class queue_source_b(_queue_source_base):
+ _item_size = gr.sizeof_char
+ _numpy = numpy.int8
+
diff --git a/grc/src/grc_gnuradio/blks2/selector.py b/grc/src/grc_gnuradio/blks2/selector.py
new file mode 100644
index 000000000..787f6547f
--- /dev/null
+++ b/grc/src/grc_gnuradio/blks2/selector.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python
+#
+# Copyright 2008 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
+
+class selector(gr.hier_block2):
+ """A hier2 block with N inputs and M outputs, where data is only forwarded through input n to output m."""
+ def __init__(self, item_size, num_inputs, num_outputs, input_index, output_index):
+ """!
+ SelectorHelper constructor.
+ @param item_size the size of the gr data stream in bytes
+ @param num_inputs the number of inputs (integer)
+ @param num_outputs the number of outputs (integer)
+ @param input_index the index for the source data
+ @param output_index the index for the destination data
+ """
+ gr.hier_block2.__init__(
+ self, 'selector',
+ gr.io_signature(num_inputs, num_inputs, item_size),
+ gr.io_signature(num_outputs, num_outputs, item_size),
+ )
+ #terminator blocks for unused inputs and outputs
+ self.input_terminators = [gr.null_sink(item_size)] * num_inputs
+ self.output_terminators = [gr.head(item_size, 0)] * num_outputs
+ self.copy = None
+ #connections
+ for i in range(num_inputs): self.connect((self, i), self.input_terminators[i])
+ for i in range(num_outputs): self.connect(gr.null_source(item_size), self.output_terminators[i], (self, i))
+ self.item_size = item_size
+ self.input_index = input_index
+ self.output_index = output_index
+ self.num_inputs = num_inputs
+ self.num_outputs = num_outputs
+ self._connect_current()
+
+ def _indexes_valid(self):
+ """!
+ Are the input and output indexes within range of the number of inputs and outputs?
+ @return true if input index and output index are in range
+ """
+ return self.input_index in range(self.num_inputs) and self.output_index in range(self.num_outputs)
+
+ def _connect_current(self):
+ """If the input and output indexes are valid:
+ disconnect the blocks at the input and output index from their terminators,
+ and connect them to one another. Then connect the terminators to one another."""
+ if self._indexes_valid():
+ self.disconnect((self, self.input_index), self.input_terminators[self.input_index])
+ self.disconnect(self.output_terminators[self.output_index], (self, self.output_index))
+ self.copy = gr.skiphead(self.item_size, 0)
+ self.connect((self, self.input_index), self.copy)
+ self.connect(self.copy, (self, self.output_index))
+ self.connect(self.output_terminators[self.output_index], self.input_terminators[self.input_index])
+
+ def _disconnect_current(self):
+ """If the input and output indexes are valid:
+ disconnect the blocks at the input and output index from one another,
+ and the terminators at the input and output index from one another.
+ Reconnect the blocks to the terminators."""
+ if self._indexes_valid():
+ self.disconnect((self, self.input_index), self.copy)
+ self.disconnect(self.copy, (self, self.output_index))
+ self.disconnect(self.output_terminators[self.output_index], self.input_terminators[self.input_index])
+ del self.copy
+ self.copy = None
+ self.connect((self, self.input_index), self.input_terminators[self.input_index])
+ self.connect(self.output_terminators[self.output_index], (self, self.output_index))
+
+ def set_input_index(self, input_index):
+ """!
+ Change the block to the new input index if the index changed.
+ @param input_index the new input index
+ """
+ if self.input_index != input_index:
+ self.lock()
+ self._disconnect_current()
+ self.input_index = input_index
+ self._connect_current()
+ self.unlock()
+
+ def set_output_index(self, output_index):
+ """!
+ Change the block to the new output index if the index changed.
+ @param output_index the new output index
+ """
+ if self.output_index != output_index:
+ self.lock()
+ self._disconnect_current()
+ self.output_index = output_index
+ self._connect_current()
+ self.unlock()
+
+class valve(selector):
+ """Wrapper for selector with 1 input and 1 output."""
+
+ def __init__(self, item_size, open):
+ """!
+ Constructor for valve.
+ @param item_size the size of the gr data stream in bytes
+ @param open true if initial valve state is open
+ """
+ if open: output_index = -1
+ else: output_index = 0
+ selector.__init__(self, item_size, 1, 1, 0, output_index)
+
+ def set_open(self, open):
+ """!
+ Callback to set open state.
+ @param open true to set valve state to open
+ """
+ if open: output_index = -1
+ else: output_index = 0
+ self.set_output_index(output_index)
+
diff --git a/grc/src/grc_gnuradio/usrp/Makefile.am b/grc/src/grc_gnuradio/usrp/Makefile.am
new file mode 100644
index 000000000..9689be8e0
--- /dev/null
+++ b/grc/src/grc_gnuradio/usrp/Makefile.am
@@ -0,0 +1,28 @@
+#
+# Copyright 2008 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 $(top_srcdir)/Makefile.common
+
+ourpythondir = $(pythondir)/grc_gnuradio/usrp
+
+ourpython_PYTHON = \
+ __init__.py \
+ simple_usrp.py
diff --git a/grc/src/grc_gnuradio/usrp/__init__.py b/grc/src/grc_gnuradio/usrp/__init__.py
new file mode 100644
index 000000000..0962df14c
--- /dev/null
+++ b/grc/src/grc_gnuradio/usrp/__init__.py
@@ -0,0 +1,25 @@
+# Copyright 2008 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 simple_usrp import simple_source_c, simple_source_s
+from simple_usrp import dual_source_c, dual_source_s
+from simple_usrp import simple_sink_c, simple_sink_s
+from simple_usrp import dual_sink_c, dual_sink_s
+
diff --git a/grc/src/grc_gnuradio/usrp/simple_usrp.py b/grc/src/grc_gnuradio/usrp/simple_usrp.py
new file mode 100644
index 000000000..118ccc16a
--- /dev/null
+++ b/grc/src/grc_gnuradio/usrp/simple_usrp.py
@@ -0,0 +1,379 @@
+# Copyright 2008 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import sys
+from gnuradio import usrp, gr
+
+####################################################################
+# Helper Functions
+####################################################################
+
+def _set_frequency(u, which, subdev, frequency, verbose=False):
+ """!
+ Set the carrier frequency for the given subdevice.
+ @param u the usrp source/sink
+ @param which specifies the DDC/DUC number
+ @param frequency the carrier frequency in Hz
+ @param verbose if true, print usrp tuning information
+ """
+ r = u.tune(which, subdev, frequency)
+ if not verbose: return
+ print subdev.side_and_name()
+ if r:
+ print " r.baseband_frequency =", r.baseband_freq
+ print " r.dxc_frequency =", r.dxc_freq
+ print " r.residual_frequency =", r.residual_freq
+ print " r.inverted =", r.inverted
+ else: print >> sys.stderr, 'Error calling tune on subdevice.'
+
+def _setup_rx_subdev(u, subdev_spec, ddc, gain, frequency, auto_tr=None, rx_ant=None):
+ """!
+ Setup a usrp receive subdevice by setting gain and frequency.
+ Add the gain and frequency callbacks to the flow graph.
+ FlexRF: Handle auto transmit/receive and set the receive antenna.
+ @param u the usrp object
+ @param subdev_spec the sub-device specification
+ @param ddc which ddc to use: 0 or 1
+ @param gain the gain to set
+ @param frequency the frequency to tune
+ @param auto_tr auto transmit/receive True, False, or None
+ @param rx_ant the receive antenna: 'TX/RX', 'RX2', or None
+ @return the subdevice
+ """
+ subdev = usrp.selected_subdev(u, subdev_spec)#get the subdev
+ subdev.set_gain(gain)
+ _set_frequency(u, ddc, subdev, frequency, verbose=True)
+ if auto_tr is not None: subdev.set_auto_tr(auto_tr)
+ if rx_ant is not None: subdev.select_rx_antenna(rx_ant)
+ return subdev
+
+def _setup_tx_subdev(u, subdev_spec, gain, frequency, auto_tr=None, tx_enb=None):
+ """!
+ Setup a usrp receive subdevice by setting gain and frequency.
+ Add the gain and frequency callbacks to the flow graph.
+ FlexRF: Handle auto transmit/receive and enable the transmitter.
+ @param u the usrp object
+ @param subdev_spec the sub-device specification
+ @param gain the gain to set
+ @param frequency the frequency to tune
+ @param auto_tr auto transmit/receive True, False, or None
+ @param tx_enb the transmit enable: True, False, or None
+ @return the subdevice
+ """
+ subdev = usrp.selected_subdev(u, subdev_spec)#get the subdev
+ subdev.set_gain(gain)
+ _set_frequency(u, subdev._which, subdev, frequency, verbose=True)
+ if auto_tr is not None: subdev.set_auto_tr(auto_tr)
+ if tx_enb is not None: subdev.set_enable(tx_enb)
+ return subdev
+
+##map the usrp contructors to IO sizes
+constructor_to_size = {
+ usrp.source_c: gr.sizeof_gr_complex,
+ usrp.sink_c: gr.sizeof_gr_complex,
+ usrp.source_s: gr.sizeof_short,
+ usrp.sink_s: gr.sizeof_short,
+}
+
+####################################################################
+####################################################################
+# Simple USRP Base Classes
+####################################################################
+####################################################################
+
+class _simple_usrp(object):
+ """A single usrp source/sink base class."""
+
+ def __init__(self, u, subdev, which):
+ """!
+ Create a simple usrp base class.
+ @param u the usrp object
+ @param subdev the subdevice object
+ @param which specifies the DDC/DUC number when tuning
+ """
+ self._u = u
+ self._subdev = subdev
+ self._which = which
+
+ def get_u(self):
+ """!
+ Get the underlying usrp object.
+ @return the usrp source/sink object.
+ """
+ return self._u
+
+ def get_subdev(self):
+ """!
+ Get the underlying subdevice.
+ @return the subdev object.
+ """
+ return self._subdev
+
+ def set_frequency(self, frequency):
+ """!
+ Set the frequency of the subdevice.
+ @param frequency the frequency to tune
+ """
+ _set_frequency(self.get_u(), self._which, self.get_subdev(), frequency)
+
+ def set_gain(self, gain):
+ """!
+ Set the gain of the subdevice.
+ @param gain the gain to set
+ """
+ self.get_subdev().set_gain(gain)
+
+####################################################################
+# Simple USRP Source
+####################################################################
+class _simple_source(gr.hier_block2, _simple_usrp):
+ """A single usrp source of IO type short or complex."""
+
+ def __init__(self, number, subdev_spec, frequency, decimation, gain, mux=None, auto_tr=None, rx_ant=None):
+ """!
+ USRP simple source contructor.
+ @param number the unit number
+ @param subdev_spec the sub-device specification tuple
+ @param frequency the frequency to tune
+ @param decimation the device decimation
+ @param gain the gain to set
+ @param mux the mux in hex or None
+ @param auto_tr auto transmit/receive True, False, or None
+ @param rx_ant the receive antenna: 'TX/RX', 'RX2', or None
+ """
+ #initialize hier2 block
+ gr.hier_block2.__init__(
+ self, 'usrp_simple_source',
+ gr.io_signature(0, 0, 0),
+ gr.io_signature(1, 1, constructor_to_size[self.constructor]),
+ )
+ #create usrp object
+ u = self.constructor(number, nchan=1)
+ if subdev_spec is None: subdev_spec = usrp.pick_rx_subdevice(u)
+ u.set_decim_rate(decimation)
+ if mux is None: mux = usrp.determine_rx_mux_value(u, subdev_spec)
+ u.set_mux(mux)
+ subdev = _setup_rx_subdev(u, subdev_spec, 0, gain, frequency, auto_tr, rx_ant)
+ _simple_usrp.__init__(self, u, subdev, 0)
+ #connect
+ self.connect(u, self)
+
+ def set_decim_rate(self, decim): self.get_u().set_decim_rate(int(decim))
+
+class simple_source_c(_simple_source): constructor = usrp.source_c
+class simple_source_s(_simple_source): constructor = usrp.source_s
+
+####################################################################
+# Simple USRP Sink
+####################################################################
+class _simple_sink(gr.hier_block2, _simple_usrp):
+ """A single usrp sink of IO type short or complex."""
+
+ def __init__(self, number, subdev_spec, frequency, interpolation, gain, mux=None, auto_tr=None, tx_enb=None):
+ """!
+ USRP simple sink contructor.
+ @param number the unit number
+ @param subdev_spec the sub-device specification tuple
+ @param frequency the frequency to tune
+ @param interpolation the device interpolation
+ @param gain the gain to set
+ @param mux the mux in hex or None
+ @param auto_tr auto transmit/receive True, False, or None
+ @param tx_enb the transmit enable: True, False, or None
+ """
+ #initialize hier2 block
+ gr.hier_block2.__init__(
+ self, 'usrp_simple_sink',
+ gr.io_signature(1, 1, constructor_to_size[self.constructor]),
+ gr.io_signature(0, 0, 0),
+ )
+ #create usrp object
+ u = self.constructor(number, nchan=1)
+ if subdev_spec is None: subdev_spec = usrp.pick_tx_subdevice(u)
+ u.set_interp_rate(interpolation)
+ if mux is None: mux = usrp.determine_tx_mux_value(u, subdev_spec)
+ u.set_mux(mux)
+ subdev = _setup_tx_subdev(u, subdev_spec, gain, frequency, auto_tr, tx_enb)
+ _simple_usrp.__init__(self, u, subdev, subdev._which)
+ #connect
+ self.connect(self, u)
+
+ def set_interp_rate(self, interp): self.get_u().set_interp_rate(int(interp))
+
+class simple_sink_c(_simple_sink): constructor = usrp.sink_c
+class simple_sink_s(_simple_sink): constructor = usrp.sink_s
+
+####################################################################
+####################################################################
+# Dual USRP Base Classes
+####################################################################
+####################################################################
+
+class _dual_usrp(object):
+ """A dual usrp source/sink base class."""
+
+ def __init__(self, u, subdev_a, subdev_b, which_a, which_b):
+ """!
+ Create a dual usrp base class.
+ @param u the usrp object
+ @param subdev_a the subdevice object side a
+ @param subdev_b the subdevice object side b
+ @param which_a specifies the DDC/DUC number when tuning side a
+ @param which_b specifies the DDC/DUC number when tuning side b
+ """
+ self._u = u
+ self._subdev_a = subdev_a
+ self._subdev_b = subdev_b
+ self._which_a = which_a
+ self._which_b = which_b
+
+ def get_u(self):
+ """!
+ Get the underlying usrp object.
+ @return the usrp source/sink object.
+ """
+ return self._u
+
+ def get_subdev_a(self):
+ """!
+ Get the underlying subdevice.
+ @return the subdev object.
+ """
+ return self._subdev_a
+
+ def get_subdev_b(self):
+ """!
+ Get the underlying subdevice.
+ @return the subdev object.
+ """
+ return self._subdev_b
+
+ def set_frequency_a(self, frequency):
+ """!
+ Set the frequency of the subdevice.
+ @param frequency the frequency to tune
+ """
+ _set_frequency(self.get_u(), self._which_a, self.get_subdev_a(), frequency)
+
+ def set_frequency_b(self, frequency):
+ """!
+ Set the frequency of the subdevice.
+ @param frequency the frequency to tune
+ """
+ _set_frequency(self.get_u(), self._which_b, self.get_subdev_b(), frequency)
+
+ def set_gain_a(self, gain):
+ """!
+ Set the gain of the subdevice.
+ @param gain the gain to set
+ """
+ self.get_subdev_a().set_gain(gain)
+
+ def set_gain_b(self, gain):
+ """!
+ Set the gain of the subdevice.
+ @param gain the gain to set
+ """
+ self.get_subdev_b().set_gain(gain)
+
+####################################################################
+# Dual USRP Source
+####################################################################
+class _dual_source(gr.hier_block2, _dual_usrp):
+ """A dual usrp source of IO type short or complex."""
+
+ def __init__(self, number, frequency_a, frequency_b, decimation, gain_a, gain_b, mux=0x3210, auto_tr=None, rx_ant_a=None, rx_ant_b=None):
+ """!
+ USRP dual source contructor.
+ @param number the unit number
+ @param frequency_a the frequency to tune side a
+ @param frequency_b the frequency to tune side b
+ @param decimation the device decimation
+ @param gain_a the gain to set side a
+ @param gain_b the gain to set side b
+ @param mux the mux in hex
+ @param auto_tr auto transmit/receive True, False, or None
+ @param rx_ant_a the receive antenna side a: 'TX/RX', 'RX2', or None
+ @param rx_ant_b the receive antenna side b: 'TX/RX', 'RX2', or None
+ """
+ #initialize hier2 block
+ gr.hier_block2.__init__(
+ self, 'usrp_dual_source',
+ gr.io_signature(0, 0, 0),
+ gr.io_signature(2, 2, constructor_to_size[self.constructor]),
+ )
+ #create usrp object
+ u = self.constructor(number, nchan=2)
+ u.set_decim_rate(decimation)
+ u.set_mux(mux)
+ subdev_a = _setup_rx_subdev(u, (0, 0), 0, gain_a, frequency_a, auto_tr, rx_ant_a)
+ subdev_b = _setup_rx_subdev(u, (1, 0), 1, gain_b, frequency_b, auto_tr, rx_ant_b)
+ _dual_usrp.__init__(self, u, subdev_a, subdev_b, 0, 1)
+ #connect
+ deinter = gr.deinterleave(constructor_to_size[self.constructor])
+ self.connect(u, deinter)
+ for i in range(2): self.connect((deinter, i), (self, i))
+
+ def set_decim_rate(self, decim): self.get_u().set_decim_rate(int(decim))
+
+class dual_source_c(_dual_source): constructor = usrp.source_c
+class dual_source_s(_dual_source): constructor = usrp.source_s
+
+####################################################################
+# Dual USRP Sink
+####################################################################
+class _dual_sink(gr.hier_block2, _dual_usrp):
+ """A dual usrp sink of IO type short or complex."""
+
+ def __init__(self, number, frequency_a, frequency_b, interpolation, gain_a, gain_b, mux=0xba98, auto_tr=None, tx_enb_a=None, tx_enb_b=None):
+ """!
+ USRP dual sink contructor.
+ @param number the unit number
+ @param subdev_spec the sub-device specification tuple
+ @param frequency the frequency to tune
+ @param interpolation the device interpolation
+ @param gain the gain to set
+ @param mux the mux in hex or None
+ @param auto_tr auto transmit/receive True, False, or None
+ @param tx_enb the transmit enable: True, False, or None
+ """
+ #initialize hier2 block
+ gr.hier_block2.__init__(
+ self, 'usrp_dual_sink',
+ gr.io_signature(2, 2, constructor_to_size[self.constructor]),
+ gr.io_signature(0, 0, 0),
+ )
+ #create usrp object
+ u = self.constructor(number, nchan=2)
+ u.set_interp_rate(interpolation)
+ u.set_mux(mux)
+ subdev_a = _setup_tx_subdev(u, (0, 0), gain_a, frequency_a, auto_tr, tx_enb_a)
+ subdev_b = _setup_tx_subdev(u, (1, 0), gain_b, frequency_b, auto_tr, tx_enb_b)
+ _dual_usrp.__init__(self, u, subdev_a, subdev_b, subdev_a._which, subdev_b._which)
+ #connect
+ inter = gr.interleave(constructor_to_size[self.constructor])
+ self.connect(inter, u)
+ for i in range(2): self.connect((self, i), (inter, i))
+
+ def set_interp_rate(self, interp): self.get_u().set_interp_rate(int(interp))
+
+class dual_sink_c(_dual_sink): constructor = usrp.sink_c
+class dual_sink_s(_dual_sink): constructor = usrp.sink_s
+
diff --git a/grc/src/grc_gnuradio/utils/Makefile.am b/grc/src/grc_gnuradio/utils/Makefile.am
new file mode 100644
index 000000000..01020139e
--- /dev/null
+++ b/grc/src/grc_gnuradio/utils/Makefile.am
@@ -0,0 +1,30 @@
+#
+# Copyright 2008 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 $(top_srcdir)/Makefile.common
+
+ourpythondir = $(pythondir)/grc_gnuradio/utils
+
+ourpython_PYTHON = \
+ __init__.py \
+ convert_hier.py \
+ expr_utils.py \
+ extract_docs.py
diff --git a/grc/src/grc_gnuradio/utils/__init__.py b/grc/src/grc_gnuradio/utils/__init__.py
new file mode 100644
index 000000000..b6402601c
--- /dev/null
+++ b/grc/src/grc_gnuradio/utils/__init__.py
@@ -0,0 +1,22 @@
+"""
+Copyright 2008 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
+"""
+##@package grc_gnuradio.utils
+#utility functions and classes
+#@author Josh Blum
+
diff --git a/grc/src/grc_gnuradio/utils/convert_hier.py b/grc/src/grc_gnuradio/utils/convert_hier.py
new file mode 100644
index 000000000..a0f3c2a85
--- /dev/null
+++ b/grc/src/grc_gnuradio/utils/convert_hier.py
@@ -0,0 +1,81 @@
+"""
+Copyright 2008 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
+"""
+##@package grc_gnuradio.utils.convert_hier
+#Utility functions to convert a grc hier block to an xml wrapper
+#@author Josh Blum
+
+from grc_gnuradio.Constants import BLOCK_DTD
+from grc import ParseXML
+from grc.Utils import odict
+
+def convert_hier(flow_graph, python_file):
+ #extract info from the flow graph
+ input_sig = flow_graph.get_input_signature()
+ output_sig = flow_graph.get_output_signature()
+ parameters = flow_graph.get_parameters()
+ block_key = flow_graph.get_option('id')
+ block_name = flow_graph.get_option('title')
+ block_category = flow_graph.get_option('category')
+ block_desc = flow_graph.get_option('description')
+ block_author = flow_graph.get_option('author')
+ #build the nested data
+ block_n = odict()
+ block_n['name'] = block_name
+ block_n['key'] = block_key
+ block_n['category'] = block_category
+ block_n['import'] = 'execfile("%s")'%python_file
+ #make data
+ block_n['make'] = '%s(\n\t%s,\n)'%(
+ block_key,
+ ',\n\t'.join(['%s=$%s'%(param.get_id(), param.get_id()) for param in parameters]),
+ )
+ #callback data
+ block_n['callback'] = ['set_%s($%s)'%(param.get_id(), param.get_id()) for param in parameters]
+ #param data
+ params_n = list()
+ for param in parameters:
+ param_n = odict()
+ param_n['name'] = param.get_param('label').get_value() or param.get_id()
+ param_n['key'] = param.get_id()
+ param_n['value'] = param.get_param('value').get_value()
+ param_n['type'] = 'raw'
+ params_n.append(param_n)
+ block_n['param'] = params_n
+ #sink data
+ if int(input_sig['nports']):
+ sink_n = odict()
+ sink_n['name'] = 'in'
+ sink_n['type'] = input_sig['type']
+ sink_n['vlen'] = input_sig['vlen']
+ sink_n['nports'] = input_sig['nports']
+ block_n['sink'] = sink_n
+ #source data
+ if int(output_sig['nports']):
+ source_n = odict()
+ source_n['name'] = 'out'
+ source_n['type'] = output_sig['type']
+ source_n['vlen'] = output_sig['vlen']
+ source_n['nports'] = output_sig['nports']
+ block_n['source'] = source_n
+ #doc data
+ block_n['doc'] = "%s\n%s\n%s"%(block_author, block_desc, python_file)
+ #write the block_n to file
+ xml_file = python_file + '.xml'
+ ParseXML.to_file({'block': block_n}, xml_file)
+ ParseXML.validate_dtd(xml_file, BLOCK_DTD)
diff --git a/grc/src/grc_gnuradio/utils/expr_utils.py b/grc/src/grc_gnuradio/utils/expr_utils.py
new file mode 100644
index 000000000..8253f018a
--- /dev/null
+++ b/grc/src/grc_gnuradio/utils/expr_utils.py
@@ -0,0 +1,140 @@
+"""
+Copyright 2008 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
+"""
+##@package grc_gnuradio.utils.expr_utils
+#Utility functions to comprehend variable expressions.
+#@author Josh Blum
+
+import string
+VAR_CHARS = string.letters + string.digits + '_'
+
+class graph(object):
+ """!
+ Simple graph structure held in a dictionary.
+ """
+
+ def __init__(self): self._graph = dict()
+
+ def __str__(self): return str(self._graph)
+
+ def add_node(self, node_key):
+ if self._graph.has_key(node_key): return
+ self._graph[node_key] = set()
+
+ def remove_node(self, node_key):
+ if not self._graph.has_key(node_key): return
+ for edges in self._graph.values():
+ if node_key in edges: edges.remove(node_key)
+ self._graph.pop(node_key)
+
+ def add_edge(self, src_node_key, dest_node_key):
+ self._graph[src_node_key].add(dest_node_key)
+
+ 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_edges(self, node_key): return self._graph[node_key]
+
+def expr_split(expr):
+ """!
+ Split up an expression by non alphanumeric characters, including underscore.
+ Leave strings in-tact.
+ #TODO ignore escaped quotes, use raw strings.
+ @param expr an expression string
+ @return a list of string tokens that form expr
+ """
+ toks = list()
+ tok = ''
+ quote = ''
+ for char in expr:
+ if quote or char in VAR_CHARS:
+ if char == quote: quote = ''
+ tok += char
+ elif char in ("'", '"'):
+ toks.append(tok)
+ tok = char
+ quote = char
+ else:
+ toks.append(tok)
+ toks.append(char)
+ tok = ''
+ toks.append(tok)
+ return filter(lambda t: t, toks)
+
+def expr_prepend(expr, vars, prepend):
+ """!
+ Search for vars in the expression and add the prepend.
+ @param expr an expression string
+ @param vars a list of variable names
+ @param prepend the prepend string
+ @return a new expression with the prepend
+ """
+ expr_splits = expr_split(expr)
+ for i, es in enumerate(expr_splits):
+ if es in vars: expr_splits[i] = prepend + es
+ return ''.join(expr_splits)
+
+def get_variable_dependencies(expr, vars):
+ """!
+ Return a set of variables used in this expression.
+ @param expr an expression string
+ @param vars a list of variable names
+ @return a subset of vars used in the expression
+ """
+ expr_toks = expr_split(expr)
+ return set(filter(lambda v: v in expr_toks, vars))
+
+def get_graph(exprs):
+ """!
+ Get a graph representing the variable dependencies
+ @param exprs a mapping of variable name to expression
+ @return a graph of variable deps
+ """
+ vars = exprs.keys()
+ #get dependencies for each expression, load into graph
+ var_graph = graph()
+ for var in vars: var_graph.add_node(var)
+ for var, expr in exprs.iteritems():
+ for dep in get_variable_dependencies(expr, vars):
+ var_graph.add_edge(dep, var)
+ return var_graph
+
+def sort_variables(exprs):
+ """!
+ Get a list of variables in order of dependencies.
+ @param exprs a mapping of variable name to expression
+ @return a list of variable names
+ @throws AssertionError circular dependencies
+ """
+ var_graph = get_graph(exprs)
+ sorted_vars = list()
+ #determine dependency order
+ while var_graph.get_nodes():
+ #get a list of nodes with no edges
+ indep_vars = filter(lambda var: not var_graph.get_edges(var), var_graph.get_nodes())
+ assert indep_vars
+ #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)
+ return reversed(sorted_vars)
+
+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/src/grc_gnuradio/utils/extract_docs.py b/grc/src/grc_gnuradio/utils/extract_docs.py
new file mode 100644
index 000000000..bc0dcd446
--- /dev/null
+++ b/grc/src/grc_gnuradio/utils/extract_docs.py
@@ -0,0 +1,109 @@
+"""
+Copyright 2008 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
+"""
+##@package grc_gnuradio.utils.extract_docs
+#Extract documentation from the gnuradio doxygen files.
+#@author Josh Blum
+
+from grc_gnuradio.Constants import *
+from lxml import etree
+import os
+
+DOXYGEN_NAME_XPATH = '/doxygen/compounddef/compoundname'
+DOXYGEN_BRIEFDESC_GR_XPATH = '/doxygen/compounddef/briefdescription'
+DOXYGEN_DETAILDESC_GR_XPATH = '/doxygen/compounddef/detaileddescription'
+DOXYGEN_BRIEFDESC_BLKS2_XPATH = '/doxygen/compounddef/sectiondef[@kind="public-func"]/memberdef/briefdescription'
+DOXYGEN_DETAILDESC_BLKS2_XPATH = '/doxygen/compounddef/sectiondef[@kind="public-func"]/memberdef/detaileddescription'
+
+def extract_txt(xml):
+ """!
+ Recursivly pull the text out of an xml tree.
+ @param xml the xml tree
+ @return a string
+ """
+ text = xml.text or ''
+ if not len(xml): return text
+ return ''.join([text] + map(extract_txt, xml))
+
+def is_match(key, file):
+ """!
+ Is the block key a match for the given file name?
+ @param key block key
+ @param file the xml file name
+ @return true if matches
+ """
+ if not file.endswith('.xml'): return False
+ file = file.replace('.xml', '') #remove file ext
+ file = file.replace('__', '_') #doxygen xml files have 2 underscores
+ if key.startswith('gr_'):
+ if not file.startswith('classgr_'): return False
+ key = key.replace('gr_', 'classgr_')
+ elif key.startswith('trellis_'):
+ if not file.startswith('classtrellis_'): return False
+ key = key.replace('trellis_', 'classtrellis_')
+ elif key.startswith('blks2_'):
+ if not file.startswith('classgnuradio_'): return False
+ if 'blks2' not in file: return False
+ file = file.replace('_1_1', '_') #weird blks2 doxygen syntax
+ key = key.replace('blks2_', '')
+ else: return False
+ for k, f in zip(*map(reversed, map(lambda x: x.split('_'), [key, file]))):
+ if k == f: continue
+ ks = k.split('x')
+ if len(ks) == 2 and f.startswith(ks[0]) and f.endswith(ks[1]): continue
+ if len(ks) > 2 and all(ki in ('x', fi) for ki, fi in zip(k, f)): continue
+ return False
+ return True
+
+def extract(key):
+ """!
+ Extract the documentation from the doxygen generated xml files.
+ If multiple files match, combine the docs.
+ @param key the block key
+ @return a string with documentation
+ """
+ #get potential xml file matches for the key
+ if os.path.exists(DOCS_DIR) and os.path.isdir(DOCS_DIR):
+ matches = filter(lambda f: is_match(key, f), os.listdir(DOCS_DIR))
+ else: matches = list()
+ #combine all matches
+ doc_strs = list()
+ for match in matches:
+ try:
+ xml_file = DOCS_DIR + '/' + match
+ xml = etree.parse(xml_file)
+ #extract descriptions
+ comp_name = extract_txt(xml.xpath(DOXYGEN_NAME_XPATH)[0]).strip('\n')
+ comp_name = ' --- ' + comp_name + ' --- '
+ if key.startswith('gr_') or key.startswith('trellis_'):
+ brief_desc = extract_txt(xml.xpath(DOXYGEN_BRIEFDESC_GR_XPATH)[0]).strip('\n')
+ detailed_desc = extract_txt(xml.xpath(DOXYGEN_DETAILDESC_GR_XPATH)[0]).strip('\n')
+ elif key.startswith('blks2_'):
+ brief_desc = extract_txt(xml.xpath(DOXYGEN_BRIEFDESC_BLKS2_XPATH)[0]).strip('\n')
+ detailed_desc = extract_txt(xml.xpath(DOXYGEN_DETAILDESC_BLKS2_XPATH)[0]).strip('\n')
+ else:
+ brief_desc = ''
+ detailed_desc = ''
+ #combine
+ doc_strs.append('\n'.join([comp_name, brief_desc, detailed_desc]).strip('\n'))
+ except IndexError: pass #bad format
+ return '\n\n'.join(doc_strs)
+
+if __name__ == '__main__':
+ import sys
+ print extract(sys.argv[1])
diff --git a/grc/src/grc_gnuradio/wxgui/Makefile.am b/grc/src/grc_gnuradio/wxgui/Makefile.am
new file mode 100644
index 000000000..2e7072eae
--- /dev/null
+++ b/grc/src/grc_gnuradio/wxgui/Makefile.am
@@ -0,0 +1,29 @@
+#
+# Copyright 2008 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 $(top_srcdir)/Makefile.common
+
+ourpythondir = $(pythondir)/grc_gnuradio/wxgui
+
+ourpython_PYTHON = \
+ __init__.py \
+ callback_controls.py \
+ top_block_gui.py
diff --git a/grc/src/grc_gnuradio/wxgui/__init__.py b/grc/src/grc_gnuradio/wxgui/__init__.py
new file mode 100644
index 000000000..c0fdca52c
--- /dev/null
+++ b/grc/src/grc_gnuradio/wxgui/__init__.py
@@ -0,0 +1,30 @@
+# Copyright 2008 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 callback_controls import \
+ button_control, \
+ drop_down_control, \
+ radio_buttons_horizontal_control, \
+ radio_buttons_vertical_control, \
+ slider_horizontal_control, \
+ slider_vertical_control, \
+ text_box_control
+from top_block_gui import top_block_gui
+
diff --git a/grc/src/grc_gnuradio/wxgui/callback_controls.py b/grc/src/grc_gnuradio/wxgui/callback_controls.py
new file mode 100644
index 000000000..c1ba784eb
--- /dev/null
+++ b/grc/src/grc_gnuradio/wxgui/callback_controls.py
@@ -0,0 +1,281 @@
+# Copyright 2008 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import wx
+import sys
+
+class LabelText(wx.StaticText):
+ """Label text class for uniform labels among all controls."""
+
+ def __init__(self, window, label):
+ wx.StaticText.__init__(self, window, -1, str(label))
+ font = self.GetFont()
+ font.SetWeight(wx.FONTWEIGHT_BOLD)
+ self.SetFont(font)
+
+class _control_base(wx.BoxSizer):
+ """Control base class"""
+
+ def __init__(self, window, callback):
+ self.window = window
+ self.callback = callback
+ wx.BoxSizer.__init__(self, wx.VERTICAL)
+
+ def get_window(self): return self.window
+
+ def call(self): return self.callback(self.get_value())
+
+ def get_value(self): raise NotImplementedError
+
+class _chooser_control_base(_control_base):
+ """House a drop down or radio buttons for variable control."""
+
+ def __init__(self, window, callback, label='Label', index=0, choices=[0], labels=[]):
+ """!
+ Chooser contructor.
+ Create the slider, text box, and label.
+ @param window the wx parent window
+ @param callback call the callback on changes
+ @param label the label title
+ @param index the default choice index
+ @param choices a list of choices
+ @param labels the choice labels or empty list
+ """
+ #initialize
+ _control_base.__init__(self, window, callback)
+ label_text = LabelText(self.get_window(), label)
+ self.Add(label_text, 0, wx.ALIGN_CENTER)
+ self.index = index
+ self.choices = choices
+ self.labels = map(str, labels or choices)
+ self._init()
+
+ def _handle_changed(self, event=None):
+ """!
+ A change is detected. Call the callback.
+ """
+ try: self.call()
+ except Exception, e: print >> sys.stderr, 'Error in exec callback from handle changed.\n', e
+
+ def get_value(self):
+ """!
+ Update the chooser.
+ @return one of the possible choices
+ """
+ self._update()
+ return self.choices[self.index]
+
+##############################################################################################
+# Button Control
+##############################################################################################
+class button_control(_chooser_control_base):
+ """House a button for variable control."""
+
+ def _init(self):
+ self.button = wx.Button(self.get_window(), -1, self.labels[self.index])
+ self.button.Bind(wx.EVT_BUTTON, self._handle_changed)
+ self.Add(self.button, 0, wx.ALIGN_CENTER)
+
+ def _update(self):
+ self.index = (self.index + 1)%len(self.choices) #circularly increment index
+ self.button.SetLabel(self.labels[self.index])
+
+##############################################################################################
+# Drop Down Control
+##############################################################################################
+class drop_down_control(_chooser_control_base):
+ """House a drop down for variable control."""
+
+ def _init(self):
+ self.drop_down = wx.Choice(self.get_window(), -1, choices=self.labels)
+ self.Add(self.drop_down, 0, wx.ALIGN_CENTER)
+ self.drop_down.Bind(wx.EVT_CHOICE, self._handle_changed)
+ self.drop_down.SetSelection(self.index)
+
+ def _update(self):
+ self.index = self.drop_down.GetSelection()
+
+##############################################################################################
+# Radio Buttons Control
+##############################################################################################
+class _radio_buttons_control_base(_chooser_control_base):
+ """House radio buttons for variable control."""
+
+ def _init(self):
+ #create box for radio buttons
+ radio_box = wx.BoxSizer(self.radio_box_orientation)
+ panel = wx.Panel(self.get_window(), -1)
+ panel.SetSizer(radio_box)
+ self.Add(panel, 0, wx.ALIGN_CENTER)
+ #create radio buttons
+ self.radio_buttons = list()
+ for label in self.labels:
+ radio_button = wx.RadioButton(panel, -1, label)
+ radio_button.SetValue(False)
+ self.radio_buttons.append(radio_button)
+ radio_box.Add(radio_button, 0, self.radio_button_align)
+ radio_button.Bind(wx.EVT_RADIOBUTTON, self._handle_changed)
+ #set one radio button active
+ self.radio_buttons[self.index].SetValue(True)
+
+ def _update(self):
+ selected_radio_button = filter(lambda rb: rb.GetValue(), self.radio_buttons)[0]
+ self.index = self.radio_buttons.index(selected_radio_button)
+
+class radio_buttons_horizontal_control(_radio_buttons_control_base):
+ radio_box_orientation = wx.HORIZONTAL
+ radio_button_align = wx.ALIGN_CENTER
+class radio_buttons_vertical_control(_radio_buttons_control_base):
+ radio_box_orientation = wx.VERTICAL
+ radio_button_align = wx.ALIGN_LEFT
+
+##############################################################################################
+# Slider Control
+##############################################################################################
+class _slider_control_base(_control_base):
+ """House a Slider and a Text Box for variable control."""
+
+ def __init__(self, window, callback, label='Label', value=50, min=0, max=100, num_steps=100):
+ """!
+ Slider contructor.
+ Create the slider, text box, and label.
+ @param window the wx parent window
+ @param callback call the callback on changes
+ @param label the label title
+ @param value the default value
+ @param min the min
+ @param max the max
+ @param num_steps the number of steps
+ """
+ #initialize
+ _control_base.__init__(self, window, callback)
+ self.min = float(min)
+ self.max = float(max)
+ self.num_steps = int(num_steps)
+ #create gui elements
+ label_text_sizer = wx.BoxSizer(self.label_text_orientation) #label and text box container
+ label_text = LabelText(self.get_window(), '%s: '%str(label))
+ self.text_box = text_box = wx.TextCtrl(self.get_window(), -1, str(value), style=wx.TE_PROCESS_ENTER)
+ text_box.Bind(wx.EVT_TEXT_ENTER, self._handle_enter) #bind this special enter hotkey event
+ for obj in (label_text, text_box): #fill the container with label and text entry box
+ label_text_sizer.Add(obj, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)
+ self.Add(label_text_sizer, 0, wx.ALIGN_CENTER)
+ #make the slider
+ self.slider = slider = wx.Slider(self.get_window(), -1, size=wx.Size(*self.slider_size), style=self.slider_style)
+ try: slider.SetRange(0, num_steps)
+ except Exception, e:
+ print >> sys.stderr, 'Error in set slider range: "%s".'%e
+ sys.exit(-1)
+ slider.Bind(wx.EVT_SCROLL, self._handle_scroll) #bind the scrolling event
+ self.Add(slider, 0, wx.ALIGN_CENTER)
+ #init slider and text box
+ self._value = value
+ self._set_slider_value(self._value) #sets the slider's value
+ self.text_box.SetValue(str(self._value))
+
+ def get_value(self):
+ """!
+ Get the current set value.
+ @return the value (float)
+ """
+ return self._value
+
+ def _set_slider_value(self, real_value):
+ """!
+ Translate the real numerical value into a slider value and,
+ write the value to the slider.
+ @param real_value the numeric value the slider should represent
+ """
+ slider_value = (float(real_value) - self.min)*self.num_steps/(self.max - self.min)
+ self.slider.SetValue(slider_value)
+
+ def _handle_scroll(self, event=None):
+ """!
+ A scroll event is detected. Read the slider, call the callback.
+ """
+ slider_value = self.slider.GetValue()
+ new_value = slider_value*(self.max - self.min)/self.num_steps + self.min
+ self.text_box.SetValue(str(new_value))
+ self._value = new_value
+ try: self.call()
+ except Exception, e: print >> sys.stderr, 'Error in exec callback from handle scroll.\n', e
+
+ def _handle_enter(self, event=None):
+ """!
+ An enter key was pressed. Read the text box, call the callback.
+ """
+ new_value = float(self.text_box.GetValue())
+ self._set_slider_value(new_value)
+ self._value = new_value
+ try: self.call()
+ except Exception, e: print >> sys.stderr, 'Error in exec callback from handle enter.\n', e
+
+class slider_horizontal_control(_slider_control_base):
+ label_text_orientation = wx.HORIZONTAL
+ slider_style = wx.SL_HORIZONTAL
+ slider_size = 200, 20
+class slider_vertical_control(_slider_control_base):
+ label_text_orientation = wx.VERTICAL
+ slider_style = wx.SL_VERTICAL
+ slider_size = 20, 200
+
+##############################################################################################
+# Text Box Control
+##############################################################################################
+class text_box_control(_control_base):
+ """House a Text Box for variable control."""
+
+ def __init__(self, window, callback, label='Label', value=50):
+ """!
+ Text box contructor.
+ Create the text box, and label.
+ @param window the wx parent window
+ @param callback call the callback on changes
+ @param label the label title
+ @param value the default value
+ """
+ #initialize
+ _control_base.__init__(self, window, callback)
+ #create gui elements
+ label_text_sizer = wx.BoxSizer(wx.HORIZONTAL) #label and text box container
+ label_text = LabelText(self.get_window(), '%s: '%str(label))
+ self.text_box = text_box = wx.TextCtrl(self.get_window(), -1, str(value), style=wx.TE_PROCESS_ENTER)
+ text_box.Bind(wx.EVT_TEXT_ENTER, self._handle_enter) #bind this special enter hotkey event
+ for obj in (label_text, text_box): #fill the container with label and text entry box
+ label_text_sizer.Add(obj, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)
+ self.Add(label_text_sizer, 0, wx.ALIGN_CENTER)
+ self.text_box.SetValue(str(value))
+
+ def get_value(self):
+ """!
+ Get the current set value.
+ @return the value (float)
+ """
+ return self._value
+
+ def _handle_enter(self, event=None):
+ """!
+ An enter key was pressed. Read the text box, call the callback.
+ If the text cannot be evaluated, do not try callback.
+ """
+ try: self._value = eval(self.text_box.GetValue())
+ except: return
+ try: self.call()
+ except Exception, e: print >> sys.stderr, 'Error in exec callback from handle enter.\n', e
diff --git a/grc/src/grc_gnuradio/wxgui/top_block_gui.py b/grc/src/grc_gnuradio/wxgui/top_block_gui.py
new file mode 100644
index 000000000..d56d20056
--- /dev/null
+++ b/grc/src/grc_gnuradio/wxgui/top_block_gui.py
@@ -0,0 +1,99 @@
+# Copyright 2008 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+import wx
+import sys, os
+from gnuradio import gr
+
+default_gui_size = (200, 100)
+
+class top_block_gui(gr.top_block):
+ """gr top block with wx gui app and grid sizer."""
+
+ def __init__(self, title='', size=default_gui_size, icon=None):
+ """!
+ Initialize the gr top block.
+ Create the wx gui elements.
+ @param title the main window title
+ @param size the main window size tuple in pixels
+ @param icon the file path to an icon or None
+ """
+ #initialize
+ gr.top_block.__init__(self)
+ self._size = size
+ #set the icon
+ if icon and os.path.isfile(icon): self._icon = icon
+ else: self._icon = None
+ #create gui elements
+ self._wx_app = wx.App()
+ self._wx_frame = wx.Frame(None , -1, title)
+ self._wx_grid = wx.GridBagSizer(5, 5)
+ self._wx_vbox = wx.BoxSizer(wx.VERTICAL)
+
+ def GetWin(self):
+ """!
+ Get the window for wx elements to fit within.
+ @return the wx frame
+ """
+ return self._wx_frame
+
+ def Add(self, win):
+ """!
+ Add a window to the wx vbox.
+ @param win the wx window
+ """
+ self._wx_vbox.Add(win, 0, wx.EXPAND)
+
+ def GridAdd(self, win, row, col, row_span=1, col_span=1):
+ """!
+ Add a window to the wx grid at the given position.
+ @param win the wx window
+ @param row the row specification (integer >= 0)
+ @param col the column specification (integer >= 0)
+ @param row_span the row span specification (integer >= 1)
+ @param col_span the column span specification (integer >= 1)
+ """
+ self._wx_grid.Add(win, wx.GBPosition(row, col), wx.GBSpan(row_span, col_span), wx.EXPAND)
+
+ def Run(self):
+ """!
+ Setup the wx gui elements.
+ Start the gr top block.
+ Block with the wx main loop.
+ """
+ #set wx app icon
+ if self._icon: self._wx_frame.SetIcon(wx.Icon(self._icon, wx.BITMAP_TYPE_ANY))
+ #set minimal window size
+ self._wx_frame.SetSizeHints(*self._size)
+ #create callback for quit
+ def _quit(event):
+ gr.top_block.stop(self)
+ self._wx_frame.Destroy()
+ #setup app
+ self._wx_vbox.Add(self._wx_grid, 0, wx.EXPAND)
+ self._wx_frame.Bind(wx.EVT_CLOSE, _quit)
+ self._wx_frame.SetSizerAndFit(self._wx_vbox)
+ self._wx_frame.Show()
+ self._wx_app.SetTopWindow(self._wx_frame)
+ #start flow graph
+ gr.top_block.start(self)
+ #blocking main loop
+ self._wx_app.MainLoop()
+