Skip to content
This repository was archived by the owner on May 10, 2024. It is now read-only.

Commit afac78f

Browse files
committed
Initial version
1 parent 915d218 commit afac78f

File tree

5 files changed

+249
-0
lines changed

5 files changed

+249
-0
lines changed

src/InteractiveControlPlots.jl

+28
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,33 @@
11
module InteractiveControlPlots
22

3+
using ControlSystems, GtkInteract, Immerse
4+
import Colors, Reactive, Measures, Compose
5+
6+
export bode_immerse, nyquist_immerse, pzmap_immerse, timeplot_immerse
7+
export labelf, null_theme
8+
export example_minimal, example_fo_sys
9+
10+
311
# package code goes here
12+
include("framework.jl")
13+
include("plots.jl")
14+
15+
examples = ["minimal", "fo_sys"]
16+
for ex in examples
17+
funcname = Symbol("example_$(ex)")
18+
@eval begin
19+
function $funcname()
20+
pdir = joinpath(Pkg.dir("InteractiveControlPlots"),"src")
21+
include(joinpath(Pkg.dir("InteractiveControlPlots"),"src/examples/"*$ex*".jl"))
22+
end
23+
end
24+
end
25+
# function example_minimal()
26+
# include(joinpath(pdir,"examples/minimal.jl"))
27+
# end
28+
# function example_fo_sys()
29+
# include(joinpath(pdir,"examples/fo_sys.jl"))
30+
# end
31+
432

533
end # module

src/examples/fo_sys.jl

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# load init commands
2+
using InteractiveControlPlots, ControlSystems, GtkInteract
3+
4+
# create sliders etc
5+
x = linspace(-1,1,2)
6+
ws = logspace(-2,2,1000)
7+
k=slider(1:0.1:3, label="K")
8+
logT = slider(-1:0.01:1, label="T")
9+
f1 = checkbox(false,label="Bode plot ")
10+
f2 = checkbox(false,label="Nyquist plot ")
11+
f3 = checkbox(false,label="PZ map ")
12+
f4 = checkbox(false,label="Step plot ")
13+
14+
15+
# create window
16+
cg1 = cairographic(width=200,height=150)
17+
cg2 = cairographic(width=200,height=150)
18+
cg3 = cairographic(width=200,height=150)
19+
cg4 = cairographic(width=200,height=150)
20+
lT = label("T: ")
21+
22+
vbl(w, len) = vbox(label(w.label),hbox(hskip(len),w))
23+
vbl(w) = vbox(label(w.label),w)
24+
25+
w = window(vbox(hbox(vbl(k),
26+
vbox(lT,logT),
27+
vbox(vbl(f1,25),vbl(f3,15)),
28+
vbox(vbl(f2,35),vbl(f4,22))),
29+
hbox(grow(cg1), grow(cg2)),
30+
hbox(grow(cg3), grow(cg4))),
31+
title="Example")
32+
33+
# loop over parameters
34+
Reactive.preserve(map(k,logT,f1,f2,f3,f4) do k,logT,f1,f2,f3,f4
35+
T = 10^logT
36+
37+
#Create the 2nd order system
38+
sys = tf(k*[1,1],[1, T, 1])
39+
40+
#Create the plots
41+
fig1 = bode_immerse(sys, ws, 1e-2, 1e2) #Ylims = (1e-2, 1e2)
42+
fig2 = nyquist_immerse(sys, ws, -2, 2, -2, 2) #x and ylims
43+
fig3 = pzmap_immerse(sys,-4,4,-4,4) # x and ylims
44+
y,t,x = step(sys, 10)
45+
fig4 = timeplot_immerse(t, y, 0, 4) #ylims
46+
47+
#Update the text
48+
push!(lT, "T: " * string(round(T,2)))
49+
50+
#Push figures
51+
push!(cg1, f1 ? fig1 : nothing)
52+
push!(cg2, f2 ? fig2 : nothing)
53+
push!(cg3, f3 ? fig3 : nothing)
54+
push!(cg4, f4 ? fig4 : nothing)
55+
56+
end)
57+
58+
# output window for display
59+
w

src/examples/minimal.jl

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using ControlSystems, GtkInteract, Immerse
2+
3+
k = slider(1:0.1:10)
4+
5+
cg1 = cairographic(width=200,height=150);
6+
7+
w = window(vbox(k,grow(cg1)))
8+
9+
Reactive.preserve(map(k) do k
10+
fig1 = Immerse.plot(y=(1:10).^k)
11+
push!(cg1, fig1)
12+
end)
13+
w

src/framework.jl

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
null_theme = Theme(
2+
minor_label_font_size=0pt, #Size of ticks
3+
major_label_font_size=0pt, #Size of axis text
4+
line_width=0pt, #Plot line
5+
panel_fill=Colors.RGB(.95,.95,.95),
6+
background_color=Colors.RGB(.95,.95,.95)
7+
)
8+
default_theme = Theme(
9+
minor_label_font_size=16pt, #Size of ticks
10+
major_label_font_size=26pt, #Size of axis text
11+
line_width=5pt, #Plot line
12+
panel_fill=Colors.RGB(.99,.99,.99),
13+
background_color=Colors.RGB(.99,.99,.99)
14+
)
15+
16+
#Almost empty plot
17+
na_plot = Immerse.plot(x=[0.0], y=[0.0], Geom.line, null_theme)
18+
19+
function Base.push!(obj::GtkInteract.CairoGraphic, p::Void)
20+
push!(obj, na_plot)
21+
end
22+
23+
function Base.push!(obj::GtkInteract.CairoGraphic, p::Gadfly.Plot)
24+
if obj.obj != nothing
25+
push!(obj.obj, p)
26+
end
27+
end
28+
29+
function get_log_ticks(x)
30+
min = ceil(log10(minimum(x)))
31+
max = floor(log10(maximum(x)))
32+
major = 10.^collect(min:max)
33+
majorText = ["\$10^{$(round(Int64,i))}\$" for i=min:max]
34+
if length(major) < 7
35+
minor = [j*10^i for i=(min-1):(max+1) for j=2:9]
36+
minor = minor[find(minimum(x) .<= minor .<= maximum(x))]
37+
else
38+
minor = Float64[]
39+
end
40+
if length(major) < 2
41+
minorText = ["\$j*10^\{$i\}\$" for i=(min-1):(max+1) for j=2:9]
42+
else
43+
minorText = fill("", length(minor))
44+
end
45+
major, minor, majorText, minorText
46+
end
47+
48+
function labelf(x)
49+
if abs(round(Int64, x) - x) < 1e-5
50+
@sprintf("10<sup>%1.0f</sup>", x)
51+
else
52+
""
53+
end
54+
end
55+
56+
function major_lines(x, y, xticks, yticks)
57+
layer(
58+
x=x, y=y,
59+
yintercept=yticks, xintercept=xticks, Geom.nil,
60+
Geom.hline, Geom.vline, Theme(line_width=0.2pt, default_color=Colors.RGB(.1,.1,.1))
61+
)
62+
end

src/plots.jl

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
function bode_immerse(sys, w, ymin, ymax)
2+
mag, phase, _ = bode(sys, w)
3+
I = find(y-> ymin .<= y .<= ymax, mag) # Filter out from range
4+
mag = mag[I]
5+
ws = w[I]
6+
xmajor, xminor, xmajorText, xminorText = get_log_ticks(w)
7+
ymajor, yminor, ymajorText, yminorText = get_log_ticks([ymin;mag;ymax])
8+
Immerse.plot(
9+
major_lines(log10(ws), log10(mag), log10(xmajor), log10(ymajor)),
10+
layer(x=log10(ws), y=log10(mag), Geom.line),
11+
Stat.xticks(ticks = log10([xmajor;xminor])),
12+
Stat.yticks(ticks = log10([ymajor;yminor])),
13+
Scale.x_continuous(labels= x -> labelf(x), maxvalue=log10(maximum(w))),
14+
Scale.y_continuous(labels= x -> labelf(x), minvalue=log10(ymin), maxvalue=log10(ymax)),
15+
Guide.xlabel("Frequency (rad/s)"),
16+
Guide.ylabel("Magnitude (abs)", orientation=:vertical),
17+
default_theme)
18+
end
19+
function nyquist_immerse(sys, w, xmin, xmax, ymin, ymax)
20+
re, im, _ = nyquist(sys, w)
21+
I = (ymin .<= im .<= ymax) & (xmin .<= re .<= xmax)
22+
re = re[I]
23+
im = im[I]
24+
Immerse.plot(
25+
major_lines(re, im, [0.0], [0.0]),
26+
layer(x=[-1], y=[0], Geom.point, Theme(default_color=Colors.RGB(.8,.0,.0), default_point_size=5pt)),
27+
layer(x=re, y = im, Geom.line(preserve_order=true)),
28+
Scale.x_continuous(minvalue = xmin, maxvalue=xmax),
29+
Scale.y_continuous(minvalue = ymin, maxvalue=ymax),
30+
Guide.xlabel("Real"),
31+
Guide.ylabel("Imaginary", orientation=:vertical),
32+
default_theme)
33+
end
34+
35+
function pzmap_immerse(sys, xmin, xmax, ymin, ymax)
36+
z, p, k = zpkdata(sys)
37+
z = z[1]; p = p[1];
38+
I(x) = (ymin .<= imag(x) .<= ymax) & (xmin .<= real(x) .<= xmax)
39+
zx = real(z[I(z)])
40+
zy = imag(z[I(z)])
41+
px = real(p[I(p)])
42+
py = imag(p[I(p)])
43+
shapes = xcross(px, py, fill(3mm,length(px)))
44+
theme = copy(default_theme)
45+
theme.default_color = Colors.RGB(0,0,1)
46+
theme.default_point_size = 6pt
47+
Immerse.plot(
48+
layer(x=zx, y=zy, Geom.point),
49+
x = px,
50+
y = py,
51+
Scale.x_continuous(minvalue = xmin, maxvalue=xmax),
52+
Scale.y_continuous(minvalue = ymin, maxvalue=ymax),
53+
Guide.xlabel("Real"),
54+
Guide.ylabel("Imaginary", orientation=:vertical),
55+
theme,
56+
Guide.annotation(Compose.compose!(Compose.context(), Gadfly.fill(Colors.RGB(1,0,0)), shapes)))
57+
end
58+
59+
function timeplot_immerse(t, y, ymin, ymax)
60+
Immerse.plot(
61+
x = t,
62+
y = y,
63+
Scale.y_continuous(minvalue = ymin, maxvalue=ymax),
64+
Guide.xlabel("Time"),
65+
Guide.ylabel("Output", orientation=:vertical),
66+
default_theme,
67+
Geom.line)
68+
end
69+
70+
function xcross(xs::AbstractArray, ys::AbstractArray, rs::AbstractArray)
71+
n = max(length(xs), length(ys), length(rs))
72+
polys = Vector{Vector{Tuple{Measures.Measure, Measures.Measure}}}(n)
73+
s = 1/sqrt(5)
74+
for i in 1:n
75+
x = Compose.x_measure(xs[i])
76+
y = Compose.y_measure(ys[i])
77+
r = rs[i]
78+
u = s*r
79+
polys[i] = Tuple{Measures.Measure, Measures.Measure}[
80+
(x, y - u), (x + u, y - 2u), (x + 2u, y - u),
81+
(x + u, y), (x + 2u, y + u), (x + u, y + 2u),
82+
(x, y + u), (x - u, y + 2u), (x - 2u, y + u),
83+
(x - u, y), (x - 2u, y - u), (x - u, y - 2u)
84+
]
85+
end
86+
return Gadfly.polygon(polys)
87+
end

0 commit comments

Comments
 (0)