From 705326bdee3cd1285e9d30d795dc0691ca957d2a Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Thu, 24 Sep 2015 10:24:41 +0200 Subject: [PATCH] Clean up and documentation removed unused classes in com.sap.sailing.polars[.test] and added documentation to used classes. --- .../test/WindSpeedAndAngleEstimatorTest.java | 260 ------------------ .../sailing/polars/PolarDataOperation.java | 8 + .../sap/sailing/polars/impl/Activator.java | 6 + .../sailing/polars/impl/CubicEquation.java | 11 + .../polars/impl/PolarDataServiceImpl.java | 11 +- .../mining/AbstractEnrichingProcessor.java | 8 + .../mining/AngleAndSpeedRegression.java | 18 ++ .../mining/AngleClusterPolarClusterKey.java | 6 + .../polars/mining/AverageAngleContainer.java | 21 -- .../polars/mining/BasePolarClusterKey.java | 7 + .../polars/mining/BearingClusterGroup.java | 19 ++ .../CubicRegressionPerCourseProcessor.java | 29 ++ .../mining/GPSFixMovingWithOriginInfo.java | 5 + .../mining/GPSFixMovingWithPolarContext.java | 7 + .../polars/mining/LegTypePolarClusterKey.java | 5 + .../PolarDataDimensionCollectionFactory.java | 7 + .../sailing/polars/mining/PolarDataMiner.java | 11 + .../polars/mining/PolarFixFilterCriteria.java | 8 + .../polars/mining/RoundedTrueWindAngle.java | 8 + ...eedRegressionPerAngleClusterProcessor.java | 17 ++ .../sailing/polars/mining/WindSpeedLevel.java | 54 ---- .../IncrementalAnyOrderLeastSquaresImpl.java | 4 + .../impl/WindSpeedAndAngleEstimator.java | 197 ------------- 23 files changed, 191 insertions(+), 536 deletions(-) delete mode 100755 java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/WindSpeedAndAngleEstimatorTest.java delete mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AverageAngleContainer.java delete mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/WindSpeedLevel.java delete mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/WindSpeedAndAngleEstimator.java diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/WindSpeedAndAngleEstimatorTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/WindSpeedAndAngleEstimatorTest.java deleted file mode 100755 index 42f5f6d66eb..00000000000 --- a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/WindSpeedAndAngleEstimatorTest.java +++ /dev/null @@ -1,260 +0,0 @@ -package com.sap.sailing.polars.test; - -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 static org.junit.Assert.assertTrue; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -import org.junit.Test; - -import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence; -import com.sap.sailing.domain.base.impl.SpeedWithBearingWithConfidenceImpl; -import com.sap.sailing.domain.common.Speed; -import com.sap.sailing.domain.common.impl.DegreeBearingImpl; -import com.sap.sailing.domain.common.impl.KnotSpeedImpl; -import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; -import com.sap.sailing.polars.regression.impl.WindSpeedAndAngleEstimator; -import com.sap.sse.common.Util.Pair; - -public class WindSpeedAndAngleEstimatorTest { - - private static final double ERROR = 0.0001; - - @Test - public void testAverageTrueWindSpeedAndAngleEstimationForSteadyGrowth() { - List>> averageBoatSpeedAndCourseForWindSpeed = new ArrayList<>(); - averageBoatSpeedAndCourseForWindSpeed.add(new Pair>( - new KnotSpeedImpl(5), new SpeedWithBearingWithConfidenceImpl(new KnotSpeedWithBearingImpl(3, - new DegreeBearingImpl(46)), 0.4, null))); - averageBoatSpeedAndCourseForWindSpeed.add(new Pair>( - new KnotSpeedImpl(6), new SpeedWithBearingWithConfidenceImpl(new KnotSpeedWithBearingImpl(4, - new DegreeBearingImpl(47)), 0.6, null))); - averageBoatSpeedAndCourseForWindSpeed.add(new Pair>( - new KnotSpeedImpl(7), new SpeedWithBearingWithConfidenceImpl(new KnotSpeedWithBearingImpl(4.5, - new DegreeBearingImpl(47.5)), 0.5, null))); - WindSpeedAndAngleEstimator estimator = new WindSpeedAndAngleEstimator(averageBoatSpeedAndCourseForWindSpeed); - - SpeedWithBearingWithConfidence result = estimator.getAverageTrueWindSpeedAndAngle(new KnotSpeedImpl(3)); - assertThat(result.getObject().getKnots(), closeTo(5, ERROR)); - assertThat(result.getObject().getBearing().getDegrees(), closeTo(46, ERROR)); - assertThat(result.getConfidence(), closeTo(0.4, ERROR)); - - result = estimator.getAverageTrueWindSpeedAndAngle(new KnotSpeedImpl(4)); - assertThat(result.getObject().getKnots(), closeTo(6, ERROR)); - assertThat(result.getObject().getBearing().getDegrees(), closeTo(47, ERROR)); - assertThat(result.getConfidence(), closeTo(0.6, ERROR)); - - result = estimator.getAverageTrueWindSpeedAndAngle(new KnotSpeedImpl(4.5)); - assertThat(result.getObject().getKnots(), closeTo(7, ERROR)); - assertThat(result.getObject().getBearing().getDegrees(), closeTo(47.5, ERROR)); - assertThat(result.getConfidence(), closeTo(0.5, ERROR)); - - result = estimator.getAverageTrueWindSpeedAndAngle(new KnotSpeedImpl(4.25)); - assertThat(result.getObject().getKnots(), closeTo(6.5, ERROR)); - assertThat(result.getObject().getBearing().getDegrees(), closeTo(47.25, ERROR)); - assertThat(result.getConfidence(), closeTo(0.55, ERROR)); - - result = estimator.getAverageTrueWindSpeedAndAngle(new KnotSpeedImpl(3.25)); - assertThat(result.getObject().getKnots(), closeTo(5.25, ERROR)); - assertThat(result.getObject().getBearing().getDegrees(), closeTo(46.25, ERROR)); - assertThat(result.getConfidence(), closeTo(0.45, ERROR)); - - result = estimator.getAverageTrueWindSpeedAndAngle(new KnotSpeedImpl(2.75)); - assertThat(result.getObject().getKnots(), closeTo(5, ERROR)); - assertThat(result.getObject().getBearing().getDegrees(), closeTo(46, ERROR)); - assertThat(result.getConfidence(), closeTo(0.2, ERROR)); - - result = estimator.getAverageTrueWindSpeedAndAngle(new KnotSpeedImpl(4.75)); - assertThat(result.getObject().getKnots(), closeTo(7, ERROR)); - assertThat(result.getObject().getBearing().getDegrees(), closeTo(47.5, ERROR)); - assertThat(result.getConfidence(), closeTo(0.25, ERROR)); - } - - /** - * Not a realistic scenario in the sailing world, but testing for the sake of correctness of the algorithm - */ - @Test - public void testAverageTrueWindSpeedAndAngleEstimationForSteadyDeclination() { - List>> averageBoatSpeedAndCourseForWindSpeed = new ArrayList<>(); - averageBoatSpeedAndCourseForWindSpeed.add(new Pair>( - new KnotSpeedImpl(7), new SpeedWithBearingWithConfidenceImpl(new KnotSpeedWithBearingImpl(3, - new DegreeBearingImpl(46)), 0.4, null))); - averageBoatSpeedAndCourseForWindSpeed.add(new Pair>( - new KnotSpeedImpl(6), new SpeedWithBearingWithConfidenceImpl(new KnotSpeedWithBearingImpl(4, - new DegreeBearingImpl(47)), 0.6, null))); - averageBoatSpeedAndCourseForWindSpeed.add(new Pair>( - new KnotSpeedImpl(5), new SpeedWithBearingWithConfidenceImpl(new KnotSpeedWithBearingImpl(4.5, - new DegreeBearingImpl(47.5)), 0.5, null))); - WindSpeedAndAngleEstimator estimator = new WindSpeedAndAngleEstimator(averageBoatSpeedAndCourseForWindSpeed); - - SpeedWithBearingWithConfidence result = estimator.getAverageTrueWindSpeedAndAngle(new KnotSpeedImpl(3)); - assertThat(result.getObject().getKnots(), closeTo(7, ERROR)); - assertThat(result.getObject().getBearing().getDegrees(), closeTo(46, ERROR)); - assertThat(result.getConfidence(), closeTo(0.4, ERROR)); - - result = estimator.getAverageTrueWindSpeedAndAngle(new KnotSpeedImpl(4)); - assertThat(result.getObject().getKnots(), closeTo(6, ERROR)); - assertThat(result.getObject().getBearing().getDegrees(), closeTo(47, ERROR)); - assertThat(result.getConfidence(), closeTo(0.6, ERROR)); - - result = estimator.getAverageTrueWindSpeedAndAngle(new KnotSpeedImpl(4.5)); - assertThat(result.getObject().getKnots(), closeTo(5, ERROR)); - assertThat(result.getObject().getBearing().getDegrees(), closeTo(47.5, ERROR)); - assertThat(result.getConfidence(), closeTo(0.5, ERROR)); - - result = estimator.getAverageTrueWindSpeedAndAngle(new KnotSpeedImpl(4.25)); - assertThat(result.getObject().getKnots(), closeTo(5.5, ERROR)); - assertThat(result.getObject().getBearing().getDegrees(), closeTo(47.25, ERROR)); - assertThat(result.getConfidence(), closeTo(0.55, ERROR)); - - result = estimator.getAverageTrueWindSpeedAndAngle(new KnotSpeedImpl(3.25)); - assertThat(result.getObject().getKnots(), closeTo(6.75, ERROR)); - assertThat(result.getObject().getBearing().getDegrees(), closeTo(46.25, ERROR)); - assertThat(result.getConfidence(), closeTo(0.45, ERROR)); - - result = estimator.getAverageTrueWindSpeedAndAngle(new KnotSpeedImpl(2.75)); - assertThat(result.getObject().getKnots(), closeTo(7, ERROR)); - assertThat(result.getObject().getBearing().getDegrees(), closeTo(46, ERROR)); - assertThat(result.getConfidence(), closeTo(0.2, ERROR)); - - result = estimator.getAverageTrueWindSpeedAndAngle(new KnotSpeedImpl(4.75)); - assertThat(result.getObject().getKnots(), closeTo(5, ERROR)); - assertThat(result.getObject().getBearing().getDegrees(), closeTo(47.5, ERROR)); - assertThat(result.getConfidence(), closeTo(0.25, ERROR)); - } - - @Test - public void testAverageTrueWindSpeedAndAngleEstimationNegative() { - List>> averageBoatSpeedAndCourseForWindSpeed = new ArrayList<>(); - averageBoatSpeedAndCourseForWindSpeed.add(new Pair>( - new KnotSpeedImpl(5), new SpeedWithBearingWithConfidenceImpl(new KnotSpeedWithBearingImpl(3, - new DegreeBearingImpl(46)), 0.4, null))); - averageBoatSpeedAndCourseForWindSpeed.add(new Pair>( - new KnotSpeedImpl(6), new SpeedWithBearingWithConfidenceImpl(new KnotSpeedWithBearingImpl(4, - new DegreeBearingImpl(47)), 0.6, null))); - averageBoatSpeedAndCourseForWindSpeed.add(new Pair>( - new KnotSpeedImpl(7), new SpeedWithBearingWithConfidenceImpl(new KnotSpeedWithBearingImpl(4.5, - new DegreeBearingImpl(47.5)), 0.5, null))); - WindSpeedAndAngleEstimator estimator = new WindSpeedAndAngleEstimator(averageBoatSpeedAndCourseForWindSpeed); - - SpeedWithBearingWithConfidence result = estimator.getAverageTrueWindSpeedAndAngle(new KnotSpeedImpl(2)); - assertThat(result, nullValue()); - - result = estimator.getAverageTrueWindSpeedAndAngle(new KnotSpeedImpl(5.5)); - assertThat(result, nullValue()); - } - - @Test - public void testAverageTrueWindSpeedAndAngleEstimationForNonReversibleFunction() { - List>> averageBoatSpeedAndCourseForWindSpeed = new ArrayList<>(); - averageBoatSpeedAndCourseForWindSpeed.add(new Pair>( - new KnotSpeedImpl(5), new SpeedWithBearingWithConfidenceImpl(new KnotSpeedWithBearingImpl(3, - new DegreeBearingImpl(46)), 0.4, null))); - averageBoatSpeedAndCourseForWindSpeed.add(new Pair>( - new KnotSpeedImpl(6), new SpeedWithBearingWithConfidenceImpl(new KnotSpeedWithBearingImpl(4, - new DegreeBearingImpl(47)), 0.6, null))); - averageBoatSpeedAndCourseForWindSpeed.add(new Pair>( - new KnotSpeedImpl(7), new SpeedWithBearingWithConfidenceImpl(new KnotSpeedWithBearingImpl(4.5, - new DegreeBearingImpl(47.5)), 0.5, null))); - averageBoatSpeedAndCourseForWindSpeed.add(new Pair>( - new KnotSpeedImpl(8), new SpeedWithBearingWithConfidenceImpl(new KnotSpeedWithBearingImpl(4.25, - new DegreeBearingImpl(47.5)), 0.25, null))); - WindSpeedAndAngleEstimator estimator = new WindSpeedAndAngleEstimator(averageBoatSpeedAndCourseForWindSpeed); - - SpeedWithBearingWithConfidence result = estimator.getAverageTrueWindSpeedAndAngle(new KnotSpeedImpl(4.25)); - assertThat(result.getObject().getKnots(), closeTo(6.5, ERROR)); - assertThat(result.getObject().getBearing().getDegrees(), closeTo(47.25, ERROR)); - assertThat(result.getConfidence(), closeTo(0.55, ERROR)); - } - - @Test - public void testAverageTrueWindSpeedAndAngleEstimationWithAllCandidatesReturned() { - List>> averageBoatSpeedAndCourseForWindSpeed = new ArrayList<>(); - averageBoatSpeedAndCourseForWindSpeed.add(new Pair>( - new KnotSpeedImpl(5), new SpeedWithBearingWithConfidenceImpl(new KnotSpeedWithBearingImpl(3, - new DegreeBearingImpl(46)), 0.4, null))); - averageBoatSpeedAndCourseForWindSpeed.add(new Pair>( - new KnotSpeedImpl(6), new SpeedWithBearingWithConfidenceImpl(new KnotSpeedWithBearingImpl(4, - new DegreeBearingImpl(47)), 0.6, null))); - averageBoatSpeedAndCourseForWindSpeed.add(new Pair>( - new KnotSpeedImpl(7), new SpeedWithBearingWithConfidenceImpl(new KnotSpeedWithBearingImpl(4.5, - new DegreeBearingImpl(47.5)), 0.5, null))); - averageBoatSpeedAndCourseForWindSpeed.add(new Pair>( - new KnotSpeedImpl(8), new SpeedWithBearingWithConfidenceImpl(new KnotSpeedWithBearingImpl(4.25, - new DegreeBearingImpl(47.5)), 0.25, null))); - averageBoatSpeedAndCourseForWindSpeed.add(new Pair>( - new KnotSpeedImpl(9), new SpeedWithBearingWithConfidenceImpl(new KnotSpeedWithBearingImpl(3, - new DegreeBearingImpl(47.5)), 0.25, null))); - WindSpeedAndAngleEstimator estimator = new WindSpeedAndAngleEstimator(averageBoatSpeedAndCourseForWindSpeed); - - Set> result = estimator - .getAverageTrueWindSpeedAndAngleCandidates(new KnotSpeedImpl(4.3)); - assertThat(result.size(), is(2)); - for (SpeedWithBearingWithConfidence candidate : result) { - assertTrue(Math.abs(candidate.getConfidence()-0.2999999)<=ERROR || - Math.abs(candidate.getConfidence()-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 candidate : result) { - assertTrue(Math.abs(candidate.getConfidence()-0.2) <= ERROR || - Math.abs(candidate.getConfidence()-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 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 - public void testAverageTrueWindSpeedAndAngleEstimationForTwoSamplingPointsWithSameBoatSpeed() { - List>> averageBoatSpeedAndCourseForWindSpeed = new ArrayList<>(); - averageBoatSpeedAndCourseForWindSpeed.add(new Pair>( - new KnotSpeedImpl(7), new SpeedWithBearingWithConfidenceImpl(new KnotSpeedWithBearingImpl(4.5, - new DegreeBearingImpl(47.5)), 0.5, null))); - averageBoatSpeedAndCourseForWindSpeed.add(new Pair>( - new KnotSpeedImpl(8), new SpeedWithBearingWithConfidenceImpl(new KnotSpeedWithBearingImpl(4.5, - new DegreeBearingImpl(47.5)), 0.4, null))); - averageBoatSpeedAndCourseForWindSpeed.add(new Pair>( - new KnotSpeedImpl(9), new SpeedWithBearingWithConfidenceImpl(new KnotSpeedWithBearingImpl(5.5, - new DegreeBearingImpl(47.5)), 0.4, null))); - averageBoatSpeedAndCourseForWindSpeed.add(new Pair>( - new KnotSpeedImpl(10), new SpeedWithBearingWithConfidenceImpl(new KnotSpeedWithBearingImpl(5.5, - new DegreeBearingImpl(47.5)), 0.5, null))); - WindSpeedAndAngleEstimator estimator = new WindSpeedAndAngleEstimator(averageBoatSpeedAndCourseForWindSpeed); - - SpeedWithBearingWithConfidence result = estimator.getAverageTrueWindSpeedAndAngle(new KnotSpeedImpl(4.5)); - assertThat(result.getObject().getKnots(), closeTo(7.44444, ERROR)); - assertThat(result.getObject().getBearing().getDegrees(), closeTo(47.5, ERROR)); - assertThat(result.getConfidence(), closeTo(0.455555, ERROR)); - - result = estimator.getAverageTrueWindSpeedAndAngle(new KnotSpeedImpl(5.5)); - assertThat(result.getObject().getKnots(), closeTo(9.555555, ERROR)); - assertThat(result.getObject().getBearing().getDegrees(), closeTo(47.5, ERROR)); - assertThat(result.getConfidence(), closeTo(0.455555, ERROR)); - } -} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataOperation.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataOperation.java index 0c73e8ca736..f2188a3fd01 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataOperation.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataOperation.java @@ -3,6 +3,14 @@ package com.sap.sailing.polars; import com.sap.sailing.polars.impl.PolarDataServiceImpl; import com.sap.sse.replication.OperationWithResult; +/** + * + * Needed in the context of the initial replication of the polar data service. + * + * @author D054528 Frederik Petersen + * + * @param + */ public interface PolarDataOperation extends OperationWithResult { } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/Activator.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/Activator.java index d7227786ca2..4e9e72b5ff3 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/Activator.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/Activator.java @@ -13,6 +13,12 @@ import org.osgi.framework.ServiceRegistration; import com.sap.sailing.domain.polars.PolarDataService; import com.sap.sse.replication.Replicable; +/** + * Handles OSGi (de-)registration of the polar data service. + * + * @author D054528 (Frederik Petersen) + * + */ public class Activator implements BundleActivator { private static final Logger logger = Logger.getLogger(Activator.class.getName()); diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/CubicEquation.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/CubicEquation.java index 0047627ba28..ebe195f994f 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/CubicEquation.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/CubicEquation.java @@ -1,5 +1,12 @@ package com.sap.sailing.polars.impl; +/** + * Represents cubic equations in the form of a*x^3 + b*x^2 + c*x + d = 0 and can solve roots for x with {@link #solve()}. + * !Note that complex roots are not returned.! + * + * @author D054528 (Frederik Petersen) + * + */ public class CubicEquation { private double a; @@ -7,6 +14,9 @@ public class CubicEquation { private double c; private double d; + /** + * Create a cubic equation in the form of a*x^3 + b*x^2 + c*x + d = 0. + */ public CubicEquation(double a, double b, double c, double d) { this.a = a; this.b = b; @@ -16,6 +26,7 @@ public class CubicEquation { /** * Following: http://www.1728.org/cubic2.htm !The complex roots are not returned! + * Returned array can be of length one or three. Depending on how many real roots there are. */ public double[] solve() { diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index 17c60cebd1b..3bb1b76d9d2 100755 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -47,14 +47,14 @@ import com.sap.sse.replication.OperationWithResult; import com.sap.sse.replication.ReplicationMasterDescriptor; import com.sap.sse.replication.impl.OperationWithResultWithIdWrapper; import com.sap.sse.replication.impl.ReplicableWithObjectInputStream; -import com.sap.sse.util.SmartFutureCache; /** - * Uses two chained {@link SmartFutureCache}s. One to store {@link PolarFix}es extracted from {@link TrackedRace}s and - * the other one for storing one polar sheet per boat class. This enables quick access to desired measures like optimal - * beat angles. + * Uses a custom datamining pipeline to aggregate incoming fixes in two regression based polar containers. + * + * For more information on polars in SAP Sailing Analytics, please see: http://wiki.sapsailing.com/wiki/Polars * * @author Frederik Petersen (D054528) + * @author Axel Uhl * */ public class PolarDataServiceImpl implements PolarDataService, @@ -79,6 +79,9 @@ public class PolarDataServiceImpl implements PolarDataService, private DomainFactory domainFactory; + /** + * Constructs the polar data service with default generation settings. + */ public PolarDataServiceImpl() { PolarSheetGenerationSettings settings = PolarSheetGenerationSettingsImpl.createBackendPolarSettings(); ClusterGroup angleClusterGroup = createAngleClusterGroup(); diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AbstractEnrichingProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AbstractEnrichingProcessor.java index 2b0999df9b7..3ecd707f765 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AbstractEnrichingProcessor.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AbstractEnrichingProcessor.java @@ -9,6 +9,11 @@ import com.sap.sse.datamining.components.ProcessorInstruction; import com.sap.sse.datamining.impl.components.AbstractParallelProcessor; import com.sap.sse.datamining.impl.components.AbstractProcessorInstruction; +/** + * This simple abstract processor can be used to enrich data. + * + * @author D054528 (Frederik Petersen) + */ public abstract class AbstractEnrichingProcessor extends AbstractParallelProcessor { @@ -28,6 +33,9 @@ public abstract class AbstractEnrichingProcessor extends }; } + /** + * Takes an input element, enriches it with context data and returns that result. + */ protected abstract ResultType enrich(InputType element); @Override diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AngleAndSpeedRegression.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AngleAndSpeedRegression.java index d681f31ea7a..4c0ae954db4 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AngleAndSpeedRegression.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AngleAndSpeedRegression.java @@ -25,6 +25,17 @@ import com.sap.sailing.polars.regression.impl.IncrementalAnyOrderLeastSquaresImp import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util.Pair; +/** + * This container has two regressions. One for boatSpeed over windSpeed and one for beatAngle over windSpeed + * estimations. + * + * It can return speed and angle for a given windSpeed and should only be used for restricted sets of input data. There + * should be one instance per BoatClass+LegType combination. This seperation needs to be taken care of in a higher level + * of the application. + * + * @author D054528 (Frederik Petersen) + * + */ public class AngleAndSpeedRegression implements Serializable { private static final long serialVersionUID = 6343595388753945979L; @@ -43,6 +54,10 @@ public class AngleAndSpeedRegression implements Serializable { angleRegression.addData(windSpeedInKnots, angleToTheWind.getObject().getDegrees()); } + /** + * Estimate the speed and angle for a given windSpeed by using the regressions contained in this class. + * Seperation by boatclass and legtype needs to be done on a higher level. + */ public SpeedWithBearingWithConfidence estimateSpeedAndAngle(Speed windSpeed) throws NotEnoughDataHasBeenAddedException { double windSpeedInKnots = windSpeed.getKnots(); @@ -57,6 +72,9 @@ public class AngleAndSpeedRegression implements Serializable { speedRegression.getNumberOfAddedPoints() / 100.0), null); } + /** + * Estimates wind speed candidates for the given input scenario based on the speed and angle regressions. + */ public Set> estimateTrueWindSpeedAndAngleCandidates(Speed speedOverGround, LegType legType, Tack tack) throws NotEnoughDataHasBeenAddedException { double[] coefficiants = speedRegression.getOrCreatePolynomialFunction().getCoefficients(); diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AngleClusterPolarClusterKey.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AngleClusterPolarClusterKey.java index 453fec195e4..22157d7b534 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AngleClusterPolarClusterKey.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AngleClusterPolarClusterKey.java @@ -4,6 +4,12 @@ import com.sap.sailing.domain.common.Bearing; import com.sap.sse.datamining.annotations.Dimension; import com.sap.sse.datamining.data.Cluster; +/** + * Allows to group the incoming fixes by beat angle range. + * + * @author D054528 (Frederik Petersen) + * + */ public interface AngleClusterPolarClusterKey extends BasePolarClusterKey { @Dimension(messageKey = "angleCluster") diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AverageAngleContainer.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AverageAngleContainer.java deleted file mode 100644 index c9c9f738e37..00000000000 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AverageAngleContainer.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.sap.sailing.polars.mining; - -import java.io.Serializable; - -public class AverageAngleContainer implements Serializable { - private static final long serialVersionUID = -6370647955050312847L; - private int dataCount = 0; - private double angleSumDeg = 0.0; - private double averageAngleDeg = 0.0; - - public void addFix(double roundedAngleDeg) { - dataCount++; - angleSumDeg += roundedAngleDeg; - averageAngleDeg = angleSumDeg / (double) dataCount; - } - - Double getAverageAngleDeg() { - return averageAngleDeg; - } - -} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/BasePolarClusterKey.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/BasePolarClusterKey.java index b8775d3f855..c429d790e37 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/BasePolarClusterKey.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/BasePolarClusterKey.java @@ -3,6 +3,13 @@ package com.sap.sailing.polars.mining; import com.sap.sailing.domain.base.BoatClass; import com.sap.sse.datamining.annotations.Dimension; +/** + * + * Allows grouping the incoming fixes by boat class. + * + * @author D054528 (Frederik Petersen) + * + */ public interface BasePolarClusterKey { @Dimension(messageKey = "boatClass") diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/BearingClusterGroup.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/BearingClusterGroup.java index 5f879362ea8..96227290990 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/BearingClusterGroup.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/BearingClusterGroup.java @@ -15,8 +15,27 @@ import com.sap.sse.datamining.impl.data.ComparatorClusterBoundary; import com.sap.sse.datamining.impl.data.ComparisonStrategy; import com.sap.sse.datamining.impl.data.FixClusterGroup; +/** + * Allows grouping angles into angle ranges. + * + * @author D054528 (Frederik Petersen) + * + */ public class BearingClusterGroup extends FixClusterGroup { + /** + * Creates an angle range grouping. + * + * E.g. call {@link #BearingClusterGroup(0, 10, 5)} if you wanr to group angle data that ranges from 0 to 10° into + * two containers. 0->5 and 5->10 + * + * @param startAngle + * smallest angle + * @param endAngle + * biggest angle + * @param clusterSize + * how big should each range be. + */ public BearingClusterGroup(int startAngle, int endAngle, int clusterSize) { super(createClusters(startAngle, endAngle, clusterSize)); } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/CubicRegressionPerCourseProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/CubicRegressionPerCourseProcessor.java index ec42958f4b6..0d052e225b8 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/CubicRegressionPerCourseProcessor.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/CubicRegressionPerCourseProcessor.java @@ -23,6 +23,13 @@ import com.sap.sse.datamining.factories.GroupKeyFactory; import com.sap.sse.datamining.impl.components.GroupedDataEntry; import com.sap.sse.datamining.shared.GroupKey; +/** + * Groups incoming fixes by boatclass and legtype into {@link AngleAndSpeedRegression} instances and + * provides access methods to regression data. + * + * @author D054528 (Frederik Petersen) + * + */ public class CubicRegressionPerCourseProcessor implements Processor, Void>, Serializable { @@ -66,6 +73,9 @@ public class CubicRegressionPerCourseProcessor implements } } + /** + * Returns speed and twa for a given scenario, if data is available in the regression. + */ public SpeedWithBearingWithConfidence getAverageSpeedAndCourseOverGround(BoatClass boatClass, Speed windSpeed, LegType legType) throws NotEnoughDataHasBeenAddedException { GroupKey key = createGroupKey(boatClass, legType); @@ -78,6 +88,9 @@ public class CubicRegressionPerCourseProcessor implements return estimatedSpeedAndAngle; } + /** + * Returns windspeed and windangle candidates for a given scenario, if enough data is available. + */ public Set> estimateTrueWindSpeedAndAngleCandidates(BoatClass boatClass, Speed speedOverGround, LegType legType, Tack tack) { GroupKey key = createGroupKey(boatClass, legType); @@ -118,6 +131,14 @@ public class CubicRegressionPerCourseProcessor implements return compoundKey; } + /** + * Allows direct access to speed regression functions for debugging purposes. + * + * For actual usage of the data please use + * {@link #getAverageSpeedAndCourseOverGround(BoatClass, Speed, LegType)} + * and + * {@link #estimateTrueWindSpeedAndAngleCandidates(BoatClass, Speed, LegType, Tack)}. + */ public PolynomialFunction getSpeedRegressionFunction(BoatClass boatClass, LegType legType) throws NotEnoughDataHasBeenAddedException { GroupKey key = createGroupKey(boatClass, legType); @@ -130,6 +151,14 @@ public class CubicRegressionPerCourseProcessor implements return polynomialFunction; } + /** + * Allows direct access to angle regression functions for debugging purposes. + * + * For actual usage of the data please use + * {@link #getAverageSpeedAndCourseOverGround(BoatClass, Speed, LegType)} + * and + * {@link #estimateTrueWindSpeedAndAngleCandidates(BoatClass, Speed, LegType, Tack)}. + */ public PolynomialFunction getAngleRegressionFunction(BoatClass boatClass, LegType legType) throws NotEnoughDataHasBeenAddedException { GroupKey key = createGroupKey(boatClass, legType); diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithOriginInfo.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithOriginInfo.java index c077c344aac..69a0671379d 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithOriginInfo.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithOriginInfo.java @@ -4,6 +4,11 @@ import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.TrackedRace; +/** + * Encapsulates a {@link GPSFixMoving} with more data needed for polar aggregation. + * @author D054528 (Frederik Petersen) + * + */ public class GPSFixMovingWithOriginInfo { private final GPSFixMoving fix; diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java index 0d02bd8ae7c..5e5fd9a5ab9 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java @@ -27,6 +27,13 @@ import com.sap.sse.common.Util.Pair; import com.sap.sse.datamining.data.Cluster; import com.sap.sse.datamining.data.ClusterGroup; +/** + * Encapsulates a {@link GPSFixMoving} with some polar context. Some of the important and computeintensive data is + * calculated once and then cached for further usage in this class. + * + * @author D054528 (Frederik Petersen) + * + */ public class GPSFixMovingWithPolarContext implements LegTypePolarClusterKey, AngleClusterPolarClusterKey { private final GPSFixMoving fix; diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/LegTypePolarClusterKey.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/LegTypePolarClusterKey.java index 4c08883af94..3c2ddf8b664 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/LegTypePolarClusterKey.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/LegTypePolarClusterKey.java @@ -3,6 +3,11 @@ package com.sap.sailing.polars.mining; import com.sap.sailing.domain.common.LegType; import com.sap.sse.datamining.annotations.Dimension; +/** + * Allows grouping incoming fixes by legtype. + * @author D054528 (Frederik Petersen) + * + */ public interface LegTypePolarClusterKey extends BasePolarClusterKey { @Dimension(messageKey = "legType") diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataDimensionCollectionFactory.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataDimensionCollectionFactory.java index 058c4bf28f5..df4c547c4c8 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataDimensionCollectionFactory.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataDimensionCollectionFactory.java @@ -10,6 +10,13 @@ import com.sap.sse.datamining.data.Cluster; import com.sap.sse.datamining.factories.FunctionFactory; import com.sap.sse.datamining.functions.Function; +/** + * Needed in the context of the datamining pipeline for the polar backend. + * It provides the information about grouping classes. + * + * @author D054528 (Frederik Petersen) + * + */ public class PolarDataDimensionCollectionFactory { private static void addTackAndLegTypeDimensions(Collection> dimensions, FunctionFactory functionFactory) diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java index 70d317cf6ee..2b8b469e76c 100755 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java @@ -55,6 +55,17 @@ import com.sap.sse.datamining.impl.components.ParallelMultiDimensionsValueNestin import com.sap.sse.datamining.impl.functions.SimpleParameterizedFunction; import com.sap.sse.util.impl.ThreadFactoryWithPriority; +/** + * Entry point for the aggregation of backend polar data and backend to that data. + * + * Creates a polar data pipeline upon creation and puts incoming GPS fixes into that pipeline. + * Also holds references to the actual data containers in which the aggregation results lay. + * + * For more information on polars in SAP Sailing Analytics, please see: http://wiki.sapsailing.com/wiki/Polars + * + * @author D054528 (Frederik Petersen) + * + */ public class PolarDataMiner { private static final int EXECUTOR_QUEUE_SIZE = 100; diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarFixFilterCriteria.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarFixFilterCriteria.java index 88141f445ae..0a5c07eaa95 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarFixFilterCriteria.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarFixFilterCriteria.java @@ -22,6 +22,14 @@ import com.sap.sse.common.Util.Pair; import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.datamining.components.FilterCriterion; +/** + * Contains several filter criteria for incoming fixes concerning their validity for the polar aggregation. + * + * It is used in the backend polar mining pipeline. + * + * @author D054528 (Frederik Petersen) + * + */ public class PolarFixFilterCriteria implements FilterCriterion { private static final Logger logger = Logger.getLogger(PolarFixFilterCriteria.class.getName()); diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/RoundedTrueWindAngle.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/RoundedTrueWindAngle.java index ef67dbbf112..7e944425b58 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/RoundedTrueWindAngle.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/RoundedTrueWindAngle.java @@ -2,6 +2,14 @@ package com.sap.sailing.polars.mining; import com.sap.sailing.domain.common.Bearing; +/** + * Rounds a {@link Bearing} and provides {@link #hashCode()} and {@link #equals(Object)} methods. + * + * (Basically an Integer class, but handles rounding in constructor). + * + * @author D054528 (Frederik Petersen) + * + */ public class RoundedTrueWindAngle { private final int angleDeg; diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/SpeedRegressionPerAngleClusterProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/SpeedRegressionPerAngleClusterProcessor.java index 9bd8d9f4f24..bc8693a6431 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/SpeedRegressionPerAngleClusterProcessor.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/SpeedRegressionPerAngleClusterProcessor.java @@ -29,6 +29,14 @@ import com.sap.sse.datamining.factories.GroupKeyFactory; import com.sap.sse.datamining.impl.components.GroupedDataEntry; import com.sap.sse.datamining.shared.GroupKey; +/** + * Holds one speed regression per BoatClass, WindSpeed, Beat Angle Range combination and provides means for adding and + * accessing data. Data is added incrementally in and not all historic fixes need to be maintained. This allows for a + * very space efficient structure. + * + * @author D054528 (Frederik Petersen) + * + */ public class SpeedRegressionPerAngleClusterProcessor implements Processor, Void>, Serializable { @@ -92,6 +100,8 @@ public class SpeedRegressionPerAngleClusterProcessor implements * There are angle clusters (size defined in the data mining pipeline construction), which each have their own * regression for boatspeed over windspeed. We don't know the thresholds or centers of the angle clusters here, so * we roughly interpolate by taking 10 values from angle-5 deg to angle+5 deg and average the speeds. + * + * At the time of writing the size of each angle range is 5° so this method provides a pretty smooth interpolation. */ public SpeedWithConfidence estimateBoatSpeed(BoatClass boatClass, Speed windSpeed, Bearing trueWindAngle) throws NotEnoughDataHasBeenAddedException { @@ -153,6 +163,13 @@ public class SpeedRegressionPerAngleClusterProcessor implements throw new RuntimeException("Polar Data Miner failed.", failure); } + + /** + * Allows direct access to angle regression functions for debugging purposes. + * + * For actual usage of the data please use + * {@link #estimateBoatSpeed(BoatClass, Speed, Bearing)}. + */ public PolynomialFunction getSpeedRegressionFunction(BoatClass boatClass, double trueWindAngle) throws NotEnoughDataHasBeenAddedException { GroupKey key = createGroupKey(boatClass, new DegreeBearingImpl(trueWindAngle)); diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/WindSpeedLevel.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/WindSpeedLevel.java deleted file mode 100644 index 07cbaac083c..00000000000 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/WindSpeedLevel.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.sap.sailing.polars.mining; - -import java.io.Serializable; - -import com.sap.sailing.domain.common.Speed; -import com.sap.sailing.domain.common.WindSpeedStepping; - -public class WindSpeedLevel implements Serializable { - - private static final long serialVersionUID = -4498750034316806342L; - private final int level; - private final WindSpeedStepping stepping; - - public WindSpeedLevel(Speed windSpeed, WindSpeedStepping stepping) { - this.level = stepping.getLevelIndexForValue(windSpeed.getKnots()); - this.stepping = stepping; - } - - @Override - public String toString() { - // FIXME right now the toString equals for different steppings. This will resolve in an equal groupkey in the - // datamining framework. The current approach is to use only the default WindStepping. - return "" + level; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + level; - result = prime * result + ((stepping == null) ? 0 : stepping.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - WindSpeedLevel other = (WindSpeedLevel) obj; - if (level != other.level) - return false; - if (stepping == null) { - if (other.stepping != null) - return false; - } else if (!stepping.equals(other.stepping)) - return false; - return true; - } - -} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/IncrementalAnyOrderLeastSquaresImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/IncrementalAnyOrderLeastSquaresImpl.java index b46250afafe..0fefd2ce27d 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/IncrementalAnyOrderLeastSquaresImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/IncrementalAnyOrderLeastSquaresImpl.java @@ -26,6 +26,8 @@ import com.sap.sse.concurrent.NamedReentrantReadWriteLock; * > http://erikerlandson.github.io/blog/2012/07/05/deriving-an-incremental-form-of-the-polynomial-regression-equations/ * * + * It can be used in parallel, because it uses locking for adding and reading data. + * * * @author Frederik Petersen D054528 * @@ -225,6 +227,8 @@ public class IncrementalAnyOrderLeastSquaresImpl implements IncrementalLeastSqua CUBIC, CUBIC_NO_INTERCEPT; /** + * Use to provide a fast alternative to actually computing the inverseMatrix from scratch. + * Right now this uses precalculated equations for cubic functions. * * @param realMatrix * matrix to inverse diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/WindSpeedAndAngleEstimator.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/WindSpeedAndAngleEstimator.java deleted file mode 100644 index 796a87da9b9..00000000000 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/WindSpeedAndAngleEstimator.java +++ /dev/null @@ -1,197 +0,0 @@ -package com.sap.sailing.polars.regression.impl; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence; -import com.sap.sailing.domain.base.impl.SpeedWithBearingWithConfidenceImpl; -import com.sap.sailing.domain.common.Speed; -import com.sap.sailing.domain.common.impl.DegreeBearingImpl; -import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; -import com.sap.sse.common.Util.Pair; - -public class WindSpeedAndAngleEstimator { - - private static final double DEFAULT_MAX_DISTANCE_TO_SAMPLING_POINT = 0.5; - private final List>> averageBoatSpeedAndCourseForWindSpeed; - private final double maxDistanceToSamplingPointFunctionInKnots; - - /** - * - * @param averageBoatSpeedAndCourseForWindSpeed - * List of sampling points. Needs to be sorted by windspeed (A of the Pair object) low to high - */ - public WindSpeedAndAngleEstimator( - List>> averageBoatSpeedAndCourseForWindSpeed) { - this(averageBoatSpeedAndCourseForWindSpeed, DEFAULT_MAX_DISTANCE_TO_SAMPLING_POINT); - } - - /** - * - * @param averageBoatSpeedAndCourseForWindSpeed - * List of sampling points. Needs to be sorted by windspeed (A of the Pair object) low to high - * @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>> averageBoatSpeedAndCourseForWindSpeed, - double maxDistanceToSamplingPointFunctionInKnots) { - this.averageBoatSpeedAndCourseForWindSpeed = averageBoatSpeedAndCourseForWindSpeed; - this.maxDistanceToSamplingPointFunctionInKnots = maxDistanceToSamplingPointFunctionInKnots; - } - - /** - * Looks at all entries in {@link #dataCountAndAngleSumMap} and determines the wind speed and true wind angle at - * which the boat has most likely been sailing to achieve the boatSpeed provided. - * - * BE ADVISED: The result candidate with the highest confidence, which is returned here, is not necessarily the - * candidate which is the most likely to be correct. It really depends on the quality and quantity of gathered polar - * data. - * - * Use {@link #getAverageTrueWindSpeedAndAngleCandidates(Speed)} to get all candidates. - * - * @return null if none was found, otherwise candidate with highest confidence, found in the list returned by - * {@link #getAverageTrueWindSpeedAndAngleCandidates(Speed)} - */ - public SpeedWithBearingWithConfidence getAverageTrueWindSpeedAndAngle(Speed boatSpeed) { - Set> resultCandidates = getAverageTrueWindSpeedAndAngleCandidates(boatSpeed); - - SpeedWithBearingWithConfidence resultCandidateWithHighestConfidence = null; - for (SpeedWithBearingWithConfidence resultCandidate : resultCandidates) { - if (resultCandidateWithHighestConfidence == null - || resultCandidate.getConfidence() > resultCandidateWithHighestConfidence.getConfidence()) { - resultCandidateWithHighestConfidence = resultCandidate; - } - } - return resultCandidateWithHighestConfidence; - } - - /** - * Uses the constructor-supplied sampling points to find wind and course candidates for the supplied boatSpeed. - * - * @return empty set if the underlying data was not sufficient to estimate wind. Otherwise a set of candidates with - * windspeed course of the boat relative to the wind and a confidence which was derived from the confidences - * of underlying fixes, the amount of underlying fixes and the distance between the input boatSpeed and the - * sampling points supplied by the gathered polar data. - */ - public Set> getAverageTrueWindSpeedAndAngleCandidates(Speed boatSpeed) { - double requestedBoatSpeedInKnots = boatSpeed.getKnots(); - Pair> last = null; - Set> resultCandidates = new HashSet<>(); - Set> resultCandidatesWithDistance = new HashSet<>(); - for (Pair> averageSpeedForWindSpeed : averageBoatSpeedAndCourseForWindSpeed) { - Speed windSpeed = averageSpeedForWindSpeed.getA(); - SpeedWithBearingWithConfidence averageBoatSpeedWithCourse = averageSpeedForWindSpeed.getB(); - double currentBoatSpeedInKnots = averageBoatSpeedWithCourse.getObject().getKnots(); - if (last != null) { - double lastBoatSpeedInKnots = last.getB().getObject().getKnots(); - - if (lastBoatSpeedInKnots <= requestedBoatSpeedInKnots - && currentBoatSpeedInKnots >= requestedBoatSpeedInKnots) { - SpeedWithBearingWithConfidenceImpl resultCandidate = createWeightedResultCandidateForLowerLast( - requestedBoatSpeedInKnots, last, windSpeed, averageBoatSpeedWithCourse, - lastBoatSpeedInKnots, currentBoatSpeedInKnots); - resultCandidates.add(resultCandidate); - } else if (lastBoatSpeedInKnots >= requestedBoatSpeedInKnots - && currentBoatSpeedInKnots <= requestedBoatSpeedInKnots) { - SpeedWithBearingWithConfidenceImpl resultCandidate = createWeightedResultCandidateForHigherLast( - requestedBoatSpeedInKnots, last, windSpeed, averageBoatSpeedWithCourse, - lastBoatSpeedInKnots, currentBoatSpeedInKnots); - resultCandidates.add(resultCandidate); - } - } - /* - * 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; - } - // check last sampling point for distance match and return withDistance set if standard set is empty - if (last != null && resultCandidates.size() == 0) { - addResultCandidateIfDistanceValid(requestedBoatSpeedInKnots, resultCandidatesWithDistance, last.getA(), - last.getB()); - resultCandidates = resultCandidatesWithDistance; - } - return resultCandidates; - } - - /** - * 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 addResultCandidateIfDistanceValid(double requestedBoatSpeedInKnots, - Set> resultCandidates, Speed windSpeed, - SpeedWithBearingWithConfidence averageBoatSpeedWithCourse) { - if (Math.abs(averageBoatSpeedWithCourse.getObject().getKnots() - requestedBoatSpeedInKnots) <= maxDistanceToSamplingPointFunctionInKnots) { - SpeedWithBearingWithConfidence resultCandidate = new SpeedWithBearingWithConfidenceImpl( - new KnotSpeedWithBearingImpl(windSpeed.getKnots(), new DegreeBearingImpl(averageBoatSpeedWithCourse - .getObject().getBearing().getDegrees())), averageBoatSpeedWithCourse.getConfidence() * 0.5, - null); - resultCandidates.add(resultCandidate); - } - } - - private SpeedWithBearingWithConfidenceImpl createWeightedResultCandidateForHigherLast( - double requestedBoatSpeedInKnots, Pair> last, Speed windSpeed, - SpeedWithBearingWithConfidence averageBoatSpeedWithCourse, double lastBoatSpeedInKnots, - double currentBoatSpeedInKnots) { - double differenceBetweenBothSpeedsInKnots = lastBoatSpeedInKnots - currentBoatSpeedInKnots; - double ratio; - if (differenceBetweenBothSpeedsInKnots < 0.00001) { - // Both sampling points have the same ratio. Use confidence to determine the ratio - ratio = last.getB().getConfidence() - / (averageBoatSpeedWithCourse.getConfidence() + last.getB().getConfidence()); - } else { - double differenceBetweenRequestedAndLower = requestedBoatSpeedInKnots - currentBoatSpeedInKnots; - ratio = differenceBetweenRequestedAndLower / differenceBetweenBothSpeedsInKnots; - } - double weightedAverageWindSpeedInKnots = ((1 - ratio) * windSpeed.getKnots()) - + (ratio * last.getA().getKnots()); - double currentCourseInDeg = averageBoatSpeedWithCourse.getObject().getBearing().getDegrees(); - double lastCourseInDeg = last.getB().getObject().getBearing().getDegrees(); - double weightedAverageAngleInDeg = ((1 - ratio) * currentCourseInDeg) + (ratio * lastCourseInDeg); - double weightedAverageConfidence = ((1 - ratio) * averageBoatSpeedWithCourse.getConfidence()) - + (ratio * last.getB().getConfidence()); - SpeedWithBearingWithConfidenceImpl resultCandidate = new SpeedWithBearingWithConfidenceImpl( - new KnotSpeedWithBearingImpl(weightedAverageWindSpeedInKnots, new DegreeBearingImpl( - weightedAverageAngleInDeg)), weightedAverageConfidence, null); - return resultCandidate; - } - - private SpeedWithBearingWithConfidenceImpl createWeightedResultCandidateForLowerLast( - double requestedBoatSpeedInKnots, Pair> last, Speed windSpeed, - SpeedWithBearingWithConfidence averageBoatSpeedWithCourse, double lastBoatSpeedInKnots, - double currentBoatSpeedInKnots) { - double differenceBetweenBothSpeedsInKnots = currentBoatSpeedInKnots - lastBoatSpeedInKnots; - double ratio; - if (differenceBetweenBothSpeedsInKnots < 0.00001) { - // Both sampling points have the same ratio. Use confidence to determine the ratio - ratio = averageBoatSpeedWithCourse.getConfidence() - / (averageBoatSpeedWithCourse.getConfidence() + last.getB().getConfidence()); - } else { - double differenceBetweenRequestedAndLower = requestedBoatSpeedInKnots - lastBoatSpeedInKnots; - ratio = differenceBetweenRequestedAndLower / differenceBetweenBothSpeedsInKnots; - } - double weightedAverageWindSpeedInKnots = (ratio * windSpeed.getKnots()) - + ((1 - ratio) * last.getA().getKnots()); - double currentCourseInDeg = averageBoatSpeedWithCourse.getObject().getBearing().getDegrees(); - double lastCourseInDeg = last.getB().getObject().getBearing().getDegrees(); - double weightedAverageAngleInDeg = (ratio * currentCourseInDeg) + ((1 - ratio) * lastCourseInDeg); - double weightedAverageConfidence = (ratio * averageBoatSpeedWithCourse.getConfidence()) - + ((1 - ratio) * last.getB().getConfidence()); - SpeedWithBearingWithConfidenceImpl resultCandidate = new SpeedWithBearingWithConfidenceImpl( - new KnotSpeedWithBearingImpl(weightedAverageWindSpeedInKnots, new DegreeBearingImpl( - weightedAverageAngleInDeg)), weightedAverageConfidence, null); - return resultCandidate; - } - -}