47
47
import math
48
48
from .ctrlutil import unwrap
49
49
from .bdalg import feedback
50
+ from .margins import stability_margins
50
51
51
52
__all__ = ['bode_plot' , 'nyquist_plot' , 'gangof4_plot' ,
52
53
'bode' , 'nyquist' , 'gangof4' ]
60
61
61
62
# Bode plot
62
63
def bode_plot (syslist , omega = None , dB = None , Hz = None , deg = None ,
63
- Plot = True , omega_limits = None , omega_num = None , * args , ** kwargs ):
64
+ Plot = True , omega_limits = None , omega_num = None ,margins = None , * args , ** kwargs ):
64
65
"""Bode plot for a system
65
66
66
67
Plots a Bode plot for the system over a (optional) frequency range.
@@ -208,7 +209,7 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
208
209
color = pltline [0 ].get_color ())
209
210
210
211
# Add a grid to the plot + labeling
211
- ax_mag .grid (True , which = 'both' )
212
+ ax_mag .grid (False if margins else True , which = 'both' )
212
213
ax_mag .set_ylabel ("Magnitude (dB)" if dB else "Magnitude" )
213
214
214
215
# Phase plot
@@ -218,6 +219,50 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
218
219
phase_plot = phase
219
220
ax_phase .semilogx (omega_plot , phase_plot , * args , ** kwargs )
220
221
222
+ #show the phase and gain margins in the plot
223
+ if margins :
224
+ margin = stability_margins (sys )
225
+ gm , pm , Wcg , Wcp = margin [0 ], margin [1 ], margin [3 ], margin [4 ]
226
+ if pm >= 0. :
227
+ phase_limit = - 180.
228
+ else :
229
+ phase_limit = 180.
230
+
231
+ ax_mag .axhline (y = 0 if dB else 1 , color = 'k' , linestyle = ':' )
232
+ ax_phase .axhline (y = phase_limit if deg else math .radians (phase_limit ), color = 'k' , linestyle = ':' )
233
+ mag_ylim = ax_mag .get_ylim ()
234
+ phase_ylim = ax_phase .get_ylim ()
235
+
236
+ if pm != float ('inf' ) and Wcp != float ('nan' ):
237
+ if dB :
238
+ ax_mag .semilogx ([Wcp , Wcp ], [0 , - (10 ** 5. )], color = 'k' , linestyle = ':' )
239
+ else :
240
+ ax_mag .loglog ([Wcp ,Wcp ], [1 ,10. ** - 20. ],color = 'k' ,linestyle = ':' )
241
+
242
+ 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 = '-' )
245
+ 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 = '-' )
248
+
249
+ if gm != float ('inf' ) and Wcg != float ('nan' ):
250
+ 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 = '-' )
253
+ 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 = '-' )
256
+
257
+ if deg :
258
+ ax_phase .semilogx ([Wcg , Wcg ], [10. ** - 20. , phase_limit ], color = 'k' , linestyle = ':' )
259
+ else :
260
+ ax_phase .semilogx ([Wcg , Wcg ], [10. ** - 20. , - math .pi ], color = 'k' , linestyle = ':' )
261
+
262
+ ax_mag .set_ylim (mag_ylim )
263
+ 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 ))
265
+
221
266
if nyquistfrq_plot :
222
267
ax_phase .axvline (nyquistfrq_plot , color = pltline [0 ].get_color ())
223
268
@@ -236,7 +281,7 @@ def genZeroCenteredSeries(val_min, val_max, period):
236
281
ylim = ax_phase .get_ylim ()
237
282
ax_phase .set_yticks (genZeroCenteredSeries (ylim [0 ], ylim [1 ], math .pi / 4. ))
238
283
ax_phase .set_yticks (genZeroCenteredSeries (ylim [0 ], ylim [1 ], math .pi / 12. ), minor = True )
239
- ax_phase .grid (True , which = 'both' )
284
+ ax_phase .grid (False if margins else True , which = 'both' )
240
285
# ax_mag.grid(which='minor', alpha=0.3)
241
286
# ax_mag.grid(which='major', alpha=0.9)
242
287
# ax_phase.grid(which='minor', alpha=0.3)
0 commit comments