Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

bandwidth feature#889

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
murrayrm merged 14 commits intopython-control:mainfromSCLiao47:feature_bandwidth
May 15, 2023
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
14 commits
Select commitHold shift + click to select a range
65988d1
solve bandwidth by bisection for zero-crossing
SCLiao47Apr 28, 2023
86ff95c
testing suggested method by Kreijstal
SCLiao47May 1, 2023
70e912f
Implemented and passed nominal test
SCLiao47May 1, 2023
0c81cb2
Handle integrator, all-pass filters
SCLiao47May 1, 2023
d59d19a
adjust for PEP8 coding style
SCLiao47May 1, 2023
bc2ddff
solve bandwidth by bisection for zero-crossing
SCLiao47Apr 28, 2023
409d0c6
testing suggested method by Kreijstal
SCLiao47May 1, 2023
723ddc8
Implemented and passed nominal test
SCLiao47May 1, 2023
6b51432
Handle integrator, all-pass filters
SCLiao47May 1, 2023
9f86b41
adjust for PEP8 coding style
SCLiao47May 1, 2023
28859c2
Merge branch 'feature_bandwidth' of https://github.com/SCLiao47/pytho…
SCLiao47May 1, 2023
a370cdb
remove _bandwidth method in lti.py
SCLiao47May 4, 2023
02172b7
resolving format issues in xfrefcn.py
SCLiao47May 7, 2023
ab68562
add docstring to lti.bandwidth
SCLiao47May 8, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 108 additions & 1 deletioncontrol/lti.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,7 +20,7 @@
from .namedio import NamedIOSystem, isdtime

__all__ = ['poles', 'zeros', 'damp', 'evalfr', 'frequency_response',
'freqresp', 'dcgain', 'pole', 'zero']
'freqresp', 'dcgain', 'bandwidth', 'pole', 'zero']


class LTI(NamedIOSystem):
Expand DownExpand Up@@ -202,6 +202,68 @@ def _dcgain(self, warn_infinite):
else:
return zeroresp

def bandwidth(self, dbdrop=-3):
"""Evaluate the bandwidth of the LTI system for a given dB drop.

Evaluate the first frequency that the response magnitude is lower than
DC gain by dbdrop dB.

Parameters
----------
dpdrop : float, optional
A strictly negative scalar in dB (default = -3) defines the
amount of gain drop for deciding bandwidth.

Returns
-------
bandwidth : ndarray
The first frequency (rad/time-unit) where the gain drops below
dbdrop of the dc gain of the system, or nan if the system has
infinite dc gain, inf if the gain does not drop for all frequency

Raises
------
TypeError
if 'sys' is not an SISO LTI instance
ValueError
if 'dbdrop' is not a negative scalar
"""
# check if system is SISO and dbdrop is a negative scalar
if not self.issiso():
raise TypeError("system should be a SISO system")

if (not np.isscalar(dbdrop)) or dbdrop >= 0:
raise ValueError("expecting dbdrop be a negative scalar in dB")

dcgain = self.dcgain()
if np.isinf(dcgain):
# infinite dcgain, return np.nan
return np.nan

# use frequency range to identify the 0-crossing (dbdrop) bracket
from control.freqplot import _default_frequency_range
omega = _default_frequency_range(self)
mag, phase, omega = self.frequency_response(omega)
idx_dropped = np.nonzero(mag - dcgain*10**(dbdrop/20) < 0)[0]

if idx_dropped.shape[0] == 0:
# no frequency response is dbdrop below the dc gain, return np.inf
return np.inf
else:
# solve for the bandwidth, use scipy.optimize.root_scalar() to
# solve using bisection
import scipy
result = scipy.optimize.root_scalar(
lambda w: np.abs(self(w*1j)) - np.abs(dcgain)*10**(dbdrop/20),
bracket=[omega[idx_dropped[0] - 1], omega[idx_dropped[0]]],
method='bisect')

# check solution
if result.converged:
return np.abs(result.root)
else:
raise Exception(result.message)

def ispassive(self):
# importing here prevents circular dependancy
from control.passivity import ispassive
Expand DownExpand Up@@ -499,6 +561,51 @@ def dcgain(sys):
return sys.dcgain()


def bandwidth(sys, dbdrop=-3):
"""Return the first freqency where the gain drop by dbdrop of the system.

Parameters
----------
sys: StateSpace or TransferFunction
Linear system
dbdrop : float, optional
By how much the gain drop in dB (default = -3) that defines the
bandwidth. Should be a negative scalar

Returns
-------
bandwidth : ndarray
The first frequency (rad/time-unit) where the gain drops below dbdrop
of the dc gain of the system, or nan if the system has infinite dc
gain, inf if the gain does not drop for all frequency

Raises
------
TypeError
if 'sys' is not an SISO LTI instance
ValueError
if 'dbdrop' is not a negative scalar

Example
-------
>>> G = ct.tf([1], [1, 1])
>>> ct.bandwidth(G)
0.9976

>>> G1 = ct.tf(0.1, [1, 0.1])
>>> wn2 = 1
>>> zeta2 = 0.001
>>> G2 = ct.tf(wn2**2, [1, 2*zeta2*wn2, wn2**2])
>>> ct.bandwidth(G1*G2)
0.1018

"""
if not isinstance(sys, LTI):
raise TypeError("sys must be a LTI instance.")

return sys.bandwidth(dbdrop)


# Process frequency responses in a uniform way
def _process_frequency_response(sys, omega, out, squeeze=None):
# Set value of squeeze argument if not set
Expand Down
2 changes: 1 addition & 1 deletioncontrol/matlab/__init__.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -189,7 +189,7 @@

== ========================== ============================================
\* :func:`dcgain` steady-state (D.C.) gain
\ lti/bandwidth system bandwidth
\* :func:`bandwidth` system bandwidth
\ lti/norm h2 and Hinfinity norms of LTI models
\* :func:`pole` system poles
\* :func:`zero` system (transmission) zeros
Expand Down
34 changes: 33 additions & 1 deletioncontrol/tests/lti_test.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,7 @@

import control as ct
from control import c2d, tf, ss, tf2ss, NonlinearIOSystem
from control.lti import LTI, evalfr, damp, dcgain, zeros, poles
from control.lti import LTI, evalfr, damp, dcgain, zeros, poles, bandwidth
from control import common_timebase, isctime, isdtime, issiso, timebaseEqual
from control.tests.conftest import slycotonly
from control.exception import slycot_check
Expand DownExpand Up@@ -104,6 +104,38 @@ def test_dcgain(self):
np.testing.assert_allclose(sys.dcgain(), 42)
np.testing.assert_allclose(dcgain(sys), 42)

def test_bandwidth(self):
# test a first-order system, compared with matlab
sys1 = tf(0.1, [1, 0.1])
np.testing.assert_allclose(sys1.bandwidth(), 0.099762834511098)
np.testing.assert_allclose(bandwidth(sys1), 0.099762834511098)

# test a second-order system, compared with matlab
wn2 = 1
zeta2 = 0.001
sys2 = sys1 * tf(wn2**2, [1, 2*zeta2*wn2, wn2**2])
np.testing.assert_allclose(sys2.bandwidth(), 0.101848388240241)
np.testing.assert_allclose(bandwidth(sys2), 0.101848388240241)

# test constant gain, bandwidth should be infinity
sysAP = tf(1,1)
np.testing.assert_allclose(bandwidth(sysAP), np.inf)

# test integrator, bandwidth should return np.nan
sysInt = tf(1, [1, 0])
np.testing.assert_allclose(bandwidth(sysInt), np.nan)

# test exception for system other than LTI
np.testing.assert_raises(TypeError, bandwidth, 1)

# test exception for system other than SISO system
sysMIMO = tf([[[-1, 41], [1]], [[1, 2], [3, 4]]],
[[[1, 10], [1, 20]], [[1, 30], [1, 40]]])
np.testing.assert_raises(TypeError, bandwidth, sysMIMO)

# test if raise exception if dbdrop is positive scalar
np.testing.assert_raises(ValueError, bandwidth, sys1, 3)

@pytest.mark.parametrize("dt1, dt2, expected",
[(None, None, True),
(None, 0, True),
Expand Down
2 changes: 2 additions & 0 deletionscontrol/xferfcn.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -74,6 +74,7 @@
'xferfcn.floating_point_format': '.4g'
}


def _float2str(value):
_num_format = config.defaults.get('xferfcn.floating_point_format', ':.4g')
return f"{value:{_num_format}}"
Expand DownExpand Up@@ -1407,6 +1408,7 @@ def _tf_factorized_polynomial_to_string(roots, gain=1, var='s'):

return multiplier + " ".join(factors)


def _tf_string_to_latex(thestr, var='s'):
""" make sure to superscript all digits in a polynomial string
and convert float coefficients in scientific notation
Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp