Skip to content

Commit 36bd4e7

Browse files
committed
added unittest,doc string and fixed deg plot of margins
1 parent 9c07bb6 commit 36bd4e7

File tree

2 files changed

+46
-14
lines changed

2 files changed

+46
-14
lines changed

control/freqplot.py

+17-14
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import scipy as sp
4646
import numpy as np
4747
import math
48+
import sys as system
4849
from .ctrlutil import unwrap
4950
from .bdalg import feedback
5051
from .margins import stability_margins
@@ -85,6 +86,8 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
8586
If Hz=True the limits are in Hz otherwise in rad/s.
8687
omega_num: int
8788
number of samples
89+
margins : boolean
90+
if True, plot gain and phase margin
8891
*args, **kwargs:
8992
Additional options to matplotlib (color, linestyle, etc)
9093
@@ -219,7 +222,7 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
219222
phase_plot = phase
220223
ax_phase.semilogx(omega_plot, phase_plot, *args, **kwargs)
221224

222-
#show the phase and gain margins in the plot
225+
# Show the phase and gain margins in the plot
223226
if margins:
224227
margin = stability_margins(sys)
225228
gm, pm, Wcg, Wcp = margin[0], margin[1], margin[3], margin[4]
@@ -235,33 +238,33 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
235238

236239
if pm != float('inf') and Wcp != float('nan'):
237240
if dB:
238-
ax_mag.semilogx([Wcp, Wcp], [0, -(10**5.)], color='k', linestyle=':')
241+
ax_mag.semilogx([Wcp, Wcp], [0.,-1e5],color='k', linestyle=':')
239242
else:
240-
ax_mag.loglog([Wcp,Wcp], [1,10.**-20.],color='k',linestyle=':')
243+
ax_mag.loglog([Wcp,Wcp], [1.,1e-8],color='k',linestyle=':')
241244

242245
if deg:
243-
ax_phase.semilogx([Wcp, Wcp], [(10**5.) if phase_limit > 0. else 10.**-20., phase_limit+pm], color='k', linestyle=':')
244-
ax_phase.semilogx([Wcp, Wcp], [phase_limit + pm, phase_limit], color='k', linestyle='-')
246+
ax_phase.semilogx([Wcp, Wcp], [1e5, phase_limit+pm],color='k', linestyle=':')
247+
ax_phase.semilogx([Wcp, Wcp], [phase_limit + pm, phase_limit],color='k')
245248
else:
246-
ax_phase.semilogx([Wcp, Wcp], [10.**-20., -math.pi+math.radians(pm)], color='k', linestyle=':')
247-
ax_phase.semilogx([Wcp, Wcp], [-math.pi +math.radians(pm), -math.pi], color='k', linestyle='-')
249+
ax_phase.semilogx([Wcp, Wcp], [1e5, math.radians(phase_limit)+math.radians(pm)],color='k', linestyle=':')
250+
ax_phase.semilogx([Wcp, Wcp], [math.radians(phase_limit) +math.radians(pm), math.radians(phase_limit)],color='k')
248251

249252
if gm != float('inf') and Wcg != float('nan'):
250253
if dB:
251-
ax_mag.semilogx([Wcg, Wcg], [-20.*np.log10(gm), -(10**5.)], color='k', linestyle=':')
252-
ax_mag.semilogx([Wcg, Wcg], [0,- 20 * np.log10(gm)], color='k', linestyle='-')
254+
ax_mag.semilogx([Wcg, Wcg], [-20.*np.log10(gm), -1e5],color='k', linestyle=':')
255+
ax_mag.semilogx([Wcg, Wcg], [0,-20*np.log10(gm)],color='k')
253256
else:
254-
ax_mag.semilogx([Wcg, Wcg], [1./gm, 10. ** -20.], color='k', linestyle=':')
255-
ax_mag.semilogx([Wcg, Wcg], [1., 1./gm], color='k', linestyle='-')
257+
ax_mag.loglog([Wcg, Wcg], [1./gm,1e-8],color='k', linestyle=':')
258+
ax_mag.loglog([Wcg, Wcg], [1.,1./gm],color='k')
256259

257260
if deg:
258-
ax_phase.semilogx([Wcg, Wcg], [10.**-20., phase_limit], color='k', linestyle=':')
261+
ax_phase.semilogx([Wcg, Wcg], [1e-8, phase_limit],color='k', linestyle=':')
259262
else:
260-
ax_phase.semilogx([Wcg, Wcg], [10. ** -20., -math.pi], color='k', linestyle=':')
263+
ax_phase.semilogx([Wcg, Wcg], [1e-8, math.radians(phase_limit)],color='k', linestyle=':')
261264

262265
ax_mag.set_ylim(mag_ylim)
263266
ax_phase.set_ylim(phase_ylim)
264-
plt.suptitle('Gm = %.2f %s(at %.2f rad/s), Pm = %.2f %s (at %.2f rad/s)'%(20*np.log10(gm) if dB else gm,'dB ' if dB else '\b',Wcg,pm if deg else math.degrees(pm),'deg' if deg else 'rad',Wcp))
267+
plt.suptitle('Gm = %.2f %s(at %.2f rad/s), Pm = %.2f %s (at %.2f rad/s)'%(20*np.log10(gm) if dB else gm,'dB ' if dB else '\b',Wcg,pm if deg else math.radians(pm),'deg' if deg else 'rad',Wcp))
265268

266269
if nyquistfrq_plot:
267270
ax_phase.axvline(nyquistfrq_plot, color=pltline[0].get_color())

control/tests/freqresp_test.py

+29
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from control.statesp import StateSpace
1313
from control.matlab import ss, tf, bode
1414
from control.exception import slycot_check
15+
from control.tests.margin_test import assert_array_almost_equal
1516
import matplotlib.pyplot as plt
1617

1718
class TestFreqresp(unittest.TestCase):
@@ -107,6 +108,34 @@ def test_mimo(self):
107108
#plt.figure(4)
108109
#bode(sysMIMO,self.omega)
109110

111+
def test_bode_margin(self):
112+
num = [1000]
113+
den = [1, 25, 100, 0]
114+
sys = ctrl.tf(num, den)
115+
ctrl.bode_plot(sys, margins=True,dB=False,deg = True)
116+
fig = plt.gcf()
117+
allaxes = fig.get_axes()
118+
119+
mag_to_infinity = (np.array([6.07828691, 6.07828691]),
120+
np.array([1.00000000e+00, 1.00000000e-08]))
121+
assert_array_almost_equal(mag_to_infinity, allaxes[0].lines[2].get_data())
122+
123+
gm_to_infinty = (np.array([10., 10.]), np.array([4.00000000e-01, 1.00000000e-08]))
124+
assert_array_almost_equal(gm_to_infinty, allaxes[0].lines[3].get_data())
125+
126+
one_to_gm = (np.array([10., 10.]), np.array([1., 0.4]))
127+
assert_array_almost_equal(one_to_gm, allaxes[0].lines[4].get_data())
128+
129+
pm_to_infinity = (np.array([6.07828691, 6.07828691]),
130+
np.array([100000., -157.46405841]))
131+
assert_array_almost_equal(pm_to_infinity, allaxes[1].lines[2].get_data())
132+
133+
pm_to_phase = (np.array([6.07828691, 6.07828691]), np.array([-157.46405841, -180.]))
134+
assert_array_almost_equal(pm_to_phase, allaxes[1].lines[3].get_data())
135+
136+
phase_to_infinity = (np.array([10., 10.]), np.array([1.00000000e-08, -1.80000000e+02]))
137+
assert_array_almost_equal(phase_to_infinity, allaxes[1].lines[4].get_data())
138+
110139
def suite():
111140
return unittest.TestLoader().loadTestsFromTestCase(TestTimeresp)
112141

0 commit comments

Comments
 (0)