Skip to content

Commit cddc6fd

Browse files
authored
add isunstable (#1039)
* add `isunstable` * bump version * update docstring
1 parent bf96ce1 commit cddc6fd

3 files changed

Lines changed: 38 additions & 4 deletions

File tree

lib/ControlSystemsBase/Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name = "ControlSystemsBase"
22
uuid = "aaaaaaaa-a6ca-5380-bf3e-84a91bcd477e"
33
authors = ["Dept. Automatic Control, Lund University"]
44
repo = "https://github.com/JuliaControl/ControlSystems.jl.git"
5-
version = "1.20.1"
5+
version = "1.20.2"
66

77
[deps]
88
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
@@ -39,7 +39,7 @@ Hungarian = "0.7.0"
3939
ImplicitDifferentiation = "0.7.2"
4040
LinearAlgebra = "<0.0.1, 1"
4141
MacroTools = "0.5"
42-
Makie = "0.24"
42+
Makie = "0.22, 0.23, 0.24"
4343
MatrixEquations = "1, 2.1"
4444
MatrixPencils = "1.8.3"
4545
Polynomials = "3.0, 4.0"

lib/ControlSystemsBase/src/types/Lti.jl

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,34 @@ common_timeevol(systems::LTISystem...) = common_timeevol(timeevol(sys) for sys i
107107
"""
108108
isstable(sys)
109109
110-
Returns `true` if `sys` is stable, else returns `false`."""
110+
Returns `true` if `sys` is stable, else returns `false`.
111+
112+
Marginally stable systems are considered unstable by this function, see [`isunstable`](@ref) for a function that returns true only for exponentially unstable systems, that is, `!isunstable(sys)` implies that `sys` is either stable or marginally stable.
113+
"""
111114
isstable(sys::LTISystem{Continuous}) = all(real.(poles(sys)) .< 0)
112115
isstable(sys::LTISystem{<:Discrete}) = all(abs.(poles(sys)) .< 1)
113116

117+
118+
"""
119+
isunstable(sys)
120+
121+
Returns `true` if `sys` is exponentially unstable, else returns `false`.
122+
Marginally stable systems (systems with a simple poles on the imaginary axis) are considered stable by this function, see [`isstable`](@ref) for a function that returns true only for exponentially stable systems.
123+
"""
124+
function isunstable(sys::LTISystem)
125+
inte, p, z, tolp, tolz = integrator_excess_with_tol(sys)
126+
if inte > 1
127+
return true
128+
end
129+
# Go through all poles on the imaginary axis and check if they are duplicated
130+
for pi in p
131+
abs(real(pi)) > sqrt(sqrt(eps(abs(pi)))) && continue # The pole is too far away to be on the imaginary axis
132+
# check if there are multiple poles with this imaginary part
133+
count_eigval_multiplicity(p, complex(0.0, imag(pi)))[1] > 1 && return true
134+
end
135+
return iscontinuous(sys) ? any(real.(p) .> tolp) : any(abs.(p) .> tolp)
136+
end
137+
114138
# Fallback since LTISystem not AbstractArray
115139
Base.size(sys::LTISystem, i::Integer) = size(sys)[i]
116140

lib/ControlSystemsBase/test/test_analysis.jl

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,4 +428,14 @@ zss = tzeros(G)
428428
ptf = poles(Gtf)
429429
ztf = tzeros(Gtf)
430430
pzpk = poles(zpk(G))
431-
zzpk = tzeros(zpk(G))
431+
zzpk = tzeros(zpk(G))
432+
433+
434+
435+
## Test isunstable
436+
using ControlSystemsBase: isunstable
437+
@test !isunstable(tf(1, [1,1]))
438+
@test isunstable(tf(1, [1,-1]))
439+
@test isunstable(tf(1, [1,0,0])) # Double integrator
440+
@test isunstable(zpk([1], [im, im, -im, -im], 1)) # Repeated pole on imaginary axis not in origin
441+
@test !isunstable(zpk([1], [im+1e-8im, im, -im-1e-8im, -im], 1)) # Almost repeated pole on imaginary axis not in origin

0 commit comments

Comments
 (0)