8000 solve bandwidth by bisection for zero-crossing · python-control/python-control@bc2ddff · GitHub
[go: up one dir, main page]

Skip to content

Commit bc2ddff

Browse files
committed
solve bandwidth by bisection for zero-crossing
1 parent c06a205 commit bc2ddff

File tree

3 files changed

+69
-1
lines changed

3 files changed

+69
-1
lines changed

control/lti.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from .namedio import NamedIOSystem, isdtime
2121

2222
__all__ = ['poles', 'zeros', 'damp', 'evalfr', 'frequency_response',
23-
'freqresp', 'dcgain', 'pole', 'zero']
23+
'freqresp', 'dcgain', 'bandwidth', 'pole', 'zero']
2424

2525

2626
class LTI(NamedIOSystem):
@@ -202,6 +202,39 @@ def _dcgain(self, warn_infinite):
202202
else:
203203
return zeroresp
204204

205+
def bandwidth(self, dbdrop=-3):
206+
"""Return the bandwidth"""
207+
raise NotImplementedError("bandwidth not implemented for %s objects" %
208+
str(self.__class__))
209+
210+
def _bandwidth(self, dbdrop=-3):
211+
# check if system is SISO and dbdrop is a negative scalar
212+
if (not self.issiso()) and (dbdrop >= 0):
213+
raise ValueError("NOT sure what to raise #TODO ")
214+
215+
# result = scipy.optimize.root(lambda w: np.abs(self(w*1j)) - np.abs(self.dcgain())*10**(dbdrop/20), x0=1)
216+
# # this will probabily fail if there is a resonant frequency larger than the bandwidth, the initial guess can be around that peak
217+
218+
# use bodeplot to identify the 0-crossing bracket
219+
from control.freqplot import _default_frequency_range
220+
omega = _default_frequency_range(self)
221+
mag, phase, omega = self.frequency_response(omega)
222+
223+
dcgain = self.dcgain()
224+
idx_out = np.nonzero(mag - dcgain*10**(dbdrop/20) < 0)[0][0]
225+
226+
# solve for the bandwidth, use scipy.optimize.root_scalar() to solve using bisection
227+
import scipy
228+
result = scipy.optimize.root_scalar(lambda w: np.abs(self(w*1j)) - np.abs(dcgain)*10**(dbdrop/20),
229+
bracket=[omega[idx_out-1], omega[idx_out]],
230+
method='bisect')
231+
232+
# check solution
233+
if result.converged:
234+
return np.abs(result.root)
235+
else:
236+
raise Exception(result.message)
237+
205238
def ispassive(self):
206239
# importing here prevents circular dependancy
207240
from control.passivity import ispassive
@@ -499,6 +532,33 @@ def dcgain(sys):
499532
return sys.dcgain()
500533

501534

535+
def bandwidth(sys, dbdrop=-3):
536+
"""Return the first freqency where the gain drop by dbdrop of the system.
537+
538+
Parameters
539+
----------
540+
sys: StateSpace or TransferFunction
541+
Linear system
542+
dbdrop : float, optional
543+
By how much the gain drop in dB (default = -3) that defines the
544+
bandwidth. Should be a negative scalar
545+
546+
Returns
547+
-------
548+
bandwidth : #TODO data-type
549+
The first frequency where the gain drops below dbdrop of the dc gain
550+
of the system.
551+
552+
Example
553+
-------
554+
>>> G = ct.tf([1], [1, 2])
555+
>>> ct.bandwidth(G)
556+
0.9976
557+
558+
"""
559+
return sys.bandwidth(dbdrop)
560+
561+
502562
# Process frequency responses in a uniform way
503563
def _process_frequency_response(sys, omega, out, squeeze=None):
504564
# Set value of squeeze argument if not set

control/statesp.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1424,6 +1424,10 @@ def dcgain(self, warn_infinite=False):
14241424
"""
14251425
return self._dcgain(warn_infinite)
14261426

1427+
def bandwidth(self, dbdrop=-3):
1428+
"""Return the bandwith"""
1429+
return self._bandwidth(dbdrop)
1430+
14271431
def dynamics(self, t, x, u=None, params=None):
14281432
"""Compute the dynamics of the system
14291433

control/xferfcn.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1246,6 +1246,10 @@ def dcgain(self, warn_infinite=False):
12461246
12471247
"""
12481248
return self._dcgain(warn_infinite)
1249+
1250+
def bandwidth(self, dbdrop=-3):
1251+
"""Return the bandwith"""
1252+
return self< 4728 /span>._bandwidth(dbdrop)
12491253

12501254
def _isstatic(self):
12511255
"""returns True if and only if all of the numerator and denominator

0 commit comments

Comments
 (0)
0