gnuplot.h

Go to the documentation of this file.
00001 
00002 // -*- C++ -*-
00003 /** \file 
00004  * Simple C++ interface to gnuplot.
00005  * Send your data via pipe to gnuplot, resulting in online diagrams of your experimental data.
00006  */
00007 
00008  
00009 
00010 #ifndef GNUPLOT_H
00011 #define GNUPLOT_H
00012 
00013 #include <cstdio>
00014 #include <map>
00015 #include <utility>
00016 #include <string>
00017 #include <sstream>
00018 #include <list>
00019 
00020 #define GNUPLOT_ONLY_INCLUDES
00021 // these includes define FILE* OpenGnuplot(void) and void CloseGnuplot(FILE*) 
00022 #ifdef _WIN32
00023 #include "gnuplot_win32.h"
00024 #else
00025 #include "gnuplot_unix.h"
00026 #endif 
00027 #undef GNUPLOT_ONLY_INCLUDES
00028 
00029 /** \defgroup baseclasses Base classes
00030 */
00031 /// \ingroup baseclasses
00032 
00033 /**
00034    Open a Gnuplot window and always show last n values of arbitrary defined channels.
00035    T can be std::string or int or anything else supports the << of streams.
00036    (It is used as name for a channel.)
00037 */
00038 
00039 
00040 template<class T> class Gnuplot {
00041 
00042 /// these includes define FILE* OpenGnuplot(void) and void CloseGnuplot(FILE*) 
00043 #ifdef _WIN32
00044 #include "gnuplot_win32.h"
00045 #else
00046 #include "gnuplot_unix.h"
00047 #endif 
00048 public: 
00049     typedef std::list<T> nameslist;
00050     /** which data type is our template */
00051     typedef T channel_name_type;
00052 
00053 protected:
00054     /** data buffer for one channel, organized as ring buffer */
00055     class Dataset{
00056         double *buffer;
00057         int buffersize;
00058         int current;
00059     public:    
00060         std::string title;
00061         std::string style;
00062         bool show;
00063         /** contruct a data buffer with (gnuplot) title and style. */
00064         Dataset(const std::string& title="",const std::string& style="",int buffersize=256)
00065             :buffersize(buffersize),title(title),style(style){
00066             buffer=new double[buffersize];
00067             for(int i=0;i<buffersize;++i)
00068                 buffer[i]=0.0;
00069             current=0;
00070             show=true;
00071         };
00072         ~Dataset(){
00073             delete []buffer;
00074         };
00075         /** put new data in (ring) buffer */
00076         void putData(double v){
00077             buffer[current++]=v;
00078             current=current%buffersize;
00079         };
00080         /** print buffer content to file pointer, usually a pipe */
00081         void plot(FILE* f, int start=0, int end=0){
00082             int _start=current+start;
00083             int _end=current+((end<=0)?buffersize:end);
00084             for(int i=_start;i<_end;++i)
00085                 fprintf(f,"%f\n",buffer[i%buffersize]);
00086         };
00087         /** get buffer content */
00088         double getData(int i){
00089             return buffer[(i+current)%buffersize];
00090         };
00091 
00092     };
00093 
00094     FILE* pipe;
00095     /** data type of container storing   channel->data  relation */
00096     typedef std::map<T, Dataset*> dataset_map;
00097     dataset_map datasets;
00098     nameslist namesets;    
00099     int buffersize;
00100     int start;
00101     int end;
00102 public:
00103     
00104     Gnuplot(int buffersize=256):buffersize(buffersize),start(0),end(buffersize){
00105         pipe=OpenGnuplot();
00106     };
00107     ~Gnuplot(){
00108         for(typename dataset_map::iterator i=datasets.begin();i!=datasets.end();++i){
00109             delete i->second;
00110         }
00111         if(pipe)CloseGnuplot(pipe);
00112     };
00113     /** send arbitrary command to gnuplot.
00114         like "set zeroaxis" or other stuff */
00115     void command(const std::string& cmd){
00116         fprintf(pipe,"%s\n",cmd.data());
00117         fflush(pipe);
00118     };
00119     /** add new channel with name. 
00120         currently uses name as title if not given.  */
00121     void addChannel(const T& name,const std::string& title="",const std::string& style="lines"){
00122         if(title != "")
00123             datasets.insert(std::make_pair(name,new Dataset(title,style,buffersize)));
00124         else {
00125             std::ostringstream str;
00126             str << name;
00127             datasets.insert(std::make_pair(name,new Dataset(str.str(),style,buffersize)));
00128         }
00129         namesets.push_back(name);
00130     };
00131     /** add new data value to buffer for channel.
00132         if channel not exists nothing happens.
00133         So you can select channels in your program by commenting out the addChannel() call */
00134     void putData(const T& channel, double data){
00135         typename dataset_map::iterator i = datasets.find(channel);
00136         if(i!=datasets.end())
00137             i->second->putData(data);
00138     }; 
00139 
00140     /** returns channel names */
00141     const nameslist& getNames(){
00142         return namesets;
00143     };
00144     
00145     /** get title  */
00146     std::string getTitle(const T& channel){
00147         typename dataset_map::iterator i = datasets.find(channel);
00148         if(i!=datasets.end())
00149             return i->second->title;
00150         return "";
00151     }; 
00152     
00153     /** switch showing of this channel on or off */
00154     void show(const T& channel, bool on=true){
00155         typename dataset_map::iterator i = datasets.find(channel);
00156         if(i!=datasets.end())
00157             i->second->show=on;
00158     }; 
00159 
00160     /** switch showing of this channel off */
00161     void hide(const T& channel){ 
00162         show(channel, false);
00163     }; 
00164 
00165     /** switch showing all channels on or off */
00166     void show_all(bool on=true){
00167         for(typename dataset_map::iterator i=datasets.begin();i!=datasets.end();++i)
00168             i->second->show=on;
00169     }; 
00170 
00171     /** switch showing all channels off */
00172     void hide_all(){ 
00173         show_all(false);
00174     }; 
00175 
00176     /** set title */
00177     void setTitle(const T& channel, const std::string& title){
00178         typename dataset_map::iterator i = datasets.find(channel);
00179         if(i!=datasets.end())
00180             i->second->title=title;
00181     }; 
00182 
00183     /** set style */
00184     void setStyle(const T& channel, const std::string& style){
00185         typename dataset_map::iterator i = datasets.find(channel);
00186         if(i!=datasets.end())
00187             i->second->style=style;
00188     }; 
00189 
00190     /** make gnuplot plot selected content of data buffers */
00191     void plot(int start, int end){
00192         // calculate real values for start and end
00193         int _start=buffersize-end;
00194         int _end=buffersize-start;
00195         fprintf(pipe,"plot ");
00196         bool first=true;
00197         for(typename dataset_map::iterator i=datasets.begin();i!=datasets.end();++i){
00198             Dataset* dataset=i->second;
00199             if(dataset->show){
00200                 if(first) first=false;
00201                 else fprintf(pipe,", ");
00202                 fprintf(pipe,"'-'");    
00203                 fprintf(pipe," title '%s'",dataset->title.data());    
00204                 fprintf(pipe," with %s",dataset->style.data());    
00205             }
00206         }
00207         fprintf(pipe,"\n");
00208         for(typename dataset_map::iterator i=datasets.begin();i!=datasets.end();++i){
00209             Dataset* dataset=i->second;
00210             if(dataset->show){
00211                 dataset->plot(pipe,_start,_end);
00212                 fprintf(pipe,"e\n");
00213             }
00214         }
00215         fflush(pipe);
00216     };    
00217     /** make gnuplot plot content of data buffers */
00218     void plot(){ plot(start,end); }
00219     
00220 
00221     /** set start of plot area 
00222         @param n steps in history (must be smaller than end)
00223     */
00224     void setStart(int n){ start=n; };
00225 
00226     /** set end of plot area 
00227         @param n steps in history (must be larger than start)
00228     */
00229     void setEnd(int n){ end=n; };
00230 
00231     /** returns buffersize */
00232     int getBuffersize() { return buffersize; };
00233     
00234     /** make gnuplot XY plot content of x against y data buffers 
00235         use it as follow:
00236         <pre>
00237         T x[]={a,b,c};
00238         T y[]={x,y,z};
00239         plotXY(x,y,3);
00240         </pre>
00241     */
00242     void plotXY(const T *x, const T *y,int size){
00243         fprintf(pipe,"plot ");
00244         bool first=true;
00245         for(int i=0;i < size; ++i){
00246             typename dataset_map::iterator iX = datasets.find(x[i]);
00247             typename dataset_map::iterator iY = datasets.find(y[i]);
00248             // check if both channels exist
00249             if(iX==datasets.end() || iY==datasets.end())
00250                 continue;
00251             if(first) first=false;
00252             else fprintf(pipe,", ");
00253             Dataset* datasetX=iX->second;
00254             Dataset* datasetY=iY->second;
00255             fprintf(pipe,"'-'");    
00256             fprintf(pipe," title '%s'",std::string(datasetX->title + "<->" + datasetY->title).data());    
00257             fprintf(pipe," with %s",datasetY->style.data());    
00258         }
00259         fprintf(pipe,"\n");
00260         for(int i=0;i < size; ++i){
00261             typename dataset_map::iterator iX = datasets.find(x[i]);
00262             typename dataset_map::iterator iY = datasets.find(y[i]);
00263             // check if both channels exist
00264             if(iX==datasets.end() || iY==datasets.end())
00265                 continue;
00266             for(int i2=0; i2<buffersize; ++i2)
00267                 fprintf(pipe,"%f %f\n",iX->second->getData(i2),iY->second->getData(i2));
00268             fprintf(pipe,"e\n");
00269         }
00270         fflush(pipe);
00271     };    
00272 
00273     /** make gnuplot XY plot content of x against y data buffers */
00274     void plotXY(const T& x, const T& y){
00275         plotXY(&x,&y,1);
00276     };    
00277 
00278     /** save content of all data buffers in files 
00279         ( file names are: name + channel_name + ext )  */
00280     void printToFile(const std::string name="", const std::string ext=".log"){
00281         for(typename dataset_map::iterator i=datasets.begin();i!=datasets.end();++i){
00282             std::string name2 = name + i->second->title + ext;
00283             FILE* f=fopen(name2.data(),"w");    
00284             i->second->plot(f);
00285             fflush(f);
00286             fclose(f);
00287         }
00288     };
00289 };
00290 
00291 #endif
00292 

Generated on Tue Apr 4 19:05:03 2006 for Robotsystem from Robot Group Leipzig by  doxygen 1.4.5