Realtime plotting

Hello,
I want to plot real-time flight control data to get a better and faster understanding of the performance of our flight controllers.
The incoming data stream might be decoded in a C-function (because it already exists).
But then I would like to plot one to three data streams in Julia with an update rate of about 30 Hz. Just an oscilloscope like plot.
In addition it would be nice to select the data stream(s) in the GUI.
Which plotting package might be the best choice?
Uwe

1 Like

Perhaps this could be useful:

I think you’ll get highest performance and frame rates from Makie, but I think GR is also able to do pretty well. I believe InspectDR is designed to be quite fast, but I haven’t used it.

1 Like

Depending on the size of the data you are plotting, PlotlyJS might also work for you. I recently used the react! function in PlotlyJS to in-place update an existing plot and Interact as an interface, and I was surprised at how responsive it was. It was updating at least at 30Hz.

2 Likes

Thanks a lot for all your responses! I will try some of your suggestions soon. :slight_smile:

I was wondering how InspectDR would handle this myself… so I made a small test:

using InspectDR
using Colors

function getmeasdata(t, φ)
	sigA = sin.(t .+ φ)
	sigB = cos.(t .+ φ)
	return (sigA, sigB)
end

#Build general structure of animation plot
function buildanimplot()
	NPOINTS = 1000
	NCYCLES = 2 #To display
	RED = RGB24(1, 0, 0)
	GREEN = RGB24(0, 1, 0)
	BLUE = RGB24(0, 0, 1)

	#collect: InspectDR does not take AbstractArray:
	t = collect(range(0, stop=NCYCLES*2pi, length=NPOINTS))
	(sigA, sigB) = getmeasdata(t, 0)

	#Using Plot2D simplified "template" constructor:
	p = InspectDR.Plot2D(:lin, [:lin, :lin],
		title = "Measured Data", xlabel = "time (s)",
		ylabels = ["Amplitude (V)", "Amplitude (V)"]
	)
	p.layout[:enable_legend] = true

	wfrmA = add(p, t, sigA, id="Signal A", strip=1)
		wfrmA.line = line(color=RED, width=2)
	wfrmB = add(p, t, sigB, id="Signal B", strip=2)
		wfrmB.line = line(color=BLUE, width=2)

	gplot = display(InspectDR.GtkDisplay(), p)
	return (gplot, t, wfrmA, wfrmB)
end

#Update animated plot in "real time":
function testanimplot()
	DURATION = 5 #sec
	NSTEPS = 1000
	RADPERSEC = 2pi
	tstep = DURATION/NSTEPS
	ϕstep = RADPERSEC * tstep

	(gplot, t, wfrmA, wfrmB) = buildanimplot()

@time begin #Curious to see how long it actually takes
	for ϕ in range(0, step=ϕstep, length=NSTEPS)
		sleep(DURATION/NSTEPS)
		(sigA, sigB) = getmeasdata(t, ϕ)
		wfrmA.ds.y = sigA
		wfrmB.ds.y = sigB
		InspectDR.refresh(gplot)
	end
end

	return gplot
end

gplot = testanimplot()

This might make it easier to see if InspectDR is adequate for your task…

2 Likes

How did you do it? Hints will be much appreciated.

The general premise is to keep your trace objects that are used in constructing the plot, update them inplace, and then call react! with your plot objects and your (modified) traces. Here’s an example:

using PlotlyJS

t = 1:25
x1 = sin.(t./(pi))
x2 = rand(25) .+ 2

trace1 = scatter(;x=t, y=x1)
trace2 = scatter(;x=t, y=x2)
l = Layout()

p = plot([trace1, trace2], l)

for i in 1:100
    trace1[:y] .= sin.((t .+ i) ./ pi)
    trace2[:y] .= rand(25) .+ 2
    react!(p, [trace1, trace2], l)
    
    sleep(0.1)
end
3 Likes

That’s cool.

Nice. That is pretty much what I ended up doing, except that I thought it too much work to sort through existing traces to change them up. I just regenerated the traces from scratch every time they needed to be plotted.

Now the trickier part: Make the plot respond to user input during the simulation/plotting.