I have a project where I’m receiving time domain data in packets, that I’d like to plot live. After looking at some options, I decided to go with plain GR.jl. My simulation of MVP code is at the end of my post.
To run the code, include that file, and then run the function
gr_live_window()
You can type q
and <ENTER>
at the terminal to stop the live plotting.
I like GR because I want other people to run this code and GR seems like one of the easiest plotting libraries to install. There are some purposeful comments in there if others want to experiment with this code. Also, I’m making a distinction between an “animated plot” and a “live plot”. I need a “live plot”, and the ability to save that “live plot” as a movie or animation is a nice-to-have.
What I’m really wondering is if there’s a way to do this more elegantly or efficiently with GR. Some issues are
- I put an ‘fps_update’ option in there and the effect is really neat, but the performance gets very sluggish about 20-30 seconds in to the plotting.
- The numbers on the y-axis can get weird since those numbers are automatically chosen based on the data range
Thanks in advance!
MVP link gist: Simulation of live plotting from received packets using GR.jl in Julia · GitHub
MVP code:
# gr_live_window.jl
using GR
function keyboard_channel()
# allow 'q' and <ENTER> sequentially pressed on the keyboard to break the loop
count = 0
kb2loop = Channel(10)
@async while true
kb_input = readline(STDIN)
# maybe we hit an error or something got messed up...
# just break and relinquish STDIN
if (count+=1) >= 5
break
end
if contains(lowercase(kb_input), "q")
put!(kb2loop, "quit")
break
end
end
return kb2loop
end
function gr_custom_plot(x, y; xylims=nothing)
GR.setviewport(0.03, 0.97, 0.05, 0.95)
# GR.setlinecolorind(218)
# GR.setfillintstyle(1)
# GR.setfillcolorind(208)
# GR.updatews()
if xylims==nothing
xylims = (minimum(x), maximum(x), minimum(y), maximum(y))
end
GR.clearws()
GR.setwindow(xylims...)
GR.fillrect(xylims...)
GR.grid(
(xylims[2]-xylims[1])/10,
(xylims[4]-xylims[3])/10,
xylims[1], 0, 5, 5
)
GR.axes(
1, #(xylims[2]-xylims[1])/30,
(xylims[4]-xylims[3])/30,
xylims[1], xylims[3],
5, 5, 0.01
)
GR.polyline(x, y)
# GR.polymarker(x, y)
GR.updatews()
end
function gr_live_window(; timeout=3600, fps_update=false)
# convenient constants
fs = 10
winsec = 30
hopsec = 1
nwin = round(Integer, winsec*fs)
nhop = round(Integer, hopsec*fs)
# let user interrupt the live plot
kb2loop = keyboard_channel()
# do the loop
frame_start = -winsec
frame_time = collect( (0:(nwin-1)) * (1/fs) )
print("I: Warming up the plotting engine... "); t1=time()
gr_custom_plot(frame_start + frame_time, zeros(Float32, nwin))
println("FINISHED [$(round(time()-t1,3)) s]")
fix = 0 # frame index
# f0 = 0.5 # Hz
fps_slide = 30
tslide = 1/fps_slide
start_time = time()
aframe = zeros(Float32, nwin)
while time() - start_time < timeout
if isready(kb2loop) && "quit"==take!(kb2loop)
break
end
# aframe = sin.(2*pi*f0.*(frame_start + frame_time))
append!(aframe, randn(nhop)); deleteat!(aframe, 1:nhop)
println("fix = $(fix+=1) @ $(time() - start_time)")
gr_custom_plot(frame_start + frame_time, aframe)
# this attempts to smoothly update at some fps or frames-per-second
if fps_update
xylims = [frame_start, frame_start+frame_time[end], minimum(aframe), maximum(aframe)]
for ix=1:fps_slide
# 0.9 is just a guess of (1-x) where x is the ratio
# (time of gr_custom_plot()) / (1/fps)
sleep(tslide)
xylims[1:2] += (tslide)
gr_custom_plot(frame_start + frame_time, aframe; xylims=xylims)
end
else
sleep(hopsec)
end
frame_start += hopsec
end # while loop
println("Finished in $(time() - start_time) seconds.")
end