Commit 3663b358 by Stefan Schreistetter

Added Fake Tracker Class. re-implemented Gaze decimator.

parent 54d4ce37
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using TrackerBridge; using TrackerBridge;
using System.Windows.Forms;
using System.Drawing;
using System.Timers;
using TrackerBridge.DSP; using TrackerBridge.DSP;
namespace GazeWebSocketServer namespace GazeWebSocketServer
...@@ -11,16 +8,18 @@ namespace GazeWebSocketServer ...@@ -11,16 +8,18 @@ namespace GazeWebSocketServer
public class Program public class Program
{ {
private static GazeServer gazeServer; private static GazeServer gazeServer;
private static GazeDecimatorFixedLength decimator = new GazeDecimatorFixedLength(1, 600);
public static void Main(string[] args) public static void Main(string[] args)
{ {
ConfigurationData.InitializeUnexistingWithDefaults(); ConfigurationData.InitializeUnexistingWithDefaults();
ConfigurationData config = ConfigurationData.ParseToObject(); ConfigurationData config = ConfigurationData.ParseToObject();
IBridgeTracker tracker;
if (args.Length > 0 && args[0] == "-d") if (args.Length > 0 && args[0] == "-d")
{ {
Task.Run(() => SimulateGazeDataHz(600)); tracker = new FakeTracker(600);
tracker.GazeDataAvailable += OnGazeDataAvailable; //TODO: Extend interface usage
//tracker.GazeDataProcessor.OutputFrequency = config.OutputFrequency;
} }
else else
{ {
...@@ -36,25 +35,9 @@ namespace GazeWebSocketServer ...@@ -36,25 +35,9 @@ namespace GazeWebSocketServer
Console.ReadLine(); Console.ReadLine();
} }
private static void SimulateGazeDataHz(Int32 frequency)
{
System.Timers.Timer timer = new System.Timers.Timer(1000.0/frequency);
timer.Elapsed += (Object sender, ElapsedEventArgs e) =>
{
Point mousePosition = Control.MousePosition;
GazeData data = new GazeData(mousePosition.X, mousePosition.Y, mousePosition.X, mousePosition.Y, 0, 0);
if (gazeServer != null && gazeServer.isRunning)
{
decimator.Input(data);
if(decimator.Output.HasValue)
gazeServer.Publish(data);
}
};
timer.AutoReset = true;
timer.Start();
}
private static void OnGazeDataAvailable(TobiiEyeTracker sender, GazeData data) private static void OnGazeDataAvailable(GazeData data, Object sender)
{ {
if (gazeServer != null && gazeServer.isRunning) if (gazeServer != null && gazeServer.isRunning)
{ {
...@@ -62,12 +45,12 @@ namespace GazeWebSocketServer ...@@ -62,12 +45,12 @@ namespace GazeWebSocketServer
} }
} }
private static void OnConnectionTimeout(TobiiEyeTracker sender) private static void OnConnectionTimeout(IBridgeTracker sender)
{ {
Console.WriteLine("Timeout while searching for trackers."); Console.WriteLine("Timeout while searching for trackers.");
} }
private static void OnConnectionEstablished(TobiiEyeTracker sender) private static void OnConnectionEstablished(IBridgeTracker sender)
{ {
Console.WriteLine($"Tracker {sender.SerialNumber} connected."); Console.WriteLine($"Tracker {sender.SerialNumber} connected.");
ConfigurationData config = ConfigurationData.ParseToObject(); ConfigurationData config = ConfigurationData.ParseToObject();
......
...@@ -3,91 +3,94 @@ using System.Collections.Generic; ...@@ -3,91 +3,94 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Timers;
namespace TrackerBridge.DSP namespace TrackerBridge.DSP
{ {
public class GazeDecimatorFixedLength : IGazeFilter class GazeDecimator : IGazeFilter
{ {
private GazeData[] buffer; public event GazeDataHandler OutputAvailable;
private Int32 currentValueIndex;
private Single inputFrequency; private Single inputFrequency;
private GazeData? output; private GazeData output;
private Single outputFrequency; private Single outputFrequency;
private Object outputLock = new Object(); private Object outputLock = new Object();
private Object inputLock = new Object(); private Object valueCountLock = new Object();
private Int64 valueCount;
public event EventHandler<GazeData> OutputAvailable; public Single InputFrequency { get => inputFrequency; set => inputFrequency = value; }
private Int64 ValueCount {
public Single InputFrequency { get {
get { return inputFrequency; } lock (valueCountLock)
{
return valueCount;
}
}
set { set {
inputFrequency = value; lock (valueCountLock)
Reset(); {
valueCount = value;
}
} }
} }
public GazeData? Output {
private GazeData Output {
get { get {
lock (outputLock) lock (outputLock)
{ {
GazeData? temp = null; return output;
if (output.HasValue)
{
temp = output.Value;
output = null;
}
return temp;
} }
} }
}
public Single OutputFrequency {
get { return outputFrequency; }
set { set {
outputFrequency = value; lock (outputLock)
Reset(); {
output = value;
}
} }
} }
public GazeDecimatorFixedLength(Single outputFrequency, Single inputFrequency) public GazeDecimator(Single outputFrequency, Single inputFrequency)
{ {
this.outputFrequency = outputFrequency; this.outputFrequency = outputFrequency;
this.inputFrequency = inputFrequency; this.InputFrequency = inputFrequency;
Reset(); Task.Run(() => TriggerOutput(outputFrequency));
} }
public void Input(GazeData input, Object sender = null)
public void Input(GazeData input)
{ {
lock (inputLock) if (ValueCount == 0)
{ {
currentValueIndex = (currentValueIndex + 1) % buffer.Length; Output = input;
} }
buffer[currentValueIndex] = input; else
if (currentValueIndex == buffer.Length-1)
{ {
GazeData sum = buffer[0]; //Cumulative moving average (CMA) -> https://en.wikipedia.org/wiki/Moving_average#Cumulative_moving_average
for (Int32 index = 1; index < buffer.Length; index++)
{
sum += buffer[index];
}
sum /= buffer.Length;
lock (outputLock) lock (outputLock)
{ {
output = sum; output += (input - output) / (ValueCount + 1);
} }
}
}
ValueCount++;
} }
public void Reset() public void Reset()
{ {
Int32 bufferLength = Convert.ToInt32(InputFrequency / OutputFrequency); ValueCount = 0;
buffer = new GazeData[bufferLength]; }
currentValueIndex = -1;
output = null; private void TriggerOutput(Single frequency)
{
Timer timer = new Timer(1000.0 / frequency); //TODO: Implement Dispose
timer.Elapsed += (Object sender, ElapsedEventArgs e) =>
{
if (ValueCount > 0)
{
OutputAvailable?.Invoke(Output, this);
Reset();
}
};
timer.AutoReset = true;
timer.Start();
} }
} }
} }
...@@ -8,9 +8,8 @@ namespace TrackerBridge.DSP ...@@ -8,9 +8,8 @@ namespace TrackerBridge.DSP
{ {
interface IGazeFilter interface IGazeFilter
{ {
event EventHandler<GazeData> OutputAvailable; event GazeDataHandler OutputAvailable;
GazeData? Output { get; } void Input(GazeData input, Object sender = null);
void Input(GazeData input);
void Reset(); void Reset();
} }
} }
using System;
using System.Drawing;
using System.Threading.Tasks;
using System.Timers;
using System.Windows.Forms;
namespace TrackerBridge
{
public class FakeTracker : IBridgeTracker
{
private event GazeDataHandler gazeDataAvailable;
public event GazeDataHandler GazeDataAvailable {
add {
gazeDataAvailable += value;
GazeDataProcessor.AddClient(value);
}
remove {
gazeDataAvailable -= value;
GazeDataProcessor.RemoveClient(value);
}
}
public event EventHandler<Single> TrackingFrequencyChanged;
private Single trackerFrequency;
public GazeDataHandler GazeDataAvailableMembers => gazeDataAvailable;
public String SerialNumber => throw new NotImplementedException();
public GazeDataProcessor GazeDataProcessor { get; }
public FakeTracker(Single frequency)
{
trackerFrequency = frequency;
GazeDataProcessor = new GazeDataProcessor(this);
//GazeDataProcessor.OutputFrequency = 1;
Task.Run(() => SimulateGazeDataHz(frequency));
}
private void SimulateGazeDataHz(Single frequency)
{
System.Timers.Timer timer = new System.Timers.Timer(1000.0 / frequency); //TODO: Implement Dispose
timer.Elapsed += (Object sender, ElapsedEventArgs e) =>
{
Point mousePosition = Control.MousePosition;
GazeData data = new GazeData(mousePosition.X, mousePosition.Y, mousePosition.X, mousePosition.Y, 0, 0);
GazeDataProcessor.Process(this, data);
};
timer.AutoReset = true;
timer.Start();
}
public Single GetTrackingFrequency()
{
return trackerFrequency;
}
public void SetTrackingFrequency(Single value)
{
throw new NotImplementedException();
}
}
}
...@@ -44,6 +44,18 @@ namespace TrackerBridge ...@@ -44,6 +44,18 @@ namespace TrackerBridge
return result; return result;
} }
public static GazeData operator -(GazeData a, GazeData b)
{
GazeData result = new GazeData(a.leftX - b.leftX,
a.leftY - b.leftY,
a.rightX - b.rightX,
a.rightY - b.rightY,
Math.Abs(a.trackerTimeStamp - b.trackerTimeStamp),
Math.Abs(a.systemTimeStamp - b.systemTimeStamp));
return result;
}
public static GazeData operator /(GazeData a, Single b) public static GazeData operator /(GazeData a, Single b)
{ {
GazeData result = new GazeData(a.leftX / b, GazeData result = new GazeData(a.leftX / b,
......
...@@ -11,8 +11,6 @@ namespace TrackerBridge ...@@ -11,8 +11,6 @@ namespace TrackerBridge
{ {
public class GazeDataProcessor public class GazeDataProcessor
{ {
public event EventHandler<GazeData> ProcessedGazeDataAvailable;
private readonly IGazeFilter[] gazeFilters; private readonly IGazeFilter[] gazeFilters;
private readonly Single screenHeight; private readonly Single screenHeight;
private readonly Single screenWidth; private readonly Single screenWidth;
...@@ -26,15 +24,37 @@ namespace TrackerBridge ...@@ -26,15 +24,37 @@ namespace TrackerBridge
} }
} }
public GazeDataProcessor(IBridgeTracker tracker) public GazeDataProcessor(IBridgeTracker tracker)
{ {
tracker.TrackingFrequencyChanged += OnTrackingFrequencyChanged; tracker.TrackingFrequencyChanged += OnTrackingFrequencyChanged;
screenHeight = Convert.ToSingle(SystemParameters.PrimaryScreenHeight); screenHeight = Convert.ToSingle(SystemParameters.PrimaryScreenHeight);
screenWidth = Convert.ToSingle(SystemParameters.PrimaryScreenWidth); screenWidth = Convert.ToSingle(SystemParameters.PrimaryScreenWidth);
gazeFilters = new IGazeFilter[1]; gazeFilters = new IGazeFilter[1];
gazeFilters[0] = new GazeDecimatorFixedLength(outputFrequency, tracker.GetTrackingFrequency()); gazeFilters[0] = new GazeDecimator(60, tracker.GetTrackingFrequency()); //TODO: Refactor frequency passthrough
//Chain filters
for(Int32 index= 0; index < gazeFilters.Length; index++)
{
if(index == gazeFilters.Length - 1)
{
gazeFilters[index].OutputAvailable += tracker.GazeDataAvailableMembers;
}
else
{
gazeFilters[index].OutputAvailable += gazeFilters[index + 1].Input;
}
}
}
public void AddClient(GazeDataHandler gazeDataHandler)
{
gazeFilters[gazeFilters.Length - 1].OutputAvailable += gazeDataHandler;
}
public void RemoveClient(GazeDataHandler gazeDataHandler)
{
gazeFilters[gazeFilters.Length - 1].OutputAvailable -= gazeDataHandler;
} }
public void Initialize() public void Initialize()
{ {
foreach (IGazeFilter filter in gazeFilters) foreach (IGazeFilter filter in gazeFilters)
...@@ -42,8 +62,8 @@ namespace TrackerBridge ...@@ -42,8 +62,8 @@ namespace TrackerBridge
filter.Reset(); filter.Reset();
} }
} }
public GazeData? Process(GazeDataEventArgs e) public void Process(Object sender, GazeDataEventArgs e)
{ {
GazeData data = new GazeData(e.LeftEye.GazePoint.PositionOnDisplayArea.X * screenWidth, GazeData data = new GazeData(e.LeftEye.GazePoint.PositionOnDisplayArea.X * screenWidth,
e.LeftEye.GazePoint.PositionOnDisplayArea.Y * screenHeight, e.LeftEye.GazePoint.PositionOnDisplayArea.Y * screenHeight,
...@@ -53,23 +73,18 @@ namespace TrackerBridge ...@@ -53,23 +73,18 @@ namespace TrackerBridge
e.SystemTimeStamp); e.SystemTimeStamp);
gazeFilters[0].Input(data); gazeFilters[0].Input(data);
for (Int32 index = 1; index < gazeFilters.Length; index++) }
{ public void Process(Object sender, GazeData input)
if (gazeFilters[index - 1].Output.HasValue) {
{ gazeFilters[0].Input(input);
gazeFilters[index].Input(gazeFilters[index - 1].Output.Value);
}
}
//TODO: If output, invoke event
return gazeFilters[gazeFilters.Length - 1].Output;
} }
private void OnTrackingFrequencyChanged(Object sender, Single frequency) private void OnTrackingFrequencyChanged(Object sender, Single frequency)
{ {
foreach(IGazeFilter filter in gazeFilters) foreach(IGazeFilter filter in gazeFilters)
{ {
if(filter.GetType() == typeof(GazeDecimatorFixedLength)) if(filter.GetType() == typeof(GazeDecimator))
{ {
GazeDecimatorFixedLength decimator = (GazeDecimatorFixedLength)filter; GazeDecimator decimator = (GazeDecimator)filter;
decimator.InputFrequency = frequency; decimator.InputFrequency = frequency;
} }
} }
......
...@@ -6,10 +6,19 @@ using System.Threading.Tasks; ...@@ -6,10 +6,19 @@ using System.Threading.Tasks;
namespace TrackerBridge namespace TrackerBridge
{ {
public delegate void ConnectionEventHandler(IBridgeTracker sender);
public delegate void GazeDataHandler(GazeData data, Object sender);
public interface IBridgeTracker public interface IBridgeTracker
{ {
event GazeDataHandler GazeDataAvailable;
event EventHandler<Single> TrackingFrequencyChanged; event EventHandler<Single> TrackingFrequencyChanged;
void SetTrackingFrequency(Single value);
GazeDataHandler GazeDataAvailableMembers { get; }
GazeDataProcessor GazeDataProcessor { get;}
String SerialNumber { get; }
Single GetTrackingFrequency(); Single GetTrackingFrequency();
void SetTrackingFrequency(Single value);
} }
} }
...@@ -9,8 +9,8 @@ namespace TrackerBridge ...@@ -9,8 +9,8 @@ namespace TrackerBridge
{ {
public class TobiiEyeTracker : IBridgeTracker public class TobiiEyeTracker : IBridgeTracker
{ {
public delegate void ConnectionEventHandler(TobiiEyeTracker sender); //public delegate void ConnectionEventHandler(TobiiEyeTracker sender);
public delegate void GazeDataHandler(TobiiEyeTracker sender, GazeData data); //public delegate void GazeDataHandler(TobiiEyeTracker sender, GazeData data);
public event ConnectionEventHandler ConnectionEvent; public event ConnectionEventHandler ConnectionEvent;
public event ConnectionEventHandler ConnectionTimeout; public event ConnectionEventHandler ConnectionTimeout;
...@@ -19,10 +19,16 @@ namespace TrackerBridge ...@@ -19,10 +19,16 @@ namespace TrackerBridge
private IEyeTracker eyeTracker = null; private IEyeTracker eyeTracker = null;
public GazeDataProcessor GazeDataProcessor { get; set; } public GazeDataProcessor GazeDataProcessor { get; }
public Boolean IsConnected { get; private set; } = false; public Boolean IsConnected { get; private set; } = false;
public String SerialNumber { get; private set; } = null; public String SerialNumber { get; private set; } = null;
public GazeDataHandler GazeDataAvailableMembers {
get {
return GazeDataAvailable;
}
}
public TobiiEyeTracker() public TobiiEyeTracker()
{ {
GazeDataProcessor = new GazeDataProcessor(this); GazeDataProcessor = new GazeDataProcessor(this);
...@@ -62,10 +68,6 @@ namespace TrackerBridge ...@@ -62,10 +68,6 @@ namespace TrackerBridge
eyeTracker.SetGazeOutputFrequency(value); eyeTracker.SetGazeOutputFrequency(value);
} }
} }
void IBridgeTracker.SetTrackingFrequency(Single value)
{
throw new NotImplementedException();
}
private void InitiateConnection() private void InitiateConnection()
{ {
...@@ -103,22 +105,13 @@ namespace TrackerBridge ...@@ -103,22 +105,13 @@ namespace TrackerBridge
ConnectionTimeout?.Invoke(this); ConnectionTimeout?.Invoke(this);
} }
} }
eyeTracker.GazeDataReceived += OnGazeDataReceived; eyeTracker.GazeDataReceived += GazeDataProcessor.Process;
eyeTracker.GazeOutputFrequencyChanged += OnGazeOutputFrequencyChanged; eyeTracker.GazeOutputFrequencyChanged += OnGazeOutputFrequencyChanged;
SerialNumber = eyeTracker.SerialNumber; SerialNumber = eyeTracker.SerialNumber;
SetTrackingFrequency(600f); SetTrackingFrequency(600f);
ConnectionEvent?.Invoke(this); ConnectionEvent?.Invoke(this);
} }
private void OnGazeDataReceived(object sender, GazeDataEventArgs e)
{
GazeData? gazeData = GazeDataProcessor.Process(e);
if (gazeData.HasValue)
{
GazeDataAvailable?.Invoke(this, gazeData.Value);
}
}
private void OnGazeOutputFrequencyChanged(Object sender, GazeOutputFrequencyEventArgs e) private void OnGazeOutputFrequencyChanged(Object sender, GazeOutputFrequencyEventArgs e)
{ {
TrackingFrequencyChanged?.Invoke(this, e.GazeOutputFrequency); TrackingFrequencyChanged?.Invoke(this, e.GazeOutputFrequency);
......
...@@ -34,9 +34,12 @@ ...@@ -34,9 +34,12 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" /> <Reference Include="PresentationFramework" />
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" /> <Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
...@@ -48,7 +51,8 @@ ...@@ -48,7 +51,8 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="DSP\GazeDecimatorFixedLength.cs" /> <Compile Include="DSP\GazeDecimator.cs" />
<Compile Include="FakeTracker.cs" />
<Compile Include="GazeData.cs" /> <Compile Include="GazeData.cs" />
<Compile Include="GazeDataProcessor.cs" /> <Compile Include="GazeDataProcessor.cs" />
<Compile Include="DSP\IGazeFilter.cs" /> <Compile Include="DSP\IGazeFilter.cs" />
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment