Page 1 of 1

Moving average of 200 readings

Posted: 24 Apr 2023, 17:05
by Zidanoff
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

Re: Moving average of 200 readings

Posted: 24 Apr 2023, 18:59
by Ath
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.

Re: Moving average of 200 readings

Posted: 24 Apr 2023, 19:41
by TD-er
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?

Re: Moving average of 200 readings

Posted: 24 Apr 2023, 20:14
by TD-er
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.

Re: Moving average of 200 readings

Posted: 24 Apr 2023, 20:37
by Ath
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

Re: Moving average of 200 readings

Posted: 25 Apr 2023, 11:20
by Zidanoff
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.

Re: Moving average of 200 readings

Posted: 25 Apr 2023, 12:01
by TD-er
Great, so I guess we should add this rules example to the documentation.

Re: Moving average of 200 readings

Posted: 25 Apr 2023, 20:14
by Ath
I've created a PR #4626 to add this as an example to the rules-documentation

Re: Moving average of 200 readings

Posted: 26 Apr 2023, 10:42
by Zidanoff
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

Re: Moving average of 200 readings

Posted: 26 Apr 2023, 11:26
by TD-er
Always really nice to see when the user finds bugs in the examples:
- Meaning they really understand it :)
- General improvement of the code/docs :) :)