/* Copyright (c) 2000 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 : DelayPlots.C Author : Rene Wilhelm Date : 13-OCT-2000 Description : Implementation of TTM DelayPlots class Language Version : C++ OSs Tested : Solaris 2.6 External Programs : ps2gif (which in turn needs ghostscript and imaging tools) $Id: DelayPlots.C,v 1.17 2002/08/09 14:32:08 henk Exp $ ------------------------------------------------------------------------------- */ #define PATH "/ncc/ttpro/data/root" // path to TTM ROOT data storage #define TYPE 'H' // hierarchical (year/month/day) storage #include #include #include #include #include #include #include #include #include "Delay.h" #include "DelayPlots.h" #include "utils.h" static char const rcsid[] = "$Id: DelayPlots.C,v 1.17 2002/08/09 14:32:08 henk Exp $"; void DelayPlots::SetSourceTarget(TestBox src, TestBox **dest) { // initialize source and targets int i; uint k; MaxBoxId=0; source = src; for (i=0; i MaxBoxId) { MaxBoxId = target[i].id; } } // create mapping between testbox id and array index // since active testbox id's not necessarily form a continuum in // the integer number space, this indirection minimizes // overhead in memory allocation id2index = new int[MaxBoxId+1]; // initialize to -1 == no such box in target list for (k=0; k<=MaxBoxId; k++) { id2index[k] = -1; } // now fill in the assignments from the list for (i=0; igetRootChain(); if (rootchain == NULL) { fprintf(stderr, "%s: ERROR: cannot create ROOT data chain!?\n", progname); exit(1); } char *pathname = new char[strlen(outdir)+32*ntargets]; FILE **file = new FILE*[ntargets]; // open all output files, write header line for (tgt=0; tgtSetBranchAddress("delay", &delay); nMeasurements = (Int_t) rootchain->GetEntries(); if (verbose) { cout << "Source Box " << source.id << ": " << nMeasurements << " measurements found" << endl; } // process all delay measurements, add output to right file for (Int_t eventNo=0; eventNo < nMeasurements; eventNo++) { if (rootchain->GetEvent(eventNo) < 0) { // problem reading this "event"? continue; } if (delay ->GetSourceId() == (Int_t) source.id) { nProcessed++; if (((targetId = delay->GetTargetId()) > (int)MaxBoxId) || (targetId < 1) ) { // Not part of the list of targets, or // invalid targetId; silently skip continue; } if ((index = id2index[targetId]) < 0) { // Not part of the list of targets // silently skip continue; } // packetId by convention equals time packet was send Double_t packettime = delay->GetPacketTime(starttime); if ((packettime < starttime) || (packettime > endtime)) { // outside selected time interval, silently skip continue; } fprintf (file[index], "%d,%d,%d,%f,%f,%x,%x,%d,%d,%f,%f\n", delay->GetSourceId(), delay->GetTargetId(), delay->GetPacketSize(), delay->GetArrivalTime(), delay->GetPacketDelay(), delay->GetSourceClock(), delay->GetTargetClock(), delay->GetNhops(), delay->GetRouteId(), delay->GetSourceNtp(), delay->GetTargetNtp()); nAccepted++; } } // close all output files for (tgt=0; tgtgetRootChain(); if (rootchain == NULL) { fprintf(stderr, "%s: ERROR: cannot create ROOT data chain!?\n", progname); exit(1); } Delay *delay = new Delay(); rootchain->SetBranchAddress("delay", &delay); nMeasurements = (Int_t) rootchain->GetEntries(); Int_t nProcessed = 0; for (Int_t eventNo=0; eventNo < nMeasurements; eventNo++) { if (rootchain->GetEvent(eventNo) < 0) { // problem reading this "event"? continue; } if (delay->GetSourceId() == (Int_t) source.id) { processDelay(delay); nProcessed++; } } if (verbose) { cout << "Source Box " << source.id << ": " << nProcessed << " measurements processed" << endl; } } void DelayPlots::processDelay(Delay *delay) { // process one delay measurement: fill histograms and percentiles int nhops, routeid; int targetId, index; if (((targetId = delay->GetTargetId()) > (int)MaxBoxId) || (targetId < 1) ) { // Not part of the list of targets, or invalid targetId; // silently skip return; } if ((index = id2index[targetId]) < 0) { // Not part of the list of targets, silently skip return; } // packetId by convention equals time packet was send Double_t packettime = delay->GetPacketTime(starttime); if ((packettime < starttime) || (packettime > endtime)) { // outside selected time interval, silently skip return; } // transform UNIX timestamp into ROOT timestamp // (i.e. seconds since 1/1/1995 instead of seconds since 1/1/1970) packettime -= ROOT_TIMEZERO; // Fill histograms PacketsSent[index]->Fill(packettime); // number of hops if ((routeid=delay->GetRouteId()) > 0) { nhops=delay->GetNhops(); RoutesNHops[index]->Fill(packettime, (Float_t) 10.0 * nhops); RoutesInfo[index]->Update(routeid, nhops) ; } PacketStatus status = delay->Status(); if (status == ClockValid) { Float_t packetdelay = delay->GetPacketDelay(); Delay2D[index]->Fill(packettime, packetdelay); Delay1D[index]->Fill(packetdelay); ClocksOK[index]->Fill(packettime); // in future perhaps: Add(value, interval); // where interval=(packettime mod delta) DelayPercentile[index]->Add(packetdelay); } else if (status == PacketLost) { PacketsLost[index]->Fill(packettime); } else { if (allpackets) { Float_t packetdelay = delay->GetPacketDelay(); Delay2D[index]->Fill(packettime, packetdelay); Delay1D[index]->Fill(packetdelay); } if (status == ClockSrcValid) { // source ok, target wrong BadTargetClock[index]->Fill(packettime); } else if (status == ClockTrgValid) { // target ok, source wrong BadSourceClock[index]->Fill(packettime); } else if (status == ClockInvalid) { // both clocks wrong BadClocks[index]->Fill(packettime); } else { cout << "ERROR: unknown packet status " << status << "Source Id " << delay->GetSourceId() << " Target Id " << delay->GetTargetId() << endl; } } } void DelayPlots::Plot(plotformat fmt, char *outdir, short lgscale) { // make plots for all targets // files are named /tttt. // = source id, = target_id, // = ps, eps, gif // I'd prefer completely dynamic allocation, but // for now 32 bytes should be enough for filename part logscale = lgscale; TPostScript *psout; char *pathnames = new char[strlen(outdir)+32*ntargets]; char *cmdbuffer = new char[strlen(outdir)+strlen(PS2GIF)+32*ntargets]; // force thin lines in PS output to avoid cluttering histograms gStyle->SetLineScalePS(0.2); for (int tgt=0; tgtRange(25.5, 18.8); // centre on A4 size paper } PlotHistos(tgt, target[tgt].id); // now update canvas (needed to write to PS file?) canvas->Update(); psout->Close(); delete psout; delete canvas; } if (fmt == gif) { // now convert them to gif; create ps2gif command line which // specificies all .ps files that need converting. // // BoundingBox parameters are taken from a manual run of the // bbfig tool; will be fine as long as ROOT PostScript prolog // does not change. // // rotate to get proper orientation // scale up a bit to make text more legible // char *newname = pathnames; for (int tgt=0; tgt cleanup ps (they can be real large) sprintf(cmdbuffer, "cd %s;%s %s", outdir, "/bin/rm ", pathnames); if (verbose) { cerr << "executing " << cmdbuffer << endl; } if (system(cmdbuffer) != 0) { cerr << "ERROR removing ps files? " << pathnames <Draw(); pad2->Draw(); pad3->Draw(); pad4->Draw(); pad5->Draw(); // Top Centre Label sprintf(buf, " Delays from tt%2.2d to tt%2.2d. Start: %s End: %s UTC", source.id, targetId, cstarttime1, cendtime1); TPaveLabel *pl = new TPaveLabel(0.274,0.956,0.748,0.998,buf,"br"); pl->SetTextSize(0.45); pl->Draw(); // Top left pad1->cd(); pad1->SetGridx(); pad1->SetGridy(); if (logscale) { pad1->SetLogy(); } RoutesNHops[index]->Draw(); Delay2D[index]->Draw("SAME"); // Top Right pad2->cd(); pad2->SetGridx(); pad2->SetGridy(); if (logscale) { pad2->SetLogx(); } Delay1D[index]->Draw(); // Bottom Left pad3->cd(); pad3->SetGridx(); pad3->SetGridy(); // capture satistics *before* adding up Histograms! int validpackets = (int) ClocksOK[index]->GetEntries(); int sourceok = (int) BadTargetClock[index]->GetEntries(); int targetok = (int) BadSourceClock[index]->GetEntries(); int badclocks = (int) BadClocks[index]->GetEntries(); int lostpackets = (int) PacketsLost[index]->GetEntries(); // add histograms to create stacked plots (1 bin = multiple data items) BadSourceClock[index]->Add(BadSourceClock[index], ClocksOK[index], 1, 1); BadTargetClock[index]->Add(BadTargetClock[index], BadSourceClock[index], 1, 1); BadClocks[index]->Add(BadClocks[index], BadTargetClock[index], 1, 1); // PacketsSent histo is the background, everything not obscured // by valid/invalid clock histograms indicates packet loss. PacketsSent[index]->Draw(); BadClocks[index]->Draw("SAME"); BadTargetClock[index]->Draw("SAME"); BadSourceClock[index]->Draw("SAME"); ClocksOK[index]->Draw("SAME"); // Bottom Right pad4->cd(); pad4->SetGridx(); pad4->SetGridy(); Arrived->Reset(); Arrived->Add(PacketsSent[index], PacketsLost[index], 1, -1); Arrived->Divide(Arrived, PacketsSent[index], 1, 1); Arrived->Draw(); // Statistics pad5->cd(); // // percentiles for packet loss (using fraction arrived per bin) LossPercentile->Reset(); for(bin=1; bin<=binsX; bin++) { LossPercentile->Add(float(1-Arrived->GetBinContent(bin))); } // write output with TLatex-class in new root-version strcpy(buf, "STATISTICS:"); Float_t x = 0.5; // Starting point Float_t y = 0.95; Int_t align = 22; // horizontally and vertically centered Font_t font = 20; // Times-Roman-bold-r write_stats(pad5, buf, x, y, align, font); y -=0.03; // write_stats(pad5, "Histogram", x, y, align, font); // y -=0.02; write_stats(pad5, "Delay @& Hops:", x, y, align, font); font = 10; // Times-Romand-medium-i y -=0.025; sprintf(buf, "Entries: %g", Delay1D[index]->GetEntries()); write_stats(pad5, buf, x, y, align, font); y -=0.02; sprintf(buf, "Overflow: %g", Delay1D[index]->GetBinContent(binsY+1) ); write_stats(pad5, buf, x, y, align, font); y -=0.02; sprintf(buf, "Underflow: %g", Delay1D[index]->GetBinContent(0) ); write_stats(pad5, buf, x, y, align, font); align = 32; Float_t x1 = 0.52; Float_t x2 = 0.95; y -=0.025; write_stats(pad5, "2.5 Perc:", x1, y, align, font); sprintf(buf, "%8.1fms", DelayPercentile[index]->GetLevel(2.5)); write_stats(pad5, buf, x2, y, align, font); y -=0.02; write_stats(pad5, "Median:", x1, y, align, font); sprintf(buf, "%8.1fms", DelayPercentile[index]->GetLevel(50.0)); write_stats(pad5, buf, x2, y, align, font); y -=0.02; write_stats(pad5, "97.5 Perc:", x1, y, align, font); sprintf(buf, "%8.1fms", DelayPercentile[index]->GetLevel(97.5)); write_stats(pad5, buf, x2, y, align, font); y -=0.02; write_stats(pad5,"Mean:", x1, y, align, font); sprintf(buf, "%8.1fms", Delay1D[index]->GetMean()); write_stats(pad5, buf, x2, y, align, font); y -=0.02; write_stats(pad5,"RMS:", x1, y, align, font); sprintf(buf, "%8.1fms", Delay1D[index]->GetRMS()); write_stats(pad5, buf, x2, y, align, font); align=22; y -=0.025; sprintf(buf, "Min. hops: %d", RoutesInfo[index]->GetMinHops()); write_stats(pad5, buf, x, y, align, font); y -=0.02; sprintf(buf, "Max. hops: %d", RoutesInfo[index]->GetMaxHops()); write_stats(pad5, buf, x, y, align, font); y -=0.03; 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; font = 20; // Times-Roman-bold-r write_stats(pad5, "Packets sent/valid:", x, y, align, font); y -=0.025; font = 10; // Times-Romand-medium-i int totalsent = (int) PacketsSent[index]->GetEntries(); sprintf( buf, "Total: %i", totalsent); write_stats(pad5, buf, x, y, align, font); y -=0.02; float percent = validpackets * 100.0 / totalsent; sprintf( buf, "Valid: %i = %.3g %%", validpackets, percent); write_stats(pad5, buf, x, y, align, font); y -=0.02; percent = targetok * 100.0 / totalsent; sprintf( buf, "Send bad: %i = %.2g %%", targetok, percent); write_stats(pad5, buf, x, y, align, font); y -=0.02; percent = sourceok * 100.0 / totalsent; sprintf( buf, "Recv bad: %i = %.2g %%", sourceok, percent); write_stats(pad5, buf, x, y, align, font); y -=0.02; percent = badclocks * 100.0 / totalsent; sprintf( buf, "2 Clocks bad: %i = %.2g %%", badclocks, percent); write_stats(pad5, buf, x, y, align, font); y -=0.02; percent = lostpackets * 100.0 / totalsent; sprintf( buf, "Lost: %i = %.2g %%", lostpackets, percent); write_stats(pad5, buf, x, y, align, font); y -=0.03; line1.SetLineColor(2); line1.DrawLine(0.05, y, 0.95, y); y -=0.04; font = 20; // Times-Roman-bold-r write_stats(pad5,"Packets lost:", x, y, align, font); font = 10; // Times-Romand-medium-i align = 32; // right justified x1 += 0.03; x2 -= 0.03; y -=0.025; write_stats(pad5, "2.5 Perc:", x1, y, align, font); sprintf(buf, "%5.1f%%", 100 * LossPercentile->GetLevel(2.5)); write_stats(pad5, buf, x2, y, align, font); y -=0.02; write_stats(pad5, "Median:", x1, y, align, font); sprintf(buf, "%5.1f%%", 100 * LossPercentile->GetLevel(50.0)); write_stats(pad5, buf, x2, y, align, font); y -=0.02; write_stats(pad5, "97.5 Perc:", x1, y, align, font); sprintf(buf, "%5.1f%%", 100 * LossPercentile->GetLevel(97.5)); write_stats(pad5, buf, x2, y, align, font); // estimate of sending process uptime int count = 0; for (bin = 1; bin <= binsX; bin++) { if (PacketsSent[index]->GetBinContent(bin) > 0) { count++; } } float uptime = count*100/(float)binsX; y -=0.025; align = 22; // centered sprintf(buf, "Uptime: %.3g %%", uptime); write_stats(pad5, buf, x, y, align, font); y -=0.03; line1.SetLineColor(3); line1.DrawLine(0.05, y, 0.95, y); y -=0.04; font = 20; // Times-Roman-bold-r write_stats(pad5, "Over-all statistic:", x, y, align, font); font = 10; // Times-Romand-medium-i y -=0.01; // y -=0.02; // write_stats(pad5, "Number of measurements", x, y, align, font); // y -=0.02; // sprintf( buf, "in period: %i", nMeasurements); // write_stats(pad5, buf, x, y, align, font); y -=0.02; Float_t days = ((endtime-starttime)/86400); if (days > 1) { sprintf( buf, "Time period: %.2g days", days); } else { sprintf( buf, "Time period: %.2g day", days); } write_stats(pad5, buf, x, y, align, font); y -=0.035; write_stats(pad5, "Number of routing", x, y, align, font); y -=0.02; sprintf( buf, "vectors: %i", RoutesInfo[index]->GetEntries()); write_stats(pad5, buf, x, y, align, font); y -=0.02; sprintf( buf, "flaps: %i", RoutesInfo[index]->GetNumFlaps()); write_stats(pad5, buf, x, y, align, font); y -=0.035; sprintf( buf, "Number of bins: %d", binsX ); write_stats(pad5, buf, x, y, align, font); y -=0.02; sprintf( buf, "Minutes/bin: %.3g", ((endtime-starttime)/binsX/60.0)); write_stats(pad5, buf, x, y, align, font); } void DelayPlots::CreateHistos() { char buf[100]; // small buffer for character strings char xtitle[100]; // buffer for x-axis title char *timeformat; // indicates how time on axis is formatted Delay2D = new TH2F*[ntargets]; Delay1D = new TH1F*[ntargets]; PacketsSent = new TH1F*[ntargets]; ClocksOK = new TH1F*[ntargets]; BadSourceClock = new TH1F*[ntargets]; BadTargetClock = new TH1F*[ntargets]; BadClocks = new TH1F*[ntargets]; RoutesNHops = new TH2F*[ntargets]; PacketsLost = new TH1F*[ntargets]; RoutesInfo = new RoutingInfo*[ntargets]; DelayPercentile = new Percentiles*[ntargets]; binsY = 250; // Some initialisation based on time period int ndiv = 0; int ndays = (endtime - starttime) / 3600 / 24; if (ndays>3) { timeformat = "%b %d"; // strcpy(xtitle, "Time [month day]"); if (ndays>7) { ndiv = -506; // 6 major divisions } } else { timeformat = "%H:%M"; // : sprintf(xtitle, " %s%26cTime [hour:minute]%26c%s", cstarttime2, ' ', ' ', cendtime2); } // transform UNIX timestamps into ROOT timestamps // (seconds since 1/1/1995 instead of seconds since 1/1/1970) time_t endtime_root = endtime - ROOT_TIMEZERO; time_t starttime_root = starttime - ROOT_TIMEZERO; for (int i=0; iSetFillColor(5); // yellow Delay1D[i]->SetXTitle("Delay [msec]"); Delay1D[i]->SetYTitle("# packets [Entries/bin]"); // Force division of "delay" scale in 1D and 2D histograms // ROOT 3.0 default behaviour is not good Delay1D[i]->SetNdivisions(-505, "x"); RoutesNHops[i]->SetNdivisions(-505, "y"); RoutesNHops[i]->SetMarkerSize(2); RoutesNHops[i]->SetMarkerColor(2); RoutesNHops[i]->SetXTitle(xtitle); RoutesNHops[i]->SetYTitle("Delay [msec]"); if (ndiv != 0) { RoutesNHops[i]->SetNdivisions(ndiv,"x"); } RoutesNHops[i]->GetXaxis()->SetTimeDisplay(1); RoutesNHops[i]->GetXaxis()->SetTimeFormat(timeformat); PacketsSent[i]->SetFillColor(7); // cyan PacketsSent[i]->SetLineColor(1); // black PacketsSent[i]->SetXTitle(xtitle); PacketsSent[i]->SetYTitle("# packets [Entries/bin]"); if (ndiv != 0) { PacketsSent[i]->SetNdivisions(ndiv,"x"); } PacketsSent[i]->GetXaxis()->SetTimeDisplay(1); PacketsSent[i]->GetXaxis()->SetTimeFormat(timeformat); BadClocks[i]->SetFillColor(2); // red BadClocks[i]->SetLineColor(1); // black BadTargetClock[i]->SetFillColor(5); // yellow BadTargetClock[i]->SetLineColor(1); // black BadSourceClock[i]->SetFillColor(6); // magenta BadSourceClock[i]->SetLineColor(1); // black ClocksOK[i]->SetFillColor(3); // green ClocksOK[i]->SetLineColor(1); // black } // finally one temporary histogram, reused for every source-target plot Arrived = new TH1F("arrrived", "Packets arrived/lost", binsX, starttime_root, endtime_root); Arrived->SetXTitle(xtitle); Arrived->SetYTitle("fraction arrived"); Arrived->SetFillColor(3); // green if (ndiv != 0) { Arrived->SetNdivisions(ndiv,"x"); } Arrived->GetXaxis()->SetTimeDisplay(1); Arrived->GetXaxis()->SetTimeFormat(timeformat); } void DelayPlots::ClearHistos () { // Reset all histograms and percentiles for (int i=0; iReset(); Delay1D[i]->Reset(); PacketsSent[i]->Reset(); ClocksOK[i]->Reset(); BadSourceClock[i]->Reset(); BadTargetClock[i]->Reset(); BadClocks[i]->Reset(); RoutesNHops[i]->Reset(); PacketsLost[i]->Reset(); RoutesInfo[i]->Reset(); DelayPercentile[i]->Reset(); } } void DelayPlots::DeleteHistos () { // delete allocated histograms, reset pointers to NULL, scalars to 0 if (ntargets != 0) { // if ntargets==0, no histograms exist for (int i=0; iSetOptStat(0); // turn off statistics box } //********************************************************/ // 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 DelayPlots::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; }