Skip to content

Instantly share code, notes, and snippets.

@nzjrs
Created July 28, 2013 17:30
Show Gist options
  • Save nzjrs/6099379 to your computer and use it in GitHub Desktop.
Save nzjrs/6099379 to your computer and use it in GitHub Desktop.
Plotting data with gnuplot in real-time
#!/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;
@ronHartikka
Copy link

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment