new module for "moving average"

Begonnen von elconfa, 09. April 2018, 17:20:01

Vorheriges Thema - Nächstes Thema

0 Mitglieder und 1 Gast betrachten dieses Thema.

elconfa

this is my code, may be someone need it, or you are able to make it better
Thanks
Massimo


FUNCTION_BLOCK FB_MovingAverage
VAR_INPUT
   IN : REAL;
   N_Cicli : INT;
   t_Pausa_1 : DINT;
   t_Pausa_2 : DINT;
END_VAR
VAR_OUTPUT
   Media : REAL;
END_VAR
VAR
   Valore : ARRAY [0..999] OF REAL;
   x, x1, x2: INT;
   SCHEDULER_1: OSCAT_BASIC.SCHEDULER;
   A, B, C, D: REAL;
   E: BOOL;
END_VAR


SCHEDULER_1(
   E0:= TRUE,
   E1:= TRUE,
   T0:= DINT_TO_TIME(t_Pausa_1),
   T1:= DINT_TO_TIME(t_Pausa_2));

IF SCHEDULER_1.Q0 THEN
   A := A + IN;
   B := B + 1;
END_IF

IF SCHEDULER_1.Q1 THEN      //MEDIA DELLE LETTURE BREVI
   C := A / B;
   A := 0;
   B := 0;
   D := 0;
   E := TRUE;
END_IF

IF E THEN
   FOR x := 1 TO (N_Cicli-1) DO
      x1 := N_Cicli - x;
      x2 := N_Cicli - x - 1;
      Valore[x1] := Valore[x2];
   END_FOR
   Valore[0] := C;
   FOR x := 0 TO (N_Cicli-1) DO
      D := D + Valore
  • ;
       END_FOR
       E := FALSE;
       Media := D / N_Cicli;   //MEDIA TRASCINATA
    END_IF



mattsches

I don't know if you are aware of the FILTER_MAV_W function block in OSCAT_Basic which does a moving average, although for WORD variables. I needed that for REALs, too, so I modified it and fixed the buffer initialization at the same time. Might be interesting for you, since it seems considerably more compact than your code. If you really need a calculation with 1000 values (which seems a lot to me), you can easily change the size of the "buffer" array.


FUNCTION_BLOCK FILTER_MAV_R
VAR_INPUT
X : REAL;
N : UINT;
RST : BOOL := FALSE;
END_VAR
VAR_OUTPUT
Y : REAL;
END_VAR
VAR
init: BOOL := FALSE;
buffer : ARRAY[0..31] OF REAL;
i: INT;
sum : REAL;
END_VAR
VAR_TEMP
tmp : INT;
END_VAR

(* limit N to size of buffer *)
N := MIN(N, SIZEOF(buffer)/4);

(* startup initialisation *)
IF NOT init OR rst OR N = 0 THEN
init := TRUE;
FOR i := 0 TO UINT_TO_INT(N)-1 DO
buffer[i] := X;
END_FOR;
sum := X * N;
Y := X;
ELSE
i := INC1(i, UINT_TO_INT(N));
sum := sum + X - buffer[i];
Y := sum / N;
buffer[i] := X;
END_IF;

elconfa

 :-[
I have to thankyou! I did not seen that function and your help is very appreciated. I just made a little change because after changing the N variable, I need to send a reset signal for a Plc cycle or the result is wrong.


(* limit N to size of buffer *)
N := MIN(N, SIZEOF(buffer)/4);

A_TRIG_N(IN:= UINT_TO_REAL(N), RES:= 0.5, Q=> rst, D=> );

(* startup initialisation *)
IF NOT init OR rst OR N = 0 THEN
   init := TRUE;
   FOR i := 0 TO UINT_TO_INT(N)-1 DO
      buffer := X;
   END_FOR;
   sum := X * N;
   Y := X;
ELSE
   i := INC1(i, UINT_TO_INT(N));
   sum := sum + X - buffer;
   Y := sum / N;
   buffer := X;
END_IF;

mattsches

Provided that you need to change N during runtime, yes, the buffer initialization makes sense. I'd prefer a leaner approach for detecting changes on N, without the A_TRIG_N instance call. But that's just my personal taste.


(* limit N to size of buffer *)
N := MIN(N, SIZEOF(buffer)/4);

(* startup initialisation *)
IF NOT init OR rst OR N = 0 OR N <> N_old  THEN
   init := TRUE;
   FOR i := 0 TO UINT_TO_INT(N)-1 DO
      buffer := X;
   END_FOR;
   sum := X * N;
   Y := X;
ELSE
   i := INC1(i, UINT_TO_INT(N));
   sum := sum + X - buffer;
   Y := sum / N;
   buffer := X;
END_IF;

N_old := N;



With N_old being an FB internal variable, of course.

Gesture

We are delighted to be part of providing good information for you.