Skip to content

Commit 10e6fe1

Browse files
committed
partial success with frd. Still need to investigate whether interpolation
should be fixed
1 parent 27b9e9c commit 10e6fe1

File tree

3 files changed

+95
-23
lines changed

3 files changed

+95
-23
lines changed

src/frdata.py

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,9 @@
7474

7575
# External function declarations
7676
from numpy import angle, any, array, empty, finfo, insert, ndarray, ones, \
77-
polyadd, polymul, polyval, roots, sort, sqrt, zeros, squeeze, inner
78-
from scipy.interpolate import interp1d
77+
polyadd, polymul, polyval, roots, sort, sqrt, zeros, squeeze, inner, \
78+
real, imag, matrix, absolute
79+
from scipy.interpolate import splprep, splev
7980
from copy import deepcopy
8081
from lti import Lti
8182
import statesp
@@ -84,7 +85,7 @@ class FRD(Lti):
8485

8586
"""The FRD class represents (measured?) frequency response
8687
TF instances and functions.
87-
4
88+
8889
The FRD class is derived from the Lti parent class. It is used
8990
throughout the python-control library to represent systems in frequency
9091
response data form.
@@ -127,7 +128,6 @@ def __init__(self, *args):
127128
# not an FRD, but still a system, second argument should be
128129
# the frequency range
129130
otherlti = args[0]
130-
print args[1]
131131
self.omega = array(args[1], dtype=float)
132132
self.omega.sort()
133133
numfreq = len(self.omega)
@@ -166,10 +166,13 @@ def __init__(self, *args):
166166

167167
# create interpolation functions
168168
self.ifunc = empty((self.fresp.shape[1], self.fresp.shape[0]),
169-
dtype=interp1d)
169+
dtype=tuple)
170170
for i in range(self.fresp.shape[1]):
171171
for j in range(self.fresp.shape[0]):
172-
self.ifunc[i,j] = interp1d(self.omega, self.fresp[i, j, :])
172+
self.ifunc[i,j],u = splprep(
173+
u=self.omega, x=[real(self.fresp[i, j, :]),
174+
imag(self.fresp[i, j, :])],
175+
w=1.0/(absolute(self.fresp[i, j, :])+0.001), s=0.01)
173176

174177
Lti.__init__(self, self.fresp.shape[1], self.fresp.shape[0])
175178

@@ -179,14 +182,15 @@ def __str__(self):
179182
mimo = self.inputs > 1 or self.outputs > 1
180183
outstr = [ 'frequency response data ' ]
181184

185+
mt, pt, wt = self.freqresp(self.omega)
182186
for i in range(self.inputs):
183187
for j in range(self.outputs):
184188
if mimo:
185189
outstr.append("Input %i to output %i:" % (i + 1, j + 1))
186190
outstr.append('Freq [rad/s] Magnitude Phase')
187191
outstr.extend(
188192
[ '%f %f %f' % (w, m, p*180.0)
189-
for m, p, w in self.freqresp(self.omega) ])
193+
for m, p, w in zip(mt[i][j], pt[i][j], wt) ])
190194

191195
return '\n'.join(outstr)
192196

@@ -335,7 +339,8 @@ def evalfr(self, omega):
335339

336340
for i in range(self.outputs):
337341
for j in range(self.inputs):
338-
out[i,j] = self.ifunc[i,j](omega)
342+
out[i,j] = inner(array([1, 1j]),
343+
splev(omega, self.ifunc[i,j], der=0))
339344

340345
return out
341346

@@ -360,7 +365,7 @@ def freqresp(self, omega):
360365

361366
for i in range(self.outputs):
362367
for j in range(self.inputs):
363-
fresp = self.ifunc[i,j](omega)
368+
fresp = matrix([[1,1j]])*splev(omega, self.ifunc[i,j])
364369
mag[i, j, :] = abs(fresp)
365370
phase[i, j, :] = angle(fresp)
366371

@@ -375,18 +380,18 @@ def feedback(self, other, sign=-1):
375380
self.inputs != other.outputs):
376381
raise ValueError(
377382
"FRD.feedback, inputs/outputs mismatch")
378-
fresp = empty(self.outputs, self.inputs, len(other.omega))
383+
fresp = empty((self.outputs, self.inputs, len(other.omega)),
384+
dtype=complex)
379385
# TODO: vectorize this
380386
# TODO: handle omega re-mapping
381387
for k, w in enumerate(other.omega):
382-
for i in range(0, self.inputs):
383-
for j in range(0, self.outputs):
388+
for i in range(self.inputs):
389+
for j in range(self.outputs):
384390
fresp[i, j, k] = \
385391
self.fresp[i, j, k] / \
386-
(1.0 + inner(self.fresp[i, :, k], other.fresp[:, i, k]))
392+
(1.0-sign*inner(self.fresp[:, j, k], other.fresp[i, :, k]))
387393

388394
return FRD(fresp, other.omega)
389-
390395

391396
def _convertToFRD(sys, omega, inputs=1, outputs=1):
392397
"""Convert a system to frequency response data form (if needed).
@@ -442,9 +447,20 @@ def _convertToFRD(sys, omega, inputs=1, outputs=1):
442447
return FRD(fresp, omega)
443448

444449
elif isinstance(sys, (int, long, float, complex)):
445-
446450
fresp = ones((outputs, inputs, len(omega)), dtype=float)*sys
447-
448451
return FRD(fresp, omega)
449-
else:
450-
raise TypeError("Can't convert given type to FRD system.")
452+
453+
# try converting constant matrices
454+
try:
455+
sys = array(sys)
456+
outputs,inputs = sys.shape
457+
fresp = empty((outputs, inputs, len(omega)), dtype=float)
458+
for i in range(outputs):
459+
for j in range(inputs):
460+
fresp[i,j,:] = sys[i,j]
461+
return FRD(fresp, omega)
462+
except:
463+
pass
464+
465+
raise TypeError('''Can't convert given type "%s" to FRD system.''' %
466+
sys.__class__)

src/freqplot.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -353,9 +353,12 @@ def default_frequency_range(syslist):
353353
if (not getattr(syslist, '__iter__', False)):
354354
syslist = (syslist,)
355355
for sys in syslist:
356-
# Add new features to the list
357-
features = np.concatenate((features, np.abs(sys.pole())))
358-
features = np.concatenate((features, np.abs(sys.zero())))
356+
try:
357+
# Add new features to the list
358+
features = np.concatenate((features, np.abs(sys.pole())))
359+
features = np.concatenate((features, np.abs(sys.zero())))
360+
except:
361+
pass
359362

360363
# Get rid of poles and zeros at the origin
361364
features = features[features != 0];

tests/frd_test.py

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@
77
import unittest
88
import numpy as np
99
from control.statesp import StateSpace
10-
from control.xferfcn import TransferFunction, _convertToTransferFunction
11-
from control.frdata import FRD
10+
from control.xferfcn import TransferFunction
11+
from control.frdata import FRD, _convertToFRD
12+
from control.matlab import bode
13+
import control.freqplot
14+
import matplotlib.pyplot as plt
1215

1316
class TestFRD(unittest.TestCase):
1417
"""These are tests for functionality and correct reporting of the
@@ -54,7 +57,57 @@ def testOperators(self):
5457
(f1 - f2).freqresp([0.1, 1.0, 10])[1],
5558
(h1 - h2).freqresp([0.1, 1.0, 10])[1])
5659

60+
def testOperatorsTf(self):
61+
# get two SISO transfer functions
62+
h1 = TransferFunction([1], [1, 2, 2])
63+
h2 = TransferFunction([1], [0.1, 1])
64+
omega = np.logspace(-1, 2, 10)
65+
f1 = FRD(h1, omega)
66+
f2 = FRD(h2, omega)
67+
68+
np.testing.assert_array_almost_equal(
69+
(f1 + h2).freqresp([0.1, 1.0, 10])[0],
70+
(h1 + h2).freqresp([0.1, 1.0, 10])[0])
71+
np.testing.assert_array_almost_equal(
72+
(f1 + h2).freqresp([0.1, 1.0, 10])[1],
73+
(h1 + h2).freqresp([0.1, 1.0, 10])[1])
74+
np.testing.assert_array_almost_equal(
75+
(f1 - h2).freqresp([0.1, 1.0, 10])[0],
76+
(h1 - h2).freqresp([0.1, 1.0, 10])[0])
77+
np.testing.assert_array_almost_equal(
78+
(f1 - h2).freqresp([0.1, 1.0, 10])[1],
79+
(h1 - h2).freqresp([0.1, 1.0, 10])[1])
80+
# the reverse does not work
81+
82+
def testFeedback(self):
83+
h1 = TransferFunction([1], [1, 2, 2])
84+
omega = np.logspace(-1, 2, 10)
85+
f1 = FRD(h1, omega)
86+
np.testing.assert_array_almost_equal(
87+
f1.feedback(1).freqresp([0.1, 1.0, 10])[0],
88+
h1.feedback(1).freqresp([0.1, 1.0, 10])[0])
89+
90+
def testFeedback2(self):
91+
h2 = StateSpace([[-1.0, 0], [0, -2.0]], [[0.4], [0.1]],
92+
[[1.0, 0], [0, 1]], [[0.0], [0.0]])
93+
#h2.feedback([[0.3, 0.2],[0.1, 0.1]])
94+
95+
def testAuto(self):
96+
omega = np.logspace(-1, 2, 10)
97+
f1 = _convertToFRD(1, omega)
98+
f2 = _convertToFRD(np.matrix([[1, 0], [0.1, -1]]), omega)
99+
f2 = _convertToFRD([[1, 0], [0.1, -1]], omega)
57100

101+
def testBode(self):
102+
h1 = TransferFunction([1], [1, 2, 2])
103+
omega = np.logspace(-1, 2, 10)
104+
f1 = FRD(h1, omega)
105+
control.freqplot.nyquist(f1, np.logspace(-1, 2, 100))
106+
plt.figure(2)
107+
control.freqplot.nyquist(f1, f1.omega)
108+
plt.show()
58109

59110
if __name__ == "__main__":
60111
unittest.main()
112+
sys.exit(0)
113+

0 commit comments

Comments
 (0)