using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Timers; namespace TrackerBridge.DSP { class GazeDecimator : IGazeFilter { public event GazeDataHandler OutputAvailable; private GazeData output; private Single outputFrequency; private Timer timer; private readonly Object outputLock = new Object(); private readonly Object valueCountLock = new Object(); private Int64 valueCount; private Int64 ValueCount { get { lock (valueCountLock) { return valueCount; } } set { lock (valueCountLock) { valueCount = value; } } } private GazeData Output { get { lock (outputLock) { return output; } } set { lock (outputLock) { output = value; } } } public GazeDecimator(GazeDataProcessor dataProcessor) { outputFrequency = dataProcessor.OutputFrequency; SetupTimer(); } public void Input(GazeData input, Object sender = null) { if (ValueCount == 0) { Output = input; } else { //Cumulative moving average (CMA) -> https://en.wikipedia.org/wiki/Moving_average#Cumulative_moving_average lock (outputLock) { output += (input - output) / (ValueCount + 1); } } ValueCount++; } public void Reset() { ValueCount = 0; } private void SetupTimer() { timer?.Stop(); timer?.Dispose(); timer = new Timer(1000.0 / outputFrequency); timer.Elapsed += (Object sender, ElapsedEventArgs e) => { if (ValueCount > 0) { OutputAvailable?.Invoke(Output, this); Reset(); } }; timer.AutoReset = true; timer.Start(); } public void Update(GazeDataProcessor dataProcessor) { outputFrequency = dataProcessor.OutputFrequency; SetupTimer(); } } }