User Tools

Site Tools



Smoothing an Input #1

Averaging and throwing out exceptional readings

Sometimes you've got a temperature or other analog input that will get some bad data, or that fluctuates around the real value enough that it makes it difficult to make decisions based on the input.

This script will help to smooth out the data. What it does is to keep the last 10 readings (thats configurable, could be more if you need it) as it adds a value it walks a list of the last 10 readings, throwing away the highest and the lowest and then averaging what remains.

Installation is simple, cut and paste this script into the ON scrip of the unit you need to manage.

--  Average temperature handler
-- for Chester to get rid of goofy temp readings
--  keeps 10 temp readins in a list in this unit's properties
-- removes the highest and lowest of them and averages the remaining 8 values
--you can change this number to change the number of elements that are averaged
--larger numbers will provide more buffering
set BufferSize to 11
-- get the list of data stored in our unit properties of the last 10 readings
-- but inside a try block as it might not be there if this is a new unit
set TheDataList to Get Unit Property "Data List"
--handling lists can be a pain
-- the Get Unit Property verb will not return an error but a blank string
-- if the property doesn't exist yet, so we first need to check to see if it's
-- an empty string and change it to an empty list
-- and a list that has only 1 element may be converted to a simple string
-- so if the variable is not a list, then we convert it to a list.
if TheDataList is "" then
	set TheDataList to {}
	if class of TheDataList is not list then
		set TheDataList to {TheDataList}
	end if
end if
--add new value to the end of the list
--and remove the oldest if we've reached 10 values
set NewValue to (future value) as number
set the end of TheDataList to NewValue
if (count of TheDataList) is BufferSize then
	set TheDataList to items 2 through BufferSize of TheDataList
end if
--resave back to the unit properties
Set Unit Property "Data List" to TheDataList
--walk the list and filter the biggest problem numbers prior to averaging.
set SkipHighestIndex to 1
set Highest to item 1 of TheDataList
set SkipLowestIndex to 1
set Lowest to item 1 of TheDataList
repeat with i from 1 to (count of TheDataList)
	set ThisData to item i of TheDataList
	if ThisData is greater than Highest then
		set Highest to ThisData
		set SkipHighestIndex to i
	end if
	if ThisData is less than Lowest then
		set Lowest to ThisData
		set SkipLowestIndex to i
	end if
end repeat
--average the data leaving out anything at those indexes
set Adder to 0
set ValueCount to 0
repeat with i from 1 to (count of TheDataList)
	if (i ≠ SkipHighestIndex) and (i ≠ SkipLowestIndex) then
		set ThisData to item i of TheDataList
		set Adder to Adder + ThisData
		set ValueCount to ValueCount + 1
	end if
end repeat
--if we haven't added at least 1 value then we can't divide by zero, so just leave the currently set value
--in that case.
if ValueCount is greater than 0 then
	change future value to (Adder / ValueCount)
end if

Common Questions and Issues

If you're testing this by using the set value verb instead of a real value you may notice that the log shows the value that you entered in the verb and not the processed value. The unit itself will reflect the processed value but in XTension versions to build 734 the log still shows the value as entered. Will sort that out in a future build.

Smoothing an Input #2

Catch new values out of range and ignore.

Another technique : I use this simpler method to catch and 'shape' any rogue readings. Note also that this assumes a couple of other units for capturing the high and low temps for the day. (these are reset at about midnite each nite by a scheduled 'daily maintenance' global script… NOTE that I just toss an error and ignore the reading… (michael)

-- handle Temperature values from the DS2438 in the RFXSensor type 3
-- temp script to convert the values coming from the W800 or RFX.
-- scale the 'future value' ... low 3 bits are = 0.125 each count
set theTemp to (future value)
if (future value) is greater than 1024 then set theTemp to theTemp - 2048
set theTemp to theTemp / 8 as integer
write log "The temperature is " & theTemp & " Centigrade"
set theTemp to (theTemp * 9 / 5) + 32
if theTemp < ((value of (thisUnit)) * 1.15) and ¬
	theTemp > ((value of (thisUnit)) * 0.85) then
	change future value to theTemp
	write log "The temperature is " & theTemp & " Fahrenheit"
	if (future value) is greater than (value of "High Temp Today") then
		set value of "High Temp Today" to (future value)
	end if
	if (future value) is less than (value of "Low Temp Today") then
		set value of "Low Temp Today" to (future value)
	end if
	change future value to (value of (thisUnit))
	write log "Extraordinary temperature reading ... ignored"
end if
tutorials/smoothing_an_input.txt · Last modified: 2009/01/19 12:48 (external edit)