|
From: <kk...@us...> - 2011-02-08 22:14:44
|
Revision: 60
http://python-control.svn.sourceforge.net/python-control/?rev=60&view=rev
Author: kkchen
Date: 2011-02-08 22:14:38 +0000 (Tue, 08 Feb 2011)
Log Message:
-----------
Implemented StateSpace.evalfr and StateSpace.freqresp in statesp.py; added
appropriate tests in TestStateSp.py.
Also made miscellaneous fixes in xferfcn.py and TestXferFcn.py.
Kevin K. Chen <kk...@pr...>
Modified Paths:
--------------
branches/control-0.4a/src/TestStateSp.py
branches/control-0.4a/src/TestXferFcn.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:14:32 UTC (rev 59)
+++ branches/control-0.4a/src/TestStateSp.py 2011-02-08 22:14:38 UTC (rev 60)
@@ -2,8 +2,53 @@
import numpy as np
import matlab
+from statesp import StateSpace
import unittest
+class TestStateSpace(unittest.TestCase):
+ """Tests for the StateSpace class."""
+
+ def testEvalFr(self):
+ """Evaluate the frequency response at one frequency."""
+
+ A = [[-2, 0.5], [0.5, -0.3]]
+ B = [[0.3, -1.3], [0.1, 0.]]
+ C = [[0., 0.1], [-0.3, -0.2]]
+ D = [[0., -0.8], [-0.3, 0.]]
+ sys = StateSpace(A, B, C, D)
+
+ resp = [[4.37636761487965e-05 - 0.0152297592997812j,
+ -0.792603938730853 + 0.0261706783369803j],
+ [-0.331544857768052 + 0.0576105032822757j,
+ 0.128919037199125 - 0.143824945295405j]]
+
+ np.testing.assert_almost_equal(sys.evalfr(1.), resp)
+
+ def testFreqResp(self):
+ """Evaluate the frequency response at multiple frequencies."""
+
+ A = [[-2, 0.5], [0.5, -0.3]]
+ B = [[0.3, -1.3], [0.1, 0.]]
+ C = [[0., 0.1], [-0.3, -0.2]]
+ D = [[0., -0.8], [-0.3, 0.]]
+ sys = StateSpace(A, B, C, D)
+
+ truemag = [[[0.0852992637230322, 0.00103596611395218],
+ [0.935374692849736, 0.799380720864549]],
+ [[0.55656854563842, 0.301542699860857],
+ [0.609178071542849, 0.0382108097985257]]]
+ truephase = [[[-0.566195599644593, -1.68063565332582],
+ [3.0465958317514, 3.14141384339534]],
+ [[2.90457947657161, 3.10601268291914],
+ [-0.438157380501337, -1.40720969147217]]]
+ trueomega = [0.1, 10.]
+
+ mag, phase, omega = sys.freqresp(trueomega)
+
+ np.testing.assert_almost_equal(mag, truemag)
+ np.testing.assert_almost_equal(phase, truephase)
+ np.testing.assert_equal(omega, trueomega)
+
class TestRss(unittest.TestCase):
"""These are tests for the proper functionality of statesp.rss."""
@@ -73,4 +118,4 @@
self.assertTrue(abs(z) < 1)
if __name__ == "__main__":
- unittest.main()
\ No newline at end of file
+ unittest.main()
Modified: branches/control-0.4a/src/TestXferFcn.py
===================================================================
--- branches/control-0.4a/src/TestXferFcn.py 2011-02-08 22:14:32 UTC (rev 59)
+++ branches/control-0.4a/src/TestXferFcn.py 2011-02-08 22:14:38 UTC (rev 60)
@@ -377,7 +377,7 @@
np.testing.assert_array_almost_equal(mag, truemag)
np.testing.assert_array_almost_equal(phase, truephase)
- np.testing.assert_array_almost_equal(omega, trueomega)
+ np.testing.assert_array_equal(omega, trueomega)
if __name__ == "__main__":
Modified: branches/control-0.4a/src/statesp.py
===================================================================
--- branches/control-0.4a/src/statesp.py 2011-02-08 22:14:32 UTC (rev 59)
+++ branches/control-0.4a/src/statesp.py 2011-02-08 22:14:38 UTC (rev 60)
@@ -43,6 +43,7 @@
import scipy as sp
from scipy import concatenate, zeros
+from numpy.linalg import solve
import xferfcn
from lti2 import Lti2
@@ -92,34 +93,31 @@
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."""
- # Generate and save a transfer function matrix
- #! TODO: This is currently limited to SISO systems
- #nout, nin = self.D.shape
+ # 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)
- # Compute the denominator from the A matrix
- den = sp.poly1d(sp.poly(self.A))
+ for k in range(numfreq):
+ fresp[:, :, k] = self.evalfr(omega[k])
- # 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)
-
- # Generate the frequency response at each frequency
- fresp = map(lambda w: num(w*1j) / den(w*1j), omega)
- mag = sp.sqrt(sp.multiply(fresp, sp.conjugate(fresp)))
+ mag = abs(fresp)
phase = sp.angle(fresp)
return mag, phase, omega
- # Method for evaluating a system at one frequency
- def evalfr(self, freq):
- #! TODO: Not implemented
- return None
-
# Compute poles and zeros
def poles(self):
return sp.roots(sp.poly(self.A))
Modified: branches/control-0.4a/src/xferfcn.py
===================================================================
--- branches/control-0.4a/src/xferfcn.py 2011-02-08 22:14:32 UTC (rev 59)
+++ branches/control-0.4a/src/xferfcn.py 2011-02-08 22:14:38 UTC (rev 60)
@@ -145,8 +145,6 @@
if mimo:
outstr += "\nInput %i to output %i:" % (i + 1, j + 1)
- lablen = 0
-
# Convert the numerator and denominator polynomials to strings.
numstr = _tfpolyToString(self.num[j][i]);
denstr = _tfpolyToString(self.den[j][i]);
@@ -158,11 +156,11 @@
# Center the numerator or denominator
if (len(numstr) < dashcount):
numstr = ' ' * \
- int(round((dashcount - len(numstr))/2) + lablen) + \
+ int(round((dashcount - len(numstr))/2)) + \
numstr
if (len(denstr) < dashcount):
denstr = ' ' * \
- int(round((dashcount - len(denstr))/2) + lablen) + \
+ int(round((dashcount - len(denstr))/2)) + \
denstr
outstr += "\n" + numstr + "\n" + dashes + "\n" + denstr + "\n"
@@ -229,7 +227,7 @@
return xTransferFunction(num, den)
def __radd__(self, other):
- """Add two transfer functions (parallel connection)"""
+ """Add two transfer functions (parallel connection)."""
return self + other;
@@ -244,7 +242,7 @@
return other + (-self)
def __mul__(self, other):
- """Multiply two transfer functions (serial connection)"""
+ """Multiply two transfer functions (serial connection)."""
# Convert the second argument to a transfer function.
if not isinstance(other, xTransferFunction):
@@ -278,13 +276,13 @@
return xTransferFunction(num, den)
def __rmul__(self, other):
- """Multiply two transfer functions (serial connection)"""
+ """Multiply two transfer functions (serial connection)."""
return self * other
# TODO: Division of MIMO transfer function objects is quite difficult.
def __div__(self, other):
- """Divide two transfer functions"""
+ """Divide two transfer functions."""
if self.inputs > 1 or self.outputs > 1 or \
other.inputs > 1 or other.outputs > 1:
@@ -302,7 +300,7 @@
# TODO: Division of MIMO transfer function objects is quite difficult.
def __rdiv__(self, other):
- """Reverse divide two transfer functions"""
+ """Reverse divide two transfer functions."""
if self.inputs > 1 or self.outputs > 1 or \
other.inputs > 1 or other.outputs > 1:
@@ -312,7 +310,7 @@
return other / self
def evalfr(self, freq):
- """Evaluate a transfer function at a single frequency"""
+ """Evaluate a transfer function at a single frequency."""
# Preallocate the output.
out = sp.empty((self.outputs, self.inputs), dtype=complex)
@@ -325,23 +323,22 @@
return out
# Method for generating the frequency response of the system
- def freqresp(self, omega):
- """Evaluate a transfer function at a list of frequencies"""
+ def freqresp(self, omega=None):
+ """Evaluate a transfer function at 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))
- # Preallocate outputs.
- mag = sp.empty((self.outputs, self.inputs, numfreq), dtype=complex)
- phase = sp.empty((self.outputs, self.inputs, numfreq), dtype=complex)
-
for i in range(self.outputs):
for j in range(self.inputs):
fresp = map(lambda w: sp.polyval(self.num[i][j], w * 1.j) / \
sp.polyval(self.den[i][j], w * 1.j), omega)
fresp = sp.array(fresp)
- mag[i][j] = abs(fresp)
- phase[i][j] = sp.angle(fresp)
+ mag[i, j] = abs(fresp)
+ phase[i, j] = sp.angle(fresp)
return mag, phase, omega
@@ -356,7 +353,7 @@
pass
def feedback(sys1, sys2, sign=-1):
- """Feedback interconnection between two transfer functions"""
+ """Feedback interconnection between two transfer functions."""
pass
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|