[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Commit-gnuradio] r5548 - in grc/branches/jblum_work: notes src src/Sign
From: |
jblum |
Subject: |
[Commit-gnuradio] r5548 - in grc/branches/jblum_work: notes src src/SignalBlockDefs |
Date: |
Sun, 27 May 2007 23:48:30 -0600 (MDT) |
Author: jblum
Date: 2007-05-27 23:48:30 -0600 (Sun, 27 May 2007)
New Revision: 5548
Modified:
grc/branches/jblum_work/notes/notes.txt
grc/branches/jblum_work/src/DataType.py
grc/branches/jblum_work/src/MathExprParser.py
grc/branches/jblum_work/src/SignalBlockDefs/Filters.py
grc/branches/jblum_work/src/SignalBlockDefs/SignalBlockConstants.py
Log:
created new math expression parser with constants, functions, and vector support
Modified: grc/branches/jblum_work/notes/notes.txt
===================================================================
--- grc/branches/jblum_work/notes/notes.txt 2007-05-28 05:41:10 UTC (rev
5547)
+++ grc/branches/jblum_work/notes/notes.txt 2007-05-28 05:48:30 UTC (rev
5548)
@@ -14,7 +14,7 @@
############ 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
+-math expressions for taps generators
-stdin/out communication protocal (non graphical)
-include dtd in saved flow graphs
Modified: grc/branches/jblum_work/src/DataType.py
===================================================================
--- grc/branches/jblum_work/src/DataType.py 2007-05-28 05:41:10 UTC (rev
5547)
+++ grc/branches/jblum_work/src/DataType.py 2007-05-28 05:48:30 UTC (rev
5548)
@@ -254,14 +254,9 @@
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)
+ elements = MathExprParser.eval_expr(DataType.parse(self))
+ if type(elements)!= type(list()): elements = [elements]
+ return map(lambda v: self.parser(v), elements)
class ByteVector(Vector, Byte):
''' A vector of bytes '''
Modified: grc/branches/jblum_work/src/MathExprParser.py
===================================================================
--- grc/branches/jblum_work/src/MathExprParser.py 2007-05-28 05:41:10 UTC
(rev 5547)
+++ grc/branches/jblum_work/src/MathExprParser.py 2007-05-28 05:48:30 UTC
(rev 5548)
@@ -23,176 +23,302 @@
and operators and parse it into a single answer.
"""
-from math import cos, sin, log, atan2, e
+import math,cmath
+from gnuradio import gr
-_ORDER_OF_OPS = ('^', '*', '/', '-', '+')
+def _number(value='0'):
+ ''' The parser method used for numeric entries. '''
+ return complex(value)
-_WHITESPACE = (' ', '\t')
+_ORDER_OF_OPS = ('^-', '^', '*', '/', '-', '+')
-def number(value):
- ''' The parser method used for numeric entries. '''
- return complex(value)
+_WHITESPACE = (' ', '\t', '')
+
+_CONSTANTS = {
+ 'pi' : _number(cmath.pi),
+ 'e' : _number(cmath.e),
+ 'j' : _number(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))
+#########################################################
+## Complex mathematical functions
+#########################################################
+
+def _complex_abs_v(*args):
+ ''' Compute the abs of a complex number or vector. '''
+ sum = 0
+ for arg in args: sum = sum + arg.real*arg.real + arg.imag*arg.imag
+ return _number(sum**0.5)
-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 _complex_real_v(*args):
+ ''' Get the real part of a number or vector. '''
+ result = list()
+ for arg in args: result.append(_number(arg.real))
+ return result
+def _complex_imag_v(*args):
+ ''' Get the imaginary part of a number or vector. '''
+ result = list()
+ for arg in args: result.append(_number(arg.imag))
+ return result
+
+def _complex_conj_v(*args):
+ ''' Get the complex conjugate of a number or vector. '''
+ result = list()
+ for arg in args: result.append(arg.conjugate())
+ return result
+
+def _complex_arg(arg):
+ ''' Get the angle of the complex number (radians). '''
+ return _number(math.atan2(arg.imag, arg.real))
+
+_FUNCTIONS = {
+ 'pow' : lambda b, p: b**p,
+ '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 _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)
+ 'sin' : cmath.sin,
+ 'cos' : cmath.cos,
+ 'tan' : cmath.tan,
+
+ 'asin' : cmath.asin,
+ 'acos' : cmath.acos,
+ 'atan' : cmath.atan,
+
+ 'sinh' : cmath.sinh, #hyperbolics
+ 'cosh' : cmath.cosh,
+ 'tanh' : cmath.tanh,
+
+ 'asinh' : cmath.asinh,
+ 'acosh' : cmath.acosh,
+ 'atanh' : cmath.atanh,
+
+ 'log10' : cmath.log10,
+ 'log' : cmath.log, #optional 2nd argument as base
+ 'ln' : lambda z: cmath.log(z), #only allow natural log
+ 'exp' : cmath.exp,
+}
-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)
+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 Exception('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]
+ if op == '+': result.append(arg1_elem + arg2_elem)
+ elif op == '-': result.append(arg1_elem - arg2_elem)
+ else: raise Exception('Error in vector +/- vector')
+ return result
+ # scalar times vector #
+ if not arg1_is_list and arg2_is_list and op == '*':
+ return map(lambda elem: arg1 * elem, arg2)
+ # vector times scalar #
+ if arg1_is_list and not arg2_is_list and op == '*':
+ return map(lambda elem: elem * arg2, arg1)
+ # vector divided by scalar #
+ if arg1_is_list and not arg2_is_list and op == '/':
+ return map(lambda elem: elem/arg2, arg1)
+ # non vectored operations #
+ if not arg1_is_list and not arg2_is_list and \
+ type(_number()) == type(arg1) == type(arg2): #non vector
operations
+ if op == '^-':
+ return arg1**(-1*arg2)
+ elif op == '^':
+ return arg1**arg2
+ elif op == '*':
+ return arg1 * arg2
+ elif op == '/':
+ return arg1 / arg2
+ elif op == '+':
+ return arg1 + arg2
+ elif op == '-':
+ return arg1 - arg2
+ raise Exception('Operation of "%s" cannot be performed on "%s",
"%s"'%(op, arg1, arg2))
+#########################################################
+## Boolean tests for special characters and symbols
+#########################################################
-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 _is_list(symbol): return type(symbol) == type(list())
-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 _is_function(symbol): return symbol in _FUNCTIONS.keys()
+
+def _is_constant(symbol): return symbol in _CONSTANTS.keys()
+
+def _is_number(symbol):
+ try: _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)
+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 {
+ '(' : ')',
+ ')' : '(',
+ '[' : ']',
+ ']' : '[',
+ '{' : '}',
+ '}' : '{',
+ None : None
+ }[bracket]
+
+#########################################################
+## 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_minus(elements):
+ ''' Rectify the functionality of the minus sign (-)
+ in the element list formed by separating the expression.
+ Minus can be an operator or an implied -1*.
+ Determine if the minus sign is used as a negative,
+ and replace it with -1*.
+ Return the list of elements with minus used only as an
operator. '''
+ 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 == '-': #minus found!
+ if _is_operator(elements[i-1]) or
_is_open_bracket(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('^-')
+ else: rectified_elements.append(element)
#regular minus
+ else: rectified_elements.append(element) #non minus
+ return rectified_elements
+
+def _nest_elements(elements, open_bracket=None):
+ ''' 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 Exception('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 Exception('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] = _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
Exception('Function "%s" has no arguments'%(function))
+ args = nested_elements[i+1]
+ if not _is_list(args): args = [args]
+ try: ans = _FUNCTIONS[function](*args)
+ except: raise Exception('Function "%s" with arguments
"%s" failed'%(function, nested_elements[i+1]))
+ # ans cannot be a tuple!
+ if type(ans) == type(tuple()): ans = list(ans)
+ nested_elements = nested_elements[0:i] + [ans] +
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 not arg1 or not arg2: raise Exception('Operator "%s"
is missing argument'%(operator))
+ ans = _handle_operation(operator, arg1, arg2)
+ nested_elements = nested_elements[0:i-1] + [ans] +
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 Exception('Unknown symbol "%s"'%(element))
+ if last_element and not _is_comma(last_element):
+ raise Exception('Expected comma, but found
"%s"'%(element))
+ vector.append(element)
+ last_element = element
+ if len(vector) == 1: 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.
+ Numbers will all be parsed. Operators are ^, *, /, -, +.
+ Raise an exception on failue. '''
+ separated_elements = _separate_expression(expr_str)
+ #print "separated", separated_elements
+ rectified_elements = _rectify_minus(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 ')
+ print eval_expr('')
\ No newline at end of file
Modified: grc/branches/jblum_work/src/SignalBlockDefs/Filters.py
===================================================================
--- grc/branches/jblum_work/src/SignalBlockDefs/Filters.py 2007-05-28
05:41:10 UTC (rev 5547)
+++ grc/branches/jblum_work/src/SignalBlockDefs/Filters.py 2007-05-28
05:48:30 UTC (rev 5548)
@@ -140,7 +140,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 +149,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
Modified: grc/branches/jblum_work/src/SignalBlockDefs/SignalBlockConstants.py
===================================================================
--- grc/branches/jblum_work/src/SignalBlockDefs/SignalBlockConstants.py
2007-05-28 05:41:10 UTC (rev 5547)
+++ grc/branches/jblum_work/src/SignalBlockDefs/SignalBlockConstants.py
2007-05-28 05:48:30 UTC (rev 5548)
@@ -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
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Commit-gnuradio] r5548 - in grc/branches/jblum_work: notes src src/SignalBlockDefs,
jblum <=