Moving average of 200 readings

Moderators: grovkillen, Stuntteam, TD-er

Post Reply
Message
Author
Zidanoff
Normal user
Posts: 19
Joined: 09 Apr 2023, 13:52
Location: Ukraine

Moving average of 200 readings

#1 Post by Zidanoff » 24 Apr 2023, 17:05

Greetings! Right now I'm using the standard averaging of readings, but is it possible to get a moving average of 200 readings? Variables are limited to 16, so the problem is not trivial.

Code: Select all

Let,10,[VAR#9]
 Let,9,[VAR#8]
 Let,8,[VAR#7]
 Let,7,[VAR#6]
 Let,6,[VAR#5]
 Let,5,[VAR#4]
 Let,4,[VAR#3]
 Let,3,[VAR#2]
 Let,2,[VAR#1]
 Let,1,%eventvalue2%
 TaskValueSet,3,4,([VAR#1]+[VAR#2]+[VAR#3]+[VAR#4]+[VAR#5]+[VAR#6]+[VAR#7]+[VAR#8]+[VAR#9]+[VAR#10])/10

User avatar
Ath
Normal user
Posts: 3520
Joined: 10 Jun 2018, 12:06
Location: NL

Re: Moving average of 200 readings

#2 Post by Ath » 24 Apr 2023, 18:59

Zidanoff wrote: 24 Apr 2023, 17:05 Variables are limited to 16, so the problem is not trivial.
Nope, variables are limited only by the amount of memory available, but processing speed will be the biggest limiting factor, repeatedly processing those 200+ script lines will put quite some stress on the CPU (but it won't protest about that), making other tasks on that unit possibly delayed in execution.
/Ton (PayPal.me)

TD-er
Core team member
Posts: 8756
Joined: 01 Sep 2017, 22:13
Location: the Netherlands
Contact:

Re: Moving average of 200 readings

#3 Post by TD-er » 24 Apr 2023, 19:41

Are you using an ESP32 or ESP8266?

When using "stats" for task values, you can perform computations over upto N readings using those plugin stats.
N is 16 for ESP8266 and 64 for ESP32 due to memory limitations on ESP8266.
See: https://espeasy.readthedocs.io/en/lates ... statistics

In recent code changes (not yet in an official build) you can also use this for Dummy tasks.

So maybe you could use some tricks to average over the last 200 samples from a task.
For example by adding every sample from a task to a dummy task value, but then modulo 4 to the next dummy task value.
Then you can simply use the average over the last 50 values for each task value of this dummy task to work with (assuming you're using an ESP32)

On ESP8266 this may be a bit tricky, but I guess you really don't need to have the exact 200 readings, but more like the average over the period of the last 200 reading?

TD-er
Core team member
Posts: 8756
Joined: 01 Sep 2017, 22:13
Location: the Netherlands
Contact:

Re: Moving average of 200 readings

#4 Post by TD-er » 24 Apr 2023, 20:14

Just an idea for a relatively efficient moving average:

Code: Select all


on MyEvent do
	// %v201% = average
	// %v202% = last element
	// %v203% = nr Elements
	// %v204% = sum

	if %v203% < 200
	  let,202,%v202%+1  // Update index of "last element"
	  let,203,%v203%+1  // Update nr Elements
	else
	  let,204,%v204%-[var#%v202%]  // Subtract oldest element from the sum
	  let,202,%v202%+1
	  if %v202% >= 200
		let,202,0      // Index of "last element" should be modulo 200
	  endif
	endif
	let,%v202%,%eventvalue%     // Store the new value in the array
	let,204,%v204%+[var#%v202%] // Add new value to the sum
	
	let,201,%v204%/%v203% // Average
endon
Not tested, just a quick dump of some idea I had when thinking about the question.

User avatar
Ath
Normal user
Posts: 3520
Joined: 10 Jun 2018, 12:06
Location: NL

Re: Moving average of 200 readings

#5 Post by Ath » 24 Apr 2023, 20:37

Did a small refinement of that code (in coordination with TD-er), making the number of elements configurable (but max. 200)

Code: Select all

on MyEvent do
  // %v201% = max elements
  // %v202% = last element
  // %v203% = nr Elements
  // %v204% = sum
  // %v205% = average

  if %v201%=0 // Not yet set?
    let,201,200 // Set max number of elements, don't set > 200!!!
  endif

  if %v203% < %v201%
    let,202,%v202%+1  // Update index of "last element"
    let,203,%v203%+1  // Update nr Elements
  else
    let,204,%v204%-[var#%v202%]  // Subtract oldest element from the sum
    let,202,%v202%+1
    if %v202% >= %v201%
      let,202,0      // Index of "last element" should be modulo max elements
    endif
  endif
  let,%v202%,%eventvalue1%     // Store the new value in the array
  let,204,%v204%+[var#%v202%] // Add new value to the sum
  
  let,205,%v204%/%v203% // Average
endon
Major difference: Output average is available in %v205% (instead of 201)
Minor difference: No tabs used in the code (that somewhat confuses the code formatting)

Instead of the last 'let', you can also use:

Code: Select all

  TaskValueSet,3,4,%v204%/%v203% // Average
/Ton (PayPal.me)

Zidanoff
Normal user
Posts: 19
Joined: 09 Apr 2023, 13:52
Location: Ukraine

Re: Moving average of 200 readings

#6 Post by Zidanoff » 25 Apr 2023, 11:20

Many thanks to TD-er and Ath! Indeed, the option with statistics combined with a dummy device will work. Also, the tricky trick with variables works great! I am using ESP32.

TD-er
Core team member
Posts: 8756
Joined: 01 Sep 2017, 22:13
Location: the Netherlands
Contact:

Re: Moving average of 200 readings

#7 Post by TD-er » 25 Apr 2023, 12:01

Great, so I guess we should add this rules example to the documentation.

User avatar
Ath
Normal user
Posts: 3520
Joined: 10 Jun 2018, 12:06
Location: NL

Re: Moving average of 200 readings

#8 Post by Ath » 25 Apr 2023, 20:14

I've created a PR #4626 to add this as an example to the rules-documentation
/Ton (PayPal.me)

Zidanoff
Normal user
Posts: 19
Joined: 09 Apr 2023, 13:52
Location: Ukraine

Re: Moving average of 200 readings

#9 Post by Zidanoff » 26 Apr 2023, 10:42

Your wonderful examples inspired to modify them a little so that everything works correctly when stepping over the maximum element. Otherwise, the last element from the sum was not removed and the error accumulated with each new transition cycle through the higher variable.

Code: Select all

 on MyEvent do
 // %v201% = max elements
  // %v202% = last element
  // %v203% = nr Elements
  // %v204% = sum
  // %v205% = average

  if %v201%=0 // Not yet set?
    let,201,200 // Set max number of elements, don't set > 200!!!
  endif

  if %v203% < %v201% // writes until the last element is filled
    let,202,%v202%+1  // Update index of "last element"
    let,203,%v203%+1  // Update nr Elements
  else // writes after the first filling cycle starting from the number of the highest element
     if %v202% = %v201% // “The last will be first, and the first last” (Matthew 20:16)
      let,202,1      // Index of "last element" should be modulo max elements
      let,204,%v204%-[var#1]  // Subtract oldest element from the sum
     else // new sequential write cycle
      let,202,%v202%+1
      let,204,%v204%-[var#%v202%]  // Subtract oldest element from the sum
    endif
  endif
  let,%v202%,%eventvalue1%     // Store the new value in the array
  let,204,%v204%+%eventvalue1% // Add new value to the sum
  let,205,%v204%/%v203% // Average
endon

TD-er
Core team member
Posts: 8756
Joined: 01 Sep 2017, 22:13
Location: the Netherlands
Contact:

Re: Moving average of 200 readings

#10 Post by TD-er » 26 Apr 2023, 11:26

Always really nice to see when the user finds bugs in the examples:
- Meaning they really understand it :)
- General improvement of the code/docs :) :)

Post Reply

Who is online

Users browsing this forum: No registered users and 40 guests