Merge remote-tracking branch 'server/master'

This commit is contained in:
Axel Uhl committed 2015-05-29 16:49:56 +02:00
commit 9aba87a8a8
16 files changed
+193 -128

No files matched your search

@@ -173,12 +173,11 @@ public interface PolarDataService {
* *
* @param boatClass * @param boatClass
* @param legType * @param legType
* @param tack
* @return The estimating function for the tack and legtype combination estimating boatspeed over windspeed for the * @return The estimating function for the tack and legtype combination estimating boatspeed over windspeed for the
* given boat class. All values in kn. * given boat class. All values in kn.
* @throws NotEnoughDataHasBeenAddedException * @throws NotEnoughDataHasBeenAddedException
*/ */
PolynomialFunction getSpeedRegressionFunction(BoatClass boatClass, LegType legType, Tack tack) throws NotEnoughDataHasBeenAddedException; PolynomialFunction getSpeedRegressionFunction(BoatClass boatClass, LegType legType) throws NotEnoughDataHasBeenAddedException;
/** /**
* This method is not intended to be used directly apart from debugging purposes. If you intend to use the polar service please * This method is not intended to be used directly apart from debugging purposes. If you intend to use the polar service please
@@ -186,12 +185,11 @@ public interface PolarDataService {
* *
* @param boatClass * @param boatClass
* @param legType * @param legType
* @param tack
* @return The estimating function for the tack and legtype combination estimating true wind angle over windspeed for the * @return The estimating function for the tack and legtype combination estimating true wind angle over windspeed for the
* given boat class. TWA in degrees and windspeeds in knots. * given boat class. TWA in degrees and windspeeds in knots.
* @throws NotEnoughDataHasBeenAddedException * @throws NotEnoughDataHasBeenAddedException
*/ */
PolynomialFunction getAngleRegressionFunction(BoatClass boatClass, LegType legType, Tack tack) throws NotEnoughDataHasBeenAddedException; PolynomialFunction getAngleRegressionFunction(BoatClass boatClass, LegType legType) throws NotEnoughDataHasBeenAddedException;
/** /**
* This method is not intended to be used directly apart from debugging purposes. If you intend to use the polar service please * This method is not intended to be used directly apart from debugging purposes. If you intend to use the polar service please
@@ -4,6 +4,18 @@
<stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/> <stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/>
<booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="true"/> <booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="true"/>
<booleanAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT_AUTO" value="true"/> <booleanAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT_AUTO" value="true"/>
<listEntry value="com.sap.sailing.gwt.ui.Simulator"/>
<listEntry value="com.sap.sailing.gwt.ui.Leaderboard"/>
<listEntry value="com.sap.sailing.gwt.ui.PolarSheets"/>
<listEntry value="com.sap.sailing.gwt.ui.RegattaOverview"/>
<listEntry value="com.sap.sailing.gwt.ui.Spectator"/>
<listEntry value="com.sap.sailing.gwt.ui.AdminConsole"/>
<listEntry value="com.sap.sailing.gwt.ui.LeaderboardEditing"/>
<listEntry value="com.sap.sailing.gwt.ui.DataMining"/>
<listEntry value="com.sap.sailing.gwt.Home"/>
<listEntry value="com.sap.sailing.gwt.ui.RaceBoard"/>
<listEntry value="com.sap.sailing.gwt.ui.YoutubePopup"/>
<listEntry value="com.sap.sailing.gwt.AutoPlay"/>
<booleanAttribute key="com.google.gwt.eclipse.core.SUPERDEVMODE_ENABLED" value="true"/> <booleanAttribute key="com.google.gwt.eclipse.core.SUPERDEVMODE_ENABLED" value="true"/>
<stringAttribute key="com.google.gwt.eclipse.core.URL" value="/gwt/Home.html"/> <stringAttribute key="com.google.gwt.eclipse.core.URL" value="/gwt/Home.html"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"> <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
@@ -56,7 +68,7 @@
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/> <booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> <stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/> <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-superDevMode -incremental -war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort auto -startupUrl /gwt/Home.html com.sap.sailing.gwt.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/PolarSheets.html com.sap.sailing.gwt.ui.PolarSheets -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.regattaoverview.RegattaOverview -startupUrl /gwt/DataMining.html com.sap.sailing.gwt.ui.DataMining -startupUrl /gwt/Simulator.html com.sap.sailing.gwt.ui.Simulator -startupUrl /gwt/VideoPopup.html com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.ui.PolarSheets com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.regattaoverview.RegattaOverview com.sap.sailing.gwt.ui.VideoPopup com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.Home com.sap.sailing.gwt.ui.RaceBoard com.sap.sailing.gwt.ui.RegattaOverview"/> <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-superDevMode -incremental -war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort auto -startupUrl /gwt/Home.html com.sap.sailing.gwt.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/PolarSheets.html com.sap.sailing.gwt.ui.PolarSheets -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.ui.RegattaOverview -startupUrl /gwt/DataMining.html com.sap.sailing.gwt.ui.DataMining -startupUrl /gwt/Simulator.html com.sap.sailing.gwt.ui.Simulator -startupUrl /gwt/VideoPopup.html com.sap.sailing.gwt.ui.VideoPopup com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.ui.PolarSheets com.sap.sailing.gwt.ui.RegattaOverview com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.Home com.sap.sailing.gwt.ui.RaceBoard"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/> <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms1024m -Xmx3072m"/> <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms1024m -Xmx3072m"/>
</launchConfiguration> </launchConfiguration>
@@ -12,7 +12,7 @@
<listEntry value="com.sap.sailing.gwt.ui.LeaderboardEditing"/> <listEntry value="com.sap.sailing.gwt.ui.LeaderboardEditing"/>
<listEntry value="com.sap.sailing.gwt.ui.AdminConsole"/> <listEntry value="com.sap.sailing.gwt.ui.AdminConsole"/>
<listEntry value="com.sap.sailing.gwt.ui.Spectator"/> <listEntry value="com.sap.sailing.gwt.ui.Spectator"/>
<listEntry value="com.sap.sailing.gwt.regattaoverview.RegattaOverview"/> <listEntry value="com.sap.sailing.gwt.ui.RegattaOverview"/>
<listEntry value="com.sap.sailing.gwt.AutoPlay"/> <listEntry value="com.sap.sailing.gwt.AutoPlay"/>
<listEntry value="com.sap.sailing.gwt.ui.VideoPopup"/> <listEntry value="com.sap.sailing.gwt.ui.VideoPopup"/>
<listEntry value="com.sap.sailing.gwt.ui.PolarSheets"/> <listEntry value="com.sap.sailing.gwt.ui.PolarSheets"/>
@@ -69,7 +69,7 @@
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/> <booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> <stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/> <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-nosuperDevMode -war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort auto -startupUrl /gwt/Home.html com.sap.sailing.gwt.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/PolarSheets.html com.sap.sailing.gwt.ui.PolarSheets -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.ui.RegattaOverview -startupUrl /gwt/DataMining.html com.sap.sailing.gwt.ui.DataMining -startupUrl /gwt/Simulator.html com.sap.sailing.gwt.ui.Simulator -startupUrl /gwt/VideoPopup.html com.sap.sailing.gwt.ui.VideoPopup -startupUrl /security/ui/Login.html com.sap.sse.security.ui.Login -startupUrl /security/ui/EditProfile.html com.sap.sse.security.ui.EditProfile -startupUrl /security/ui/EmailValidation.html com.sap.sse.security.ui.EmailValidation com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.ui.PolarSheets com.sap.sailing.gwt.ui.VideoPopup com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.Home com.sap.sailing.gwt.ui.RaceBoard com.sap.sailing.gwt.regattaoverview.RegattaOverview"/> <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-nosuperDevMode -war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort auto -startupUrl /gwt/Home.html com.sap.sailing.gwt.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/PolarSheets.html com.sap.sailing.gwt.ui.PolarSheets -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.ui.RegattaOverview -startupUrl /gwt/DataMining.html com.sap.sailing.gwt.ui.DataMining -startupUrl /gwt/Simulator.html com.sap.sailing.gwt.ui.Simulator -startupUrl /gwt/VideoPopup.html com.sap.sailing.gwt.ui.VideoPopup -startupUrl /security/ui/Login.html com.sap.sse.security.ui.Login -startupUrl /security/ui/EditProfile.html com.sap.sse.security.ui.EditProfile -startupUrl /security/ui/EmailValidation.html com.sap.sse.security.ui.EmailValidation com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.ui.PolarSheets com.sap.sailing.gwt.ui.VideoPopup com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.ui.RegattaOverview com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.Home com.sap.sailing.gwt.ui.RaceBoard"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/> <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m -Dgwt-usearchives=false -Dgwt.persistentunitcache=false"/> <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m -Dgwt-usearchives=false -Dgwt.persistentunitcache=false"/>
</launchConfiguration> </launchConfiguration>
@@ -10,6 +10,7 @@ import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence; import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence;
import com.sap.sailing.domain.base.SpeedWithConfidence; import com.sap.sailing.domain.base.SpeedWithConfidence;
import com.sap.sailing.domain.base.impl.SpeedWithBearingWithConfidenceImpl;
import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.LegType; import com.sap.sailing.domain.common.LegType;
import com.sap.sailing.domain.common.ManeuverType; import com.sap.sailing.domain.common.ManeuverType;
@@ -20,6 +21,7 @@ import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.common.confidence.BearingWithConfidence; import com.sap.sailing.domain.common.confidence.BearingWithConfidence;
import com.sap.sailing.domain.common.confidence.impl.BearingWithConfidenceImpl; import com.sap.sailing.domain.common.confidence.impl.BearingWithConfidenceImpl;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.polars.PolarDataService; import com.sap.sailing.domain.polars.PolarDataService;
@@ -60,9 +62,19 @@ public class PolarDataServiceImpl implements PolarDataService {
} }
@Override @Override
public SpeedWithBearingWithConfidence<Void> getAverageSpeedWithBearing(BoatClass boatClass, public SpeedWithBearingWithConfidence<Void> getAverageSpeedWithBearing(BoatClass boatClass, Speed windSpeed,
Speed windSpeed, LegType legType, Tack tack, boolean useRegressionForSpeed) throws NotEnoughDataHasBeenAddedException { LegType legType, Tack tack, boolean useRegressionForSpeed) throws NotEnoughDataHasBeenAddedException {
return polarDataMiner.getAverageSpeedAndCourseOverGround(boatClass, windSpeed, legType, tack, useRegressionForSpeed); SpeedWithBearingWithConfidence<Void> averageSpeedAndCourseOverGround = polarDataMiner
.getAverageSpeedAndCourseOverGround(boatClass, windSpeed, legType, useRegressionForSpeed);
if ((tack == Tack.PORT && legType == LegType.UPWIND) || (tack == Tack.STARBOARD && legType == LegType.DOWNWIND)) {
DegreeBearingImpl bearing = new DegreeBearingImpl(-averageSpeedAndCourseOverGround.getObject().getBearing()
.getDegrees());
KnotSpeedWithBearingImpl speed = new KnotSpeedWithBearingImpl(averageSpeedAndCourseOverGround.getObject()
.getKnots(), bearing);
averageSpeedAndCourseOverGround = new SpeedWithBearingWithConfidenceImpl<Void>(speed,
averageSpeedAndCourseOverGround.getConfidence(), null);
}
return averageSpeedAndCourseOverGround;
} }
@@ -139,21 +151,21 @@ public class PolarDataServiceImpl implements PolarDataService {
} }
@Override @Override
public PolynomialFunction getSpeedRegressionFunction(BoatClass boatClass, LegType legType, Tack tack) public PolynomialFunction getSpeedRegressionFunction(BoatClass boatClass, LegType legType)
throws NotEnoughDataHasBeenAddedException { throws NotEnoughDataHasBeenAddedException {
return polarDataMiner.getSpeedRegressionFunction(boatClass, legType, tack); return polarDataMiner.getSpeedRegressionFunction(boatClass, legType);
} }
@Override @Override
public PolynomialFunction getAngleRegressionFunction(BoatClass boatClass, LegType legType, Tack tack) public PolynomialFunction getAngleRegressionFunction(BoatClass boatClass, LegType legType)
throws NotEnoughDataHasBeenAddedException { throws NotEnoughDataHasBeenAddedException {
return polarDataMiner.getAngleRegressionFunction(boatClass, legType, tack); return polarDataMiner.getAngleRegressionFunction(boatClass, legType);
} }
@Override @Override
public PolynomialFunction getSpeedRegressionFunction(BoatClass boatClass, double trueWindAngle) public PolynomialFunction getSpeedRegressionFunction(BoatClass boatClass, double trueWindAngle)
throws NotEnoughDataHasBeenAddedException { throws NotEnoughDataHasBeenAddedException {
return polarDataMiner.getSpeedRegressionFunction(boatClass, trueWindAngle); return polarDataMiner.getSpeedRegressionFunction(boatClass, Math.abs(trueWindAngle));
} }
@Override @Override
@@ -171,14 +183,9 @@ public class PolarDataServiceImpl implements PolarDataService {
if (boatClass == null || windSpeed == null) { if (boatClass == null || windSpeed == null) {
throw new IllegalArgumentException("Boatclass and windspeed cannot be null."); throw new IllegalArgumentException("Boatclass and windspeed cannot be null.");
} }
SpeedWithBearingWithConfidence<Void> port = getAverageSpeedWithBearing(boatClass, windSpeed, legType, Tack.PORT); SpeedWithBearingWithConfidence<Void> speed = polarDataMiner.getAverageSpeedAndCourseOverGround(boatClass, windSpeed, legType, true);
SpeedWithBearingWithConfidence<Void> starboard = getAverageSpeedWithBearing(boatClass, windSpeed, legType, Bearing bearing = new DegreeBearingImpl(speed.getObject().getBearing().getDegrees() * 2);
Tack.STARBOARD); BearingWithConfidence<Void> bearingWithConfidence = new BearingWithConfidenceImpl<Void>(bearing, speed.getConfidence(),
double angleSumInDeg = Math.abs(port.getObject().getBearing().getDegrees())
+ Math.abs(starboard.getObject().getBearing().getDegrees());
Bearing bearing = new DegreeBearingImpl(angleSumInDeg);
double confidence = (port.getConfidence() + starboard.getConfidence()) / 2;
BearingWithConfidence<Void> bearingWithConfidence = new BearingWithConfidenceImpl<Void>(bearing, confidence,
null); null);
return bearingWithConfidence; return bearingWithConfidence;
} }
@@ -8,9 +8,11 @@ import org.apache.commons.math.analysis.polynomials.PolynomialFunction;
import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence; import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence;
import com.sap.sailing.domain.base.impl.SpeedWithBearingWithConfidenceImpl; import com.sap.sailing.domain.base.impl.SpeedWithBearingWithConfidenceImpl;
import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.LegType;
import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.common.confidence.BearingWithConfidence; import com.sap.sailing.domain.common.confidence.BearingWithConfidence;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
@@ -30,7 +32,7 @@ public class AngleAndSpeedRegression {
private double maxWindSpeedInKnots = -1; private double maxWindSpeedInKnots = -1;
public void addData(WindWithConfidence<Pair<Position, TimePoint>> windSpeed, public void addData(WindWithConfidence<Pair<Position, TimePoint>> windSpeed,
BearingWithConfidence<Integer> angleToTheWind, SpeedWithBearingWithConfidence<TimePoint> boatSpeed) { BearingWithConfidence<Void> angleToTheWind, SpeedWithBearingWithConfidence<TimePoint> boatSpeed) {
double windSpeedInKnots = windSpeed.getObject().getKnots(); double windSpeedInKnots = windSpeed.getObject().getKnots();
if (windSpeedInKnots > maxWindSpeedInKnots) { if (windSpeedInKnots > maxWindSpeedInKnots) {
maxWindSpeedInKnots = windSpeedInKnots; maxWindSpeedInKnots = windSpeedInKnots;
@@ -51,7 +53,7 @@ public class AngleAndSpeedRegression {
return new SpeedWithBearingWithConfidenceImpl<Void>(speedWithBearing, Math.min(1, speedRegression.getNumberOfAddedPoints() / 100.0), null); return new SpeedWithBearingWithConfidenceImpl<Void>(speedWithBearing, Math.min(1, speedRegression.getNumberOfAddedPoints() / 100.0), null);
} }
public Set<SpeedWithBearingWithConfidence<Void>> estimateTrueWindSpeedAndAngleCandidates(Speed speedOverGround) throws NotEnoughDataHasBeenAddedException { public Set<SpeedWithBearingWithConfidence<Void>> estimateTrueWindSpeedAndAngleCandidates(Speed speedOverGround, LegType legType, Tack tack) throws NotEnoughDataHasBeenAddedException {
double[] coefficiants = speedRegression.getOrCreatePolynomialFunction().getCoefficients(); double[] coefficiants = speedRegression.getOrCreatePolynomialFunction().getCoefficients();
CubicEquation equation = new CubicEquation(coefficiants[2], coefficiants[1], coefficiants[0], -speedOverGround.getKnots()); CubicEquation equation = new CubicEquation(coefficiants[2], coefficiants[1], coefficiants[0], -speedOverGround.getKnots());
@@ -64,6 +66,9 @@ public class AngleAndSpeedRegression {
boolean angleFound; boolean angleFound;
try { try {
angle = angleRegression.getOrCreatePolynomialFunction().value(windSpeedCandidateInKnots); angle = angleRegression.getOrCreatePolynomialFunction().value(windSpeedCandidateInKnots);
if ((tack == Tack.PORT && legType == LegType.UPWIND) || (tack == Tack.STARBOARD && legType == LegType.DOWNWIND)) {
angle = -angle;
}
angleFound = true; angleFound = true;
} catch(NotEnoughDataHasBeenAddedException e) { } catch(NotEnoughDataHasBeenAddedException e) {
angleFound = false; angleFound = false;
@@ -45,12 +45,13 @@ public class CubicRegressionPerCourseProcessor implements Processor<GroupedDataE
regressions.put(key, regression); regressions.put(key, regression);
} }
} }
regression.addData(fix.getWind(), fix.getAngleToTheWind(), fix.getBoatSpeed()); regression.addData(fix.getWind(), fix.getAbsoluteAngleToTheWind(), fix.getBoatSpeed());
} }
} }
public SpeedWithBearingWithConfidence<Void> getAverageSpeedAndCourseOverGround(BoatClass boatClass, Speed windSpeed, LegType legType, Tack tack) throws NotEnoughDataHasBeenAddedException { public SpeedWithBearingWithConfidence<Void> getAverageSpeedAndCourseOverGround(BoatClass boatClass,
GroupKey key = createGroupKey(boatClass, legType, tack); Speed windSpeed, LegType legType) throws NotEnoughDataHasBeenAddedException {
GroupKey key = createGroupKey(boatClass, legType);
SpeedWithBearingWithConfidence<Void> estimatedSpeedAndAngle = null; SpeedWithBearingWithConfidence<Void> estimatedSpeedAndAngle = null;
if (regressions.containsKey(key)) { if (regressions.containsKey(key)) {
estimatedSpeedAndAngle = regressions.get(key).estimateSpeedAndAngle(windSpeed); estimatedSpeedAndAngle = regressions.get(key).estimateSpeedAndAngle(windSpeed);
@@ -62,11 +63,11 @@ public class CubicRegressionPerCourseProcessor implements Processor<GroupedDataE
public Set<SpeedWithBearingWithConfidence<Void>> estimateTrueWindSpeedAndAngleCandidates(BoatClass boatClass, public Set<SpeedWithBearingWithConfidence<Void>> estimateTrueWindSpeedAndAngleCandidates(BoatClass boatClass,
Speed speedOverGround, LegType legType, Tack tack) { Speed speedOverGround, LegType legType, Tack tack) {
GroupKey key = createGroupKey(boatClass, legType, tack); GroupKey key = createGroupKey(boatClass, legType);
Set<SpeedWithBearingWithConfidence<Void>> result = new HashSet<>(); Set<SpeedWithBearingWithConfidence<Void>> result = new HashSet<>();
if (regressions.containsKey(key)) { if (regressions.containsKey(key)) {
try { try {
result = regressions.get(key).estimateTrueWindSpeedAndAngleCandidates(speedOverGround); result = regressions.get(key).estimateTrueWindSpeedAndAngleCandidates(speedOverGround, legType, tack);
} catch (NotEnoughDataHasBeenAddedException e) { } catch (NotEnoughDataHasBeenAddedException e) {
// Return empty result // Return empty result
} }
@@ -76,20 +77,14 @@ public class CubicRegressionPerCourseProcessor implements Processor<GroupedDataE
return result; return result;
} }
private GroupKey createGroupKey(final BoatClass boatClass, final LegType legType, private GroupKey createGroupKey(final BoatClass boatClass, final LegType legType) {
final Tack tack) { LegTypePolarClusterKey key = new LegTypePolarClusterKey() {
TackAndLegTypePolarClusterKey key = new TackAndLegTypePolarClusterKey() {
@Override @Override
public BoatClass getBoatClass() { public BoatClass getBoatClass() {
return boatClass; return boatClass;
} }
@Override
public Tack getTack() {
return tack;
}
@Override @Override
public LegType getLegType() { public LegType getLegType() {
return legType; return legType;
@@ -106,9 +101,9 @@ public class CubicRegressionPerCourseProcessor implements Processor<GroupedDataE
return compoundKey; return compoundKey;
} }
public PolynomialFunction getSpeedRegressionFunction(BoatClass boatClass, LegType legType, Tack tack) public PolynomialFunction getSpeedRegressionFunction(BoatClass boatClass, LegType legType)
throws NotEnoughDataHasBeenAddedException { throws NotEnoughDataHasBeenAddedException {
GroupKey key = createGroupKey(boatClass, legType, tack); GroupKey key = createGroupKey(boatClass, legType);
PolynomialFunction polynomialFunction; PolynomialFunction polynomialFunction;
if (regressions.containsKey(key)) { if (regressions.containsKey(key)) {
polynomialFunction = regressions.get(key).getSpeedRegressionFunction(); polynomialFunction = regressions.get(key).getSpeedRegressionFunction();
@@ -118,9 +113,9 @@ public class CubicRegressionPerCourseProcessor implements Processor<GroupedDataE
return polynomialFunction; return polynomialFunction;
} }
public PolynomialFunction getAngleRegressionFunction(BoatClass boatClass, LegType legType, Tack tack) public PolynomialFunction getAngleRegressionFunction(BoatClass boatClass, LegType legType)
throws NotEnoughDataHasBeenAddedException { throws NotEnoughDataHasBeenAddedException {
GroupKey key = createGroupKey(boatClass, legType, tack); GroupKey key = createGroupKey(boatClass, legType);
PolynomialFunction polynomialFunction; PolynomialFunction polynomialFunction;
if (regressions.containsKey(key)) { if (regressions.containsKey(key)) {
polynomialFunction = regressions.get(key).getAngleRegressionFunction(); polynomialFunction = regressions.get(key).getAngleRegressionFunction();
@@ -11,16 +11,16 @@ import com.sap.sailing.domain.common.LegType;
import com.sap.sailing.domain.common.NoWindException; import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.common.WindSource; import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.WindSourceType; import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.confidence.BearingWithConfidence; import com.sap.sailing.domain.common.confidence.BearingWithConfidence;
import com.sap.sailing.domain.common.confidence.ConfidenceFactory; import com.sap.sailing.domain.common.confidence.ConfidenceFactory;
import com.sap.sailing.domain.common.confidence.impl.BearingWithConfidenceImpl; import com.sap.sailing.domain.common.confidence.impl.BearingWithConfidenceImpl;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.GPSFixTrack; import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.TrackedLeg; import com.sap.sailing.domain.tracking.TrackedLeg;
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.WindWithConfidence; import com.sap.sailing.domain.tracking.WindWithConfidence;
import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimePoint;
@@ -36,9 +36,10 @@ public class GPSFixMovingWithPolarContext implements MovingAveragePolarClusterKe
private final ClusterGroup<Speed> windSpeedClusterGroup; private final ClusterGroup<Speed> windSpeedClusterGroup;
private final Set<WindSource> windSourcesToExcludeForSpeed; private final Set<WindSource> windSourcesToExcludeForSpeed;
private final ClusterGroup<Bearing> angleClusterGroup; private final ClusterGroup<Bearing> angleClusterGroup;
private final BearingWithConfidence<Integer> trueWindAngle; private final BearingWithConfidence<Void> absTrueWindAngle;
private final WindWithConfidence<Pair<Position, TimePoint>> wind; private final WindWithConfidence<Pair<Position, TimePoint>> wind;
private final SpeedWithBearingWithConfidence<TimePoint> boatSpeed; private final SpeedWithBearingWithConfidence<TimePoint> boatSpeed;
private LegType legType;
public GPSFixMovingWithPolarContext(GPSFixMoving fix, TrackedRace race, Competitor competitor, public GPSFixMovingWithPolarContext(GPSFixMoving fix, TrackedRace race, Competitor competitor,
ClusterGroup<Speed> windSpeedClusterGroup, ClusterGroup<Bearing> angleClusterGroup) { ClusterGroup<Speed> windSpeedClusterGroup, ClusterGroup<Bearing> angleClusterGroup) {
@@ -48,7 +49,7 @@ public class GPSFixMovingWithPolarContext implements MovingAveragePolarClusterKe
this.windSpeedClusterGroup = windSpeedClusterGroup; this.windSpeedClusterGroup = windSpeedClusterGroup;
this.angleClusterGroup = angleClusterGroup; this.angleClusterGroup = angleClusterGroup;
this.windSourcesToExcludeForSpeed = collectWindSourcesToIgnoreForSpeed(); this.windSourcesToExcludeForSpeed = collectWindSourcesToIgnoreForSpeed();
this.trueWindAngle = computeTrueWindAngle(); this.absTrueWindAngle = computeTrueWindAngleAbsolute();
this.wind = race.getWindWithConfidence(fix.getPosition(), fix.getTimePoint(), windSourcesToExcludeForSpeed); this.wind = race.getWindWithConfidence(fix.getPosition(), fix.getTimePoint(), windSourcesToExcludeForSpeed);
this.boatSpeed = computeBoatSpeed(); this.boatSpeed = computeBoatSpeed();
} }
@@ -65,19 +66,25 @@ public class GPSFixMovingWithPolarContext implements MovingAveragePolarClusterKe
return competitor; return competitor;
} }
public BearingWithConfidence<Integer> getAngleToTheWind() { public BearingWithConfidence<Void> getAbsoluteAngleToTheWind() {
return trueWindAngle; return absTrueWindAngle;
} }
private BearingWithConfidence<Integer> computeTrueWindAngle() { private BearingWithConfidence<Void> computeTrueWindAngleAbsolute() {
SpeedWithBearing boatSpeed = race.getTrack(competitor).getEstimatedSpeed(fix.getTimePoint()); BearingWithConfidenceImpl<Void> result = null;
Bearing bearing = null;
try {
TrackedLegOfCompetitor currentLeg = race.getCurrentLeg(competitor, fix.getTimePoint());
if (currentLeg != null) {
Bearing realBearing = currentLeg.getBeatAngle(fix.getTimePoint());
bearing = new DegreeBearingImpl(Math.abs(realBearing.getDegrees()));
}
} catch (NoWindException e) {
bearing = null;
}
WindWithConfidence<Pair<Position, TimePoint>> wind = race.getWindWithConfidence(fix.getPosition(), fix.getTimePoint()); WindWithConfidence<Pair<Position, TimePoint>> wind = race.getWindWithConfidence(fix.getPosition(), fix.getTimePoint());
if (bearing != null && wind != null) {
BearingWithConfidenceImpl<Integer> result = null; result = new BearingWithConfidenceImpl<Void>(bearing, wind.getConfidence(), null);
if (wind != null && boatSpeed != null) {
Bearing bearing = boatSpeed.getBearing();
Bearing difference = wind.getObject().getFrom().getDifferenceTo(bearing);
result = new BearingWithConfidenceImpl<Integer>(difference, wind.getConfidence(), 0);
} }
return result; return result;
} }
@@ -133,29 +140,30 @@ public class GPSFixMovingWithPolarContext implements MovingAveragePolarClusterKe
return race.getRace().getBoatClass(); return race.getRace().getBoatClass();
} }
@Override
public Tack getTack() {
return getTack(getAngleToTheWind().getObject());
}
public static Tack getTack(Bearing angleDifferenceWindToBoat) {
return angleDifferenceWindToBoat.getDegrees() <= 0 ? Tack.PORT : Tack.STARBOARD;
}
@Override @Override
public LegType getLegType() { public LegType getLegType() {
TimePoint timePoint = fix.getTimePoint(); LegType result = null;
try { if (legType == null) {
final TrackedLeg currentLeg = race.getCurrentLeg(timePoint); TimePoint timePoint = fix.getTimePoint();
return currentLeg==null?null:currentLeg.getLegType(timePoint); try {
} catch (NoWindException e) { TrackedLegOfCompetitor currentLegOfCompetitor = race.getCurrentLeg(getCompetitor(), timePoint);
return null; if (currentLegOfCompetitor != null) {
final TrackedLeg currentLeg = currentLegOfCompetitor.getTrackedLeg();
legType = currentLeg == null ? null : currentLeg.getLegType(timePoint);
}
} catch (NoWindException e) {
legType = null;
}
result = legType;
} else {
result = legType;
} }
return result;
} }
@Override @Override
public Cluster<Bearing> getAngleCluster() { public Cluster<Bearing> getAngleCluster() {
return angleClusterGroup.getClusterFor(getAngleToTheWind().getObject()); return angleClusterGroup.getClusterFor(getAbsoluteAngleToTheWind().getObject());
} }
} }
@@ -1,13 +1,9 @@
package com.sap.sailing.polars.mining; package com.sap.sailing.polars.mining;
import com.sap.sailing.domain.common.LegType; import com.sap.sailing.domain.common.LegType;
import com.sap.sailing.domain.common.Tack;
import com.sap.sse.datamining.shared.annotations.Dimension; import com.sap.sse.datamining.shared.annotations.Dimension;
public interface TackAndLegTypePolarClusterKey extends BasePolarClusterKey { public interface LegTypePolarClusterKey extends BasePolarClusterKey {
@Dimension(messageKey = "tack")
Tack getTack();
@Dimension(messageKey = "legType") @Dimension(messageKey = "legType")
LegType getLegType(); LegType getLegType();
@@ -4,7 +4,7 @@ import com.sap.sailing.domain.common.Speed;
import com.sap.sse.datamining.data.Cluster; import com.sap.sse.datamining.data.Cluster;
import com.sap.sse.datamining.shared.annotations.Dimension; import com.sap.sse.datamining.shared.annotations.Dimension;
public interface MovingAveragePolarClusterKey extends TackAndLegTypePolarClusterKey { public interface MovingAveragePolarClusterKey extends LegTypePolarClusterKey {
/** /**
* *
@@ -6,14 +6,13 @@ import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence; import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence;
import com.sap.sailing.domain.common.LegType; import com.sap.sailing.domain.common.LegType;
import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sse.datamining.components.Processor; import com.sap.sse.datamining.components.Processor;
import com.sap.sse.datamining.impl.components.GroupedDataEntry; import com.sap.sse.datamining.impl.components.GroupedDataEntry;
public interface MovingAverageProcessor extends Processor<GroupedDataEntry<GPSFixMovingWithPolarContext>, Void>{ public interface MovingAverageProcessor extends Processor<GroupedDataEntry<GPSFixMovingWithPolarContext>, Void>{
SpeedWithBearingWithConfidence<Void> getAverageSpeedAndCourseOverGround(BoatClass boatClass, SpeedWithBearingWithConfidence<Void> getAverageSpeedAndCourseOverGround(BoatClass boatClass,
Speed windSpeed, LegType legType, Tack tack) throws NotEnoughDataHasBeenAddedException; Speed windSpeed, LegType legType) throws NotEnoughDataHasBeenAddedException;
Set<BoatClass> getAvailableBoatClasses(); Set<BoatClass> getAvailableBoatClasses();
} }
@@ -16,7 +16,6 @@ import com.sap.sailing.domain.common.LegType;
import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.common.confidence.BearingWithConfidence; import com.sap.sailing.domain.common.confidence.BearingWithConfidence;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
@@ -77,7 +76,7 @@ public class MovingAverageProcessorImpl implements MovingAverageProcessor {
averageAngleContainers.put(key, averageAngleContainer); averageAngleContainers.put(key, averageAngleContainer);
} }
} }
BearingWithConfidence<Integer> angleToTheWind = fix.getAngleToTheWind(); BearingWithConfidence<Void> angleToTheWind = fix.getAbsoluteAngleToTheWind();
WindWithConfidence<Pair<Position, TimePoint>> windSpeed = fix.getWind(); WindWithConfidence<Pair<Position, TimePoint>> windSpeed = fix.getWind();
SpeedWithBearingWithConfidence<TimePoint> boatSpeedWithConfidence = fix.getBoatSpeed(); SpeedWithBearingWithConfidence<TimePoint> boatSpeedWithConfidence = fix.getBoatSpeed();
availableBoatClasses.add(element.getDataEntry().getBoatClass()); availableBoatClasses.add(element.getDataEntry().getBoatClass());
@@ -177,21 +176,20 @@ public class MovingAverageProcessorImpl implements MovingAverageProcessor {
@Override @Override
public SpeedWithBearingWithConfidence<Void> getAverageSpeedAndCourseOverGround(BoatClass boatClass, public SpeedWithBearingWithConfidence<Void> getAverageSpeedAndCourseOverGround(BoatClass boatClass,
Speed windSpeed, LegType legType, Tack tack) throws NotEnoughDataHasBeenAddedException { Speed windSpeed, LegType legType) throws NotEnoughDataHasBeenAddedException {
SpeedWithBearingWithConfidence<Void> result = estimateSpeedAndAngle(boatClass, windSpeed, legType, tack); SpeedWithBearingWithConfidence<Void> result = estimateSpeedAndAngle(boatClass, windSpeed, legType);
return result; return result;
} }
private SpeedWithBearingWithConfidence<Void> estimateSpeedAndAngle(BoatClass boatClass, Speed windSpeed, LegType legType, Tack tack) throws NotEnoughDataHasBeenAddedException { private SpeedWithBearingWithConfidence<Void> estimateSpeedAndAngle(BoatClass boatClass, Speed windSpeed, LegType legType) throws NotEnoughDataHasBeenAddedException {
GroupKey key = createGroupKey(boatClass, windSpeed, legType, tack); GroupKey key = createGroupKey(boatClass, windSpeed, legType);
Bearing angleDiffTrueWindToBoat = estimateAngle(key); Bearing angleDiffTrueWindToBoat = estimateAngle(key);
SpeedWithConfidence<Void> estimatedSpeed = estimateBoatSpeed(key).getSpeedWithConfidence(); SpeedWithConfidence<Void> estimatedSpeed = estimateBoatSpeed(key).getSpeedWithConfidence();
SpeedWithBearing speedWithBearing = new KnotSpeedWithBearingImpl(estimatedSpeed.getObject().getKnots(), angleDiffTrueWindToBoat); SpeedWithBearing speedWithBearing = new KnotSpeedWithBearingImpl(estimatedSpeed.getObject().getKnots(), angleDiffTrueWindToBoat);
return new SpeedWithBearingWithConfidenceImpl<Void>(speedWithBearing, estimatedSpeed.getConfidence(), null); return new SpeedWithBearingWithConfidenceImpl<Void>(speedWithBearing, estimatedSpeed.getConfidence(), null);
} }
private GroupKey createGroupKey(final BoatClass boatClass, final Speed windSpeed, final LegType legType, private GroupKey createGroupKey(final BoatClass boatClass, final Speed windSpeed, final LegType legType) {
final Tack tack) {
MovingAveragePolarClusterKey key = new MovingAveragePolarClusterKey() { MovingAveragePolarClusterKey key = new MovingAveragePolarClusterKey() {
@Override @Override
@@ -204,11 +202,6 @@ public class MovingAverageProcessorImpl implements MovingAverageProcessor {
return speedClusterGroup.getClusterFor(windSpeed); return speedClusterGroup.getClusterFor(windSpeed);
} }
@Override
public Tack getTack() {
return tack;
}
@Override @Override
public LegType getLegType() { public LegType getLegType() {
return legType; return legType;
@@ -6,7 +6,6 @@ import java.util.Collection;
import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.LegType; import com.sap.sailing.domain.common.LegType;
import com.sap.sailing.domain.common.Tack;
import com.sap.sse.datamining.data.Cluster; import com.sap.sse.datamining.data.Cluster;
import com.sap.sse.datamining.factories.FunctionFactory; import com.sap.sse.datamining.factories.FunctionFactory;
import com.sap.sse.datamining.functions.Function; import com.sap.sse.datamining.functions.Function;
@@ -30,13 +29,9 @@ public class PolarDataDimensionCollectionFactory {
private static void addTackAndLegTypeDimensions(Collection<Function<?>> dimensions, FunctionFactory functionFactory) private static void addTackAndLegTypeDimensions(Collection<Function<?>> dimensions, FunctionFactory functionFactory)
throws NoSuchMethodException { throws NoSuchMethodException {
Function<Tack> tackFunction = functionFactory
.createMethodWrappingFunction(MovingAveragePolarClusterKey.class.getMethod("getTack",
new Class<?>[0]));
Function<LegType> legTypeFunction = functionFactory Function<LegType> legTypeFunction = functionFactory
.createMethodWrappingFunction(MovingAveragePolarClusterKey.class.getMethod("getLegType", .createMethodWrappingFunction(MovingAveragePolarClusterKey.class.getMethod("getLegType",
new Class<?>[0])); new Class<?>[0]));
dimensions.add(tackFunction);
dimensions.add(legTypeFunction); dimensions.add(legTypeFunction);
} }
@@ -38,8 +38,8 @@ import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl;
import com.sap.sailing.domain.common.impl.PolarSheetsDataImpl; import com.sap.sailing.domain.common.impl.PolarSheetsDataImpl;
import com.sap.sailing.domain.common.impl.PolarSheetsHistogramDataImpl; import com.sap.sailing.domain.common.impl.PolarSheetsHistogramDataImpl;
import com.sap.sailing.domain.common.impl.WindSpeedSteppingWithMaxDistance; import com.sap.sailing.domain.common.impl.WindSpeedSteppingWithMaxDistance;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.polars.impl.CubicEquation; import com.sap.sailing.polars.impl.CubicEquation;
import com.sap.sse.datamining.components.FilterCriterion; import com.sap.sse.datamining.components.FilterCriterion;
@@ -212,7 +212,7 @@ public class PolarDataMiner {
private ClusterGroup<Bearing> createAngleClusterGroup() { private ClusterGroup<Bearing> createAngleClusterGroup() {
return new BearingClusterGroup(-180, 180, 5); return new BearingClusterGroup(0, 180, 5);
} }
public void addFix(GPSFixMoving fix, Competitor competitor, TrackedRace trackedRace) { public void addFix(GPSFixMoving fix, Competitor competitor, TrackedRace trackedRace) {
@@ -326,8 +326,14 @@ public class PolarDataMiner {
SpeedWithConfidence<Void> speedWithConfidence; SpeedWithConfidence<Void> speedWithConfidence;
try { try {
int convertedAngle = convertAngleIfNecessary(angle); int convertedAngle = convertAngleIfNecessary(angle);
speedWithConfidence = speedRegressionPerAngleClusterProcessor.estimateBoatSpeed(boatClass, SpeedWithConfidence<Void> regressionResult = speedRegressionPerAngleClusterProcessor.estimateBoatSpeed(boatClass,
new KnotSpeedImpl(windSpeed), new DegreeBearingImpl(convertedAngle)); new KnotSpeedImpl(windSpeed), new DegreeBearingImpl(convertedAngle));
if (regressionResult.getConfidence() > 0.1) {
speedWithConfidence = regressionResult;
} else {
// Low confidence. So put in 0 speed for chart
speedWithConfidence = new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(0), regressionResult.getConfidence(), null);
}
} catch (NotEnoughDataHasBeenAddedException e) { } catch (NotEnoughDataHasBeenAddedException e) {
// No data so put in a 0 speed with 0 confidence // No data so put in a 0 speed with 0 confidence
speedWithConfidence = new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(0), 0, null); speedWithConfidence = new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(0), 0, null);
@@ -392,24 +398,24 @@ public class PolarDataMiner {
} }
public SpeedWithBearingWithConfidence<Void> getAverageSpeedAndCourseOverGround(BoatClass boatClass, public SpeedWithBearingWithConfidence<Void> getAverageSpeedAndCourseOverGround(BoatClass boatClass,
Speed windSpeed, LegType legType, Tack tack, boolean useRegressionForSpeed) throws NotEnoughDataHasBeenAddedException { Speed windSpeed, LegType legType, boolean useRegressionForSpeed) throws NotEnoughDataHasBeenAddedException {
SpeedWithBearingWithConfidence<Void> averageSpeedAndCourseOverGround = null; SpeedWithBearingWithConfidence<Void> averageSpeedAndCourseOverGround = null;
if (useRegressionForSpeed) { if (useRegressionForSpeed) {
averageSpeedAndCourseOverGround = cubicRegressionPerCourseProcessor.getAverageSpeedAndCourseOverGround(boatClass, windSpeed, legType, tack); averageSpeedAndCourseOverGround = cubicRegressionPerCourseProcessor.getAverageSpeedAndCourseOverGround(boatClass, windSpeed, legType);
} else { } else {
averageSpeedAndCourseOverGround = movingAverageProcessor.getAverageSpeedAndCourseOverGround(boatClass, windSpeed, legType, tack); averageSpeedAndCourseOverGround = movingAverageProcessor.getAverageSpeedAndCourseOverGround(boatClass, windSpeed, legType);
} }
return averageSpeedAndCourseOverGround; return averageSpeedAndCourseOverGround;
} }
public PolynomialFunction getSpeedRegressionFunction(BoatClass boatClass, LegType legType, Tack tack) public PolynomialFunction getSpeedRegressionFunction(BoatClass boatClass, LegType legType)
throws NotEnoughDataHasBeenAddedException { throws NotEnoughDataHasBeenAddedException {
return cubicRegressionPerCourseProcessor.getSpeedRegressionFunction(boatClass, legType, tack); return cubicRegressionPerCourseProcessor.getSpeedRegressionFunction(boatClass, legType);
} }
public PolynomialFunction getAngleRegressionFunction(BoatClass boatClass, LegType legType, Tack tack) public PolynomialFunction getAngleRegressionFunction(BoatClass boatClass, LegType legType)
throws NotEnoughDataHasBeenAddedException { throws NotEnoughDataHasBeenAddedException {
return cubicRegressionPerCourseProcessor.getAngleRegressionFunction(boatClass, legType, tack); return cubicRegressionPerCourseProcessor.getAngleRegressionFunction(boatClass, legType);
} }
public PolynomialFunction getSpeedRegressionFunction(BoatClass boatClass, double trueWindAngle) public PolynomialFunction getSpeedRegressionFunction(BoatClass boatClass, double trueWindAngle)
@@ -3,11 +3,13 @@ package com.sap.sailing.polars.mining;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
import java.util.NavigableSet; import java.util.NavigableSet;
import java.util.logging.Logger;
import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Course; import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence; import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence;
import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.LegType;
import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.confidence.BearingWithConfidence; import com.sap.sailing.domain.common.confidence.BearingWithConfidence;
import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.GPSFixMoving;
@@ -22,6 +24,8 @@ import com.sap.sse.datamining.components.FilterCriterion;
public class PolarFixFilterCriteria implements FilterCriterion<GPSFixMovingWithPolarContext> { public class PolarFixFilterCriteria implements FilterCriterion<GPSFixMovingWithPolarContext> {
private static final Logger logger = Logger.getLogger(PolarFixFilterCriteria.class.getName());
/** /**
* 0 if every competitor should be included. * 0 if every competitor should be included.
* 1 if only the leading competitor should be included and so on. * 1 if only the leading competitor should be included and so on.
@@ -57,11 +61,39 @@ public class PolarFixFilterCriteria implements FilterCriterion<GPSFixMovingWithP
isInLeadingCompetitors = isInLeadingCompetitors(element.getRace(), element.getCompetitor(), isInLeadingCompetitors = isInLeadingCompetitors(element.getRace(), element.getCompetitor(),
pctOfLeadingCompetitorsToInclude); pctOfLeadingCompetitorsToInclude);
} }
return (importantDataIsNotNull && afterStartTime && beforeFinishTime && noDirectionChange && isInLeadingCompetitors); boolean isValid = importantDataIsNotNull && afterStartTime && beforeFinishTime && noDirectionChange && isInLeadingCompetitors;
if (/*Remove boolean for logging of weird input into polar service*/Boolean.FALSE && isValid) {
checkForAbnormalFixesAndLog(element);
}
return isValid;
} }
private void checkForAbnormalFixesAndLog(GPSFixMovingWithPolarContext element) {
double twa = element.getAbsoluteAngleToTheWind().getObject().getDegrees();
if (element.getLegType() == LegType.DOWNWIND) {
if (Math.abs(twa) < 100) {
logger.warning(String.format(
"Very wide downwind fix input to polar data container. Race: %s, Competitor: %s, Timepoint: %s, "
+ "TWA: %s", element.getRace().getRace().getName(), element.getCompetitor().getName(),
element.getFix().getTimePoint(), twa));
}
} else if (element.getLegType() == LegType.UPWIND) {
if (Math.abs(twa) > 70) {
logger.warning(String.format(
"Very wide upwind fix input to polar data container. Race: %s, Competitor: %s, Timepoint: %s, "
+ "TWA: %s", element.getRace().getRace().getName(), element.getCompetitor().getName(),
element.getFix().getTimePoint(), twa));
} else if (Math.abs(twa) < 35) {
logger.warning(String.format(
"Very narrow upwind fix input to polar data container. Race: %s, Competitor: %s, Timepoint: %s, "
+ "TWA: %s", element.getRace().getRace().getName(), element.getCompetitor().getName(),
element.getFix().getTimePoint(), twa));
}
}
}
private boolean importantDataIsNotNull(GPSFixMovingWithPolarContext element) { private boolean importantDataIsNotNull(GPSFixMovingWithPolarContext element) {
BearingWithConfidence<Integer> angleToTheWind = element.getAngleToTheWind(); BearingWithConfidence<Void> angleToTheWind = element.getAbsoluteAngleToTheWind();
WindWithConfidence<Pair<Position, TimePoint>> windSpeed = element.getWind(); WindWithConfidence<Pair<Position, TimePoint>> windSpeed = element.getWind();
SpeedWithBearingWithConfidence<TimePoint> boatSpeedWithConfidence = element.getBoatSpeed(); SpeedWithBearingWithConfidence<TimePoint> boatSpeedWithConfidence = element.getBoatSpeed();
boolean result = false; boolean result = false;
@@ -26,9 +26,11 @@ import com.sap.sse.datamining.shared.GroupKey;
public class SpeedRegressionPerAngleClusterProcessor implements Processor<GroupedDataEntry<GPSFixMovingWithPolarContext>, Void>{ public class SpeedRegressionPerAngleClusterProcessor implements Processor<GroupedDataEntry<GPSFixMovingWithPolarContext>, Void>{
private static final Logger logger = Logger.getLogger(CubicRegressionPerCourseProcessor.class.getName()); private static final Logger logger = Logger.getLogger(CubicRegressionPerCourseProcessor.class.getName());
private final Map<GroupKey, IncrementalLeastSquares> regressions = new HashMap<>(); private final Map<GroupKey, IncrementalLeastSquares> regressions = new HashMap<>();
private final Map<BoatClass, Long> fixCountPerBoatClass = new HashMap<>();
private final ClusterGroup<Bearing> angleClusterGroup; private final ClusterGroup<Bearing> angleClusterGroup;
@@ -46,6 +48,13 @@ private static final Logger logger = Logger.getLogger(CubicRegressionPerCoursePr
public void processElement(GroupedDataEntry<GPSFixMovingWithPolarContext> element) { public void processElement(GroupedDataEntry<GPSFixMovingWithPolarContext> element) {
GroupKey key = element.getKey(); GroupKey key = element.getKey();
IncrementalLeastSquares regression; IncrementalLeastSquares regression;
BoatClass boatClass = element.getDataEntry().getBoatClass();
synchronized (fixCountPerBoatClass) {
if (!fixCountPerBoatClass.containsKey(boatClass)) {
fixCountPerBoatClass.put(boatClass, 0L);
}
fixCountPerBoatClass.put(boatClass, fixCountPerBoatClass.get(boatClass) + 1);
}
synchronized (regressions) { synchronized (regressions) {
regression = regressions.get(key); regression = regressions.get(key);
if (regression == null) { if (regression == null) {
@@ -66,18 +75,29 @@ private static final Logger logger = Logger.getLogger(CubicRegressionPerCoursePr
public SpeedWithConfidence<Void> estimateBoatSpeed(BoatClass boatClass, Speed windSpeed, Bearing trueWindAngle) throws NotEnoughDataHasBeenAddedException { public SpeedWithConfidence<Void> estimateBoatSpeed(BoatClass boatClass, Speed windSpeed, Bearing trueWindAngle) throws NotEnoughDataHasBeenAddedException {
double speedSum = 0; double speedSum = 0;
double numberOfSpeeds = 0; double numberOfSpeeds = 0;
for (int i = -5; i <= 5; i++) { long fixCount = 0;
GroupKey key = createGroupKey(boatClass, new DegreeBearingImpl(trueWindAngle.getDegrees() + i)); for (int i = -2; i <= 2; i++) {
GroupKey key = createGroupKey(boatClass, new DegreeBearingImpl(Math.abs(trueWindAngle.getDegrees()) + i));
if (regressions.containsKey(key)) { if (regressions.containsKey(key)) {
speedSum += regressions.get(key).getOrCreatePolynomialFunction().value(windSpeed.getKnots()); IncrementalLeastSquares incrementalLeastSquares = regressions.get(key);
numberOfSpeeds++; fixCount = fixCount + incrementalLeastSquares.getNumberOfAddedPoints();
if (fixCount > 10) {
speedSum += incrementalLeastSquares.getOrCreatePolynomialFunction().value(windSpeed.getKnots());
numberOfSpeeds++;
}
} }
} }
if (numberOfSpeeds < 1) { fixCount = (long) (fixCount / numberOfSpeeds);
long fixCountOverall = 0;
if (numberOfSpeeds < 2 || fixCount < 10) {
throw new NotEnoughDataHasBeenAddedException("Not enough data has been added to Per Course Regressions"); throw new NotEnoughDataHasBeenAddedException("Not enough data has been added to Per Course Regressions");
} else {
synchronized (fixCountPerBoatClass) {
fixCountOverall = fixCountPerBoatClass.get(boatClass);
}
} }
Speed speed = new KnotSpeedImpl(speedSum / numberOfSpeeds); Speed speed = new KnotSpeedImpl(speedSum / numberOfSpeeds);
return new SpeedWithConfidenceImpl<Void>(speed, /*FIXME*/ 0.5, null); return new SpeedWithConfidenceImpl<Void>(speed, Math.min(1, 5.0 * ((double) fixCount / fixCountOverall)), null);
} }
private GroupKey createGroupKey(final BoatClass boatClass, final Bearing angle) { private GroupKey createGroupKey(final BoatClass boatClass, final Bearing angle) {
@@ -85,16 +85,15 @@ public class PolarResource extends AbstractSailingServerResource {
StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder = new StringBuilder();
for (BoatClass boatClass : boatClasses) { for (BoatClass boatClass : boatClasses) {
for (LegType legType : new LegType[] { LegType.UPWIND, LegType.DOWNWIND }) { for (LegType legType : new LegType[] { LegType.UPWIND, LegType.DOWNWIND }) {
for (Tack tack : new Tack[] { Tack.PORT, Tack.STARBOARD }) { try {
try { PolynomialFunction speedFunction = polarDataService.getSpeedRegressionFunction(boatClass, legType);
PolynomialFunction speedFunction = polarDataService.getSpeedRegressionFunction(boatClass, legType, tack); stringBuilder.append("Speed: " + boatClass + " " + legType + ": " + speedFunction.toString() + "\n");
stringBuilder.append("Speed: " + boatClass + " " + legType + " " + tack + ": " + speedFunction.toString() + "\n"); PolynomialFunction angleFunction = polarDataService.getAngleRegressionFunction(boatClass, legType);
PolynomialFunction angleFunction = polarDataService.getAngleRegressionFunction(boatClass, legType, tack); stringBuilder.append("Angle: " + boatClass + " " + legType + ": " + angleFunction.toString() + "\n");
stringBuilder.append("Angle: " + boatClass + " " + legType + " " + tack + ": " + angleFunction.toString() + "\n"); } catch (NotEnoughDataHasBeenAddedException e) {
} catch (NotEnoughDataHasBeenAddedException e) { stringBuilder.append("No data for " + boatClass + " " + legType + "\n");
stringBuilder.append("No data for " + boatClass + " " + legType + " " + tack + "\n"); }
}
}
} }
for (double trueWindAngle = -177.5; trueWindAngle < 180; trueWindAngle = trueWindAngle + 5) { for (double trueWindAngle = -177.5; trueWindAngle < 180; trueWindAngle = trueWindAngle + 5) {
try { try {