Created
July 28, 2013 17:30
-
-
Save nzjrs/6099379 to your computer and use it in GitHub Desktop.
Plotting data with gnuplot in real-time
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/perl -w | |
#Salvaged from archive.org | |
#http://web.archive.org/web/20100309204315/http://www.lysium.de/blog/index.php?/archives/234-Plotting-data-with-gnuplot-in-real-time.html | |
#The input (on stdin or from a file) looks like this, as for the original version: | |
#0:0.09983 | |
#1:0.99500 | |
#2:0.69314 | |
#0:0.19866 | |
#1:0.98006 | |
#0:0.29552 | |
#1:0.95533 | |
#2:1.79175 | |
#0:0.38941 | |
#1:0.92106 | |
#0:0.47942 | |
#1:0.87758 | |
#2:2.30258 | |
#0:0.56464 | |
#1:0.82533 | |
#0:0.64421 | |
#1:0.76484 | |
#2:2.63905 | |
#... | |
#The number before the colon specifies to which stream the data point belongs, the number after the colon is the (next) data point. Note that in this example, the data points for stream 0 and 1 (sin and cos in the example above) come twice as fast as the data points for stream 2 (log in the example above). | |
#The command line to generate these plots looks like this: | |
#perl ./driveGnuPlotStreams.pl 3 2 \ # number of streams and windows | |
# 50 50 \ # width of sliding window in each window | |
# -1 1 -2 6 \ # min/max values for each window | |
# 500x300+0+0 500x300+500+0 \ # geometry of each window | |
# 'sin' 'cos' 'log' \ # title of each stream | |
# 0 0 1 # in which window to plot each stream | |
#The first two numbers on the command line give the number of streams and number of windows to plot, respectively. The second line gives the number of data points to plot, that is, the size of the sliding window. The third line gives the min/max values for each window. The fourth line gives the geometry of the windows. The fifth line gives the titles of the streams. And the sixth and last line gives the window number in which each stream is to be plotted. Compared to the original program, I changed the position of the geometry specification and the titles, so that the options for the windows and the streams are each grouped together. | |
use strict; | |
sub usage { | |
print "Usage: $0 <options>\n"; | |
print <<OEF; | |
where options are (in order): | |
NumberOfStreams How many streams to plot (M) | |
NumberOfWindows How many windows to use (N) | |
Window0_WindowSampleSize this many samples per window for window0 | |
<Window1_WindowSampleSize> ...for window1 | |
... | |
<WindowN_WindowSampleSize> ...for windowN | |
Window0_YRangeMin Window0_YRangeMax Min and Max values for window0 | |
<Window1_YRangeMin Window1_YRangeMax> ...for window1 | |
... | |
<WindowN_YRangeMin WindowN_YRangeMax> ...for windowN | |
Window0_geometry WIDTHxHEIGHT+XOFF+YOFF (in pixels) | |
<Window1_geometry> ...for window1 | |
... | |
<WindowN_geometry> ...for windowN | |
Stream0_Title Title used for stream 0 | |
<Stream1_Title> ...for stream1 | |
... | |
<StreamN_Title> ...for streamM | |
WindowNumber0 Window into which stream 0 is plotted | |
<WindowNumber1> ... for stream1 | |
... | |
<WindowNumberM> ... for streamM | |
OEF | |
exit(1); | |
} | |
sub WrongParameter { | |
my $cause = shift; | |
print "Expected parameter missing ($cause)...\n\n"; | |
usage; | |
exit(1); | |
} | |
sub main { | |
my $argIdx = 0; | |
my $numberOfStreams = shift or WrongParameter("number of streams"); | |
my $numberOfWindows = shift or WrongParameter("number of windows"); | |
print "Will display $numberOfStreams Streams in $numberOfWindows windows...\n"; | |
my @sampleSizes; | |
for(my $i=0; $i<$numberOfWindows; $i++) { | |
my $samples = shift or WrongParameter("sample size $i"); | |
push @sampleSizes, $samples; | |
print "Window $i will use a window of $samples samples\n"; | |
} | |
my @ranges; | |
for(my $i=0; $i<$numberOfWindows; $i++) { | |
my $miny = shift; | |
WrongParameter("min y of window $i") if !defined($miny); | |
my $maxy = shift; | |
WrongParameter("max y of window $i") if !defined($maxy); | |
push @ranges, [ $miny, $maxy ]; | |
print "Window $i will use a range of [$miny, $maxy]\n"; | |
} | |
my @geometries; | |
for(my $i=0; $i<$numberOfWindows; $i++) { | |
my $geometry = shift or WrongParameter("geometry $i"); | |
push @geometries, $geometry; | |
print "Window $i will use a geometry of '$geometry'\n"; | |
} | |
my @titles; | |
for(my $i=0; $i<$numberOfStreams; $i++) { | |
my $title = shift or WrongParameter("title $i"); | |
push @titles, $title; | |
print "Stream $i will use a title of '$title'\n"; | |
} | |
my @streams; # streams in a window | |
my @windows; # window of a stream | |
for(my $i=0; $i<$numberOfStreams; $i++) { | |
my $window = shift; | |
WrongParameter("window of stream $i") if !defined $window; | |
push @{$streams[$window]}, $i; | |
$windows[$i] = $window; | |
print "Stream $i will be plotted in window $window\n"; | |
} | |
# check that every window has a stream | |
for my $windowIdx(0..$numberOfWindows-1) { | |
if (!defined($streams[$windowIdx]) or @{$streams[$windowIdx]} == 0) { | |
warn "Warning: Window $windowIdx has no streams!\n"; | |
} | |
} | |
my @gnuplots; | |
my @buffers; | |
my @xcounters; | |
for (0..$numberOfStreams-1) { | |
my @data = []; | |
push @buffers, @data; | |
push @xcounters, 0; | |
} | |
for(my $i=0; $i<$numberOfWindows; $i++) { | |
local *PIPE; | |
my $geometry = $geometries[$i]; | |
open PIPE, "|gnuplot -geometry $geometry" || die "Can't initialize gnuplot number ".($i+1)."\n"; | |
select((select(PIPE), $| = 1)[0]); | |
push @gnuplots, *PIPE; | |
print PIPE "set xtics\n"; | |
print PIPE "set ytics\n"; | |
print PIPE "set yrange [".($ranges[$i]->[0]).":".($ranges[$i]->[1])."]\n"; | |
print PIPE "set style data linespoints\n"; | |
print PIPE "set grid\n"; | |
print PIPE "set term x11\n"; | |
} | |
my $streamIdx = 0; | |
# replace @ARGV with remaining args for <> below | |
@ARGV = @_; | |
while(<>) { | |
chomp; | |
my @parts = split /:/; | |
#print "$.: parts=", join("-", @parts), "\n"; | |
$streamIdx = $parts[0]; | |
my $windowIdx = $windows[$streamIdx]; | |
my $buf = $buffers[$streamIdx]; | |
my $pip = $gnuplots[$windowIdx]; | |
# data buffering (up to stream sample size) | |
my $xcounter = $xcounters[$streamIdx]; | |
push @{$buf}, "$xcounter $parts[1]"; | |
$xcounters[$streamIdx]++; | |
my $max_xcounter = $xcounter; | |
for my $stream (@{$streams[$windowIdx]}) { | |
if ($xcounters[$stream] > $max_xcounter) { | |
$max_xcounter = $xcounters[$stream]; | |
} | |
} | |
print $pip "set xrange [".($max_xcounter-$sampleSizes[$windowIdx]).":".($max_xcounter)."]\n"; | |
my @plots; | |
for my $stream (@{$streams[$windowIdx]}) { | |
if (@{$buffers[$stream]} > 0) { | |
push @plots, "\"-\" title '$titles[$stream]'"; | |
} | |
} | |
print $pip "plot ", join(", ", @plots), "\n"; | |
for my $stream (@{$streams[$windowIdx]}) { | |
if (@{$buffers[$stream]} > 0) { | |
for my $elem (reverse @{$buffers[$stream]}) { | |
print $pip "$elem\n"; | |
} | |
print $pip "e\n"; | |
} | |
} | |
if (scalar(@{$buf})>$sampleSizes[$windowIdx]) { | |
shift @{$buf}; | |
} | |
} | |
for(my $i=0; $i<$numberOfWindows; $i++) { | |
my $pip = $gnuplots[$i]; | |
print $pip "exit;\n"; | |
close $pip; | |
} | |
} | |
main @ARGV; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for preserving!
I'm using this code - it works great.
I know of no other way to make a "real time" strip chart via from perl data.
On some machines, the strip chart jumps on top of any new window that comes up on it.
Anyone know why this would be?
Very bad!
Anyone know why this would be?
On other machines, I think maybe the problem doesn't happen.
There might be a difference in SUSE version.
Anyone know why this would be?