|
From: <kk...@us...> - 2011-02-08 22:15:44
|
Revision: 72
http://python-control.svn.sourceforge.net/python-control/?rev=72&view=rev
Author: kkchen
Date: 2011-02-08 22:15:38 +0000 (Tue, 08 Feb 2011)
Log Message:
-----------
Added StateSpace binary operator tests to TestStateSp.py; bug fixes.
- {StateSpace,XferFcn}.{poles(),zeros()} changed to pole(), zero() to be
consistent with MATLAB.
- Bug fix in StateSpace.zero().
- Bug fix in StateSpace.__sub__().
- Bug fix in StateSpace.__mul__().
- Rearranged the StateSpace member functions to a more sensible order.
Kevin K. Chen <kk...@pr...>
Modified Paths:
--------------
branches/control-0.4a/src/TestStateSp.py
branches/control-0.4a/src/matlab.py
branches/control-0.4a/src/statesp.py
branches/control-0.4a/src/xferfcn.py
Modified: branches/control-0.4a/src/TestStateSp.py
===================================================================
--- branches/control-0.4a/src/TestStateSp.py 2011-02-08 22:15:32 UTC (rev 71)
+++ branches/control-0.4a/src/TestStateSp.py 2011-02-08 22:15:38 UTC (rev 72)
@@ -8,6 +8,89 @@
class TestStateSpace(unittest.TestCase):
"""Tests for the StateSpace class."""
+ def setUp(self):
+ """Set up a MIMO system to test operations on."""
+
+ A = [[-3., 4., 2.], [-1., -3., 0.], [2., 5., 3.]]
+ B = [[1., 4.], [-3., -3.], [-2., 1.]]
+ C = [[4., 2., -3.], [1., 4., 3.]]
+ D = [[-2., 4.], [0., 1.]]
+
+ a = [[4., 1.], [2., -3]]
+ b = [[5., 2.], [-3., -3.]]
+ c = [[2., -4], [0., 1.]]
+ d = [[3., 2.], [1., -1.]]
+
+ self.sys1 = StateSpace(A, B, C, D)
+ self.sys2 = StateSpace(a, b, c, d)
+
+ def testPole(self):
+ """Evaluate the poles of a MIMO system."""
+
+ p = self.sys1.pole()
+
+ np.testing.assert_array_almost_equal(p, [3.34747678408874,
+ -3.17373839204437 + 1.47492908003839j,
+ -3.17373839204437 - 1.47492908003839j])
+
+ def testZero(self):
+ """Evaluate the zeros of a SISO system."""
+
+ sys = StateSpace(self.sys1.A, [[3.], [-2.], [4.]], [[-1., 3., 2.]], [[-4.]])
+ z = sys.zero()
+
+ np.testing.assert_array_almost_equal(z, [4.26864638637134,
+ -3.75932319318567 + 1.10087776649554j,
+ -3.75932319318567 - 1.10087776649554j])
+
+ def testAdd(self):
+ """Add two MIMO systems."""
+
+ A = [[-3., 4., 2., 0., 0.], [-1., -3., 0., 0., 0.],
+ [2., 5., 3., 0., 0.], [0., 0., 0., 4., 1.], [0., 0., 0., 2., -3.]]
+ B = [[1., 4.], [-3., -3.], [-2., 1.], [5., 2.], [-3., -3.]]
+ C = [[4., 2., -3., 2., -4.], [1., 4., 3., 0., 1.]]
+ D = [[1., 6.], [1., 0.]]
+
+ sys = self.sys1 + self.sys2
+
+ np.testing.assert_array_almost_equal(sys.A, A)
+ np.testing.assert_array_almost_equal(sys.B, B)
+ np.testing.assert_array_almost_equal(sys.C, C)
+ np.testing.assert_array_almost_equal(sys.D, D)
+
+ def testSub(self):
+ """Subtract two MIMO systems."""
+
+ A = [[-3., 4., 2., 0., 0.], [-1., -3., 0., 0., 0.],
+ [2., 5., 3., 0., 0.], [0., 0., 0., 4., 1.], [0., 0., 0., 2., -3.]]
+ B = [[1., 4.], [-3., -3.], [-2., 1.], [5., 2.], [-3., -3.]]
+ C = [[4., 2., -3., -2., 4.], [1., 4., 3., 0., -1.]]
+ D = [[-5., 2.], [-1., 2.]]
+
+ sys = self.sys1 - self.sys2
+
+ np.testing.assert_array_almost_equal(sys.A, A)
+ np.testing.assert_array_almost_equal(sys.B, B)
+ np.testing.assert_array_almost_equal(sys.C, C)
+ np.testing.assert_array_almost_equal(sys.D, D)
+
+ def testMul(self):
+ """Multiply two MIMO systems."""
+
+ A = [[4., 1., 0., 0., 0.], [2., -3., 0., 0., 0.], [2., 0., -3., 4., 2.],
+ [-6., 9., -1., -3., 0.], [-4., 9., 2., 5., 3.]]
+ B = [[5., 2.], [-3., -3.], [7., -2.], [-12., -3.], [-5., -5.]]
+ C = [[-4., 12., 4., 2., -3.], [0., 1., 1., 4., 3.]]
+ D = [[-2., -8.], [1., -1.]]
+
+ sys = self.sys1 * self.sys2
+
+ np.testing.assert_array_almost_equal(sys.A, A)
+ np.testing.assert_array_almost_equal(sys.B, B)
+ np.testing.assert_array_almost_equal(sys.C, C)
+ np.testing.assert_array_almost_equal(sys.D, D)
+
def testEvalFr(self):
"""Evaluate the frequency response at one frequency."""
@@ -79,7 +162,7 @@
for inputs in range(1, self.maxIO):
for outputs in range(1, self.maxIO):
sys = matlab.rss(states, inputs, outputs)
- p = sys.poles()
+ p = sys.pole()
for z in p:
self.assertTrue(z.real < 0)
@@ -113,7 +196,7 @@
for inputs in range(1, self.maxIO):
for outputs in range(1, self.maxIO):
sys = matlab.drss(states, inputs, outputs)
- p = sys.poles()
+ p = sys.pole()
for z in p:
self.assertTrue(abs(z) < 1)
Modified: branches/control-0.4a/src/matlab.py
===================================================================
--- branches/control-0.4a/src/matlab.py 2011-02-08 22:15:32 UTC (rev 71)
+++ branches/control-0.4a/src/matlab.py 2011-02-08 22:15:38 UTC (rev 72)
@@ -357,8 +357,13 @@
def pole(sys):
"""Return system poles."""
- return sys.poles()
+ return sys.pole()
+def zero(sys):
+ """Return system zeros."""
+
+ return sys.zero()
+
def evalfr(sys, omega):
"""Evaluate the transfer function of an LTI system at a single frequency
omega."""
Modified: branches/control-0.4a/src/statesp.py
===================================================================
--- branches/control-0.4a/src/statesp.py 2011-02-08 22:15:32 UTC (rev 71)
+++ branches/control-0.4a/src/statesp.py 2011-02-08 22:15:38 UTC (rev 72)
@@ -93,52 +93,6 @@
str += "D = " + self.D.__str__() + "\n"
return str
- def evalfr(self, freq):
- """Method for evaluating a system at one frequency."""
-
- fresp = self.C * solve(freq * 1.j * sp.eye(self.states) - self.A,
- self.B) + self.D
- return fresp
-
- # Method for generating the frequency response of the system
- def freqresp(self, omega=None):
- """Compute the response of a system to a list of frequencies."""
-
- # Preallocate outputs.
- numfreq = len(omega)
- mag = sp.empty((self.outputs, self.inputs, numfreq))
- phase = sp.empty((self.outputs, self.inputs, numfreq))
- fresp = sp.empty((self.outputs, self.inputs, numfreq), dtype=complex)
-
- for k in range(numfreq):
- fresp[:, :, k] = self.evalfr(omega[k])
-
- mag = abs(fresp)
- phase = sp.angle(fresp)
-
- return mag, phase, omega
-
- # Compute poles and zeros
- def poles(self):
- """Compute the poles of a state space system."""
-
- return sp.roots(sp.poly(self.A))
-
- def zeros(self):
- """Compute the zeros of a state space system."""
-
- if self.inputs > 1 or self.outputs > 1:
- raise NotImplementedError("StateSpace.zeros is currently \
-implemented only for SISO systems.")
-
- den = sp.poly1d(sp.poly(self.A))
- # Compute the numerator based on zeros
- #! TODO: This is currently limited to SISO systems
- num = sp.poly1d(\
- sp.poly(self.A - sp.dot(self.B, self.C)) + (self.D[0] - 1) * den)
-
- return (sp.roots(num))
-
# Negation of a system
def __neg__(self):
"""Negate a state space system."""
@@ -183,19 +137,18 @@
def __sub__(self, other):
"""Subtract two state space systems."""
- return __add__(self, other.__neg__())
+ return self.__add__(-other)
# Multiplication of two transfer functions (series interconnection)
def __mul__(self, other):
"""Serial interconnection between two state space systems."""
# Check for a couple of special cases
- if (isinstance(other, (int, long, float, complex))):
+ if isinstance(other, (int, long, float, complex)):
# Just multiplying by a scalar; change the output
A, B = self.A, self.B;
C = self.C * other;
D = self.D * other;
-
else:
# Check to make sure the dimensions are OK
if (self.outputs != other.inputs):
@@ -203,14 +156,15 @@
of second's inputs."
# Concatenate the various arrays
- A = concatenate((
- concatenate(( self.A, zeros((self.A.shape[0],
- other.A.shape[-1])) ),axis=1),
- concatenate(( other.B*self.C, other.A ),axis=1),
- ),axis=0)
- B = concatenate( (self.B, other.B*self.D), axis=0 )
- C = concatenate( (other.D*self.C, other.C), axis=1 )
- D = other.D*self.D
+ A = concatenate(
+ (concatenate((other.A, zeros((other.A.shape[0], self.A.shape[1]))),
+ axis=1),
+ concatenate((self.B * other.C, self.A), axis=1)),
+ axis=0)
+ B = concatenate((other.B, self.B * other.D), axis=0)
+ C = concatenate((self.D * other.C, self.C),axis=1)
+ D = self.D * other.D
+
return StateSpace(A, B, C, D)
# Reverse multiplication of two transfer functions (series interconnection)
@@ -219,7 +173,7 @@
"""Serial interconnection between two state space systems"""
# Check for a couple of special cases
- if (isinstance(other, (int, long, float, complex))):
+ if isinstance(other, (int, long, float, complex)):
# Just multiplying by a scalar; change the input
A, C = self.A, self.C;
B = self.B * other;
@@ -229,6 +183,52 @@
else:
raise TypeError("can't interconnect systems")
+ # Compute poles and zeros
+ def pole(self):
+ """Compute the poles of a state space system."""
+
+ return sp.roots(sp.poly(self.A))
+
+ def zero(self):
+ """Compute the zeros of a state space system."""
+
+ if self.inputs > 1 or self.outputs > 1:
+ raise NotImplementedError("StateSpace.zeros is currently \
+implemented only for SISO systems.")
+
+ den = sp.poly1d(sp.poly(self.A))
+ # Compute the numerator based on zeros
+ #! TODO: This is currently limited to SISO systems
+ num = sp.poly1d(\
+ sp.poly(self.A - sp.dot(self.B, self.C)) + (self.D[0, 0] - 1) * den)
+
+ return (sp.roots(num))
+
+ def evalfr(self, freq):
+ """Method for evaluating a system at one frequency."""
+
+ fresp = self.C * solve(freq * 1.j * sp.eye(self.states) - self.A,
+ self.B) + self.D
+ return fresp
+
+ # Method for generating the frequency response of the system
+ def freqresp(self, omega=None):
+ """Compute the response of a system to a list of frequencies."""
+
+ # Preallocate outputs.
+ numfreq = len(omega)
+ mag = sp.empty((self.outputs, self.inputs, numfreq))
+ phase = sp.empty((self.outputs, self.inputs, numfreq))
+ fresp = sp.empty((self.outputs, self.inputs, numfreq), dtype=complex)
+
+ for k in range(numfreq):
+ fresp[:, :, k] = self.evalfr(omega[k])
+
+ mag = abs(fresp)
+ phase = sp.angle(fresp)
+
+ return mag, phase, omega
+
# Feedback around a state space system
def feedback(self, other, sign=-1):
"""Feedback interconnection between two state space systems."""
Modified: branches/control-0.4a/src/xferfcn.py
===================================================================
--- branches/control-0.4a/src/xferfcn.py 2011-02-08 22:15:32 UTC (rev 71)
+++ branches/control-0.4a/src/xferfcn.py 2011-02-08 22:15:38 UTC (rev 72)
@@ -352,12 +352,12 @@
return mag, phase, omega
- def poles(self):
+ def pole(self):
"""Compute poles of a transfer function."""
pass
- def zeros(self):
+ def zero(self):
"""Compute zeros of a transfer function."""
pass
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|