Skip to content

Commit 601b581

Browse files
authored
Merge pull request python-control#187 from murrayrm/fix_warnings-15jan08
Fix deprecation and formatting warnings
2 parents af8d4ee + 2444296 commit 601b581

File tree

5 files changed

+147
-29
lines changed

5 files changed

+147
-29
lines changed

control/freqplot.py

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -171,22 +171,47 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
171171
#! TODO: Not current implemented; just use subplot for now
172172

173173
if (Plot):
174+
# Set up the axes with labels so that multiple calls to
175+
# bode_plot will superimpose the data. This was implicit
176+
# before matplotlib 2.1, but changed after that (See
177+
# https://github.com/matplotlib/matplotlib/issues/9024).
178+
# The code below should work on all cases.
179+
180+
# Get the current figure
181+
fig = plt.gcf()
182+
ax_mag = None
183+
ax_phase = None
184+
185+
# Get the current axes if they already exist
186+
for ax in fig.axes:
187+
if ax.get_label() == 'control-bode-magnitude':
188+
ax_mag = ax
189+
elif ax.get_label() == 'control-bode-phase':
190+
ax_phase = ax
191+
192+
# If no axes present, create them from scratch
193+
if ax_mag is None or ax_phase is None:
194+
plt.clf()
195+
ax_mag = plt.subplot(211, label = 'control-bode-magnitude')
196+
ax_phase = plt.subplot(212, label = 'control-bode-phase',
197+
sharex=ax_mag)
198+
174199
# Magnitude plot
175-
ax_mag = plt.subplot(211);
176200
if dB:
177-
pltline = ax_mag.semilogx(omega_plot, 20 * np.log10(mag), *args, **kwargs)
201+
pltline = ax_mag.semilogx(omega_plot, 20 * np.log10(mag),
202+
*args, **kwargs)
178203
else:
179204
pltline = ax_mag.loglog(omega_plot, mag, *args, **kwargs)
180205

181206
if nyquistfrq_plot:
182-
ax_mag.axvline(nyquistfrq_plot, color=pltline[0].get_color())
207+
ax_mag.axvline(nyquistfrq_plot,
208+
color=pltline[0].get_color())
183209

184210
# Add a grid to the plot + labeling
185211
ax_mag.grid(True, which='both')
186212
ax_mag.set_ylabel("Magnitude (dB)" if dB else "Magnitude")
187213

188214
# Phase plot
189-
ax_phase = plt.subplot(212, sharex=ax_mag);
190215
if deg:
191216
phase_plot = phase * 180. / math.pi
192217
else:
@@ -353,28 +378,50 @@ def gangof4_plot(P, C, omega=None):
353378
L = P * C;
354379
S = feedback(1, L);
355380
T = L * S;
356-
381+
382+
# Set up the axes with labels so that multiple calls to
383+
# gangof4_plot will superimpose the data. See details in bode_plot.
384+
plot_axes = {'t' : None, 's' : None, 'ps' : None, 'cs' : None}
385+
for ax in plt.gcf().axes:
386+
label = ax.get_label()
387+
if label.startswith('control-gangof4-'):
388+
key = label[len('control-gangof4-'):]
389+
if key not in plot_axes:
390+
raise RuntimeError("unknown gangof4 axis type '{}'".format(label))
391+
plot_axes[key] = ax
392+
393+
# if any of the axes are missing, start from scratch
394+
if any((ax is None for ax in plot_axes.values())):
395+
plt.clf()
396+
plot_axes = {'t' : plt.subplot(221,label='control-gangof4-t'),
397+
'ps' : plt.subplot(222,label='control-gangof4-ps'),
398+
'cs' : plt.subplot(223,label='control-gangof4-cs'),
399+
's' : plt.subplot(224,label='control-gangof4-s')}
400+
401+
#
357402
# Plot the four sensitivity functions
403+
#
404+
358405
#! TODO: Need to add in the mag = 1 lines
359406
mag_tmp, phase_tmp, omega = T.freqresp(omega);
360407
mag = np.squeeze(mag_tmp)
361408
phase = np.squeeze(phase_tmp)
362-
plt.subplot(221); plt.loglog(omega, mag);
409+
plot_axes['t'].loglog(omega, mag);
363410

364411
mag_tmp, phase_tmp, omega = (P * S).freqresp(omega);
365412
mag = np.squeeze(mag_tmp)
366413
phase = np.squeeze(phase_tmp)
367-
plt.subplot(222); plt.loglog(omega, mag);
414+
plot_axes['ps'].loglog(omega, mag);
368415

369416
mag_tmp, phase_tmp, omega = (C * S).freqresp(omega);
370417
mag = np.squeeze(mag_tmp)
371418
phase = np.squeeze(phase_tmp)
372-
plt.subplot(223); plt.loglog(omega, mag);
419+
plot_axes['cs'].loglog(omega, mag);
373420

374421
mag_tmp, phase_tmp, omega = S.freqresp(omega);
375422
mag = np.squeeze(mag_tmp)
376423
phase = np.squeeze(phase_tmp)
377-
plt.subplot(224); plt.loglog(omega, mag);
424+
plot_axes['s'].loglog(omega, mag);
378425

379426
#
380427
# Utility functions

control/statesp.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,18 +1078,18 @@ def ss(*args):
10781078
output equations:
10791079
10801080
.. math::
1081-
\dot x = A \cdot x + B \cdot u
1081+
\\dot x = A \\cdot x + B \\cdot u
10821082
1083-
y = C \cdot x + D \cdot u
1083+
y = C \\cdot x + D \\cdot u
10841084
10851085
``ss(A, B, C, D, dt)``
10861086
Create a discrete-time state space system from the matrices of
10871087
its state and output equations:
10881088
10891089
.. math::
1090-
x[k+1] = A \cdot x[k] + B \cdot u[k]
1090+
x[k+1] = A \\cdot x[k] + B \\cdot u[k]
10911091
1092-
y[k] = C \cdot x[k] + D \cdot u[ki]
1092+
y[k] = C \\cdot x[k] + D \\cdot u[ki]
10931093
10941094
The matrices can be given as *array like* data types or strings.
10951095
Everything that the constructor of :class:`numpy.matrix` accepts is

control/tests/freqresp_test.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import unittest
1010
import numpy as np
11+
import control as ctrl
1112
from control.statesp import StateSpace
1213
from control.matlab import ss, tf, bode
1314
from control.exception import slycot_check
@@ -34,6 +35,50 @@ def test_siso(self):
3435
systf = tf(sys)
3536
bode(systf)
3637

38+
def test_superimpose(self):
39+
# Test to make sure that multiple calls to plots superimpose their
40+
# data on the same axes unless told to do otherwise
41+
42+
# Generate two plots in a row; should be on the same axes
43+
plt.figure(1); plt.clf()
44+
ctrl.bode_plot(ctrl.tf([1], [1,2,1]))
45+
ctrl.bode_plot(ctrl.tf([5], [1, 1]))
46+
47+
# Check to make sure there are two axes and that each axes has two lines
48+
assert len(plt.gcf().axes) == 2
49+
for ax in plt.gcf().axes:
50+
# Make sure there are 2 lines in each subplot
51+
assert len(ax.get_lines()) == 2
52+
53+
# Generate two plots as a list; should be on the same axes
54+
plt.figure(2); plt.clf();
55+
ctrl.bode_plot([ctrl.tf([1], [1,2,1]), ctrl.tf([5], [1, 1])])
56+
57+
# Check to make sure there are two axes and that each axes has two lines
58+
assert len(plt.gcf().axes) == 2
59+
for ax in plt.gcf().axes:
60+
# Make sure there are 2 lines in each subplot
61+
assert len(ax.get_lines()) == 2
62+
63+
# Generate two separate plots; only the second should appear
64+
plt.figure(3); plt.clf();
65+
ctrl.bode_plot(ctrl.tf([1], [1,2,1]))
66+
plt.clf()
67+
ctrl.bode_plot(ctrl.tf([5], [1, 1]))
68+
69+
# Check to make sure there are two axes and that each axes has one line
70+
assert len(plt.gcf().axes) == 2
71+
for ax in plt.gcf().axes:
72+
# Make sure there is only 1 line in the subplot
73+
assert len(ax.get_lines()) == 1
74+
75+
# Now add a line to the magnitude plot and make sure if is there
76+
for ax in plt.gcf().axes:
77+
if ax.get_label() == 'control-bode-magnitude':
78+
break
79+
ax.semilogx([1e-2, 1e1], 20 * np.log10([1, 1]), 'k-')
80+
assert len(ax.get_lines()) == 2
81+
3782
def test_doubleint(self):
3883
# 30 May 2016, RMM: added to replicate typecast bug in freqresp.py
3984
A = np.matrix('0, 1; 0, 0');

doc/control.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@ Frequency domain plotting
4444
gangof4_plot
4545
nichols_plot
4646

47+
Note: For plotting commands that create multiple axes on the same plot, the
48+
individual axes can be retrieved using the axes label (retrieved using the
49+
`get_label` method for the matplotliib axes object). The following labels
50+
are currently defined:
51+
52+
* Bode plots: `control-bode-magnitude`, `control-bode-phase`
53+
* Gang of 4 plots: `control-gangof4-s`, `control-gangof4-cs`,
54+
`control-gangof4-ps`, `control-gangof4-t`
55+
4756
Time domain simulation
4857
======================
4958

examples/pvtol-nested.py

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -81,26 +81,45 @@
8181
T = feedback(L, 1);
8282

8383
# Compute stability margins
84-
#! Not yet implemented
85-
# (gm, pm, wgc, wpc) = margin(L);
84+
(gm, pm, wgc, wpc) = margin(L);
85+
print("Gain margin: %g at %g" % (gm, wgc))
86+
print("Phase margin: %g at %g" % (pm, wpc))
8687

87-
#! TODO: this figure has something wrong; axis limits mismatch
8888
figure(6); clf;
89-
bode(L);
89+
bode(L, logspace(-4, 3));
9090

91-
# Add crossover line
92-
subplot(211); hold(True);
93-
loglog([1e-4, 1e3], [1, 1], 'k-')
91+
# Add crossover line to the magnitude plot
92+
#
93+
# Note: in matplotlib before v2.1, the following code worked:
94+
#
95+
# subplot(211); hold(True);
96+
# loglog([1e-4, 1e3], [1, 1], 'k-')
97+
#
98+
# In later versions of matplotlib the call to subplot will clear the
99+
# axes and so we have to extract the axes that we want to use by hand.
100+
# In addition, hold() is deprecated so we no longer require it.
101+
#
102+
for ax in gcf().axes:
103+
if ax.get_label() == 'control-bode-magnitude':
104+
break
105+
ax.semilogx([1e-4, 1e3], 20 * np.log10([1, 1]), 'k-')
94106

107+
#
95108
# Replot phase starting at -90 degrees
96-
bode(L, logspace(-4, 3));
109+
#
110+
# Get the phase plot axes
111+
for ax in gcf().axes:
112+
if ax.get_label() == 'control-bode-phase':
113+
break
114+
115+
# Recreate the frequency response and shift the phase
97116
(mag, phase, w) = freqresp(L, logspace(-4, 3));
98117
phase = phase - 360;
99-
subplot(212);
100-
semilogx([1e-4, 1e3], [-180, -180], 'k-')
101-
hold(True);
102-
semilogx(w, np.squeeze(phase), 'b-')
103-
axis([1e-4, 1e3, -360, 0]);
118+
119+
# Replot the phase by hand
120+
ax.semilogx([1e-4, 1e3], [-180, -180], 'k-')
121+
ax.semilogx(w, np.squeeze(phase), 'b-')
122+
ax.axis([1e-4, 1e3, -360, 0]);
104123
xlabel('Frequency [deg]'); ylabel('Phase [deg]');
105124
# set(gca, 'YTick', [-360, -270, -180, -90, 0]);
106125
# set(gca, 'XTick', [10^-4, 10^-2, 1, 100]);
@@ -109,7 +128,6 @@
109128
# Nyquist plot for complete design
110129
#
111130
figure(7); clf;
112-
axis([-700, 5300, -3000, 3000]); hold(True);
113131
nyquist(L, (0.0001, 1000));
114132
axis([-700, 5300, -3000, 3000]);
115133

@@ -118,7 +136,6 @@
118136

119137
# Expanded region
120138
figure(8); clf; subplot(231);
121-
axis([-10, 5, -20, 20]); hold(True);
122139
nyquist(L);
123140
axis([-10, 5, -20, 20]);
124141

@@ -136,7 +153,7 @@
136153

137154
figure(9);
138155
(Yvec, Tvec) = step(T, linspace(0, 20));
139-
plot(Tvec.T, Yvec.T); hold(True);
156+
plot(Tvec.T, Yvec.T);
140157

141158
(Yvec, Tvec) = step(Co*S, linspace(0, 20));
142159
plot(Tvec.T, Yvec.T);

0 commit comments

Comments
 (0)