Commit 7f1eeecc by Stefan Schreistetter

Finished prototype of gaze decimator.

parent 6906eca1
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="SteveCadwallader.CodeMaid.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</sectionGroup>
</configSections>
<userSettings>
<SteveCadwallader.CodeMaid.Properties.Settings>
<setting name="Reorganizing_MemberTypeConstructors" serializeAs="String">
<value>Constructors||6||Constructors</value>
</setting>
<setting name="Reorganizing_MemberTypeProperties" serializeAs="String">
<value>Properties||5||Properties</value>
</setting>
<setting name="Reorganizing_MemberTypeEnums" serializeAs="String">
<value>Enums||1||Enums</value>
</setting>
<setting name="Reorganizing_MemberTypeDestructors" serializeAs="String">
<value>Destructors||7||Destructors</value>
</setting>
<setting name="Reorganizing_MemberTypeDelegates" serializeAs="String">
<value>Delegates||2||Delegates</value>
</setting>
<setting name="Reorganizing_MemberTypeFields" serializeAs="String">
<value>Fields||4||Fields</value>
</setting>
<setting name="Reorganizing_MemberTypeInterfaces" serializeAs="String">
<value>Interfaces||8||Interfaces</value>
</setting>
<setting name="Reorganizing_MemberTypeEvents" serializeAs="String">
<value>Events||3||Events</value>
</setting>
</SteveCadwallader.CodeMaid.Properties.Settings>
</userSettings>
</configuration>
\ No newline at end of file
......@@ -3,6 +3,7 @@
<appSettings>
<add key="TrackerSerialNumber" value="TPSP1-010108442544" />
<add key="TrackerFrequency_Hz" value="120" />
<add key="OutputFrequency_Hz" value="60" />
<add key="WebSocketPort" value="8001" />
<add key="WebSocketEndpoint" value="/gaze" />
<add key="DebugFlag" value="false" />
......
......@@ -11,6 +11,7 @@ namespace GazeWebSocketServer
{
public string TrackerSerialNumber { get; private set; }
public float TrackerFrequency { get; private set; }
public float OutputFrequency { get; private set; }
public int WebSocketPort { get; private set; }
public string WebSocketEndpoint { get; private set; }
public bool DebugFlag { get; private set; }
......@@ -21,6 +22,7 @@ namespace GazeWebSocketServer
{
TrackerSerialNumber = ConfigurationManager.AppSettings["TrackerSerialNumber"],
TrackerFrequency = float.Parse(ConfigurationManager.AppSettings["TrackerFrequency_Hz"]),
OutputFrequency = float.Parse(ConfigurationManager.AppSettings["OutputFrequency_Hz"]),
WebSocketPort = int.Parse(ConfigurationManager.AppSettings["WebSocketPort"]),
WebSocketEndpoint = ConfigurationManager.AppSettings["WebSocketEndpoint"],
DebugFlag = bool.Parse(ConfigurationManager.AppSettings["DebugFlag"])
......@@ -32,6 +34,7 @@ namespace GazeWebSocketServer
{
InitializeSetting("TrackerSerialNumber", "TPSP1-");
InitializeSetting("TrackerFrequency_Hz", "600");
InitializeSetting("OutputFrequency_Hz", "60");
InitializeSetting("WebSocketPort", "8001");
InitializeSetting("WebSocketEndpoint", "/gaze");
InitializeSetting("DebugFlag", "false");
......
......@@ -4,12 +4,15 @@ using TrackerBridge;
using System.Windows.Forms;
using System.Drawing;
using System.Timers;
using TrackerBridge.DSP;
namespace GazeWebSocketServer
{
public class Program
{
private static GazeServer gazeServer;
private static GazeDecimator decimator = new GazeDecimator(1, 600);
public static void Main(string[] args)
{
ConfigurationData.InitializeUnexistingWithDefaults();
......@@ -17,7 +20,7 @@ namespace GazeWebSocketServer
if (args.Length > 0 && args[0] == "-d")
{
Task.Run(() => SimulateGazeDataHz(120));
Task.Run(() => SimulateGazeDataHz(10));
}
else
{
......@@ -35,14 +38,16 @@ namespace GazeWebSocketServer
private static void SimulateGazeDataHz(Int32 frequency)
{
System.Timers.Timer timer = new System.Timers.Timer(1.0/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)
{
gazeServer.Publish(data);
decimator.Input(data);
if(decimator.Output.HasValue)
gazeServer.Publish(data);
}
};
timer.AutoReset = true;
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TrackerBridge.DSP
{
public class GazeDecimator : IGazeFilter
{
private GazeData[] buffer;
private Int32 currentValueIndex;
private Single inputFrequency;
private GazeData? output;
private Single outputFrequency;
private Object outputLock = new Object();
private Object inputLock = new Object();
public Single InputFrequency {
get { return inputFrequency; }
set {
inputFrequency = value;
Reset();
}
}
public GazeData? Output {
get {
lock (outputLock)
{
GazeData? temp = null;
if (output.HasValue)
{
temp = output.Value;
output = null;
}
return temp;
}
}
}
public Single OutputFrequency {
get { return outputFrequency; }
set {
outputFrequency = value;
Reset();
}
}
public GazeDecimator(Single outputFrequency, Single inputFrequency)
{
this.outputFrequency = outputFrequency;
this.inputFrequency = inputFrequency;
Reset();
}
public void Input(GazeData input)
{
lock (inputLock)
{
buffer[currentValueIndex] = input;
currentValueIndex = (currentValueIndex + 1) % buffer.Length;
if (currentValueIndex == 0)
{
GazeData sum = buffer[0];
for (Int32 index = 1; index < buffer.Length; index++)
{
sum += buffer[index];
}
lock (outputLock)
{
output = sum / buffer.Length;
}
}
}
}
public void Reset()
{
Int32 bufferLength = Convert.ToInt32(InputFrequency / OutputFrequency);
buffer = new GazeData[bufferLength];
currentValueIndex = 0;
output = null;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TrackerBridge.DSP
{
interface IGazeFilter
{
GazeData? Output { get; }
void Input(GazeData input);
void Reset();
}
}
......@@ -31,5 +31,29 @@ namespace TrackerBridge
FormattableString message = $"{leftX};{leftY};{rightX};{rightY};{trackerTimeStamp};{systemTimeStamp}";
return FormattableString.Invariant(message);
}
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)
{
GazeData result = new GazeData(a.leftX / b,
a.leftY / b,
a.rightX / b,
a.rightY / b,
a.trackerTimeStamp / Convert.ToInt64(b),
a.systemTimeStamp / Convert.ToInt64(b));
return result;
}
}
}
......@@ -5,21 +5,44 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Tobii.Research;
using TrackerBridge.DSP;
namespace TrackerBridge
{
public class GazeDataProcessor
{
readonly float screenHeight;
readonly float screenWidth;
public event EventHandler<GazeData> ProcessedGazeDataAvailable;
public GazeDataProcessor()
private readonly IGazeFilter[] gazeFilters;
private readonly Single screenHeight;
private readonly Single screenWidth;
private Single outputFrequency;
public Single OutputFrequency {
get { return outputFrequency; }
set {
outputFrequency = value;
Initialize();
}
}
public GazeDataProcessor(IBridgeTracker tracker)
{
tracker.TrackingFrequencyChanged += OnTrackingFrequencyChanged;
screenHeight = Convert.ToSingle(SystemParameters.PrimaryScreenHeight);
screenWidth = Convert.ToSingle(SystemParameters.PrimaryScreenWidth);
gazeFilters = new IGazeFilter[1];
gazeFilters[0] = new GazeDecimator(outputFrequency, tracker.GetTrackingFrequency());
}
public void Initialize()
{
foreach (IGazeFilter filter in gazeFilters)
{
filter.Reset();
}
}
public GazeData Extract(GazeDataEventArgs e)
public GazeData? Process(GazeDataEventArgs e)
{
GazeData data = new GazeData(e.LeftEye.GazePoint.PositionOnDisplayArea.X * screenWidth,
e.LeftEye.GazePoint.PositionOnDisplayArea.Y * screenHeight,
......@@ -27,7 +50,28 @@ namespace TrackerBridge
e.RightEye.GazePoint.PositionOnDisplayArea.Y * screenHeight,
e.DeviceTimeStamp,
e.SystemTimeStamp);
return data;
gazeFilters[0].Input(data);
for (Int32 index = 1; index < gazeFilters.Length; index++)
{
if (gazeFilters[index - 1].Output.HasValue)
{
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)
{
foreach(IGazeFilter filter in gazeFilters)
{
if(filter.GetType() == typeof(GazeDecimator))
{
GazeDecimator decimator = (GazeDecimator)filter;
decimator.InputFrequency = frequency;
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TrackerBridge
{
public interface IBridgeTracker
{
event EventHandler<Single> TrackingFrequencyChanged;
void SetTrackingFrequency(Single value);
Single GetTrackingFrequency();
}
}
......@@ -7,32 +7,38 @@ using Tobii.Research;
namespace TrackerBridge
{
public class TobiiEyeTracker
public class TobiiEyeTracker : IBridgeTracker
{
public delegate void ConnectionEventHandler(TobiiEyeTracker sender);
public delegate void GazeDataHandler(TobiiEyeTracker sender, GazeData data);
public event ConnectionEventHandler ConnectionEvent;
public event ConnectionEventHandler ConnectionTimeout;
public event GazeDataHandler GazeDataAvailable;
public Boolean IsConnected { get; private set; }
public String SerialNumber { get => serialNumber; }
public event EventHandler<Single> TrackingFrequencyChanged;
private IEyeTracker eyeTracker = null;
private String serialNumber = null;
private GazeDataProcessor gazeDataProcessor;
public GazeDataProcessor GazeDataProcessor { get; set; }
public Boolean IsConnected { get; private set; } = false;
public String SerialNumber { get; private set; } = null;
public TobiiEyeTracker()
{
gazeDataProcessor = new GazeDataProcessor();
GazeDataProcessor = new GazeDataProcessor(this);
}
public TobiiEyeTracker(String serialNumber) : this()
{
this.serialNumber = serialNumber;
SerialNumber = serialNumber;
}
public void Connect()
{
Task task = new Task(() => InitiateConnection());
task.Start();
}
public float GetTrackingFrequency()
public Single GetTrackingFrequency()
{
if (eyeTracker != null)
{
......@@ -43,18 +49,22 @@ namespace TrackerBridge
throw new NullReferenceException();
}
}
Single IBridgeTracker.GetTrackingFrequency()
{
throw new NotImplementedException();
}
public void SetTrackingFrequency(float value)
{
if (eyeTracker != null)
if (eyeTracker != null && eyeTracker.GetAllGazeOutputFrequencies().Contains(value))
{
eyeTracker.SetGazeOutputFrequency(value);
}
}
public void Connect()
void IBridgeTracker.SetTrackingFrequency(Single value)
{
Task task = new Task(() => InitiateConnection());
task.Start();
throw new NotImplementedException();
}
private void InitiateConnection()
......@@ -65,7 +75,7 @@ namespace TrackerBridge
EyeTrackerCollection eyeTrackers = EyeTrackingOperations.FindAllEyeTrackers();
if (eyeTrackers.Count > 0)
{
if (serialNumber == null)
if (SerialNumber == null)
{
eyeTracker = eyeTrackers[0];
break;
......@@ -74,7 +84,7 @@ namespace TrackerBridge
{
foreach (IEyeTracker t in eyeTrackers)
{
if (serialNumber == t.SerialNumber)
if (SerialNumber == t.SerialNumber)
{
eyeTracker = t;
break;
......@@ -94,15 +104,24 @@ namespace TrackerBridge
}
}
eyeTracker.GazeDataReceived += OnGazeDataReceived;
serialNumber = eyeTracker.SerialNumber;
eyeTracker.GazeOutputFrequencyChanged += OnGazeOutputFrequencyChanged;
SerialNumber = eyeTracker.SerialNumber;
SetTrackingFrequency(600f);
ConnectionEvent?.Invoke(this);
}
private void OnGazeDataReceived(object sender, GazeDataEventArgs e)
{
GazeData gazeData = gazeDataProcessor.Extract(e);
GazeDataAvailable?.Invoke(this, gazeData);
GazeData? gazeData = GazeDataProcessor.Process(e);
if (gazeData.HasValue)
{
GazeDataAvailable?.Invoke(this, gazeData.Value);
}
}
private void OnGazeOutputFrequencyChanged(Object sender, GazeOutputFrequencyEventArgs e)
{
TrackingFrequencyChanged?.Invoke(this, e.GazeOutputFrequency);
}
}
}
\ No newline at end of file
......@@ -48,8 +48,11 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="DSP\GazeDecimator.cs" />
<Compile Include="GazeData.cs" />
<Compile Include="GazeDataProcessor.cs" />
<Compile Include="DSP\IGazeFilter.cs" />
<Compile Include="IBridgeTracker.cs" />
<Compile Include="TobiiEyeTracker.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
......
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