@@ -215,37 +215,31 @@ def _bandwidth(self, dbdrop=-3):
215
215
if not (np .isscalar (dbdrop )) or dbdrop >= 0 :
216
216
raise ValueError ("expecting dbdrop be a negative scalar in dB" )
217
217
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
231
223
from control .freqplot import _default_frequency_range
232
224
omega = _default_frequency_range (self )
233
225
mag , phase , omega = self .frequency_response (omega )
226
+ idx_dropped = np .nonzero (mag - dcgain * 10 ** (dbdrop / 20 ) < 0 )[0 ]
234
227
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
247
231
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 )
249
243
250
244
def ispassive (self ):
251
245
# importing here prevents circular dependancy
@@ -557,10 +551,17 @@ def bandwidth(sys, dbdrop=-3):
557
551
558
552
Returns
559
553
-------
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
+
564
565
Example
565
566
-------
566
567
>>> G = ct.tf([1], [1, 1])
@@ -575,6 +576,9 @@ def bandwidth(sys, dbdrop=-3):
575
576
0.1018
576
577
577
578
"""
579
+ if not isinstance (sys , LTI ):
580
+ raise TypeError ("sys must be a LTI instance." )
581
+
578
582
return sys .bandwidth (dbdrop )
579
583
580
584
0 commit comments