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
- """Bode plot for a system
64
+ Plot = True , omega_limits = None , omega_num = None ,margins = None , * args , ** kwargs ):
65
+ """
66
+ Bode plot for a system
65
67
66
68
Plots a Bode plot for the system over a (optional) frequency range.
67
69
68
70
Parameters
69
71
----------
70
72
syslist : linsys
71
73
List of linear input/output systems (single system is OK)
72
- omega : freq_range
73
- Range of frequencies in rad/sec
74
+ omega : list
75
+ List of frequencies in rad/sec to be used for frequency response
74
76
dB : boolean
75
77
If True, plot result in dB
76
78
Hz : boolean
@@ -84,7 +86,9 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
84
86
If Hz=True the limits are in Hz otherwise in rad/s.
85
87
omega_num: int
86
88
number of samples
87
- *args, **kwargs:
89
+ margins : boolean
90
+ if True, plot gain and phase margin
91
+ \*args, \**kwargs:
88
92
Additional options to matplotlib (color, linestyle, etc)
89
93
90
94
Returns
@@ -105,7 +109,7 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
105
109
2. If a discrete time model is given, the frequency response is plotted
106
110
along the upper branch of the unit circle, using the mapping z = exp(j
107
111
\omega dt) where omega ranges from 0 to pi/dt and dt is the discrete
108
- time base . If not timebase is specified (dt = True), dt is set to 1.
112
+ timebase . If not timebase is specified (dt = True), dt is set to 1.
109
113
110
114
Examples
111
115
--------
@@ -208,7 +212,7 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
208
212
color = pltline [0 ].get_color ())
209
213
210
214
# Add a grid to the plot + labeling
211
- ax_mag .grid (True , which = 'both' )
215
+ ax_mag .grid (False if margins else True , which = 'both' )
212
216
ax_mag .set_ylabel ("Magnitude (dB)" if dB else "Magnitude" )
213
217
214
218
# Phase plot
@@ -218,6 +222,50 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
218
222
phase_plot = phase
219
223
ax_phase .semilogx (omega_plot , phase_plot , * args , ** kwargs )
220
224
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
+
221
269
if nyquistfrq_plot :
222
270
ax_phase .axvline (nyquistfrq_plot , color = pltline [0 ].get_color ())
223
271
@@ -236,7 +284,7 @@ def genZeroCenteredSeries(val_min, val_max, period):
236
284
ylim = ax_phase .get_ylim ()
237
285
ax_phase .set_yticks (genZeroCenteredSeries (ylim [0 ], ylim [1 ], math .pi / 4. ))
238
286
ax_phase .set_yticks (genZeroCenteredSeries (ylim [0 ], ylim [1 ], math .pi / 12. ), minor = True )
239
- ax_phase .grid (True , which = 'both' )
287
+ ax_phase .grid (False if margins else True , which = 'both' )
240
288
# ax_mag.grid(which='minor', alpha=0.3)
241
289
# ax_mag.grid(which='major', alpha=0.9)
242
290
# ax_phase.grid(which='minor', alpha=0.3)
@@ -253,7 +301,8 @@ def genZeroCenteredSeries(val_min, val_max, period):
253
301
# Nyquist plot
254
302
def nyquist_plot (syslist , omega = None , Plot = True , color = 'b' ,
255
303
labelFreq = 0 , * args , ** kwargs ):
256
- """Nyquist plot for a system
304
+ """
305
+ Nyquist plot for a system
257
306
258
307
Plots a Nyquist plot for the system over a (optional) frequency range.
259
308
@@ -267,7 +316,7 @@ def nyquist_plot(syslist, omega=None, Plot=True, color='b',
267
316
If True, plot magnitude
268
317
labelFreq : int
269
318
Label every nth frequency on the plot
270
- *args, **kwargs:
319
+ \ *args, \ **kwargs:
271
320
Additional options to matplotlib (color, linestyle, etc)
272
321
273
322
Returns
@@ -283,6 +332,7 @@ def nyquist_plot(syslist, omega=None, Plot=True, color='b',
283
332
--------
284
333
>>> sys = ss("1. -2; 3. -4", "5.; 7", "6. 8", "9.")
285
334
>>> real, imag, freq = nyquist_plot(sys)
335
+
286
336
"""
287
337
# If argument was a singleton, turn it into a list
288
338
if (not getattr (syslist , '__iter__' , False )):
0 commit comments