Improved WindSpeedAndAngleEstimator's distance based validation

also tested it.

If the requested boat speed is not inbetween the values of the sampling
points the distance between the input speed and each sampling point is
compared and, if small enough, added to the resultset with halved
confidence
This commit is contained in:
Frederik Petersen committed 2015-01-05 14:26:25 +01:00
1 parent 5103029ea7
commit 792dcd098e
2 files changed
+92 -17

No files matched your search

@@ -1,11 +1,14 @@
package com.sap.sailing.polars.test;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.closeTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.junit.Test;
@@ -172,9 +175,70 @@ public class WindSpeedAndAngleEstimatorTest {
assertThat(result.getObject().getKnots(), closeTo(6.5, ERROR));
assertThat(result.getObject().getBearing().getDegrees(), closeTo(47.25, ERROR));
assertThat(result.getConfidence(), closeTo(0.55, ERROR));
}
@SuppressWarnings("unchecked")
@Test
public void testAverageTrueWindSpeedAndAngleEstimationWithAllCandidatesReturned() {
List<Pair<Speed, SpeedWithBearingWithConfidence<Void>>> averageBoatSpeedAndCourseForWindSpeed = new ArrayList<>();
averageBoatSpeedAndCourseForWindSpeed.add(new Pair<Speed, SpeedWithBearingWithConfidence<Void>>(
new KnotSpeedImpl(5), new SpeedWithBearingWithConfidenceImpl<Void>(new KnotSpeedWithBearingImpl(3,
new DegreeBearingImpl(46)), 0.4, null)));
averageBoatSpeedAndCourseForWindSpeed.add(new Pair<Speed, SpeedWithBearingWithConfidence<Void>>(
new KnotSpeedImpl(6), new SpeedWithBearingWithConfidenceImpl<Void>(new KnotSpeedWithBearingImpl(4,
new DegreeBearingImpl(47)), 0.6, null)));
averageBoatSpeedAndCourseForWindSpeed.add(new Pair<Speed, SpeedWithBearingWithConfidence<Void>>(
new KnotSpeedImpl(7), new SpeedWithBearingWithConfidenceImpl<Void>(new KnotSpeedWithBearingImpl(4.5,
new DegreeBearingImpl(47.5)), 0.5, null)));
averageBoatSpeedAndCourseForWindSpeed.add(new Pair<Speed, SpeedWithBearingWithConfidence<Void>>(
new KnotSpeedImpl(8), new SpeedWithBearingWithConfidenceImpl<Void>(new KnotSpeedWithBearingImpl(4.25,
new DegreeBearingImpl(47.5)), 0.25, null)));
averageBoatSpeedAndCourseForWindSpeed.add(new Pair<Speed, SpeedWithBearingWithConfidence<Void>>(
new KnotSpeedImpl(9), new SpeedWithBearingWithConfidenceImpl<Void>(new KnotSpeedWithBearingImpl(3,
new DegreeBearingImpl(47.5)), 0.25, null)));
WindSpeedAndAngleEstimator estimator = new WindSpeedAndAngleEstimator(averageBoatSpeedAndCourseForWindSpeed);
Set<SpeedWithBearingWithConfidence<Void>> result = estimator
.getAverageTrueWindSpeedAndAngleCandidates(new KnotSpeedImpl(4.3));
assertThat(result.size(), is(2));
for (SpeedWithBearingWithConfidence<Void> candidate : result) {
assertThat(candidate.getConfidence(), anyOf(closeTo(0.2999999, ERROR), closeTo(0.54, ERROR)));
if (Math.abs(candidate.getConfidence() - 0.2999999) < ERROR) {
assertThat(candidate.getObject().getKnots(), closeTo(7.8, ERROR));
assertThat(candidate.getObject().getBearing().getDegrees(), closeTo(47.5, ERROR));
} else {
assertThat(candidate.getObject().getKnots(), closeTo(6.6, ERROR));
assertThat(candidate.getObject().getBearing().getDegrees(), closeTo(47.3, ERROR));
}
}
result = estimator
.getAverageTrueWindSpeedAndAngleCandidates(new KnotSpeedImpl(2.9));
assertThat(result.size(), is(2));
for (SpeedWithBearingWithConfidence<Void> candidate : result) {
assertThat(candidate.getConfidence(), anyOf(closeTo(0.2, ERROR), closeTo(0.125, ERROR)));
if (Math.abs(candidate.getConfidence() - 0.2) < ERROR) {
assertThat(candidate.getObject().getKnots(), closeTo(5, ERROR));
assertThat(candidate.getObject().getBearing().getDegrees(), closeTo(46, ERROR));
} else {
assertThat(candidate.getObject().getKnots(), closeTo(9, ERROR));
assertThat(candidate.getObject().getBearing().getDegrees(), closeTo(47.5, ERROR));
}
}
result = estimator
.getAverageTrueWindSpeedAndAngleCandidates(new KnotSpeedImpl(4.9));
assertThat(result.size(), is(1));
SpeedWithBearingWithConfidence<Void> candidate = result.iterator().next();
assertThat(candidate.getObject().getKnots(), closeTo(7, ERROR));
assertThat(candidate.getObject().getBearing().getDegrees(), closeTo(47.5, ERROR));
assertThat(candidate.getConfidence(), closeTo(0.25, ERROR));
}
@Test
@@ -15,7 +15,7 @@ public class WindSpeedAndAngleEstimator {
private static final double DEFAULT_MAX_DISTANCE_TO_SAMPLING_POINT = 0.5;
private final List<Pair<Speed, SpeedWithBearingWithConfidence<Void>>> averageBoatSpeedAndCourseForWindSpeed;
private final double maxDistanceToSamplingPointIfOnlyOnOneSideInKnots;
private final double maxDistanceToSamplingPointFunctionInKnots;
/**
*
@@ -32,15 +32,15 @@ public class WindSpeedAndAngleEstimator {
*
* @param averageBoatSpeedAndCourseForWindSpeed
* List of sampling points. Needs to be sorted by windspeed (A of the Pair object) low to high
* @param maxDistanceToSamplingPointIfOnlyOnOneSideInKnots
* @param maxDistanceToSamplingPointFunctionInKnots
* If there is only a sampling point on one side, what's the maximal distance to consider valid when
* comparing?
*/
public WindSpeedAndAngleEstimator(
List<Pair<Speed, SpeedWithBearingWithConfidence<Void>>> averageBoatSpeedAndCourseForWindSpeed,
double maxDistanceToSamplingPointIfOnlyOnOneSideInKnots) {
double maxDistanceToSamplingPointFunctionInKnots) {
this.averageBoatSpeedAndCourseForWindSpeed = averageBoatSpeedAndCourseForWindSpeed;
this.maxDistanceToSamplingPointIfOnlyOnOneSideInKnots = maxDistanceToSamplingPointIfOnlyOnOneSideInKnots;
this.maxDistanceToSamplingPointFunctionInKnots = maxDistanceToSamplingPointFunctionInKnots;
}
/**
@@ -76,6 +76,7 @@ public class WindSpeedAndAngleEstimator {
double requestedBoatSpeedInKnots = boatSpeed.getKnots();
Pair<Speed, SpeedWithBearingWithConfidence<Void>> last = null;
Set<SpeedWithBearingWithConfidence<Void>> resultCandidates = new HashSet<>();
Set<SpeedWithBearingWithConfidence<Void>> resultCandidatesWithDistance = new HashSet<>();
for (Pair<Speed, SpeedWithBearingWithConfidence<Void>> averageSpeedForWindSpeed : averageBoatSpeedAndCourseForWindSpeed) {
Speed windSpeed = averageSpeedForWindSpeed.getA();
SpeedWithBearingWithConfidence<Void> averageBoatSpeedWithCourse = averageSpeedForWindSpeed.getB();
@@ -96,28 +97,38 @@ public class WindSpeedAndAngleEstimator {
lastBoatSpeedInKnots, currentBoatSpeedInKnots);
resultCandidates.add(resultCandidate);
}
} else {
//This will be run when there is no lower sampling point windspeed wise
addResultCandidateForOneSideSamplingPointSituationIfDistanceValid(requestedBoatSpeedInKnots,
resultCandidates, windSpeed, averageBoatSpeedWithCourse, currentBoatSpeedInKnots);
}
/*
* This will be run when there was no candidate found in between the two sampling points (or we are at the
* beginning) and add a Candidate if the requested point was maxDistanceToSamplingPointFunctionInKnots or
* less away from the last regarded sampling point. The candidate (if valid) is then added to the
* resultCandidatesWithDistance set. Confidences are halved and the set is only used if the standard result
* set is empty in the end
*/
if (resultCandidates.size() == 0 && last != null) {
addResultCandidateIfDistanceValid(requestedBoatSpeedInKnots, resultCandidatesWithDistance, last.getA(),
last.getB());
}
last = averageSpeedForWindSpeed;
}
// This will be run when there is no higher sampling point windspeed wise
addResultCandidateForOneSideSamplingPointSituationIfDistanceValid(requestedBoatSpeedInKnots, resultCandidates,
last.getA(), last.getB(), last.getB().getObject().getKnots());
// check last sampling point for distance match and return withDistance set if standard set is empty
if (resultCandidates.size() == 0) {
addResultCandidateIfDistanceValid(requestedBoatSpeedInKnots, resultCandidatesWithDistance, last.getA(),
last.getB());
resultCandidates = resultCandidatesWithDistance;
}
return resultCandidates;
}
/**
* Confidence will be halved in situations where there is only one sampling point either to the left or the right of
* the regarded windspeed
* Confidence will be halved in situations where there the requested speed does not lie between any sampling points.
* The speed and angle values are equal to the sampling point values.
*/
private void addResultCandidateForOneSideSamplingPointSituationIfDistanceValid(double requestedBoatSpeedInKnots,
private void addResultCandidateIfDistanceValid(double requestedBoatSpeedInKnots,
Set<SpeedWithBearingWithConfidence<Void>> resultCandidates, Speed windSpeed,
SpeedWithBearingWithConfidence<Void> averageBoatSpeedWithCourse, double currentBoatSpeedInKnots) {
if (Math.abs(currentBoatSpeedInKnots - requestedBoatSpeedInKnots) <= maxDistanceToSamplingPointIfOnlyOnOneSideInKnots) {
SpeedWithBearingWithConfidence<Void> averageBoatSpeedWithCourse) {
if (Math.abs(averageBoatSpeedWithCourse.getObject().getKnots() - requestedBoatSpeedInKnots) <= maxDistanceToSamplingPointFunctionInKnots) {
SpeedWithBearingWithConfidence<Void> resultCandidate = new SpeedWithBearingWithConfidenceImpl<Void>(
new KnotSpeedWithBearingImpl(windSpeed.getKnots(), new DegreeBearingImpl(averageBoatSpeedWithCourse
.getObject().getBearing().getDegrees())), averageBoatSpeedWithCourse.getConfidence() * 0.5,