@@ -215,37 +215,31 @@ def _bandwidth(self, dbdrop=-3):
215215 if not (np .isscalar (dbdrop )) or dbdrop >= 0 :
216216 raise ValueError ("expecting dbdrop be a negative scalar in dB" )
217217
218- # # # this will probabily fail if there is a resonant frequency larger than the bandwidth, the initial guess can be around that peak
219- # G1 = ct.tf(0.1, [1, 0.1])
220- # wn2 = 0.9
221- # zeta2 = 0.001
222- # G2 = ct.tf(wn2**2, [1, 2*zeta2*wn2, wn2**2])
223- # ct.bandwidth(G1*G2)
224- # import scipy
225- # result = scipy.optimize.root(lambda w: np.abs(self(w*1j)) - np.abs(self.dcgain())*10**(dbdrop/20), x0=1)
226-
227- # if result.success:
228- # return np.abs(result.x)[0]
229-
230- # use bodeplot to identify the 0-crossing bracket
218+ dcgain = self .dcgain ()
219+ if np .isinf (dcgain ):
220+ return np .nan
221+
222+ # use frequency range to identify the 0-crossing (dbdrop) bracket
231223 from control .freqplot import _default_frequency_range
232224 omega = _default_frequency_range (self )
233225 mag , phase , omega = self .frequency_response (omega )
226+ idx_dropped = np .nonzero (mag - dcgain * 10 ** (dbdrop / 20 ) < 0 )[0 ]
234227
235- dcgain = self .dcgain ()
236- idx_dropped = np .nonzero (mag - dcgain * 10 ** (dbdrop / 20 ) < 0 )[0 ][0 ]
237-
238- # solve for the bandwidth, use scipy.optimize.root_scalar() to solve using bisection
239- import scipy
240- result = scipy .optimize .root_scalar (lambda w : np .abs (self (w * 1j )) - np .abs (dcgain )* 10 ** (dbdrop / 20 ),
241- bracket = [omega [idx_dropped - 1 ], omega [idx_dropped ]],
242- method = 'bisect' )
243-
244- # check solution
245- if result .converged :
246- return np .abs (result .root )
228+ if idx_dropped .shape [0 ] == 0 :
229+ # no frequency response is dbdrop below the dc gain.
230+ return np .inf
247231 else :
248- raise Exception (result .message )
232+ # solve for the bandwidth, use scipy.optimize.root_scalar() to solve using bisection
233+ import scipy
234+ result = scipy .optimize .root_scalar (lambda w : np .abs (self (w * 1j )) - np .abs (dcgain )* 10 ** (dbdrop / 20 ),
235+ bracket = [omega [idx_dropped [0 ] - 1 ], omega [idx_dropped [0 ]]],
236+ method = 'bisect' )
237+
238+ # check solution
239+ if result .converged :
240+ return np .abs (result .root )
241+ else :
242+ raise Exception (result .message )
249243
250244 def ispassive (self ):
251245 # importing here prevents circular dependancy
@@ -557,10 +551,17 @@ def bandwidth(sys, dbdrop=-3):
557551
558552 Returns
559553 -------
560- bandwidth : #TODO data-type
561- The first frequency where the gain drops below dbdrop of the dc gain
562- of the system.
563-
554+ bandwidth : ndarray
555+ The first frequency (rad/time-unit) where the gain drops below dbdrop of the dc gain
556+ of the system, or nan if the system has infinite dc gain, inf if the gain does not drop for all frequency
557+
558+ Raises
559+ ------
560+ TypeError
561+ if 'sys' is not an SISO LTI instance
562+ ValueError
563+ if 'dbdrop' is not a negative scalar
564+
564565 Example
565566 -------
566567 >>> G = ct.tf([1], [1, 1])
@@ -575,6 +576,9 @@ def bandwidth(sys, dbdrop=-3):
575576 0.1018
576577
577578 """
579+ if not isinstance (sys , LTI ):
580+ raise TypeError ("sys must be a LTI instance." )
581+
578582 return sys .bandwidth (dbdrop )
579583
580584
0 commit comments