@@ -194,7 +194,9 @@ def bode_plot(syslist, omega=None,
194194 if not hasattr (syslist , '__iter__' ):
195195 syslist = (syslist ,)
196196
197+
197198 if omega is None :
199+ omega_was_given = False # used do decide whether to include nyq. freq
198200 if omega_limits is None :
199201 # Select a default range if none is provided
200202 omega = default_frequency_range (syslist , Hz = Hz ,
@@ -212,6 +214,46 @@ def bode_plot(syslist, omega=None,
212214 omega = np .logspace (np .log10 (omega_limits [0 ]),
213215 np .log10 (omega_limits [1 ]), num = num ,
214216 endpoint = True )
217+ else :
218+ omega_was_given = True
219+
220+ if plot :
221+ # Set up the axes with labels so that multiple calls to
222+ # bode_plot will superimpose the data. This was implicit
223+ # before matplotlib 2.1, but changed after that (See
224+ # https://github.com/matplotlib/matplotlib/issues/9024).
225+ # The code below should work on all cases.
226+
227+ # Get the current figure
228+
229+ if 'sisotool' in kwargs :
230+ fig = kwargs ['fig' ]
231+ ax_mag = fig .axes [0 ]
232+ ax_phase = fig .axes [2 ]
233+ sisotool = kwargs ['sisotool' ]
234+ del kwargs ['fig' ]
235+ del kwargs ['sisotool' ]
236+ else :
237+ fig = plt .gcf ()
238+ ax_mag = None
239+ ax_phase = None
240+ sisotool = False
241+
242+ # Get the current axes if they already exist
243+ for ax in fig .axes :
244+ if ax .get_label () == 'control-bode-magnitude' :
245+ ax_mag = ax
246+ elif ax .get_label () == 'control-bode-phase' :
247+ ax_phase = ax
248+
249+ # If no axes present, create them from scratch
250+ if ax_mag is None or ax_phase is None :
251+ plt .clf ()
252+ ax_mag = plt .subplot (211 ,
253+ label = 'control-bode-magnitude' )
254+ ax_phase = plt .subplot (212 ,
255+ label = 'control-bode-phase' ,
256+ sharex = ax_mag )
215257
216258 mags , phases , omegas , nyquistfrqs = [], [], [], []
217259 for sys in syslist :
@@ -223,8 +265,11 @@ def bode_plot(syslist, omega=None,
223265 omega_sys = np .asarray (omega )
224266 if sys .isdtime (strict = True ):
225267 nyquistfrq = 2. * math .pi * 1. / sys .dt / 2.
226- omega_sys = omega_sys [omega_sys < nyquistfrq ]
227- # TODO: What distance to the Nyquist frequency is appropriate?
268+ if not omega_was_given :
269+ # include nyquist frequency
270+ omega_sys = np .hstack ((
271+ omega_sys [omega_sys < nyquistfrq ],
272+ nyquistfrq ))
228273 else :
229274 nyquistfrq = None
230275
@@ -285,56 +330,28 @@ def bode_plot(syslist, omega=None,
285330 omega_plot = omega_sys
286331 if nyquistfrq :
287332 nyquistfrq_plot = nyquistfrq
288-
289- # Set up the axes with labels so that multiple calls to
290- # bode_plot will superimpose the data. This was implicit
291- # before matplotlib 2.1, but changed after that (See
292- # https://github.com/matplotlib/matplotlib/issues/9024).
293- # The code below should work on all cases.
294-
295- # Get the current figure
296-
297- if 'sisotool' in kwargs :
298- fig = kwargs ['fig' ]
299- ax_mag = fig .axes [0 ]
300- ax_phase = fig .axes [2 ]
301- sisotool = kwargs ['sisotool' ]
302- del kwargs ['fig' ]
303- del kwargs ['sisotool' ]
304- else :
305- fig = plt .gcf ()
306- ax_mag = None
307- ax_phase = None
308- sisotool = False
309-
310- # Get the current axes if they already exist
311- for ax in fig .axes :
312- if ax .get_label () == 'control-bode-magnitude' :
313- ax_mag = ax
314- elif ax .get_label () == 'control-bode-phase' :
315- ax_phase = ax
316-
317- # If no axes present, create them from scratch
318- if ax_mag is None or ax_phase is None :
319- plt .clf ()
320- ax_mag = plt .subplot (211 ,
321- label = 'control-bode-magnitude' )
322- ax_phase = plt .subplot (212 ,
323- label = 'control-bode-phase' ,
324- sharex = ax_mag )
325-
333+ phase_plot = phase * 180. / math .pi if deg else phase
334+ mag_plot = mag
326335 #
327336 # Magnitude plot
328337 #
338+
339+ if nyquistfrq_plot :
340+ # add data for vertical nyquist freq indicator line
341+ # so it is a single plot action. This preserves line
342+ # order when creating legend eg. legend('sys1', 'sys2)
343+ omega_plot = np .hstack ((omega_plot , nyquistfrq ,nyquistfrq ))
344+ mag_plot = np .hstack ((mag_plot ,
345+ 0.7 * min (mag_plot ),1.3 * max (mag_plot )))
346+ phase_range = max (phase_plot ) - min (phase_plot )
347+ phase_plot = np .hstack ((phase_plot ,
348+ min (phase_plot ) - 0.2 * phase_range ,
349+ max (phase_plot ) + 0.2 * phase_range ))
329350 if dB :
330- pltline = ax_mag .semilogx (omega_plot , 20 * np .log10 (mag ),
351+ ax_mag .semilogx (omega_plot , 20 * np .log10 (mag_plot ),
331352 * args , ** kwargs )
332353 else :
333- pltline = ax_mag .loglog (omega_plot , mag , * args , ** kwargs )
334-
335- if nyquistfrq_plot :
336- ax_mag .axvline (nyquistfrq_plot ,
337- color = pltline [0 ].get_color ())
354+ ax_mag .loglog (omega_plot , mag_plot , * args , ** kwargs )
338355
339356 # Add a grid to the plot + labeling
340357 ax_mag .grid (grid and not margins , which = 'both' )
@@ -343,7 +360,6 @@ def bode_plot(syslist, omega=None,
343360 #
344361 # Phase plot
345362 #
346- phase_plot = phase * 180. / math .pi if deg else phase
347363
348364 # Plot the data
349365 ax_phase .semilogx (omega_plot , phase_plot , * args , ** kwargs )
@@ -463,10 +479,6 @@ def bode_plot(syslist, omega=None,
463479 'deg' if deg else 'rad' ,
464480 Wcp , 'Hz' if Hz else 'rad/s' ))
465481
466- if nyquistfrq_plot :
467- ax_phase .axvline (
468- nyquistfrq_plot , color = pltline [0 ].get_color ())
469-
470482 # Add a grid to the plot + labeling
471483 ax_phase .set_ylabel ("Phase (deg)" if deg else "Phase (rad)" )
472484
0 commit comments