Skip to content

Commit 836d54d

Browse files
authored
Implement forward euler for c2d and d2c (#364)
* Implement forward euler for c2d and d2c * add missing B factor and test simuation * caution against fwdeuler with long time step * Splat tuple * c2d on discrete throws method error * Compat 1.3
1 parent 3091433 commit 836d54d

File tree

2 files changed

+37
-18
lines changed

2 files changed

+37
-18
lines changed

src/discrete.jl

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,14 @@ export rstd, rstc, dab, c2d_roots2poly, c2d_poly2poly, zpconv#, lsima, indirect_
66
sysd = c2d(sys::TransferFunction, Ts, method=:zoh)
77
88
Convert the continuous system `sys` into a discrete system with sample time
9-
`Ts`, using the provided method. Currently only `:zoh` and `:foh` are provided.
9+
`Ts`, using the provided method. Currently only `:zoh`, `:foh` and `:fwdeuler` are provided. Note that the forward-Euler method generally requires the sample time to be very small in relation to the time-constants of the system.
1010
1111
Returns the discrete system `sysd`, and for StateSpace systems a matrix `x0map` that transforms the
1212
initial conditions to the discrete domain by
1313
`x0_discrete = x0map*[x0; u0]`"""
14-
function c2d(sys::StateSpace, Ts::Real, method::Symbol=:zoh)
15-
if isdiscrete(sys)
16-
error("sys must be a continuous time system")
17-
end
14+
function c2d(sys::StateSpace{Continuous}, Ts::Real, method::Symbol=:zoh)
1815
A, B, C, D = ssdata(sys)
16+
T = promote_type(eltype.((A,B,C,D))...)
1917
ny, nu = size(sys)
2018
nx = nstates(sys)
2119
if method === :zoh
@@ -25,41 +23,49 @@ function c2d(sys::StateSpace, Ts::Real, method::Symbol=:zoh)
2523
Bd = M[1:nx, nx+1:nx+nu]
2624
Cd = C
2725
Dd = D
28-
x0map = [Matrix{Float64}(I, nx, nx) zeros(nx, nu)] # Cant use I if nx==0
26+
x0map = [Matrix{T}(I, nx, nx) zeros(nx, nu)] # Cant use I if nx==0
2927
elseif method === :foh
3028
M = exp([A*Ts B*Ts zeros(nx, nu);
31-
zeros(nu, nx + nu) Matrix{Float64}(I, nu, nu);
29+
zeros(nu, nx + nu) Matrix{T}(I, nu, nu);
3230
zeros(nu, nx + 2*nu)])
3331
M1 = M[1:nx, nx+1:nx+nu]
3432
M2 = M[1:nx, nx+nu+1:nx+2*nu]
3533
Ad = M[1:nx, 1:nx]
3634
Bd = Ad*M2 + M1 - M2
3735
Cd = C
3836
Dd = D + C*M2
39-
x0map = [Matrix{Float64}(I, nx, nx) (-M2)]
37+
x0map = [Matrix{T}(I, nx, nx) (-M2)]
38+
elseif method === :fwdeuler
39+
Ad, Bd, Cd, Dd = (I+Ts*A), Ts*B, C, D
40+
x0map = I(nx)
4041
elseif method === :tustin || method === :matched
41-
error("NotImplemented: Only `:zoh` and `:foh` implemented so far")
42+
error("NotImplemented: Only `:zoh`, `:foh` and `:fwdeuler` implemented so far")
4243
else
4344
error("Unsupported method: ", method)
4445
end
4546
return StateSpace(Ad, Bd, Cd, Dd, Ts), x0map
4647
end
4748

49+
"""
50+
d2c(sys::AbstractStateSpace{<:Discrete}, method::Symbol = :zoh)
4851
52+
Convert discrete-time system to a continuous time system, assuming that the discrete-time system was discretized using `method`. Available methods are `:zoh, :fwdeuler´.
53+
"""
4954
function d2c(sys::AbstractStateSpace{<:Discrete}, method::Symbol=:zoh)
50-
A, B, C, D = ssdata(sys)
55+
A, B, Cc, Dc = ssdata(sys)
5156
ny, nu = size(sys)
5257
nx = nstates(sys)
53-
if method == :zoh
58+
if method === :zoh
5459
M = log([A B;
5560
zeros(nu, nx) I])./sys.Ts
5661
Ac = M[1:nx, 1:nx]
5762
Bc = M[1:nx, nx+1:nx+nu]
58-
Cc = C
59-
Dc = D
6063
if eltype(A) <: Real
6164
Ac,Bc = real.((Ac, Bc))
6265
end
66+
elseif method === :fwdeuler
67+
Ac = (A-I)./sys.Ts
68+
Bc = B./sys.Ts
6369
else
6470
error("Unsupported method: ", method)
6571
end
@@ -210,12 +216,11 @@ function c2d_poly2poly(p,h)
210216
end
211217

212218

213-
function c2d(G::TransferFunction, h, args...)
214-
@assert iscontinuous(G)
219+
function c2d(G::TransferFunction{<:Continuous}, h, args...; kwargs...)
215220
ny, nu = size(G)
216221
@assert (ny + nu == 2) "c2d(G::TransferFunction, h) not implemented for MIMO systems"
217222
sys = ss(G)
218-
sysd = c2d(sys, h, args...)[1]
223+
sysd = c2d(sys, h, args...; kwargs...)[1]
219224
return convert(TransferFunction, sysd)
220225
end
221226

test/test_discrete.jl

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ Gd = c2d(G, 0.2)
6666

6767

6868
# ERRORS
69-
@test_throws ErrorException c2d(ss([1], [2], [3], [4], 0.01), 0.01) # Already discrete
70-
@test_throws ErrorException c2d(ss([1], [2], [3], [4], -1), 0.01) # Already discrete
69+
@test_throws MethodError c2d(ss([1], [2], [3], [4], 0.01), 0.01) # Already discrete
70+
@test_throws MethodError c2d(ss([1], [2], [3], [4], -1), 0.01) # Already discrete
7171

7272

7373
# d2c
@@ -83,6 +83,20 @@ Gd = c2d(G, 0.2)
8383
@test d2c(sysd) sys
8484
end
8585

86+
# forward euler
87+
@test c2d(C_111, 1, :fwdeuler)[1].A == I + C_111.A
88+
@test d2c(c2d(C_111, 0.01, :fwdeuler)[1], :fwdeuler) C_111
89+
@test d2c(c2d(C_212, 0.01, :fwdeuler)[1], :fwdeuler) C_212
90+
@test d2c(c2d(C_221, 0.01, :fwdeuler)[1], :fwdeuler) C_221
91+
@test d2c(c2d(C_222_d, 0.01, :fwdeuler)[1], :fwdeuler) C_222_d
92+
@test d2c(c2d(G, 0.01, :fwdeuler), :fwdeuler) G
93+
94+
95+
Cd = c2d(C_111, 0.001, :fwdeuler)[1]
96+
t = 0:0.001:2
97+
y,_ = step(C_111, t)
98+
yd,_ = step(Cd, t)
99+
@test norm(yd-y) / norm(y) < 1e-3
86100

87101
# dab
88102
import DSP: conv

0 commit comments

Comments
 (0)