Averaging Process Data

Introduction

The DCS is sometimes called upon to produce various averages of process data. Environmental compliance is often the reason for the averages.

Two types of averages are often used:

  • Periodic average over a set time (e.g., one minute, 5 minutes, 60 minutes, etc.).
  • Rolling average over a set time (e.g., 15 minutes, 60 minutes, etc.)

This document describes how to implement these averages in a Foxboro I/A system.

PERIODIC AVERAGE

Background

The periodic average starts at the beginning of the time-period and stops at the end of the time-period. The time-period may be one minute or any multiple that is evenly divisible into 60.

The Control Processor (CP) clock is used to determine when the time-period starts and ends. During each execution of the logic (e.g., 1 second), the input is added to the running total for the period and the number of readings is incremented. The input can be scaled to prevent an excessively large running total. Bad values are eliminated from the average by not incrementing the number of readings.

When the time-period is over, the running total is divided by the number of readings and the result is stored as the average for that period. If an insufficient number of good readings have occurred in the time-period, then the average is marked as bad.

Periodic Average CALCA Block

The periodic average is a calculated by a CALCA block.

Output Parameters

  • RO01 = average for previous time-period.
  • RO02 = running total for current time-period.
  • RO03 = number of readings in current time-period.
  • IO01 = current modulus (i.e., remainder after integer-dividing current clock minute by number of minutes in time-period).
  • IO02 = modulus from previous block execution.
  • IO03 = current modulus minus previous modulus.

Configuration Parameters

  • PERIOD = 2 (1.0 sec execution frequency).
  • MA = lock into auto.
  • TIMINI = 3.
  • RI01 = process input to be averaged.
  • RI08 = CP clock time input (connect to CP****_STA:STATION.MINUTE). Note: CP**** is CP letterbug.
  • M01 = input scaling factor that is multiplied by the process input value before updating the running total. The factor prevents the running total from becoming too large.
  • M02 = number of minutes in time-period.
  • M03 = minimum number of good readings (average is marked bad if insufficient good readings are obtained during the time-period).

Periodic Average CALCA Block Steps

STEP01 = IN RI08 ;CURR MINUTE
STEP02 = IN M02 ;MINUTES IN AVG
STEP03 = IMOD
STEP04 = OUT IO01 ;CURR MODULUS
STEP05 = BII 19
STEP06 = IN IO02 ;PREV MODULUS
STEP07 = SUB
STEP08 = OUT IO03 ;MODULUS DIFF
STEP09 = BIZ 23
STEP10 = IN IO01 ;CURR MODULUS
STEP11 = BIZ 13
STEP12 = GTO 23
STEP13 = SUB RO03 M03 ;READINGS < MINIMUM
STEP14 = BIN 20
STEP15 = DIV RO02 RO03 ;RUNNING TOT / READINGS
STEP16 = DIV M01 ;SCALE
STEP17 = OUT RO01 ;UPDATE AVERAGE
STEP18 = CBD RO01 ;CLEAR BAD
STEP19 = GTO 21
STEP20 = SBD RO01 ;SET BAD
STEP21 = CLR RO02 ;ZERO RUNNING TOTAL
STEP22 = CLR RO03 ;ZERO READINGS
STEP23 = CST
STEP24 = RBD RI01 ;BAD INPUT?
STEP25 = BIT 30
STEP26 = MUL RI01 M01 ;SCALE INPUT
STEP27 = ADD RO02
STEP28 = OUT RO02 ;RUNNING TOTAL
STEP29 = INC RO03 ;INCREMENT READINGS
STEP30 = CST
STEP31 = IN IO01 ;CURR MODULUS
STEP32 = OUT IO02 ;PREV MODULUS
STEP33 = END

Periodic Average Block Logic Details

Steps 1-4: Use the IMOD function to divide RI08 by M02 to determine the modulus (i.e., remainder of the divide). The modulus is stored into IO01.

Steps 5-8: If this is the first time the block is being executed (BII=1), then bypass the comparison to the previous modulus and go to step 19. Otherwise, subtract the previous modulus in IO02 and store the difference in IO03.

Steps 9-12: If IO03 is zero, then the minute signal from the clock did not change, so go to step 23. Otherwise, the minute signal has changed so check to see if IO01 is zero. If so, then we are at the end of the time-period and go to step 13. Otherwise, go to step 23.

Steps 13-19: We are at the end of the time-period, so check that the number of readings RO03 is less than the minimum required for a good average M03. If so, then go to step 20. Otherwise, divide the running total RO02 by the number of readings RO03, divide by the scale factor M01, and store the result in RO01. Mark the average as good (CBD). Go to step 21.

Steps 20: Mark the average as bad (SBD).

Steps 21-22: Zero the running total RO02 and the number of readings RO03.

Steps 23-25: Check the process input for bad value. If bad, go to step 30.

Steps 26-29: Multiply the process input RI01 by the scale factor M01 and add the result to the running total RO02. Increment the number of readings RO03 by one.

Steps 30-32: Store the current modulus IO01 into the previous modulus IO02 for comparison in the next execution.

ROLLING AVERAGE

Background

The rolling average utilizes an array of process inputs covering a given time-period. The process input is obtained at a frequency called the update period, which can be one minute or any multiple that is evenly divisible into 60. The time-period may be 15 minutes or any value that is evenly divisible into the update period.

The process input can be a snapshot reading or an average for the update period. If an average is desired, the periodic average logic described above can be used and its output connected to the rolling average input.

The Control Processor (CP) clock is used to determine when the new update period starts. During each execution (e.g., 1 second), the logic looks for the beginning of the update period. When it is detected, the input is stored into the oldest value in the array of values and the average of the values is calculated. The input can be scaled to prevent an excessively large value when totaling the array values. Bad values are eliminated from the average by not incrementing the number of readings.

Examples

A typical rolling average will have a time-period of 15 minutes and an update period of one minute. This results in an array of 15 values, one for each update period. Every minute, a new process value is stored to the oldest array value and the 15 values are averaged to become the rolling average output.

The rolling average can also have a time-period of one hour and an update period of one minute. This results in an array of 60 values.

The largest allowed time-period is one day with a one-minute update period. This results in an array of 1440 values.

Rolling Average IND Block

The rolling average logic is performed in an IND block. The HLBL sequence program is shown at the end of this document.

Output Parameters

  • RO0001 = rolling average for previous update period.
  • RO0002 = rolling sum of scaled array entries.
  • RO0003 = oldest value in array.
  • RO0004 = newest value in array.
  • RO0015 = value in array element designated by II0008.
  • IO0001 = number of good readings.
  • IO0002 = array element where the next input value is to be stored.
  • IO0003 = current minute from CP clock.
  • IO0004 = clock minute from previous execution.
  • IO0005 = remainder from previous integer division of update period into current minute from CP clock. Zero signifies beginning of new update period.

Configuration Parameters

  • PERIOD = 2 (1.0 sec execution frequency).
  • MA = must be 1.
  • RSTMA = must be 1.
  • ACTIVE = must be 1.
  • RSTACT = must be 1.
  • RI0001 = process input via connection.
  • RI0014 = input scaling factor that is multiplied by the process input value before updating the rolling total. The factor prevents the rolling total from becoming too large.
  • RI0015 = CP clock time input (connect to CP****_STA:STATION.MINUTE). Note: CP**** is CP letterbug.
  • II0001 = update period (minutes). Maximum is 60. Must be evenly divisible into 60.
  • II0002 = time-period (minutes). Maximum is 1440. Must be evenly divisible into the update period.
  • II0008 = array element to display in RO0015.
  • BI0001 = must be toggled on whenever the update period or the time-period is changed. The change will not be recognized by the program until BI0001 is toggled on.

ROLLING AVERAGE HLBL SEQUENCE PROGRAM

INDEPENDENT_SEQUENCE
{-******************************************************************************
–PROGRAM: ROLL_AVG CALCULATES A ROLLING AVERAGE OF AN INPUT OBTAINED
— ONCE EVERY UPDATE PERIOD.

–DEFINITION SECTION **********************************************************}

CONSTANTS

BADVAL = -99999.0 ; {BAD VALUE}
BADVCK = -99998.0 ; {BAD VALUE CHECK}
MAX_VAL = 1440 ; {MAX VALUES IN ARRAY}

VARIABLES

VALUES : R[1440]; {ARRAY VALUES}

DENOM : I ; {DENOMINATOR IN AVG CALC}
MAX_I : I ; {NUMBER OF VALUES IN AVG (MAX 3600)}
UPDATE : I ; {MINUTES BETWEEN UPDATES (1..60)}
AVG_MIN : I ; {MINUTES IN AVG (1..1440)}
SCALE : R ; {SCALING FACTOR}

USER_LABELS

INPUT : RI0001 ; {INPUT TO BE AVERAGED}
SCALE_IN : RI0014 ; {INPUT SCALING FACTOR}
IMIN : RI0015 ; {Minutes in Current Hour fm CP Clock}

UPD : II0001 ; {MINUTES BETWEEN UPDATES (1..60). MUST EVENLY DIVIDE INTO 60}
AVGMIN : II0002 ; {MINUTES IN AVG (1..1440). MUST BE EVENLY DIVIDED BY UPDATE}

ARRAY_I : II0008 ; {FLOW ARRAY ELEMENT TO DISPLAY IN RO0015}

RESET : BI0001 ; {RESET UPDATE OR AVG_MIN}

{CALCULATION RESULTS}

ROLLAVG : RO0001 ; {RAW ROLLING AVERAGE}
ROLLSUM : RO0002 ; {RAW ROLLING SUM}
OLD_VAL : RO0003 ; {OLDEST VALUE IN ARRAY}
NEW_VAL : RO0004 ; {NEWEST VALUE IN ARRAY}

ARRAY_V : RO0015 ; {VALUE IN ARRAY ELEMENT ARRAY_I}

READINGS : IO0001 ; {NUMBER OF GOOD READINGS}
POINTER : IO0002 ; {POINTS TO ARRAY ELEMENT NEXT VALUE IS STORED IN}
MN : IO0003 ; {CURRENT MINUTE}
LASTMN : IO0004 ; {MN SAVED FROM PREV EXECUTION}
REMAIN : IO0005 ; {REMAINDER FROM MOD DIV}

STATEMENTS

{**********************************************************************
Initialization Section
**********************************************************************}

<<INIT_LOOP>>;

VALUES := SET_ARRAY(BADVAL);
READINGS := 0;
ROLLSUM := 0.0;
ROLLAVG := 0.0;
POINTER := 0;

MN := ROUND(IMIN);

UPDATE := UPD;
AVG_MIN := AVGMIN;
SCALE := SCALE_IN;

{INITIALIZE KEY VARIABLES IF ENTRIES ARE ZERO}

IF SCALE = 0.0 THEN SCALE := 1.0; ENDIF;
IF UPDATE = 0 THEN UPDATE := 1; ENDIF;
IF AVG_MIN = 0 THEN AVG_MIN := 15; ENDIF;

{CALCULATE ARRAY SIZE BASED ON UPDATE & MINUTES IN AVG}

MAX_I := AVG_MIN DIV UPDATE;
IF MAX_I > MAX_VAL THEN MAX_I := MAX_VAL; ENDIF;

RESET := FALSE;

{**********************************************************************
Normal Operation Section
**********************************************************************}

<<BEGIN_LOOP>>
WAIT 0.5;

IF RESET THEN GOTO INIT_LOOP; ENDIF;

{VIEW SELECTED ARRAY ELEMENT}
{ENTER ELEMENT NO. IN II0008 AND VIEW CONTENTS IN RI0015}

IF ARRAY_I < 1 THEN ARRAY_I := 1;
ELSE IF ARRAY_I > MAX_I THEN ARRAY_I := MAX_I; ENDIF;
ENDIF;

ARRAY_V:= VALUES[ARRAY_I];

{CHECK FOR UPDATE TIME FROM CP CLOCK}

LASTMN := MN;
MN := ROUND(IMIN);
IF LASTMN = MN THEN GOTO BEGIN_LOOP; ENDIF;
REMAIN := MN MOD UPDATE;
IF REMAIN > 0 THEN GOTO BEGIN_LOOP; ENDIF;

{INCREMENT POINTER – RESET TO 1 IF AT END OF ARRAY}

IF POINTER = MAX_I THEN POINTER := 1;
ELSE POINTER := POINTER + 1; ENDIF;

{CHECK FOR BAD INPUT – STORE BADVAL MARKER (-99999) IF FOUND}

IF INPUT.BAD THEN NEW_VAL := BADVAL;
ELSE NEW_VAL := INPUT; ENDIF;

{REPLACE OLDEST VALUE IN ARRAY WITH NEWEST VALUE}

OLD_VAL := VALUES[POINTER];
VALUES[POINTER] := NEW_VAL;

{UPDATE ROLLING SUM WITH NEW VALUE IF GOOD}
{SCALE FACTOR IS USED TO REDUCE MAGNITUDE OF ROLLING SUM}

IF NEW_VAL > BADVCK THEN READINGS := READINGS + 1;
ROLLSUM := ROLLSUM + SCALE*NEW_VAL;
ENDIF;

{REMOVE OLDEST VALUE FROM ROLLING SUM IF GOOD}

IF OLD_VAL > BADVCK THEN READINGS := READINGS – 1;
ROLLSUM := ROLLSUM – SCALE*OLD_VAL;
ENDIF;

{LIMIT READINGS TO ARRAY SIZE}

IF READINGS > MAX_I THEN READINGS := MAX_I; ENDIF;
IF READINGS <= 0 THEN READINGS := 0;
ROLLSUM := 0.0 ; ENDIF;

{PREVENT DIVIDE BY ZERO}

IF READINGS > 0 THEN DENOM := READINGS;
ELSE DENOM := 1; ENDIF;

{CALCULATE AVG FROM ROLLING SUM}

ROLLAVG := ROLLSUM / DENOM / SCALE;

GOTO BEGIN_LOOP;

ENDSEQUENCE