Skip to content

Commit aeb187f

Browse files
committed
set up frd() as full factory function for FRD I/Osystems
1 parent 4734912 commit aeb187f

File tree

6 files changed

+190
-167
lines changed

6 files changed

+190
-167
lines changed

LICENSE

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
Copyright (c) 2009-2016 by California Institute of Technology
2-
Copyright (c) 2016-2023 by python-control developers
2+
Copyright (c) 2012 by Delft University of Technology
3+
Copyright (c) 2016-2024 by python-control developers
34
All rights reserved.
45

56
Redistribution and use in source and binary forms, with or without

control/bdalg.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,8 +279,8 @@ def feedback(sys1, sys2=1, sign=-1):
279279
if isinstance(sys2, (int, float, complex, np.number, np.ndarray,
280280
tf.TransferFunction)):
281281
sys1 = tf._convert_to_transfer_function(sys1)
282-
elif isinstance(sys2, frd.FRD):
283-
sys1 = frd._convert_to_FRD(sys1, sys2.omega)
282+
elif isinstance(sys2, frd.FrequencyResponseData):
283+
sys1 = frd._convert_to_frd(sys1, sys2.omega)
284284
else:
285285
sys1 = ss._convert_to_statespace(sys1)
286286

control/frdata.py

Lines changed: 66 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,27 @@
1-
# Copyright (c) 2010 by California Institute of Technology
2-
# Copyright (c) 2012 by Delft University of Technology
3-
# All rights reserved.
4-
#
5-
# Redistribution and use in source and binary forms, with or without
6-
# modification, are permitted provided that the following conditions
7-
# are met:
8-
#
9-
# 1. Redistributions of source code must retain the above copyright
10-
# notice, this list of conditions and the following disclaimer.
11-
#
12-
# 2. Redistributions in binary form must reproduce the above copyright
13-
# notice, this list of conditions and the following disclaimer in the
14-
# documentation and/or other materials provided with the distribution.
15-
#
16-
# 3. Neither the names of the California Institute of Technology nor
17-
# the Delft University of Technology nor
18-
# the names of its contributors may be used to endorse or promote
19-
# products derived from this software without specific prior
20-
# written permission.
21-
#
22-
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23-
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24-
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25-
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CALTECH
26-
# OR THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27-
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28-
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
29-
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
30-
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31-
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32-
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33-
# SUCH DAMAGE.
1+
# frdata.py - frequency response data representation and functions
342
#
353
# Author: M.M. (Rene) van Paassen (using xferfcn.py as basis)
364
# Date: 02 Oct 12
375

38-
396
"""
407
Frequency response data representation and functions.
418
429
This module contains the FRD class and also functions that operate on
4310
FRD data.
4411
"""
4512

46-
# External function declarations
4713
from copy import copy
4814
from warnings import warn
4915

5016
import numpy as np
51-
from numpy import angle, array, empty, ones, \
52-
real, imag, absolute, eye, linalg, where, sort
53-
from scipy.interpolate import splprep, splev
17+
from numpy import absolute, angle, array, empty, eye, imag, linalg, ones, \
18+
real, sort, where
19+
from scipy.interpolate import splev, splprep
5420

55-
from .lti import LTI, _process_frequency_response
21+
from . import config
5622
from .exception import pandas_check
5723
from .iosys import InputOutputSystem, _process_iosys_keywords, common_timebase
58-
from . import config
24+
from .lti import LTI, _process_frequency_response
5925

6026
__all__ = ['FrequencyResponseData', 'FRD', 'frd']
6127

@@ -100,6 +66,10 @@ class constructor, using the :func:~~control.frd` factory function
10066
dt : float, True, or None
10167
System timebase.
10268
69+
See Also
70+
--------
71+
frd
72+
10373
Notes
10474
-----
10575
The main data members are 'omega' and 'fresp', where 'omega' is a 1D array
@@ -120,7 +90,6 @@ class constructor, using the :func:~~control.frd` factory function
12090
for a more detailed description.
12191
12292
"""
123-
12493
#
12594
# Class attributes
12695
#
@@ -206,11 +175,12 @@ def __init__(self, *args, **kwargs):
206175
"Needs 1 or 2 arguments; received %i." % len(args))
207176

208177
#
209-
# Process key word arguments
178+
# Process keyword arguments
210179
#
211180

212-
# If data was generated by a system, keep track of that
213-
self.sysname = kwargs.pop('sysname', None)
181+
# If data was generated by a system, keep track of that (used when
182+
# plotting data). Otherwise, use the system name, if given.
183+
self.sysname = kwargs.pop('sysname', kwargs.get('name', None))
214184

215185
# Keep track of default properties for plotting
216186
self.plot_phase = kwargs.pop('plot_phase', None)
@@ -280,7 +250,7 @@ def __str__(self):
280250
"""String representation of the transfer function."""
281251

282252
mimo = self.ninputs > 1 or self.noutputs > 1
283-
outstr = ['Frequency response data']
253+
outstr = [f"{InputOutputSystem.__str__(self)}"]
284254

285255
for i in range(self.ninputs):
286256
for j in range(self.noutputs):
@@ -322,7 +292,7 @@ def __add__(self, other):
322292

323293
# Convert the second argument to a frequency response function.
324294
# or re-base the frd to the current omega (if needed)
325-
other = _convert_to_FRD(other, omega=self.omega)
295+
other = _convert_to_frd(other, omega=self.omega)
326296

327297
# Check that the input-output sizes are consistent.
328298
if self.ninputs != other.ninputs:
@@ -359,7 +329,7 @@ def __mul__(self, other):
359329
return FRD(self.fresp * other, self.omega,
360330
smooth=(self.ifunc is not None))
361331
else:
362-
other = _convert_to_FRD(other, omega=self.omega)
332+
other = _convert_to_frd(other, omega=self.omega)
363333

364334
# Check that the input-output sizes are consistent.
365335
if self.ninputs != other.noutputs:
@@ -386,7 +356,7 @@ def __rmul__(self, other):
386356
return FRD(self.fresp * other, self.omega,
387357
smooth=(self.ifunc is not None))
388358
else:
389-
other = _convert_to_FRD(other, omega=self.omega)
359+
other = _convert_to_frd(other, omega=self.omega)
390360

391361
# Check that the input-output sizes are consistent.
392362
if self.noutputs != other.ninputs:
@@ -414,7 +384,7 @@ def __truediv__(self, other):
414384
return FRD(self.fresp * (1/other), self.omega,
415385
smooth=(self.ifunc is not None))
416386
else:
417-
other = _convert_to_FRD(other, omega=self.omega)
387+
other = _convert_to_frd(other, omega=self.omega)
418388

419389
if (self.ninputs > 1 or self.noutputs > 1 or
420390
other.ninputs > 1 or other.noutputs > 1):
@@ -433,7 +403,7 @@ def __rtruediv__(self, other):
433403
return FRD(other / self.fresp, self.omega,
434404
smooth=(self.ifunc is not None))
435405
else:
436-
other = _convert_to_FRD(other, omega=self.omega)
406+
other = _convert_to_frd(other, omega=self.omega)
437407

438408
if (self.ninputs > 1 or self.noutputs > 1 or
439409
other.ninputs > 1 or other.noutputs > 1):
@@ -572,8 +542,8 @@ def __call__(self, s=None, squeeze=None, return_magphase=None):
572542
------
573543
ValueError
574544
If `s` is not purely imaginary, because
575-
:class:`FrequencyDomainData` systems are only defined at imaginary
576-
frequency values.
545+
:class:`FrequencyResponseData` systems are only defined at
546+
imaginary values (corresponding to real frequencies).
577547
578548
"""
579549
if s is None:
@@ -638,7 +608,7 @@ def freqresp(self, omega):
638608
def feedback(self, other=1, sign=-1):
639609
"""Feedback interconnection between two FRD objects."""
640610

641-
other = _convert_to_FRD(other, omega=self.omega)
611+
other = _convert_to_frd(other, omega=self.omega)
642612

643613
if (self.noutputs != other.ninputs or self.ninputs != other.noutputs):
644614
raise ValueError(
@@ -710,7 +680,7 @@ def to_pandas(self):
710680
FRD = FrequencyResponseData
711681

712682

713-
def _convert_to_FRD(sys, omega, inputs=1, outputs=1):
683+
def _convert_to_frd(sys, omega, inputs=1, outputs=1):
714684
"""Convert a system to frequency response data form (if needed).
715685
716686
If sys is already an frd, and its frequency range matches or
@@ -721,14 +691,14 @@ def _convert_to_FRD(sys, omega, inputs=1, outputs=1):
721691
manually, as in:
722692
723693
>>> import numpy as np
724-
>>> from control.frdata import _convert_to_FRD
694+
>>> from control.frdata import _convert_to_frd
725695
726696
>>> omega = np.logspace(-1, 1)
727-
>>> frd = _convert_to_FRD(3., omega) # Assumes inputs = outputs = 1
697+
>>> frd = _convert_to_frd(3., omega) # Assumes inputs = outputs = 1
728698
>>> frd.ninputs, frd.noutputs
729699
(1, 1)
730700
731-
>>> frd = _convert_to_FRD(1., omega, inputs=3, outputs=2)
701+
>>> frd = _convert_to_frd(1., omega, inputs=3, outputs=2)
732702
>>> frd.ninputs, frd.noutputs
733703
(3, 2)
734704
@@ -777,51 +747,67 @@ def _convert_to_FRD(sys, omega, inputs=1, outputs=1):
777747
sys.__class__)
778748

779749

780-
def frd(*args):
781-
"""frd(d, w)
782-
783-
Construct a frequency response data model.
750+
def frd(*args, **kwargs):
751+
"""frd(response, omega[, dt])
784752
785-
frd models store the (measured) frequency response of a system.
753+
Construct a frequency response data (FRD) model.
786754
787-
This function can be called in different ways:
755+
A frequency response data model stores the (measured) frequency response
756+
of a system. This factory function can be called in different ways:
788757
789-
``frd(response, freqs)``
758+
``frd(response, omega)``
790759
Create an frd model with the given response data, in the form of
791-
complex response vector, at matching frequency freqs [in rad/s]
760+
complex response vector, at matching frequencies ``omega`` [in rad/s].
792761
793-
``frd(sys, freqs)``
762+
``frd(sys, omega)``
794763
Convert an LTI system into an frd model with data at frequencies
795-
freqs.
764+
``omega``.
796765
797766
Parameters
798767
----------
799-
response: array_like, or list
800-
complex vector with the system response
801-
freq: array_lik or lis
802-
vector with frequencies
803-
sys: LTI (StateSpace or TransferFunction)
804-
A linear system
768+
response : array_like or LTI system
769+
Complex vector with the system response or an LTI system that can
770+
be used to copmute the frequency response at a list of frequencies.
771+
omega : array_like
772+
Vector of frequencies at which the response is evaluated.
773+
dt : float, True, or None
774+
System timebase.
775+
smooth : bool, optional
776+
If ``True``, create an interpolation function that allows the
777+
frequency response to be computed at any frequency within the range
778+
of frequencies give in ``omega``. If ``False`` (default),
779+
frequency response can only be obtained at the frequencies
780+
specified in ``omega``.
805781
806782
Returns
807783
-------
808-
sys: FRD
809-
New frequency response system
784+
sys : :class:`FrequencyResponseData`
785+
New frequency response data system.
786+
787+
Other Parameters
788+
----------------
789+
inputs, outputs : str, or list of str, optional
790+
List of strings that name the individual signals of the transformed
791+
system. If not given, the inputs and outputs are the same as the
792+
original system.
793+
name : string, optional
794+
System name. If unspecified, a generic name <sys[id]> is generated
795+
with a unique integer id.
810796
811797
See Also
812798
--------
813-
FRD, ss, tf
799+
FrequencyResponseData, frequency_response, ss, tf
814800
815801
Examples
816802
--------
817803
>>> # Create from measurements
818804
>>> response = [1.0, 1.0, 0.5]
819-
>>> freqs = [1, 10, 100]
820-
>>> F = ct.frd(response, freqs)
805+
>>> omega = [1, 10, 100]
806+
>>> F = ct.frd(response, omega)
821807
822808
>>> G = ct.tf([1], [1, 1])
823809
>>> freqs = [1, 10, 100]
824-
>>> F = ct.frd(G, freqs)
810+
>>> F = ct.frd(G, omega)
825811
826812
"""
827-
return FRD(*args)
813+
return FrequencyResponseData(*args, **kwargs)

0 commit comments

Comments
 (0)