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 <murray@sumatra.local> + + * 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 <murray@sumatra.local> + + * 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 <murray@sumatra.local> + + * src/statefbk.py (ctrb): new function for testing controllability + * src/statefbk.py (obsv): new function for testing observabiilty + +2010-09-02 Richard Murray <murray@sumatra.local> + + * 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 <murray@sumatra.local> + + * 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 <murray@sumatra.local> + + * 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 <murray@sumatra.local> + + * 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 <murray@kona-2.local> + * ./ 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] |