4747import math
4848from .ctrlutil import unwrap
4949from .bdalg import feedback
50+ from .margins import stability_margins
5051
5152__all__ = ['bode_plot' , 'nyquist_plot' , 'gangof4_plot' ,
5253 'bode' , 'nyquist' , 'gangof4' ]
6061
6162# Bode plot
6263def 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 ):
6465 """
6566 Bode plot for a system
6667
@@ -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
@@ -209,7 +212,7 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
209212 color = pltline [0 ].get_color ())
210213
211214 # Add a grid to the plot + labeling
212- ax_mag .grid (True , which = 'both' )
215+ ax_mag .grid (False if margins else True , which = 'both' )
213216 ax_mag .set_ylabel ("Magnitude (dB)" if dB else "Magnitude" )
214217
215218 # Phase plot
@@ -219,6 +222,50 @@ 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
225+ # Show the phase and gain margins in the plot
226+ if margins :
227+ margin = stability_margins (sys )
228+ gm , pm , Wcg , Wcp = margin [0 ], margin [1 ], margin [3 ], margin [4 ]
229+ if pm >= 0. :
230+ phase_limit = - 180.
231+ else :
232+ phase_limit = 180.
233+
234+ ax_mag .axhline (y = 0 if dB else 1 , color = 'k' , linestyle = ':' )
235+ ax_phase .axhline (y = phase_limit if deg else math .radians (phase_limit ), color = 'k' , linestyle = ':' )
236+ mag_ylim = ax_mag .get_ylim ()
237+ phase_ylim = ax_phase .get_ylim ()
238+
239+ if pm != float ('inf' ) and Wcp != float ('nan' ):
240+ if dB :
241+ ax_mag .semilogx ([Wcp , Wcp ], [0. ,- 1e5 ],color = 'k' , linestyle = ':' )
242+ else :
243+ ax_mag .loglog ([Wcp ,Wcp ], [1. ,1e-8 ],color = 'k' ,linestyle = ':' )
244+
245+ if deg :
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' )
248+ else :
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' )
251+
252+ if gm != float ('inf' ) and Wcg != float ('nan' ):
253+ if dB :
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' )
256+ else :
257+ ax_mag .loglog ([Wcg , Wcg ], [1. / gm ,1e-8 ],color = 'k' , linestyle = ':' )
258+ ax_mag .loglog ([Wcg , Wcg ], [1. ,1. / gm ],color = 'k' )
259+
260+ if deg :
261+ ax_phase .semilogx ([Wcg , Wcg ], [1e-8 , phase_limit ],color = 'k' , linestyle = ':' )
262+ else :
263+ ax_phase .semilogx ([Wcg , Wcg ], [1e-8 , math .radians (phase_limit )],color = 'k' , linestyle = ':' )
264+
265+ ax_mag .set_ylim (mag_ylim )
266+ ax_phase .set_ylim (phase_ylim )
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 ))
268+
222269 if nyquistfrq_plot :
223270 ax_phase .axvline (nyquistfrq_plot , color = pltline [0 ].get_color ())
224271
@@ -237,7 +284,7 @@ def genZeroCenteredSeries(val_min, val_max, period):
237284 ylim = ax_phase .get_ylim ()
238285 ax_phase .set_yticks (genZeroCenteredSeries (ylim [0 ], ylim [1 ], math .pi / 4. ))
239286 ax_phase .set_yticks (genZeroCenteredSeries (ylim [0 ], ylim [1 ], math .pi / 12. ), minor = True )
240- ax_phase .grid (True , which = 'both' )
287+ ax_phase .grid (False if margins else True , which = 'both' )
241288 # ax_mag.grid(which='minor', alpha=0.3)
242289 # ax_mag.grid(which='major', alpha=0.9)
243290 # ax_phase.grid(which='minor', alpha=0.3)
0 commit comments