Rolling average - circular buffer questions

For general Flowcode discussion that does not belong in the other sections.
Post Reply
RGV250
Posts: 398
http://meble-kuchenne.info.pl
Joined: Sat Mar 19, 2022 4:53 pm
Has thanked: 41 times
Been thanked: 40 times

Rolling average - circular buffer questions

Post by RGV250 »

Hello,
I am looking at creating a rolling average macro, in a PLC I would create an array and populate it rolling over to zero and start again. I would then read the data back and if it crosses zero continue reading from the highest element.

As FC has a circular buffer I thought I would look into that, there are no examples so I have some questions.
Is there a limit to the size, if I am doing 15 mins I will need 900+ for 1 reading / second.
Put Data says it returns a zero if the buffer is full. If I set the storage type to "Store last x values" is this still correct as the buffer will always be full?
I think I will need to us GetIndexedValue but there is no information on which end of the buffer is the reference IE if I set the size to 1000 does it fill from 0 to 1000 or 1000 back to 0. If I then only want to read 900 values do I do 1000 to 100 step -1 or 0 to 900 step 1. This syntax might not be right for FC but I hope it makes sense.

Regards,
Bob

chipfryer27
Valued Contributor
Posts: 1877
Joined: Thu Dec 03, 2020 10:57 am
Has thanked: 409 times
Been thanked: 633 times

Re: Rolling average - circular buffer questions

Post by chipfryer27 »

Hi

Just a quick thought before bed.

Have you looked under both the Math and DSP>Math components as they may be able to assist?

Regards

mnfisher
Valued Contributor
Posts: 1824
Joined: Wed Dec 09, 2020 9:37 pm
Has thanked: 153 times
Been thanked: 861 times

Re: Rolling average - circular buffer questions

Post by mnfisher »

Should be able to do what you want it to..

If you set to save the last '5' values (as an example - I know you will need more)

So it will always contain 5 values (after at least 5 have been read) - and adding new will overwrite the earliest data. However - get indexed value returns the value at the index of the array - so to give an example:

Buffer holds 1,2,3,4,5 (Why no demo with 900 items!)
PutData(6)
Buf = 6,2,3,4,5 (The data loops around - so now GetIndex (0) returns 6)

If you are averaging the last 900 (or 5) values - reading the values 0..899 (or 0..4 in my case) will still give the result you are after.

So 1,2,3,4,5 - Average(Sum(GetIndex[0..4]) = 15/5 = 3
6,2,3,4,5 - Average = 20 / 5 = 4

If you need the correct order of the values ( - getting faster 2,3,4,5,6) then an alternative is to store to an array - and once it is full shift everything along to the 'left' (strictly lower indexes) by 1

Array = 1,2,3,4,5 - Add new value (6)
for(i = 0; i < 4; i++) array = array[i + 1];
Array[4] = 6
Array = 2,3,4,5,6

This has the disadvantage that you are moving 900 (or more) values every time you add a value to the buffer (at least once it is full) - however this is a quick operation - and new reading every second - would be no issue.

As per Iain's reply - look at Math->running average - this may well do what you need!

Obviously your MCU will need sufficient memory to hold all the data - circular buffer uses an array (and extra head / tail pointers to keep track of the data)

Martin

RGV250
Posts: 398
Joined: Sat Mar 19, 2022 4:53 pm
Has thanked: 41 times
Been thanked: 40 times

Re: Rolling average - circular buffer questions

Post by RGV250 »

Hi Martin,
So 1,2,3,4,5 - Average(Sum(GetIndex[0..4]) = 15/5 = 3
Will do what I require and a very neat solution. I may allow to select the option for 5, 10, 15 mins so if I have 0-899 or 0-8
For the last 5 mins would I do 0,1,2,3,4,5,6,7,8 Average(3(GetIndex[6..8]) or Average(3(GetIndex[0..2])
For the last 10 mins would I do 0,1,2,3,4,5,6,7,8 Average(6(GetIndex[3..8]) or Average(6(GetIndex[0..5])

Regards,
Bob

mnfisher
Valued Contributor
Posts: 1824
Joined: Wed Dec 09, 2020 9:37 pm
Has thanked: 153 times
Been thanked: 861 times

Re: Rolling average - circular buffer questions

Post by mnfisher »

If using the circular buffer - then if you don't mind destroying the contents of the buffer - then it's possible - it returns the earliest samples first (so - if you want the last 10 - then discard until 10 (or 10 minutes worth) remain - then average)
If you need to keep all the values (user chooses last 5mins then - last 10mins) - then using an array and shifting might work better?

Martin

RGV250
Posts: 398
Joined: Sat Mar 19, 2022 4:53 pm
Has thanked: 41 times
Been thanked: 40 times

Re: Rolling average - circular buffer questions

Post by RGV250 »

Hi Martin,
Thanks, I am surprised you say the circular buffer destroys the data, is this normal, in the Wiki GetindexedValue says it leaves the contents and pointers untouched. Or is in because in the example it is reading an array?
If that is the case I can loop through the values rather than reading in an array to do the same thing.
I cannot destroy the data as it is a rolling average that will constantly be being updated.

If it does indeed destroy the data I will stick with the way I know, a lot more long winded but I know it should work.
What I do is populate the array util it is full and then start again from zero overwriting the previous value, when I read the data (assuming 900 points) say from position 500 when I get to zero I read the next 400 from the end of the array. I think this is a lot more efficient than shifting the whole array every new entry.

Regards,
Bob

mnfisher
Valued Contributor
Posts: 1824
Joined: Wed Dec 09, 2020 9:37 pm
Has thanked: 153 times
Been thanked: 861 times

Re: Rolling average - circular buffer questions

Post by mnfisher »

It does 'destroy' the data if you use GetValue.

So if you need the last 5 values then GetIndexValue doesn't work - it does what you say - it writes to the end of the array and then starts again at the beginning.

It would be possible to create a circular buffer that has the behaviour you require (possibly by modifying the component code) - or use the shifted array as above.

Martin

Post Reply