|
From: <kk...@us...> - 2011-02-08 18:04:11
|
Revision: 34
http://python-control.svn.sourceforge.net/python-control/?rev=34&view=rev
Author: kkchen
Date: 2011-02-08 18:04:02 +0000 (Tue, 08 Feb 2011)
Log Message:
-----------
Imported SourceForce repo to hg. Created control-0.4a branch.
Added Paths:
-----------
.hgignore
branches/control-0.4a/
branches/control-0.4a/ChangeLog
branches/control-0.4a/MANIFEST.in
branches/control-0.4a/Pending
branches/control-0.4a/README
branches/control-0.4a/build/
branches/control-0.4a/build/lib/
branches/control-0.4a/build/lib/control/
branches/control-0.4a/build/lib/control/TestBDAlg.py
branches/control-0.4a/build/lib/control/TestConvert.py
branches/control-0.4a/build/lib/control/TestFreqRsp.py
branches/control-0.4a/build/lib/control/TestMatlab.py
branches/control-0.4a/build/lib/control/TestModelsimp.py
branches/control-0.4a/build/lib/control/TestSlycot.py
branches/control-0.4a/build/lib/control/TestStateSp.py
branches/control-0.4a/build/lib/control/TestStatefbk.py
branches/control-0.4a/build/lib/control/TestXferFcn.py
branches/control-0.4a/build/lib/control/__init__.py
branches/control-0.4a/build/lib/control/bdalg.py
branches/control-0.4a/build/lib/control/ctrlutil.py
branches/control-0.4a/build/lib/control/delay.py
branches/control-0.4a/build/lib/control/exception.py
branches/control-0.4a/build/lib/control/freqplot.py
branches/control-0.4a/build/lib/control/lti.py
branches/control-0.4a/build/lib/control/matlab.py
branches/control-0.4a/build/lib/control/modelsimp.py
branches/control-0.4a/build/lib/control/pzmap.py
branches/control-0.4a/build/lib/control/rlocus.py
branches/control-0.4a/build/lib/control/robust.py
branches/control-0.4a/build/lib/control/statefbk.py
branches/control-0.4a/build/lib/control/statesp.py
branches/control-0.4a/build/lib/control/test.py
branches/control-0.4a/build/lib/control/xferfcn.py
branches/control-0.4a/doc/
branches/control-0.4a/doc/control.tex
branches/control-0.4a/examples/
branches/control-0.4a/examples/pvtol-lqr.py
branches/control-0.4a/examples/pvtol-nested-ss.py
branches/control-0.4a/examples/pvtol-nested.py
branches/control-0.4a/examples/secord-matlab.py
branches/control-0.4a/examples/slicot-test.py
branches/control-0.4a/examples/type2_type3.py
branches/control-0.4a/external/
branches/control-0.4a/external/controls.py
branches/control-0.4a/external/yottalab.py
branches/control-0.4a/setup.py
branches/control-0.4a/src/
branches/control-0.4a/src/__init__.py
branches/control-0.4a/src/bdalg.py
branches/control-0.4a/src/ctrlutil.py
branches/control-0.4a/src/delay.py
branches/control-0.4a/src/exception.py
branches/control-0.4a/src/freqplot.py
branches/control-0.4a/src/matlab.py
branches/control-0.4a/src/pzmap.py
branches/control-0.4a/src/rlocus.py
branches/control-0.4a/src/statefbk.py
branches/control-0.4a/src/statesp.py
branches/control-0.4a/src/xferfcn.py
Added: .hgignore
===================================================================
--- .hgignore (rev 0)
+++ .hgignore 2011-02-08 18:04:02 UTC (rev 34)
@@ -0,0 +1,17 @@
+
+# Automatically generated by `hgimportsvn`
+syntax:glob
+.svn
+.hgsvn
+
+# These lines are suggested according to the svn:ignore property
+# Feel free to enable them by uncommenting them
+syntax:glob
+
+.DS_Store
+.*.swp
+build/*
+doc/_build/*
+doc/_templates/*
+doc/_static/*
+*.pyc
Added: branches/control-0.4a/ChangeLog
===================================================================
--- branches/control-0.4a/ChangeLog (rev 0)
+++ branches/control-0.4a/ChangeLog 2011-02-08 18:04:02 UTC (rev 34)
@@ -0,0 +1,144 @@
+2010-11-05 Richard Murray <mu...@su...>
+
+ * external/yottalab.py: New file containing Roberto Bucher's control
+ library functions. OK to start pulling these into the main library,
+ with attribution, but note that they use modifications of the
+ default library => some rewrites will be needed.
+
+2010-09-11 Richard Murray <mu...@su...>
+
+ * src/matlab.py (step): Added local step response function that uses
+ lsim2() instead of signal.step (which can't handle integrators).
+ This function may not be needed when new scipy step2() function is
+ available.
+ (impulse): Added local impulse response function that sets the
+ initial condition based on the input matrix and then uses the
+ lsim2() function to compute the response.
+
+ * examples/test-response.py: Added test script for making sure that
+ time repsonse functions are working as desired
+
+ * src/matlab.py (lsim): Added local version of lsim that calls
+ signal.lsim2 (actual ODE integrator)
+
+2010-09-06 Richard Murray <mu...@su...>
+
+ * src/statefbk.py (ctrb): new function for testing controllability
+ * src/statefbk.py (obsv): new function for testing observabiilty
+
+2010-09-02 Richard Murray <mu...@su...>
+
+ * src/statefbk.py (place): Use np.size() instead of len() for
+ finding length of placed_eigs for better compatability with
+ different python versions [courtesy of Roberto Bucher]
+
+ * src/delay.py (pade): New file for delay-based computations +
+ initial implementation of pade() [courtesy Sawyer Fuller]
+
+2010-06-17 Richard Murray <mu...@su...>
+
+ * src/rlocus.py: changed num, den to nump, denp for clarity
+ * src/rlocus.py: new file with Ryan Krauss's root locus code
+
+2010-06-06 Richard Murray <mu...@su...>
+
+ * examples/pvtol-lqr.py: Added example to test out LQR routines
+
+ * src/matlab.py (bode): created a wrapper that allows MATLAB style
+ arguments for bode (eg, bode(sys1, sys2))
+
+ * src/ctrlutil.py (issys): added new function to check if an object
+ is a system (state space or transfer function). Will generalize
+ this latter to look for other compatible classes.
+
+ * src/freqplot.py (bode): Compute frequency range of bode plot based
+ on poles and zeros
+ (bode): Allow bode plot to be passed a list (or tuple) as the first
+ argument, in which case multiple bode plots are generated
+
+ * src/statesp.py (StateSpace.zeros): new function to compute zeros
+ for a state space system
+ (StateSpace): defined new functions to compute poles of a state
+ space system
+
+ * src/xferfcn.py (TransferFunction): defined new functions to
+ compute poles and zeros of a transfer function.
+
+2010-05-31 Richard Murray <mu...@su...>
+
+ * src/exception.py (ControlNotImplemented): added new exception, to
+ be used for functions that are not yet implemented
+
+ * src/statefbk.py (lqr): added lqr function (using slycot). Still
+ needs to be verified to make sure calculations are correct.
+
+ * ChangeLog: converted to standard GNU formation (old style below)
+ * setup.py: updated package number to v0.3, changed URL to
+ sourceforge wiki
+
+------------------
+31 May 2010, RMM: added place() function using slycot
+ * New module: statefbk - functions to design state feedback controllers
+ * Uses Enrico Avventi slycot wrappers (http://github.com/avventi/Slycot)
+ * Also added some exception types: ControlSlycot and ControlDimension
+ * Added new example to test slycot interface (directly)
+
+29 May 2010, RMM: updated function documentation
+ * Added __doc__ strings for all current functions
+ * Added __doc__ string to matlab module, listing control toolbox functions
+
+22 May 2010, RMM: tweaked comments and released v0.3a
+ * Changed copyright information on modified files to 2010
+ * Updated "to do" comments to use "#! TODO:" as prefix
+
+11 Feb 2010, GR: implemented and tested state space feedback
+15 Jan 2010, GR: added new example, improved bode
+
+4 Jan 2010, GR: updated bode plots
+ * made bode plot more like matlab
+ * added options for plotting in dB, Hz
+
+27 Dec 2009, GR: important bug fix: feedback TFs were being divided by two
+
+10 Oct 09, RMM: reset matplotlib import in secord-matlab
+ * Using 'from matplotlib import *' causes error with figures
+ * On my other computer, got error when trying to import 'matplotlib.pyplot'
+ * Need to sort out versions and figure out proper import structure
+
+13 Sep 09, RMM: added basic state space functionality
+ * Updated StateSpace routines to allow BD algebra with constants
+ * Updated pvtol-nested example to try to use state space representation
+ (not completely working yet)
+
+12 Sep 09, RMM: code restructuring for transfer functions
+ * Implemented feedback() method in bldalg; partially working
+ for mixture of TF, SS and numbers (not thoroughly tested yet)
+ * New feedback method for TransferFunctions
+ * Updated gangof4 to use new feedback function
+
+9 Sep 09, RMM: updated pzmap to generate a plot (turn off with Plot=False)
+
+8 Sep 09, RMM: rewrite of xferfcn to handle type casting better
+ * Appropriate functions now call convertToTransferFunction
+ * Restricted transfer function to SISO only
+
+7 Sep 09, RMM: additional fixes
+ * Implemented block diagram operations for SISO transfer functions
+ * Modified frequency response functions for transfer functions
+ * Added rudimentary pole/zero computations
+ * Added comments on things that need to be fixed (search for !)
+
+5 Sep 09, RMM: updates to get standard examples working
+ * Copied and converted pvtol_nested.py from AM08, Chapter 11
+ * Updated freqresp to use poly1d for computing values
+ * Added outputs to bode plot - return mag and phase subplot handles
+
+2009-05-24 Richard Murray <mu...@ko...>
+ * ./ Initial creation of package files and ChangeLog
+ * Using simpler text format since emacs python mode doesn't support
+ ChangeLog entries anyway
+
+
+Local Variables:
+mode:text
+End:
Added: branches/control-0.4a/MANIFEST.in
===================================================================
--- branches/control-0.4a/MANIFEST.in (rev 0)
+++ branches/control-0.4a/MANIFEST.in 2011-02-08 18:04:02 UTC (rev 34)
@@ -0,0 +1,5 @@
+include README
+include setup.py
+include src/*.py
+include examples/README examples/*.py
+prune examples/*-test.py
Added: branches/control-0.4a/Pending
===================================================================
--- branches/control-0.4a/Pending (rev 0)
+++ branches/control-0.4a/Pending 2011-02-08 18:04:02 UTC (rev 34)
@@ -0,0 +1,65 @@
+List of Pending changes for control-python
+RMM, 5 Sep 09
+
+This file contains brief notes on features that need to be added to
+the python control library. Mainly intended to keep track of "bigger
+picture" things that need to be done.
+
+--> See src/matlab.py for a list of MATLAB functions that eventually need
+ to be implemented.
+
+OPEN BUGS
+ * step() doesn't handle systems with a pole at the origin (use lsim2)
+
+Transfer code from Roberto Bucher's yottalab to python-control
+ acker - pole placement using Ackermann method
+ c2d - contimous to discrete time conversion
+ full_obs - full order observer
+ red_obs - reduced order observer
+ comp_form - state feedback controller+observer in compact form
+ comp_form_i - state feedback controller+observer+integ in compact form
+ dsimul - simulate discrete time systems
+ dstep - step response (plot) of discrete time systems
+ dimpulse - imoulse response (plot) of discrete time systems
+ bb_step - step response (plot) of continous time systems
+ sysctr - system+controller+observer+feedback
+ care - Solve Riccati equation for contimous time systems
+ dare - Solve Riccati equation for discrete time systems
+ dlqr - discrete linear quadratic regulator
+ minreal - minimal state space representation
+
+Transfer code from Ryan Krauss's control.py to python-control
+ * phase margin computations (as part of margin command)
+ * step reponse
+ * c2d, c2d_tustin (compare to Bucher version first)
+
+Examples and test cases
+ * Put together unit tests for all functions (after deciding on framework)
+ * Figure out how to import 'figure' command properly (version issue?)
+ * Figure out source of BadCoefficients warning messages (pvtol-lqr and others)
+
+TransferFunction class fixes
+ * evalfr is not working (num, den stored as ndarrays, not poly1ds)
+
+Block diagram algebra fixes
+ * Implement state space block diagram algebra
+ * Produce minimal realizations to avoid later errors
+
+State space class fixes
+ * Convert pvtol to state space systems and rerun example
+ * Implement pzmap for state space systems
+
+LTI updates
+ * Implement control.matlab.step (with semantics similar to MATLAB)
+
+Basic functions to be added
+ * margin - compute gain and phase margin (no plot)
+ * lqr - compute optimal feedback gains (use SLICOT SB02ND.f)
+ * lyap - solve Lyapunov equation (use SLICOT SB03MD.f)
+ * See http://www.slicot.org/shared/libindex.html for list of functions
+
+----
+Instructions for building python package
+ * python setup.py build
+ * python setup.py install
+ * python setup.py sdist
Added: branches/control-0.4a/README
===================================================================
--- branches/control-0.4a/README (rev 0)
+++ branches/control-0.4a/README 2011-02-08 18:04:02 UTC (rev 34)
@@ -0,0 +1,18 @@
+Python Control System Library
+RMM, 23 May 09
+
+This directory contains the source code for the Python Control Systems
+Library (python-control). This library is still under development,
+but is intended to serve as a wrapper for standard control system
+algorithms in the python programming environment.
+
+Installation instructions
+-------------------------
+Standard python package installation:
+
+ python setup.py install
+
+To see if things are working, you can run the script
+examples/secord-matlab.py (using ipython -pylab). It should generate
+a step response, Bode plot and Nyquist plot for a simple second order
+linear system.
Added: branches/control-0.4a/build/lib/control/TestBDAlg.py
===================================================================
--- branches/control-0.4a/build/lib/control/TestBDAlg.py (rev 0)
+++ branches/control-0.4a/build/lib/control/TestBDAlg.py 2011-02-08 18:04:02 UTC (rev 34)
@@ -0,0 +1,166 @@
+#!/usr/bin/env python
+
+import numpy as np
+from xferfcn import TransferFunction
+from statesp import StateSpace
+from bdalg import feedback
+import unittest
+
+class TestFeedback(unittest.TestCase):
+ """These are tests for the feedback function in bdalg.py. Currently, some
+ of the tests are not implemented, or are not working properly. TODO: these
+ need to be fixed."""
+
+ def setUp(self):
+ """This contains some random LTI systems and scalars for testing."""
+
+ # Two random SISO systems.
+ self.sys1 = TransferFunction([1, 2], [1, 2, 3])
+ self.sys2 = StateSpace([[1., 4.], [3., 2.]], [[1.], [-4.]],
+ [[1., 0.]], [[0.]])
+ # Two random scalars.
+ self.x1 = 2.5
+ self.x2 = -3.
+
+ def testScalarScalar(self):
+ """Scalar system with scalar feedback block."""
+
+ ans1 = feedback(self.x1, self.x2)
+ ans2 = feedback(self.x1, self.x2, 1.)
+
+ self.assertAlmostEqual(ans1.num[0][0][0] / ans1.den[0][0][0],
+ -2.5 / 6.5)
+ self.assertAlmostEqual(ans2.num[0][0][0] / ans2.den[0][0][0], 2.5 / 8.5)
+
+ def testScalarSS(self):
+ """Scalar system with state space feedback block."""
+
+ ans1 = feedback(self.x1, self.sys2)
+ ans2 = feedback(self.x1, self.sys2, 1.)
+
+ np.testing.assert_array_almost_equal(ans1.A, [[-1.5, 4.], [13., 2.]])
+ np.testing.assert_array_almost_equal(ans1.B, [[2.5], [-10.]])
+ np.testing.assert_array_almost_equal(ans1.C, [[-2.5, 0.]])
+ np.testing.assert_array_almost_equal(ans1.D, [[2.5]])
+ np.testing.assert_array_almost_equal(ans2.A, [[3.5, 4.], [-7., 2.]])
+ np.testing.assert_array_almost_equal(ans2.B, [[2.5], [-10.]])
+ np.testing.assert_array_almost_equal(ans2.C, [[2.5, 0.]])
+ np.testing.assert_array_almost_equal(ans2.D, [[2.5]])
+
+ def testScalarTF(self):
+ """Scalar system with transfer function feedback block."""
+
+ ans1 = feedback(self.x1, self.sys1)
+ ans2 = feedback(self.x1, self.sys1, 1.)
+
+ np.testing.assert_array_almost_equal(ans1.num, [[[2.5, 5., 7.5]]])
+ np.testing.assert_array_almost_equal(ans1.den, [[[1., 4.5, 8.]]])
+ np.testing.assert_array_almost_equal(ans2.num, [[[2.5, 5., 7.5]]])
+ np.testing.assert_array_almost_equal(ans2.den, [[[1., -0.5, -2.]]])
+
+ def testSSScalar(self):
+ """State space system with scalar feedback block."""
+
+ ans1 = feedback(self.sys2, self.x1)
+ ans2 = feedback(self.sys2, self.x1, 1.)
+
+ np.testing.assert_array_almost_equal(ans1.A, [[-1.5, 4.], [13., 2.]])
+ np.testing.assert_array_almost_equal(ans1.B, [[1.], [-4.]])
+ np.testing.assert_array_almost_equal(ans1.C, [[1., 0.]])
+ np.testing.assert_array_almost_equal(ans1.D, [[0.]])
+ np.testing.assert_array_almost_equal(ans2.A, [[3.5, 4.], [-7., 2.]])
+ np.testing.assert_array_almost_equal(ans2.B, [[1.], [-4.]])
+ np.testing.assert_array_almost_equal(ans2.C, [[1., 0.]])
+ np.testing.assert_array_almost_equal(ans2.D, [[0.]])
+
+ def testSSSS1(self):
+ """State space system with state space feedback block."""
+
+ ans1 = feedback(self.sys2, self.sys2)
+ ans2 = feedback(self.sys2, self.sys2, 1.)
+
+ np.testing.assert_array_almost_equal(ans1.A, [[1., 4., -1., 0.],
+ [3., 2., 4., 0.], [1., 0., 1., 4.], [-4., 0., 3., 2]])
+ np.testing.assert_array_almost_equal(ans1.B, [[1.], [-4.], [0.], [0.]])
+ np.testing.assert_array_almost_equal(ans1.C, [[1., 0., 0., 0.]])
+ np.testing.assert_array_almost_equal(ans1.D, [[0.]])
+ np.testing.assert_array_almost_equal(ans2.A, [[1., 4., 1., 0.],
+ [3., 2., -4., 0.], [1., 0., 1., 4.], [-4., 0., 3., 2.]])
+ np.testing.assert_array_almost_equal(ans2.B, [[1.], [-4.], [0.], [0.]])
+ np.testing.assert_array_almost_equal(ans2.C, [[1., 0., 0., 0.]])
+ np.testing.assert_array_almost_equal(ans2.D, [[0.]])
+
+ def testSSSS2(self):
+ """State space system with state space feedback block, including a
+ direct feedthrough term."""
+
+ sys3 = StateSpace([[-1., 4.], [2., -3]], [[2.], [3.]], [[-3., 1.]],
+ [[-2.]])
+ sys4 = StateSpace([[-3., -2.], [1., 4.]], [[-2.], [-6.]], [[2., -3.]],
+ [[3.]])
+
+ ans1 = feedback(sys3, sys4)
+ ans2 = feedback(sys3, sys4, 1.)
+
+ np.testing.assert_array_almost_equal(ans1.A,
+ [[-4.6, 5.2, 0.8, -1.2], [-3.4, -1.2, 1.2, -1.8],
+ [-1.2, 0.4, -1.4, -4.4], [-3.6, 1.2, 5.8, -3.2]])
+ np.testing.assert_array_almost_equal(ans1.B,
+ [[-0.4], [-0.6], [-0.8], [-2.4]])
+ np.testing.assert_array_almost_equal(ans1.C, [[0.6, -0.2, -0.8, 1.2]])
+ np.testing.assert_array_almost_equal(ans1.D, [[0.4]])
+ np.testing.assert_array_almost_equal(ans2.A,
+ [[-3.57142857142857, 4.85714285714286, 0.571428571428571,
+ -0.857142857142857],
+ [-1.85714285714286, -1.71428571428571, 0.857142857142857,
+ -1.28571428571429],
+ [0.857142857142857, -0.285714285714286, -1.85714285714286,
+ -3.71428571428571],
+ [2.57142857142857, -0.857142857142857, 4.42857142857143,
+ -1.14285714285714]])
+ np.testing.assert_array_almost_equal(ans2.B, [[0.285714285714286],
+ [0.428571428571429], [0.571428571428571], [1.71428571428571]])
+ np.testing.assert_array_almost_equal(ans2.C, [[-0.428571428571429,
+ 0.142857142857143, -0.571428571428571, 0.857142857142857]])
+ np.testing.assert_array_almost_equal(ans2.D, [[-0.285714285714286]])
+
+
+ def testSSTF(self):
+ """State space system with transfer function feedback block."""
+
+ # This functionality is not implemented yet.
+ pass
+
+ def testTFScalar(self):
+ """Transfer function system with scalar feedback block."""
+
+ ans1 = feedback(self.sys1, self.x1)
+ ans2 = feedback(self.sys1, self.x1, 1.)
+
+ np.testing.assert_array_almost_equal(ans1.num, [[[1., 2.]]])
+ np.testing.assert_array_almost_equal(ans1.den, [[[1., 4.5, 8.]]])
+ np.testing.assert_array_almost_equal(ans2.num, [[[1., 2.]]])
+ np.testing.assert_array_almost_equal(ans2.den, [[[1., -0.5, -2.]]])
+
+ def testTFSS(self):
+ """Transfer function system with state space feedback block."""
+
+ # This functionality is not implemented yet.
+ pass
+
+ def testTFTF(self):
+ """Transfer function system with transfer function feedback block."""
+
+ ans1 = feedback(self.sys1, self.sys1)
+ ans2 = feedback(self.sys1, self.sys1, 1.)
+
+ np.testing.assert_array_almost_equal(ans1.num, [[[1., 4., 7., 6.]]])
+ np.testing.assert_array_almost_equal(ans1.den,
+ [[[1., 4., 11., 16., 13.]]])
+ np.testing.assert_array_almost_equal(ans2.num, [[[1., 4., 7., 6.]]])
+ np.testing.assert_array_almost_equal(ans2.den, [[[1., 4., 9., 8., 5.]]])
+def suite():
+ return unittest.TestLoader().loadTestsFromTestCase(TestFeedback)
+
+if __name__ == "__main__":
+ unittest.main()
Added: branches/control-0.4a/build/lib/control/TestConvert.py
===================================================================
--- branches/control-0.4a/build/lib/control/TestConvert.py (rev 0)
+++ branches/control-0.4a/build/lib/control/TestConvert.py 2011-02-08 18:04:02 UTC (rev 34)
@@ -0,0 +1,91 @@
+#!/usr/bin/env python
+
+"""TestConvert.py
+
+Test state space and transfer function conversion.
+
+Currently, this unit test script is not complete. It converts several random
+state spaces back and forth between state space and transfer function
+representations. Ideally, it should be able to assert that the conversion
+outputs are correct. This is not yet implemented.
+
+Also, the conversion seems to enter an infinite loop once in a while. The cause
+of this is unknown.
+
+"""
+
+import numpy as np
+import matlab
+import unittest
+
+class TestConvert(unittest.TestCase):
+ """Test state space and transfer function conversions."""
+
+ def setUp(self):
+ """Set up testing parameters."""
+
+ # Number of times to run each of the randomized tests.
+ self.numTests = 1 #almost guarantees failure
+ # Maximum number of states to test + 1
+ self.maxStates = 20
+ # Maximum number of inputs and outputs to test + 1
+ self.maxIO = 20
+ # Set to True to print systems to the output.
+ self.debug = True
+
+ def printSys(self, sys, ind):
+ """Print system to the standard output."""
+
+ if self.debug:
+ print "sys%i:\n" % ind
+ print sys
+
+ def testConvert(self):
+ """Test state space to transfer function conversion."""
+ #Currently it only tests that a TF->SS->TF generates an unchanged TF
+
+ #print __doc__
+
+ for states in range(1, self.maxStates):
+ for inputs in range(1, self.maxIO):
+ for outputs in range(1, self.maxIO):
+ #start with a random SS system and transform to TF
+ #then back to SS, check that the matrices are the same.
+ ssOriginal = matlab.rss(states, inputs, outputs)
+ self.printSys(ssOriginal, 1)
+
+ tfOriginal = matlab.tf(ssOriginal)
+ self.printSys(tfOriginal, 2)
+
+ ssTransformed = matlab.ss(tfOriginal)
+ self.printSys(ssTransformed, 3)
+
+ tfTransformed = matlab.tf(ssTransformed)
+ self.printSys(tfTransformed, 4)
+
+ for inputNum in range(inputs):
+ for outputNum in range(outputs):
+ np.testing.assert_array_almost_equal(\
+ tfOriginal.num[outputNum][inputNum],\
+ tfTransformed.num[outputNum][inputNum])
+
+ np.testing.assert_array_almost_equal(\
+ tfOriginal.den[outputNum][inputNum],\
+ tfTransformed.den[outputNum][inputNum])
+
+ #To test the ss systems is harder because they aren't the same
+ #realization. This could be done with checking that they have the
+ #same freq response with bode, but apparently it doesn't work
+ #the way it should right now:
+ ## Bode should work like this:
+ #[mag,phase,freq]=bode(sys)
+ #it doesn't seem to......
+ #This should be added.
+
+
+
+def suite():
+ return unittest.TestLoader().loadTestsFromTestCase(TestConvert)
+
+if __name__ == "__main__":
+ unittest.main()
Added: branches/control-0.4a/build/lib/control/TestFreqRsp.py
===================================================================
--- branches/control-0.4a/build/lib/control/TestFreqRsp.py (rev 0)
+++ branches/control-0.4a/build/lib/control/TestFreqRsp.py 2011-02-08 18:04:02 UTC (rev 34)
@@ -0,0 +1,63 @@
+#!/usr/bin/env python
+
+### MUST BE CONVERTED TO A UNIT TEST!!!
+
+
+# Script to test frequency response and frequency response plots like bode, nyquist and gang of 4.
+# Especially need to ensure that nothing SISO is broken and that MIMO at least handles exceptions and has some default to SISO in place.
+
+
+import unittest
+from statesp import StateSpace
+from matlab import ss, tf, bode
+import numpy as np
+import matplotlib.pyplot as plt
+
+# SISO
+plt.close('all')
+
+A = np.matrix('1,1;0,1')
+B = np.matrix('0;1')
+C = np.matrix('1,0')
+D = 0
+sys = StateSpace(A,B,C,D)
+#or try
+#sys = ss(A,B,C,D)
+
+# test frequency response
+omega = np.linspace(10e-2,10e2,1000)
+frq=sys.freqresp(omega)
+
+# MIMO
+B = np.matrix('1,0;0,1')
+D = np.matrix('0,0')
+sysMIMO = ss(A,B,C,D)
+frqMIMO = sysMIMO.freqresp(omega)
+
+plt.figure(1)
+bode(sys)
+
+systf = tf(sys)
+tfMIMO = tf(sysMIMO)
+
+print systf.pole()
+#print tfMIMO.pole() # - should throw not implemented exception
+#print tfMIMO.zero() # - should throw not implemented exception
+
+plt.figure(2)
+bode(systf)
+
+#bode(sysMIMO) # - should throw not implemented exception
+#bode(tfMIMO) # - should throw not implemented exception
+
+#plt.figure(3)
+#plt.semilogx(omega,20*np.log10(np.squeeze(frq[0])))
+
+#plt.figure(4)
+#bode(sysMIMO,omega)
+
+
+def suite():
+ pass
+ #Uncomment this once it is a real unittest
+ #return unittest.TestLoader().loadTestsFromTestCase(TestFreqRsp)
Added: branches/control-0.4a/build/lib/control/TestMatlab.py
===================================================================
--- branches/control-0.4a/build/lib/control/TestMatlab.py (rev 0)
+++ branches/control-0.4a/build/lib/control/TestMatlab.py 2011-02-08 18:04:02 UTC (rev 34)
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+
+from matlab import *
+import numpy as np
+import unittest
+
+class TestMatlab(unittest.TestCase):
+ def testStep(self):
+ A = np.matrix("1. -2.; 3. -4.")
+ B = np.matrix("5.; 7.")
+ C = np.matrix("6. 8.")
+ D = np.matrix("9.")
+ sys = ss(A,B,C,D)
+ t = np.linspace(0, 1, 10)
+ t, yout = step(sys, T=t)
+ youttrue = np.matrix("9. 17.6457 24.7072 30.4855 35.2234 39.1165 42.3227 44.9694 47.1599 48.9776")
+ np.testing.assert_array_almost_equal(yout, youttrue,decimal=4)
+
+ def testImpulse(self):
+ A = np.matrix("1. -2.; 3. -4.")
+ B = np.matrix("5.; 7.")
+ C = np.matrix("6. 8.")
+ D = np.matrix("9.")
+ sys = ss(A,B,C,D)
+ t = np.linspace(0, 1, 10)
+ t, yout = impulse(sys, T=t)
+ youttrue = np.matrix("86. 70.1808 57.3753 46.9975 38.5766 31.7344 26.1668 21.6292 17.9245 14.8945")
+ np.testing.assert_array_almost_equal(yout, youttrue,decimal=4)
+
+# def testInitial(self):
+# A = np.matrix("1. -2.; 3. -4.")
+# B = np.matrix("5.; 7.")
+# C = np.matrix("6. 8.")
+# D = np.matrix("9.")
+# sys = ss(A,B,C,D)
+# t = np.linspace(0, 1, 10)
+# x0 = np.matrix(".5; 1.")
+# t, yout = initial(sys, T=t, X0=x0)
+# youttrue = np.matrix("11. 8.1494 5.9361 4.2258 2.9118 1.9092 1.1508 0.5833 0.1645 -0.1391")
+# np.testing.assert_array_almost_equal(yout, youttrue,decimal=4)
+
+
+def suite():
+ return unittest.TestLoader().loadTestsFromTestCase(TestMatlab)
+
+if __name__ == '__main__':
+ unittest.main()
Added: branches/control-0.4a/build/lib/control/TestModelsimp.py
===================================================================
--- branches/control-0.4a/build/lib/control/TestModelsimp.py (rev 0)
+++ branches/control-0.4a/build/lib/control/TestModelsimp.py 2011-02-08 18:04:02 UTC (rev 34)
@@ -0,0 +1,96 @@
+#!/usr/bin/env python
+
+from modelsimp import *
+from matlab import *
+import numpy as np
+import unittest
+
+class TestModelsimp(unittest.TestCase):
+ def testHSVD(self):
+ A = np.matrix("1. -2.; 3. -4.")
+ B = np.matrix("5.; 7.")
+ C = np.matrix("6. 8.")
+ D = np.matrix("9.")
+ sys = ss(A,B,C,D)
+ hsv = hsvd(sys)
+ hsvtrue = np.matrix("24.42686 0.5731395")
+ np.testing.assert_array_almost_equal(hsv, hsvtrue)
+
+ def testMarkov(self):
+ U = np.matrix("1.; 1.; 1.; 1.; 1.")
+ Y = U
+ M = 3
+ H = markov(Y,U,M)
+ Htrue = np.matrix("1.; 0.; 0.")
+ np.testing.assert_array_almost_equal( H, Htrue )
+
+ def testModredMatchDC(self):
+ #balanced realization computed in matlab for the transfer function:
+ # num = [1 11 45 32], den = [1 15 60 200 60]
+ A = np.matrix('-1.958, -1.194, 1.824, -1.464; \
+ -1.194, -0.8344, 2.563, -1.351; \
+ -1.824, -2.563, -1.124, 2.704; \
+ -1.464, -1.351, -2.704, -11.08')
+ B = np.matrix('-0.9057; -0.4068; -0.3263; -0.3474')
+ C = np.matrix('-0.9057, -0.4068, 0.3263, -0.3474')
+ D = np.matrix('0.')
+ sys = ss(A,B,C,D)
+ rsys = modred(sys,[2, 3],'matchdc')
+ Artrue = np.matrix('-4.431, -4.552; -4.552, -5.361')
+ Brtrue = np.matrix('-1.362; -1.031')
+ Crtrue = np.matrix('-1.362, -1.031')
+ Drtrue = np.matrix('-0.08384')
+ np.testing.assert_array_almost_equal(rsys.A, Artrue,decimal=3)
+ np.testing.assert_array_almost_equal(rsys.B, Brtrue,decimal=3)
+ np.testing.assert_array_almost_equal(rsys.C, Crtrue,decimal=3)
+ np.testing.assert_array_almost_equal(rsys.D, Drtrue,decimal=2)
+
+ def testModredTruncate(self):
+ #balanced realization computed in matlab for the transfer function:
+ # num = [1 11 45 32], den = [1 15 60 200 60]
+ A = np.matrix('-1.958, -1.194, 1.824, -1.464; \
+ -1.194, -0.8344, 2.563, -1.351; \
+ -1.824, -2.563, -1.124, 2.704; \
+ -1.464, -1.351, -2.704, -11.08')
+ B = np.matrix('-0.9057; -0.4068; -0.3263; -0.3474')
+ C = np.matrix('-0.9057, -0.4068, 0.3263, -0.3474')
+ D = np.matrix('0.')
+ sys = ss(A,B,C,D)
+ rsys = modred(sys,[2, 3],'truncate')
+ Artrue = np.matrix('-1.958, -1.194; -1.194, -0.8344')
+ Brtrue = np.matrix('-0.9057; -0.4068')
+ Crtrue = np.matrix('-0.9057, -0.4068')
+ Drtrue = np.matrix('0.')
+ np.testing.assert_array_almost_equal(rsys.A, Artrue)
+ np.testing.assert_array_almost_equal(rsys.B, Brtrue)
+ np.testing.assert_array_almost_equal(rsys.C, Crtrue)
+ np.testing.assert_array_almost_equal(rsys.D, Drtrue)
+
+ def testBalredTruncate(self):
+ #controlable canonical realization computed in matlab for the transfer function:
+ # num = [1 11 45 32], den = [1 15 60 200 60]
+ A = np.matrix('-15., -7.5, -6.25, -1.875; \
+ 8., 0., 0., 0.; \
+ 0., 4., 0., 0.; \
+ 0., 0., 1., 0.')
+ B = np.matrix('2.; 0.; 0.; 0.')
+ C = np.matrix('0.5, 0.6875, 0.7031, 0.5')
+ D = np.matrix('0.')
+ sys = ss(A,B,C,D)
+ orders = 2
+ rsys = balred(sys,orders,method='truncate')
+ Artrue = np.matrix('-1.958, -1.194; -1.194, -0.8344')
+ Brtrue = np.matrix('0.9057; 0.4068')
+ Crtrue = np.matrix('0.9057, 0.4068')
+ Drtrue = np.matrix('0.')
+ np.testing.assert_array_almost_equal(rsys.A, Artrue,decimal=2)
+ np.testing.assert_array_almost_equal(rsys.B, Brtrue,decimal=4)
+ np.testing.assert_array_almost_equal(rsys.C, Crtrue,decimal=4)
+ np.testing.assert_array_almost_equal(rsys.D, Drtrue,decimal=4)
+
+def suite():
+ return unittest.TestLoader().loadTestsFromTestCase(TestModelsimp)
+
+
+if __name__ == '__main__':
+ unittest.main()
Added: branches/control-0.4a/build/lib/control/TestSlycot.py
===================================================================
--- branches/control-0.4a/build/lib/control/TestSlycot.py (rev 0)
+++ branches/control-0.4a/build/lib/control/TestSlycot.py 2011-02-08 18:04:02 UTC (rev 34)
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+
+#### THIS MUST BE MADE INTO A UNITTEST TO BE PART OF THE TESTING FUNCTIONS!!!!
+
+
+import numpy as np
+from slycot import tb04ad, td04ad
+import matlab
+
+numTests = 1
+maxStates = 3
+maxIO = 3
+
+for states in range(1, maxStates):
+ for inputs in range(1, maxIO):
+ for outputs in range(1, maxIO):
+ sys1 = matlab.rss(states, inputs, outputs)
+ print "sys1"
+ print sys1
+
+ sys2 = tb04ad(states,inputs,outputs,sys1.A,sys1.B,sys1.C,sys1.D,outputs,outputs,inputs)
+ print "sys2"
+ print sys2
+
+ ldwork = 100*max(1,states+max(states,max(3*inputs,3*outputs)))
+ dwork = np.zeros(ldwork)
+ sys3 = td04ad(inputs,outputs,sys2[4],sys2[5],sys2[6])
+ #sys3 = td04ad(inputs,outputs,sys2[4],sys2[5],sys2[6],ldwork)
+ print "sys3"
+ print sys3
+
+ sys4 = tb04ad(states,inputs,outputs,sys3[1][0:states,0:states],sys3[2][0:states,0:inputs],sys3[3][0:outputs,0:states],sys3[4],outputs,outputs,inputs)
+ print "sys4"
+ print sys4
+
+#These are here for once the above is made into a unittest.
+def suite():
+ return unittest.TestLoader().loadTestsFromTestCase(TestSlycot)
+
+if __name__=='__main__':
+ unittest.main()
+
Added: branches/control-0.4a/build/lib/control/TestStateSp.py
===================================================================
--- branches/control-0.4a/build/lib/control/TestStateSp.py (rev 0)
+++ branches/control-0.4a/build/lib/control/TestStateSp.py 2011-02-08 18:04:02 UTC (rev 34)
@@ -0,0 +1,209 @@
+#!/usr/bin/env python
+
+import numpy as np
+import unittest
+import matlab
+from statesp import StateSpace
+
+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."""
+
+ 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."""
+
+ def setUp(self):
+ # Number of times to run each of the randomized tests.
+ self.numTests = 100
+ # Maxmimum number of states to test + 1
+ self.maxStates = 10
+ # Maximum number of inputs and outputs to test + 1
+ self.maxIO = 5
+
+ def testShape(self):
+ """Test that rss outputs have the right state, input, and output
+ size."""
+
+ for states in range(1, self.maxStates):
+ for inputs in range(1, self.maxIO):
+ for outputs in range(1, self.maxIO):
+ sys = matlab.rss(states, inputs, outputs)
+ self.assertEqual(sys.states, states)
+ self.assertEqual(sys.inputs, inputs)
+ self.assertEqual(sys.outputs, outputs)
+
+ def testPole(self):
+ """Test that the poles of rss outputs have a negative real part."""
+
+ for states in range(1, self.maxStates):
+ for inputs in range(1, self.maxIO):
+ for outputs in range(1, self.maxIO):
+ sys = matlab.rss(states, inputs, outputs)
+ p = sys.pole()
+ for z in p:
+ self.assertTrue(z.real < 0)
+
+class TestDrss(unittest.TestCase):
+ """These are tests for the proper functionality of statesp.drss."""
+
+ def setUp(self):
+ # Number of times to run each of the randomized tests.
+ self.numTests = 100
+ # Maxmimum number of states to test + 1
+ self.maxStates = 10
+ # Maximum number of inputs and outputs to test + 1
+ self.maxIO = 5
+
+ def testShape(self):
+ """Test that drss outputs have the right state, input, and output
+ size."""
+
+ for states in range(1, self.maxStates):
+ for inputs in range(1, self.maxIO):
+ for outputs in range(1, self.maxIO):
+ sys = matlab.drss(states, inputs, outputs)
+ self.assertEqual(sys.states, states)
+ self.assertEqual(sys.inputs, inputs)
+ self.assertEqual(sys.outputs, outputs)
+
+ def testPole(self):
+ """Test that the poles of drss outputs have less than unit magnitude."""
+
+ for states in range(1, self.maxStates):
+ for inputs in range(1, self.maxIO):
+ for outputs in range(1, self.maxIO):
+ sys = matlab.drss(states, inputs, outputs)
+ p = sys.pole()
+ for z in p:
+ self.assertTrue(abs(z) < 1)
+
+
+def suite():
+ return unittest.TestLoader().loadTestsFromTestCase(TestStateSpace)
+
+
+if __name__ == "__main__":
+ unittest.main()
Added: branches/control-0.4a/build/lib/control/TestStatefbk.py
===================================================================
--- branches/control-0.4a/build/lib/control/TestStatefbk.py (rev 0)
+++ branches/control-0.4a/build/lib/control/TestStatefbk.py 2011-02-08 18:04:02 UTC (rev 34)
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+
+from statefbk import ctrb, obsv, place, lqr, gram
+from matlab import *
+import numpy as np
+import unittest
+
+class TestStatefbk(unittest.TestCase):
+ def testCtrbSISO(self):
+ A = np.matrix("1. 2.; 3. 4.")
+ B = np.matrix("5.; 7.")
+ Wctrue = np.matrix("5. 19.; 7. 43.")
+ Wc = ctrb(A,B)
+ np.testing.assert_array_almost_equal(Wc, Wctrue)
+
+ def testCtrbMIMO(self):
+ A = np.matrix("1. 2.; 3. 4.")
+ B = np.matrix("5. 6.; 7. 8.")
+ Wctrue = np.matrix("5. 6. 19. 22.; 7. 8. 43. 50.")
+ Wc = ctrb(A,B)
+ np.testing.assert_array_almost_equal(Wc, Wctrue)
+
+ def testObsvSISO(self):
+ A = np.matrix("1. 2.; 3. 4.")
+ C = np.matrix("5. 7.")
+ Wotrue = np.matrix("5. 7.; 26. 38.")
+ Wo = obsv(A,C)
+ np.testing.assert_array_almost_equal(Wo, Wotrue)
+
+ def testObsvMIMO(self):
+ A = np.matrix("1. 2.; 3. 4.")
+ C = np.matrix("5. 6.; 7. 8.")
+ Wotrue = np.matrix("5. 6.; 7. 8.; 23. 34.; 31. 46.")
+ Wo = obsv(A,C)
+ np.testing.assert_array_almost_equal(Wo, Wotrue)
+
+ def testCtrbObsvDuality(self):
+ A = np.matrix("1.2 -2.3; 3.4 -4.5")
+ B = np.matrix("5.8 6.9; 8. 9.1")
+ Wc = ctrb(A,B);
+ A = np.transpose(A)
+ C = np.transpose(B)
+ Wo = np.transpose(obsv(A,C));
+ np.testing.assert_array_almost_equal(Wc,Wo)
+
+ def testGramWc(self):
+ A = np.matrix("1. -2.; 3. -4.")
+ B = np.matrix("5. 6.; 7. 8.")
+ C = np.matrix("4. 5.; 6. 7.")
+ D = np.matrix("13. 14.; 15. 16.")
+ sys = ss(A, B, C, D)
+ Wctrue = np.matrix("18.5 24.5; 24.5 32.5")
+ Wc = gram(sys,'c')
+ np.testing.assert_array_almost_equal(Wc, Wctrue)
+
+ def testGramWo(self):
+ A = np.matrix("1. -2.; 3. -4.")
+ B = np.matrix("5. 6.; 7. 8.")
+ C = np.matrix("4. 5.; 6. 7.")
+ D = np.matrix("13. 14.; 15. 16.")
+ sys = ss(A, B, C, D)
+ Wotrue = np.matrix("257.5 -94.5; -94.5 56.5")
+ Wo = gram(sys,'o')
+ np.testing.assert_array_almost_equal(Wo, Wotrue)
+
+ def testGramWo2(self):
+ A = np.matrix("1. -2.; 3. -4.")
+ B = np.matrix("5.; 7.")
+ C = np.matrix("6. 8.")
+ D = np.matrix("9.")
+ sys = ss(A,B,C,D)
+ Wotrue = np.matrix("198. -72.; -72. 44.")
+ Wo = gram(sys,'o')
+ np.testing.assert_array_almost_equal(Wo, Wotrue)
+
+ def testGramsys(self):
+ num =[1.]
+ den = [1., 1., 1.]
+ sys = tf(num,den)
+ self.assertRaises(ValueError, gram, sys, 'o')
+ self.assertRaises(ValueError, gram, sys, 'c')
+
+def suite():
+ return unittest.TestLoader().loadTestsFromTestCase(TestStatefbk)
+
+
+if __name__ == '__main__':
+ unittest.main()
Added: branches/control-0.4a/build/lib/control/TestXferFcn.py
===================================================================
--- branches/control-0.4a/build/lib/control/TestXferFcn.py (rev 0)
+++ branches/control-0.4a/build/lib/control/TestXferFcn.py 2011-02-08 18:04:02 UTC (rev 34)
@@ -0,0 +1,437 @@
+#!/usr/bin/env python
+
+import numpy as np
+from statesp import StateSpace
+from xferfcn import TransferFunction, _convertToTransferFunction
+import unittest
+
+class TestXferFcn(unittest.TestCase):
+ """These are tests for functionality and correct reporting of the transfer
+ function class. Throughout these tests, we will give different input
+ formats to the xTranferFunction constructor, to try to break it. These
+ tests have been verified in MATLAB."""
+
+ # Tests for raising exceptions.
+
+ def testBadInputType(self):
+ """Give the constructor invalid input types."""
+
+ self.assertRaises(TypeError, TransferFunction, [[0., 1.], [2., 3.]],
+ [[5., 2.], [3., 0.]])
+
+ def testInconsistentDimension(self):
+ """Give the constructor a numerator and denominator of different
+ sizes."""
+
+ self.assertRaises(ValueError, TransferFunction, [[[1.]]],
+ [[[1.], [2., 3.]]])
+ self.assertRaises(ValueError, TransferFunction, [[[1.]]],
+ [[[1.]], [[2., 3.]]])
+ self.assertRaises(ValueError, TransferFunction, [[[1.]]],
+ [[[1.], [1., 2.]], [[5., 2.], [2., 3.]]])
+
+ def testInconsistentColumns(self):
+ """Give the constructor inputs that do not have the same number of
+ columns in each row."""
+
+ self.assertRaises(ValueError, TransferFunction, 1.,
+ [[[1.]], [[2.], [3.]]])
+ self.assertRaises(ValueError, TransferFunction, [[[1.]], [[2.], [3.]]],
+ 1.)
+
+ def testZeroDenominator(self):
+ """Give the constructor a transfer function with a zero denominator."""
+
+ self.assertRaises(ValueError, TransferFunction, 1., 0.)
+ self.assertRaises(ValueError, TransferFunction,
+ [[[1.], [2., 3.]], [[-1., 4.], [3., 2.]]],
+ [[[1., 0.], [0.]], [[0., 0.], [2.]]])
+
+ def testAddInconsistentDimension(self):
+ """Add two transfer function matrices of different sizes."""
+
+ sys1 = TransferFunction([[[1., 2.]]], [[[4., 5.]]])
+ sys2 = TransferFunction([[[4., 3.]], [[1., 2.]]],
+ [[[1., 6.]], [[2., 4.]]])
+ self.assertRaises(ValueError, sys1.__add__, sys2)
+ self.assertRaises(ValueError, sys1.__sub__, sys2)
+ self.assertRaises(ValueError, sys1.__radd__, sys2)
+ self.assertRaises(ValueError, sys1.__rsub__, sys2)
+
+ def testMulInconsistentDimension(self):
+ """Multiply two transfer function matrices of incompatible sizes."""
+
+ sys1 = TransferFunction([[[1., 2.], [4., 5.]], [[2., 5.], [4., 3.]]],
+ [[[6., 2.], [4., 1.]], [[6., 7.], [2., 4.]]])
+ sys2 = TransferFunction([[[1.]], [[2.]], [[3.]]],
+ [[[4.]], [[5.]], [[6.]]])
+ self.assertRaises(ValueError, sys1.__mul__, sys2)
+ self.assertRaises(ValueError, sys2.__mul__, sys1)
+ self.assertRaises(ValueError, sys1.__rmul__, sys2)
+ self.assertRaises(ValueError, sys2.__rmul__, sys1)
+
+ # Tests for TransferFunction._truncatecoeff
+
+ def testTruncateCoeff1(self):
+ """Remove extraneous zeros in polynomial representations."""
+
+ sys1 = TransferFunction([0., 0., 1., 2.], [[[0., 0., 0., 3., 2., 1.]]])
+
+ np.testing.assert_array_equal(sys1.num, [[[1., 2.]]])
+ np.testing.assert_array_equal(sys1.den, [[[3., 2., 1.]]])
+
+ def testTruncateCoeff2(self):
+ """Remove extraneous zeros in polynomial representations."""
+
+ sys1 = TransferFunction([0., 0., 0.], 1.)
+
+ np.testing.assert_array_equal(sys1.num, [[[0.]]])
+ np.testing.assert_array_equal(sys1.den, [[[1.]]])
+
+ # Tests for TransferFunction.__neg__
+
+ def testNegScalar(self):
+ """Negate a direct feedthrough system."""
+
+ sys1 = TransferFunction(2., np.array([-3]))
+ sys2 = - sys1
+
+ np.testing.assert_array_equal(sys2.num, [[[-2.]]])
+ np.testing.assert_array_equal(sys2.den, [[[-3.]]])
+
+ def testNegSISO(self):
+ """Negate a SISO system."""
+
+ sys1 = TransferFunction([1., 3., 5], [1., 6., 2., -1.])
+ sys2 = - sys1
+
+ np.testing.assert_array_equal(sys2.num, [[[-1., -3., -5.]]])
+ np.testing.assert_array_equal(sys2.den, [[[1., 6., 2., -1.]]])
+
+ def testNegMIMO(self):
+ """Negate a MIMO system."""
+
+ num1 = [[[1., 2.], [0., 3.], [2., -1.]],
+ [[1.], [4., 0.], [1., -4., 3.]]]
+ num3 = [[[-1., -2.], [0., -3.], [-2., 1.]],
+ [[-1.], [-4., 0.], [-1., 4., -3.]]]
+ den1 = [[[-3., 2., 4.], [1., 0., 0.], [2., -1.]],
+ [[3., 0., .0], [2., -1., -1.], [1.]]]
+
+ sys1 = TransferFunction(num1, den1)
+ sys2 = - sys1
+ sys3 = TransferFunction(num3, den1)
+
+ for i in range(sys3.outputs):
+ for j in range(sys3.inputs):
+ np.testing.assert_array_equal(sys2.num[i][j], sys3.num[i][j])
+ np.testing.assert_array_equal(sys2.den[i][j], sys3.den[i][j])
+
+ # Tests for TransferFunction.__add__
+
+ def testAddScalar(self):
+ """Add two direct feedthrough systems."""
+
+ sys1 = TransferFunction(1., [[[1.]]])
+ sys2 = TransferFunction(np.array([2.]), [1.])
+ sys3 = sys1 + sys2
+
+ np.testing.assert_array_equal(sys3.num, 3.)
+ np.testing.assert_array_equal(sys3.den, 1.)
+
+ def testAddSISO(self):
+ """Add two SISO systems."""
+
+ sys1 = TransferFunction([1., 3., 5], [1., 6., 2., -1])
+ sys2 = TransferFunction([[np.array([-1., 3.])]], [[[1., 0., -1.]]])
+ sys3 = sys1 + sys2
+
+ # If sys3.num is [[[0., 20., 4., -8.]]], then this is wrong!
+ np.testing.assert_array_equal(sys3.num, [[[20., 4., -8]]])
+ np.testing.assert_array_equal(sys3.den, [[[1., 6., 1., -7., -2., 1.]]])
+
+ def testAddMIMO(self):
+ """Add two MIMO systems."""
+
+ num1 = [[[1., 2.], [0., 3.], [2., -1.]],
+ [[1.], [4., 0.], [1., -4., 3.]]]
+ den1 = [[[-3., 2., 4.], [1., 0., 0.], [2., -1.]],
+ [[3., 0., .0], [2., -1., -1.], [1.]]]
+ num2 = [[[0., 0., -1], [2.], [-1., -1.]],
+ [[1., 2.], [-1., -2.], [4.]]]
+ den2 = [[[-1.], [1., 2., 3.], [-1., -1.]],
+ [[-4., -3., 2.], [0., 1.], [1., 0.]]]
+ num3 = [[[3., -3., -6], [5., 6., 9.], [-4., -2., 2]],
+ [[3., 2., -3., 2], [-2., -3., 7., 2.], [1., -4., 3., 4]]]
+ den3 = [[[3., -2., -4.], [1., 2., 3., 0., 0.], [-2., -1., 1.]],
+ [[-12., -9., 6., 0., 0.], [2., -1., -1.], [1., 0.]]]
+
+ sys1 = TransferFunction(num1, den1)
+ sys2 = TransferFunction(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])
+
+ # Tests for TransferFunction.__sub__
+
+ def testSubScalar(self):
+ """Add two direct feedthrough systems."""
+
+ sys1 = TransferFunction(1., [[[1.]]])
+ sys2 = TransferFunction(np.array([2.]), [1.])
+ sys3 = sys1 - sys2
+
+ np.testing.assert_array_equal(sys3.num, -1.)
+ np.testing.assert_array_equal(sys3.den, 1.)
+
+ def testSubSISO(self):
+ """Add two SISO systems."""
+
+ sys1 = TransferFunction([1., 3., 5], [1., 6., 2., -1])
+ sys2 = TransferFunction([[np.array([-1., 3.])]], [[[1., 0., -1.]]])
+ sys3 = sys1 - sys2
+ sys4 = sys2 - sys1
+
+ np.testing.assert_array_equal(sys3.num, [[[2., 6., -12., -10., -2.]]])
+ np.testing.assert_array_equal(sys3.den, [[[1., 6., 1., -7., -2., 1.]]])
+ np.testing.assert_array_equal(sys4.num, [[[-2., -6., 12., 10., 2.]]])
+ np.testing.assert_array_equal(sys4.den, [[[1., 6., 1., -7., -2., 1.]]])
+
+ def testSubMIMO(self):
+ """Add two MIMO systems."""
+
+ num1 = [[[1., 2.], [0., 3.], [2., -1.]],
+ [[1.], [4., 0.], [1., -4., 3.]]]
+ den1 = [[[-3., 2., 4.], [1., 0., 0.], [2., -1.]],
+ [[3., 0., .0], [2., -1., -1.], [1.]]]
+ num2 = [[[0., 0., -1], [2.], [-1., -1.]],
+ [[1., 2.], [-1., -2.], [4.]]]
+ den2 = [[[-1.], [1., 2., 3.], [-1., -1.]],
+ [[-4., -3., 2.], [0., 1.], [1., 0.]]]
+ num3 = [[[-3., 1., 2.], [1., 6., 9.], [0.]],
+ [[-3., -10., -3., 2], [2., 3., 1., -2], [1., -4., 3., -4]]]
+ den3 = [[[3., -2., -4], [1., 2., 3., 0., 0.], [1]],
+ [[-12., -9., 6., 0., 0.], [2., -1., -1], [1., 0.]]]
+
+ sys1 = TransferFunction(num1, den1)
+ sys2 = TransferFunction(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])
+
+ # Tests for TransferFunction.__mul__
+
+ def testMulScalar(self):
+ """Multiply two direct feedthrough systems."""
+
+ sys1 = TransferFunction(2., [1.])
+ sys2 = TransferFunction(1., 4.)
+ sys3 = sys1 * sys2
+ sys4 = sys1 * sys2
+
+ np.testing.assert_array_equal(sys3.num, [[[2.]]])
+ np.testing.assert_array_equal(sys3.den, [[[4.]]])
+ np.testing.assert_array_equal(sys3.num, sys4.num)
+ np.testing.assert_array_equal(sys3.den, sys4.den)
+
+ def testMulSISO(self):
+ """Multiply two SISO systems."""
+
+ sys1 = TransferFunction([1., 3., 5], [1., 6., 2., -1])
+ sys2 = TransferFunction([[[-1., 3.]]], [[[1., 0., -1.]]])
+ sys3 = sys1 * sys2
+ sys4 = sys2 * sys1
+
+ np.testing.assert_array_equal(sys3.num, [[[-1., 0., 4., 15.]]])
+ np.testing.assert_array_equal(sys3.den, [[[1., 6., 1., -7., -2., 1.]]])
+ np.testing.assert_array_equal(sys3.num, sys4.num)
+ np.testing.assert_array_equal(sys3.den, sys4.den)
+
+ def testMulMIMO(self):
+ """Multiply two MIMO systems."""
+
+ num1 = [[[1., 2.], [0., 3.], [2., -1.]],
+ [[1.], [4., 0.], [1., -4., 3.]]]
+ den1 = [[[-3., 2., 4.], [1., 0., 0.], [2., -1.]],
+ [[3., 0., .0], [2., -1., -1.], [1.]]]
+ num2 = [[[0., 1., 2.]],
+ [[1., -5.]],
+ [[-2., 1., 4.]]]
+ den2 = [[[1., 0., 0., 0.]],
+ [[-2., 1., 3.]],
+ [[4., -1., -1., 0.]]]
+ num3 = [[[-24., 52., -14., 245., -490., -115., 467., -95., -56., 12.,
+ 0., 0., 0.]],
+ [[24., -132., 138., 345., -768., -106., 510., 41., -79., -69.,
+ -23., 17., 6., 0.]]]
+ den3 = [[[48., -92., -84., 183., 44., -97., -2., 12., 0., 0., 0., 0.,
+ 0., 0.]],
+ [[-48., 60., 84., -81., -45., 21., 9., 0., 0., 0., 0., 0., 0.]]]
+
+ sys1 = TransferFunction(num1, den1)
+ sys2 = TransferFunction(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])
+
+ # Tests for TransferFunction.__div__
+
+ def testDivScalar(self):
+ """Divide two direct feedthrough systems."""
+
+ sys1 = TransferFunction(np.array([3.]), -4.)
+ sys2 = TransferFunction(5., 2.)
+ sys3 = sys1 / sys2
+
+ np.testing.assert_array_equal(sys3.num, [[[6.]]])
+ np.testing.assert_array_equal(sys3.den, [[[-20.]]])
+
+ def testDivSISO(self):
+ """Divide two SISO systems."""
+
+ sys1 = TransferFunction([1., 3., 5], [1., 6., 2., -1])
+ sys2 = TransferFunction([[[-1., 3.]]], [[[1., 0., -1.]]])
+ sys3 = sys1 / sys2
+ sys4 = sys2 / sys1
+
+ np.testing.assert_array_equal(sys3.num, [[[1., 3., 4., -3., -5.]]])
+ np.testing.assert_array_equal(sys3.den, [[[-1., -3., 16., 7., -3.]]])
+ np.testing.assert_array_equal(sys4.num, sys3.den)
+ np.testing.assert_array_equal(sys4.den, sys3.num)
+
+ # Tests for TransferFunction.evalfr.
+
+ def testEvalFrSISO(self):
+ """Evaluate the frequency response of a SISO system at one frequency."""
+
+ sys = TransferFunction([1., 3., 5], [1., 6., 2., -1])
+
+ np.testing.assert_array_almost_equal(sys.evalfr(1.),
+ np.array([[-0.5 - 0.5j]]))
+ np.testing.assert_array_almost_equal(sys.evalfr(32.),
+ np.array([[0.00281959302585077 - 0.030628473607392j]]))
+
+ def testEvalFrMIMO(self):
+ """Evaluate the frequency response of a MIMO system at one frequency."""
+
+ num = [[[1., 2.], [0., 3.], [2., -1.]],
+ [[1.], [4., 0.], [1., -4., 3.]]]
+ den = [[[-3., 2., 4.], [1., 0., 0.], [2., -1.]],
+ [[3., 0., .0], [2., -1., -1.], [1.]]]
+ sys = TransferFunction(num, den)
+ resp = [[0.147058823529412 + 0.0882352941176471j, -0.75, 1.],
+...
[truncated message content] |