From: Tony V. <ton...@al...> - 2010-06-06 21:11:49
|
Doesn't look like I received it. I also checked my spam folder just in case. -tv On Jun 6, 2010, at 10:31, Richard Murray <mu...@cd...> wrote: > Tony and Erik: > > Can you let me know if you got the note below from SourceForge. I > just posted some changes to the package (needed them for an example > I'm putting together) and want to make sure that everyone is getting > copies of the changes that got posted. If not, I'll sort out what > we need to change to make sure you get these sorts of e-mails. > > Thanks. > > -richard > > Begin forwarded message: > >> From: mur...@us... >> Date: 6 June 2010 10:29:43 PDT >> To: mu...@cd... >> Subject: SF.net SVN: python-control:[21] trunk >> >> Revision: 21 >> http://python-control.svn.sourceforge.net/python-control/?rev=21&view=rev >> Author: murrayrm >> Date: 2010-06-06 17:29:42 +0000 (Sun, 06 Jun 2010) >> >> Log Message: >> ----------- >> Added changes to allow bode() to plot frequency resp for multiple >> systems >> * freqplot.bode now allows a list as the first argument >> * Added matlab.bode wrapper for MATLAB syntax: bode(sys1, ..., sysN) >> >> Added automatic frequency range determination in bode >> * Computes range of frequencies based on pole/zero locations >> * Eventually need to split this out as a ctrlutil function for use >> elsewhere >> >> Modified Paths: >> -------------- >> trunk/ChangeLog >> trunk/src/ctrlutil.py >> trunk/src/freqplot.py >> trunk/src/matlab.py >> trunk/src/pzmap.py >> trunk/src/statesp.py >> trunk/src/xferfcn.py >> >> Modified: trunk/ChangeLog >> =================================================================== >> --- trunk/ChangeLog 2010-06-01 02:08:20 UTC (rev 20) >> +++ trunk/ChangeLog 2010-06-06 17:29:42 UTC (rev 21) >> @@ -1,3 +1,25 @@ >> +2010-06-06 Richard Murray <murray@sumatra.local> >> + >> + * 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 >> >> Modified: trunk/src/ctrlutil.py >> =================================================================== >> --- trunk/src/ctrlutil.py 2010-06-01 02:08:20 UTC (rev 20) >> +++ trunk/src/ctrlutil.py 2010-06-06 17:29:42 UTC (rev 21) >> @@ -40,6 +40,12 @@ >> # >> # $Id$ >> >> +# Packages that we need access to >> +import scipy as sp >> +import statesp >> +import xferfcn >> + >> +# Specific functions that we use >> from scipy import pi >> >> # Utility function to unwrap an angle measurement >> @@ -78,3 +84,12 @@ >> >> # return the updated list >> return angle >> + >> +# Determine if an object is a system >> +def issys(object): >> + # Check for a member of one of the classes that we define here >> + if (isinstance(object, (statesp.StateSpace, >> xferfcn.TransferFunction))): >> + return True >> + >> + # Didn't find anything that matched >> + return False >> >> Modified: trunk/src/freqplot.py >> =================================================================== >> --- trunk/src/freqplot.py 2010-06-01 02:08:20 UTC (rev 20) >> +++ trunk/src/freqplot.py 2010-06-06 17:29:42 UTC (rev 21) >> @@ -42,11 +42,12 @@ >> >> import matplotlib.pyplot as plt >> import scipy as sp >> +import numpy as np >> from ctrlutil import unwrap >> from bdalg import feedback >> >> # Bode plot >> -def bode(sys, omega=None, dB=False, Hz=False): >> +def bode(syslist, omega=None, dB=False, Hz=False): >> """Bode plot for a system >> >> Usage >> @@ -57,8 +58,8 @@ >> >> Parameters >> ---------- >> - sys : linsys >> - Linear input/output system >> + syslist : linsys >> + List of linear input/output systems (single system is OK) >> omega : freq_range >> Range of frequencies (list or bounds) in rad/sec >> dB : boolean >> @@ -76,45 +77,80 @@ >> 1. Use (mag, phase, freq) = sys.freqresp(freq) to generate the >> frequency response for a system. >> """ >> + # If argument was a singleton, turn it into a list >> + if (not getattr(syslist, '__iter__', False)): >> + syslist = (syslist,) >> + >> + # >> # Select a default range if none is provided >> + # >> + # This code looks at the poles and zeros of all of the systems >> that >> + # we are plotting and sets the frequency range to be one >> decade above >> + # and below the min and max feature frequencies, rounded to >> the nearest >> + # integer. It excludes poles and zeros at the origin. If no >> features >> + # are found, it turns logspace(-1, 1) >> + # >> if (omega == None): >> - omega = sp.logspace(-2, 2); >> + # Find the list of all poles and zeros in the systems >> + features = np.array(()) >> + for sys in syslist: >> + # Add new features to the list >> + features = np.concatenate((features, np.abs(sys.poles))) >> + features = np.concatenate((features, np.abs(sys.zeros))) >> >> - # Get the magnitude and phase of the system >> - mag, phase, omega = sys.freqresp(omega) >> - if Hz: >> - omega = omega/(2*sp.pi) >> - if dB: >> - mag = 20*sp.log10(mag) >> - phase = unwrap(phase*180/sp.pi, 360) >> + # Get rid of poles and zeros at the origin >> + features = features[features != 0]; >> >> - # Get the dimensions of the current axis, which we will divide >> up >> - #! TODO: Not current implemented; just use subplot for now >> + # Make sure there is at least one point in the range >> + if (features.shape[0] == 0): features = [1]; >> >> - # Magnitude plot >> - plt.subplot(211); >> - if dB: >> - plt.semilogx(omega, mag) >> - else: >> - plt.loglog(omega, mag) >> - plt.grid(True) >> - plt.grid(True, which='minor') >> - if dB: >> - plt.ylabel("Magnitude (dB)") >> - else: >> - plt.ylabel("Magnitude") >> + # Take the log of the features >> + features = np.log10(features) >> >> - # Phase plot >> - plt.subplot(212); >> - plt.semilogx(omega, phase) >> - plt.grid(True) >> - plt.grid(True, which='minor') >> - plt.ylabel("Phase (deg)") >> - if Hz: >> - plt.xlabel("Frequency (Hz)") >> - else: >> - plt.xlabel("Frequency (rad/sec)") >> + # Set the to be an order of magnitude beyond any features >> + omega = sp.logspace(np.floor(np.min(features))-1, >> + np.ceil(np.max(features))+1) >> >> + for sys in syslist: >> + # Get the magnitude and phase of the system >> + mag, phase, omega = sys.freqresp(omega) >> + if Hz: omega = omega/(2*sp.pi) >> + if dB: mag = 20*sp.log10(mag) >> + phase = unwrap(phase*180/sp.pi, 360) >> + >> + # Get the dimensions of the current axis, which we will >> divide up >> + #! TODO: Not current implemented; just use subplot for now >> + >> + # Magnitude plot >> + plt.subplot(211); >> + if dB: >> + plt.semilogx(omega, mag) >> + plt.ylabel("Magnitude (dB)") >> + else: >> + plt.loglog(omega, mag) >> + plt.ylabel("Magnitude") >> + >> + # Add a grid to the plot >> + plt.grid(True) >> + plt.grid(True, which='minor') >> + plt.hold(True); >> + >> + # Phase plot >> + plt.subplot(212); >> + plt.semilogx(omega, phase) >> + plt.hold(True) >> + >> + # Add a grid to the plot >> + plt.grid(True) >> + plt.grid(True, which='minor') >> + plt.ylabel("Phase (deg)") >> + >> + # Label the frequency axis >> + if Hz: >> + plt.xlabel("Frequency (Hz)") >> + else: >> + plt.xlabel("Frequency (rad/sec)") >> + >> return (211, 212) >> >> # Nyquist plot >> >> Modified: trunk/src/matlab.py >> =================================================================== >> --- trunk/src/matlab.py 2010-06-01 02:08:20 UTC (rev 20) >> +++ trunk/src/matlab.py 2010-06-06 17:29:42 UTC (rev 21) >> @@ -50,6 +50,7 @@ >> >> # Libraries that we make use of >> import scipy as sp # SciPy library (used all over) >> +import numpy as np # NumPy library >> import scipy.signal as signal # Signal processing library >> >> # Import MATLAB-like functions that are defined in other packages >> @@ -57,11 +58,16 @@ >> from scipy.signal import lsim, impulse, step >> from scipy import linspace, logspace >> >> -# Import MATLAB-like functions that belong elsewhere in python- >> control >> -from ctrlutil import unwrap >> -from freqplot import bode, nyquist, gangof4 >> +# Control system library >> +import ctrlutil >> +import freqplot >> from statesp import StateSpace >> from xferfcn import TransferFunction >> +from exception import * >> + >> +# Import MATLAB-like functions that can be used as-is >> +from ctrlutil import unwrap >> +from freqplot import nyquist, gangof4 >> from bdalg import series, parallel, negate, feedback >> from pzmap import pzmap >> from statefbk import place, lqr >> @@ -304,3 +310,63 @@ >> def freqresp(H, omega): >> """Return the frequency response for an object H at frequency >> omega""" >> return H.freqresp(omega) >> + >> +# Bode plots >> +def bode(*args, **keywords): >> + """Bode plot of the frequency response >> + >> + Usage >> + ===== >> + bode(sys) >> + bode(sys, w) >> + bode(sys1, sys2, ..., sysN) >> + bode(sys1, sys2, ..., sysN, w) >> + bode(sys1, 'plotstyle1', ..., sysN, 'plotstyleN') >> + """ >> + >> + # If the first argument is a list, then assume python-control >> calling format >> + if (getattr(args[0], '__iter__', False)): >> + return freqplot.bode(*args, **keywords) >> + >> + # Otherwise, run through the arguments and collect up arguments >> + syslist = []; plotstyle=[]; omega=None; >> + i = 0; >> + while i < len(args): >> + # Check to see if this is a system of some sort >> + if (ctrlutil.issys(args[i])): >> + # Append the system to our list of systems >> + syslist.append(args[i]) >> + i += 1 >> + >> + # See if the next object is a plotsytle (string) >> + if (i < len(args) and isinstance(args[i], str)): >> + plotstyle.append(args[i]) >> + i += 1 >> + >> + # Go on to the next argument >> + continue >> + >> + # See if this is a frequency list >> + elif (isinstance(args[i], (list, np.ndarray))): >> + omega = args[i] >> + i += 1 >> + break >> + >> + else: >> + raise ControlArgument("unrecognized argument type") >> + >> + # Check to make sure that we processed all arguments >> + if (i < len(args)): >> + raise ControlArgument("not all arguments processed") >> + >> + # Check to make sure we got the same number of plotstyles as >> systems >> + if (len(plotstyle) != 0 and len(syslist) != len(plotstyle)): >> + raise ControlArgument("number of systems and plotstyles >> should be equal") >> + >> + # Warn about unimplemented plotstyles >> + #! TODO: remove this when plot styles are implemented in bode() >> + if (len(plotstyle) != 0): >> + print("Warning (matabl.bode): plot styles not implemented"); >> + >> + # Call the bode command >> + return freqplot.bode(syslist, omega, **keywords) >> >> Modified: trunk/src/pzmap.py >> =================================================================== >> --- trunk/src/pzmap.py 2010-06-01 02:08:20 UTC (rev 20) >> +++ trunk/src/pzmap.py 2010-06-06 17:29:42 UTC (rev 21) >> @@ -45,6 +45,7 @@ >> import xferfcn >> >> # Compute poles and zeros for a system >> +#! TODO: extend this to handle state space systems >> def pzmap(sys, Plot=True): >> """Plot a pole/zero map for a transfer function""" >> if (isinstance(sys, xferfcn.TransferFunction)): >> >> Modified: trunk/src/statesp.py >> =================================================================== >> --- trunk/src/statesp.py 2010-06-01 02:08:20 UTC (rev 20) >> +++ trunk/src/statesp.py 2010-06-06 17:29:42 UTC (rev 21) >> @@ -98,6 +98,18 @@ >> #! TODO: Not implemented >> return None >> >> + # Compute poles and zeros >> + def poles(self): return sp.roots(sp.poly(self.A)) >> + def zeros(self): >> + den = sp.poly1d(sp.poly(self.A)) >> + >> + # Compute the numerator based on zeros >> + #! TODO: This is currently limited to SISO systems >> + num = sp.poly1d(\ >> + sp.poly(self.A - sp.dot(self.B, self.C)) + (self.D[0] >> - 1) * den) >> + >> + return (sp.roots(num)) >> + >> # Negation of a system >> def __neg__(self): >> """Negate a state space system""" >> >> Modified: trunk/src/xferfcn.py >> =================================================================== >> --- trunk/src/xferfcn.py 2010-06-01 02:08:20 UTC (rev 20) >> +++ trunk/src/xferfcn.py 2010-06-06 17:29:42 UTC (rev 21) >> @@ -190,6 +190,10 @@ >> >> return mag, phase, omega >> >> + # Compute poles and zeros >> + def poles(self): return sp.roots(self.den) >> + def zeros(self): return sp.roots(self.num) >> + >> # Feedback around a transfer function >> def feedback(sys1, sys2, sign=-1): >> """Feedback interconnection between two transfer functions""" >> >> >> This was sent by the SourceForge.net collaborative development >> platform, the world's largest Open Source development site. > > > --- > --- > --- > --------------------------------------------------------------------- > ThinkGeek and WIRED's GeekDad team up for the Ultimate > GeekDad Father's Day Giveaway. ONE MASSIVE PRIZE to the > lucky parental unit. See the prize list and enter to win: > http://p.sf.net/sfu/thinkgeek-promo > _______________________________________________ > python-control-discuss mailing list > pyt...@li... > https://lists.sourceforge.net/lists/listinfo/python-control-discuss |