Skip to content

Commit 40d3550

Browse files
committed
Squash olof3:sampletimes2 into master. Implements type stable sample times
1 parent d183cb1 commit 40d3550

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+740
-519
lines changed

.travis.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ language: julia
22
julia:
33
- 1.0
44
- 1.3
5+
- 1.5
56
- nightly
67
matrix:
78
allow_failures:
9+
- julia: 1.3
810
- julia: nightly
911
fast_finish: true
1012
notifications:

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,5 @@ IterTools = "1.0"
2727
LaTeXStrings = "1.0"
2828
OrdinaryDiffEq = "5.2"
2929
Plots = "0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 1.0"
30-
Polynomials = "0.7, 0.8, 1.0"
30+
Polynomials = "1.0"
3131
julia = "1.0"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ControlSystems.jl
22

3-
[![Build Status](https://travis-ci.org/JuliaControl/ControlSystems.jl.svg?branch=master)](https://travis-ci.org/JuliaControl/ControlSystems.jl)
3+
[![Build Status](https://travis-ci.com/JuliaControl/ControlSystems.jl.svg?branch=master)](https://travis-ci.com/JuliaControl/ControlSystems.jl)
44
[![PkgEval](https://juliaci.github.io/NanosoldierReports/pkgeval_badges/C/ControlSystems.svg)](https://juliaci.github.io/NanosoldierReports/pkgeval_badges/report.html)
55
[![Gitter](https://badges.gitter.im/JuliaControl/ControlSystems.jl.svg)](https://gitter.im/JuliaControl/ControlSystems.jl?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
66

docs/src/man/creating_systems.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ The syntax for creating a transfer function is
1010
```julia
1111
tf(num, den, Ts=0)
1212
```
13-
where `num` and `den` are the polinomial coefficients of the numerator and denominator of the polynomial and `Ts` is the sample time.
13+
where `num` and `den` are the polynomial coefficients of the numerator and denominator of the polynomial and `Ts` is the sample time.
1414
### Example:
1515
```julia
1616
tf([1.0],[1,2,1])
@@ -29,7 +29,7 @@ Continuous-time transfer function model
2929
The transfer functions created using this method will be of type `TransferFunction{SisoRational}`.
3030

3131
## zpk - Pole-Zero-Gain Representation
32-
Sometimes it's better to represent the transferfunction by its poles, zeros and gain, this can be done using
32+
Sometimes it's better to represent the transfer function by its poles, zeros and gain, this can be done using
3333
```julia
3434
zpk(zeros, poles, gain, Ts=0)
3535
```
@@ -72,7 +72,7 @@ A state-space system is created using
7272
```julia
7373
ss(A,B,C,D,Ts=0)
7474
```
75-
and they behave similarily to transfer functions. State-space systems with heterogeneous matrix types are also available, which can be used to create systems with static or sized matrices, e.g.,
75+
and they behave similarly to transfer functions. State-space systems with heterogeneous matrix types are also available, which can be used to create systems with static or sized matrices, e.g.,
7676
```jldoctest HSS
7777
using StaticArrays
7878
import ControlSystems.HeteroStateSpace
@@ -83,7 +83,7 @@ import ControlSystems.HeteroStateSpace
8383
function HeteroStateSpace(A,B,C,D,Ts=0,f::F=to_static) where F
8484
HeteroStateSpace(f(A),f(B),f(C),f(D),Ts)
8585
end
86-
@inline HeteroStateSpace(s,f) = HeteroStateSpace(s.A,s.B,s.C,s.D,s.Ts,f)
86+
@inline HeteroStateSpace(s,f) = HeteroStateSpace(s.A,s.B,s.C,s.D,s.timeevol,f)
8787
ControlSystems._string_mat_with_headers(a::SizedArray) = ControlSystems._string_mat_with_headers(Matrix(a)); # Overload for printing purposes
8888
```
8989
Notice the different matrix types used

src/ControlSystems.jl

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ export LTISystem,
66
HeteroStateSpace,
77
TransferFunction,
88
DelayLtiSystem,
9+
Continuous,
10+
Discrete,
911
ss,
1012
tf,
1113
zpk,
@@ -84,7 +86,9 @@ export LTISystem,
8486
numvec,
8587
denvec,
8688
numpoly,
87-
denpoly
89+
denpoly,
90+
iscontinuous,
91+
isdiscrete
8892

8993

9094
# QUESTION: are these used? LaTeXStrings, Requires, IterTools
@@ -103,6 +107,11 @@ import DSP: conv
103107

104108
abstract type AbstractSystem end
105109

110+
include("types/TimeEvolution.jl")
111+
## Added interface:
112+
# timeevol(Lti) -> TimeEvolution (not exported)
113+
114+
106115
include("types/Lti.jl")
107116

108117
include("types/SisoTf.jl")
@@ -116,6 +125,7 @@ include("types/SisoTfTypes/conversion.jl")
116125

117126
include("types/StateSpace.jl")
118127

128+
# TODO Sample time
119129
include("types/PartionedStateSpace.jl")
120130
include("types/DelayLtiSystem.jl")
121131

@@ -154,13 +164,13 @@ include("plotting.jl")
154164
@deprecate num numvec
155165
@deprecate den denvec
156166
@deprecate norminf hinfnorm
167+
@deprecate diagonalize(s::AbstractStateSpace, digits) diagonalize(s::AbstractStateSpace)
157168

158169
function covar(D::Union{AbstractMatrix,UniformScaling}, R)
159170
@warn "This call is deprecated due to ambiguity, use covar(ss(D), R) or covar(ss(D, Ts), R) instead"
160171
D*R*D'
161172
end
162173

163-
164174
# The path has to be evaluated upon initial import
165175
const __CONTROLSYSTEMS_SOURCE_DIR__ = dirname(Base.source_path())
166176

src/analysis.jl

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pole(sys::SisoTf) = error("pole is not implemented for type $(typeof(sys))")
88
# converting to zpk before works better in the cases I have tested.
99
pole(sys::TransferFunction) = pole(zpk(sys))
1010

11-
function pole(sys::TransferFunction{SisoZpk{T,TR}}) where {T, TR}
11+
function pole(sys::TransferFunction{<:TimeEvolution,SisoZpk{T,TR}}) where {T, TR}
1212
# With right TR, this code works for any SisoTf
1313

1414
# Calculate least common denominator of the minors,
@@ -118,7 +118,7 @@ Compute the zeros, poles, and gains of system `sys`.
118118
119119
`k` : Matrix{Float64}, (ny x nu)"""
120120
function zpkdata(sys::LTISystem)
121-
G = convert(TransferFunction{SisoZpk}, sys)
121+
G = convert(TransferFunction{typeof(timeevol(sys)),SisoZpk}, sys)
122122

123123
zs = map(x -> x.z, G.matrix)
124124
ps = map(x -> x.p, G.matrix)
@@ -133,9 +133,8 @@ Compute the natural frequencies, `Wn`, and damping ratios, `zeta`, of the
133133
poles, `ps`, of `sys`"""
134134
function damp(sys::LTISystem)
135135
ps = pole(sys)
136-
if !iscontinuous(sys)
137-
Ts = sys.Ts == -1 ? 1 : sys.Ts
138-
ps = log(ps)/Ts
136+
if isdiscrete(sys)
137+
ps = log(ps)/sys.Ts
139138
end
140139
Wn = abs.(ps)
141140
order = sortperm(Wn; by=z->(abs(z), real(z), imag(z)))
@@ -446,7 +445,7 @@ function delaymargin(G::LTISystem)
446445
ϕₘ *= π/180
447446
ωϕₘ = m[3][i]
448447
dₘ = ϕₘ/ωϕₘ
449-
if G.Ts > 0
448+
if isdiscrete(G)
450449
dₘ /= G.Ts # Give delay margin in number of sample times, as matlab does
451450
end
452451
dₘ

src/connections.jl

Lines changed: 25 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,18 @@ Append systems in block diagonal form
2222
"""
2323
function append(systems::(ST where ST<:AbstractStateSpace)...)
2424
ST = promote_type(typeof.(systems)...)
25-
Ts = systems[1].Ts
26-
if !all(s.Ts == Ts for s in systems)
27-
error("Sampling time mismatch")
28-
end
25+
timeevol = common_timeevol(systems...)
2926
A = blockdiag([s.A for s in systems]...)
3027
B = blockdiag([s.B for s in systems]...)
3128
C = blockdiag([s.C for s in systems]...)
3229
D = blockdiag([s.D for s in systems]...)
33-
return ST(A, B, C, D, Ts)
30+
return ST(A, B, C, D, timeevol)
3431
end
3532

3633
function append(systems::TransferFunction...)
37-
Ts = systems[1].Ts
38-
if !all(s.Ts == Ts for s in systems)
39-
error("Sampling time mismatch")
40-
end
34+
timeevol = common_timeevol(systems...)
4135
mat = blockdiag([s.matrix for s in systems]...)
42-
return TransferFunction(mat, Ts)
36+
return TransferFunction(mat, timeevol)
4337
end
4438

4539
append(systems::LTISystem...) = append(promote(systems...)...)
@@ -64,16 +58,12 @@ function Base.vcat(systems::ST...) where ST <: AbstractStateSpace
6458
if !all(s.nu == nu for s in systems)
6559
error("All systems must have same input dimension")
6660
end
67-
Ts = systems[1].Ts
68-
if !all(s.Ts == Ts for s in systems)
69-
error("Sampling time mismatch")
70-
end
7161
A = blockdiag([s.A for s in systems]...)
7262
B = vcat([s.B for s in systems]...)
7363
C = blockdiag([s.C for s in systems]...)
7464
D = vcat([s.D for s in systems]...)
75-
76-
return ST(A, B, C, D, Ts)
65+
timeevol = common_timeevol(systems...)
66+
return ST(A, B, C, D, timeevol)
7767
end
7868

7969
function Base.vcat(systems::TransferFunction...)
@@ -82,12 +72,10 @@ function Base.vcat(systems::TransferFunction...)
8272
if !all(s.nu == nu for s in systems)
8373
error("All systems must have same input dimension")
8474
end
85-
Ts = systems[1].Ts
86-
if !all(s.Ts == Ts for s in systems)
87-
error("Sampling time mismatch")
88-
end
75+
timeevol = common_timeevol(systems...)
8976
mat = vcat([s.matrix for s in systems]...)
90-
return TransferFunction(mat, Ts)
77+
78+
return TransferFunction(mat, timeevol)
9179
end
9280

9381
Base.vcat(systems::LTISystem...) = vcat(promote(systems...)...)
@@ -98,16 +86,13 @@ function Base.hcat(systems::ST...) where ST <: AbstractStateSpace
9886
if !all(s.ny == ny for s in systems)
9987
error("All systems must have same output dimension")
10088
end
101-
Ts = systems[1].Ts
102-
if !all(s.Ts == Ts for s in systems)
103-
error("Sampling time mismatch")
104-
end
89+
timeevol = common_timeevol(systems...)
10590
A = blockdiag([s.A for s in systems]...)
10691
B = blockdiag([s.B for s in systems]...)
10792
C = hcat([s.C for s in systems]...)
10893
D = hcat([s.D for s in systems]...)
10994

110-
return ST(A, B, C, D, Ts)
95+
return ST(A, B, C, D, timeevol)
11196
end
11297

11398
function Base.hcat(systems::TransferFunction...)
@@ -116,12 +101,10 @@ function Base.hcat(systems::TransferFunction...)
116101
if !all(s.ny == ny for s in systems)
117102
error("All systems must have same output dimension")
118103
end
119-
Ts = systems[1].Ts
120-
if !all(s.Ts == Ts for s in systems)
121-
error("Sampling time mismatch")
122-
end
104+
timeevol = common_timeevol(systems...)
123105
mat = hcat([s.matrix for s in systems]...)
124-
return TransferFunction(mat, Ts)
106+
107+
return TransferFunction(mat, timeevol)
125108
end
126109

127110
Base.hcat(systems::LTISystem...) = hcat(promote(systems...)...)
@@ -139,52 +122,15 @@ function Base.typed_hcat(::Type{T}, X...) where {T<:LTISystem}
139122
hcat(convert.(T, X)...)
140123
end
141124
# Ambiguity
142-
Base.typed_hcat(::Type{T}, X::Number...) where {T<:LTISystem, N} = hcat(convert.(T, X)...)
125+
Base.typed_hcat(::Type{S}, X::Number...) where {S<:LTISystem} = hcat(convert.(S, X)...)
126+
Base.typed_hcat(::Type{S}, X::Union{AbstractArray{<:Number,1}, AbstractArray{<:Number,2}}...) where {S<:LTISystem} = hcat(convert.(S, X)...)
143127

144128
# Catch special cases where inv(sys) might not be possible after promotion, like improper tf
145129
function /(sys1::Union{StateSpace,AbstractStateSpace}, sys2::LTISystem)
146130
sys1new, sys2new = promote(sys1, 1/sys2)
147131
return sys1new*sys2new
148132
end
149133

150-
# function hvcat(rows::Tuple{Vararg{Int}}, systems::Union{Number,AbstractVecOrMat{<:Number},LTISystem}...)
151-
# T = Base.promote_typeof(systems...)
152-
# nbr = length(rows) # number of block rows
153-
# rs = Array{T,1}(nbr)
154-
# a = 1
155-
# for i = 1:nbr
156-
# rs[i] = hcat(convert.(T,systems[a:a-1+rows[i]])...)
157-
# a += rows[i]
158-
# end
159-
# vcat(rs...)
160-
# end
161-
162-
# function _get_common_sampling_time(sys_vec::Union{AbstractVector{LTISystem},AbstractVecOrMat{<:Number},Number})
163-
# Ts = -1.0 # Initalize corresponding to undefined sampling time
164-
#
165-
# for sys in sys_vec
166-
# if !all(s.Ts == Ts for s in systems])
167-
# error("Sampling time mismatch")
168-
# end
169-
# end
170-
#
171-
# end
172-
173-
174-
# function Base.hcat{T<:Number}(systems::Union{T,AbstractVecOrMat{T},TransferFunction}...)
175-
# S = promote_type(map(e->typeof(e),systems)...) # TODO: Should be simplified
176-
#
177-
# idx_first_tf = findfirst(e -> isa(e, TransferFunction), systems)
178-
# Ts = sys_tuple[idx_first_tf].Ts
179-
#
180-
# if S <: TransferFunction
181-
# hcat(map(e->convert(TransferFunction,e),systems)...)
182-
# else
183-
# cat(2,systems...)
184-
# end
185-
# end
186-
187-
188134
blockdiag(mats::AbstractMatrix...) = blockdiag(promote(mats...)...)
189135

190136
function blockdiag(mats::AbstractMatrix{T}...) where T
@@ -214,16 +160,16 @@ feedback(L::TransferFunction) = L/(1+L)
214160
feedback(P1::TransferFunction, P2::TransferFunction) = P1/(1+P1*P2)
215161

216162
#Efficient implementations
217-
function feedback(L::TransferFunction{T}) where T<:SisoRational
163+
function feedback(L::TransferFunction{<:TimeEvolution,T}) where T<:SisoRational
218164
if size(L) != (1,1)
219165
error("MIMO TransferFunction feedback isn't implemented, use L/(1+L)")
220166
end
221167
P = numpoly(L)
222168
Q = denpoly(L)
223-
tf(P, P+Q, L.Ts)
169+
tf(P, P+Q, L.timeevol)
224170
end
225171

226-
function feedback(L::TransferFunction{T}) where {T<:SisoZpk}
172+
function feedback(L::TransferFunction{TE, T}) where {TE<:TimeEvolution, T<:SisoZpk}
227173
if size(L) != (1,1)
228174
error("MIMO TransferFunction feedback isn't implemented, use L/(1+L)")
229175
end
@@ -233,7 +179,7 @@ function feedback(L::TransferFunction{T}) where {T<:SisoZpk}
233179
kden = denpol[end] # Get coeff of s^n
234180
# Create siso system
235181
sisozpk = T(L.matrix[1].z, roots(denpol), k/kden)
236-
return TransferFunction{T}(fill(sisozpk,1,1), L.Ts)
182+
return TransferFunction{TE,T}(fill(sisozpk,1,1), L.timeevol)
237183
end
238184

239185
"""
@@ -255,17 +201,13 @@ function feedback(sys::Union{StateSpace, DelayLtiSystem})
255201
end
256202

257203
function feedback(sys1::StateSpace,sys2::StateSpace)
258-
if sys1.Ts != sys2.Ts # FIXME: replace with common_sample_time
259-
error("Sampling time mismatch")
260-
end
261-
204+
timeevol = common_timeevol(sys1,sys2)
262205
!(iszero(sys1.D) || iszero(sys2.D)) && error("There cannot be a direct term (D) in both sys1 and sys2")
263206
A = [sys1.A+sys1.B*(-sys2.D)*sys1.C sys1.B*(-sys2.C);
264207
sys2.B*sys1.C sys2.A+sys2.B*sys1.D*(-sys2.C)]
265208
B = [sys1.B; sys2.B*sys1.D]
266209
C = [sys1.C sys1.D*(-sys2.C)]
267-
268-
ss(A, B, C, sys1.D, sys1.Ts)
210+
ss(A, B, C, sys1.D, timeevol)
269211
end
270212

271213

@@ -288,9 +230,7 @@ See Zhou etc. for similar (somewhat less symmetric) formulas.
288230
U1=:, Y1=:, U2=:, Y2=:, W1=:, Z1=:, W2=Int[], Z2=Int[],
289231
Wperm=:, Zperm=:, pos_feedback::Bool=false)
290232

291-
if sys1.Ts != sys2.Ts # FIXME: replace with common_sample_time
292-
error("Sampling time mismatch")
293-
end
233+
timeevol = common_timeevol(sys1,sys2)
294234

295235
if !(isa(Y1, Colon) || allunique(Y1)); @warn "Connecting single output to multiple inputs Y1=$Y1"; end
296236
if !(isa(Y2, Colon) || allunique(Y2)); @warn "Connecting single output to multiple inputs Y2=$Y2"; end
@@ -359,7 +299,7 @@ See Zhou etc. for similar (somewhat less symmetric) formulas.
359299
s2_D12*R2*s1_D21 s2_D11 + α*s2_D12*R2*s1_D22*s2_D21]
360300
end
361301

362-
return StateSpace(A, B[:, Wperm], C[Zperm,:], D[Zperm, Wperm], sys1.Ts)
302+
return StateSpace(A, B[:, Wperm], C[Zperm,:], D[Zperm, Wperm], timeevol)
363303
end
364304

365305

@@ -369,7 +309,7 @@ end
369309
"""
370310
function feedback2dof(P::TransferFunction,R,S,T)
371311
!issiso(P) && error("Feedback not implemented for MIMO systems")
372-
tf(conv(poly2vec(numpoly(P)[1]),T),zpconv(poly2vec(denpoly(P)[1]),R,poly2vec(numpoly(P)[1]),S))
312+
tf(conv(poly2vec(numpoly(P)[1]),T),zpconv(poly2vec(denpoly(P)[1]),R,poly2vec(numpoly(P)[1]),S), P.timeevol)
373313
end
374314

375315
feedback2dof(B,A,R,S,T) = tf(conv(B,T),zpconv(A,R,B,S))

0 commit comments

Comments
 (0)