/****************************************************************************
- * RRDtool 1.2.11 Copyright by Tobi Oetiker, 1997-2005
+ * RRDtool 1.2.12 Copyright by Tobi Oetiker, 1997-2005
****************************************************************************
* rrd__graph.c produce graphs from data in rrdfiles
****************************************************************************/
};
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 ...
conv_if(VRULE,GF_VRULE)
conv_if(LINE,GF_LINE)
conv_if(AREA,GF_AREA)
- conv_if(STACK,GF_STACK)
+ conv_if(STACK,GF_STACK)
conv_if(TICK,GF_TICK)
conv_if(DEF,GF_DEF)
conv_if(CDEF,GF_CDEF)
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)
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
*/
for(i=0;i<im->gdes_c;i++) {
if((im->gdes[i].gf==GF_LINE) ||
(im->gdes[i].gf==GF_AREA) ||
- (im->gdes[i].gf==GF_TICK) ||
- (im->gdes[i].gf==GF_STACK)) {
+ (im->gdes[i].gf==GF_TICK)) {
if((im->gdes[i].p_data = malloc((im->xsize +1)
* sizeof(rrd_value_t)))==NULL){
rrd_set_error("malloc data_proc");
case GF_TICK:
if (!im->gdes[ii].stack)
paintval = 0.0;
- case GF_STACK:
value = im->gdes[ii].yrule;
if (isnan(value) || (im->gdes[ii].gf == GF_TICK)) {
/* The time of the data doesn't necessarily match
im->gdes[ii].p_data[i] = DNAN;
}
break;
+ case GF_STACK:
+ rrd_set_error("STACK should already be turned into LINE or AREA here");
+ return -1;
+ break;
default:
break;
}
case GF_LINE:
case GF_AREA:
case GF_TICK:
- case GF_STACK:
case GF_HRULE:
case GF_VRULE:
graphelement = 1;
case GF_SHIFT:
case GF_XPORT:
break;
+ case GF_STACK:
+ rrd_set_error("STACK should already be turned into LINE or AREA here");
+ return -1;
+ break;
}
}
return graphelement;
double range;
double scaledrange;
int pixel,i;
- int gridind;
+ int gridind=0;
int decimals, fractionals;
im->ygrid_scale.labfact=2;
- gridind=-1;
range = im->maxval - im->minval;
scaledrange = range / im->magfact;
else {
for(i=0;ylab[i].grid > 0;i++){
pixel = im->ysize / (scaledrange / ylab[i].grid);
- if (pixel > 7) {
- gridind = i;
- break;
- }
+ gridind = i;
+ if (pixel > 7)
+ break;
}
for(i=0; i<4;i++) {
if (pixel * ylab[gridind].lfac[i] >= 2.5 * im->text_prop[TEXT_PROP_AXIS].size) {
- im->ygrid_scale.labfact = ylab[gridind].lfac[i];
+ im->ygrid_scale.labfact = ylab[gridind].lfac[i];
break;
- }
+ }
}
im->ygrid_scale.gridstep = ylab[gridind].grid * im->magfact;
int i;
double scaledstep;
char graph_label[100];
+ int nlabels=0;
double X0=im->xorigin;
double X1=im->xorigin+im->xsize;
MaxY = scaledstep*(double)egrid;
for (i = sgrid; i <= egrid; i++){
double Y0=ytr(im,im->ygrid_scale.gridstep*i);
+ double YN=ytr(im,im->ygrid_scale.gridstep*(i+1));
if ( Y0 >= im->yorigin-im->ysize
&& Y0 <= im->yorigin){
- if(i % im->ygrid_scale.labfact == 0){
+ /* Make sure at least 2 grid labels are shown, even if it doesn't agree
+ with the chosen settings. Add a label if required by settings, or if
+ there is only one label so far and the next grid line is out of bounds. */
+ if(i % im->ygrid_scale.labfact == 0 || ( nlabels==1 && (YN < im->yorigin-im->ysize || YN > im->yorigin) )){
if (im->symbol == ' ') {
if(im->extra_flags & ALTYGRID) {
sprintf(graph_label,im->ygrid_scale.labfmt,scaledstep*(double)i);
}
}
}
+ nlabels++;
gfx_new_text ( im->canvas,
X0-im->text_prop[TEXT_PROP_AXIS].size, Y0,
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;
5.5, im->tabwidth, 270,
GFX_H_RIGHT, GFX_V_TOP,
"RRDTOOL / TOBI OETIKER");
+
+ /* graph watermark */
+ if(im->watermark[0] != '\0') {
+ gfx_new_text( im->canvas,
+ im->ximg/2, im->yimg-6,
+ ( im->graph_col[GRC_FONT] & 0xffffff00 ) | 0x00000044,
+ im->text_prop[TEXT_PROP_AXIS].font,
+ 5.5, im->tabwidth, 0,
+ GFX_H_CENTER, GFX_V_BOTTOM,
+ im->watermark);
+ }
/* graph labels */
if( !(im->extra_flags & NOLEGEND) & !(im->extra_flags & ONLY_GRAPH) ) {
** |v+--+-------------------------------+--------+
** | |..............legends......................|
** +-+-------------------------------------------+
+ ** | watermark |
+ ** +---------------------------------------------+
*/
int Xvertical=0,
Ytitle =0,
#if 0
Xlegend =0, Ylegend =0,
#endif
- Xspacing =15, Yspacing =15;
+ Xspacing =15, Yspacing =15,
+
+ Ywatermark =4;
if (im->extra_flags & ONLY_GRAPH) {
im->xorigin =0;
xtr(im,0);
/* The vertical size is interesting... we need to compare
- ** the sum of {Ytitle, Ymain, Yxlabel, Ylegend} with Yvertical
- ** however we need to know {Ytitle+Ymain+Yxlabel} in order to
- ** start even thinking about Ylegend.
+ ** the sum of {Ytitle, Ymain, Yxlabel, Ylegend, Ywatermark} with
+ ** Yvertical however we need to know {Ytitle+Ymain+Yxlabel}
+ ** in order to start even thinking about Ylegend or Ywatermark.
**
** Do it in three portions: First calculate the inner part,
- ** then do the legend, then adjust the total height of the img.
+ ** then do the legend, then adjust the total height of the img,
+ ** adding space for a watermark if one exists;
*/
/* reserve space for main and/or pie */
*/
if(leg_place(im)==-1)
return -1;
-
+
+ if (im->watermark[0] != '\0') {
+ im->yimg += Ywatermark;
+ }
#if 0
if (Xlegend > im->ximg) {
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;
+}
+
/* draw that picture thing ... */
int
graph_paint(image_desc_t *im, char ***calcpr)
gfx_node_t *node;
double areazero = 0.0;
- enum gf_en stack_gf = GF_PRINT;
graph_desc_t *lastgdes = NULL;
/* if we are lazy and there is nothing to PRINT ... quit now */
for (ii = 0; ii < im->xsize; ii++)
{
if (!isnan(im->gdes[i].p_data[ii]) &&
- im->gdes[i].p_data[ii] > 0.0)
- {
- /* generate a tick */
- gfx_new_line(im->canvas, im -> xorigin + ii,
- im -> yorigin - (im -> gdes[i].yrule * im -> ysize),
- im -> xorigin + ii,
- im -> yorigin,
- 1.0,
- im -> gdes[i].col );
- }
+ im->gdes[i].p_data[ii] != 0.0)
+ {
+ if (im -> gdes[i].yrule > 0 ) {
+ gfx_new_line(im->canvas,
+ im -> xorigin + ii, im->yorigin,
+ im -> xorigin + ii, im->yorigin - im -> gdes[i].yrule * im -> ysize,
+ 1.0,
+ im -> gdes[i].col );
+ } else if ( im -> gdes[i].yrule < 0 ) {
+ gfx_new_line(im->canvas,
+ im -> xorigin + ii, im->yorigin - im -> ysize,
+ im -> xorigin + ii, im->yorigin - ( 1 - im -> gdes[i].yrule ) * im -> ysize,
+ 1.0,
+ im -> gdes[i].col );
+
+ }
+ }
}
break;
case GF_LINE:
case GF_AREA:
- stack_gf = im->gdes[i].gf;
- case GF_STACK:
/* fix data points at oo and -oo */
for(ii=0;ii<im->xsize;ii++){
if (isinf(im->gdes[i].p_data[ii])){
********************************************************* */
if (im->gdes[i].col != 0x0){
/* GF_LINE and friend */
- if(stack_gf == GF_LINE ){
+ if(im->gdes[i].gf == GF_LINE ){
+ double last_y=0.0;
node = NULL;
- for(ii=1;ii<im->xsize;ii++){
+ for(ii=1;ii<im->xsize;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);
};
}
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],
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;
}
break;
#endif
+ case GF_STACK:
+ rrd_set_error("STACK should already be turned into LINE or AREA here");
+ return -1;
+ break;
} /* switch */
}
im->gdes[im->gdes_c-1].step=im->step;
+ im->gdes[im->gdes_c-1].step_orig=im->step;
im->gdes[im->gdes_c-1].stack=0;
+ im->gdes[im->gdes_c-1].linewidth=0;
im->gdes[im->gdes_c-1].debug=0;
im->gdes[im->gdes_c-1].start=im->start;
im->gdes[im->gdes_c-1].end=im->end;
/* copies input untill the first unescaped colon is found
or until input ends. backslashes have to be escaped as well */
int
-scan_for_col(char *input, int len, char *output)
+scan_for_col(const char *const input, int len, char *const output)
{
int inp,outp=0;
for (inp=0;
#endif
#ifdef HAVE_SETLOCALE
setlocale(LC_TIME,"");
+#ifdef HAVE_MBSTOWCS
+ setlocale(LC_CTYPE,"");
+#endif
#endif
im->yorigin=0;
im->xorigin=0;
im->step = 0;
im->ylegend[0] = '\0';
im->title[0] = '\0';
+ im->watermark[0] = '\0';
im->minval = DNAN;
im->maxval = DNAN;
im->unitsexponent= 9999;
windir = getenv("windir");
/* %windir% is something like D:\windows or C:\winnt */
if (windir != NULL) {
- strncpy(rrd_win_default_font,windir,999);
- rrd_win_default_font[999] = '\0';
+ strncpy(rrd_win_default_font,windir,500);
+ rrd_win_default_font[500] = '\0';
strcat(rrd_win_default_font,"\\fonts\\");
strcat(rrd_win_default_font,RRD_DEFAULT_FONT);
for(i=0;i<DIM(text_prop);i++){
{"tabwidth", required_argument, 0, 'T'},
{"font-render-mode", required_argument, 0, 'R'},
{"font-smoothing-threshold", required_argument, 0, 'B'},
+ {"watermark", required_argument, 0, 'W'},
{"alt-y-mrtg", no_argument, 0, 1000}, /* this has no effect it is just here to save old apps from crashing when they use it */
{0,0,0,0}};
int option_index = 0;
int col_start,col_end;
opt = getopt_long(argc, argv,
- "s:e:x:y:v:w:h:iu:l:rb:oc:n:m:t:f:a:I:zgjFYAMEX:L:S:T:NR:B:",
+ "s:e:x:y:v:w:h:iu:l:rb:oc:n:m:t:f:a:I:zgjFYAMEX:L:S:T:NR:B:W:",
long_options, &option_index);
if (opt == EOF)
case 'n':{
char prop[15];
double size = 1;
- char font[1024];
+ char font[1024] = "";
if(sscanf(optarg,
"%10[A-Z]:%lf:%1000s",
im->canvas->font_aa_threshold = atof(optarg);
break;
+ case 'W':
+ strncpy(im->watermark,optarg,100);
+ im->watermark[99]='\0';
+ break;
+
case '?':
if (optopt != 0)
rrd_set_error("unknown option '%c'", optopt);
/* '%s', '%S' and '%%' are allowed */
if (*ptr == 's' || *ptr == 'S' || *ptr == '%') ptr++;
+ /* %c is allowed (but use only with vdef!) */
+ else if (*ptr == 'c') {
+ ptr++;
+ n=1;
+ }
+
/* or else '% 6.2lf' and such are allowed */
else {
-
/* optional padding character */
if (*ptr == ' ' || *ptr == '+' || *ptr == '-') ptr++;
-
+
/* This should take care of 'm.n' with all three optional */
while (*ptr >= '0' && *ptr <= '9') ptr++;
if (*ptr == '.') ptr++;
int
vdef_parse(gdes,str)
struct graph_desc_t *gdes;
-char *str;
+const char *const str;
{
/* A VDEF currently is either "func" or "param,func"
* so the parsing is rather simple. Change if needed.
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
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;
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;step<steps;step++) {
+ if (finite(data[step*src->ds_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;
}