|
From: <kk...@us...> - 2011-02-08 22:14:23
|
Revision: 56
http://python-control.svn.sourceforge.net/python-control/?rev=56&view=rev
Author: kkchen
Date: 2011-02-08 22:14:17 +0000 (Tue, 08 Feb 2011)
Log Message:
-----------
Changes to xferfcn.py:
- Check that we don't have any zero denominators.
- Bug fix in _addSISO
- Exceptions more specific
Also added a simple SISO multiplication test to TestXferFcn.py.
Kevin K. Chen <kk...@pr...>
Modified Paths:
--------------
branches/control-0.4a/src/TestXferFcn.py
branches/control-0.4a/src/xferfcn.py
Modified: branches/control-0.4a/src/TestXferFcn.py
===================================================================
--- branches/control-0.4a/src/TestXferFcn.py 2011-02-08 22:14:13 UTC (rev 55)
+++ branches/control-0.4a/src/TestXferFcn.py 2011-02-08 22:14:17 UTC (rev 56)
@@ -57,13 +57,22 @@
sys1 = xTransferFunction(num1, den1)
sys2 = xTransferFunction(num2, den2)
-
sys3 = sys1 + sys2
for i in range(sys3.outputs):
for j in range(sys3.inputs):
np.testing.assert_array_equal(sys3.num[i][j], num3[i][j])
np.testing.assert_array_equal(sys3.den[i][j], den3[i][j])
+
+ def testMulSISO1(self):
+ """Multiply two direct feedthrough systems."""
+
+ sys1 = xTransferFunction(2., [1.])
+ sys2 = xTransferFunction(1., 4.)
+ sys3 = sys1 * sys2
+
+ np.testing.assert_array_equal(sys3.num, [[[2.]]])
+ np.testing.assert_array_equal(sys3.den, [[[4.]]])
if __name__ == "__main__":
unittest.main()
\ No newline at end of file
Modified: branches/control-0.4a/src/xferfcn.py
===================================================================
--- branches/control-0.4a/src/xferfcn.py 2011-02-08 22:14:13 UTC (rev 55)
+++ branches/control-0.4a/src/xferfcn.py 2011-02-08 22:14:17 UTC (rev 56)
@@ -67,7 +67,7 @@
"""This is the constructor. The default transfer function is 1 (unit
gain direct feedthrough)."""
- # Make num and den into 3-d numpy arrays, if necessary.
+ # Make num and den into lists of lists of arrays, if necessary.
data = [num, den]
for i in range(len(data)):
if isinstance(data[i], (int, float, long, complex)):
@@ -93,19 +93,34 @@
inputs = len(num[0])
outputs = len(num)
+ # Make sure the numerator and denominator matrices have consistent
+ # sizes.
if inputs != len(den[0]):
- raise ValueError("The numerator and denominator matrices must have \
-the same column\n(input) size.")
+ raise ValueError("The numerator has %i input(s), but the \
+denominator has %i\ninput(s)." % (inputs, len(den[0])))
if outputs != len(den):
- raise ValueError("The numerator and denominator matrices must have \
-the same row\n(output) size.")
- for i in range(1, outputs):
+ raise ValueError("The numerator has %i output(s), but the \
+denominator has %i\noutput(s)." % (outputs, len(den)))
+
+ # Make sure that each row has the same number of columns.
+ for i in range(outputs):
if len(num[i]) != inputs:
- raise ValueError("Each row of the numerator matrix must have \
-the same number of\nelements.")
+ raise ValueError("Row 0 of the numerator matrix has %i \
+elements, but row %i\nhas %i." % (inputs, i, len(num[i])))
if len(den[i]) != inputs:
- raise ValueError("Each row of the denominator matrix must have \
-the same number of\nelements.")
+ raise ValueError("Row 0 of the denominator matrix has %i \
+elements, but row %i\nhas %i." % (inputs, i, len(den[i])))
+
+ # Check that we don't have any zero denominators.
+ for j in range(inputs):
+ iszero = True
+ for k in den[i][j]:
+ if k:
+ iszero = False
+ break
+ if iszero:
+ raise ValueError("Input %i, output %i has a zero \
+denominator." % (j + 1, i + 1))
self.num = num
self.den = den
@@ -180,11 +195,11 @@
# Check that the input-output sizes are consistent.
if self.inputs != other.inputs:
- raise ValueError("The two systems to be added must have the same \
-input size.")
+ raise ValueError("The first summand has %i input(s), but the second \
+has %i." % (self.inputs, other.inputs))
if self.outputs != other.outputs:
- raise ValueError("The two systems to be added must have the same \
-output size.")
+ raise ValueError("The first summand has %i output(s), but the second \
+has %i." % (self.outputs, other.outputs))
# Preallocate the numerator and denominator of the sum.
num = [[[] for j in range(self.inputs)] for i in range(self.outputs)]
@@ -192,7 +207,7 @@
for i in range(self.outputs):
for j in range(self.inputs):
- num[i][j], den[i][j] = addSISO(self.num[i][j], self.den[i][j],
+ num[i][j], den[i][j] = _addSISO(self.num[i][j], self.den[i][j],
other.num[i][j], other.den[i][j])
return xTransferFunction(num, den)
@@ -221,15 +236,15 @@
# Check that the input-output sizes are consistent.
if self.inputs != other.outputs:
- raise ValueError("C = A * B: A must have the same number of \
-columns (inputs) as B has\nrows (outputs).")
+ raise ValueError("C = A * B: A has %i column(s) (input(s)), but B \
+has %i row(s)\n(output(s))." % (self.inputs, other.outputs))
inputs = other.inputs
outputs = self.outputs
# Preallocate the numerator and denominator of the sum.
num = [[[0] for j in range(inputs)] for i in range(outputs)]
- den = [[[0] for j in range(inputs)] for i in range(outputs)]
+ den = [[[1] for j in range(inputs)] for i in range(outputs)]
# Temporary storage for the summands needed to find the (i, j)th element
# of the product.
@@ -241,7 +256,7 @@
for k in range(self.inputs): # Multiply & add.
num_summand[k] = sp.polymul(self.num[i][k], other.num[k][j])
den_summand[k] = sp.polymul(self.den[i][k], other.den[k][j])
- num[i][j], den[i][j] = addSISO(num[i][j], den[i][j],
+ num[i][j], den[i][j] = _addSISO(num[i][j], den[i][j],
num_summand[k], den_summand[k])
return xTransferFunction(num, den)
@@ -251,16 +266,42 @@
return self * other
- def __div__(self, sys):
+ # TODO: Division of MIMO transfer function objects is quite difficult.
+ def __div__(self, other):
"""Divide two transfer functions"""
- pass
+ if self.inputs > 1 or self.outputs > 1 or \
+ other.inputs > 1 or other.outputs > 1:
+ raise NotImplementedError("xTransferFunction.__div__ is currently \
+implemented only for SISO systems.")
+
+ # Convert the second argument to a transfer function.
+ if not isinstance(other, xTransferFunction):
+ other = ss2tf(other)
+
+ num = sp.polymul(self.num[0][0], other.den[0][0])
+ den = sp.polymul(self.den[0][0], other.num[0][0])
+ return xTransferFunction(num, den)
+
+ # TODO: Division of MIMO transfer function objects is quite difficult.
def __rdiv__(self, sys):
"""Reverse divide two transfer functions"""
- pass
+ if self.inputs > 1 or self.outputs > 1 or \
+ other.inputs > 1 or other.outputs > 1:
+ raise NotImplementedError("xTransferFunction.__rdiv__ is currently \
+implemented only for SISO systems.")
+
+ # Convert the second argument to a transfer function.
+ if not isinstance(other, xTransferFunction):
+ other = ss2tf(other)
+
+ num = sp.polymul(self.den[0][0], other.num[0][0])
+ den = sp.polymul(self.num[0][0], other.den[0][0])
+ return xTransferFunction(num, den)
+
def evalfr(self, freq):
"""Evaluate a transfer function at a single frequency"""
@@ -354,6 +395,7 @@
def __sub__(self, other):
"""Subtract two transfer functions"""
return self + (-other)
+
def __rsub__(self, other):
"""Subtract two transfer functions"""
return other + (-self)
@@ -505,7 +547,7 @@
thestr = newstr
return thestr
-def addSISO(num1, den1, num2, den2):
+def _addSISO(num1, den1, num2, den2):
"""Return num/den = num1/den1 + num2/den2, where each numerator and
denominator is a list of polynomial coefficients."""
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|