Skip to content

Commit

Permalink
#91 added midpoint option
Browse files Browse the repository at this point in the history
  • Loading branch information
drmason789 committed May 1, 2022
1 parent 24ece72 commit c469e2b
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 23 deletions.
62 changes: 46 additions & 16 deletions Trixter.XDream.API.Testing/Filters/MeanValueFilterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,19 @@ public class MeanValueFilterTests
{
const double tolerance = 1.0e-10;

public enum ValueType
{
Value,
MidPoint
}


[Test]
public void TestMeanValueFilter_SplitMidSample()
[TestCase(ValueType.MidPoint)]
[TestCase(ValueType.Value)]
public void TestMeanValueFilter_SplitMidSample(ValueType valueType)
{
MeanValueFilter mvf = new MeanValueFilter(500);
MeanValueFilter mvf = new MeanValueFilter(500, valueType == ValueType.MidPoint);

DateTimeOffset t0 = DateTimeOffset.Now;

Expand All @@ -27,14 +36,19 @@ public void TestMeanValueFilter_SplitMidSample()

// Not looking for 500 here because this filter doesn't split samples at the cutoff point.
Assert.AreEqual(300, mvf.Period);
Assert.AreEqual((2 + 3) / 2d, mvf.Value, tolerance);
if(valueType == ValueType.Value)
Assert.AreEqual((2 + 3) / 2d, mvf.Value, tolerance);
else
Assert.AreEqual((1.5 + 2.5) / 2d, mvf.Value, tolerance);
}


[Test]
public void TestMeanValueFilter_SplitOnSampleBoundary()
[TestCase(ValueType.MidPoint)]
[TestCase(ValueType.Value)]
public void TestMeanValueFilter_SplitOnSampleBoundary(ValueType valueType)
{
MeanValueFilter mvf = new MeanValueFilter(300);
MeanValueFilter mvf = new MeanValueFilter(300, valueType == ValueType.MidPoint);

DateTimeOffset t0 = DateTimeOffset.Now;

Expand All @@ -43,13 +57,18 @@ public void TestMeanValueFilter_SplitOnSampleBoundary()
mvf.Add(3, t0.AddMilliseconds(600));

Assert.AreEqual(300, mvf.Period);
Assert.AreEqual(3, mvf.Value, tolerance);
if (valueType == ValueType.Value)
Assert.AreEqual(3, mvf.Value, tolerance);
else
Assert.AreEqual(2.5, mvf.Value, tolerance);
}

[Test]
public void TestWeightedMeanValueFilter_SplitMidSample()
[TestCase(ValueType.MidPoint)]
[TestCase(ValueType.Value)]
public void TestWeightedMeanValueFilter_SplitMidSample(ValueType valueType)
{
WeightedMeanValueFilter mvf = new WeightedMeanValueFilter(500);
WeightedMeanValueFilter mvf = new WeightedMeanValueFilter(500, valueType == ValueType.MidPoint);

DateTimeOffset t0 = DateTimeOffset.Now;

Expand All @@ -58,13 +77,19 @@ public void TestWeightedMeanValueFilter_SplitMidSample()
mvf.Add(3, t0.AddMilliseconds(600));

Assert.AreEqual(500, mvf.Period);
Assert.AreEqual((3d * 300d + 2d * 200d) / 500d, mvf.Value, tolerance);
if(valueType==ValueType.Value)
Assert.AreEqual((3d * 300d + 2d * 200d) / 500d, mvf.Value, tolerance);
else
Assert.AreEqual((2.5d * 300d + 1.5d * 200d) / 500d, mvf.Value, tolerance);

}

[Test]
public void TestWeightedMeanValueFilter_SplitOnSampleBoundary()
[TestCase(ValueType.MidPoint)]
[TestCase(ValueType.Value)]
public void TestWeightedMeanValueFilter_SplitOnSampleBoundary(ValueType valueType)
{
WeightedMeanValueFilter mvf = new WeightedMeanValueFilter(300);
WeightedMeanValueFilter mvf = new WeightedMeanValueFilter(300, valueType==ValueType.MidPoint);

DateTimeOffset t0 = DateTimeOffset.Now;

Expand All @@ -73,20 +98,25 @@ public void TestWeightedMeanValueFilter_SplitOnSampleBoundary()
mvf.Add(3, t0.AddMilliseconds(600));

Assert.AreEqual(300, mvf.Period);
Assert.AreEqual(3, mvf.Value, tolerance);
if(valueType==ValueType.Value)
Assert.AreEqual(3, mvf.Value, tolerance);
else
Assert.AreEqual(2.5, mvf.Value, tolerance);
}


[Test]
[TestCase(typeof(MeanValueFilter), 1000, 0.48544358988983127d, -0.0015909654334074566d)]
[TestCase(typeof(WeightedMeanValueFilter), 1000, 0.48562740929878312d, 0.0078560187173342937d)]
public void SpeedTest(Type type, int period, double expected, double expectedDelta)
[TestCase(typeof(MeanValueFilter), 1000, ValueType.Value, 0.48544358988983127d, -0.0015909654334074566d)]
[TestCase(typeof(WeightedMeanValueFilter), 1000, ValueType.Value, 0.48562740929878312d, 0.0078560187173342937d)]
[TestCase(typeof(MeanValueFilter), 1000, ValueType.MidPoint,0.48544358988983127d, -0.0015909654334074566d)]
[TestCase(typeof(WeightedMeanValueFilter), 1000, ValueType.MidPoint, 0.48169939994004934d, 0.0078560187173342937d)]
public void SpeedTest(Type type, int period, ValueType valueType, double expected, double expectedDelta)
{
const int iterations = 1000000;

Random random = new Random(10);
DateTimeOffset t = DateTimeOffset.Now;
MeanValueFilterBase mvf = (MeanValueFilterBase)Activator.CreateInstance(type, new object[] { period });
MeanValueFilterBase mvf = (MeanValueFilterBase)Activator.CreateInstance(type, new object[] { period, valueType==ValueType.MidPoint });
double[] dT = Enumerable.Range(0, iterations).Select(x => random.NextDouble() * 10 + 0.0001).ToArray();
double[] v = Enumerable.Range(0, iterations).Select(x => random.NextDouble()).ToArray();
Stopwatch stopwatch = new Stopwatch();
Expand Down
2 changes: 1 addition & 1 deletion Trixter.XDream.API/Filters/MeanValueFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace Trixter.XDream.API.Filters
[DebuggerDisplay("Count={Count} period={Period} v={Value} dv/ms={DeltaPerMillisecond}")]
internal class MeanValueFilter : MeanValueFilterBase
{
public MeanValueFilter(int periodMilliseconds):base(periodMilliseconds, new MeanCalculator(), new MeanCalculator())
public MeanValueFilter(int periodMilliseconds, bool useMidPoint=false):base(periodMilliseconds, new MeanCalculator(), new MeanCalculator(), useMidPoint)
{
}

Expand Down
12 changes: 9 additions & 3 deletions Trixter.XDream.API/Filters/MeanValueFilterBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ internal abstract class MeanValueFilterBase
protected IMeanCalculator MeanValue { get; private set; }
protected IMeanCalculator MeanDelta { get; private set; }

/// <summary>
/// Indicates where a sample's <see cref="Sample.MidPoint"/> should be used instead of its <see cref="Sample.Value"/>.
/// </summary>
public bool UseMidPoint { get; }

/// <summary>
/// The period over which the mean value is calculated, over which samples are kept.
/// </summary>
Expand Down Expand Up @@ -65,13 +70,14 @@ internal abstract class MeanValueFilterBase
/// </summary>
public int Count => this.samples.Count;

protected MeanValueFilterBase(int periodMilliseconds, IMeanCalculator meanValue, IMeanCalculator meanDelta)
protected MeanValueFilterBase(int periodMilliseconds, IMeanCalculator meanValue, IMeanCalculator meanDelta, bool useMidPoint)
{
this.periodMilliseconds = periodMilliseconds;
this.samples = new SampleList();
this.startTime = DateTimeOffset.MinValue;
this.MeanValue = meanValue;
this.MeanDelta = meanDelta;
this.UseMidPoint = useMidPoint;
}


Expand Down Expand Up @@ -128,7 +134,7 @@ protected void Add(Sample sample)
return;

double dT = sample.dT;
this.MeanValue.Add(sample.Value, dT);
this.MeanValue.Add(this.UseMidPoint? sample.MidPoint:sample.Value, dT);
if (sample.Delta != null)
this.MeanDelta.Add(sample.Delta.Value, dT);

Expand All @@ -146,7 +152,7 @@ protected void Remove(Sample sample)
return;

double dT = sample.dT;
this.MeanValue.Remove(sample.Value, dT);
this.MeanValue.Remove(this.UseMidPoint ? sample.MidPoint : sample.Value, dT);
if (sample.Delta != null)
this.MeanDelta.Remove(sample.Delta.Value, dT);

Expand Down
2 changes: 1 addition & 1 deletion Trixter.XDream.API/Filters/Sample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ internal class Sample
private string DebuggerDisplay => $"{T:0.0000}: v={Value} d={Delta}";

protected Sample previous, next;


public double MidPoint => this.Previous == null ? this.Value : (this.Value + this.Previous.Value) * 0.5;
public double Value;
public double? Delta;

Expand Down
4 changes: 2 additions & 2 deletions Trixter.XDream.API/Filters/WeightedMeanValueFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Trixter.XDream.API.Filters
[DebuggerDisplay("Count={Count} period={Period} v={Value} dv/ms={DeltaPerMillisecond}")]
internal class WeightedMeanValueFilter : MeanValueFilterBase
{
public WeightedMeanValueFilter(int periodMilliseconds) : base(periodMilliseconds, new WeightedMeanCalculator(), new WeightedMeanCalculator())
public WeightedMeanValueFilter(int periodMilliseconds, bool useMidPoint=false) : base(periodMilliseconds, new WeightedMeanCalculator(), new WeightedMeanCalculator(), useMidPoint)
{

}
Expand All @@ -29,7 +29,7 @@ protected override void Trim(Sample current, double limit, out bool remove)
current.T = limit;
double newdT = next.dT;

this.MeanValue.Update(next.Value, olddT, newdT);
this.MeanValue.Update(this.UseMidPoint ? next.MidPoint:next.Value, olddT, newdT);
if (next.Delta != null)
this.MeanDelta.Update(next.Delta.Value, olddT, newdT);
}
Expand Down

0 comments on commit c469e2b

Please sign in to comment.