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
Rolling average - circular buffer questions
-
RGV250
- Posts: 398
- http://meble-kuchenne.info.pl
- Joined: Sat Mar 19, 2022 4:53 pm
- Has thanked: 41 times
- Been thanked: 40 times
-
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
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
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
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
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
Re: Rolling average - circular buffer questions
Hi Martin,
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
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-8So 1,2,3,4,5 - Average(Sum(GetIndex[0..4]) = 15/5 = 3
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
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
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
Re: Rolling average - circular buffer questions
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
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
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
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