- Notifications
You must be signed in to change notification settings - Fork441
Standardize squeeze processing in frequency response functions#507
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
Uh oh!
There was an error while loading.Please reload this page.
Changes fromall commits
848112d
f367cf6
451d6d2
90da4fb
b338e32
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -15,6 +15,7 @@ | ||
import numpy as np | ||
from numpy import absolute, real, angle, abs | ||
from warnings import warn | ||
from . import config | ||
__all__ = ['issiso', 'timebase', 'common_timebase', 'timebaseEqual', | ||
'isdtime', 'isctime', 'pole', 'zero', 'damp', 'evalfr', | ||
@@ -111,7 +112,7 @@ def damp(self): | ||
Z = -real(splane_poles)/wn | ||
return wn, Z, poles | ||
def frequency_response(self, omega, squeeze=None): | ||
"""Evaluate the linear time-invariant system at an array of angular | ||
frequencies. | ||
@@ -124,30 +125,36 @@ def frequency_response(self, omega, squeeze=True): | ||
G(exp(j*omega*dt)) = mag*exp(j*phase). | ||
In general the system may be multiple input, multiple output (MIMO), | ||
where`m = self.inputs` number of inputs and `p = self.outputs` number | ||
ofoutputs. | ||
Parameters | ||
---------- | ||
omega : float or1Darray_like | ||
A list, tuple, array, or scalar value of frequencies in | ||
radians/sec at which the system will be evaluated. | ||
squeeze : bool, optional | ||
If squeeze=True, remove single-dimensional entries from the shape | ||
of the output even if the system is not SISO. If squeeze=False, | ||
keep all indices (output, input and, if omega is array_like, | ||
frequency) even if the system is SISO. The default value can be | ||
set using config.defaults['control.squeeze_frequency_response']. | ||
Returns | ||
------- | ||
mag : ndarray | ||
The magnitude (absolute value, not dB or log10) of the system | ||
frequency response. If the system is SISO and squeeze is not | ||
True, the array is 1D, indexed by frequency. If the system is not | ||
SISO or squeeze is False, the array is 3D, indexed by the output, | ||
input, and frequency. If ``squeeze`` is True then | ||
single-dimensional axes are removed. | ||
phase : ndarray | ||
The wrapped phase in radians of the system frequency response. | ||
omega : ndarray | ||
The (sorted) frequencies at which the response was evaluated. | ||
""" | ||
omega = np.sort(np.array(omega, ndmin=1)) | ||
if isdtime(self, strict=True): | ||
@@ -463,9 +470,8 @@ def damp(sys, doprint=True): | ||
(p.real, p.imag, d, w)) | ||
return wn, damping, poles | ||
def evalfr(sys, x, squeeze=None): | ||
"""Evaluate the transfer function of an LTI system for complex frequency x. | ||
Returns the complex frequency response `sys(x)` where `x` is `s` for | ||
continuous-time systems and `z` for discrete-time systems, with | ||
@@ -481,17 +487,24 @@ def evalfr(sys, x, squeeze=True): | ||
---------- | ||
sys: StateSpace or TransferFunction | ||
Linear system | ||
x : complex scalar or1Darray_like | ||
Complex frequency(s) | ||
squeeze : bool, optional (default=True) | ||
If squeeze=True, remove single-dimensional entries from the shape of | ||
the output even if the system is not SISO. If squeeze=False, keep all | ||
indices (output, input and, if omega is array_like, frequency) even if | ||
the system is SISO. The default value can be set using | ||
config.defaults['control.squeeze_frequency_response']. | ||
Returns | ||
------- | ||
fresp : complex ndarray | ||
The frequency response of the system. If the system is SISO and | ||
squeeze is not True, the shape of the array matches the shape of | ||
omega. If the system is not SISO or squeeze is False, the first two | ||
dimensions of the array are indices for the output and input and the | ||
remaining dimensions match omega. If ``squeeze`` is True then | ||
single-dimensional axes are removed. | ||
See Also | ||
-------- | ||
@@ -511,13 +524,13 @@ def evalfr(sys, x, squeeze=True): | ||
>>> # This is the transfer function matrix evaluated at s = i. | ||
.. todo:: Add example with MIMO system | ||
""" | ||
return sys.__call__(x, squeeze=squeeze) | ||
def freqresp(sys, omega, squeeze=None): | ||
"""Frequency response of an LTI system at multiple angular frequencies. | ||
In general the system may be multiple input, multiple output (MIMO), where | ||
`m = sys.inputs` number of inputs and `p = sys.outputs` number of | ||
outputs. | ||
@@ -526,22 +539,27 @@ def freqresp(sys, omega, squeeze=True): | ||
---------- | ||
sys: StateSpace or TransferFunction | ||
Linear system | ||
omega : float or1Darray_like | ||
A list of frequencies in radians/sec at which the system should be | ||
evaluated. The list can be either a python list or a numpy array | ||
and will be sorted before evaluation. | ||
squeeze : bool, optional | ||
If squeeze=True, remove single-dimensional entries from the shape of | ||
the output even if the system is not SISO. If squeeze=False, keep all | ||
indices (output, input and, if omega is array_like, frequency) even if | ||
the system is SISO. The default value can be set using | ||
config.defaults['control.squeeze_frequency_response']. | ||
Returns | ||
------- | ||
mag : ndarray | ||
The magnitude (absolute value, not dB or log10) of the system | ||
frequency response. If the system is SISO and squeeze is not True, | ||
the array is 1D, indexed by frequency. If the system is not SISO or | ||
squeeze is False, the array is 3D, indexed by the output, input, and | ||
frequency. If ``squeeze`` is True then single-dimensional axes are | ||
removed. | ||
phase : ndarray | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. In ‘notes’ on line 575ish below, this function is actually a wrapper for :meth:’lti.frequency_response’ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Because of the way the documentation is built, the | ||
The wrapped phase in radians of the system frequency response. | ||
omega : ndarray | ||
The list of sorted frequencies at which the response was | ||
@@ -579,6 +597,7 @@ def freqresp(sys, omega, squeeze=True): | ||
#>>> # input to the 1st output, and the phase (in radians) of the | ||
#>>> # frequency response from the 1st input to the 2nd output, for | ||
#>>> # s = 0.1i, i, 10i. | ||
""" | ||
return sys.frequency_response(omega, squeeze=squeeze) | ||
@@ -593,3 +612,34 @@ def dcgain(sys): | ||
at the origin | ||
""" | ||
return sys.dcgain() | ||
# Process frequency responses in a uniform way | ||
def _process_frequency_response(sys, omega, out, squeeze=None): | ||
# Set value of squeeze argument if not set | ||
if squeeze is None: | ||
squeeze = config.defaults['control.squeeze_frequency_response'] | ||
if not hasattr(omega, '__len__'): | ||
# received a scalar x, squeeze down the array along last dim | ||
out = np.squeeze(out, axis=2) | ||
# | ||
# Get rid of unneeded dimensions | ||
# | ||
# There are three possible values for the squeeze keyword at this point: | ||
# | ||
# squeeze=None: squeeze input/output axes iff SISO | ||
# squeeze=True: squeeze all single dimensional axes (ala numpy) | ||
# squeeze-False: don't squeeze any axes | ||
# | ||
if squeeze is True: | ||
# Squeeze everything that we can if that's what the user wants | ||
return np.squeeze(out) | ||
elif squeeze is None and sys.issiso(): | ||
# SISO system output squeezed unless explicitly specified otherwise | ||
return out[0][0] | ||
elif squeeze is False or squeeze is None: | ||
return out | ||
else: | ||
raise ValueError("unknown squeeze value") |
Uh oh!
There was an error while loading.Please reload this page.