1- """margin .py
1+ """margins .py
22
33Functions for computing stability margins and related functions.
44
55Routines in this module:
66
7- margin .stability_margins
8- margin .phase_crossover_frequencies
9- margin .margin
7+ margins .stability_margins
8+ margins .phase_crossover_frequencies
9+ margins .margin
1010"""
1111
1212# Python 3 compatibility (needs to go here)
@@ -211,41 +211,40 @@ def stability_margins(sysdata, returnall=False, epsw=0.0):
211211
212212 else :
213213 # a bit coarse, have the interpolated frd evaluated again
214- def mod (w ):
215- """to give the function to calculate |G(jw)| = 1"""
214+ def _mod (w ):
215+ """Calculate |G(jw)| - 1"""
216216 return np .abs (sys ._evalfr (w )[0 ][0 ]) - 1
217217
218- def arg (w ):
219- """function to calculate the phase angle at -180 deg"""
218+ def _arg (w ):
219+ """Calculate the phase angle at -180 deg"""
220220 return np .angle (- sys ._evalfr (w )[0 ][0 ])
221221
222- def dstab (w ):
223- """function to calculate the distance from -1 point"""
222+ def _dstab (w ):
223+ """Calculate the distance from -1 point"""
224224 return np .abs (sys ._evalfr (w )[0 ][0 ] + 1. )
225225
226226 # Find all crossings, note that this depends on omega having
227227 # a correct range
228- widx = np .where (np .diff (np .sign (mod (sys .omega ))))[0 ]
228+ widx = np .where (np .diff (np .sign (_mod (sys .omega ))))[0 ]
229229 wc = np .array (
230- [ sp .optimize .brentq (mod , sys .omega [i ], sys .omega [i + 1 ])
231- for i in widx if i + 1 < len ( sys . omega ) ])
230+ [sp .optimize .brentq (_mod , sys .omega [i ], sys .omega [i + 1 ])
231+ for i in widx ])
232232
233233 # find the phase crossings ang(H(jw) == -180
234- widx = np .where (np .diff (np .sign (arg (sys .omega ))))[0 ]
234+ widx = np .where (np .diff (np .sign (_arg (sys .omega ))))[0 ]
235235 widx = widx [np .real (sys ._evalfr (sys .omega [widx ])[0 ][0 ]) <= 0 ]
236236 w_180 = np .array (
237- [ sp .optimize .brentq (arg , sys .omega [i ], sys .omega [i + 1 ])
238- for i in widx if i + 1 < len ( sys . omega ) ])
237+ [sp .optimize .brentq (_arg , sys .omega [i ], sys .omega [i + 1 ])
238+ for i in widx ])
239239
240240 # find all stab margins?
241- widx = np .where (np .diff (np .sign (np .diff (dstab (sys .omega )))))[0 ]
242- wstab = np .array ([ sp .optimize .minimize_scalar (
243- dstab , bracket = (sys .omega [i ], sys .omega [i + 1 ])).x
244- for i in widx if i + 1 < len (sys .omega ) and
245- np .diff (np .diff (dstab (sys .omega [i - 1 :i + 2 ])))[0 ] > 0 ])
246- wstab = wstab [(wstab >= sys .omega [0 ]) *
247- (wstab <= sys .omega [- 1 ])]
248-
241+ widx , = np .where (np .diff (np .sign (np .diff (_dstab (sys .omega )))) > 0 )
242+ wstab = np .array (
243+ [sp .optimize .minimize_scalar (_dstab ,
244+ bracket = (sys .omega [i ], sys .omega [i + 1 ])
245+ ).x
246+ for i in widx ])
247+ wstab = wstab [(wstab >= sys .omega [0 ]) * (wstab <= sys .omega [- 1 ])]
249248
250249 # margins, as iterables, converted frdata and xferfcn calculations to
251250 # vector for this
@@ -254,13 +253,13 @@ def dstab(w):
254253 GM = 1.0 / gain_w_180
255254 SM = np .abs (sys ._evalfr (wstab )[0 ][0 ]+ 1 )
256255 PM = np .remainder (np .angle (sys ._evalfr (wc )[0 ][0 ], deg = True ), 360.0 ) - 180.0
257-
256+
258257 if returnall :
259258 return GM , PM , SM , w_180 , wc , wstab
260259 else :
261260 if GM .shape [0 ] and not np .isinf (GM ).all ():
262261 with np .errstate (all = 'ignore' ):
263- gmidx = np .where (np .abs (np .log (GM )) ==
262+ gmidx = np .where (np .abs (np .log (GM )) ==
264263 np .min (np .abs (np .log (GM ))))
265264 else :
266265 gmidx = - 1
@@ -276,7 +275,6 @@ def dstab(w):
276275
277276
278277# Contributed by Steffen Waldherr <waldherr@ist.uni-stuttgart.de>
279- #! TODO - need to add test functions
280278def phase_crossover_frequencies (sys ):
281279 """Compute frequencies and gains at intersections with real axis
282280 in Nyquist plot.
0 commit comments