For the last couple of years, I've been working on European Space Agency (ESA) projects - writing rather complex code generators. In the ESA project I am currently working on, I am also the technical lead; and I recently faced the need to (quickly) provide real-time plotting of streaming data. Being a firm believer in open-source, after a little Googling I found Gnuplot. From my (somewhat limited) viewpoint, Gnuplot appears to be the LaTEX equivalent in the world of graphs: amazing functionality that is also easily accessible. Equally important, Gnuplot follows the powerful paradigm that UNIX established: it comes with an easy to use scripting language, thus allowing its users to prescribe actions and "glue" Gnuplot together with other applications - and form powerful combinations.
To that end, I humbly submit a little creation of mine: a Perl script that spawns instances of Gnuplot and plots streaming data in real-time.
The resulting script is relatively simple - and easy to use:
bash ./driveGnuPlots.pl Usage: ./driveGnuPlots.pl <options> where options are (in order): NumberOfStreams How many streams to plot (windows) Stream1_WindowSampleSize <Stream2...> This many window samples for each stream Stream1_Title <Stream2_Title> ... Title used for each stream (Optional) Stream1_geometry <...>. Sizes and positions in pixels The last parameters (the optionally provided geometries of the gnuplot windows) are of the form: WIDTHxHEIGHT+XOFF+YOFFNote that the script uses the "autoscale" feature of GnuPlot, to automatically adapt to the incoming value ranges.
#!/usr/bin/perl -w use strict; use Time::HiRes qw/sleep/; # First, set the standard output to auto-flush select((select(STDOUT), $| = 1)[0]); # And loop 5000 times, printing values... my $offset = 0.0; while(1) { print "0:".sin($offset)."\n"; print "1:".cos($offset)."\n"; $offset += 0.1; if ($offset > 500) { last; } sleep(0.02); }
This is how we test the plotting script (assuming we saved the sample code above in sinuses.pl): <
bash$ ./sinuses.pl | ./driveGnuPlots.pl 2 50 500 "Sine" "Cosine"To stop the plotting, use Ctrl-C on the terminal you spawned from.
The parameters we passed to driveGnuPlots.pl are:
Executive summary: plotting streaming data is now as simple as selecting them out from your "producer" program (filtering its standard output through any means you wish: grep, sed, awk, etc), and outputing them, one number per line. Just remember to prefix with the stream number ("0:", "1:", etc, to allow for multiple streams), and make sure you flush your standard output, e.g.
For this kind of output: bash$ /path/to/programName ...(other stuff) Measure: 7987.3 ...(other stuff) Measure: 8364.4 Measure: 8128.1 ... You would do this: bash$ /path/to/programName | \ grep --line-buffered '^Measure:' | \ awk -F: '{printf("0:%f\n", $2); fflush();}' | \ driveGnuPlots.pl 1 50 "My data"In the code above, grep filters out the lines that start with "Measure:", and awk selects the 2nd column ($2) and prefixes it with "0:" (since this is the 1st - and only, in this example - stream we will display). Notice that we used the proper options to force the standard output's flushing for both grep (--line-buffered) and awk (fflush() called).
bash$ ./sinus.pl | ./driveGnuPlots.pl 2 50 50 Sinus Cosinus 512x384+0+0 512x384+512+0The provisioning of titles and GnuPlot window placement information, makes the script very well-suited for live demonstrations.
P.S. UNIX power in all its glory: it took me 30min to code this, and another 30 to debug it. Using pipes to spawned copies of gnuplots, we are able to do something that would require one or maybe two orders of magnitude more effort in any conventional programming language (yes, even accounting for custom graph libraries - you do have to learn their API and do your windows/interface handling...)
Index | CV | Updated: Sat Oct 8 12:33:59 2022 |