5
5
from filter_design import tf2zpk, zpk2tf, normalize
6
from Numeric import product, zeros, concatenate, \
7
array, dot, transpose, arange, ones, Float
7
from numpy import product, zeros, array, dot, transpose, arange, ones, \
9
9
import scipy.interpolate as interpolate
10
10
import scipy.integrate as integrate
11
11
import scipy.linalg as linalg
12
from scipy_base import r_, c_, eye, real, atleast_1d, atleast_2d, poly, \
12
from numpy import r_, eye, real, atleast_1d, atleast_2d, poly, \
13
13
squeeze, diag, asarray
14
from Matrix import Matrix as Mat
16
15
def tf2ss(num, den):
17
16
"""Transfer function to state-space representation.
25
24
A, B, C, D -- state space representation of the system.
27
26
# Controller canonical state-space representation.
28
# if M+1 = len(num) and K+1 = len(den) then we must have M <= K
27
# if M+1 = len(num) and K+1 = len(den) then we must have M <= K
29
28
# states are found by asserting that X(s) = U(s) / D(s)
30
# then Y(s) = N(s) * X(s)
29
# then Y(s) = N(s) * X(s)
32
31
# A, B, C, and D follow quite naturally.
34
33
num, den = normalize(num, den) # Strips zeros, checks arrays
35
34
nn = len(num.shape)
37
num = asarray([num],num.typecode())
36
num = asarray([num], num.dtype)
41
40
raise ValueError, "Improper transfer function."
42
41
if (M == 0 or K == 0): # Null system
43
return array([],Float), array([], Float), array([], Float), \
42
return array([],float), array([], float), array([], float), \
46
45
# pad numerator to have same number of columns has denominator
47
num = c_[zeros((num.shape[0],K-M),num.typecode()), num]
46
num = r_['-1',zeros((num.shape[0],K-M), num.dtype), num]
49
48
if num.shape[-1] > 0:
55
return array([], Float), array([], Float), array([], Float), D
54
return array([], float), array([], float), array([], float), D
57
56
frow = -array([den[1:]])
58
57
A = r_[frow, eye(K-2, K-1)]
104
103
raise ValueError, "C and D must have the same number of rows."
106
raise ValueErrro, "B and D must have the same number of columns."
105
raise ValueError, "B and D must have the same number of columns."
108
107
return A, B, C, D
110
109
def ss2tf(A, B, C, D, input=0):
111
110
"""State-space to transfer function.
123
122
# transfer function is C (sI - A)**(-1) B + D
124
123
A, B, C, D = map(asarray, (A, B, C, D))
125
# Check consistency and
124
# Check consistency and
126
125
# make them all rank-2 arrays
127
126
A, B, C, D = abcd_normalize(A, B, C, D)
142
if (product(B.shape) == 0) and (product(C.shape) == 0):
143
num = Numeric.ravel(D)
144
if (product(D.shape) == 0) and (product(A.shape) == 0):
141
if (product(B.shape,axis=0) == 0) and (product(C.shape,axis=0) == 0):
143
if (product(D.shape,axis=0) == 0) and (product(A.shape,axis=0) == 0):
149
num_states = A.shape[0]
148
num_states = A.shape[0]
150
149
type_test = A[:,0] + B[:,0] + C[0,:] + D
151
num = Numeric.zeros((nout, num_states+1),type_test.typecode())
150
num = numpy.zeros((nout, num_states+1), type_test.dtype)
152
151
for k in range(nout):
153
152
Ck = atleast_2d(C[k,:])
154
153
num[k] = poly(A - dot(B,Ck)) + (D[k]-1)*den
181
180
z, p, k -- zeros and poles in sequences and gain constant.
183
182
return tf2zpk(*ss2tf(A,B,C,D,input=input))
186
185
"""Linear Time Invariant class which simplifies representation.
188
187
def __init__(self,*args,**kwords):
223
222
self.__dict__['zeros'], self.__dict__['poles'], \
224
223
self.__dict__['gain'] = ss2zpk(*args)
225
224
self.__dict__['num'], self.__dict__['den'] = ss2tf(*args)
226
self.inputs = B.shape[-1]
227
self.outputs = C.shape[0]
225
self.inputs = self.B.shape[-1]
226
self.outputs = self.C.shape[0]
229
228
raise ValueError, "Needs 2, 3, or 4 arguments."
284
283
U -- an input array describing the input at each time T
285
284
(linear interpolation is assumed between given times).
286
If there are multiple inputs, then each column of the
285
If there are multiple inputs, then each column of the
287
286
rank-2 array represents an input.
288
287
T -- the time steps at which the input is defined and at which
289
288
the output is desired.
306
305
# being the number of inputs
308
307
# rather than use lsim, use direct integration and matrix-exponential.
309
if isinstance(system, lti):
308
if isinstance(system, lti):
312
311
sys = lti(*system)
313
312
U = atleast_1d(U)
314
313
T = atleast_1d(T)
315
314
if len(U.shape) == 1:
316
U.shape = (U.shape[0],1)
315
U = U.reshape((U.shape[0],1))
318
317
if len(T.shape) != 1:
319
318
raise ValueError, "T must be a rank-1 array."
320
319
if sU[0] != len(T):
323
322
raise ValueError, "System does not define that many inputs."
326
X0 = zeros(sys.B.shape[0],sys.A.typecode())
325
X0 = zeros(sys.B.shape[0],sys.A.dtype)
328
327
# for each output point directly integrate assume zero-order hold
329
328
# or linear interpolation.
331
ufunc = interpolate.linear_1d(T, U, axis=0, bounds_error=0, fill_value=0)
330
ufunc = interpolate.interp1d(T, U, kind='linear', axis=0, bounds_error=False)
333
332
def fprime(x, t, sys, ufunc):
334
return dot(sys.A,x) + squeeze(dot(sys.B,ufunc([t])))
333
return dot(sys.A,x) + squeeze(dot(sys.B,nan_to_num(ufunc([t]))))
336
335
xout = integrate.odeint(fprime, X0, T, args=(sys, ufunc))
337
336
yout = dot(sys.C,transpose(xout)) + dot(sys.D,transpose(U))
352
351
U -- an input array describing the input at each time T
353
352
(interpolation is assumed between given times).
354
If there are multiple inputs, then each column of the
353
If there are multiple inputs, then each column of the
355
354
rank-2 array represents an input.
356
355
T -- the time steps at which the input is defined and at which
357
356
the output is desired.
390
389
raise ValueError, "System does not define that many inputs."
393
X0 = zeros(sys.B.shape[0],sys.A.typecode())
392
X0 = zeros(sys.B.shape[0], sys.A.dtype)
395
xout = zeros((len(T),sys.B.shape[0]),sys.A.typecode())
394
xout = zeros((len(T),sys.B.shape[0]), sys.A.dtype)
398
397
AT, BT = transpose(sys.A), transpose(sys.B)
400
399
lam, v = linalg.eig(A)
401
400
vt = transpose(v)
402
401
vti = linalg.inv(vt)
403
GT = dot(dot(vti,diag(Numeric.exp(dt*lam))),vt).astype(xout.typecode())
402
GT = dot(dot(vti,diag(numpy.exp(dt*lam))),vt).astype(xout.dtype)
404
403
ATm1 = linalg.inv(AT)
405
404
ATm2 = dot(ATm1,ATm1)
406
I = eye(A.shape[0],typecode=A.typecode())
405
I = eye(A.shape[0],dtype=A.dtype)
408
407
F1T = dot(dot(BT,GTmI),ATm1)
413
412
dt1 = T[k] - T[k-1]
416
GT = dot(dot(vti,diag(Numeric.exp(dt*lam))),vt).astype(xout.typecode())
415
GT = dot(dot(vti,diag(numpy.exp(dt*lam))),vt).astype(xout.dtype)
418
417
F1T = dot(dot(BT,GTmI),ATm1)
458
457
vals = linalg.eigvals(sys.A)
459
458
tc = 1.0/min(abs(real(vals)))
460
459
T = arange(0,7*tc,7*tc / float(N))
461
h = zeros(T.shape, sys.A.typecode())
460
h = zeros(T.shape, sys.A.dtype)
462
461
s,v = linalg.eig(sys.A)
463
462
vi = linalg.inv(v)
465
464
for k in range(len(h)):
466
es = diag(Numeric.exp(s*T[k]))
467
eA = (dot(dot(v,es),vi)).astype(h.typecode())
465
es = diag(numpy.exp(s*T[k]))
466
eA = (dot(dot(v,es),vi)).astype(h.dtype)
468
467
h[k] = squeeze(dot(dot(C,eA),B))
495
494
vals = linalg.eigvals(sys.A)
496
495
tc = 1.0/min(abs(real(vals)))
497
496
T = arange(0,7*tc,7*tc / float(N))
498
U = ones(T.shape, sys.A.typecode())
497
U = ones(T.shape, sys.A.dtype)
499
498
vals = lsim(sys, U, T, X0=X0)
500
499
return vals[0], vals[1]