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
You can type
<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: https://gist.github.com/standarddeviant/a1be022413563275b5cac84461ce93a7
# 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-xylims)/10, (xylims-xylims)/10, xylims, 0, 5, 5 ) GR.axes( 1, #(xylims-xylims)/30, (xylims-xylims)/30, xylims, xylims, 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