X-Git-Url: https://git.verplant.org/?a=blobdiff_plain;f=src%2Frrd_graph.c;h=97f6d68bb7241f6ae7c819e84447f3ffb8785fa8;hb=afdb6e580328bfea9570035f4d30e1c80ba6034a;hp=a06417e3aecccae9c03df913dea3ea27eca29f1b;hpb=5a3f89cd664c1c7ccf9cbffe3422e42e87bff7c3;p=rrdtool.git diff --git a/src/rrd_graph.c b/src/rrd_graph.c index a06417e..97f6d68 100644 --- a/src/rrd_graph.c +++ b/src/rrd_graph.c @@ -1,5 +1,5 @@ /**************************************************************************** - * RRDtool 1.2.10 Copyright by Tobi Oetiker, 1997-2005 + * RRDtool 1.2.11 Copyright by Tobi Oetiker, 1997-2005 **************************************************************************** * rrd__graph.c produce graphs from data in rrdfiles ****************************************************************************/ @@ -42,22 +42,25 @@ text_prop_t text_prop[] = { }; xlab_t xlab[] = { - {0, TMT_SECOND,30, TMT_MINUTE,5, TMT_MINUTE,5, 0,"%H:%M"}, - {2, TMT_MINUTE,1, TMT_MINUTE,5, TMT_MINUTE,5, 0,"%H:%M"}, - {5, TMT_MINUTE,2, TMT_MINUTE,10, TMT_MINUTE,10, 0,"%H:%M"}, - {10, TMT_MINUTE,5, TMT_MINUTE,20, TMT_MINUTE,20, 0,"%H:%M"}, - {30, TMT_MINUTE,10, TMT_HOUR,1, TMT_HOUR,1, 0,"%H:%M"}, - {60, TMT_MINUTE,30, TMT_HOUR,2, TMT_HOUR,2, 0,"%H:%M"}, - {180, TMT_HOUR,1, TMT_HOUR,6, TMT_HOUR,6, 0,"%H:%M"}, - /*{300, TMT_HOUR,3, TMT_HOUR,12, TMT_HOUR,12, 12*3600,"%a %p"}, this looks silly*/ - {600, TMT_HOUR,6, TMT_DAY,1, TMT_DAY,1, 24*3600,"%a"}, - {1800, TMT_HOUR,12, TMT_DAY,1, TMT_DAY,2, 24*3600,"%a"}, - {3600, TMT_DAY,1, TMT_WEEK,1, TMT_WEEK,1, 7*24*3600,"Week %V"}, - {3*3600, TMT_WEEK,1, TMT_MONTH,1, TMT_WEEK,2, 7*24*3600,"Week %V"}, - {6*3600, TMT_MONTH,1, TMT_MONTH,1, TMT_MONTH,1, 30*24*3600,"%b"}, - {48*3600, TMT_MONTH,1, TMT_MONTH,3, TMT_MONTH,3, 30*24*3600,"%b"}, - {10*24*3600, TMT_YEAR,1, TMT_YEAR,1, TMT_YEAR,1, 365*24*3600,"%y"}, - {-1,TMT_MONTH,0,TMT_MONTH,0,TMT_MONTH,0,0,""} + {0, 0, TMT_SECOND,30, TMT_MINUTE,5, TMT_MINUTE,5, 0,"%H:%M"}, + {2, 0, TMT_MINUTE,1, TMT_MINUTE,5, TMT_MINUTE,5, 0,"%H:%M"}, + {5, 0, TMT_MINUTE,2, TMT_MINUTE,10, TMT_MINUTE,10, 0,"%H:%M"}, + {10, 0, TMT_MINUTE,5, TMT_MINUTE,20, TMT_MINUTE,20, 0,"%H:%M"}, + {30, 0, TMT_MINUTE,10, TMT_HOUR,1, TMT_HOUR,1, 0,"%H:%M"}, + {60, 0, TMT_MINUTE,30, TMT_HOUR,2, TMT_HOUR,2, 0,"%H:%M"}, + {180, 0, TMT_HOUR,1, TMT_HOUR,6, TMT_HOUR,6, 0,"%H:%M"}, + {180, 1*24*3600, TMT_HOUR,1, TMT_HOUR,6, TMT_HOUR,6, 0,"%a %H:%M"}, + /*{300, 0, TMT_HOUR,3, TMT_HOUR,12, TMT_HOUR,12, 12*3600,"%a %p"}, this looks silly*/ + {600, 0, TMT_HOUR,6, TMT_DAY,1, TMT_DAY,1, 24*3600,"%a"}, + {600, 1*24*3600, TMT_HOUR,6, TMT_DAY,1, TMT_DAY,1, 24*3600,"%a %d"}, + {1800, 0, TMT_HOUR,12, TMT_DAY,1, TMT_DAY,2, 24*3600,"%a"}, + {1800, 1*24*3600, TMT_HOUR,12, TMT_DAY,1, TMT_DAY,2, 24*3600,"%a %d"}, + {3600, 0, TMT_DAY,1, TMT_WEEK,1, TMT_WEEK,1, 7*24*3600,"Week %V"}, + {3*3600, 0, TMT_WEEK,1, TMT_MONTH,1, TMT_WEEK,2, 7*24*3600,"Week %V"}, + {6*3600, 0, TMT_MONTH,1, TMT_MONTH,1, TMT_MONTH,1, 30*24*3600,"%b"}, + {48*3600, 0, TMT_MONTH,1, TMT_MONTH,3, TMT_MONTH,3, 30*24*3600,"%b"}, + {10*24*3600, 0, TMT_YEAR,1, TMT_YEAR,1, TMT_YEAR,1, 365*24*3600,"%y"}, + {-1,0,TMT_MONTH,0,TMT_MONTH,0,TMT_MONTH,0,0,""} }; /* sensible logarithmic y label intervals ... @@ -688,7 +691,7 @@ data_fetch(image_desc_t *im ) int i,ii; int skip; - /* pull the data from the log files ... */ + /* pull the data from the rrd files ... */ for (i=0;i< (int)im->gdes_c;i++){ /* only GF_DEF elements fetch data */ if (im->gdes[i].gf != GF_DEF) @@ -702,9 +705,9 @@ data_fetch(image_desc_t *im ) if ((strcmp(im->gdes[i].rrd, im->gdes[ii].rrd) == 0) && (im->gdes[i].cf == im->gdes[ii].cf) && (im->gdes[i].cf_reduce == im->gdes[ii].cf_reduce) - && (im->gdes[i].start == im->gdes[ii].start) - && (im->gdes[i].end == im->gdes[ii].end) - && (im->gdes[i].step == im->gdes[ii].step)) { + && (im->gdes[i].start_orig == im->gdes[ii].start_orig) + && (im->gdes[i].end_orig == im->gdes[ii].end_orig) + && (im->gdes[i].step_orig == im->gdes[ii].step_orig)) { /* OK, the data is already there. ** Just copy the header portion */ @@ -1768,7 +1771,9 @@ vertical_grid( factor=(im->end - im->start)/im->xsize; xlab_sel=0; while ( xlab[xlab_sel+1].minsec != -1 - && xlab[xlab_sel+1].minsec <= factor){ xlab_sel++; } + && xlab[xlab_sel+1].minsec <= factor) { xlab_sel++; } /* pick the last one */ + while ( xlab[xlab_sel-1].minsec == xlab[xlab_sel].minsec + && xlab[xlab_sel].length > (im->end - im->start)) { xlab_sel--; } /* go back to the smallest size */ im->xlab_user.gridtm = xlab[xlab_sel].gridtm; im->xlab_user.gridst = xlab[xlab_sel].gridst; im->xlab_user.mgridtm = xlab[xlab_sel].mgridtm; @@ -2242,13 +2247,12 @@ graph_size_location(image_desc_t *im, int elements /* reserve space for main and/or pie */ im->yimg = Ymain + Yxlabel; - + #ifdef WITH_PIECHART if (im->yimg < Ypie) im->yimg = Ypie; #endif im->yorigin = im->yimg - Yxlabel; - ytr(im,DNAN); /* reserve space for the title *or* some padding above the graph */ if (Ytitle) { @@ -2260,7 +2264,7 @@ graph_size_location(image_desc_t *im, int elements } /* reserve space for padding below the graph */ im->yimg += Yspacing; - + /* Determine where to place the legends onto the image. ** Adjust im->yimg to match the space requirements. */ @@ -2289,6 +2293,40 @@ graph_size_location(image_desc_t *im, int elements } #endif + ytr(im,DNAN); + return 0; +} + +/* from http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm */ +/* yes we are loosing precision by doing tos with floats instead of doubles + but it seems more stable this way. */ + +static int AlmostEqual2sComplement (float A, float B, int maxUlps) +{ + + int aInt = *(int*)&A; + int bInt = *(int*)&B; + int intDiff; + /* Make sure maxUlps is non-negative and small enough that the + default NAN won't compare as equal to anything. */ + + /* assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024); */ + + /* Make aInt lexicographically ordered as a twos-complement int */ + + if (aInt < 0) + aInt = 0x80000000l - aInt; + + /* Make bInt lexicographically ordered as a twos-complement int */ + + if (bInt < 0) + bInt = 0x80000000l - bInt; + + intDiff = abs(aInt - bInt); + + if (intDiff <= maxUlps) + return 1; + return 0; } @@ -2472,31 +2510,35 @@ graph_paint(image_desc_t *im, char ***calcpr) if (im->gdes[i].col != 0x0){ /* GF_LINE and friend */ if(stack_gf == GF_LINE ){ + double last_y=0.0; node = NULL; - for(ii=1;iixsize;ii++){ + for(ii=1;iixsize;ii++){ if (isnan(im->gdes[i].p_data[ii]) || (im->slopemode==1 && isnan(im->gdes[i].p_data[ii-1]))){ node = NULL; continue; } if ( node == NULL ) { + last_y = ytr(im,im->gdes[i].p_data[ii]); if ( im->slopemode == 0 ){ node = gfx_new_line(im->canvas, - ii-1+im->xorigin,ytr(im,im->gdes[i].p_data[ii]), - ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]), + ii-1+im->xorigin,last_y, + ii+im->xorigin,last_y, im->gdes[i].linewidth, im->gdes[i].col); } else { node = gfx_new_line(im->canvas, ii-1+im->xorigin,ytr(im,im->gdes[i].p_data[ii-1]), - ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]), + ii+im->xorigin,last_y, im->gdes[i].linewidth, im->gdes[i].col); } } else { - if ( im->slopemode==0 ){ - gfx_add_point(node,ii-1+im->xorigin,ytr(im,im->gdes[i].p_data[ii])); + double new_y = ytr(im,im->gdes[i].p_data[ii]); + if ( im->slopemode==0 && ! AlmostEqual2sComplement(new_y,last_y,4)){ + gfx_add_point(node,ii-1+im->xorigin,new_y); }; - gfx_add_point(node,ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii])); + last_y = new_y; + gfx_add_point(node,ii+im->xorigin,new_y); }; } @@ -2512,7 +2554,7 @@ graph_paint(image_desc_t *im, char ***calcpr) if ( idxI > 0 && ( drawem != 0 || ii==im->xsize)){ int cntI=1; int lastI=0; - while (cntI < idxI && foreY[lastI] == foreY[cntI] && foreY[lastI] == foreY[cntI+1]){cntI++;} + while (cntI < idxI && AlmostEqual2sComplement(foreY[lastI],foreY[cntI],4) && AlmostEqual2sComplement(foreY[lastI],foreY[cntI+1],4)){cntI++;} node = gfx_new_area(im->canvas, backX[0],backY[0], foreX[0],foreY[0], @@ -2520,14 +2562,14 @@ graph_paint(image_desc_t *im, char ***calcpr) while (cntI < idxI) { lastI = cntI; cntI++; - while ( cntI < idxI && foreY[lastI] == foreY[cntI] && foreY[lastI] == foreY[cntI+1]){cntI++;} + while ( cntI < idxI && AlmostEqual2sComplement(foreY[lastI],foreY[cntI],4) && AlmostEqual2sComplement(foreY[lastI],foreY[cntI+1],4)){cntI++;} gfx_add_point(node,foreX[cntI],foreY[cntI]); } gfx_add_point(node,backX[idxI],backY[idxI]); while (idxI > 1){ lastI = idxI; idxI--; - while ( idxI > 1 && backY[lastI] == backY[idxI] && backY[lastI] == backY[idxI-1]){idxI--;} + while ( idxI > 1 && AlmostEqual2sComplement(backY[lastI], backY[idxI],4) && AlmostEqual2sComplement(backY[lastI],backY[idxI-1],4)){idxI--;} gfx_add_point(node,backX[idxI],backY[idxI]); } idxI=-1; @@ -2839,6 +2881,7 @@ rrd_graph_init(image_desc_t *im) im->xsize = 400; im->ysize = 100; im->step = 0; + im->step_orig = 0; im->ylegend[0] = '\0'; im->title[0] = '\0'; im->minval = DNAN; @@ -3182,7 +3225,7 @@ rrd_graph_options(int argc, char *argv[],image_desc_t *im) case 'n':{ char prop[15]; double size = 1; - char font[1024]; + char font[1024] = ""; if(sscanf(optarg, "%10[A-Z]:%lf:%1000s", @@ -3402,6 +3445,9 @@ char *str; else if (!strcmp("TOTAL", func)) gdes->vf.op = VDEF_TOTAL; else if (!strcmp("FIRST", func)) gdes->vf.op = VDEF_FIRST; else if (!strcmp("LAST", func)) gdes->vf.op = VDEF_LAST; + else if (!strcmp("LSLSLOPE", func)) gdes->vf.op = VDEF_LSLSLOPE; + else if (!strcmp("LSLINT", func)) gdes->vf.op = VDEF_LSLINT; + else if (!strcmp("LSLCORREL",func)) gdes->vf.op = VDEF_LSLCORREL; else { rrd_set_error("Unknown function '%s' in VDEF '%s'\n" ,func @@ -3437,6 +3483,9 @@ char *str; case VDEF_TOTAL: case VDEF_FIRST: case VDEF_LAST: + case VDEF_LSLSLOPE: + case VDEF_LSLINT: + case VDEF_LSLCORREL: if (isnan(param)) { gdes->vf.param = DNAN; gdes->vf.val = DNAN; @@ -3595,6 +3644,48 @@ printf("DEBUG: %3li:%10.2f %c\n",step,array[step],step==field?'*':' '); dst->vf.when = src->start + (step+1)*src->step; } break; + case VDEF_LSLSLOPE: + case VDEF_LSLINT: + case VDEF_LSLCORREL:{ + /* Bestfit line by linear least squares method */ + + int cnt=0; + double SUMx, SUMy, SUMxy, SUMxx, SUMyy, slope, y_intercept, correl ; + SUMx = 0; SUMy = 0; SUMxy = 0; SUMxx = 0; SUMyy = 0; + + for (step=0;stepds_cnt])) { + cnt++; + SUMx += step; + SUMxx += step * step; + SUMxy += step * data[step*src->ds_cnt]; + SUMy += data[step*src->ds_cnt]; + SUMyy += data[step*src->ds_cnt]*data[step*src->ds_cnt]; + }; + } + + slope = ( SUMx*SUMy - cnt*SUMxy ) / ( SUMx*SUMx - cnt*SUMxx ); + y_intercept = ( SUMy - slope*SUMx ) / cnt; + correl = (SUMxy - (SUMx*SUMy)/cnt) / sqrt((SUMxx - (SUMx*SUMx)/cnt)*(SUMyy - (SUMy*SUMy)/cnt)); + + if (cnt) { + if (dst->vf.op == VDEF_LSLSLOPE) { + dst->vf.val = slope; + dst->vf.when = cnt*src->step; + } else if (dst->vf.op == VDEF_LSLINT) { + dst->vf.val = y_intercept; + dst->vf.when = cnt*src->step; + } else if (dst->vf.op == VDEF_LSLCORREL) { + dst->vf.val = correl; + dst->vf.when = cnt*src->step; + }; + + } else { + dst->vf.val = DNAN; + dst->vf.when = 0; + } + } + break; } return 0; }