[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Commit-gnuradio] r5730 - in grc/trunk: . data license notes src src/Ele
From: |
jblum |
Subject: |
[Commit-gnuradio] r5730 - in grc/trunk: . data license notes src src/Elements src/Graphics src/SignalBlockDefs |
Date: |
Wed, 6 Jun 2007 23:57:26 -0600 (MDT) |
Author: jblum
Date: 2007-06-06 23:57:25 -0600 (Wed, 06 Jun 2007)
New Revision: 5730
Added:
grc/trunk/README.txt
grc/trunk/data/preferences.grc.dtd
grc/trunk/src/Editor.py
grc/trunk/src/ExecFlowGraph.py
grc/trunk/src/ExecFlowGraphGUI.py
Removed:
grc/trunk/license/README.txt
grc/trunk/notes/stock_id.lst
grc/trunk/src/FlowGraphApp.py
grc/trunk/src/Run.py
Modified:
grc/trunk/notes/clean.sh
grc/trunk/notes/notes.txt
grc/trunk/src/ActionHandler.py
grc/trunk/src/Actions.py
grc/trunk/src/Constants.py
grc/trunk/src/DataType.py
grc/trunk/src/Elements/Element.py
grc/trunk/src/Elements/GraphicalParam.py
grc/trunk/src/Elements/SignalBlock.py
grc/trunk/src/Elements/Socket.py
grc/trunk/src/Elements/__init__.py
grc/trunk/src/Graphics/Bars.py
grc/trunk/src/Graphics/Dialogs.py
grc/trunk/src/Graphics/FlowGraph.py
grc/trunk/src/Graphics/MainWindow.py
grc/trunk/src/Graphics/__init__.py
grc/trunk/src/MathExprParser.py
grc/trunk/src/Messages.py
grc/trunk/src/Preferences.py
grc/trunk/src/SignalBlockDefs/Conversions.py
grc/trunk/src/SignalBlockDefs/Filters.py
grc/trunk/src/SignalBlockDefs/GraphicalSinks.py
grc/trunk/src/SignalBlockDefs/Misc.py
grc/trunk/src/SignalBlockDefs/Modulators.py
grc/trunk/src/SignalBlockDefs/SignalBlockConstants.py
grc/trunk/src/SignalBlockDefs/SignalBlockTree.py
grc/trunk/src/SignalBlockDefs/Sinks.py
grc/trunk/src/SignalBlockDefs/__init__.py
grc/trunk/src/Variables.py
Log:
merged developer branch into trunk
Copied: grc/trunk/README.txt (from rev 5729, grc/branches/jblum_work/README.txt)
===================================================================
--- grc/trunk/README.txt (rev 0)
+++ grc/trunk/README.txt 2007-06-07 05:57:25 UTC (rev 5730)
@@ -0,0 +1,13 @@
+Hello!
+
+Thank you for downloading GNU Radio Companion.
+This program is free software.
+A GPL license is distributed with this program.
+This license covers all the source code/python files.
+You will also find a "creative common license" for the grc icon.
+
+Intructions for GRC are available at:
+http://gnuradio.org/trac/wiki/GNURadioCompanion
+
+If you have questions, problems, suggestions, or want to contribute,
+please email me at jblum at jhu dot edu
Copied: grc/trunk/data/preferences.grc.dtd (from rev 5729,
grc/branches/jblum_work/data/preferences.grc.dtd)
===================================================================
--- grc/trunk/data/preferences.grc.dtd (rev 0)
+++ grc/trunk/data/preferences.grc.dtd 2007-06-07 05:57:25 UTC (rev 5730)
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<!--
+Copyright 2007 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
+-->
+<!--
+ preferences.grc.dtd
+ Josh Blum
+ The document type definitions for grc preferences.
+-->
+<!DOCTYPE preferences [
+ <!-- The preferences contains:
+ identifying information: time stamp, host name, version,
+ and a list of categories, each with their own preferences.
-->
+ <!ELEMENT preferences (timestamp?, hostname?, version?, categories>
+ <!ELEMENT timestamp (#PCDATA)> <!-- The time stamp for the
modification date. (optional) -->
+ <!ELEMENT hostname (#PCDATA)> <!-- The hostname of the
computer. (optional) -->
+ <!ELEMENT version (#PCDATA)> <!-- The program and program
version. (optional) -->
+ <!ELEMENT categories (category*)> <!-- The list of
categories. (tag required) -->
+ <!ELEMENT category (title?, prefs)> <!-- A
category. (0 or more) -->
+ <!ELEMENT title (#PCDATA)> <!-- The title
of the category. (optional) -->
+ <!ELEMENT prefs (pref*)> <!-- The list
of preferences for this category. (tag required) -->
+ <!ELEMENT pref (#PCDATA)> <!-- A
preference. (0 or more) -->
+]>
Deleted: grc/trunk/license/README.txt
Modified: grc/trunk/notes/clean.sh
===================================================================
--- grc/trunk/notes/clean.sh 2007-06-07 05:22:11 UTC (rev 5729)
+++ grc/trunk/notes/clean.sh 2007-06-07 05:57:25 UTC (rev 5730)
@@ -1,8 +1,7 @@
#!/bin/bash
-cd ../src
+#clean up compliled python files, and temporary files from editing
+cd ..
pwd
rm -rf `find ./ -type f -name "*.pyc"`
-rm -rf `find ./ -type f -name "*py~"`
-chmod a-x `find ./ -type f -name "*"`
-chmod a+x Run.py FlowGraphApp.py
-
+rm -rf `find ./ -type f -name "*.py~"`
+rm -rf `find ./ -type f -name "*.bak"`
Modified: grc/trunk/notes/notes.txt
===================================================================
--- grc/trunk/notes/notes.txt 2007-06-07 05:22:11 UTC (rev 5729)
+++ grc/trunk/notes/notes.txt 2007-06-07 05:57:25 UTC (rev 5730)
@@ -3,21 +3,25 @@
-usrp dual and quad souce
-blks blocks, add filesave for message reports
-combine add/mult with add/mult vector
+-selector blocks
############ Known Problems: ####################
-in vars window, stop_editing doesnt work
-file save, ask for overwrite even when appending file extension
-blocks need to fix themselves when they go out of bounds, like in a resize
-socket controllers should be intelligent on shrinkage
+-the nested data variables should indicate that they are nested data ->
variable_data
+-rearanging variables doesnt have a handler
+-changing an enum doesnt call update for params with variable datatypes
############ Features to Add: ####################
-save settings after close (working directory)
-create sub-flow graphs to be used in larger flow graphs
--math expressions, pi, e, sin, cos, tan, log, ln
-stdin/out communication protocal (non graphical)
--make grc.xml parsers to match the dtd
-include dtd in saved flow graphs
--create dtd for preferences
+-immediate display of tool tips in entry boxes
+-function to import taps from a file
+-fm demod example with expansion of wfm_recv block
############ wxPython Features: ####################
-dump wx running graph to png?
@@ -28,16 +32,17 @@
self.Bind(wx.EVT_CHOICE, self.scale, self.scaling_chooser)
selection = event.GetSelection()
-############ User Opts: ####################
---
+############ User Prefs: ####################
+--save scrollbar positions (hidden pref)
############ Uninteresting Features: ####################
-add the concept of a current working directory
-auto param to usrp diagnose dialog
--add a trim option for dangling sockets
-use popen3 once it works in cygwin
-arrow like images for socket, to replace in/out strings
-min/max in DataTypes can be other data types
-press enter to close params
+############ Get GTK Stock IDs: ####################
+ gtk.stock_list_ids()
\ No newline at end of file
Deleted: grc/trunk/notes/stock_id.lst
Modified: grc/trunk/src/ActionHandler.py
===================================================================
--- grc/trunk/src/ActionHandler.py 2007-06-07 05:22:11 UTC (rev 5729)
+++ grc/trunk/src/ActionHandler.py 2007-06-07 05:57:25 UTC (rev 5730)
@@ -102,13 +102,13 @@
self.handle_states(SOCKET_CONTROLLER_INC)
elif self.flow_graph.get_focus_flag() and keyname in ('minus',
'KP_Subtract'): #mouse focus
self.handle_states(SOCKET_CONTROLLER_DEC)
- #################### Run/Stop/Print
###############################
+ #################### Exec/Stop/Print
###############################
elif keyname == 'F5':
- self.handle_states(FLOW_GRAPH_RUN)
+ self.handle_states(FLOW_GRAPH_EXEC)
elif keyname == 'F7':
self.handle_states(FLOW_GRAPH_STOP)
elif keyname == 'Print':
- self.handle_states(FLOW_GRAPH_SNAP_SHOT)
+ self.handle_states(FLOW_GRAPH_SCREEN_CAPTURE)
#propagate this if the fg is not in focus or nothing is selected
return self.flow_graph.get_focus_flag() and
self.flow_graph.is_selected()
@@ -140,8 +140,8 @@
# enable a select few actions
Graphics.enable_usrp_diagnostics() #try to enable
usrp diagnostics
for action in (APPLICATION_QUIT, FLOW_GRAPH_NEW,
FLOW_GRAPH_OPEN, FLOW_GRAPH_SAVE_AS,
-
ABOUT_WINDOW_DISPLAY, COLORS_WINDOW_DISPLAY, HOTKEYS_WINDOW_DISPLAY,
-
FLOW_GRAPH_WINDOW_RESIZE, PREFS_WINDOW_DISPLAY, FLOW_GRAPH_SNAP_SHOT):
+
ABOUT_WINDOW_DISPLAY, COLORS_WINDOW_DISPLAY, HOTKEYS_WINDOW_DISPLAY,
MATH_EXPR_WINDOW_DISPLAY,
+
FLOW_GRAPH_WINDOW_RESIZE, PREFS_WINDOW_DISPLAY, FLOW_GRAPH_SCREEN_CAPTURE):
get_action_from_name(action).set_sensitive(True)
if self.flow_graph_file_path == '':
initial_state =
ParseXML.from_xml(ParseXML.from_file(INITIAL_FLOW_GRAPH_FILE))
@@ -152,8 +152,8 @@
self.handle_states(NOTHING_SELECT)
Messages.send_start_load(self.flow_graph_file_path)
initial_state =
ParseXML.from_xml(ParseXML.from_file(self.flow_graph_file_path))
- errors =
self.flow_graph.from_nested_data(initial_state)
- Messages.send_end_load(errors)
+
self.flow_graph.from_nested_data(initial_state)
+ Messages.send_end_load()
self.state_cache =
StateCache(initial_state)
except Exception, e: #if the data is bad,
display error and call INIT with None for the file path
Messages.send_fail_load(e)
@@ -233,6 +233,8 @@
Graphics.DataTypeColorsDialog()
elif state == HOTKEYS_WINDOW_DISPLAY:
Graphics.HotKeysDialog()
+ elif state == MATH_EXPR_WINDOW_DISPLAY:
+ Graphics.MathExprDialog()
elif state == FLOW_GRAPH_WINDOW_RESIZE:
dimensions =
Graphics.FlowGraphWindowSizeDialog(self.flow_graph).run()
if dimensions != None:
@@ -294,7 +296,7 @@
if file_path != None:
self.flow_graph_file_path = file_path
self.handle_states(FLOW_GRAPH_SAVE)
- elif state == FLOW_GRAPH_SNAP_SHOT:
+ elif state == FLOW_GRAPH_SCREEN_CAPTURE:
fc = Graphics.FlowGraphFileDialog('save image',
self.flow_graph_file_path)
file_path = fc.run()
if file_path != None:
@@ -306,7 +308,7 @@
##############################################################################################
# Run/Stop
##############################################################################################
- elif state == FLOW_GRAPH_RUN:
+ elif state == FLOW_GRAPH_EXEC:
if self.pid_file == None:
self.handle_states(FLOW_GRAPH_SAVE)
if self.flow_graph_file_path != '': RUN(self)
@@ -317,9 +319,9 @@
except: print "could not kill pid file:
%s"%self.pid_file
MUTEX.unlock()
else: print "!!! State not handled !!!"
- # set the running button if the flow graph is valid and is not
already running #
+ # set the exec button if the flow graph is valid and is not
already running #
while not MUTEX.testandset(): pass #try to lock repeatedly
until lock is aquired
-
get_action_from_name(FLOW_GRAPH_RUN).set_sensitive(self.flow_graph.is_valid()
and self.pid_file == None)
+
get_action_from_name(FLOW_GRAPH_EXEC).set_sensitive(self.flow_graph.is_valid()
and self.pid_file == None)
MUTEX.unlock()
# hide/show the reports window based on the preferences #
self.main_window.show_reports_window(Preferences.show_reports_window())
@@ -351,7 +353,7 @@
self.pid_file = self.action_handler.pid_file =
'/tmp/grc-%d.pid'%os.getpid()
self.report_file = '/tmp/grc-%d.report'%os.getpid()
self.flow_graph_file_path =
self.action_handler.flow_graph_file_path
- get_action_from_name(FLOW_GRAPH_RUN).set_sensitive(False)
+ get_action_from_name(FLOW_GRAPH_EXEC).set_sensitive(False)
get_action_from_name(FLOW_GRAPH_STOP).set_sensitive(True)
Messages.send_start_run(self.flow_graph_file_path)
self.start()
@@ -371,5 +373,5 @@
while not MUTEX.testandset(): pass #try to lock repeatedly
until lock is aquired
self.action_handler.pid_file = None
get_action_from_name(FLOW_GRAPH_STOP).set_sensitive(False)
- get_action_from_name(FLOW_GRAPH_RUN).set_sensitive(True)
+ get_action_from_name(FLOW_GRAPH_EXEC).set_sensitive(True)
MUTEX.unlock()
Modified: grc/trunk/src/Actions.py
===================================================================
--- grc/trunk/src/Actions.py 2007-06-07 05:22:11 UTC (rev 5729)
+++ grc/trunk/src/Actions.py 2007-06-07 05:57:25 UTC (rev 5730)
@@ -64,15 +64,16 @@
FLOW_GRAPH_SAVE_AS = 'flow graph save as'
FLOW_GRAPH_NEW = 'flow graph new'
FLOW_GRAPH_WINDOW_RESIZE = 'flow graph window resize'
-FLOW_GRAPH_RUN = 'flow graph run'
+FLOW_GRAPH_EXEC = 'flow graph exec'
FLOW_GRAPH_STOP = 'flow graph stop'
-FLOW_GRAPH_SNAP_SHOT = 'flow graph snap shot'
+FLOW_GRAPH_SCREEN_CAPTURE = 'flow graph screen capture'
ABOUT_WINDOW_DISPLAY = 'about window display'
COLORS_WINDOW_DISPLAY = 'colors window display'
HOTKEYS_WINDOW_DISPLAY = 'hotkeys window display'
USRP_DIAGNOSTICS_DISPLAY = 'usrp diagnostics display'
PREFS_WINDOW_DISPLAY = 'prefs window display'
+MATH_EXPR_WINDOW_DISPLAY = 'expression window display'
######################################################################################################
# actions
@@ -96,9 +97,10 @@
gtk.Action(ABOUT_WINDOW_DISPLAY, '_About', 'About this program',
'gtk-about'),
gtk.Action(COLORS_WINDOW_DISPLAY, '_Colors', 'Data Type Colors',
'gtk-select-color'),
gtk.Action(HOTKEYS_WINDOW_DISPLAY, '_HotKeys', 'Hot Keys', 'gtk-info'),
- gtk.Action(FLOW_GRAPH_RUN, '_Run', 'Run the flow graph', 'gtk-execute'),
+ gtk.Action(MATH_EXPR_WINDOW_DISPLAY, '_Math Expressions', 'Mathematical
Expressions', 'gtk-convert'),
+ gtk.Action(FLOW_GRAPH_EXEC, '_Execute', 'Execute the flow graph',
'gtk-execute'),
gtk.Action(FLOW_GRAPH_STOP, '_Stop', 'Stop the flow graph', 'gtk-stop'),
- gtk.Action(FLOW_GRAPH_SNAP_SHOT, 'S_nap Shot', 'Take a snap shot image
of the flow graph', 'gtk-print'),
+ gtk.Action(FLOW_GRAPH_SCREEN_CAPTURE, 'S_creen Capture', 'Create a
screen capture of the flow graph', 'gtk-print'),
]
def get_action_from_name(action_name):
Modified: grc/trunk/src/Constants.py
===================================================================
--- grc/trunk/src/Constants.py 2007-06-07 05:22:11 UTC (rev 5729)
+++ grc/trunk/src/Constants.py 2007-06-07 05:57:25 UTC (rev 5730)
@@ -152,7 +152,7 @@
# A state is recorded for each change to the flow graph, the size
dictates how many states we can record
######################################################################################################
-""" The size of the state saving cache in the gFloGraph (for undo/redo
functionality) """
+""" The size of the state saving cache in the flow graph (for undo/redo
functionality) """
STATE_CACHE_SIZE = 42
######################################################################################################
@@ -189,7 +189,7 @@
if sys.platform == 'darwin': PYEXEC = 'pythonw'
""" The default command to run a flow graph file """
-DEFAULT_FLOW_GRAPH_EXEC = '%s %s/FlowGraphApp.py'%(PYEXEC, SRC_DIR)
+DEFAULT_FLOW_GRAPH_EXEC = '%s %s/ExecFlowGraphGUI.py'%(PYEXEC, SRC_DIR)
""" The default user preferences file. """
PREFERENCES_FILE_PATH = os.path.abspath(DEFAULT_FILE_PATH + '/.grc.xml')
Modified: grc/trunk/src/DataType.py
===================================================================
--- grc/trunk/src/DataType.py 2007-06-07 05:22:11 UTC (rev 5729)
+++ grc/trunk/src/DataType.py 2007-06-07 05:57:25 UTC (rev 5730)
@@ -35,6 +35,7 @@
self.set_data(data)
self.max = max
self.min = min
+ self.msg = ''
def set_data(self, data):
''' Store the data as a string. '''
self.data = str(data)
@@ -49,14 +50,14 @@
If min or max was specified,
the parsed value must be less than or equal to min and/or
greater than or equal to max. If the parsed value is a vector,
- then the value is the length of the vector. '''
- try:
+ then the value is the length of the vector. '''
+ try:
value = self.parse()
- # for vector/string data types we check if len(obj) is
in range
- if type(value) in (type(list()),type(str())): value =
len(value)
- return (self.max == None or (self.max != None and value
<= self.max)) and\
- (self.min == None or (self.min
!= None and value >= self.min))
- except: return False
+ self.msg = ''
+ return True
+ except Exception, e:
+ self.msg = str(e)
+ return False
def get_type(self):
''' Get a string describing the specific data type. '''
return self.type
@@ -66,6 +67,11 @@
def get_num_bytes(self):
''' How man bytes in this data type? '''
return self.num_bytes
+ def verify_bounds(self, value):
+ ''' Is the value within the bounds of this data type.
+ Raise an exception if not. '''
+ if self.max != None and value > self.max: raise
ValueError('Value "%s" was greater than the max "%s".'%(value, self.max))
+ if self.min != None and value < self.min: raise
ValueError('Value "%s" was greater than the min "%s".'%(value, self.min))
#############################################################################################
# Regular Types
@@ -76,15 +82,18 @@
base_type = 'number'
def parse(self):
''' Evaluate the math expressions in the data type. '''
- return
self.parser(MathExprParser.eval_expr(DataType.parse(self)))
+ num = MathExprParser.eval_expr(DataType.parse(self))
+ parsed = self.parser(num)
+ self.verify_bounds(parsed)
+ return parsed
class Int(Number):
''' The integer data type. '''
type = 'int'
num_bytes = gr.sizeof_int
def parser(self, value):
- ''' Return the real part cast as an int. '''
- return int(complex(value).real)
+ ''' Return an int value or raise error. '''
+ return MathExprParser.verify_int(value)
class Byte(Int):
''' The byte data type is identical to int in this case. '''
@@ -101,8 +110,8 @@
type = 'float'
num_bytes = gr.sizeof_float
def parser(self, value):
- ''' Return the real part cast as a float. '''
- return float(complex(value).real)
+ ''' Return a float value or raise error. '''
+ return MathExprParser.verify_float(value)
class Complex(Number):
''' The complex data type. '''
@@ -110,17 +119,33 @@
num_bytes = gr.sizeof_gr_complex
def parser(self, value):
''' Return the value as complex. '''
- return complex(value)
+ return MathExprParser.verify_complex(value)
#############################################################################################
# Special Types
#############################################################################################
+class RawExpr(DataType):
+ ''' A raw mathematical expression. '''
+ type = 'raw'
+ def __init__(self, data=''):
+ ''' Raw data type contructor. Default value is blank string.
'''
+ DataType.__init__(self, data)
+ def parse(self):
+ ''' Return the raw data returned by the parser '''
+ data = DataType.parse(self)
+ return MathExprParser.eval_expr(data)
+
class String(DataType):
''' The string data type. '''
type = 'string'
def __init__(self, data='', min=None, max=None):
''' String data type contructor. Default value is blank string.
'''
DataType.__init__(self, data, min, max)
+ def parse(self):
+ ''' Verify the the string's length is in bounds. '''
+ string = DataType.parse(self)
+ self.verify_bounds(len(string))
+ return string
class Hex(DataType):
''' The hex data type. '''
@@ -234,7 +259,7 @@
var_keys = list()
for key in Variables.get_keys():
# use the key if we specified all variables or the key
was not for a ranged variable
- if self.all_variables or Variables.get_values(key)[3]
== '': var_keys.append(key)
+ if self.all_variables or not Variables.is_ranged(key):
var_keys.append(key)
return var_keys
class Bool(Enum):
@@ -254,14 +279,11 @@
Do not use, this is a base class only. '''
base_type = 'vector'
def parse(self):
- elements = list()
- data = DataType.parse(self)
- while data.strip() != '':
- index = data.find(',')
- if index == -1: index = len(data)
- elements.append(data[:index])
- data = data[index+1:]
- return map(lambda v: self.parser(MathExprParser.eval_expr(v)),
elements)
+ ''' Verify that the length of the vector is within bounds.
'''
+ elements = MathExprParser.eval_expr(DataType.parse(self))
+ if type(elements) != type(list()): elements = [elements]
#ensure that elements is a list
+ self.verify_bounds(len(elements))
+ return map(lambda v: self.parser(v), elements)
class ByteVector(Vector, Byte):
''' A vector of bytes '''
@@ -303,11 +325,5 @@
if __name__ == '__main__':
print "put things here to test DataTypes"
- dt = String('2fsg56', min=3, max=5)
- print dt.is_valid(),dt.parse()
- dt = IntVector('2, 3,', min=3, max=5)
- print dt.is_valid(),dt.parse()
- dt = Hex('0xfe', min=0, max=254)
- print dt.is_valid(), dt.parse()
\ No newline at end of file
Copied: grc/trunk/src/Editor.py (from rev 5729,
grc/branches/jblum_work/src/Editor.py)
===================================================================
--- grc/trunk/src/Editor.py (rev 0)
+++ grc/trunk/src/Editor.py 2007-06-07 05:57:25 UTC (rev 5730)
@@ -0,0 +1,57 @@
+#! /usr/bin/env python
+"""
+Copyright 2007 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
+"""
+"""
+ Editor.py
+ Josh Blum
+ Execute the flow graph editor GUI. This file must be called by the
python interpreter.
+"""
+
+from Constants import VERSION,DEFAULT_FILE_EXTENSION
+from optparse import OptionParser
+
+if __name__ == "__main__":
+ usage = "usage: %prog optional_flow_graph"+DEFAULT_FILE_EXTENSION
+ version = """
+GNU Radio Companion %s
+
+This file is part of GNU Radio
+GRC comes with ABSOLUTELY NO WARRANTY.
+This is free software,
+and you are welcome to redistribute it.
+"""%VERSION
+ parser = OptionParser(usage=usage, version=version)
+ (options, args) = parser.parse_args()
+ # "test" import modules that this program will use #
+ try:
+ import pygtk
+ pygtk.require('2.0')
+ import gtk
+ import xml.dom.minidom
+ import xml.dom.ext
+ import gnuradio
+ except ImportError, e: #print error and exit
+ print '\nMissing critical component: "%s"\nExiting!'%(e,)
+ exit(-1)
+ # end import of modules #
+ from ActionHandler import ActionHandler
+ if len(args): ActionHandler(args[0])
+ else: ActionHandler()
+ exit(0)
+
Modified: grc/trunk/src/Elements/Element.py
===================================================================
--- grc/trunk/src/Elements/Element.py 2007-06-07 05:22:11 UTC (rev 5729)
+++ grc/trunk/src/Elements/Element.py 2007-06-07 05:57:25 UTC (rev 5730)
@@ -122,9 +122,7 @@
def set_rotation(self, rotation):
""" set the rotation """
if rotation not in POSSIBLE_ROTATIONS:
- print >>sys.stderr, 'Error: '+str(rotation)+' is not
one of the \
- possible rotations: '+str(POSSIBLE_ROTATIONS)
- sys.exit(1)
+ raise Exception('"%s" is not one of the possible
rotations: (%s)'%(rotation,POSSIBLE_ROTATIONS))
self.rotation = rotation
def update(self):
Modified: grc/trunk/src/Elements/GraphicalParam.py
===================================================================
--- grc/trunk/src/Elements/GraphicalParam.py 2007-06-07 05:22:11 UTC (rev
5729)
+++ grc/trunk/src/Elements/GraphicalParam.py 2007-06-07 05:57:25 UTC (rev
5730)
@@ -47,7 +47,8 @@
self.label.set_size_request(140,30)
self.label.show()
self.pack_start(self.label, False)
- self.set_markup = lambda m: self.label.set_markup(m)
+ self.set_markup = lambda m: self.label.set_markup(m)
+ self.tp = None
class EntryParam(InputParam):
""" Provide an entry box for strings and numbers. """
@@ -59,6 +60,10 @@
input.show()
self.pack_start(input, False)
self.get_text = input.get_text
+ # tool tip fun #
+ self.tp = gtk.Tooltips()
+ self.tp.set_tip(self.entry, "")
+ self.tp.enable()
class FileParam(EntryParam):
""" Provide an entry box for filename and a button to browse for a
file. """
@@ -131,9 +136,13 @@
if old_data != new_data: data_type.set_data(new_data)
# Set the markup on the label, red for errors in
cooresponding data type. #
cname = self.get_cname()
- if not data_type.is_valid(): self.input.set_markup('<span
foreground="red"><b>'+cname+'</b></span>')
- else: self.input.set_markup(cname)
-
+ if not data_type.is_valid():
+ self.input.set_markup('<span
foreground="red"><b>'+cname+'</b></span>')
+ if self.input.tp:
self.input.tp.set_tip(self.input.entry, data_type.msg)
+ else:
+ self.input.set_markup(cname)
+ if self.input.tp:
self.input.tp.set_tip(self.input.entry, str(data_type.parse()))
+
def get_markup(self):
""" Create a markup to display the Param as a label on the
SignalBlock.
If the data type is an Enum type, use the cname of the Enum's
current choice.
@@ -172,13 +181,14 @@
if len(head) >= suggested_length:
dt_str = head
else: dt_str =
tail[0:suggested_length-len(head)] + '...' + head
elif self.get_data_type().get_base_type() ==
Vector().get_base_type(): #vector types
- # only keep the first X elements of the
list,
- X = 4 #display more for non complex vectors
+ for i,e in enumerate(data): data[i] = to_str(e)
+ # only keep the first X elements of the
list:
+ X = (100/len(str(data))) + 1 #display more
for non complex vectors
dt_str = '[' # replace the
removed elements with a '...'
- for i,e in enumerate(data):
- if i < X or i > len(data)-2: #leave
one on the end
- dt_str = dt_str + to_str(e)
- if i < len(data)-1 and i+1 !=
X: dt_str = dt_str + ', '
+ for i,f in enumerate(data):
+ if i < X or i+1 == len(data): #leave
one on the end
+ dt_str = dt_str + f
+ if i < len(data)-1 and (i+1 !=
X or X+1 == len(data)): dt_str = dt_str + ', '
elif i == X: dt_str = dt_str + ' ... '
dt_str = dt_str + ']'
elif self.get_data_type().get_type() ==
Hex().get_type(): dt_str = hex(data) #hex, base 16
Modified: grc/trunk/src/Elements/SignalBlock.py
===================================================================
--- grc/trunk/src/Elements/SignalBlock.py 2007-06-07 05:22:11 UTC (rev
5729)
+++ grc/trunk/src/Elements/SignalBlock.py 2007-06-07 05:57:25 UTC (rev
5730)
@@ -156,4 +156,42 @@
sockets_list.remove(last_socket)
self.get_parent().update()
#removing sockets could delete a connection, update all blocks
+##########################################################################
+## Import a Signal Block
+##########################################################################
+ def from_nested_data(fg, nested_data, sb_constructor):
+ ''' Create a signal block from the nested data.
+ Pass the flow graph/parent of the signal block,
+ the nested data for a signal block,
+ and the contructor for the signal block.
+ Return (the signal block, the runnable signal block)
tuple. '''
+ import ParseXML,Messages,SignalBlockDefs
+ find_data = ParseXML.find_data
+ signal_block = find_data([nested_data], 'signal_block')
+ tag = find_data(signal_block, 'tag')
+ id = find_data(signal_block, 'id')
+ # These graphical parameters are optional and will
default to 0 if not found #
+ x_coordinate = find_data(signal_block, 'x_coordinate')
+ if x_coordinate == None: x_coordinate = 0
+ y_coordinate = find_data(signal_block, 'y_coordinate')
+ if y_coordinate == None: y_coordinate = 0
+ rotation = find_data(signal_block, 'rotation')
+ if rotation == None: rotation = 0
+ # Load Parameters #
+ params = find_data(signal_block, 'params')
+ try:
+ signal_block,runnable_signal_block =
SignalBlockDefs.get_signal_block(
+ fg, (int(x_coordinate),int(y_coordinate)),
int(rotation), tag, id, sb_constructor,
+ )
+ for i,param in enumerate(params):
+ try:
+ param = find_data([param], 'param')
+
signal_block.get_params()[i].get_data_type().set_data(param)
+ except: Messages.send_error_load('An invalid
parameter was parsed, ignoring parameter "%d" in block "%s".'%(i, id))
+ return signal_block, runnable_signal_block
+ except SignalBlockDefs.TagNotFoundException:
+ Messages.send_error_load('The definition for "%s" was
not found, ignoring block "%s".'%(tag, id))
+ except Exception, e: Messages.send_error_load('Signal block
"%s" could not be created:\n\t%s'%(id,e))
+ return None, None #not found!, return None tuple
+ from_nested_data = staticmethod(from_nested_data)
\ No newline at end of file
Modified: grc/trunk/src/Elements/Socket.py
===================================================================
--- grc/trunk/src/Elements/Socket.py 2007-06-07 05:22:11 UTC (rev 5729)
+++ grc/trunk/src/Elements/Socket.py 2007-06-07 05:57:25 UTC (rev 5730)
@@ -23,7 +23,7 @@
"""
from Element import Element
-import Utils
+import Utils,DataType
from Connection import TooManyConnectionsException
class Socket(Element):
@@ -72,7 +72,7 @@
def get_data_type(self):
''' Get the data type. If the parsed vlen > 1, then return the
vectorized data type.'''
- if self.vlen != None and self.vlen.is_valid() and
self.vlen.parse() > 1: return vectorize(self.data_type)
+ if self.vlen != None and self.vlen.is_valid() and
self.vlen.parse() > 1: return DataType.vectorize(self.data_type)
return self.data_type
def get_index(self):
Modified: grc/trunk/src/Elements/__init__.py
===================================================================
--- grc/trunk/src/Elements/__init__.py 2007-06-07 05:22:11 UTC (rev 5729)
+++ grc/trunk/src/Elements/__init__.py 2007-06-07 05:57:25 UTC (rev 5730)
@@ -29,8 +29,11 @@
from Element import Element
from Param import Param
-from GraphicalSignalBlock import GraphicalSignalBlock
-from GraphicalSocket import
GraphicalSocket,GraphicalOutputSocket,GraphicalInputSocket
-from GraphicalConnection import GraphicalConnection
-from GraphicalElement import GraphicalElement
-from GraphicalParam import GraphicalParam
+try: #do not import if gtk module dne
+ from GraphicalSignalBlock import GraphicalSignalBlock
+ from GraphicalSocket import
GraphicalSocket,GraphicalOutputSocket,GraphicalInputSocket
+ from GraphicalConnection import GraphicalConnection
+ from GraphicalElement import GraphicalElement
+ from GraphicalParam import GraphicalParam
+except ImportError: pass
+
Copied: grc/trunk/src/ExecFlowGraph.py (from rev 5729,
grc/branches/jblum_work/src/ExecFlowGraph.py)
===================================================================
--- grc/trunk/src/ExecFlowGraph.py (rev 0)
+++ grc/trunk/src/ExecFlowGraph.py 2007-06-07 05:57:25 UTC (rev 5730)
@@ -0,0 +1,115 @@
+#! /usr/bin/env python
+"""
+Copyright 2007 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
+"""
+"""
+ ExecFlowGraph.py
+ Josh Blum
+ Use a xml input file to build and run a gnu radio flow graph without
graphics.
+"""
+
+import ParseXML
+import Variables
+import SignalBlockDefs
+from Elements import SignalBlock
+from gnuradio import gr
+import os
+from Constants import DEFAULT_FILE_EXTENSION
+from optparse import OptionParser
+
+##############################################################################################
+# Flow Graph Builder
+##############################################################################################
+class FlowGraphBuilder(gr.flow_graph):
+ """ Parse the input file to build the gnuradio flow graph.
+ Register the variables, handle signal block callbacks. """
+ def __init__(self, file_path):
+ nested_data = ParseXML.from_xml(ParseXML.from_file(file_path))
+ gr.flow_graph.__init__(self)
+ self.callbacks = list()
+ self.var_keys = list()
+ self.parse_nested_data(nested_data)
+
+ def add_window(self, window, type='', title=''):
+ ''' Empty method for adding a window in the GUI flow graph.
'''
+ pass
+
+ def get_panel(self):
+ ''' Empty method for getting the wx panel in the GUI flow
graph. '''
+ print '''
+Graphical Sinks are not supported in ExecFlowGraph.py:
+ use ExecFlowGraphGUI.py [-d] or remove the Graphical Sink.
+Exiting!'''
+ exit(-1)
+
+ def add_callback(self, function, data_type_params):
+ """ Register a callback function with its associated data.
"""
+ self.callbacks.append((function, data_type_params))
+
+ def parse_callbacks(self):
+ """ For each call back, parse all of the data and
+ call the registered callback function on that data. """
+ for function, data_type_params in self.callbacks:
+ try:
+ if type(data_type_params) in (type(list()),
type(tuple())):
+ function(*map(lambda param:
param.parse(), data_type_params))
+ else: function(data_type_params.parse())
+ except: print "error parsing a callback -> ignoring..."
+
+ def parse_nested_data(self, nested_data):
+ """ Parse nested data from a flow graph file and build the
graphics.
+ This code is partially copied from FlowGraph.py, with
unnecessary code removed. """
+ find_data = ParseXML.find_data
+ flow_graph = find_data([nested_data], 'flow_graph')
+ vars = find_data(flow_graph, 'vars')
+ signal_blocks = find_data(flow_graph, 'signal_blocks')
+ connections = find_data(flow_graph, 'connections')
+ self.var_keys = Variables.from_nested_data(vars)
+ signal_blocks_dict = dict()
+ for signal_block in signal_blocks:
+ signal_block,runnable_signal_block =
SignalBlock.from_nested_data(None, signal_block, SignalBlock)
+ data_type_params = list()
+ data_type_params.append(self)#put the flow graph in the
front of the list
+ for param in signal_block.get_params(): #load the list
of arguments to the runnable block
+ data_type_params.append(param.get_data_type())
+ signal_blocks_dict[signal_block.get_id()] =
runnable_signal_block(*data_type_params)
+ for connection in connections:
+ connection = find_data([connection], 'connection')
+ input_signal_block_id = find_data(connection,
'input_signal_block_id')
+ input_socket_index = int(find_data(connection,
'input_socket_index'))
+ output_signal_block_id = find_data(connection,
'output_signal_block_id')
+ output_socket_index = int(find_data(connection,
'output_socket_index'))
+ self.connect(# use this flow graph's connect method
#
+ (signal_blocks_dict[output_signal_block_id],
output_socket_index),
+ (signal_blocks_dict[input_signal_block_id],
input_socket_index))
+
+if __name__ == '__main__':
+ usage = "usage: %prog [-p] flow_graph"+DEFAULT_FILE_EXTENSION
+ parser = OptionParser(usage=usage)
+ parser.add_option("-p", "--pid_file", action="store", type="string",
dest="pid_file", help="record process id")
+ (options, args) = parser.parse_args()
+ if options.pid_file:
+ try: open(options.pid_file, 'w').write('%d\n' % os.getpid())
+ except: print "could not create pid file: %s"%options.pid_file
+ if len(args): #only create the flow graph if a file was passed
+ fg = FlowGraphBuilder(args[0])
+ fg.start()
+ raw_input('Flow Graph Running...\nPress Enter to Exit: ')
+ fg.stop()
+ else: parser.print_help()
+
\ No newline at end of file
Copied: grc/trunk/src/ExecFlowGraphGUI.py (from rev 5729,
grc/branches/jblum_work/src/ExecFlowGraphGUI.py)
===================================================================
--- grc/trunk/src/ExecFlowGraphGUI.py (rev 0)
+++ grc/trunk/src/ExecFlowGraphGUI.py 2007-06-07 05:57:25 UTC (rev 5730)
@@ -0,0 +1,221 @@
+#! /usr/bin/env python
+"""
+Copyright 2007 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
+"""
+"""
+ ExecFlowGraphGUI.py
+ Josh Blum
+ Use a xml input file to build and run a gnu radio flow graph with wx
GUI elements.
+"""
+
+import Variables
+from ExecFlowGraph import FlowGraphBuilder
+import wx
+import os
+from Constants import *
+import math
+from optparse import OptionParser
+
+##############################################################################################
+# Variable Control
+##############################################################################################
+class VariableControl(wx.BoxSizer):
+ """ House a Slider and a Text Box for variable control. """
+ def __init__(self, parent, key, parse_callbacks):
+ """ Create the slider, text box, and label given the variable
key.
+ parse_callbacks is a method to update the registered
signal block callback methods. """
+ self.key = key
+ self.parse_callbacks = parse_callbacks
+ value, min, max, step = Variables.get_values(key)
+ wx.BoxSizer.__init__(self, wx.VERTICAL)
+ label_text_sizer = wx.BoxSizer(wx.HORIZONTAL) #label and text
box container
+ label = wx.StaticText(parent, -1, key+' -> ')
+ self.text_box = text_box = wx.TextCtrl(parent, -1, 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_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)
+ ### Determine the slider width based on the number of steps
###
+ self.num_steps = num_steps =
(float(max)-float(min))/float(step)
+ if num_steps == DEFAULT_SLIDER_STEPS: slider_width =
DEFAULT_SLIDER_WIDTH
+ elif num_steps > DEFAULT_SLIDER_STEPS: # make the slider wider
than default
+ slider_width = MAX_SLIDER_WIDTH -
(MAX_SLIDER_WIDTH-DEFAULT_SLIDER_WIDTH)*DEFAULT_SLIDER_STEPS/num_steps
+ elif num_steps < DEFAULT_SLIDER_STEPS: # make the slider slimer
then default
+ slider_width = MIN_SLIDER_WIDTH +
(DEFAULT_SLIDER_WIDTH-MIN_SLIDER_WIDTH)*num_steps/DEFAULT_SLIDER_STEPS
+ self.slider_width = slider_width = int(slider_width) #make
sure width is an integer number of pixels
+ ### make the slider ###
+ self.slider = slider = wx.Slider(parent, -1,
size=wx.Size(slider_width, SLIDER_HEIGHT), style=wx.SL_HORIZONTAL)
+ try: slider.SetRange(0, num_steps)
+ except Exception, e:
+ sys.stderr.write('Slider, "%s", has too many
steps!\n'%key)
+ sys.exit(1)
+ slider.Bind(wx.EVT_SCROLL, self.handle_scroll) #bind the
scrolling event
+ self.Add(slider, 0, wx.ALIGN_CENTER)
+ self.handle_enter()#sets the slider's value
+
+ def get_slider_width(self):
+ """ Get the width of the slider in pixels. """
+ return self.slider_width
+
+ def get_slider_value(self):
+ """ Read the value from the slider slider.
+ Translate the slider value into a real numerical value. """
+ slider_value = self.slider.GetValue()
+ value, min, max, step = Variables.get_values(self.key)
+ return str(slider_value*(float(max)-float(min))/self.num_steps
+ float(min))
+
+ def set_slider_value(self, real_value):
+ """ Translate the real numerical value into a slider value.
+ Write the value to the slider. """
+ value, min, max, step = Variables.get_values(self.key)
+ slider_value =
(float(real_value)-float(min))*self.num_steps/(float(max)-float(min))
+ self.slider.SetValue(slider_value)
+
+ def handle_scroll(self, event=None):
+ """ A scroll event is detected. Read the slider. Try to
register the new entry:
+ If the registration passes, update the text box, and parse the
callbacks.
+ If the registration fails, restore the slider to the previous
registered values. """
+ key = self.key
+ new_value = str(self.get_slider_value()) #the new value
must be a string!
+ value, min, max, step = Variables.get_values(key)
+ Variables.unregister(key)
+ try:
+ Variables.register(key, new_value, min, max, step)
+ self.text_box.SetValue(str(new_value))
+ self.parse_callbacks()
+ except:
+ Variables.register(key, value, min, max, step)
+ self.set_slider_value(value)
+
+ def handle_enter(self, event=None):
+ """ An enter key was pressed. Read the text box. Try to
register the new entry:
+ If the registration passes, move the slider, and parse the
callbacks.
+ If the registration fails, restore the text box to the
previous registered value. """
+ key = self.key
+ new_value = str(self.text_box.GetValue()) #the new value
must be a string!
+ value, min, max, step = Variables.get_values(key)
+ Variables.unregister(key)
+ try:
+ Variables.register(key, new_value, min, max, step)
+ self.set_slider_value(new_value)
+ self.parse_callbacks()
+ except:
+ Variables.register(key, value, min, max, step)
+ self.text_box.SetValue(value)
+
+##############################################################################################
+# Flow Graph Frame
+##############################################################################################
+class FlowGraphFrame(wx.Frame, FlowGraphBuilder):
+ """ A FlowGraphFrame is a wx.Frame and a FlowGraphBuilder.
+ This flow graph frame parses a saved flow graph file,
+ houses all the sliders and graphical sinks,
+ and starts the flow graph. """
+ def __init__(self, file_path):
+ file_path = os.path.abspath(file_path)
+ wx.Frame.__init__(self, None , -1, MAIN_WINDOW_PREFIX+' -
Executing: '+file_path)
+ if WX_APP_ICON: self.SetIcon(wx.Icon(WX_APP_ICON,
wx.BITMAP_TYPE_ANY))
+ self.SetSizeHints(200, 100)
+ # create holder for sliders and graphs #
+ self.sliders_dict = dict()
+ self.sink_windows = dict()
+ graphs_box = wx.GridSizer(1, 1, 0, 0)
+ self.panel = panel = wx.ScrolledWindow(self, -1)
+ panel.SetSizer(graphs_box)
+ # create the main box for all components #
+ main_box = wx.BoxSizer(wx.VERTICAL)
+ self.sliders_box = wx.BoxSizer(wx.VERTICAL)
+ main_box.Add(self.sliders_box, 0, wx.ALIGN_CENTER)
+ main_box.Add(panel, 0, wx.EXPAND)
+ self.Bind(wx.EVT_CLOSE, self.Quit)
+ # fill in all the sliders and graphs #
+ FlowGraphBuilder.__init__(self, file_path)
+ self.create_sliders()
+ graphs_box.SetCols(int(math.sqrt(len(self.sink_windows))))
+ for key in sorted(self.sink_windows.keys()):
graphs_box.Add(self.sink_windows[key], 0, wx.EXPAND)
+ self.SetSizerAndFit(main_box)
+ self.Show()
+ self.start() #start the flow graph
+
+ def Quit(self, event):
+ ''' Exit the application. '''
+ self.stop() #stop the flow graph
+ self.Destroy() #destroy the wx frame
+ sys.exit(0)
+
+ def add_window(self, window, type='', title=''):
+ ''' Register the scope/fft window with a unique key. '''
+ key = str(type) + str(title)
+ while self.sink_windows.has_key(key): key = key + '_'
+ self.sink_windows[key] = window
+
+ def get_panel(self):
+ ''' Get the panel that will parent the graphical blocks.
'''
+ return self.panel
+
+ def create_sliders(self):
+ ''' Using the list of variable keys imported by the builder,
+ create sliders for those variables with ranges. '''
+ len = 0
+ for key in self.var_keys:
+ value, min, max, step = Variables.get_values(key)
+ if Variables.is_ranged(key):
+ variable_control = VariableControl(self, key,
self.parse_callbacks)
+ width = variable_control.get_slider_width()
+ ### Create a new row of sliders if the
current row is full ###
+ if len == 0 or len + width > MAX_SLIDERS_WIDTH:
+ len = 0
+ slider_box = wx.BoxSizer(wx.HORIZONTAL)
+ self.sliders_box.Add(slider_box, 0,
wx.ALIGN_CENTER)
+ slider_box.Add(variable_control, 0,
wx.ALIGN_CENTER)
+ len = len + width
+
+##############################################################################################
+# Flow Graph App
+##############################################################################################
+class FlowGraphApp(wx.App):
+ """ The FlowGraphApp is the wx.App containing the FlowGraphFrame.
"""
+ def __init__(self, flow_graph_file_path):
+ self.flow_graph_file_path = flow_graph_file_path
+ wx.App.__init__(self, 0)
+
+ def OnInit(self):
+ """ If the creation of the frame fails, return False so the
program will exit. """
+ try:
+
self.SetTopWindow(FlowGraphFrame(self.flow_graph_file_path))#first argument is
the flow graph
+ return True
+ except Exception, e:
+ sys.stderr.write(str(e)+'\n')
+ sys.exit(1)
+
+if __name__ == '__main__':
+ usage = "usage: %prog [-p] flow_graph"+DEFAULT_FILE_EXTENSION
+ parser = OptionParser(usage=usage)
+ parser.add_option("-p", "--pid_file", action="store", type="string",
dest="pid_file", help="record process id")
+ parser.add_option("-d", "--disable-wx", action="store_true",
dest="no_wx", default=False, help="disable the wx gui")
+ (options, args) = parser.parse_args()
+ if options.pid_file:
+ try: open(options.pid_file, 'w').write('%d\n' % os.getpid())
+ except: print "could not create pid file: %s"%options.pid_file
+ if len(args): #only create the wx app if a flow graph was passed
+ app = FlowGraphApp(args[0])
+ if options.no_wx: raw_input('Flow Graph Running...\nPress Enter
to Exit: ') #do not start wx
+ else: app.MainLoop() #start the wxApp MainLoop
+ else: parser.print_help()
+
+
\ No newline at end of file
Deleted: grc/trunk/src/FlowGraphApp.py
Modified: grc/trunk/src/Graphics/Bars.py
===================================================================
--- grc/trunk/src/Graphics/Bars.py 2007-06-07 05:22:11 UTC (rev 5729)
+++ grc/trunk/src/Graphics/Bars.py 2007-06-07 05:57:25 UTC (rev 5730)
@@ -28,68 +28,62 @@
import gtk
TOOLBAR_LIST = [
- FLOW_GRAPH_NEW,
- FLOW_GRAPH_OPEN,
- FLOW_GRAPH_SAVE,
- None,
- FLOW_GRAPH_SNAP_SHOT,
- None,
- FLOW_GRAPH_UNDO,
- FLOW_GRAPH_REDO,
- None,
- FLOW_GRAPH_RUN,
- FLOW_GRAPH_STOP,
- None,
- ELEMENT_DELETE,
- SIGNAL_BLOCK_ROTATE_LEFT,
- SIGNAL_BLOCK_ROTATE_RIGHT,
- ]
+ FLOW_GRAPH_NEW,
+ FLOW_GRAPH_OPEN,
+ FLOW_GRAPH_SAVE,
+ None,
+ FLOW_GRAPH_SCREEN_CAPTURE,
+ None,
+ FLOW_GRAPH_UNDO,
+ FLOW_GRAPH_REDO,
+ None,
+ FLOW_GRAPH_EXEC,
+ FLOW_GRAPH_STOP,
+ None,
+ ELEMENT_DELETE,
+ SIGNAL_BLOCK_ROTATE_LEFT,
+ SIGNAL_BLOCK_ROTATE_RIGHT,
+]
MENU_BAR_LIST = [
- (gtk.Action('File', '_File',
None, None),
- [
- FLOW_GRAPH_NEW,
- FLOW_GRAPH_OPEN,
- None,
- FLOW_GRAPH_SAVE,
-
FLOW_GRAPH_SAVE_AS,
- None,
-
FLOW_GRAPH_SNAP_SHOT,
- None,
-
APPLICATION_QUIT,
- ]),
- (gtk.Action('Edit', '_Edit',
None, None),
- [
- FLOW_GRAPH_UNDO,
- FLOW_GRAPH_REDO,
- None,
- ELEMENT_DELETE,
-
SIGNAL_BLOCK_ROTATE_LEFT,
-
SIGNAL_BLOCK_ROTATE_RIGHT,
-
SIGNAL_BLOCK_PARAM_MODIFY,
- ]),
- (gtk.Action('Run', '_Run',
None, None),
- [
- FLOW_GRAPH_RUN,
- FLOW_GRAPH_STOP,
- ]),
- (gtk.Action('Options',
'_Options', None, None),
- [
-
FLOW_GRAPH_WINDOW_RESIZE,
-
USRP_DIAGNOSTICS_DISPLAY,
-
PREFS_WINDOW_DISPLAY,
- ]),
- (gtk.Action('Help', '_Help',
None, None),
- [
-
ABOUT_WINDOW_DISPLAY,
-
COLORS_WINDOW_DISPLAY,
-
HOTKEYS_WINDOW_DISPLAY,
- ])
+ (gtk.Action('File', '_File', None, None), [
+ FLOW_GRAPH_NEW,
+ FLOW_GRAPH_OPEN,
+ None,
+ FLOW_GRAPH_SAVE,
+ FLOW_GRAPH_SAVE_AS,
+ None,
+ FLOW_GRAPH_SCREEN_CAPTURE,
+ None,
+ APPLICATION_QUIT,
+ ]),
+ (gtk.Action('Edit', '_Edit', None, None), [
+ FLOW_GRAPH_UNDO,
+ FLOW_GRAPH_REDO,
+ None,
+ ELEMENT_DELETE,
+ SIGNAL_BLOCK_ROTATE_LEFT,
+ SIGNAL_BLOCK_ROTATE_RIGHT,
+ SIGNAL_BLOCK_PARAM_MODIFY,
+ ]),
+ (gtk.Action('Execute', '_Execute', None, None), [
+ FLOW_GRAPH_EXEC,
+ FLOW_GRAPH_STOP,
+ ]),
+ (gtk.Action('Options', '_Options', None, None), [
+ FLOW_GRAPH_WINDOW_RESIZE,
+ PREFS_WINDOW_DISPLAY,
+ ]),
+ (gtk.Action('Help', '_Help', None, None), [
+ ABOUT_WINDOW_DISPLAY,
+ COLORS_WINDOW_DISPLAY,
+ HOTKEYS_WINDOW_DISPLAY,
+ MATH_EXPR_WINDOW_DISPLAY,
+ USRP_DIAGNOSTICS_DISPLAY,
+ ])
- ]
+]
-
-
class Toolbar(gtk.Toolbar):
""" Toolbar is a gtk Toolbar with actions added from the toolbar
list """
def __init__(self):
Modified: grc/trunk/src/Graphics/Dialogs.py
===================================================================
--- grc/trunk/src/Graphics/Dialogs.py 2007-06-07 05:22:11 UTC (rev 5729)
+++ grc/trunk/src/Graphics/Dialogs.py 2007-06-07 05:57:25 UTC (rev 5730)
@@ -129,6 +129,13 @@
self.set_comments("""\
Thank you to all those from the mailing list who tested GNU Radio Companion
and offered advice.
--
+Special Thanks:
+A. Brinton Cooper -> starting the project
+CER Technology Fellowship Grant -> initial funding
+William R. Kenan Jr. Fund -> usrp & computers
+Patrick Strasser -> the GRC icon
+Achilleas Anastasopoulos -> trellis support
+--
GNU Radio Companion, \
This file is part of GNU Radio
GRC comes with ABSOLUTELY NO WARRANTY. \
@@ -191,3 +198,74 @@
self.vbox.pack_start(label)
self.run()
self.destroy()
+
+######################################################################################################
+class MathExprDialog(gtk.Dialog):
+ """ A dialog to test math expressions for the parser. """
+ HELP_MSG = """\
+<b>Operators - Complex <i>(complex arguments)</i></b>
+ + - * / ^
+<b>Trigonometric Functions <i>(complex arguments, radians)</i></b>
+ sin(z) cos(z) tan(z) asin(z) acos(z) atan(z)
+ sinh(z) cosh(z) tanh(z) asinh(z) acosh(z) atanh(z)
+<b>Complex Components <i>(complex arguments)</i></b>
+ mag(z*) real(z*) imag(z*) conj(z*) arg(z)
+<b>Misc Functions <i>(complex arguments)</i></b>
+ abs(z*) pow(b, p) sqrt(z) log10(z) log(z, [b=e]) ln(z) exp(z)
+<b>Misc Functions <i>(floating point arguments)</i></b>
+ floor(f) ceil(f) radians(deg) degrees(rad) mod(f1, f2) atan2(y, x)
sgn(f) min(f*) max(f*)
+<b>Filter Taps Generators <i>(floating point arguments, rates and
frequencies in Hz)</i></b>
+ low_pass(gain, samp_rate, cutoff_freq, width, [window=hamming],
[beta=6.76])
+ high_pass(gain, samp_rate, cutoff_freq, width, [window=hamming],
[beta=6.76])
+ band_pass(gain, samp_rate, low_cutoff_freq, high_cutoff_freq, width,
[window=hamming], [beta=6.76])
+ complex_band_pass(gain, samp_rate, low_cutoff_freq, high_cutoff_freq,
width, [window=hamming], [beta=6.76])
+ band_reject(gain, samp_rate, low_cutoff_freq, high_cutoff_freq, width,
[window=hamming], [beta=6.76])
+ gaussian(gain, spb, bt, int ntaps)
+ hilbert(int ntaps, [window=rectangular], [beta=6.76])
+ root_raised_cosine(gain, samp_rate, symbol_rate, alpha, int ntaps)
+ window(window, int ntaps, beta)
+<b>Window Types for Filters</b>
+ hamming hann blackman rectangular kaiser
+<b>Other Constants</b>
+ pi e j
+<b>Using Vectors</b>
+ () (num1,) (num2, num2, ...)
+<b>Test Your Expressions Below:</b>\
+"""
+ def __init__(self):
+ """ Create a new gtk Dialog with a close button, an extry
box, and an output text box. """
+ gtk.Dialog.__init__(self, buttons=('gtk-close',
gtk.RESPONSE_CLOSE))
+ # Create the title and label #
+ self.set_title('Mathematical Expressions')
+ label = gtk.Label()
+ label.set_markup(self.HELP_MSG)
+ label.show()
+ self.vbox.pack_start(label, False)
+ self.set_size_request(800, 600)
+ # create the entry box and connect it to a new handler #
+ self.param = GraphicalParam('Expression', RawExpr(''))
+ self.input_obj = self.param.get_input_object()
+ self.input_obj.entry.connect("changed", self.handle_changed)
+ self.vbox.pack_start(self.input_obj, False)
+ # create a text box for parser output #
+ self.text_box = TextDisplay()
+ self.text_box.set_text('')
+ # add the scrolled window for the text box #
+ scrolled_window = gtk.ScrolledWindow()
+ scrolled_window.set_policy(gtk.POLICY_AUTOMATIC,
gtk.POLICY_AUTOMATIC)
+ scrolled_window.add_with_viewport(self.text_box)
+ scrolled_window.show()
+ self.vbox.pack_start(scrolled_window, True)
+ self.handle_changed() #initial display
+ self.run()
+ self.destroy()
+
+ def handle_changed(self, widget=None):
+ ''' Handle changed in the param's entry box.
+ Call the default change handler and then update the
text box. '''
+ self.param.handle_changed()
+ if self.param.get_data_type().is_valid():
+ text = str(self.param.get_data_type().parse())
+ else: text = self.param.get_data_type().msg
+ self.text_box.set_text(text)
+
\ No newline at end of file
Modified: grc/trunk/src/Graphics/FlowGraph.py
===================================================================
--- grc/trunk/src/Graphics/FlowGraph.py 2007-06-07 05:22:11 UTC (rev 5729)
+++ grc/trunk/src/Graphics/FlowGraph.py 2007-06-07 05:57:25 UTC (rev 5730)
@@ -39,6 +39,7 @@
import Messages
import Preferences
import ParseXML
+import Messages
class FlowGraph(gtk.DrawingArea):
""" FlowGraph is the data structure to store graphical signal
blocks, their graphical input/output
@@ -327,7 +328,12 @@
if self.mouse_pressed and self.selected_element != None:
self.selected_element.draw(self.pixmap)
self.window.draw_drawable(self.gc, self.pixmap, 0, 0,
0, 0, -1, -1)
-
+
+##########################################################################
+##
+## Export the Flow Graph
+##
+##########################################################################
def to_nested_data(self):
''' Dump all the values in this flow graph into a nested
data format. '''
vars_list = list()
@@ -344,7 +350,10 @@
('vars', vars_list),
('signal_blocks', signal_blocks_list),
('connections', connections_list)
- ])
+ ])
+
##########################################################################
+ ## Export the Variables
+
##########################################################################
for key in self.variable_modification_window.to_key_list():
row = (key,) + Variables.get_values(key)
vars_list.append(('var', [
@@ -354,6 +363,9 @@
('max', row[3]),
('step', row[4]),
]))
+
##########################################################################
+ ## Export the Signal Blocks
+
##########################################################################
for element in filter(Utils.is_signal_block, self.elements):
params_list = list()
signal_blocks_list.append(('signal_block', [
@@ -366,6 +378,9 @@
]))
for param in element.get_params():
params_list.append(('param',
str(param.get_data_type().get_data())) )
+
##########################################################################
+ ## Export the Connections
+
##########################################################################
for element in filter(Utils.is_connection, self.elements):
connections_list.append(('connection', [
('input_signal_block_id',
str(element.get_input_socket().get_parent().get_id())),
@@ -375,53 +390,43 @@
]))
#print 'To',nested_data
return nested_data
-
+
+##########################################################################
+##
+## Import the Flow Graph
+##
+##########################################################################
def from_nested_data(self, nested_data):
- ''' Set all the values in this flow graph using the nested
data. '''
+ ''' Set all the values in this flow graph using the nested
data.
+ Return a string of errors, one error per line. '''
#print 'From',nested_data
- errors = ''
#TODO: use a non-destructive methode to clear the elements list
self.elements = list() #clear the elements
find_data = ParseXML.find_data
flow_graph = find_data([nested_data], 'flow_graph')
+ # window width and height are optional #
window_width = find_data(flow_graph, 'window_width')
+ if window_width == None: window_width = MAX_WINDOW_WIDTH
window_height = find_data(flow_graph, 'window_height')
+ if window_height == None: window_height = MAX_WINDOW_HEIGHT
self.set_size_request(int(window_width),int(window_height))
vars = find_data(flow_graph, 'vars')
signal_blocks = find_data(flow_graph, 'signal_blocks')
connections = find_data(flow_graph, 'connections')
- Variables.clear_vars()
- key_list = list()
- for var in vars:
- var = find_data([var], 'var')
- key = find_data(var, 'key')
- value = find_data(var, 'value')
- min = find_data(var, 'min')
- max = find_data(var, 'max')
- step = find_data(var, 'step')
- Variables.register(key, value, min, max, step)
- key_list.append(key)
- self.variable_modification_window.from_key_list(key_list)
+
##########################################################################
+ ## Import the Variables
+
##########################################################################
+ keys = Variables.from_nested_data(vars)
+ self.variable_modification_window.from_key_list(keys)
+
##########################################################################
+ ## Import the Signal Blocks
+
##########################################################################
for signal_block in signal_blocks:
- signal_block = find_data([signal_block], 'signal_block')
- tag = find_data(signal_block, 'tag')
- id = find_data(signal_block, 'id')
- x_coordinate = int(find_data(signal_block,
'x_coordinate'))
- y_coordinate = int(find_data(signal_block,
'y_coordinate'))
- rotation = int(find_data(signal_block, 'rotation'))
- params = find_data(signal_block, 'params')
- try:
- signal_block = SignalBlockDefs.get_signal_block(
- self, (x_coordinate,y_coordinate),
rotation, tag, id, GraphicalSignalBlock
- )[0]
- for i,param in enumerate(params):
- try:
- param = find_data([param],
'param')
-
signal_block.get_params()[i].get_data_type().set_data(param)
- except: errors = errors + 'An invalid
parameter was parsed, ignoring parameter "%d" in block "%s".\n'%(i, id)
- self.elements.append(signal_block)
- except SignalBlockDefs.TagNotFoundException:
- errors = errors + 'The definition for "%s" was
not found, ignoring block "%s".\n'%(tag, id)
+ signal_block =
GraphicalSignalBlock.from_nested_data(self, signal_block,
GraphicalSignalBlock)[0] #index 0 for just signal block
+ if signal_block: self.elements.append(signal_block)
+
##########################################################################
+ ## Import the Connections
+
##########################################################################
for connection in connections:
connection = find_data([connection], 'connection')
input_signal_block_id = find_data(connection,
'input_signal_block_id')
@@ -434,9 +439,9 @@
if element.get_id() == output_signal_block_id:
output_socket = element.get_output_socket(output_socket_index)
try: self.elements.append(GraphicalConnection(self,
input_socket, output_socket))
except (InvalidConnectionException,
TooManyConnectionsException):
- errors = errors + 'Could not connect "%s"
input[%d] and "%s" output[%d].\n'%(
- input_signal_block_id,
input_socket_index, output_signal_block_id, output_socket_index)
+ Messages.send_error_load('Could not connect
"%s" input[%d] and "%s" output[%d].'%(
+ input_signal_block_id,
input_socket_index, output_signal_block_id, output_socket_index))
self.selected_element = None
self.update()
- return errors
+ # done importing the flow graph #
Modified: grc/trunk/src/Graphics/MainWindow.py
===================================================================
--- grc/trunk/src/Graphics/MainWindow.py 2007-06-07 05:22:11 UTC (rev
5729)
+++ grc/trunk/src/Graphics/MainWindow.py 2007-06-07 05:57:25 UTC (rev
5730)
@@ -92,7 +92,7 @@
def set_title(self, title):
""" set the title and prepend the program name """
- prepend_str = MAIN_WINDOW_PREFIX + ' - '
+ prepend_str = MAIN_WINDOW_PREFIX + ' - Editing: '
gtk.Window.set_title(self, prepend_str + str(title))
def show_reports_window(self, show):
Modified: grc/trunk/src/Graphics/__init__.py
===================================================================
--- grc/trunk/src/Graphics/__init__.py 2007-06-07 05:22:11 UTC (rev 5729)
+++ grc/trunk/src/Graphics/__init__.py 2007-06-07 05:57:25 UTC (rev 5730)
@@ -24,5 +24,5 @@
# only import the modules that need external access #
from MainWindow import MainWindow
from FlowGraphFileDialog import FlowGraphFileDialog
-from Dialogs import
PreferencesDialog,FlowGraphWindowSizeDialog,LooseChangesMessage,AboutDialog,DataTypeColorsDialog,HotKeysDialog
+from Dialogs import
PreferencesDialog,FlowGraphWindowSizeDialog,LooseChangesMessage,AboutDialog,DataTypeColorsDialog,HotKeysDialog,MathExprDialog
from USRPDiagnostics import USRPDiagnosticsDialog,enable_usrp_diagnostics
Modified: grc/trunk/src/MathExprParser.py
===================================================================
--- grc/trunk/src/MathExprParser.py 2007-06-07 05:22:11 UTC (rev 5729)
+++ grc/trunk/src/MathExprParser.py 2007-06-07 05:57:25 UTC (rev 5730)
@@ -19,180 +19,426 @@
"""
MathExprParser.py
Josh Blum
- Take a sting containing numbers, floats, complex
- and operators and parse it into a single answer.
+ This file provides:
+ eval_expr for evaluating string expressions,
+ verify_complex, verify_float, verify_int for casting numbers,
+ and verify_number for picking the best representation.
"""
-from math import cos, sin, log, atan2, e
+import math,cmath
+from gnuradio import gr
-_ORDER_OF_OPS = ('^', '*', '/', '-', '+')
+_ORDER_OF_OPS = ('^-', '^', '*', '/', '-', '+')
-_WHITESPACE = (' ', '\t')
+_WHITESPACE = (' ', '\t', '')
-def number(value):
- ''' The parser method used for numeric entries. '''
- return complex(value)
+_CONSTANTS = { #constans must be lower case
+ 'pi' : cmath.pi,
+ 'e' : cmath.e,
+ 'j' : 1j,
-def complex_pow(base, power):
- ''' Compute a complex base raised to a complex power. '''
- base = complex(base)
- power = complex(power)
- r = abs(base)
- s = atan2(base.imag,base.real)
- x = power.real
- y = power.imag
- if r == 0: return 0
- else: return pow(r, x)*pow(e,
-s*y)*(cos(y*log(r)+x*s)+1j*sin(y*log(r)+x*s))
+ 'hamming' : gr.firdes.WIN_HAMMING, #filter windows
+ 'hann' : gr.firdes.WIN_HANN,
+ 'blackman' : gr.firdes.WIN_BLACKMAN,
+ 'rectangular' : gr.firdes.WIN_RECTANGULAR,
+ 'kaiser' : gr.firdes.WIN_KAISER,
+}
+
+#########################################################
+## Number parsers
+#########################################################
-def eval_expr(expr_str):
- ''' Evaluate a mathematical expression string with numbers, and
operators.
- Numbers will all be parsed. Operators are ^, *, /, -, +.
- Raise an exception on failue. '''
- expr_list = list()
- _convert_expr_str_to_list(expr_str, expr_list)
- #print expr_list
- _rectify_operators_in_expr_list(expr_list)
- #print expr_list
- return _eval_list(expr_list)
+def verify_complex(num):
+ ''' Verify that the number is a complex number.
+ Raise an exception if number cannot be cast to a complex.
+ Return the value as a complex. '''
+ try: c_num = complex(num) #ensure that the num can be complex
+ except: raise ValueError('"%s" is not a number.'%(num,))
+ return c_num
+def verify_float(num):
+ ''' Verify that the number has no imaginary part.
+ Raise an exception if the imaginary part is non zero.
+ Return the value as a float. '''
+ c_num = verify_complex(num) #ensure that the num can be complex
+ if c_num.imag != 0: raise ValueError('Expected a float but found
"%s".'%(c_num,))
+ return float(c_num.real)
+
+def verify_int(num):
+ ''' Verify that the number has no imaginary part.
+ Verify that the number has no decimal part.
+ Otherwise raise an exception.
+ Return the value as a int '''
+ f_num = verify_float(num)
+ if f_num - int(f_num) != 0: raise ValueError('Expected an integer but
found "%s".'%(f_num,))
+ return int(f_num)
-def _eval_list(expr_list):
- ''' Recursivly simplify a nested expression list. Each list will
contain an
- operand with 2 arguments or a single argument. An argument can be
another list
- to evaluate or a number. Raise an exception on failue.'''
- if type(expr_list) != type(list()): return number(expr_list)
- else:
- if len(expr_list) == 1: return _eval_list(expr_list[0])
- else:
- val_before = _eval_list(expr_list[0])
- val_after = _eval_list(expr_list[2])
- op = expr_list[1]
- if op == '^': return complex_pow(val_before,val_after)
- elif op == '*': return val_before * val_after
- elif op == '/': return val_before / val_after
- elif op == '+': return val_before + val_after
- elif op == '-': return val_before - val_after
- else: raise IllegalSymbolException('should be
programmed out: '+op)
+def verify_number(num):
+ ''' Try to cast the number into its most primative form.
+ In this order: int, float, or complex.
+ Raise an exception if num is not a number.
+ Return a number of type listed above. '''
+ try: return verify_int(num) #try int
+ except:
+ try: return verify_float(num) #try float
+ except: return verify_complex(num) #complex or fail
-def _rectify_operators_in_expr_list(expr_list):
- ''' Recursivly interate through a list of operators and arguments.
Arguments can be
- a list, or a number. Operators are ^, *, /, -, + and _. The underscore
is
- subbed in to represent an attatched negative sign.
- This operator will group all operands and arguments into nested lists
following order of operations.
- Each list will have no more than two arguments and a single
operator.
- Raise an exception on failue (syntax problems). '''
- if len(expr_list) == 0: raise IllegalSyntaxException('empty!')
- for op in _ORDER_OF_OPS:
- flag = True
- while op in expr_list and flag:
- i = expr_list.index(op)
- if i+1 < len(expr_list) and expr_list[i+1] == '_':
- if i-2 < len(expr_list) and expr_list[i+2] !=
'_':
- expr_list.insert(i+3, [int(0), '-',
expr_list[i+2]])
- del expr_list[i+1:i+3]
- else: raise IllegalSyntaxException('should be
programmed out: - -')
- elif expr_list[i] == op:
- if i > 0 and i+1 < len(expr_list) and not
_is_operator(expr_list[i-1]) and not _is_operator(expr_list[i+1]):
- if len(expr_list) > 3:
- expr_list.insert(i+2,
[expr_list[i-1], op, expr_list[i+1]])
- del expr_list[i-1:i+2]
- else: flag = False
- else: raise IllegalSyntaxException('operators
missing arguments: '+str(expr_list[i]))
- # end while #
- for symbol in expr_list:
- if type(symbol) == type(list()):
_rectify_operators_in_expr_list(symbol)
+#########################################################
+## Complex mathematical functions
+#########################################################
+
+def _complex_abs_v(*args):
+ ''' Compute the abs of a complex number or vector. '''
+ sum = 0
+ for arg in args:
+ if _is_list(arg): sum = sum + _complex_abs_v(*arg)
+ else: sum = sum + \
+ verify_complex(arg).real*verify_complex(arg).real + \
+ verify_complex(arg).imag*verify_complex(arg).imag
+ return verify_number(cmath.sqrt(sum))
+def _complex_real_v(*args):
+ ''' Get the real part of a number or vector. '''
+ result = list()
+ for arg in args:
+ if _is_list(arg): result.append(_complex_real_v(*arg))
+ else: result.append(verify_number(verify_complex(arg).real))
+ return result
-def _convert_expr_str_to_list(expr_str, expr_list,opening_bracket_type = None):
- ''' Recursivly interate through the expression string. Pick out
varibles, numbers, and operands.
- This operation will pay attention to matching bracket pairs and add
nested loops for each pair.
- Raise an exception on failue (syntax and bracket problems). '''
- prev_symbol = None
- while True:
- symbol, expr_str = _get_next_symbol_from_expr(expr_str)
- if symbol == None:
- if opening_bracket_type != None: raise
MissingBracketException(_get_oposing_bracket(opening_bracket_type))
- return ''
- elif _is_close_bracket(symbol):
- if opening_bracket_type == None:
- raise
MissingBracketException(_get_oposing_bracket(symbol))
- elif opening_bracket_type !=
_get_oposing_bracket(symbol):
- raise
MissingBracketException(_get_oposing_bracket(opening_bracket_type))
- return expr_str
- elif _is_open_bracket(symbol):
- sub_list = list()
- expr_list.append(sub_list)
- expr_str = _convert_expr_str_to_list(expr_str,
sub_list, symbol)
- else:
- if symbol == '-' and prev_symbol == None:
- expr_list.append(int(0))
- expr_list.append('-')
- symbol = '_' #underscore indicates a
negative sign
- elif symbol == '-' and prev_symbol == '_': raise
IllegalSyntaxException('- -')
- elif symbol == '-' and _is_operator(prev_symbol):
- symbol = '_' #underscore indicates a
negative sign
- expr_list.append(symbol)
- else: expr_list.append(symbol)
- prev_symbol = symbol
- if len(expr_list) > 1 and not (_is_operator(expr_list[-2]) or
expr_list[-2] == '_') and not _is_operator(expr_list[-1]):
- raise IllegalSyntaxException(str(expr_list[-2])+'
'+str(expr_list[-1]))
+def _complex_imag_v(*args):
+ ''' Get the imaginary part of a number or vector. '''
+ result = list()
+ for arg in args:
+ if _is_list(arg): result.append(_complex_imag_v(*arg))
+ else: result.append(verify_number(verify_complex(arg).imag))
+ return result
-def _get_next_symbol_from_expr(expr_str):
- ''' Remove the first symbol from an expression string. The symbol
can be a number,
- operator, or bracket. Raise an exception on failue (invalid symbols).
'''
- # remove leading whitespace #
- while expr_str != '' and _is_whitespace(expr_str[0]): expr_str =
expr_str[1:]
- symbol = ''
- if expr_str == '': return None,None
- elif _is_operator(expr_str[0]) or _is_bracket(expr_str[0]): return
expr_str[0],expr_str[1:]
- elif _is_numeric(expr_str[0]):
- while expr_str != '' and not _is_terminating_char(expr_str[0]):
- if _is_floating_point(expr_str[0]) and len(expr_str) ==
1: #e is the last char?
- raise IllegalSymbolException(symbol+expr_str)
- elif _is_floating_point(expr_str[0]) and expr_str[1] ==
'-':#e is followed by a minus?
- symbol = symbol + expr_str[0]
- expr_str = expr_str[1:]
- symbol = symbol + expr_str[0]
- expr_str = expr_str[1:]
- try: return number(symbol),expr_str
- except: raise IllegalSymbolException(symbol)
- else: raise IllegalSymbolException(expr_str[0])
+def _complex_conj_v(*args):
+ ''' Get the complex conjugate of a number or vector. '''
+ result = list()
+ for arg in args:
+ if _is_list(arg): result.append(_complex_conj_v(*arg))
+ else:
result.append(verify_number(verify_complex(arg).conjugate()))
+ return result
+
+def _complex_arg(arg):
+ ''' Get the angle of the complex number (radians). '''
+ return verify_number(math.atan2(verify_complex(arg).imag,
verify_complex(arg).real))
+
+def _handle_filter(filter, *filter_args):
+ ''' Pass the filter arguments to the filter function.
+ Format the arguments to floats and ints.
+ Raise errors for invalid filter arguments.
+ Return a list of taps. '''
+ filter_args = list(filter_args) #make filter args a list
+ filter_props = { # map a filter to filter argument properties:
+ # (allowed lengths of args), (args that are floats), (args that
are ints)
+ gr.firdes.band_pass : ((5, 6, 7), (0, 1, 2, 3, 4, 6), ()),
+ gr.firdes.band_reject : ((5, 6, 7), (0, 1, 2, 3, 4, 6), ()),
+ gr.firdes.complex_band_pass : ((5, 6, 7), (0, 1, 2, 3, 4, 6),
()),
+
+ gr.firdes.high_pass : ((4, 5, 6), (0, 1, 2, 3, 5), ()),
+ gr.firdes.low_pass : ((4, 5, 6), (0, 1, 2, 3, 5), ()),
+
+ gr.firdes.gaussian : ((4,), (0, 1, 2), (3,)),
+ gr.firdes.hilbert : ((1, 2, 3), (2,), (0,)),
+ gr.firdes.root_raised_cosine : ((5,), (0, 1, 2, 3), (4,)),
+ gr.firdes.window : ((3,), (2,), (1,)),
+ }
+ arg_lens, floats, ints = filter_props[filter]
+ # handle the filter arguments #
+ if len(filter_args) not in arg_lens: raise SyntaxError('Invalid number
of arguments for "%s".'%filter)
+ for i,arg in enumerate(filter_args):
+ if i in floats: filter_args[i] = verify_float(arg)
+ if i in ints: filter_args[i] = verify_int(arg)
+ taps = list(filter(*filter_args)) #the taps must be a list
+ return taps
+
+_FUNCTIONS = {
+ 'pow' : lambda b, p: verify_number(b**p), #complex power
+ 'sqrt' : cmath.sqrt,
+
+ 'abs' : _complex_abs_v,
+ 'mag' : _complex_abs_v,
+ 'real' : _complex_real_v,
+ 'imag' : _complex_imag_v,
+ 'conj' : _complex_conj_v,
+ 'arg' : _complex_arg,
-def _is_terminating_char(char_str): return _is_operator(char_str) or
_is_bracket(char_str) or _is_whitespace(char_str)
+ 'sin' : lambda z: verify_number(cmath.sin(z)),
+ 'cos' : lambda z: verify_number(cmath.cos(z)),
+ 'tan' : lambda z: verify_number(cmath.tan(z)),
+
+ 'asin' : lambda z: verify_number(cmath.asin(z)),
+ 'acos' : lambda z: verify_number(cmath.acos(z)),
+ 'atan' : lambda z: verify_number(cmath.atan(z)),
+
+ 'sinh' : lambda z: verify_number(cmath.sinh(z)), #hyperbolics
+ 'cosh' : lambda z: verify_number(cmath.cosh(z)),
+ 'tanh' : lambda z: verify_number(cmath.tanh(z)),
+
+ 'asinh' : lambda z: verify_number(cmath.asinh(z)),
+ 'acosh' : lambda z: verify_number(cmath.acosh(z)),
+ 'atanh' : lambda z: verify_number(cmath.atanh(z)),
+
+ 'log10' : lambda z: verify_number(cmath.log10(z)),
+ 'log' : lambda *z: verify_number(cmath.log(*z)), #optional 2nd
argument as base
+ 'ln' : lambda z: cmath.log(z), #only allow natural log
+ 'exp' : lambda z: verify_number(cmath.exp(z)),
+
+ 'band_pass' : lambda *args: _handle_filter(gr.firdes.band_pass, *args),
#taps generators
+ 'band_reject' : lambda *args: _handle_filter(gr.firdes.band_reject,
*args),
+ 'complex_band_pass' : lambda *args:
_handle_filter(gr.firdes.complex_band_pass, *args),
+ 'gaussian' : lambda *args: _handle_filter(gr.firdes.gaussian, *args),
+ 'high_pass' : lambda *args: _handle_filter(gr.firdes.high_pass, *args),
+ 'hilbert' : lambda *args: _handle_filter(gr.firdes.hilbert, *args),
+ 'low_pass' : lambda *args: _handle_filter(gr.firdes.low_pass, *args),
+ 'root_raised_cosine' : lambda *args:
_handle_filter(gr.firdes.root_raised_cosine, *args),
+ 'window' : lambda *args: _handle_filter(gr.firdes.window, *args),
+
+ 'floor' : lambda f: math.floor(verify_float(f)),
+ 'ceil' : lambda f: math.ceil(verify_float(f)),
+ 'radians' : lambda f: math.radians(verify_float(f)),
+ 'degrees' : lambda f: math.degrees(verify_float(f)),
+ 'mod' : lambda f1, f2: math.fmod(verify_float(f1), verify_float(f2)),
+ 'atan2' : lambda y, x: math.atan2(verify_float(y), verify_float(x)),
+ 'sgn' : lambda f: abs(verify_float(f))/verify_float(f),
+ 'min' : lambda *f: verify_number(min(*map(verify_float, f))),
+ 'max' : lambda *f: verify_number(max(*map(verify_float, f))),
+}
+#########################################################
+## Boolean tests for special characters and symbols
+#########################################################
+
+def _is_list(symbol): return type(symbol) == type(list())
+
+def _is_function(symbol): return symbol in _FUNCTIONS.keys()
+
+def _is_constant(symbol): return symbol in _CONSTANTS.keys()
+
+def _is_number(symbol):
+ try: verify_number(symbol)
+ except: return False
+ return True
+
+def _is_terminating_char(char_str):
+ return \
+ _is_operator(char_str) or \
+ _is_bracket(char_str) or \
+ _is_whitespace(char_str) or \
+ _is_comma(char_str)
+
+def _is_comma(char_str): return char_str == ','
+
def _is_whitespace(char_str): return char_str in _WHITESPACE
-def _is_floating_point(char_str): return char_str in ('e', 'E')
+def _is_operator(char_str): return char_str in _ORDER_OF_OPS
-def _is_operator(char_str): return char_str in ('+', '-', '*', '/', '^')
-
-def _is_numeric(char_str): return char_str.isdigit() or char_str in ('.')
-
def _is_bracket(char_str): return _is_open_bracket(char_str) or
_is_close_bracket(char_str)
def _is_open_bracket(char_str): return char_str in ('(', '[', '{')
def _is_close_bracket(char_str): return char_str in (')', ']', '}')
-def _get_oposing_bracket(bracket): return {
-
'(' : ')',
-
')' : '(',
-
'[' : ']',
-
']' : '[',
-
'{' : '}',
-
'}' : '{',
-
None : None
-
}[bracket]
+def _get_oposing_bracket(bracket):
+ return {
+ '(' : ')',
+ ')' : '(',
+ '[' : ']',
+ ']' : '[',
+ '{' : '}',
+ '}' : '{',
+ '' : ''
+ }[bracket]
+
+#########################################################
+## Symbolic operations
+#########################################################
+
+def _handle_operation(op, arg1, arg2):
+ ''' Handle basic operations specified in the order of operations.
+ All calculations are complex, some vector operations are
supported.
+ Return the number/list representing the result. '''
+ if op not in _ORDER_OF_OPS: raise NameError('Unknown operator:
"%s".'%(op))
+ arg1_is_list = _is_list(arg1)
+ arg2_is_list = _is_list(arg2)
+ # addition and subtraction of vectors #
+ if arg1_is_list and arg2_is_list and \
+ len(arg1) == len(arg2) and op in ('+', '-'):
+ result = list()
+ for i,arg1_elem in enumerate(arg1):
+ arg2_elem = arg2[i]
+ result.append(_handle_operation(op, arg1_elem,
arg2_elem))
+ return result
+ # scalar times vector #
+ if not arg1_is_list and arg2_is_list and op == '*':
+ return map(lambda elem: _handle_operation(op, arg1, elem), arg2)
+ # vector times/divided scalar #
+ if arg1_is_list and not arg2_is_list and op in ('*', '/'):
+ return map(lambda elem: _handle_operation(op, elem, arg2), arg1)
+ # non vectored operations #
+ if not arg1_is_list and not arg2_is_list and \
+ _is_number(arg1) and _is_number(arg2): #non vector operations
+ if op == '^-':
+ return verify_number(arg1**(-1*arg2))
+ elif op == '^':
+ return verify_number(arg1**arg2)
+ elif op == '*':
+ return verify_number(arg1 * arg2)
+ elif op == '/':
+ try: return verify_number(verify_complex(arg1) /
verify_complex(arg2)) #make sure to use non-integer division
+ except ZeroDivisionError: raise
ZeroDivisionError('Cannot divide "%s" by 0.'%(arg1,))
+ elif op == '+':
+ return verify_number(arg1 + arg2)
+ elif op == '-':
+ return verify_number(arg1 - arg2)
+ raise ArithmeticError('Operation of "%s" cannot be performed on "%s",
"%s".'%(op, arg1, arg2))
+
+#########################################################
+## Evaluate the expression string
+#########################################################
-class IllegalSymbolException(Exception):
- def __str__(self): return 'Symbol not recognized:
'+Exception.__str__(self)
-
-class MissingBracketException(Exception):
- def __str__(self): return 'Expected bracket: '+Exception.__str__(self)
-
-class IllegalSyntaxException(Exception):
- def __str__(self): return 'Bad Syntax: '+Exception.__str__(self)
-
+def _separate_expression(expr_str):
+ ''' Separate the string into a list of elements.
+ Use only the lower-cased version of the expression string.
+ Terminating characters will dictate the separations.
+ Terminating characters will be included in the list.
+ Whitespace characters will be removed from the list.
+ Return a list of strings representing each element. '''
+ elements = list()
+ element = ''
+ for char_str in expr_str.lower():
+ if _is_terminating_char(char_str):
+ elements.append(element)
+ elements.append(char_str)
+ element = ''
+ else: element = element + char_str
+ elements.append(element) #append the remaining characters
+ # remove whitespace #
+ return filter(lambda elem: not _is_whitespace(elem), elements)
+
+def _rectify_operators(elements):
+ ''' Rectify the functionality of the minus sign (-),
+ and the plus sign (+) (in the floating point format).
+ Determine if the minus sign is used as a negative,
+ and replace it with -1* or ^-. Also rejoin floating point
formats.
+ Return the list of elements with minus and plus used only as
operators. '''
+ rectified_elements = list()
+ for i,element in enumerate(elements):
+ if i == 0 and element == '-': #first element was a minus
+ rectified_elements.extend(['-1', '*'])
+ elif i != 0 and element == '-' and i+1 != len(elements):
#minus found!
+ if _is_operator(elements[i-1]) or \
+ _is_open_bracket(elements[i-1]) or \
+ _is_comma(elements[i-1]): #negative implied
+ if elements[i-1] != '^':
rectified_elements.extend(['-1', '*'])
+ else: #^ proceeds - in order of ops, remove the
^ and append ^- as a special operator
+ rectified_elements.pop() #remove
last element
+ rectified_elements.append('^-')
+ elif elements[i-1][-1] == 'e' and
_is_number(elements[i-1][0:-1]): #found a -floating point format
+ f = rectified_elements.pop() #remove last
element
+ elements[i+1] = "%s-%s"%(f, elements[i+1])
+ else: rectified_elements.append(element)
#regular minus
+ elif i != 0 and element == '+' and i+1 != len(elements) and \
+ elements[i-1][-1] == 'e' and
_is_number(elements[i-1][0:-1]): #found a +floating point format
+ f = rectified_elements.pop() #remove last element
+ elements[i+1] = "%s+%s"%(f, elements[i+1])
+ else: rectified_elements.append(element) #non minus
+ return rectified_elements
+
+def _nest_elements(elements, open_bracket=''):
+ ''' Read through the elements, recursing at each open bracket.
+ Put elements inside bracket pairs into a list.
+ Raise an Error if bracket pairs do not match.
+ Return the nested elements. '''
+ nested_elements = list()
+ while elements: #continue until elements is empty
+ element = elements.pop(0)
+ if _is_open_bracket(element):
+ nested_elements.append(_nest_elements(elements,
element))
+ elif _is_close_bracket(element):
+ if element != _get_oposing_bracket(open_bracket):
#matching open/close brackets?
+ raise SyntaxError('Expected "%s", but found
"%s".'%(_get_oposing_bracket(open_bracket), element))
+ return nested_elements
+ else: nested_elements.append(element)
+ if open_bracket: #any unclosed bracket pairs?
+ raise SyntaxError('Expected "%s", but found
nothing.'%(_get_oposing_bracket(open_bracket),))
+ return nested_elements
+
+def _simplify_nested_elements(nested_elements):
+ ''' Read through the nested elements and simplify expressions.
+ Work according to the order of operations specified above.
+ Recurse at each list, substitute constans, parse numbers,
handle functions.
+ Make comma separated values into lists (represent vectors).
+ Return the final value of the expression. '''
+ for i,element in enumerate(nested_elements):
+ # simplify the nested lists #
+ if _is_list(element): nested_elements[i] =
_simplify_nested_elements(element)
+ # substitute in values for constants #
+ elif _is_constant(element): nested_elements[i] =
_CONSTANTS[element]
+ # substitute in values for numbers #
+ elif _is_number(element): nested_elements[i] =
verify_number(element)
+ # handle functions #
+ for function in _FUNCTIONS.keys():
+ while function in nested_elements:
+ i = nested_elements.index(function)
+ if i+1 == len(nested_elements): raise
SyntaxError('Function "%s" has no arguments.'%(function))
+ args = nested_elements[i+1]
+ if not _is_list(args): args = [args]
+ try: ans = _FUNCTIONS[function](*args)
+ except Exception, e:
+ raise ValueError('Function "%s" with arguments
"%s" failed.\n\t%s'%(function, nested_elements[i+1], e))
+ if not _is_list(ans): ans = [ans] # ans must be a
list
+ ans_with_commas = list()
+ for an in ans: ans_with_commas.extend([an, ','])
+ nested_elements = nested_elements[0:i] +
ans_with_commas[0:-1] + nested_elements[i+2:]
+ # simplify operations #
+ for operator in _ORDER_OF_OPS:
+ while operator in nested_elements:
+ i = nested_elements.index(operator)
+ arg1 = arg2 = None
+ if i > 0: arg1 = nested_elements[i-1]
+ if i+1 < len(nested_elements): arg2 =
nested_elements[i+1]
+ #raise error if arg1 or arg2 is None
+ if arg1 == None or arg2 == None: raise
SyntaxError('Operator "%s" is missing argument.'%(operator))
+ ans = _handle_operation(operator, arg1, arg2)
+ if not _is_list(ans): ans = [ans] # ans must be a
list
+ ans_with_commas = list()
+ for an in ans: ans_with_commas.extend([an, ','])
+ nested_elements = nested_elements[0:i-1] +
ans_with_commas[0:-1] + nested_elements[i+2:]
+ # convert comma separated elements into a list #
+ vector = list()
+ last_element = None
+ for element in nested_elements:
+ if not _is_comma(element):
+ if not _is_list(element) and not _is_number(element)
and element not in _CONSTANTS.values():
+ raise NameError('Unknown symbol
"%s".'%(element))
+ if last_element and not _is_comma(last_element):
+ raise SyntaxError('Expected comma, but found
"%s".'%(element))
+ vector.append(element)
+ elif _is_comma(element) and _is_comma(last_element):
+ raise SyntaxError('Commas must be separated by
non-commas.')
+ last_element = element
+ if len(vector) == 1 and not _is_comma(last_element): return vector[0]
#return single number
+ return vector #otherwise return vector
+
+def eval_expr(expr_str):
+ ''' Evaluate a mathematical expression string with numbers, and
operators.
+ Raise an exception on failue.
+ Return a list containing the parsed expression. '''
+ separated_elements = _separate_expression(expr_str)
+ #print "separated", separated_elements
+ rectified_elements = _rectify_operators(separated_elements)
+ #print "rectified", rectified_elements
+ nested_elements = _nest_elements(rectified_elements)
+ #print "nested", nested_elements
+ simplified = _simplify_nested_elements(nested_elements)
+ return simplified
+
if __name__ == '__main__':
- print eval_expr('-4j + -(2)^2 ')
+ ''' Evaluate expressions passed by argv(1). '''
+ import sys
+ if len(sys.argv) > 1: print eval_expr(sys.argv[1])
+ else: print "No expressions passed!"
\ No newline at end of file
Modified: grc/trunk/src/Messages.py
===================================================================
--- grc/trunk/src/Messages.py 2007-06-07 05:22:11 UTC (rev 5729)
+++ grc/trunk/src/Messages.py 2007-06-07 05:57:25 UTC (rev 5730)
@@ -48,8 +48,10 @@
------------------
Loading Flow Graph ------------------\n""")
send('Trying to load: "%s"'%file_path + '\n')
-def send_end_load(errors):
- if errors: send('>>> Errors:\n%s'%errors)
+def send_error_load(error):
+ send('>>> Error: %s\n'%error)
+
+def send_end_load():
send(">>> Done\n")
def send_fail_load(error):
Modified: grc/trunk/src/Preferences.py
===================================================================
--- grc/trunk/src/Preferences.py 2007-06-07 05:22:11 UTC (rev 5729)
+++ grc/trunk/src/Preferences.py 2007-06-07 05:57:25 UTC (rev 5730)
@@ -118,7 +118,6 @@
categories = find_data(preferences, 'categories')
for i,category in enumerate(categories):
category = find_data([category], 'category')
- title = find_data(category, 'title')
prefs = find_data(category, 'prefs')
for j,pref in enumerate(prefs):
pref = find_data([pref], 'pref')
Deleted: grc/trunk/src/Run.py
Modified: grc/trunk/src/SignalBlockDefs/Conversions.py
===================================================================
--- grc/trunk/src/SignalBlockDefs/Conversions.py 2007-06-07 05:22:11 UTC
(rev 5729)
+++ grc/trunk/src/SignalBlockDefs/Conversions.py 2007-06-07 05:57:25 UTC
(rev 5730)
@@ -27,14 +27,17 @@
from SignalBlockConstants import all_choices,all_vector_choices
from Constants import MAX_NUM_SOCKETS
-def ComplexComponents(sb):
- sb.add_input_socket('in', Complex())
- sb.add_output_socket('out', Float())
+def ComplexComponents(sb):
+ vlen = Int(1, min=1)
+ sb.add_input_socket('in', Complex(), vlen=vlen)
+ sb.add_output_socket('out', Float(), vlen=vlen)
sb.add_param('Output Type', Enum([('Real', gr.complex_to_real),
('Imaginary', gr.complex_to_imag),
('Magnitude', gr.complex_to_mag),
+
('Magnitude Squared', gr.complex_to_mag_squared),
('Phase Angle', gr.complex_to_arg)]), type=True)
- return sb, lambda fg, type: type.parse()(1)
+ sb.add_param('Vector Length', vlen)
+ return sb, lambda fg, type, vlen: type.parse()(vlen.parse())
def ComplexConjugate(sb):
sb.add_input_socket('in', Complex())
@@ -48,12 +51,14 @@
sb.set_docs('''The imaginary input socket (jin) can be unconnected.''')
return sb, lambda fg: gr.float_to_complex()
-def ComplexToFloat(sb):
- sb.add_output_socket('out', Float())
- sb.add_output_socket('jout', Float(), optional=True)
+def ComplexToFloat(sb):
+ vlen = Int(1, min=1)
+ sb.add_output_socket('out', Float(), vlen=vlen)
+ sb.add_output_socket('jout', Float(), vlen=vlen, optional=True)
sb.add_input_socket('in', Complex())
+ sb.add_param('Vector Length', vlen)
sb.set_docs('''The imaginary output socket (jout) can be
unconnected.''')
- return sb, lambda fg: gr.complex_to_float()
+ return sb, lambda fg, vlen: gr.complex_to_float(vlen.parse())
def ShortToFloat(sb):
sb.add_input_socket('in', Short())
@@ -158,7 +163,7 @@
sb.add_param('Num Inputs', Int(3, min=1, max=MAX_NUM_SOCKETS),
show_label=False,
input_sockets_controller=True)
sb.add_param('Vector Length', vlen)
- sb.set_docs('''2 <= Num Inputs <= %d'''%MAX_NUM_SOCKETS)
+ sb.set_docs('''1 <= Num Inputs <= %d'''%MAX_NUM_SOCKETS)
return sb, lambda fg, type, num_inputs, vlen:
gr.interleave(type.parse().get_num_bytes()*vlen.parse())
def Deinterleave(sb):
@@ -170,7 +175,7 @@
sb.add_param('Num Outputs', Int(3, min=1, max=MAX_NUM_SOCKETS),
show_label=False,
output_sockets_controller=True)
sb.add_param('Vector Length', vlen)
- sb.set_docs('''2 <= Num Outputs <= %d'''%MAX_NUM_SOCKETS)
+ sb.set_docs('''1 <= Num Outputs <= %d'''%MAX_NUM_SOCKETS)
return sb, lambda fg, type, num_outputs, vlen:
gr.deinterleave(type.parse().get_num_bytes()*vlen.parse())
def StreamsToStream(sb):
@@ -185,7 +190,7 @@
sb.add_param('Vector Length', vlen)
sb.set_docs('''\
Interleave N streams into a single stream.
-2 <= Num Streams <= %d'''%MAX_NUM_SOCKETS)
+1 <= Num Streams <= %d'''%MAX_NUM_SOCKETS)
return sb, lambda fg, type, num_streams, vlen:
fcn(type.parse().get_num_bytes()*vlen.parse(), num_streams.parse())
def StreamToStreams(sb):
@@ -200,7 +205,7 @@
sb.add_param('Vector Length', vlen)
sb.set_docs('''\
Deinterleave a stream into N streams.
-2 <= Num Streams <= %d'''%MAX_NUM_SOCKETS)
+1 <= Num Streams <= %d'''%MAX_NUM_SOCKETS)
return sb, lambda fg, type, num_streams, vlen:
fcn(type.parse().get_num_bytes()*vlen.parse(), num_streams.parse())
def StreamsToVector(sb):
Modified: grc/trunk/src/SignalBlockDefs/Filters.py
===================================================================
--- grc/trunk/src/SignalBlockDefs/Filters.py 2007-06-07 05:22:11 UTC (rev
5729)
+++ grc/trunk/src/SignalBlockDefs/Filters.py 2007-06-07 05:57:25 UTC (rev
5730)
@@ -123,6 +123,23 @@
sb.add_param('FB Taps', FloatVector())
return sb, lambda fg, fftaps, fbtaps: fcn(fftaps.parse(),
fbtaps.parse())
+def FilterDelay(sb):
+ fcn = gr.filter_delay_fc
+ sb.add_input_socket('in1', Float())
+ sb.add_input_socket('in2', Float(), optional=True)
+ sb.add_output_socket('out', Complex())
+ sb.add_param('Taps', FloatVector())
+ sb.set_docs('''\
+The block takes one or two float stream and outputs a complex stream. \
+If only one float stream is input, \
+the real output is a delayed version of this input and the imaginary output is
the filtered output. \
+If two floats are connected to the input, \
+then the real output is the delayed version of the first input, \
+the imaginary output is the filtered output. \
+The delay in the real path accounts for the group delay introduced by the
filter in the imaginary path.\
+''')
+ return sb, lambda fg, taps: fcn(taps.parse())
+
###########################################################################
# Special filters using taps generators
###########################################################################
@@ -140,7 +157,7 @@
('Blackman', gr.firdes.WIN_BLACKMAN),
('Rectangular', gr.firdes.WIN_RECTANGULAR),
('Kaiser', gr.firdes.WIN_KAISER),
- ]
+]
filter_choices = [
('FFT: Float->Float', (gr.fft_filter_fff, Float(), Float())),
('FIR: Complex->Complex', (gr.fir_filter_ccf, Complex(), Complex())),
@@ -149,7 +166,7 @@
('Interp FIR: Complex->Complex', (gr.interp_fir_filter_ccf, Complex(),
Complex())),
('Interp FIR: Float->Float', (gr.interp_fir_filter_fff, Float(),
Float())),
('Interp FIR: Float->Short', (gr.interp_fir_filter_fsf, Float(),
Short())),
- ]
+]
def LowPassFilter(sb):
taps_maker = gr.firdes.low_pass
@@ -246,6 +263,15 @@
sb.add_param('Num Taps', Int(10, min=0))
return sb, lambda fg, num_taps: fcn(num_taps.parse())
+def Goertzel(sb):
+ fcn = gr.goertzel_fc
+ sb.add_input_socket('in', Float())
+ sb.add_output_socket('out', Complex())
+ sb.add_param('Rate', Int())
+ sb.add_param('Length', Int())
+ sb.add_param('Frequency', Float())
+ return sb, lambda fg, rate, len, freq:
fcn(rate.parse(),len.parse(),freq.parse())
+
def PowerSquelch(sb):
fcn = gr.simple_squelch_cc
sb.add_input_socket('in', Complex())
@@ -310,17 +336,6 @@
sb.set_docs('''Constant Modulus Adaptive Filter.''')
return sb, lambda fg, num_taps, mod, mu: fcn(num_taps.parse(),
mod.parse(), mu.parse())
-def CostasLoop(sb):
- fcn = gr.costas_loop_cc
- sb.add_input_socket('in', Complex())
- sb.add_output_socket('out', Complex())
- sb.add_param('Alpha', Float())
- sb.add_param('Beta', Float())
- sb.add_param('Max Freq', Float())
- sb.add_param('Min Freq', Float())
- sb.add_param('Order', Enum([('2', 2), ('4', 4)]))
- return sb, lambda fg, *args: fcn(*map(lambda a: a.parse(), args))
-
def ClockRecovery(sb):
type = Enum([('Complex', (gr.clock_recovery_mm_cc, Complex())),
('Float', (gr.clock_recovery_mm_ff,
Float())),])
Modified: grc/trunk/src/SignalBlockDefs/GraphicalSinks.py
===================================================================
--- grc/trunk/src/SignalBlockDefs/GraphicalSinks.py 2007-06-07 05:22:11 UTC
(rev 5729)
+++ grc/trunk/src/SignalBlockDefs/GraphicalSinks.py 2007-06-07 05:57:25 UTC
(rev 5730)
@@ -86,7 +86,6 @@
baseband_freq=baseband_freq.parse(),
fft_rate=fft_rate.parse())
block.set_average(average) #set the
average option outside the contructor
fg.add_window(block.win, waterfall_display_priority,
title.parse())
- fg.add_callback(block.set_sample_rate, samp_rate)
fg.add_callback(block.set_baseband_freq, baseband_freq)
th = gr.throttle(type.parse()[1].get_num_bytes(),
samp_rate.parse())
fg.connect(th, block)
@@ -115,7 +114,6 @@
sample_rate=samp_rate.parse(),
frame_decim=frame_decim.parse(),
v_scale=v_scale, t_scale=t_scale.parse())
fg.add_window(block.win, scope_display_priority, title.parse())
- fg.add_callback(block.set_sample_rate, samp_rate)
th = gr.throttle(type.parse()[1].get_num_bytes(),
samp_rate.parse())
fg.connect(th, block)
return th
@@ -140,7 +138,6 @@
elif marker == 1: block.win.set_format_dot()
elif marker == 2: block.win.set_format_line()
fg.add_window(block.win, constellation_display_pritority,
title.parse())
- fg.add_callback(block.set_sample_rate, samp_rate)
th = gr.throttle(Complex().get_num_bytes(), samp_rate.parse())
fg.connect(th, block)
return th
Modified: grc/trunk/src/SignalBlockDefs/Misc.py
===================================================================
--- grc/trunk/src/SignalBlockDefs/Misc.py 2007-06-07 05:22:11 UTC (rev
5729)
+++ grc/trunk/src/SignalBlockDefs/Misc.py 2007-06-07 05:57:25 UTC (rev
5730)
@@ -25,6 +25,7 @@
from DataType import *
from gnuradio import gr
from SignalBlockConstants import default_samp_rate,all_choices
+from Constants import MAX_NUM_SOCKETS
def Throttle(sb):
fcn = gr.throttle
@@ -35,7 +36,7 @@
sb.add_param('Type', type, False, type=True)
sb.add_param('Sampling Rate', Float(default_samp_rate))
sb.add_param('Vector Length', vlen)
- sb.set_docs('''Connect a throttle to control the flow rate of data.''')
+ sb.set_docs('''Connect a throttle to control the actual rate of the
data.''')
return sb, lambda fg, type, samp_rate, vlen:
fcn(type.parse().get_num_bytes()*vlen.parse(), samp_rate.parse())
def Head(sb):
@@ -47,9 +48,21 @@
sb.add_param('Type', type, False, type=True)
sb.add_param('Num Items', Int(1000, min=0))
sb.add_param('Vector Length', vlen)
- sb.set_docs('''Copies the first num items to the output then signals
done.''')
- return sb, lambda fg, type, samp_rate, vlen:
fcn(type.parse().get_num_bytes()*vlen.parse(), samp_rate.parse())
+ sb.set_docs('''Copies the first num items to the output then stops.''')
+ return sb, lambda fg, type, num_items, vlen:
fcn(type.parse().get_num_bytes()*vlen.parse(), num_items.parse())
+def SkipHead(sb):
+ fcn = gr.skiphead
+ type = Enum(all_choices, 1)
+ vlen = Int(1, min=1)
+ sb.add_input_socket('in', Variable(type), vlen=vlen)
+ sb.add_output_socket('out', Variable(type), vlen=vlen)
+ sb.add_param('Type', type, False, type=True)
+ sb.add_param('Num Items', Int(1000, min=0))
+ sb.add_param('Vector Length', vlen)
+ sb.set_docs('''Skips the first num items, and then copies input to
output.''')
+ return sb, lambda fg, type, num_items, vlen:
fcn(type.parse().get_num_bytes()*vlen.parse(), num_items.parse())
+
def RMS(sb):
type = Enum([('Complex', (gr.rms_cf, Complex())),
('Float', (gr.rms_ff, Float())),
@@ -72,5 +85,3 @@
def Note(sb):
sb.add_param('Note', String(''))
return sb, lambda *args: None
-
-
\ No newline at end of file
Modified: grc/trunk/src/SignalBlockDefs/Modulators.py
===================================================================
--- grc/trunk/src/SignalBlockDefs/Modulators.py 2007-06-07 05:22:11 UTC (rev
5729)
+++ grc/trunk/src/SignalBlockDefs/Modulators.py 2007-06-07 05:57:25 UTC (rev
5730)
@@ -192,4 +192,50 @@
sb.set_docs('''Mu is the fractional delay between 0 and 1''')
return sb, lambda fg, *args: fcn(fg, *map(lambda a: a.parse(), args))
+###########################################################################
+# Phase Locked Loops
+###########################################################################
+
+def CostasLoop(sb):
+ fcn = gr.costas_loop_cc
+ sb.add_input_socket('in', Complex())
+ sb.add_output_socket('bout', Complex())
+ sb.add_output_socket('nout', Complex(), optional=True)
+ sb.add_param('Alpha', Float())
+ sb.add_param('Beta', Float())
+ sb.add_param('Max Freq', Float())
+ sb.add_param('Min Freq', Float())
+ sb.add_param('Order', Enum([('2', 2), ('4', 4)]))
+ sb.set_docs('''\
+Carrier tracking PLL for QPSK input. \
+The Costas loop has two output streams: \
+"bout" is the baseband I stream, "nout" is the normalized frequency of the
loop. \
+The "nout" may be left open. ''')
+ return sb, lambda fg, *args: fcn(*map(lambda a: a.parse(), args))
+
+def PLL(sb):
+ type = Enum([ #fcn, output
type
+ ('Carrier Tracking', (gr.pll_carriertracking_cc, Complex())),
+ ('Freq Det', (gr.pll_freqdet_cf, Float())),
+ ('Ref Out', (gr.pll_refout_cc, Complex())),
+ ])
+ sb.add_input_socket('in', Complex())
+ sb.add_output_socket('out', Variable(type, index=1))
+ sb.add_param('Type', type, type=True)
+ sb.add_param('Alpha', Float())
+ sb.add_param('Beta', Float())
+ sb.add_param('Max Freq', Float())
+ sb.add_param('Min Freq', Float())
+ sb.set_docs('''\
+All settings max_freq and min_freq are in terms of radians per sample, NOT
HERTZ. \
+Alpha is the phase gain (first order, units of radians per radian), and \
+beta is the frequency gain (second order, units of radians per sample per
radian).
+
+Carrier Tracking - This PLL locks onto a [possibly noisy] reference carrier on
the input and outputs that signal, downconverted to DC.
+
+Freq Det - This PLL locks onto a [possibly noisy] reference carrier on the
input and outputs an estimate of that frequency in radians per sample.
+
+Ref Out - This PLL locks onto a [possibly noisy] reference carrier on the
input and outputs a clean version which is phase and frequency aligned to it.
+''')
+ return sb, lambda fg, type, *args: type.parse()[0](*map(lambda a:
a.parse(), args))
\ No newline at end of file
Modified: grc/trunk/src/SignalBlockDefs/SignalBlockConstants.py
===================================================================
--- grc/trunk/src/SignalBlockDefs/SignalBlockConstants.py 2007-06-07
05:22:11 UTC (rev 5729)
+++ grc/trunk/src/SignalBlockDefs/SignalBlockConstants.py 2007-06-07
05:57:25 UTC (rev 5730)
@@ -29,14 +29,16 @@
('Float', Float()),
('Int', Int()),
('Short', Short()),
- ('Byte', Byte()),]
+ ('Byte', Byte()),
+]
all_vector_choices = [
('Complex Vector', (ComplexVector(), Complex())),
('Float Vector', (FloatVector(), Float())),
('Int Vector', (IntVector(), Int())),
('Short Vector', (ShortVector(), Short())),
- ('Byte Vector', (ByteVector(), Byte()))]
+ ('Byte Vector', (ByteVector(), Byte()))
+]
default_samp_rate = '$samp_rate'
@@ -46,7 +48,8 @@
('24KHz', int(24e3)),
('32KHz', int(32e3)),
('44.1KHz', int(44.1e3)),
- ('48KHz', int(48e3)),]
+ ('48KHz', int(48e3)),
+]
default_audio_rate_index = 3
default_udp_port = 3456
Modified: grc/trunk/src/SignalBlockDefs/SignalBlockTree.py
===================================================================
--- grc/trunk/src/SignalBlockDefs/SignalBlockTree.py 2007-06-07 05:22:11 UTC
(rev 5729)
+++ grc/trunk/src/SignalBlockDefs/SignalBlockTree.py 2007-06-07 05:57:25 UTC
(rev 5730)
@@ -110,6 +110,7 @@
('Freq Xlating FIR Filter',
Filters.FreqXlatingFIRFilter),
('Rational Resampler',
Filters.RationalResampler),
('IIR Filer', Filters.IIRFiler),
+ ('Filter Delay', Filters.FilterDelay),
]),
('Filters', [
('Low Pass Filter', Filters.LowPassFilter),
@@ -119,7 +120,8 @@
('Window', Filters.Window),
('Root Raised Cosine',
Filters.RootRaisedCosine),
('Single Pole IIR Filter',
Filters.SinglePoleIIRFilter),
- ('Hilbert', Filters.Hilbert),
+ ('Hilbert', Filters.Hilbert),
+ ('Goertzel', Filters.Goertzel),
('Power Squelch', Filters.PowerSquelch),
('Downsample', Filters.Downsample),
('Upsample', Filters.Upsample),
@@ -127,7 +129,6 @@
('Automatic Gain Control',
Filters.AutomaticGainControl),
('Automatic Gain Control2',
Filters.AutomaticGainControl2),
('CMA Filter', Filters.CMAFilter),
- ('Costas Loop', Filters.CostasLoop),
('Clock Recovery', Filters.ClockRecovery),
('FFT', Filters.FFT),
('IFFT', Filters.IFFT),
@@ -136,6 +137,8 @@
('Frequency Modulation',
Modulators.FrequencyModulation),
('Phase Modulation',
Modulators.PhaseModulation),
('Quadrature Demodulation',
Modulators.QuadratureDemodulation),
+ ('Costas Loop', Modulators.CostasLoop),
+ ('Phase Locked Loop', Modulators.PLL),
('WFM Receive', Modulators.WFMReceive),
('WFM Transmit', Modulators.WFMTransmit),
('NBFM Receive', Modulators.NBFMReceive),
@@ -163,13 +166,14 @@
('BCJR Algorithm Combined with Metric',
Trellis.Soft_In_Soft_Out_Combined),
('Interleaver', Trellis.Interleaver),
('Deinterleaver', Trellis.Deinterleaver),
- ]),
+ ]),
('Misc', [
('Throttle', Misc.Throttle),
- ('Head', Misc.Head),
+ ('Head', Misc.Head),
+ ('Skip Head', Misc.SkipHead),
('RMS', Misc.RMS),
('About', Misc.About),
- ('Note', Misc.Note),
+ ('Note', Misc.Note),
]),
]
@@ -189,9 +193,9 @@
try:
get_signal_block(None, (0,0), 0, tag[0], '')
if tag[0] in tags_set: # remove redundant tags
#
- print 'Removing redundant tag "%s" in category
"%s"...'%(tag[0], category)
- tags_to_remove.append(tag)
- else: tags_set.add(tag[0])
+ print 'Removing redundant tag "%s" in category
"%s"...'%(tag[0], category)
+ tags_to_remove.append(tag)
+ else: tags_set.add(tag[0])
except (ImportError, AttributeError), e:
print e, " in %s! -> continuing..."%tag[0]
tags_to_remove.append(tag)
@@ -203,7 +207,7 @@
if len(tags) == 0:
print 'Removing category "%s", it was emptied...'%category
cats_to_remove.append((category,tags))
-for cat in cats_to_remove: TAGS.remove(cat)
+for cat in cats_to_remove: SB_TREE.remove(cat)
class TagNotFoundException(Exception):
Modified: grc/trunk/src/SignalBlockDefs/Sinks.py
===================================================================
--- grc/trunk/src/SignalBlockDefs/Sinks.py 2007-06-07 05:22:11 UTC (rev
5729)
+++ grc/trunk/src/SignalBlockDefs/Sinks.py 2007-06-07 05:57:25 UTC (rev
5730)
@@ -96,11 +96,6 @@
self.fg = fg
self.var_key = var_key
self.msgq = msgq
- # make sure that the variable exists and that it has no
range #
- if not Variables.has_key(self.var_key):
- raise Exception('Error: variable "%s" does not
exist!'%self.var_key)
- elif Variables.get_values(self.var_key)[1] != '':
- raise Exception('Error: variable "%s" has a
range!'%self.var_key)
threading.Thread.__init__(self)
print 'Created variable sink thread for variable "%s"'%var_key
@@ -117,21 +112,14 @@
# If so, we take only the last one
if nitems > 1:
start = itemsize * (nitems - 1)
- s = s[start:start+itemsize]
-
+ s = s[start:start+itemsize]
# parse the data to a complex or
float/int string #
complex_data = Numeric.fromstring (s,
Numeric.Float32)
if len(complex_data) == 2: new_value = "%f +
%fj"%(complex_data[0], complex_data[1])
else: new_value = "%f"%(complex_data[0],)
-
- old_value = Variables.get_value(self.var_key)
- # write the new value #
- try:
- Variables.unregister(self.var_key)
- Variables.register(self.var_key,
new_value)
- except:
- print "error registering"
- Variables.register(self.var_key,
old_value) #error registering, restore variable
+ # write the new value #
+ Variables.unregister(self.var_key)
+ Variables.register(self.var_key, new_value)
# parse the call backs #
self.fg.parse_callbacks()
except: break
Modified: grc/trunk/src/SignalBlockDefs/__init__.py
===================================================================
--- grc/trunk/src/SignalBlockDefs/__init__.py 2007-06-07 05:22:11 UTC (rev
5729)
+++ grc/trunk/src/SignalBlockDefs/__init__.py 2007-06-07 05:57:25 UTC (rev
5730)
@@ -23,4 +23,4 @@
"""
from DataType import *
-from SignalBlockTree import SB_TREE,get_signal_block
+from SignalBlockTree import SB_TREE,get_signal_block,TagNotFoundException
Modified: grc/trunk/src/Variables.py
===================================================================
--- grc/trunk/src/Variables.py 2007-06-07 05:22:11 UTC (rev 5729)
+++ grc/trunk/src/Variables.py 2007-06-07 05:57:25 UTC (rev 5730)
@@ -43,10 +43,6 @@
Partially completed ranges will be intelligently filled in.
Partially blanked ranges will be cleared.
All parameters must be strings! '''
- # if step is None, make it a blank string, for backwards
compadibility #
- if step == None: step = ''
- # if min, max, and step are all None, make them blank strings #
- if step == max == min == None: step = min = max = ''
# make sure that all parameters are strings #
for var in (key, value, min, max, step):
if type(var) != type(str()): raise TypeError('"%s" var is not
of type string'%var)
@@ -105,6 +101,11 @@
''' Get the tuple for this key, otherwise None. '''
if has_key(key): return VARS_DICT[key]
else: return None
+
+def is_ranged(key):
+ ''' Does this variable have a range:
+ min, max, and step are not blank. '''
+ return get_values(key)[1] != ''
def get_value(key):
''' Get the value for this key, otherwise None. '''
@@ -116,6 +117,43 @@
with the string representation of the variable. '''
for key in get_keys(): string = string.replace(VAR_MARKER + key,
get_value(key))
return string
+
+##########################################################################
+## Import the Variables
+##########################################################################
+def from_nested_data(nested_data):
+ ''' Clear all of the registered variables,
+ and load all variables from nested data.
+ Return a list of keys, in the order they were added. '''
+ import ParseXML,Messages
+ find_data = ParseXML.find_data
+ clear_vars()
+ keys = list()
+ for var in nested_data:
+ var = find_data([var], 'var')
+ key = find_data(var, 'key')
+ value = find_data(var, 'value')
+ # min, max, and step are optional #
+ min = find_data(var, 'min')
+ max = find_data(var, 'max')
+ step = find_data(var, 'step')
+ # if min, max, and step are all None, make them blank
strings #
+ if step == max == min == None: step = min = max = ''
+ # if step is None, make it a blank string, for backwards
compadibility #
+ if step == None: step = ''
+ try:
+ register(key, value, min, max, step)
+ keys.append(key)
+ except Exception, e:
+ Messages.send_error_load('Could not register variable
with: "%s, %s, %s, %s, %s":\n\t%s'%(key, value, min, max, step, e))
+ try:
+ register(key, value) #error!, register only
the value
+ keys.append(key)
+ except Exception, e:
+ Messages.send_error_load('Could not register
variable with: "%s, %s":\n\t%s'%(key, value, e))
+ Messages.send_error_load('Variable "%s" not
registered!'%key)
+ pass #dont register!
+ return keys
if __name__ == '__main__':
print is_key_name_valid('sdfa13_fsf_dsd')
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Commit-gnuradio] r5730 - in grc/trunk: . data license notes src src/Elements src/Graphics src/SignalBlockDefs,
jblum <=