mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-16 09:11:02 +00:00
cache ScalableBearingAndScalableDouble for ManeuverClassification, avoiding expensive creation; use approximation for ScalableBearing difference which is much faster
This commit is contained in:
1 parent
882f18300f
commit
500dcf41e9
4 files changed
+97
-3
No files matched your search
+21
@@ -63,4 +63,25 @@ public class ScalableBearing implements ScalableValueWithDistance<DoublePair, Be
|
|||||||
public double getDistance(Bearing other) {
|
public double getDistance(Bearing other) {
|
||||||
return Math.abs(divide(1).getDifferenceTo(other).getDegrees());
|
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);
|
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);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+10
-1
@@ -147,6 +147,7 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl {
|
|||||||
private final Bearing middleManeuverCourse;
|
private final Bearing middleManeuverCourse;
|
||||||
private final Distance maneuverLoss;
|
private final Distance maneuverLoss;
|
||||||
private Pair<Double, SpeedWithBearingWithConfidence<Void>>[] likelihoodAndTWSBasedOnSpeedAndAngleCache;
|
private Pair<Double, SpeedWithBearingWithConfidence<Void>>[] likelihoodAndTWSBasedOnSpeedAndAngleCache;
|
||||||
|
private ScalableBearingAndScalableDouble scalableMiddleManeuverCourseAndManeuverAngleDegCache;
|
||||||
|
|
||||||
protected ManeuverClassification(Competitor competitor, Maneuver maneuver) {
|
protected ManeuverClassification(Competitor competitor, Maneuver maneuver) {
|
||||||
super();
|
super();
|
||||||
@@ -185,6 +186,14 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl {
|
|||||||
public Bearing getMiddleManeuverCourse() {
|
public Bearing getMiddleManeuverCourse() {
|
||||||
return middleManeuverCourse;
|
return middleManeuverCourse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ScalableBearingAndScalableDouble getScalableMiddleManeuverCourseAndManeuverAngleDeg() {
|
||||||
|
if (scalableMiddleManeuverCourseAndManeuverAngleDegCache == null) {
|
||||||
|
scalableMiddleManeuverCourseAndManeuverAngleDegCache = new ScalableBearingAndScalableDouble(
|
||||||
|
getMiddleManeuverCourse(), getManeuverAngleDeg());
|
||||||
|
}
|
||||||
|
return scalableMiddleManeuverCourseAndManeuverAngleDegCache;
|
||||||
|
}
|
||||||
|
|
||||||
public SpeedWithBearingWithConfidence<Void> getEstimatedWindSpeedAndBearing(ManeuverType maneuverType) {
|
public SpeedWithBearingWithConfidence<Void> getEstimatedWindSpeedAndBearing(ManeuverType maneuverType) {
|
||||||
Pair<Double, SpeedWithBearingWithConfidence<Void>> likelihoodAndTWSBasedOnSpeedAndAngle = polarService.getManeuverLikelihoodAndTwsTwa(
|
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 =
|
KMeansMappingClusterer<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble> clusterer =
|
||||||
new KMeansMappingClusterer<>(numberOfClusters,
|
new KMeansMappingClusterer<>(numberOfClusters,
|
||||||
getManeuverClassifications(maneuvers),
|
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
|
// use an evenly distributed set of cluster seeds for clustering wind direction estimations
|
||||||
Stream.concat(IntStream.range(0, numberOfClusters/2).mapToObj((i)->
|
Stream.concat(IntStream.range(0, numberOfClusters/2).mapToObj((i)->
|
||||||
new Pair<>(new DegreeBearingImpl(((double) i)*360./(double) numberOfClusters/2), 45.)),
|
new Pair<>(new DegreeBearingImpl(((double) i)*360./(double) numberOfClusters/2), 45.)),
|
||||||
|
|||||||
+6
-2
@@ -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)
|
* 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
|
* 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
|
* 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)
|
* @author Axel Uhl (D043530)
|
||||||
*
|
*
|
||||||
@@ -44,7 +48,7 @@ public class ScalableBearingAndScalableDouble implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getDistance(Pair<Bearing, Double> other) {
|
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());
|
final double b2DiffDeg = Math.abs(b2.divide(1)-other.getB());
|
||||||
return Math.sqrt(b1DiffDeg*b1DiffDeg + b2DiffDeg*b2DiffDeg);
|
return Math.sqrt(b1DiffDeg*b1DiffDeg + b2DiffDeg*b2DiffDeg);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user