Skip to content

Commit 689c59d

Browse files
committed
Move Matlab plots to new module matlab.plots
1 parent 456fcaf commit 689c59d

6 files changed

Lines changed: 124 additions & 143 deletions

File tree

control/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
from .margins import stability_margins, phase_crossover_frequencies
5656
from .mateqn import lyap, dlyap, care, dare
5757
from .modelsimp import hsvd, modred, balred, era, markov, minreal
58-
from .nichols import nichols_plot, nichols
58+
from .nichols import *
5959
from .phaseplot import phase_plot, box_grid
6060
from .pzmap import pzmap
6161
from .rlocus import root_locus

control/matlab/__init__.py

Lines changed: 7 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -73,17 +73,12 @@
7373
# config.use_matlab()
7474

7575
# Control system library
76-
from .. import ctrlutil
77-
from .. import freqplot
78-
from .. import timeresp
79-
from .. import margins
8076
from ..statesp import *
8177
from ..xferfcn import *
8278
from ..lti import *
8379
from ..frdata import *
8480
from ..dtime import sample_system
8581
from ..exception import ControlArgument
86-
from .timeresp import *
8782

8883
# Import MATLAB-like functions that can be used as-is
8984
from ..ctrlutil import unwrap
@@ -96,6 +91,10 @@
9691
from ..modelsimp import hsvd, balred, modred, minreal
9792
from ..mateqn import lyap, dlyap, dare, care
9893

94+
# Import functions specific to Matlab compatibility package
95+
from .timeresp import *
96+
from .plots import *
97+
9998
__doc__ += r"""
10099
The following tables give an overview of the module ``control.matlab``.
101100
They also show the implementation progress and the planned features of the
@@ -387,136 +386,7 @@
387386
388387
"""
389388

390-
# Bode plots
391-
def bode(*args, **keywords):
392-
"""Bode plot of the frequency response
393-
394-
Plots a bode gain and phase diagram
395-
396-
Parameters
397-
----------
398-
sys : LTI, or list of LTI
399-
System for which the Bode response is plotted and give. Optionally
400-
a list of systems can be entered, or several systems can be
401-
specified (i.e. several parameters). The sys arguments may also be
402-
interspersed with format strings. A frequency argument (array_like)
403-
may also be added, some examples:
404-
* >>> bode(sys, w) # one system, freq vector
405-
* >>> bode(sys1, sys2, ..., sysN) # several systems
406-
* >>> bode(sys1, sys2, ..., sysN, w)
407-
* >>> bode(sys1, 'plotstyle1', ..., sysN, 'plotstyleN') # + plot formats
408-
omega: freq_range
409-
Range of frequencies in rad/s
410-
dB : boolean
411-
If True, plot result in dB
412-
Hz : boolean
413-
If True, plot frequency in Hz (omega must be provided in rad/sec)
414-
deg : boolean
415-
If True, return phase in degrees (else radians)
416-
Plot : boolean
417-
If True, plot magnitude and phase
418-
419-
Examples
420-
--------
421-
>>> sys = ss("1. -2; 3. -4", "5.; 7", "6. 8", "9.")
422-
>>> mag, phase, omega = bode(sys)
423-
424-
.. todo::
425-
426-
Document these use cases
427-
428-
* >>> bode(sys, w)
429-
* >>> bode(sys1, sys2, ..., sysN)
430-
* >>> bode(sys1, sys2, ..., sysN, w)
431-
* >>> bode(sys1, 'plotstyle1', ..., sysN, 'plotstyleN')
432-
"""
433-
434-
# If the first argument is a list, then assume python-control calling format
435-
if (getattr(args[0], '__iter__', False)):
436-
return freqplot.bode(*args, **keywords)
437-
438-
# Otherwise, run through the arguments and collect up arguments
439-
syslist = []; plotstyle=[]; omega=None;
440-
i = 0;
441-
while i < len(args):
442-
# Check to see if this is a system of some sort
443-
if (ctrlutil.issys(args[i])):
444-
# Append the system to our list of systems
445-
syslist.append(args[i])
446-
i += 1
447-
448-
# See if the next object is a plotsytle (string)
449-
if (i < len(args) and isinstance(args[i], str)):
450-
plotstyle.append(args[i])
451-
i += 1
452-
453-
# Go on to the next argument
454-
continue
455-
456-
# See if this is a frequency list
457-
elif (isinstance(args[i], (list, np.ndarray))):
458-
omega = args[i]
459-
i += 1
460-
break
461-
462-
else:
463-
raise ControlArgument("unrecognized argument type")
464-
465-
# Check to make sure that we processed all arguments
466-
if (i < len(args)):
467-
raise ControlArgument("not all arguments processed")
468-
469-
# Check to make sure we got the same number of plotstyles as systems
470-
if (len(plotstyle) != 0 and len(syslist) != len(plotstyle)):
471-
raise ControlArgument("number of systems and plotstyles should be equal")
472-
473-
# Warn about unimplemented plotstyles
474-
#! TODO: remove this when plot styles are implemented in bode()
475-
#! TODO: uncomment unit test code that tests this out
476-
if (len(plotstyle) != 0):
477-
print("Warning (matabl.bode): plot styles not implemented");
478-
479-
# Call the bode command
480-
return freqplot.bode(syslist, omega, **keywords)
481-
482-
# Nichols chart grid
483-
def ngrid():
484-
nichols_grid()
485-
ngrid.__doc__ = re.sub('nichols_grid', 'ngrid', nichols_grid.__doc__)
486-
487-
# Root locus plot
488-
def rlocus(sys, klist = None, **keywords):
489-
"""Root locus plot
490-
491-
The root-locus plot has a callback function that prints pole location,
492-
gain and damping to the Python consol on mouseclicks on the root-locus
493-
graph.
494-
495-
Parameters
496-
----------
497-
sys: StateSpace or TransferFunction
498-
Linear system
499-
klist: iterable, optional
500-
optional list of gains
501-
xlim : control of x-axis range, normally with tuple, for
502-
other options, see matplotlib.axes
503-
ylim : control of y-axis range
504-
Plot : boolean (default = True)
505-
If True, plot magnitude and phase
506-
PrintGain: boolean (default = True)
507-
If True, report mouse clicks when close to the root-locus branches,
508-
calculate gain, damping and print
509-
510-
Returns
511-
-------
512-
rlist:
513-
list of roots for each gain
514-
klist:
515-
list of gains used to compute roots
516-
"""
517-
from ..rlocus import root_locus
518-
519-
return root_locus(sys, klist, **keywords)
389+
from ..margins import stability_margins
520390

521391
def margin(*args):
522392
"""Calculate gain and phase margins and associated crossover frequencies
@@ -548,9 +418,9 @@ def margin(*args):
548418
"""
549419
if len(args) == 1:
550420
sys = args[0]
551-
margin = margins.stability_margins(sys)
421+
margin = stability_margins(sys)
552422
elif len(args) == 3:
553-
margin = margins.stability_margins(args)
423+
margin = stability_margins(args)
554424
else:
555425
raise ValueError("Margin needs 1 or 3 arguments; received %i."
556426
% len(args))
@@ -649,9 +519,6 @@ def damp(sys, doprint=True):
649519
(p.real, p.imag, d, w))
650520
return wn, damping, poles
651521

652-
# Simulation routines
653-
# Call corresponding functions in timeresp, with arguments transposed
654-
655522
# Convert a continuous time system to a discrete time system
656523
def c2d(sysc, Ts, method='zoh'):
657524
'''

control/matlab/plots.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
"""
2+
Plotting routines for the Matlab compatibility module
3+
"""
4+
5+
import numpy as np
6+
7+
__all__ = ['bode', 'rlocus', 'ngrid']
8+
9+
def bode(*args, **keywords):
10+
"""Bode plot of the frequency response
11+
12+
Plots a bode gain and phase diagram
13+
14+
Parameters
15+
----------
16+
sys : LTI, or list of LTI
17+
System for which the Bode response is plotted and give. Optionally
18+
a list of systems can be entered, or several systems can be
19+
specified (i.e. several parameters). The sys arguments may also be
20+
interspersed with format strings. A frequency argument (array_like)
21+
may also be added, some examples:
22+
* >>> bode(sys, w) # one system, freq vector
23+
* >>> bode(sys1, sys2, ..., sysN) # several systems
24+
* >>> bode(sys1, sys2, ..., sysN, w)
25+
* >>> bode(sys1, 'plotstyle1', ..., sysN, 'plotstyleN') # + plot formats
26+
omega: freq_range
27+
Range of frequencies in rad/s
28+
dB : boolean
29+
If True, plot result in dB
30+
Hz : boolean
31+
If True, plot frequency in Hz (omega must be provided in rad/sec)
32+
deg : boolean
33+
If True, return phase in degrees (else radians)
34+
Plot : boolean
35+
If True, plot magnitude and phase
36+
37+
Examples
38+
--------
39+
>>> sys = ss("1. -2; 3. -4", "5.; 7", "6. 8", "9.")
40+
>>> mag, phase, omega = bode(sys)
41+
42+
.. todo::
43+
44+
Document these use cases
45+
46+
* >>> bode(sys, w)
47+
* >>> bode(sys1, sys2, ..., sysN)
48+
* >>> bode(sys1, sys2, ..., sysN, w)
49+
* >>> bode(sys1, 'plotstyle1', ..., sysN, 'plotstyleN')
50+
"""
51+
52+
# If the first argument is a list, then assume python-control calling format
53+
from ..freqplot import bode as bode_orig
54+
if (getattr(args[0], '__iter__', False)):
55+
return bode_orig(*args, **keywords)
56+
57+
# Otherwise, run through the arguments and collect up arguments
58+
syslist = []; plotstyle=[]; omega=None;
59+
i = 0;
60+
while i < len(args):
61+
# Check to see if this is a system of some sort
62+
from ..ctrlutil import issys
63+
if (issys(args[i])):
64+
# Append the system to our list of systems
65+
syslist.append(args[i])
66+
i += 1
67+
68+
# See if the next object is a plotsytle (string)
69+
if (i < len(args) and isinstance(args[i], str)):
70+
plotstyle.append(args[i])
71+
i += 1
72+
73+
# Go on to the next argument
74+
continue
75+
76+
# See if this is a frequency list
77+
elif (isinstance(args[i], (list, np.ndarray))):
78+
omega = args[i]
79+
i += 1
80+
break
81+
82+
else:
83+
raise ControlArgument("unrecognized argument type")
84+
85+
# Check to make sure that we processed all arguments
86+
if (i < len(args)):
87+
raise ControlArgument("not all arguments processed")
88+
89+
# Check to make sure we got the same number of plotstyles as systems
90+
if (len(plotstyle) != 0 and len(syslist) != len(plotstyle)):
91+
raise ControlArgument("number of systems and plotstyles should be equal")
92+
93+
# Warn about unimplemented plotstyles
94+
#! TODO: remove this when plot styles are implemented in bode()
95+
#! TODO: uncomment unit test code that tests this out
96+
if (len(plotstyle) != 0):
97+
print("Warning (matlab.bode): plot styles not implemented");
98+
99+
# Call the bode command
100+
return bode_orig(syslist, omega, **keywords)
101+
102+
from ..nichols import nichols_grid
103+
def ngrid():
104+
return nichols_grid()
105+
ngrid.__doc__ = nichols_grid.__doc__
106+
107+
from ..rlocus import root_locus
108+
def rlocus(*args, **kwargs):
109+
return root_locus(*args, **kwargs)
110+
rlocus.__doc__ = root_locus.__doc__

control/nichols.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545
from .ctrlutil import unwrap
4646
from .freqplot import default_frequency_range
4747

48+
__all__ = ['nichols_plot', 'nichols']
49+
4850
# Nichols plot
4951
def nichols_plot(syslist, omega=None, grid=True):
5052
"""Nichols plot for a system

control/rlocus.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@
5757
# Main function: compute a root locus diagram
5858
def root_locus(sys, kvect=None, xlim=None, ylim=None, plotstr='-', Plot=True,
5959
PrintGain=True):
60-
"""Calculate the root locus by finding the roots of 1+k*TF(s)
60+
"""Root locus plot
61+
62+
Calculate the root locus by finding the roots of 1+k*TF(s)
6163
where TF is self.num(s)/self.den(s) and each k is an element
6264
of kvect.
6365

control/tests/matlab_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ def testRlocus(self):
310310
rlocus(self.siso_tf1)
311311
rlocus(self.siso_tf2)
312312
klist = [1, 10, 100]
313-
rlist, klist_out = rlocus(self.siso_tf2, klist=klist, Plot=False)
313+
rlist, klist_out = rlocus(self.siso_tf2, klist, Plot=False)
314314
np.testing.assert_equal(len(rlist), len(klist))
315315
np.testing.assert_array_equal(klist, klist_out)
316316

0 commit comments

Comments
 (0)