/* Copyright (c) 2001 RIPE NCC All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the author not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* ------------------------------------------------------------------------------- Module Header Filename : ipdv.C Author : Reinhard Sojka Date : 05-NOV-2001 Description : Program for creating TTM IPDV ("jitter") plots Language Version : C++ OSs Tested : Solaris 2.6 Command line options: --help get an overview of the options -6 IP version 6 data -s source box no. -p start time and end time, format is YYYYMMDD/YYYYMMDD, any ISO 8601 format is ok, like YYYYMMDDThh:mm:ss or similar -t target box no., -1 for all connected boxes, optional, default = -1 -d date in file name, 'on' or 'off', optional, default = 'off' (ipdv.tt_A.tt_B.start.end.*) -e enable error plot, 'on' or 'off', optional (default = 'off') -f output file format, 'gif', 'ps', 'pdf' or 'eps', optional (default = 'gif') -m maximum delay to be displayed on histograms, optional (default = 800 ms) -z minimum delay to be displayed on histograms, optional (default = 0 ms) -u maximum IPDV to be displayed on histograms, optional (default = 400 ms) -o output directory for plots, optional (default is current directory) -h create a HTML file for the webpage, 'on' or 'off', optional (default = 'off') -r write percentiles to a file, 'on' or 'off', optional (default = 'off') -v verbose, 'on' or 'off', optional, default = 'off' Notes/Description: program to read the data of a time interval betweeen some minutes and several days in different lists, filtered by testboxnumber sort the data by arrival time and calculata IPDV for the (existing) testboxes in file; calculates also the experimental error and draws an error plot> plots will be stored as *.eps file in current folder normalized delay histograms, added statistic pad autozoom for delay distribution and IPDV distribution added percentiles for statistics pad output as gif or eps, options/switches made conform with delay calculation changed time input, changed handling of time formats and creation of file names now with the correct time in the histograms (Root has its own time stamp, starting 1995) percentiles can be writtten to a file added the possibility to customize the plots external programs: ps2gif to convert eps into gif format $Id: ipdv.C,v 1.8 2003/06/02 10:42:19 ruben Exp $ ------------------------------------------------------------------------------- */ static char const rcsid[] = "$Id: ipdv.C,v 1.8 2003/06/02 10:42:19 ruben Exp $"; static char const *rcsid_p = rcsid; // Prevent g++ from optimizing out rcsid #define TTCONFIG_COMMAND "/ncc/ttpro/bin/ttconfig" #define TTCONFIG_COMMAND_ARGS "-v active" //#define DEFAULT_OUTPUTDIR "/ncc/ttpro/spool/plots" #include "iostream.h" #include "fstream.h" #include "stdio.h" #include "string.h" #include "time.h" #include "stdlib.h" #include "unistd.h" #include #ifndef __CINT__ #include "TROOT.h" #include "TApplication.h" #include "TCanvas.h" #include "TChain.h" #include "TTree.h" #include "TFile.h" #include "TH1.h" #include "TH2.h" #include "TF1.h" #include "TGraphErrors.h" #include "TPaveLabel.h" #include "TText.h" #include "TLine.h" #include "TStyle.h" #include "TPostScript.h" #endif #include "Delay.h" #include "Percentiles.h" //Rene's class to calculate the percentiles #include "iso8601.h" //to convert input string into "readable" time //create structure to store one set of data in a list //maybe a bit oldfashioned, but works fine. creating an own class would make sense struct ttmData{ UInt_t PacketId; // Identifier of the packet Int_t SourceId; // ID# of the sending testbox // Int_t SourcePort; // Port from which the packet was sent Int_t TargetId; // ID# of the receiving testbox // Int_t TargetPort; // Port to which the packet was sent // Int_t PacketSize; // Packet size in bytes Double_t ArrivalTime; // -1.0 if undefined Double_t PacketDelay; // Delay in ms, -1.0 if undefined // UInt_t SourceClock; // Sending clock NTP status // UInt_t TargetClock; // Receiving clock NTP status Int_t Nhops; // Number of hops, -1 if unknown // Int_t RouteId; // Routing vector number, -1 if unknown Float_t SourceNtp; // Sending clock NTP estimated error Float_t TargetNtp; // Receiving clock NTP estimated error ttmData *next; }; // * * * declaration of subroutines * * * void CreateAxisStyle(char* xTitle, char* xTimeFormat, Double_t minXax, Double_t maxXax, Int_t outputFormat); void CreatePlotTitle(char* titleOfWholePlot, Int_t boxNo, char* SourceBox, Double_t minXax, Double_t maxXax); void CreateChar(char* SourceBox, char* startYYYYMMDD, char* endYYYYMMDD, Int_t start_Box, Double_t minXax, Double_t maxXax); time_t CreateRootFileName(char* sourceFile, time_t act_TimeStamp, Int_t start_Box); void CreatePlotFileNameDate(char* plotFileName, Int_t boxNo, char* SourceBox, char* startYYYYMMDD, char* endYYYYMMDD, Int_t outputFormat); void CreatePlotFileNameStd(char* plotFileName, Int_t boxNo, char* SourceBox, Int_t outputFormat); void WriteStatisticsPad(TPad* pad, Int_t allPackets, Int_t validPackets, Int_t noOfFlaps, Percentiles* ipdvPercentile, Float_t rmsIPDV, Float_t meanDelay, Float_t rmsDelay); void write_stats(TPad* pad, const char* ptr_text, const Float_t x, const Float_t y, const Short_t align, Font_t font); void PutDataInElement(Delay* delay, ttmData* element); void SortList(ttmData* access); void TerminationMessage(); void CalculateAndFillHistograms(ttmData* access, TH1F* h1, TH2F* h2, TH2F* h3, TH2F* h4, TH1F* h5, TGraphErrors* ipdvErr, Percentiles* ipdvPercentile, Int_t enableErrorPlot); void SetHistogramProperties(TH1F* h1, TH2F* h2, TH2F* h3, TH2F* h4, TH1F* h5, TH2F* dummy, char* xTitle, char* xTimeFormat); void DrawHistograms(TH1F* h1, TH2F* h2, TH2F* h3, TH2F* h4, TH1F* h5, TPad* pad1, TPad* pad2, TPad* pad3, TPad* pad4, Int_t customIPDVPlot, Int_t customDelayPlot); void DrawErrorPlot(TH2F* dummy, TGraphErrors* ipdvErr, TPad* errorPad); void ExtractInput(int argc, char** argv, Int_t* start_Box, Int_t* end_Box, time_t& endtime, time_t& duration, Int_t* enableErrorPlot, Int_t* outputFormat, Int_t* enableDateInFileName, Int_t* savePercentiles, Int_t* createHTML, Int_t* printVerbose, char* outputDir, Int_t* maxIPDV, Int_t* customIPDVPlot, Int_t* maxCustomDelay, Int_t* minCustomDelay, Int_t* customDelayPlot); void SavePercentiles(Percentiles* ipdvPercentile, char* SourceBox, Int_t boxNo, ofstream &IntoFile, Int_t firstLine); void CreateHTMLFile(Percentiles* ipdvPercentile, char* anyString, Int_t boxNo, ofstream &IntoFile, Int_t paragraphID); Int_t CheckPlotParameter(Int_t* maxIPDV, Int_t* maxCustomDelay, Int_t* minCustomDelay); // * * * G L O B A L V A R I A B L E S * * * const Int_t MAX = 125; //number of testboxes char fn_Path[30] = "/ncc/ttpro/data/root/"; //path to root files, dont't forget "/" at the end const time_t convToRootTime = 788918400; //Root has its own time, starting 1995 - needed for histograms char *progname; TStyle *rootstyle; //to get access to gStyle int ipVersion = 4; // default IPv4 data // * * * M A I N * * * int main(int argc, char **argv) { progname = argv[0]; //assigns the name, which is used to start the program, to argv[0] // ROOT initialisation TROOT simple ("TTM", "Plots"); gROOT->SetBatch(); // no graphics windows TApplication* app = new TApplication("delayPlots", NULL, NULL, NULL, 0); // ensure correct timezone for date<->time_t conversions putenv("TZ=GMT+0"); //set default values and get the input data Int_t start_Box = 1; Int_t end_Box = -1; //source and target box // XXX duration is wrong interpreted here time_t endtime; time_t duration; // FIXME Should be a plottype Int_t outputFormat = 0; //standard output format is gif - gif=0, ps=1, eps=2, pdf=3 // XXX Are they all used Int_t enableErrorPlot = 0; //error plot is disabled by default Int_t savePercentiles = 0; //write percentiles to file, disabled by default Int_t enableDateInFileName = 0; //output file name, default = ipdv.tt_A.tt_B.* Int_t createHTML = 0; //create a HTML fiel to link the plots from the web site Int_t printVerbose = 0; //verbose Int_t maxIPDV = 400; //by default is IPDV vs time histograms from -400 to 400 ms Int_t customIPDVPlot = 0; //make the IPDV plots with specified parameters, default autozoom Int_t maxCustomDelay = 800; //by default the maximum for the delays histograms is 800 ms Int_t minCustomDelay = 0; //by default the delays histogram start at 0 ms Int_t customDelayPlot = 0; //make the delay plots with specified parameters, default is autozoom char outputDir[50]; strcpy(outputDir, ""); //directory to write output files, default is current directory //call subroutine // XXX Should use class TTMOptions!!! ExtractInput(argc, argv, &start_Box, &end_Box, endtime, duration, &enableErrorPlot, &outputFormat, &enableDateInFileName, &savePercentiles, &createHTML, &printVerbose, outputDir, &maxIPDV, &customIPDVPlot, &maxCustomDelay, &minCustomDelay, &customDelayPlot); //call subroutine //make sure that the parameter for custom plots make sense CheckPlotParameter(&maxIPDV, &maxCustomDelay, &minCustomDelay); //call subroutine //create pointers - lists are organized in an array ttmData arrayOfLists[MAX]; //the only known element of a list ttmData *access = arrayOfLists; //use pointers to access the lists ttmData *last = NULL; //last element of list //create arrays Int_t allPackets[MAX]; //for statistics Int_t validPackets[MAX]; Int_t boxNo; //contains the actual testbox Double_t routingVectorId[MAX]; //will store the actual routing vector //Int_t noOfVectors[MAX]; //how many vectors per List Int_t noOfFlaps[MAX]; //how often changed the routing vector Percentiles thePercentiles[MAX]; //will contain (and calculate) the percentiles of the IPDV distribution Percentiles *ipdvPercentile = thePercentiles; //use pointers to access the percentiles //initialize pointers and arrays for(boxNo = 0; boxNo < MAX; boxNo++) { (access + boxNo)->next = last; //set access to end of list allPackets[boxNo] = 0; validPackets[boxNo] = 0; routingVectorId[boxNo] = 0.0; noOfFlaps[boxNo] = -1; //will be incremented to 0 when first routing vector is assigned(min. 1 vector) (ipdvPercentile + boxNo)->Reset(); //initialize content } //create a chain of files and read first file if(printVerbose > 0) //info - if you want to see it cout << endl << "loading file(s)..." << endl; //just for info char sourceFile[250]; //file name, will be added to chain TChain chain("tree"); //add one or more files to chain, if necessary time_t act_TimeStamp = endtime - duration; do { act_TimeStamp = CreateRootFileName(sourceFile, act_TimeStamp, start_Box); struct stat statbuf; if (stat(sourceFile, &statbuf) < 0) { perror(sourceFile); } else { chain.Add(sourceFile); } if(printVerbose > 0) //info - if you want to see it cout << sourceFile << endl; //debug - just for info }while(act_TimeStamp <= (endtime)); // XXX should round endtime to end of day //create a delay object to store data per event (a whole set of data) Delay *delay = new Delay(); chain.SetBranchAddress("delay", &delay); //get number of lines(=events) in source file Int_t nTotalEvents = chain.GetEntries() - 1; //to get range of x axis for histograms - calculate it from start parameters in a subroutine Double_t minXax = (Double_t) endtime - duration; Double_t maxXax = (Double_t) endtime; Int_t readPackets = 0; //debug //find selected packets and store them in the list //read in starts from the end to get a list which starts with the first line if(printVerbose > 0) //info - if you want to see it cout << endl << "putting data in list(s)..." << endl; //just for info for (Int_t eventNo = nTotalEvents; eventNo > (-1); eventNo--) { chain.GetEvent(eventNo); //read a set of data //check if there is enough space in the arrays if(delay->GetTargetId() >= MAX) //too much testboxes { //maybe importaint in case of further use TerminationMessage(); // call subroutine to display message return(0); //end program } //select packets if( (delay->GetArrivalTime() < maxXax) && (delay->GetArrivalTime() > minXax) ) { allPackets[delay->GetTargetId()]++; //increment counter if(delay->Status() == ClockValid ) //only valid packets { //select packets, create element and put it in (correct) list if(end_Box > 0) //only one box selected { if(end_Box == delay->GetTargetId() ) { ttmData *element = new ttmData; PutDataInElement(delay, element); //call subroutine to fill element //put element in list element->next = (access + (element->TargetId))->next; (access + (element->TargetId))->next = element; //new starting point readPackets++; //counter - debug validPackets[element->TargetId]++; //increase counter for valid packets } } else //extract data from all target boxes in chain { ttmData *element = new ttmData; PutDataInElement(delay, element); //call subroutine to copy data in element //put element in list element->next = (access + (element->TargetId))->next; (access + (element->TargetId))->next = element; //new starting point readPackets++; //counter - debug validPackets[element->TargetId]++; //increase counter for valid packets } //find out how often the route was switched - maybe a bit uneffective here if(routingVectorId[delay->GetTargetId()] != delay->GetRouteId() ) { routingVectorId[delay->GetTargetId()] = delay->GetRouteId(); noOfFlaps[delay->GetTargetId()]++; } } //endif ClocksOK() } } //end for-loop if( printVerbose > 0) //info - if you want to see it printf("sorting ...\n"); //just for info //sorting all lists by PacketId for(boxNo = 0; boxNo < MAX; boxNo++) { //sorting only lists containing data if( ((access + boxNo)->next) && ((access + boxNo)->next->next) && ((access + boxNo)->next->next->next) ) { SortList((access + boxNo)); //call subroutine to sort the list } } // force thin lines in PS output to avoid cluttering histograms gStyle->SetLineScalePS(0.2); //create canvas TCanvas *c1 = new TCanvas("c1", "IPDV plots", 0, 0, 1270, 990); //create pads to display histograms and statistics TPad *pad1 = new TPad("pad1", "This is pad 1",0.01,0.49,0.43,0.95); //1-4 will display histograms TPad *pad2 = new TPad("pad2", "This is pad 2",0.44,0.49,0.86,0.95); TPad *pad3 = new TPad("pad3", "This is pad 3",0.01,0.02,0.43,0.48); TPad *pad4 = new TPad("pad4", "This is pad 4",0.44,0.02,0.86,0.48); TPad *pad5 = new TPad("pad5", "This is pad 5",0.87,0.02,0.99,0.95); //will display statistics //draw grid pad1->SetGridx(); pad1->SetGridy(); pad2->SetGridx(); pad2->SetGridy(); pad3->SetGridx(); pad3->SetGridy(); pad4->SetGridx(); pad4->SetGridy(); //force them to be visible on the canvas - histograms don't appear without this procedure pad1->Draw(); pad2->Draw(); pad3->Draw(); pad4->Draw(); pad5->Draw(); //prepare title of the canvas c1 TPaveLabel *pl1 = new TPaveLabel(0.175, 0.956, 0.825, 0.998, "foo", "br"); //prepare, text will be filled later pl1->SetTextSize(0.45); pl1->Draw(); if (ipVersion == 6) { TPaveLabel *ip = new TPaveLabel(0.01,0.956,0.1,0.998,"IPv6","br"); ip->SetTextColor(2); ip->SetTextSize(0.8); ip->Draw(); } // and once again for the next canvas with a pad to display the error plot TCanvas *c2 = new TCanvas("c2", "more IPDV plots", 0, 0, 1270, 990); TPad *errorPad = new TPad("errorPad", "to display errors",0.02,0.04,0.98,0.92); //will display error graph errorPad->SetGridx(); errorPad->SetGridy(); errorPad->Draw(); //prepare title of the canvas c2 TPaveLabel *pl2 = new TPaveLabel(0.175, 0.956, 0.825, 0.998, "foo", "br"); //prepare, text will be filled later pl2->SetTextSize(0.45); pl2->Draw(); //create a 1dim-histogram //TH1F *h1 = new TH1F("h1", "IPDV", 800, -400, 400); TH1F *h1 = new TH1F("h1", "IPDV", 800, ((-1) * maxIPDV), maxIPDV); //TH1F *h5 = new TH1F("h5", "PacketDelay", 800, 0, 800); TH1F *h5 = new TH1F("h5", "PacketDelay", 800, minCustomDelay, maxCustomDelay); //create 2dim histograms with dynamic borders according to selected time frame //TH2F *h2 = new TH2F("h2", "IPDV vs time", 500,(minXax-convToRootTime), (maxXax-convToRootTime), 400, -400, 400); TH2F *h2 = new TH2F("h2", "IPDV vs time", 500,(minXax-convToRootTime), (maxXax-convToRootTime), 400, ((-1) * maxIPDV), maxIPDV); //TH2F *h3 = new TH2F("h3", "delay (black) and hops*10 (red)", 500, (minXax-convToRootTime), (maxXax-convToRootTime), 400, 0, 800); //delay TH2F *h3 = new TH2F("h3", "delay (black) and hops*10 (red)", 500, (minXax-convToRootTime), (maxXax-convToRootTime), 400, minCustomDelay, maxCustomDelay); //delay //TH2F *h4 = new TH2F("h4", "delay (black) and hops*10 (red)", 500, (minXax-convToRootTime), (maxXax-convToRootTime), 400, 0, 800); //nhops TH2F *h4 = new TH2F("h4", "delay (black) and hops*10 (red)", 500, (minXax-convToRootTime), (maxXax-convToRootTime), 400, minCustomDelay, maxCustomDelay); //nhops TH2F *dummy = new TH2F("dummy", "IPDV error", 500, (minXax-convToRootTime), (maxXax-convToRootTime), 400, -400, 400); //just a placeholder to get the axes for the error diagram //create title and style of X axis char xTitle[100]; char xTimeFormat[6]; CreateAxisStyle(xTitle, xTimeFormat, minXax, maxXax, outputFormat); //call subroutine for calculation //set properties of the histograms SetHistogramProperties(h1, h2, h3, h4, h5, dummy, xTitle, xTimeFormat); //call subroutine //convert variables(numbers) in strings, so they can be used in file names char SourceBox[4]; //number of source box char startYYYYMMDD[9]; char endYYYYMMDD[9]; CreateChar(SourceBox, startYYYYMMDD, endYYYYMMDD, start_Box, minXax, maxXax); //write percentiles to a file, if selceted at data input ofstream IntoFile; //file handler - declaration here, otherwise compiler complains if(savePercentiles > 0) { char percFileName[250]; //selected target Box strcpy(percFileName, outputDir); strcat(percFileName, "ipdv_percentiles.tt"); strcat(percFileName, SourceBox); strcat(percFileName, ".txt"); //open file and write data IntoFile.open(percFileName); //open (or create) file SavePercentiles((ipdvPercentile + 0), SourceBox, 0, IntoFile, 1); //call subroutine to write the first lines } //write percentiles to a file, if selceted at data input ofstream theHTML_File; //file handler - declaration here, otherwise compiler complains if(createHTML > 0) { char htmlFileName[250]; //selected target Box strcpy(htmlFileName, outputDir); strcat(htmlFileName, "ipdv_LinkPage.tt"); strcat(htmlFileName, SourceBox); strcat(htmlFileName, ".html"); //open file and write data theHTML_File.open(htmlFileName); //open (or create) file CreateHTMLFile((ipdvPercentile + 0), SourceBox, 0, theHTML_File, 0); //call subroutine to write the first lines } //format key for plot file - see class TPostScript Int_t formatKey; if(outputFormat == 2) //standard output format is gif - gif=0, ps=1, eps=2 formatKey = 113; //113=eps else formatKey = 112; //112=ps - gif will be created from a *.ps file (by an external Program) //other strings for filenames and titles char plotFileName[60]; //file name of plot char errorPlotFileName[70]; //file name of plot char titleOfWholePlot[250]; //title of the plot, displayed on top of the canvas c1 char buffer[250]; //calculate IPDVs and draw+save plots - loop over all data if(printVerbose > 0) //info - if you want to see it cout << "calculating and drawing ..." << endl; //just for info for(boxNo = 0; boxNo < MAX; boxNo++) { //calcualting only lists containing data if( ((access + boxNo)->next) && ((access + boxNo)->next->next) && ((access + boxNo)->next->next->next) ) { //clear histograms //h1->Reset("ICE"); h1->Reset(); //option "ICE" doesn't reset the number of entries, maybe fixed in a new version of ROOT h2->Reset("ICE"); h3->Reset("ICE"); h4->Reset("ICE"); //h5->Reset("ICE"); h5->Reset(); //option "ICE" doesn't reset the number of entries, maybe fixed in a new version of ROOT //create a TGraphErrors - has to be done inside loop TGraphErrors *ipdvErr = new TGraphErrors(validPackets[boxNo]); //constructor will need the number //of entries //do the calculation - call subroutine CalculateAndFillHistograms( (access + boxNo), h1, h2, h3, h4, h5, ipdvErr, (ipdvPercentile + boxNo), enableErrorPlot ); //create the file name and prepare to save the image, plus adding of the output directory if(enableDateInFileName > 0 ) CreatePlotFileNameDate(plotFileName, boxNo, SourceBox, startYYYYMMDD, endYYYYMMDD, outputFormat); else CreatePlotFileNameStd(plotFileName, boxNo, SourceBox, outputFormat); strcpy(buffer, outputDir); //create whole path and file name strcat(buffer, plotFileName); //plotFileName may be used later for the name of the error plot //prepare to write to file TPostScript *epsHandle = new TPostScript(buffer, formatKey); if(formatKey != 113) //not for *.eps because of unexpected results epsHandle->Range(25.5, 18.8); //center of A4 page (according to delays program) //draw the histograms on canvas c1 c1->cd(); DrawHistograms(h1, h2, h3, h4, h5, pad1, pad2, pad3, pad4, customIPDVPlot, customDelayPlot);//call subroutine //create title of the plot to be displayed on top of the canvas CreatePlotTitle(titleOfWholePlot, boxNo, SourceBox, minXax, maxXax); c1->cd(); pl1->SetLabel( titleOfWholePlot); pl1->Draw(); //write data to statistics pad pad5->cd(); WriteStatisticsPad(pad5, allPackets[boxNo], validPackets[boxNo], noOfFlaps[boxNo], (ipdvPercentile + boxNo), (h1->GetRMS()), (h5->GetMean()), (h5->GetRMS()) ); //call subroutine //write canvas to disk and delete handler c1->cd(); c1->Update(); epsHandle->Close(); delete epsHandle; //convert *.ps to *.gif - if it has been selected at data input if( outputFormat == 0) { sprintf(buffer, "ps2gif -b 41 28 574 750.75 -s 1.2 -r %s%s", outputDir, plotFileName); system(buffer); //call system function //and now delete the eps file sprintf(buffer, "%s%s", outputDir, plotFileName); unlink(buffer); } else if( outputFormat == 3) { // convert to pdf sprintf(buffer, "ROOTps2pdf %s%s", outputDir, plotFileName); system(buffer); //call system function //and now delete the eps file sprintf(buffer, "%s%s", outputDir, plotFileName); unlink(buffer); } //draw error plot - if it has been enabled at data input if(enableErrorPlot > 0) { strcpy(errorPlotFileName, outputDir); //create file name for error graph, start with path strcat(errorPlotFileName, "error_"); //create file name for error graph strcat(errorPlotFileName, plotFileName); //draw plot TPostScript *epsErrorHandle = new TPostScript(errorPlotFileName, formatKey); c2->cd(); DrawErrorPlot(dummy, ipdvErr, errorPad); //call subroutine c2->cd(); pl2->SetLabel( titleOfWholePlot); pl2->Draw(); //save to disk c2->cd(); c2->Update(); epsErrorHandle->Close(); delete epsErrorHandle; //and convert *.ps to *.gif - if it has been selected at data input if( outputFormat == 0) { sprintf(buffer, "ps2gif -b 41 28 574 750.75 -s 1.2 -r %s", errorPlotFileName); system(buffer); //call the system function //and now delete the eps file sprintf(buffer, "rm -f %s", errorPlotFileName); system(buffer); //call the system function } } //write percentiles to a file or write HTML file, if selceted at data input if(savePercentiles > 0) SavePercentiles((ipdvPercentile + boxNo), "foo", boxNo, IntoFile, 0); //subroutine to write data if(createHTML > 0) CreateHTMLFile((ipdvPercentile + boxNo), plotFileName, boxNo, theHTML_File, 1); //call subroutine //delete error plot delete ipdvErr; } //endif } //end for loop //write the end of the file - if file creation has been selected as data input if(createHTML > 0) CreateHTMLFile((ipdvPercentile + boxNo), "foo", boxNo, theHTML_File, 2); //subroutine to write data //clean up and delete list Int_t delPackets = 0; ttmData *del_element = new ttmData; //to delete the list for(boxNo = 0; boxNo < MAX; boxNo++) { if((access + boxNo)->next) //delete only lists containing data { do { del_element = (access + boxNo)->next; //first element (access + boxNo)->next = (access + boxNo)->next->next; //get next element delete del_element; //delete element delPackets++; //counter - debug }while((access + boxNo)->next); } //endif } //end for loop delete delay; delete h1; //histograms delete h2; delete h3; delete h4; delete h5; delete dummy; //delete f_gauss; delete pad1; delete pad2; delete pad3; delete pad4; delete pad5; delete errorPad; delete pl1; delete pl2; //labels delete c1; delete c2; delete app; //close files IntoFile.close(); //close file theHTML_File.close(); if(printVerbose > 0) //info - if you want to see it { cout << readPackets << " packets filtered" << endl; cout << delPackets << " packets deleted" << endl; } return(1); } // * * * end main * * * // * * * S U B R O U T I N E S * * * //generates the axis title and the (time) format string for the x axis void CreateAxisStyle(char* xTitle, char* xTimeFormat, Double_t minXax, Double_t maxXax, Int_t outputFormat) { //generates the axis title, depending on the time frame //converts unix timeformat into "readable" strings char minText[20]; char maxText[8]; long t_minXax = (long) minXax; //type long is needed for conversion long t_maxXax = (long) maxXax; tm *timestruct = gmtime(&t_minXax); //convert the first parameter into a time structure Int_t ndays = (Int_t) (maxXax - minXax) / 3600 / 24; //from Unix time format to days if (ndays > 3) { strftime(minText, 20, "%Y", timestruct); //extract only year sprintf(xTitle, " %s%26cTime [month day]%35c", minText,' ', ' '); strcpy(xTimeFormat, "%b %d"); // - axis style } else { strftime(minText, 20, "%Y %b %d", timestruct); timestruct = gmtime(&t_maxXax); strftime(maxText, 8, "%b %d", timestruct); sprintf(xTitle, " %s%20cTime [hour:minute]%21c%s", minText,' ', ' ', maxText); if(outputFormat == 2) //different look for eps (=2) output - don't know why sprintf(xTitle, " %s%13cTime [hour:minute]%14c%s", minText,' ', ' ', maxText); strcpy(xTimeFormat, "%H:%M"); // - axis style } return; } //generates the title for the canvas c1 void CreatePlotTitle(char* titleOfWholePlot, Int_t boxNo, char* SourceBox, Double_t minXax, Double_t maxXax) { char TargetBox[4]; //selected target Box if(boxNo < 10) sprintf(TargetBox, "0%d", boxNo); //convert Int_t into string else sprintf(TargetBox, "%d", boxNo); //convert Int_t into string char timeToText[20]; long t_minXax = (long) minXax; //type long is needed for conversion long t_maxXax = (long) maxXax; tm *timestruct = gmtime(&t_minXax); //convert the first parameter into a time structure strcpy(titleOfWholePlot, "Delay Variations from tt"); strcat(titleOfWholePlot, SourceBox); strcat(titleOfWholePlot, " to tt"); strcat(titleOfWholePlot, TargetBox); strcat(titleOfWholePlot, " Start: "); strftime(timeToText, 20, "%Y-%b-%d, %H:%M", timestruct); //extract date and time strcat(titleOfWholePlot, timeToText); strcat(titleOfWholePlot, " End: "); timestruct = gmtime(&t_maxXax); strftime(timeToText, 20, "%Y-%b-%d, %H:%M", timestruct); //extract date and time strcat(titleOfWholePlot, timeToText); strcat(titleOfWholePlot, " UTC"); return; } //converts numbers into strings, needed for filenames void CreateChar(char* SourceBox, char* startYYYYMMDD, char* endYYYYMMDD, Int_t start_Box, Double_t minXax, Double_t maxXax) { //generates the strings needed for generating the strings for plot file name (version with date in name) //converts unix timeformat into "readable" strings long t_minXax = (long) minXax; //type long is needed for conversion long t_maxXax = (long) maxXax; tm *timestruct = gmtime(&t_minXax); //convert the first parameter into a time structure //create strings strftime(startYYYYMMDD, 9, "%Y%m%d", timestruct); timestruct = gmtime(&t_maxXax); //convert the first parameter into a time structure strftime(endYYYYMMDD, 9, "%Y%m%d", timestruct); //convert the source box into string if(start_Box < 10) { sprintf( SourceBox, "0%d", start_Box); } else { sprintf( SourceBox, "%d", start_Box); } return; } //create the root file, to add this filt to the tree time_t CreateRootFileName(char* sourceFile, time_t act_TimeStamp, Int_t start_Box) { char srcBox[10]; //source box //need 2 letters - maybe a problem when more than 100 boxes are in the field if(start_Box < 10) { sprintf( srcBox, "tt0%d", start_Box); } else { sprintf( srcBox, "tt%d", start_Box); } //special handling of tt02 ("osdorp") and tt04 ("jordaan") if(start_Box == 2) strcpy(srcBox, "osdorp"); if(start_Box ==4) strcpy(srcBox, "jordaan"); char buffer[50]; time_t calcTime = act_TimeStamp - 15; //to be sure to get all data, maybe not really necesary tm *timestruct = gmtime(&calcTime); //convert parameter into a time structure //create file name (incl. path) strcpy(sourceFile, fn_Path); //fn_Path is global strftime(buffer, 45, "%Y/%m/%d/", timestruct); //make the file name strcat(sourceFile, buffer); if (ipVersion == 6) { strcat(sourceFile, "IPv6."); } strcat(sourceFile, srcBox); strftime(buffer, 45, ".ripe.net.%Y%m%d.root", timestruct); strcat(sourceFile, buffer); act_TimeStamp = act_TimeStamp + 86400; //86400 = 60*60*24 = seconds per day return act_TimeStamp; } //create the file name of the plot - default void CreatePlotFileNameStd(char* plotFileName, Int_t boxNo, char* SourceBox, Int_t outputFormat) { char TargetBox[4]; //selected target Box if(boxNo < 10) sprintf(TargetBox, "0%d", boxNo); //convert Int_t into string else sprintf(TargetBox, "%d", boxNo); //convert Int_t into string if (ipVersion == 6) { strcpy(plotFileName, "IPv6.ipdv.tt"); } else { strcpy(plotFileName, "ipdv.tt"); } strcat(plotFileName, SourceBox); strcat(plotFileName, ".tt"); strcat(plotFileName, TargetBox); if(outputFormat == 2) //specify output Format - gif=0, ps=1, eps=2 strcat(plotFileName, ".eps"); else strcat(plotFileName, ".ps"); //gif will be generated from an external program return; } //create the file name of the plot file - with date, coded in file name void CreatePlotFileNameDate(char* plotFileName, Int_t boxNo, char* SourceBox, char* startYYYYMMDD, char* endYYYYMMDD, Int_t outputFormat) { char TargetBox[4]; //selected target Box if(boxNo < 10) sprintf(TargetBox, "0%d", boxNo); //convert Int_t into string else sprintf(TargetBox, "%d", boxNo); //convert Int_t into string if (ipVersion == 6) { strcpy(plotFileName, "IPv6.ipdv.plot."); } else { strcpy(plotFileName, "ipdv.plot."); } strcat(plotFileName, SourceBox); strcat(plotFileName, "."); strcat(plotFileName, TargetBox); strcat(plotFileName, "."); strcat(plotFileName, startYYYYMMDD); strcat(plotFileName, "."); strcat(plotFileName, endYYYYMMDD); if(outputFormat == 2) //specify output Format - gif=0, ps=1, eps=2 strcat(plotFileName, ".eps"); else strcat(plotFileName, ".ps"); //gif will be generated from an external program return; } //write (and calculate) data in the statistics pad void WriteStatisticsPad(TPad* pad, Int_t allPackets, Int_t validPackets, Int_t noOfFlaps, Percentiles* ipdvPercentile, Float_t rmsIPDV, Float_t meanDelay, Float_t rmsDelay) { Double_t x, y; //coordinates for printing char buf[30]; Int_t align; //align of the text Font_t font; //font of the text //clear pad pad->Clear(); //write the title x = 0.5; y = 0.95; //starting point align = 22; //horizontally and vertically centered font = 60; // helvetiva-bold-r - see TAttText class write_stats(pad, "STATISTICS", x, y, align, font); //call subroutine //write valid and invalid packets y = y - 0.03; //draw a line to seperate data on pad TLine line1(0.0,0.0,0.0,0.0); line1.SetLineWidth(2); line1.SetLineColor(4); line1.DrawLine(0.05, y, 0.95, y); y -=0.04; write_stats(pad, "Packets sent/valid", x, y, align, font); //call subroutine y = y - 0.025; sprintf(buf, "Total: %d=100.00%%", allPackets); font = 40; //helvetica-medium-r - see TAttText class write_stats(pad, buf, x, y, align, font); //call subroutine y = y - 0.025; Double_t percent = validPackets * 100.0 / allPackets; sprintf(buf, "Valid: %d=%.2f%%", validPackets, percent); write_stats(pad, buf, x, y, align, font); //call subroutine y = y - 0.05; strcpy(buf, "Number of Routing"); write_stats(pad, buf, x, y, align, font); //call subroutine y = y - 0.025; sprintf(buf, "flaps: %d", noOfFlaps); write_stats(pad, buf, x, y, align, font); //call subroutine //percentiles y = y - 0.03; //draw a line to seperate data on pad //line1.SetLineWidth(2); //line1.SetLineColor(4); line1.DrawLine(0.05, y, 0.95, y); y -=0.04; font = 60; // helvetiva-bold-r - see TAttText class write_stats(pad, "IPDV Percentiles", x, y, align, font); //call subroutine y = y - 0.03; font = 40; //helvetica-medium-r - see TAttText class sprintf(buf, "Mean: %7.2fms", ipdvPercentile->GetLevel(50.0) ); write_stats(pad, buf, x, y, align, font); //call subroutine y = y - 0.025; sprintf(buf, "RMS: %7.2fms", rmsIPDV ); write_stats(pad, buf, x, y, align, font); //call subroutine y = y - 0.025; sprintf(buf, "50%% > %5.1fms", ipdvPercentile->GetLevel(25.0) ); write_stats(pad, buf, x, y, align, font); //call subroutine y = y - 0.025; sprintf(buf, "50%% < %5.1fms", ipdvPercentile->GetLevel(75.0) ); write_stats(pad, buf, x, y, align, font); //call subroutine y = y - 0.025; sprintf(buf, "85.0%% > %5.1fms", ipdvPercentile->GetLevel(7.5) ); write_stats(pad, buf, x, y, align, font); //call subroutine y = y - 0.025; sprintf(buf, "85.0%% < %5.1fms", ipdvPercentile->GetLevel(92.5) ); write_stats(pad, buf, x, y, align, font); //call subroutine y = y - 0.025; sprintf(buf, "97.5%% > %5.1fms", ipdvPercentile->GetLevel(1.25) ); write_stats(pad, buf, x, y, align, font); //call subroutine y = y - 0.025; sprintf(buf, "97.5%% < %5.1fms", ipdvPercentile->GetLevel(98.75) ); write_stats(pad, buf, x, y, align, font); //call subroutine //mean and RMS of delay distribution y = y - 0.03; //draw a line to seperate data on pad //line1.SetLineWidth(2); //line1.SetLineColor(4); line1.DrawLine(0.05, y, 0.95, y); y -=0.04; font = 60; // helvetiva-bold-r - see TAttText class write_stats(pad, "Delay Distribution", x, y, align, font); //call subroutine y = y - 0.025; sprintf(buf, "Mean: %6.2fms", meanDelay); font = 40; //helvetica-medium-r - see TAttText class write_stats(pad, buf, x, y, align, font); //call subroutine y = y - 0.025; sprintf(buf, "RMS: %6.2fms", rmsDelay); write_stats(pad, buf, x, y, align, font); //call subroutine } //********************************************************/ // function name: write_stats // arguments: *pad -> pointer to current pad // *ptr_text -> pointer to string // x -> x-coordinate NTC // y -> y-coordinate NTC // align -> alignment (see ROOT TattText class) // font -> font id (see ROOT TattText class) // purpose: writes 'ptr_text' on 'pad' as TText on // coordinates 'x' and 'y' with alignment // 'align'. Use TLatex-class for a better // correspondence between screen and print-out // in newer versions of ROOT (above 2.21/08) // Author: Johann Gutauer /**************************************************************/ void write_stats(TPad* pad, const char* ptr_text, const Float_t x, const Float_t y, const Short_t align, Font_t font) { pad->cd(); TText *xlabel = new TText(x, y, ptr_text); xlabel -> SetTextFont(font); xlabel -> SetTextColor(1); xlabel -> SetTextSize(0.11); xlabel -> SetTextAlign(align); xlabel->Draw(); return; } //put a set of data from a root file into an a ttmData element void PutDataInElement(Delay* delay, ttmData* element) { element->PacketId = delay->GetPacketId(); element->SourceId = delay->GetSourceId(); // element->SourcePort = delay->GetSourcePort(); element->TargetId = delay->GetTargetId(); // element->TargetPort = delay->GetTargetPort(); // element->PacketSize = delay->GetPacketSize(); element->ArrivalTime = delay->GetArrivalTime(); element->PacketDelay = delay->GetPacketDelay(); // element->SourceClock = delay->GetSourceClock(); // element->TargetClock = delay->GetTargetClock(); element->Nhops = delay->GetNhops(); // element->RouteId = delay->GetRouteId(); element->SourceNtp = delay->GetSourceNtp(); element->TargetNtp = delay->GetTargetNtp(); return; } //sort one list - Bubble Sort void SortList(ttmData* access) { ttmData *worker; //will do most of the work ttmData store; //needed for swapping content of elements ttmData *store_NN = 0; //need for swapping elements ttmData *store_NNN = 0; //need for swapping elements worker = access->next; //begin of list do { //compare first and second element of a list if( (access->next->ArrivalTime) > (access->next->next->ArrivalTime) ) { //exchange the contents of the first and second element //store data of first element for later use store.PacketId = access->next->PacketId; store.SourceId = access->next->SourceId; //store.SourcePort = access->next->SourcePort; store.TargetId = access->next->TargetId; //store.TargetPort = access->next->TargetPort; //store.PacketSize = access->next->PacketSize; store.ArrivalTime = access->next->ArrivalTime; store.PacketDelay = access->next->PacketDelay; //store.SourceClock = access->next->SourceClock; //store.TargetClock = access->next->TargetClock; store.Nhops = access->next->Nhops; //store.RouteId = access->next->RouteId; store.SourceNtp = access->next->SourceNtp; store.TargetNtp = access->next->TargetNtp; //overwrite data of first element access->next->PacketId = access->next->next->PacketId; access->next->SourceId = access->next->next->SourceId; //access->next->SourcePort = access->next->next->SourcePort; access->next->TargetId = access->next->next->TargetId; //access->next->TargetPort = access->next->next->TargetPort; //access->next->PacketSize = access->next->next->PacketSize; access->next->ArrivalTime = access->next->next->ArrivalTime; access->next->PacketDelay = access->next->next->PacketDelay; //access->next->SourceClock = access->next->next->SourceClock; //access->next->TargetClock = access->next->next->TargetClock; access->next->Nhops = access->next->next->Nhops; //access->next->RouteId = access->next->next->RouteId; access->next->SourceNtp = access->next->next->SourceNtp; access->next->TargetNtp = access->next->next->TargetNtp; //overwrite data in second element access->next->next->SourceId = store.SourceId; //access->next->next->SourcePort = store.SourcePort; access->next->next->TargetId = store.TargetId; //access->next->next->TargetPort = store.TargetPort; //access->next->next->PacketSize = store.PacketSize; access->next->next->ArrivalTime = store.ArrivalTime; access->next->next->PacketDelay = store.PacketDelay; //access->next->next->SourceClock = store.SourceClock; //access->next->next->TargetClock = store.TargetClock; access->next->next->Nhops = store.Nhops; //access->next->next->RouteId = store.RouteId; access->next->next->SourceNtp = store.SourceNtp; access->next->next->TargetNtp = store.TargetNtp; } //compare the rest of the list if( (worker->next->ArrivalTime) > (worker->next->next->ArrivalTime) ) { store_NN = worker->next->next; store_NNN = worker->next->next->next; worker->next->next->next = worker->next; worker->next->next = store_NNN; worker->next = store_NN; //and start again worker = access->next; } else { //next element worker = worker->next; } }while(worker->next->next); return; } //if the global variable "MAX" is not sufficient to the number of boxes, this message will be displayed void TerminationMessage() { cout << endl << "insufficent array size in program!!!!" << endl; cout << "there are more testboxes than at "; cout << "the time this program was written" << endl; cout << "please set MAX to a higher value and recompile... " << endl << endl; return; } //calculates delay variation and errors, put it in histograms, fill percentiles void CalculateAndFillHistograms(ttmData* access, TH1F* h1, TH2F* h2, TH2F* h3, TH2F* h4, TH1F* h5, TGraphErrors* ipdvErr, Percentiles* ipdvPercentile, Int_t enableErrorPlot) { //Double_t actError, nextError; //calculate experimantal error of clocks Double_t actError_2, nextError_2; //calculate experimantal error of clocks - no square root is calcualted Double_t ipdvError; //error of whole IPDV calculation (per pair) Double_t ipdVariation; //the IPDV Int_t packetNo = 0; //needed to fill error plot (TGraphErrors will be created in loop) ttmData *helper; //need help helper = access->next; //start with the first element of the list do { //IPDV(i) = D(i+1) - D(i) - according to page 5, // see page 8, where it is defined the other way around-informed the authors ipdVariation = (helper->next->PacketDelay) - (helper->PacketDelay); //fill the histograms h1->Fill(ipdVariation); h2->Fill(((helper->ArrivalTime)-convToRootTime), ipdVariation); h3->Fill(((helper->ArrivalTime)-convToRootTime), helper->PacketDelay); h4->Fill(((helper->ArrivalTime)-convToRootTime), (helper->Nhops) * 10); h5->Fill(helper->PacketDelay); //add (or fill) percentiles ipdvPercentile->Add(ipdVariation); //calculate and fill error diagram - if it has been enabled at data input if(enableErrorPlot > 0) { //calculate errors //expError(packet) = sqrt(SourceNtp^2 + TargetNtp^2) /* actError = sqrt(((helper->SourceNtp)*(helper->SourceNtp)) + ((helper->TargetNtp)*(helper->TargetNtp))); nextError = sqrt(((helper->next->SourceNtp)*(helper->next->SourceNtp)) + ((helper->next->TargetNtp)*(helper->next->TargetNtp))); //ipdvError = sqrt(actError^2 + nextError^2) ipdvError = sqrt((actError * actError) + (nextError * nextError)); */ //calcualte errors - faster version //expError(packet) = sqrt(SourceNtp^2 + TargetNtp^2); ipdvError = sqrt(actError^2 + nextError^2) //no square root on expError, so no "^2" in the ipdvError actError_2 = (((helper->SourceNtp)*(helper->SourceNtp)) + ((helper->TargetNtp)*(helper->TargetNtp))); nextError_2 = (((helper->next->SourceNtp)*(helper->next->SourceNtp)) + ((helper->next->TargetNtp)*(helper->next->TargetNtp))); //ipdvError = sqrt(actError^2 + nextError^2) ipdvError = sqrt(actError_2 + nextError_2); //fill diagram ipdvErr->SetPoint(packetNo, ((helper->ArrivalTime)-convToRootTime), ipdVariation); //point ipdvErr->SetPointError(packetNo, 0.0, ipdvError); //error for this point } helper = helper->next; //get next one packetNo++; }while(helper->next); return; } //set the histogram title, time format, axis title , .... void SetHistogramProperties(TH1F* h1, TH2F* h2, TH2F* h3, TH2F* h4, TH1F* h5, TH2F* dummy, char* xTitle, char* xTimeFormat) { //set properties of the histograms //histogram for IPDV distribution h1->SetMarkerColor(1); //h1->SetFillColor(7); h1->SetFillColor(4); h1->SetStats(kFALSE); //no statisics displayed on histogram h1->GetXaxis()->SetTitle("[ms]"); h1->GetYaxis()->SetTitle("[%] "); //histogram for IPDV vs time h2->SetMarkerColor(4); h2->GetXaxis()->SetTimeDisplay(1); h2->GetXaxis()->SetTimeFormat(xTimeFormat); h2->GetXaxis()->SetTitle(xTitle); h2->GetYaxis()->SetTitle("[ms] "); h2->SetStats(kFALSE); //histograms for hops and delay vs time h3->SetMarkerColor(1); //delay h3->GetXaxis()->SetTimeDisplay(1); h3->GetXaxis()->SetTimeFormat(xTimeFormat); h3->GetXaxis()->SetTitle(xTitle); h3->GetYaxis()->SetTitle("[ms] "); h3->SetStats(kFALSE); h4->SetMarkerColor(2); //nhops h4->SetStats(kFALSE); //histogram for delay ditribution h5->SetMarkerColor(2); h5->SetFillColor(5); h5->SetStats(kFALSE); h5->GetXaxis()->SetTitle("[ms]"); h5->GetYaxis()->SetTitle("[%] "); //set properties of the error diagram axis ("dummy" is a place holder - TGraphErrors will be created in loop) dummy->GetXaxis()->SetTimeDisplay(1); dummy->GetXaxis()->SetTimeFormat(xTimeFormat); dummy->GetXaxis()->SetTitle(xTitle); dummy->GetYaxis()->SetTitle("[ms] "); dummy->SetStats(kFALSE); return; } //draw the histograms on canvas c1 void DrawHistograms(TH1F* h1, TH2F* h2, TH2F* h3, TH2F* h4, TH1F* h5, TPad* pad1, TPad* pad2, TPad* pad3, TPad* pad4, Int_t customIPDVPlot, Int_t customDelayPlot) { //first, declaration of some variables Int_t binNo = 0; //for loops, will contain the number of the bon for zoom operation Double_t limit = 0.0; //exit condition for loop Double_t sumBins = 0.0; //to add up the bins in a loop Double_t scale = 0.0; //factor to normalize the histograms if(customIPDVPlot == 0) //by default - with auto zooming { //dymanic zoom for the IPDV distribution - displays approx. 99% of the contents h1->SetAxisRange(-400, 400); //needs initalisation, but don't know why limit = 0.005 * h1->GetEntries(); // only right half will be analyzed, distribution is symetric sumBins = 0.0; for(binNo=801; binNo > 419 ; binNo--) //801 to get the overflow bin - histogram range is { //from -400 ms to 400 ms sumBins = sumBins + h1->GetBinContent(binNo); if( (sumBins > limit) ) break; } //end finding the dynamic zoom range //normalize the histogram with the IPDV distribution pad1->cd(); scale = 100 / h1->Integral(); //normalize the histogram to percent h1->Scale(scale); h1->SetAxisRange((400-binNo), (binNo-400)); //zoom in - dynamic h1->Draw(); //draw the histogram with IPDVs vs time pad2->cd(); h2->GetYaxis()->SetRange(0, 400); //initialisation - in bin numbers if(binNo < 540) h2->GetYaxis()->SetRange(125, 275); //zoom in on y-axis - numbers are bin numbers h2->Draw(); } else //draw customzed IPDV plots { //normalize the histogram with the IPDV distribution pad1->cd(); scale = 100 / h1->Integral(); //normalize the histogram to percent h1->Scale(scale); h1->Draw(); //draw the histogram with IPDVs vs time pad2->cd(); h2->Draw(); } if(customDelayPlot == 0) //by default - with auto zooming { //normalize and draw the delay distribution pad3->cd(); //dynamic zoom, more than 99% of the values (in the bins) will be displayed - don't know if it is really usefull h5->SetAxisRange(0, 800); //needs initalisation, but don't know why limit = 0.01 * h5->GetEntries(); sumBins = 0.0; for(binNo=801; binNo > 29; binNo--) //801 to get the overflow bin { sumBins = sumBins + h5->GetBinContent(binNo); if( (sumBins > limit) ) break; } //end finding the dynamic zoom range scale = 100 / h5->Integral(); //normalize the histogram to percents h5->Scale(scale); if(binNo < 750) h5->SetAxisRange(0, (binNo + 10) ); //zoom in - dynamic h5->Draw(); //draw the histograms with the delays(h3) and hops(h4) pad4->cd(); h3->GetYaxis()->SetRange(0, 400); //initialisation - numbers are bin numbers if(binNo < 250) //zoom in - dynamic h3->GetYaxis()->SetRange(0, 125); //zoom in on y-axis - bin numbers else h3->GetYaxis()->SetRange(0, 200); //zoom in on y-axis - bin numbers if(binNo > 390) //just in case h3->GetYaxis()->SetRange(0, 400); //numbers are bin numbers h3->Draw(); h4->Draw("sameP"); } else //customized delay plots { //normalize and draw the delay distribution pad3->cd(); scale = 100 / h5->Integral(); //normalize the histogram to percents h5->Scale(scale); h5->Draw(); //draw the histograms with the delays(h3) and hops(h4) pad4->cd(); h3->Draw(); h4->Draw("sameP"); } return; } //draw the error plot on canvas c2 void DrawErrorPlot(TH2F* dummy, TGraphErrors* ipdvErr, TPad* errorPad) { errorPad->cd(); dummy->Draw(); //just to get the axes ipdvErr->SetMarkerStyle(25); ipdvErr->SetMarkerSize(0.25); ipdvErr->SetMarkerColor(3); ipdvErr->Draw("P"); //drawn without axes return; } //extract the data from the command line so the calculation can be done //inspired by Rene's program getargs.C void ExtractInput(int argc, char** argv, Int_t* start_Box, Int_t* end_Box, time_t& endtime, time_t& duration, Int_t* enableErrorPlot, Int_t* outputFormat, Int_t* enableDateInFileName, Int_t* savePercentiles, Int_t* createHTML, Int_t* printVerbose, char* outputDir, Int_t* maxIPDV, Int_t* customIPDVPlot, Int_t* maxCustomDelay, Int_t* minCustomDelay, Int_t* customDelayPlot) { Int_t noOfEntries = 0; //to make sure all mandatory switches are present char ch; char * pEnd; //needed for conversion, see documentation of "strtod" and "strtol" Int_t helpme = 0; extern char *optarg; //check if any (or too litle) arguments are there if(argc <= 1) { cout << "program to calculate delay variations (aka.: IPDV, Jitter)" << endl; cout << "use the option --help to get help" << endl; //friendly advise cout << "usage: " << progname << " -s startbox -p starttime/endtime [-t target] [-o outputdir] [-f format] [-m max. delay] [-z min.delay] [-u max. IPDV] [-v] [-6] [-r] [-h] [-e] [-d]" << endl; //friendly advise exit(0); } while ((ch = getopt(argc, argv, "46rdehv?s:t:f:m:o:p:u:z:-:")) != -1) switch(ch) { case '4': ipVersion = 4; break; case '6': ipVersion = 6; break; case 's': //*start_Box = (Int_t) strtol (optarg, &pEnd, 0); //convert string into number (long) sscanf(optarg, "%d", start_Box); noOfEntries++; break; case 't': //*end_Box = (Int_t) strtol (optarg, &pEnd, 0); //convert string into number sscanf(optarg, "%d", end_Box); break; case 'd': *enableDateInFileName = 1; break; case 'e': *enableErrorPlot = 1; break; case 'f': *outputFormat = 0; //default (=gif) - gif=0, ps=1, eps=2, pdf=3 if(strncmp(optarg, "ps", 2) == 0) *outputFormat = 1; if(strncmp(optarg, "eps", 3) == 0) *outputFormat = 2; if(strncmp(optarg, "pdf", 3) == 0) *outputFormat = 3; break; case 'm': //max. delay in histogram *maxCustomDelay = (Int_t) strtod (optarg, &pEnd); //convert string into number *customDelayPlot = 1; break; case 'z': //min. delay in histogram *minCustomDelay = (Int_t) strtod (optarg, &pEnd); //convert string into number *customDelayPlot = 1; break; case 'u': //max. IPDV in histogram *maxIPDV = (Int_t) strtod (optarg, &pEnd); //convert string into number *customIPDVPlot = 1; break; case 'o': strcpy(outputDir, optarg); //make sure that string ends with '/' , otherwise add it to the string if(((strrchr(outputDir, '/'))-outputDir+1 ) != (strlen(outputDir)) ) strcat(outputDir, "/"); //add "/" if it is not already there break; case 'p': decodePeriod(optarg, endtime, duration); //call function in "iso8601.h" to decode time noOfEntries++; break; case 'r': *savePercentiles = 1; break; case 'h': *createHTML = 1; break; case 'v': *printVerbose = 1; break; case '?': //this part is a bit dirty, but should be sufficent for the beginning case '-': //to get --help default: cout << "program to calculate delay variations (aka.: IPDV, Jitter)" << endl; cout << "options: " << "-s source box no." << endl; cout << " " << "-p start time and end time, format is YYYYMMDD/YYYYMMDD," << endl; cout << " " << " any ISO 8601 format is ok, like YYYYMMDDThh:mm:ss or similar" << endl; cout << endl; cout << " " << "-t target box no., -1 for all connected boxes, optional, default = -1" << endl; cout << endl; cout << " " << "-d date in file name (ipdv.tt_A.tt_B.start.end.*), optional, default = 'off' " << endl; cout << " " << "-f output file format, 'gif', 'ps', 'pdf' or 'eps', optional, default = 'gif' " << endl; cout << " " << "-m maximum delay in ms to be displayed on histograms, optional" << endl; cout << " " << "-z minimum delay in ms to be displayed on histograms, optional" << endl; cout << " " << "-u maximum IPDV in ms to be displayed on histograms, optional" << endl; cout << " " << "-o output directory for plots, optional, default is current directory " << endl; cout << " " << "-r write percentiles to a file, optional, default = 'off' " << endl; cout << " " << "-h create a HTML file for the webpage, optional, default = 'off' " << endl; cout << " " << "-e enable error plot, optional, default = 'off' " << endl; cout << " " << "-v verbose, 'on' or 'off', optional, default = 'off' " << endl; exit(0); } if(noOfEntries < 2) { cout << "insuffucent input or misstyped a switch" << endl; cout << "use the option --help to get help" << endl; //friendly advise exit(0); } return; } //create a HTML file to link the plots from the web site - if it has been selected at data input void CreateHTMLFile(Percentiles* ipdvPercentile, char* anyString, Int_t boxNo, ofstream &IntoFile, Int_t paragraphID) { //paragraphID = 0 - write the first line of the file if(paragraphID == 0) { IntoFile << "" << endl << "" << endl; IntoFile << "Delay Variations - Testbox " << anyString << "" << endl; IntoFile << "" << endl << "" << endl; IntoFile << "

from Testbox " << anyString << " to

" << endl; //IntoFile << "" << endl; //Opera doesn't like this style IntoFile << "
" << endl; IntoFile << "" << endl; //write table headline IntoFile << ""; IntoFile << ""; //box number IntoFile << ""; //mean IntoFile << ""; //50% lower edge IntoFile << ""; //50% upper edge IntoFile << ""; //85% lower edge IntoFile << ""; //85% upper edge IntoFile << ""; //97.5% lower edge IntoFile << ""; //97.5% upper edge IntoFile << "" << endl; } //paragraphID = 1 - add a line of data to the file if(paragraphID == 1) { //manipulate file name from *.eps to *.gif char buffer[250] = ""; strncat(buffer, anyString, (strlen(anyString)-2) ); //cut "ps" from filename strcat(buffer, "gif"); //and add "gif" //create a line for the table on the website IntoFile << ""; IntoFile << ""; //box number IntoFile << ""; //mean IntoFile << ""; //50% lower edge IntoFile << ""; //50% upper edge IntoFile << ""; //85% lower edge IntoFile << ""; //85% upper edge IntoFile << ""; //97.5% lower edge IntoFile << ""; //97.5% upper edge IntoFile << "" << endl; } //paragraphID = 2 - add last line to the file if(paragraphID == 2) { IntoFile << "
ttmean50%
lower edge
50%
upper edge
85%
lower edge
85%
upper edge
97.5%
lower edge
97.5%
upper edge
" << boxNo << "" << ipdvPercentile->GetLevel(50.0) << " ms" << ipdvPercentile->GetLevel(25.0) << " ms" << ipdvPercentile->GetLevel(75.0) << " ms" << ipdvPercentile->GetLevel(7.5) << " ms" << ipdvPercentile->GetLevel(92.5) << " ms" << ipdvPercentile->GetLevel(1.25) << " ms" << ipdvPercentile->GetLevel(98.75) << " ms
" << endl << "" << endl << "" << endl; } return; } //write percentiles to file - if it has been selected at data input void SavePercentiles(Percentiles* ipdvPercentile, char* SourceBox, Int_t boxNo, ofstream &IntoFile, Int_t firstLine) { //firstLine = 1 - write the first line of the file if(firstLine > 0) { IntoFile << "# percentiles of all boxes connected to testbox tt " << SourceBox << endl << endl; IntoFile << "#testbox mean 50%_lower 50%_upper 85%_lower 85%_upper 97.5%_lower 97.5%_upper" << endl <GetLevel(50.0); //mean IntoFile.width(10); IntoFile << ipdvPercentile->GetLevel(25.0); //50% lower edge IntoFile.width(10); IntoFile << ipdvPercentile->GetLevel(75.0); //50% upper edge IntoFile.width(10); IntoFile << ipdvPercentile->GetLevel(7.5); //85% lower edge IntoFile.width(10); IntoFile << ipdvPercentile->GetLevel(92.5); //85% upper edge IntoFile.width(10); IntoFile << ipdvPercentile->GetLevel(1.25); //97.5% lower edge IntoFile.width(10); IntoFile << ipdvPercentile->GetLevel(98.75); //97.5% upper edge IntoFile << endl; } return; } //check if the parameters for the custom plots make sense (and the program doesn't crash) Int_t CheckPlotParameter(Int_t* maxIPDV, Int_t* maxCustomDelay, Int_t* minCustomDelay) { /* *maxIPDV > 0 *minCustomDelay >= 0 *minCustomDelay < *maxCustomDelay */ if( (*maxIPDV > 0) && (*minCustomDelay >= 0) && (*minCustomDelay < *maxCustomDelay) ) return(1); else //program should run with these values (hope so) { if(*maxIPDV < 0) *maxIPDV = 2; if(*minCustomDelay < 0) *minCustomDelay = 0; if(*maxCustomDelay < *minCustomDelay) *maxCustomDelay = *minCustomDelay + 10; //no better ideas } return(1); }