@@ -211,36 +211,40 @@ def fun(wdt):
211211# Sawyer B. Fuller <minster@uw.edu>, removed a lot of the innards
212212# and replaced with analytical polynomial functions for LTI systems.
213213#
214- # idea for the frequency data solution copied/adapted from
214+ # The idea for the frequency data solution copied/adapted from
215215# https://github.com/alchemyst/Skogestad-Python/blob/master/BODE.py
216216# Rene van Paassen <rene.vanpaassen@gmail.com>
217217#
218218# RvP, July 8, 2014, corrected to exclude phase=0 crossing for the gain
219219# margin polynomial
220+ #
220221# RvP, July 8, 2015, augmented to calculate all phase/gain crossings with
221222# frd data. Correct to return smallest phase
222223# margin, smallest gain margin and their frequencies
223- # RvP, Jun 10, 2017, modified the inclusion of roots found for phase
224- # crossing to include all >= 0, made subsequent calc
225- # insensitive to div by 0
226- # also changed the selection of which crossings to
227- # return on basis of "A note on the Gain and Phase
224+ #
225+ # RvP, Jun 10, 2017, modified the inclusion of roots found for phase crossing
226+ # to include all >= 0, made subsequent calc insensitive to
227+ # div by 0. Also changed the selection of which crossings
228+ # to return on basis of "A note on the Gain and Phase
228229# Margin Concepts" Journal of Control and Systems
229- # Engineering, Yazdan Bavafi-Toosi, Dec 2015, vol 3
230- # issue 1, pp 51-59, closer to Matlab behavior, but
231- # not completely identical in edge cases, which don't
232- # cross but touch gain=1
230+ # Engineering, Yazdan Bavafi-Toosi, Dec 2015, vol 3 issue
231+ # 1, pp 51-59, closer to Matlab behavior, but not
232+ # completely identical in edge cases, which don't cross but
233+ # touch gain=1.
234+ #
233235# BG, Nov 9, 2020, removed duplicate implementations of the same code
234236# for crossover frequencies and enhanced to handle discrete
235237# systems
238+
239+
236240def stability_margins (sysdata , returnall = False , epsw = 0.0 ):
237241 """Calculate stability margins and associated crossover frequencies.
238242
239243 Parameters
240244 ----------
241245 sysdata: LTI system or (mag, phase, omega) sequence
242246 sys : LTI system
243- Linear SISO system
247+ Linear SISO system representing the loop transfer function
244248 mag, phase, omega : sequence of array_like
245249 Arrays of magnitudes (absolute values, not dB), phases (degrees),
246250 and corresponding frequencies. Crossover frequencies returned are
@@ -255,18 +259,25 @@ def stability_margins(sysdata, returnall=False, epsw=0.0):
255259
256260 Returns
257261 -------
258- gm: float or array_like
262+ gm : float or array_like
259263 Gain margin
260- pm: float or array_loke
264+ pm : float or array_loke
261265 Phase margin
262- sm: float or array_like
266+ sm : float or array_like
263267 Stability margin, the minimum distance from the Nyquist plot to -1
264- wg: float or array_like
265- Frequency for gain margin (at phase crossover, phase = -180 degrees)
266- wp: float or array_like
267- Frequency for phase margin (at gain crossover, gain = 1)
268- ws: float or array_like
269- Frequency for stability margin (complex gain closest to -1)
268+ wpc : float or array_like
269+ Phase crossover frequency (where phase crosses -180 degrees)
270+ wgc : float or array_like
271+ Gain crossover frequency (where gain crosses 1)
272+ wms : float or array_like
273+ Stability margin frequency (where Nyquist plot is closest to -1)
274+
275+ Note that the gain margin is determined by the gain of the loop
276+ transfer function at the phase crossover frequency(s), the phase
277+ margin is determined by the phase of the loop transfer function at
278+ the gain crossover frequency(s), and the stability margin is
279+ determined by the frequency of maximum sensitivity (given by the
280+ magnitude of 1/(1+L)).
270281 """
271282 try :
272283 if isinstance (sysdata , frdata .FRD ):
@@ -398,7 +409,8 @@ def _dstab(w):
398409 (not SM .shape [0 ] and float ('inf' )) or np .amin (SM ),
399410 (not gmidx != - 1 and float ('nan' )) or w_180 [gmidx ][0 ],
400411 (not wc .shape [0 ] and float ('nan' )) or wc [pmidx ][0 ],
401- (not wstab .shape [0 ] and float ('nan' )) or wstab [SM == np .amin (SM )][0 ])
412+ (not wstab .shape [0 ] and float ('nan' )) or
413+ wstab [SM == np .amin (SM )][0 ])
402414
403415
404416# Contributed by Steffen Waldherr <waldherr@ist.uni-stuttgart.de>
@@ -455,7 +467,7 @@ def margin(*args):
455467 ----------
456468 sysdata : LTI system or (mag, phase, omega) sequence
457469 sys : StateSpace or TransferFunction
458- Linear SISO system
470+ Linear SISO system representing the loop transfer function
459471 mag, phase, omega : sequence of array_like
460472 Input magnitude, phase (in deg.), and frequencies (rad/sec) from
461473 bode frequency response data
@@ -466,17 +478,16 @@ def margin(*args):
466478 Gain margin
467479 pm : float
468480 Phase margin (in degrees)
469- wg : float
470- Frequency for gain margin (at phase crossover, phase = -180 degrees)
471- wp : float
472- Frequency for phase margin (at gain crossover, gain = 1)
481+ wpc : float or array_like
482+ Phase crossover frequency (where phase crosses -180 degrees)
483+ wgc : float or array_like
484+ Gain crossover frequency (where gain crosses 1)
473485
474486 Margins are calculated for a SISO open-loop system.
475487
476- If there is more than one gain crossover, the one at the smallest
477- margin (deviation from gain = 1), in absolute sense, is
478- returned. Likewise the smallest phase margin (in absolute sense)
479- is returned.
488+ If there is more than one gain crossover, the one at the smallest margin
489+ (deviation from gain = 1), in absolute sense, is returned. Likewise the
490+ smallest phase margin (in absolute sense) is returned.
480491
481492 Examples
482493 --------
@@ -491,6 +502,6 @@ def margin(*args):
491502 margin = stability_margins (args )
492503 else :
493504 raise ValueError ("Margin needs 1 or 3 arguments; received %i."
494- % len (args ))
505+ % len (args ))
495506
496507 return margin [0 ], margin [1 ], margin [3 ], margin [4 ]
0 commit comments