Rolling average for analog input

I have a analog value (power consumption) that I would like to generate a rolling average (and min/max) value  such that displayed value would constantly update to the average, min, and max of the past 60 minutes

any advice / help with this would be appreciated.

  • Cycle import the historian value within 60 minutes to excel then calculate by function.
  • In reply to dpwu:

    Doesn't look like I have the excel add-in. Any other way to do this?
  • In reply to AggieRalph:

    in menu ‘data’ can find and import data with timestamps, also refresh data every xx minutes.
  • We generate rolling averages using CALC blocks. For a sixty minute average, we would create two, each populating a 1 X 30 (or whatever number of samples) array with the most recent value, dropping the "oldest". The first block would be on a relatively "fast" sampling rate (e.g. 30 x 2 seconds gives you a 1 minute rolling average) and the second would do a similar chore (1 x 60 array) once a minute with the results of the first CALC block. Read the CALC block help for info on defining arrays (can be a module-level variable) and think about initial conditions - you may want to have some code to initialize to the current value on download, for example.
  • In reply to John Rezabek:

    thanks for the answer,  I ended up doing something similar just not in Calc blocks.   I had already had something figured out by the time you posted this, but I will look into it to see if I can figure it out.   haven't found how arrays work in deltav yet.

    Here is what I ended up using.

    This gives you the rolling average over the past hour based on data taken every 5 secs, but avg/min/max are only calculated every 10 min (possible to do more frequent updates but would require a bit more coding and this is sufficient for my purposes)
    gives you the min and max value over the same time period
    Just need to change input variable to another input to change the data you are analyzing

    This was just my first go at this and after doing it, I realize there are several areas I could improve my code  (using more act blocks instead of nested if/then)  At a later time I might try to go through and improve this to a simpler structure, but that will have to wait till another time.

    Downside is it takes an hour after download before you have true data, but in my application we are not doing downloads that often so not really an issue


    Below is the code I used. Action block 1 is at limit of what is available.

    Basic idea is it uses nested if/then statements with timer block that triggers every 5 sec.
    counter blocks count up 1-12 for a total of 60 sec and at each 5 sec step recording the value and figuring out if the min/max is lower/higher then others.

    once a min it takes those 12 sec steps and avg them to a min average and takes the min/max values for that min and records them
    Every 10 min it takes the average / min / max from the past 10 min and records those values
    Every 10 min it takes the average / min / max from the 6 past recorded 10 min intervals and marks that as the hour average / min / max
    my code formating was lost when I copied it here so it might be hard to follow.


    Action block 1

    IF '^/SECCOUNTER'=0 THEN
    '^/SECCOUNTER.CV' := 1;
    END_IF;
    IF '^/MINCOUNTER'=0 THEN
    '^/MINCOUNTER.CV' := 1;
    END_IF;
    IF '^/TENMINCOUNTER'=0 THEN
    '^/TENMINCOUNTER.CV' := 1;
    END_IF;

    IF '^/MIN'=0 THEN
    '^/MIN.CV' := 99999;
    END_IF;


    IF '^/SECCOUNTER'=1 THEN
    '^/STEP1' := '^/PARAM1.CV';
    IF '^/PARAM1.CV' > '^/MAX.CV' THEN
    '^/MAX.CV' := '^/PARAM1.CV';
    END_IF;
    IF '^/PARAM1.CV' < '^/MIN.CV' THEN
    '^/MIN.CV' := '^/PARAM1.CV';
    END_IF;
    END_IF;

    IF '^/SECCOUNTER'= 2 THEN
    '^/STEP2' := '^/PARAM1.CV';
    IF '^/PARAM1.CV' > '^/MAX.CV' THEN
    '^/MAX.CV' := '^/PARAM1.CV';
    END_IF;
    IF '^/PARAM1.CV' < '^/MIN.CV' THEN
    '^/MIN.CV' := '^/PARAM1.CV';
    END_IF;
    END_IF;

    IF '^/SECCOUNTER'= 3 THEN
    '^/STEP3' := '^/PARAM1.CV';
    IF '^/PARAM1.CV' > '^/MAX.CV' THEN
    '^/MAX.CV' := '^/PARAM1.CV';
    END_IF;
    IF '^/PARAM1.CV' < '^/MIN.CV' THEN
    '^/MIN.CV' := '^/PARAM1.CV';
    END_IF;
    END_IF;

    IF '^/SECCOUNTER'= 4 THEN
    '^/STEP4' := '^/PARAM1.CV';
    IF '^/PARAM1.CV' > '^/MAX.CV' THEN
    '^/MAX.CV' := '^/PARAM1.CV';
    END_IF;
    IF '^/PARAM1.CV' < '^/MIN.CV' THEN
    '^/MIN.CV' := '^/PARAM1.CV';
    END_IF;
    END_IF;

    IF '^/SECCOUNTER'= 5 THEN
    '^/STEP5' := '^/PARAM1.CV';
    IF '^/PARAM1.CV' > '^/MAX.CV' THEN
    '^/MAX.CV' := '^/PARAM1.CV';
    END_IF;
    IF '^/PARAM1.CV' < '^/MIN.CV' THEN
    '^/MIN.CV' := '^/PARAM1.CV';
    END_IF;
    END_IF;

    IF '^/SECCOUNTER'= 6 THEN
    '^/STEP6' := '^/PARAM1.CV';
    IF '^/PARAM1.CV' > '^/MAX.CV' THEN
    '^/MAX.CV' := '^/PARAM1.CV';
    END_IF;
    IF '^/PARAM1.CV' < '^/MIN.CV' THEN
    '^/MIN.CV' := '^/PARAM1.CV';
    END_IF;
    END_IF;

    IF '^/SECCOUNTER'= 7 THEN
    '^/STEP7' := '^/PARAM1.CV';
    IF '^/PARAM1.CV' > '^/MAX.CV' THEN
    '^/MAX.CV' := '^/PARAM1.CV';
    END_IF;
    IF '^/PARAM1.CV' < '^/MIN.CV' THEN
    '^/MIN.CV' := '^/PARAM1.CV';
    END_IF;
    END_IF;

    IF '^/SECCOUNTER'= 8 THEN
    '^/STEP8' := '^/PARAM1.CV';
    IF '^/PARAM1.CV' > '^/MAX.CV' THEN
    '^/MAX.CV' := '^/PARAM1.CV';
    END_IF;
    IF '^/PARAM1.CV' < '^/MIN.CV' THEN
    '^/MIN.CV' := '^/PARAM1.CV';
    END_IF;
    END_IF;

    IF '^/SECCOUNTER'= 9 THEN
    '^/STEP9' := '^/PARAM1.CV';
    IF '^/PARAM1.CV' > '^/MAX.CV' THEN
    '^/MAX.CV' := '^/PARAM1.CV';
    END_IF;
    IF '^/PARAM1.CV' < '^/MIN.CV' THEN
    '^/MIN.CV' := '^/PARAM1.CV';
    END_IF;
    END_IF;

    IF '^/SECCOUNTER'= 10 THEN
    '^/STEP10' := '^/PARAM1.CV';
    IF '^/PARAM1.CV' > '^/MAX.CV' THEN
    '^/MAX.CV' := '^/PARAM1.CV';
    END_IF;
    IF '^/PARAM1.CV' < '^/MIN.CV' THEN
    '^/MIN.CV' := '^/PARAM1.CV';
    END_IF;
    END_IF;

    IF '^/SECCOUNTER'= 11 THEN
    '^/STEP11' := '^/PARAM1.CV';
    IF '^/PARAM1.CV' > '^/MAX.CV' THEN
    '^/MAX.CV' := '^/PARAM1.CV';
    END_IF;
    IF '^/PARAM1.CV' < '^/MIN.CV' THEN
    '^/MIN.CV' := '^/PARAM1.CV';
    END_IF;
    END_IF;

    IF '^/SECCOUNTER'< 12 THEN
    '^/SECCOUNTER' := '^/SECCOUNTER' + 1
    ELSE
    '^/STEP12' := '^/PARAM1.CV';
    '^/SECCOUNTER.CV' := 0;
    IF '^/PARAM1.CV' > '^/MAX.CV' THEN
    '^/MAX.CV' := '^/PARAM1.CV';
    END_IF;
    IF '^/PARAM1.CV' < '^/MIN.CV' THEN
    '^/MIN.CV' := '^/PARAM1.CV';
    END_IF;

    IF '^/MINCOUNTER' = 1 THEN
    '^/MIN1.CV' := ('^/STEP1.CV' + '^/STEP2.CV' + '^/STEP3.CV' + '^/STEP4.CV' + '^/STEP5.CV' + '^/STEP6.CV' + '^/STEP7.CV' + '^/STEP8.CV' + '^/STEP9.CV' + '^/STEP10.CV' + '^/STEP11.CV' + '^/STEP12.CV') / 12;
    '^/MAXM1.CV' := '^/MAX.CV';
    '^/MINM1.CV' := '^/MIN.CV';
    END_IF;

    IF '^/MINCOUNTER' = 2 THEN
    '^/MIN2.CV' := ('^/STEP1.CV' + '^/STEP2.CV' + '^/STEP3.CV' + '^/STEP4.CV' + '^/STEP5.CV' + '^/STEP6.CV' + '^/STEP7.CV' + '^/STEP8.CV' + '^/STEP9.CV' + '^/STEP10.CV' + '^/STEP11.CV' + '^/STEP12.CV') / 12;
    '^/MAXM2.CV' := '^/MAX.CV';
    '^/MINM2.CV' := '^/MIN.CV';
    END_IF;

    IF '^/MINCOUNTER' = 3 THEN
    '^/MIN3.CV' := ('^/STEP1.CV' + '^/STEP2.CV' + '^/STEP3.CV' + '^/STEP4.CV' + '^/STEP5.CV' + '^/STEP6.CV' + '^/STEP7.CV' + '^/STEP8.CV' + '^/STEP9.CV' + '^/STEP10.CV' + '^/STEP11.CV' + '^/STEP12.CV') / 12;
    '^/MAXM3.CV' := '^/MAX.CV';
    '^/MINM3.CV' := '^/MIN.CV';
    END_IF;

    IF '^/MINCOUNTER' = 4 THEN
    '^/MIN4.CV' := ('^/STEP1.CV' + '^/STEP2.CV' + '^/STEP3.CV' + '^/STEP4.CV' + '^/STEP5.CV' + '^/STEP6.CV' + '^/STEP7.CV' + '^/STEP8.CV' + '^/STEP9.CV' + '^/STEP10.CV' + '^/STEP11.CV' + '^/STEP12.CV') / 12;
    '^/MAXM4.CV' := '^/MAX.CV';
    '^/MINM4.CV' := '^/MIN.CV';
    END_IF;

    IF '^/MINCOUNTER' = 5 THEN
    '^/MIN5.CV' := ('^/STEP1.CV' + '^/STEP2.CV' + '^/STEP3.CV' + '^/STEP4.CV' + '^/STEP5.CV' + '^/STEP6.CV' + '^/STEP7.CV' + '^/STEP8.CV' + '^/STEP9.CV' + '^/STEP10.CV' + '^/STEP11.CV' + '^/STEP12.CV') / 12;
    '^/MAXM5.CV' := '^/MAX.CV';
    '^/MINM5.CV' := '^/MIN.CV';
    END_IF;

    IF '^/MINCOUNTER' = 6 THEN
    '^/MIN6.CV' := ('^/STEP1.CV' + '^/STEP2.CV' + '^/STEP3.CV' + '^/STEP4.CV' + '^/STEP5.CV' + '^/STEP6.CV' + '^/STEP7.CV' + '^/STEP8.CV' + '^/STEP9.CV' + '^/STEP10.CV' + '^/STEP11.CV' + '^/STEP12.CV') / 12;
    '^/MAXM6.CV' := '^/MAX.CV';
    '^/MINM6.CV' := '^/MIN.CV';
    END_IF;

    IF '^/MINCOUNTER' = 7 THEN
    '^/MIN7.CV' := ('^/STEP1.CV' + '^/STEP2.CV' + '^/STEP3.CV' + '^/STEP4.CV' + '^/STEP5.CV' + '^/STEP6.CV' + '^/STEP7.CV' + '^/STEP8.CV' + '^/STEP9.CV' + '^/STEP10.CV' + '^/STEP11.CV' + '^/STEP12.CV') / 12;
    '^/MAXM7.CV' := '^/MAX.CV';
    '^/MINM7.CV' := '^/MIN.CV';
    END_IF;

    IF '^/MINCOUNTER' = 8 THEN
    '^/MIN8.CV' := ('^/STEP1.CV' + '^/STEP2.CV' + '^/STEP3.CV' + '^/STEP4.CV' + '^/STEP5.CV' + '^/STEP6.CV' + '^/STEP7.CV' + '^/STEP8.CV' + '^/STEP9.CV' + '^/STEP10.CV' + '^/STEP11.CV' + '^/STEP12.CV') / 12;
    '^/MAXM8.CV' := '^/MAX.CV';
    '^/MINM8.CV' := '^/MIN.CV';
    END_IF;

    IF '^/MINCOUNTER' = 9 THEN
    '^/MIN9.CV' := ('^/STEP1.CV' + '^/STEP2.CV' + '^/STEP3.CV' + '^/STEP4.CV' + '^/STEP5.CV' + '^/STEP6.CV' + '^/STEP7.CV' + '^/STEP8.CV' + '^/STEP9.CV' + '^/STEP10.CV' + '^/STEP11.CV' + '^/STEP12.CV') / 12;
    '^/MAXM9.CV' := '^/MAX.CV';
    '^/MINM9.CV' := '^/MIN.CV';
    END_IF;


    IF '^/MINCOUNTER' < 10 THEN
    '^/MINCOUNTER' := '^/MINCOUNTER' + 1
    ELSE
    '^/MIN10.CV' := ('^/STEP1.CV' + '^/STEP2.CV' + '^/STEP3.CV' + '^/STEP4.CV' + '^/STEP5.CV' + '^/STEP6.CV' + '^/STEP7.CV' + '^/STEP8.CV' + '^/STEP9.CV' + '^/STEP10.CV' + '^/STEP11.CV' + '^/STEP12.CV') / 12;
    '^/MINCOUNTER' := 1;
    '^/MAXM10.CV' := '^/MAX.CV';
    '^/MINM10.CV' := '^/MIN.CV';
    '^/MIN.CV' := 99999;
    '^/MAX.CV' := 0;
    VAR T1; T2 END_VAR;
    T1 :='^/MAXM1.CV';
    T2 :='^/MINM1.CV';
    IF '^/MAXM2.CV' > T1 THEN T1 := '^/MAXM2.CV' END_IF;
    IF '^/MINM2.CV' < T2 THEN T2 := '^/MINM2.CV' END_IF;
    IF '^/MAXM3.CV' > T1 THEN T1 := '^/MAXM3.CV' END_IF;
    IF '^/MINM3.CV' < T2 THEN T2 := '^/MINM3.CV' END_IF;
    IF '^/MAXM4.CV' > T1 THEN T1 := '^/MAXM4.CV' END_IF;
    IF '^/MINM4.CV' < T2 THEN T2 := '^/MINM4.CV' END_IF;
    IF '^/MAXM5.CV' > T1 THEN T1 := '^/MAXM5.CV' END_IF;
    IF '^/MINM5.CV' < T2 THEN T2 := '^/MINM5.CV' END_IF;
    IF '^/MAXM6.CV' > T1 THEN T1 := '^/MAXM6.CV' END_IF;
    IF '^/MINM6.CV' < T2 THEN T2 := '^/MINM6.CV' END_IF;
    IF '^/MAXM7.CV' > T1 THEN T1 := '^/MAXM7.CV' END_IF;
    IF '^/MINM7.CV' < T2 THEN T2 := '^/MINM7.CV' END_IF;
    IF '^/MAXM8.CV' > T1 THEN T1 := '^/MAXM8.CV' END_IF;
    IF '^/MINM8.CV' < T2 THEN T2 := '^/MINM8.CV' END_IF;
    IF '^/MAXM9.CV' > T1 THEN T1 := '^/MAXM9.CV' END_IF;
    IF '^/MINM9.CV' < T2 THEN T2 := '^/MINM9.CV' END_IF;
    IF '^/MAXM10.CV' > T1 THEN T1 := '^/MAXM10.CV' END_IF;
    IF '^/MINM10.CV' < T2 THEN T2 := '^/MINM10.CV' END_IF;
    IF '^/TENMINCOUNTER.CV'=1 THEN
    '^/TENM1.CV' := ('^/MIN1.CV' + '^/MIN2.CV' + '^/MIN3.CV' + '^/MIN4.CV' + '^/MIN5.CV' + '^/MIN6.CV' + '^/MIN7.CV' + '^/MIN8.CV' + '^/MIN9.CV' + '^/MIN10.CV') / 10;
    '^/MAXMT1' := T1;
    '^/MINMT1' := T2;
    END_IF ;
    IF '^/TENMINCOUNTER.CV'=2 THEN
    '^/TENM2.CV' := ('^/MIN1.CV' + '^/MIN2.CV' + '^/MIN3.CV' + '^/MIN4.CV' + '^/MIN5.CV' + '^/MIN6.CV' + '^/MIN7.CV' + '^/MIN8.CV' + '^/MIN9.CV' + '^/MIN10.CV') / 10;
    '^/MAXMT2' := T1;
    '^/MINMT2' := T2;
    END_IF;
    IF '^/TENMINCOUNTER.CV'=3 THEN
    '^/TENM3.CV' := ('^/MIN1.CV' + '^/MIN2.CV' + '^/MIN3.CV' + '^/MIN4.CV' + '^/MIN5.CV' + '^/MIN6.CV' + '^/MIN7.CV' + '^/MIN8.CV' + '^/MIN9.CV' + '^/MIN10.CV') / 10;
    '^/MAXMT3' := T1;
    '^/MINMT3' := T2;
    END_IF;
    IF '^/TENMINCOUNTER.CV'=4 THEN
    '^/TENM4.CV' := ('^/MIN1.CV' + '^/MIN2.CV' + '^/MIN3.CV' + '^/MIN4.CV' + '^/MIN5.CV' + '^/MIN6.CV' + '^/MIN7.CV' + '^/MIN8.CV' + '^/MIN9.CV' + '^/MIN10.CV') / 10;
    '^/MAXMT4' := T1;
    '^/MINMT4' := T2;
    END_IF;
    IF '^/TENMINCOUNTER.CV'=5 THEN
    '^/TENM5.CV' := ('^/MIN1.CV' + '^/MIN2.CV' + '^/MIN3.CV' + '^/MIN4.CV' + '^/MIN5.CV' + '^/MIN6.CV' + '^/MIN7.CV' + '^/MIN8.CV' + '^/MIN9.CV' + '^/MIN10.CV') / 10;
    '^/MAXMT5' := T1;
    '^/MINMT5' := T2;
    END_IF;
    IF '^/TENMINCOUNTER.CV' < 6 THEN
    '^/TENMINCOUNTER.CV' := '^/TENMINCOUNTER.CV' + 1;
    ELSE
    '^/TENMINCOUNTER.CV' := 1;
    '^/TENM6.CV' := ('^/MIN1.CV' + '^/MIN2.CV' + '^/MIN3.CV' + '^/MIN4.CV' + '^/MIN5.CV' + '^/MIN6.CV' + '^/MIN7.CV' + '^/MIN8.CV' + '^/MIN9.CV' + '^/MIN10.CV') / 10;
    '^/MAXMT6' := T1;
    '^/MINMT6' := T2;
    END_IF;
    END_IF;
    END_IF;


    Action block 2

    '^/HOURAVERAGE' := ('^/TENM1' + '^/TENM2' + '^/TENM3' + '^/TENM4' + '^/TENM5' + '^/TENM6') / 6;

    IF '^/SECCOUNTER' = 12 THEN
    VAR T1; T2 END_VAR;
    T1 :='^/MAXM1.CV';
    T2 :='^/MINM1.CV';
    IF '^/MAXM2.CV' > T1 THEN T1 := '^/MAXM2.CV' END_IF;
    IF '^/MINM2.CV' < T2 THEN T2 := '^/MINM2.CV' END_IF;
    IF '^/MAXM3.CV' > T1 THEN T1 := '^/MAXM3.CV' END_IF;
    IF '^/MINM3.CV' < T2 THEN T2 := '^/MINM3.CV' END_IF;
    IF '^/MAXM4.CV' > T1 THEN T1 := '^/MAXM4.CV' END_IF;
    IF '^/MINM4.CV' < T2 THEN T2 := '^/MINM4.CV' END_IF;
    IF '^/MAXM5.CV' > T1 THEN T1 := '^/MAXM5.CV' END_IF;
    IF '^/MINM5.CV' < T2 THEN T2 := '^/MINM5.CV' END_IF;
    IF '^/MAXM6.CV' > T1 THEN T1 := '^/MAXM6.CV' END_IF;
    IF '^/MINM6.CV' < T2 THEN T2 := '^/MINM6.CV' END_IF;
    IF '^/MAXM7.CV' > T1 THEN T1 := '^/MAXM7.CV' END_IF;
    IF '^/MINM7.CV' < T2 THEN T2 := '^/MINM7.CV' END_IF;
    IF '^/MAXM8.CV' > T1 THEN T1 := '^/MAXM8.CV' END_IF;
    IF '^/MINM8.CV' < T2 THEN T2 := '^/MINM8.CV' END_IF;
    IF '^/MAXM9.CV' > T1 THEN T1 := '^/MAXM9.CV' END_IF;
    IF '^/MINM9.CV' < T2 THEN T2 := '^/MINM9.CV' END_IF;
    IF '^/MAXM10.CV' > T1 THEN T1 := '^/MAXM10.CV' END_IF;
    IF '^/MINM10.CV' < T2 THEN T2 := '^/MINM10.CV' END_IF;
    IF '^/MAXMT1.CV' > T1 THEN T1 := '^/MAXMT1.CV' END_IF;
    IF '^/MINMT1.CV' < T2 THEN T2 := '^/MINMT1.CV' END_IF;
    IF '^/MAXMT2.CV' > T1 THEN T1 := '^/MAXMT2.CV' END_IF;
    IF '^/MINMT2.CV' < T2 THEN T2 := '^/MINMT2.CV' END_IF;
    IF '^/MAXMT3.CV' > T1 THEN T1 := '^/MAXMT3.CV' END_IF;
    IF '^/MINMT3.CV' < T2 THEN T2 := '^/MINMT3.CV' END_IF;
    IF '^/MAXMT4.CV' > T1 THEN T1 := '^/MAXMT4.CV' END_IF;
    IF '^/MINMT4.CV' < T2 THEN T2 := '^/MINMT4.CV' END_IF;
    IF '^/MAXMT5.CV' > T1 THEN T1 := '^/MAXMT5.CV' END_IF;
    IF '^/MINMT5.CV' < T2 THEN T2 := '^/MINMT5.CV' END_IF;
    IF '^/MAXMT6.CV' > T1 THEN T1 := '^/MAXMT6.CV' END_IF;
    IF '^/MINMT6.CV' < T2 THEN T2 := '^/MINMT6.CV' END_IF;
    '^/HOURMAX' := T1;
    '^/HOURMIN' := T2;

    END_IF;

  • In reply to John Rezabek:

    I had read somewhere in books online that using a filter block with a time constant much greater than the execution time would also work for a rolling average. I did some testing with a coworker who a function block similar to what you have suggested. Both results were near identical. Do you see any drawback to using a filter block?
  • In reply to krsimpton:

    Was the data you were looking at fairly constant? ---- about to go on my off time and will experiment with the filter block when I get back (in a month), but I suspect it wont work well for me because I don't want to loose the data from the peaks/valleys of the input, (also reason why I am keeping track of min max which the filter block can't do) I am surprised there isn't a programed block to track min / max for analog inputs something like (analog input) (analog output) (time) where analog output = max/min from analog input for time.
  • In reply to John Rezabek:

    at least in my version I don't see anything in the CALC block help referring to arrays. Can you post an example?
  • In reply to AggieRalph:

    If you search the application exchange , there are four examples there - I have not tried them but sometimes they are more elegant than the brute force method.

    You are correct - at least in 13.3, the help you access either from the Calc block or the expression editor does not take you anywhere having to do with array definitions. If you search for "floating point array" you will get some information. You define them as a module-level parameter and then reference them in the CALC block.

    The code would look something like this:

    array(1,30) := array(1,29)

    array(1,29) := array(1,28)

    array(1,28) := array(1,27)

    .

    .

    .

    array(1,3) := array(1,2)

    array(1,2) := array(1,1)

    array(1,1) := IN1.CV  'the current value


    Then you can sum up the array and divide by 30 to get the current rolling average . . .