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. |