cache ScalableBearingAndScalableDouble for ManeuverClassification, avoiding expensive creation; use approximation for ScalableBearing difference which is much faster

This commit is contained in:
Axel Uhl committed 2015-01-24 02:24:59 +01:00
1 parent 882f18300f
commit 500dcf41e9
4 files changed
+97 -3

No files matched your search

@@ -63,4 +63,25 @@ public class ScalableBearing implements ScalableValueWithDistance<DoublePair, Be
public double getDistance(Bearing other) {
return Math.abs(divide(1).getDifferenceTo(other).getDegrees());
}
/**
* Computing the difference to a {@link bearing}, as implemented by {@link #getDistance(Bearing)} is correct but
* slow because the {@link Math#atan2(double)} function is used to {@link ScalableBearing#divide(double) bring the
* scalable bearing down to a bearing}. A less precise, yet much faster to compute and still monotonous distance
* measure computes the length of the line segment connecting this bearing's projection onto a circle with the
* projection of the bearing <code>a</code> to the same circle. The distance returned by this method will provide
* almost equal values for very small differences (below 1deg) but significantly smaller values for big differences.
*
* @return an approximate difference in degrees, fairly accurate for small differences, but returning 360.0/PI
* (approximately 114) instead of 180 for the maximum difference
*/
public double getApproximateDegreeDistanceTo(Bearing a) {
double scale = Math.sqrt(sin*sin + cos*cos);
double x = cos/scale;
double y = sin/scale;
double ax = Math.cos(a.getRadians());
double ay = Math.sin(a.getRadians());
double normalDistance = Math.sqrt((x-ax)*(x-ax) + (y-ay)*(y-ay));
return normalDistance / Math.PI * 180.;
}
}
@@ -531,5 +531,65 @@ public class ConfidenceTest {
assertTrue(average.getObject().getLatDeg() < 49.15);
assertEquals((8.0*4/4 + 9.0*2/4 + 8.5*1/4)/(7./4.), average.getObject().getLngDeg(), 0.01);
}
@Test
public void simpleApproximateScalableBearingDistanceTest1() {
com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing sb = new com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing(new DegreeBearingImpl(100));
Bearing b = new DegreeBearingImpl(280);
assertEquals(360./Math.PI, sb.getApproximateDegreeDistanceTo(b), 0.001);
}
@Test
public void simpleApproximateScalableBearingDistanceTest2() {
com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing sb = new com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing(new DegreeBearingImpl(0));
Bearing b = new DegreeBearingImpl(180);
assertEquals(360./Math.PI, sb.getApproximateDegreeDistanceTo(b), 0.001);
}
@Test
public void simpleApproximateScalableBearingDistanceTest3() {
com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing sb = new com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing(new DegreeBearingImpl(100));
Bearing b = new DegreeBearingImpl(101);
assertEquals(1, sb.getApproximateDegreeDistanceTo(b), 0.01);
}
@Test
public void simpleApproximateScalableBearingDistanceTest4() {
com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing sb = new com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing(new DegreeBearingImpl(0));
Bearing b = new DegreeBearingImpl(1);
assertEquals(1, sb.getApproximateDegreeDistanceTo(b), 0.01);
}
@Test
public void scaledApproximateScalableBearingDistanceTest1() {
com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing sb = new com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing(new DegreeBearingImpl(100)).
multiply(123);
Bearing b = new DegreeBearingImpl(280);
assertEquals(360./Math.PI, sb.getApproximateDegreeDistanceTo(b), 0.001);
}
@Test
public void scaledApproximateScalableBearingDistanceTest2() {
com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing sb = new com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing(new DegreeBearingImpl(0)).
multiply(123);
Bearing b = new DegreeBearingImpl(180);
assertEquals(360./Math.PI, sb.getApproximateDegreeDistanceTo(b), 0.001);
}
@Test
public void scaledApproximateScalableBearingDistanceTest3() {
com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing sb = new com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing(new DegreeBearingImpl(100)).
multiply(123);
Bearing b = new DegreeBearingImpl(101);
assertEquals(1, sb.getApproximateDegreeDistanceTo(b), 0.01);
}
@Test
public void scaledApproximateScalableBearingDistanceTest4() {
com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing sb = new com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing(new DegreeBearingImpl(0)).
multiply(123);
Bearing b = new DegreeBearingImpl(1);
assertEquals(1, sb.getApproximateDegreeDistanceTo(b), 0.01);
}
}
@@ -147,6 +147,7 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl {
private final Bearing middleManeuverCourse;
private final Distance maneuverLoss;
private Pair<Double, SpeedWithBearingWithConfidence<Void>>[] likelihoodAndTWSBasedOnSpeedAndAngleCache;
private ScalableBearingAndScalableDouble scalableMiddleManeuverCourseAndManeuverAngleDegCache;
protected ManeuverClassification(Competitor competitor, Maneuver maneuver) {
super();
@@ -185,6 +186,14 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl {
public Bearing getMiddleManeuverCourse() {
return middleManeuverCourse;
}
public ScalableBearingAndScalableDouble getScalableMiddleManeuverCourseAndManeuverAngleDeg() {
if (scalableMiddleManeuverCourseAndManeuverAngleDegCache == null) {
scalableMiddleManeuverCourseAndManeuverAngleDegCache = new ScalableBearingAndScalableDouble(
getMiddleManeuverCourse(), getManeuverAngleDeg());
}
return scalableMiddleManeuverCourseAndManeuverAngleDegCache;
}
public SpeedWithBearingWithConfidence<Void> getEstimatedWindSpeedAndBearing(ManeuverType maneuverType) {
Pair<Double, SpeedWithBearingWithConfidence<Void>> likelihoodAndTWSBasedOnSpeedAndAngle = polarService.getManeuverLikelihoodAndTwsTwa(
@@ -339,7 +348,7 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl {
KMeansMappingClusterer<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble> clusterer =
new KMeansMappingClusterer<>(numberOfClusters,
getManeuverClassifications(maneuvers),
(mc)->new ScalableBearingAndScalableDouble(mc.getMiddleManeuverCourse(), mc.getManeuverAngleDeg()), // maps maneuver classification to cluster metric
(mc)->mc.getScalableMiddleManeuverCourseAndManeuverAngleDeg(), // maps maneuver classification to cluster metric
// use an evenly distributed set of cluster seeds for clustering wind direction estimations
Stream.concat(IntStream.range(0, numberOfClusters/2).mapToObj((i)->
new Pair<>(new DegreeBearingImpl(((double) i)*360./(double) numberOfClusters/2), 45.)),
@@ -12,7 +12,11 @@ import com.sap.sse.common.scalablevalue.ScalableValueWithDistance;
* Addition} works component wise, and so does {@link #multiply(double) multiplication} and {@link #divide(double)
* division}. The {@link #getDistance(Pair) distance} is calculated as the Euklidian two-dimensional distance between
* the two bearing pairs, with "degrees" as conceptual unit. For example, another object of this type that is 2 and 3 degrees
* apart from this object will have a distance of {@link Math#sqrt(double) Math.sqrt(13)}.
* apart from this object will have a distance of {@link Math#sqrt(double) Math.sqrt(13)}.<p>
*
* The {@link #getDistance(Pair)} implementation uses {@link ScalableBearing#getApproximateDegreeDistanceTo(Bearing)} on
* the scalable bearing part of this object. This is only an approximation but works a lot faster and is still monotonous.
* It works well for small angles.
*
* @author Axel Uhl (D043530)
*
@@ -44,7 +48,7 @@ public class ScalableBearingAndScalableDouble implements
@Override
public double getDistance(Pair<Bearing, Double> other) {
final double b1DiffDeg = b1.divide(1).getDifferenceTo(other.getA()).getDegrees();
final double b1DiffDeg = b1.getApproximateDegreeDistanceTo(other.getA());
final double b2DiffDeg = Math.abs(b2.divide(1)-other.getB());
return Math.sqrt(b1DiffDeg*b1DiffDeg + b2DiffDeg*b2DiffDeg);
}