mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-13 22:15:26 +00:00
Bugfixed and improved maneuver graph
This commit is contained in:
1 parent
2685ef282e
commit
14ec6f00ca
31 files changed
+604
-197
No files matched your search
+1
-1
@@ -157,7 +157,7 @@ public class SpeedRegressionPerAngleClusterProcessor implements
|
||||
}
|
||||
}
|
||||
}
|
||||
long avgFixesCount = allFixesCount / numberOfWindSpeeds;
|
||||
long avgFixesCount = numberOfWindSpeeds == 0 ? 0 : allFixesCount / numberOfWindSpeeds;
|
||||
long fixCountOverall = 0;
|
||||
if (numberOfWindSpeeds < 2 || allFixesCount < 10) {
|
||||
throw new NotEnoughDataHasBeenAddedException("Not enough data has been added to Per Course Regressions");
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="resources"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
@@ -0,0 +1 @@
|
||||
bin/
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>com.sap.sailing.windestimation.test</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.ManifestBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.SchemaBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.pde.PluginNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -0,0 +1,28 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Wind estimation test
|
||||
Bundle-SymbolicName: com.sap.sailing.windestimation.test
|
||||
Fragment-Host: com.sap.sailing.windestimation
|
||||
Bundle-Version: 1.0.0.qualifier
|
||||
Bundle-Vendor: SAP
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Import-Package: org.osgi.framework;version="1.5.0"
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: org.eclipse.osgi;bundle-version="3.10.1",
|
||||
org.json.simple;bundle-version="1.1.0",
|
||||
org.mongodb.mongo-java-driver;bundle-version="2.13.0",
|
||||
org.apache.httpcomponents.httpclient;bundle-version="[4.2.5,4.3.0)",
|
||||
org.apache.httpcomponents.httpcore;bundle-version="[4.0.0,4.3.0)",
|
||||
com.sap.sse;bundle-version="1.0.0",
|
||||
com.sap.sse.common;bundle-version="1.0.0",
|
||||
com.sap.sailing.domain;bundle-version="1.0.0",
|
||||
com.sap.sailing.domain.shared.android,
|
||||
com.sap.sailing.domain.common;bundle-version="1.0.0",
|
||||
com.sap.sailing.server.gateway.serialization;bundle-version="1.0.0",
|
||||
com.sap.sailing.server.gateway.serialization.shared.android;bundle-version="1.0.0",
|
||||
com.sap.sailing.polars,
|
||||
org.apache.commons.io,
|
||||
com.sap.sse.replication,
|
||||
org.junit;bundle-version="4.8.2",
|
||||
slf4j.api
|
||||
Automatic-Module-Name: com.sap.sailing.velum.resultimport.test
|
||||
@@ -0,0 +1,5 @@
|
||||
source.. = src/,\
|
||||
resources/
|
||||
output.. = bin/
|
||||
bin.includes = META-INF/,\
|
||||
.
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>root</artifactId>
|
||||
<groupId>com.sap.sailing</groupId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>com.sap.sailing.windestimation.test</artifactId>
|
||||
<packaging>eclipse-test-plugin</packaging>
|
||||
</project>
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
package com.sap.sailing.windestimation.maneuvergraph.maneuvernode;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class WindRangeCalculationTest {
|
||||
|
||||
private static final double TOLERANCE = 0.0001;
|
||||
|
||||
@Test
|
||||
public void testWithinRangeCalculation() {
|
||||
WindRangeForManeuverNode range = new WindRangeForManeuverNode(20, 10);
|
||||
assertEquals(range.isWindCourseWithinRange(20), true);
|
||||
assertEquals(range.isWindCourseWithinRange(25), true);
|
||||
assertEquals(range.isWindCourseWithinRange(30), true);
|
||||
assertEquals(range.isWindCourseWithinRange(19), false);
|
||||
assertEquals(range.isWindCourseWithinRange(200), false);
|
||||
assertEquals(range.isWindCourseWithinRange(31), false);
|
||||
|
||||
range = new WindRangeForManeuverNode(350, 20);
|
||||
assertEquals(range.isWindCourseWithinRange(350), true);
|
||||
assertEquals(range.isWindCourseWithinRange(0), true);
|
||||
assertEquals(range.isWindCourseWithinRange(10), true);
|
||||
assertEquals(range.isWindCourseWithinRange(349), false);
|
||||
assertEquals(range.isWindCourseWithinRange(180), false);
|
||||
assertEquals(range.isWindCourseWithinRange(11), false);
|
||||
|
||||
range = new WindRangeForManeuverNode(340, 20);
|
||||
assertEquals(range.isWindCourseWithinRange(0), true);
|
||||
assertEquals(range.isWindCourseWithinRange(1), false);
|
||||
assertEquals(range.isWindCourseWithinRange(339), false);
|
||||
assertEquals(range.isWindCourseWithinRange(180), false);
|
||||
|
||||
range = new WindRangeForManeuverNode(340, 350);
|
||||
assertEquals(range.isWindCourseWithinRange(341), true);
|
||||
assertEquals(range.isWindCourseWithinRange(330), true);
|
||||
assertEquals(range.isWindCourseWithinRange(331), false);
|
||||
assertEquals(range.isWindCourseWithinRange(339), false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWindRangeIntersectionCalculation() {
|
||||
WindRangeForManeuverNode range = new WindRangeForManeuverNode(20, 15);
|
||||
WindRangeForManeuverNode other = new WindRangeForManeuverNode(10, 20);
|
||||
IntersectedWindRange intersect = range.intersect(other);
|
||||
assertEquals(intersect.getFromPortside(), 20, TOLERANCE);
|
||||
assertEquals(intersect.getAngleTowardStarboard(), 10, TOLERANCE);
|
||||
assertEquals(intersect.getViolationRange(), 0, TOLERANCE);
|
||||
IntersectedWindRange intersect2 = other.intersect(range);
|
||||
assertEquals(intersect, intersect2);
|
||||
|
||||
range = new WindRangeForManeuverNode(340, 15);
|
||||
other = new WindRangeForManeuverNode(350, 20);
|
||||
intersect = range.intersect(other);
|
||||
assertEquals(intersect.getFromPortside(), 350, TOLERANCE);
|
||||
assertEquals(intersect.getAngleTowardStarboard(), 5, TOLERANCE);
|
||||
assertEquals(intersect.getViolationRange(), 0, TOLERANCE);
|
||||
intersect2 = other.intersect(range);
|
||||
assertEquals(intersect, intersect2);
|
||||
|
||||
range = new WindRangeForManeuverNode(340, 30);
|
||||
other = new WindRangeForManeuverNode(10, 20);
|
||||
intersect = range.intersect(other);
|
||||
assertEquals(intersect.getFromPortside(), 10, TOLERANCE);
|
||||
assertEquals(intersect.getAngleTowardStarboard(), 0, TOLERANCE);
|
||||
assertEquals(intersect.getViolationRange(), 0, TOLERANCE);
|
||||
intersect2 = other.intersect(range);
|
||||
assertEquals(intersect, intersect2);
|
||||
|
||||
range = new WindRangeForManeuverNode(340, 30);
|
||||
other = new WindRangeForManeuverNode(330, 20);
|
||||
intersect = range.intersect(other);
|
||||
assertEquals(intersect.getFromPortside(), 340, TOLERANCE);
|
||||
assertEquals(intersect.getAngleTowardStarboard(), 10, TOLERANCE);
|
||||
assertEquals(intersect.getViolationRange(), 0, TOLERANCE);
|
||||
intersect2 = other.intersect(range);
|
||||
assertEquals(intersect, intersect2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWindRangeIntersectionCalculationWithViolations() {
|
||||
WindRangeForManeuverNode range = new WindRangeForManeuverNode(20, 15);
|
||||
WindRangeForManeuverNode other = new WindRangeForManeuverNode(10, 4);
|
||||
IntersectedWindRange intersect = range.intersect(other);
|
||||
assertEquals(intersect.getFromPortside(), 10, TOLERANCE);
|
||||
assertEquals(intersect.getAngleTowardStarboard(), 4, TOLERANCE);
|
||||
assertEquals(intersect.getViolationRange(), 6, TOLERANCE);
|
||||
IntersectedWindRange intersect2 = other.intersect(range);
|
||||
assertEquals(new IntersectedWindRange(range.getFromPortside(), range.getAngleTowardStarboard(),
|
||||
intersect.getViolationRange()), intersect2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWindRangeInversion() {
|
||||
WindRangeForManeuverNode range = new WindRangeForManeuverNode(10, 15);
|
||||
WindRangeForManeuverNode invert = range.invert();
|
||||
assertEquals(invert.getFromPortside(), 25, TOLERANCE);
|
||||
assertEquals(invert.getAngleTowardStarboard(), 345, TOLERANCE);
|
||||
assertEquals(invert.invert(), range);
|
||||
}
|
||||
|
||||
}
|
||||
+2
-1
@@ -32,7 +32,8 @@ public class ManeuverTypeGraphBasedWindEstimatorImpl
|
||||
List<CompetitorTrackWithEstimationData<ManeuverForEstimation>> transformedCompetitorTracks = maneuverForEstimationTransformer
|
||||
.transform(filteredCompetitorTracks);
|
||||
ManeuverTypeSequenceGraph maneuverGraph = new ManeuverTypeSequenceGraph(transformedCompetitorTracks,
|
||||
new ManeuverClassifiersCache(60000, false, true, getPolarService()), new ManeuverNodeBestPathsCalculator());
|
||||
new ManeuverClassifiersCache(60000, false, true, getPolarService()),
|
||||
new ManeuverNodeBestPathsCalculator());
|
||||
return maneuverGraph.estimateWindTrack();
|
||||
}
|
||||
|
||||
|
||||
+27
-14
@@ -112,6 +112,7 @@ public abstract class AbstractPersistenceManager<T> implements PersistenceManage
|
||||
private final long numberOfElements;
|
||||
private long currentElementNumber = 0;
|
||||
private int numberOfCharsDuringLastStatusLog = 0;
|
||||
private long limit = Long.MAX_VALUE;
|
||||
|
||||
public PersistedElementsIteratorImpl() {
|
||||
numberOfElements = countElements();
|
||||
@@ -133,21 +134,27 @@ public abstract class AbstractPersistenceManager<T> implements PersistenceManage
|
||||
|
||||
private void prepareNext() {
|
||||
long nextElementNumber = currentElementNumber + 1;
|
||||
if (nextElementNumber % (numberOfElements / 100) == 0) {
|
||||
LoggingUtil.delete(numberOfCharsDuringLastStatusLog);
|
||||
numberOfCharsDuringLastStatusLog = LoggingUtil
|
||||
.logInfo("Loading element " + nextElementNumber + "/" + numberOfElements + " ("
|
||||
+ (nextElementNumber * 100 / numberOfElements) + " %) from " + getCollectionName());
|
||||
}
|
||||
try {
|
||||
Pair<String, T> nextElementWithDbId = getNextElement(lastDbId);
|
||||
if (this.nextElement != null) {
|
||||
this.lastDbId = nextElementWithDbId.getA();
|
||||
this.nextElement = nextElementWithDbId.getB();
|
||||
this.currentElementNumber = nextElementNumber;
|
||||
if (nextElementNumber <= limit) {
|
||||
if (nextElementNumber % (numberOfElements / 100) == 0) {
|
||||
LoggingUtil.delete(numberOfCharsDuringLastStatusLog);
|
||||
numberOfCharsDuringLastStatusLog = LoggingUtil
|
||||
.logInfo("Loading element " + nextElementNumber + "/" + numberOfElements + " ("
|
||||
+ (nextElementNumber * 100 / numberOfElements) + " %) from " + getCollectionName());
|
||||
}
|
||||
} catch (JsonDeserializationException | ParseException e) {
|
||||
throw new RuntimeException(e);
|
||||
try {
|
||||
Pair<String, T> nextElementWithDbId = getNextElement(lastDbId);
|
||||
if (nextElementWithDbId != null) {
|
||||
this.lastDbId = nextElementWithDbId.getA();
|
||||
this.nextElement = nextElementWithDbId.getB();
|
||||
this.currentElementNumber = nextElementNumber;
|
||||
} else {
|
||||
this.nextElement = null;
|
||||
}
|
||||
} catch (JsonDeserializationException | ParseException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
this.nextElement = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,6 +162,12 @@ public abstract class AbstractPersistenceManager<T> implements PersistenceManage
|
||||
return numberOfElements;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PersistedElementsIterator<T> limit(long limit) {
|
||||
this.limit = limit;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -32,7 +32,7 @@ public abstract class AbstractRaceWithEstimationDataPersistenceManager<T> extend
|
||||
|
||||
@Override
|
||||
public void dropDb() {
|
||||
getDb().dropDatabase();
|
||||
getDb().getCollection(getCollectionName()).drop();
|
||||
}
|
||||
|
||||
public abstract CompetitorTrackWithEstimationDataJsonDeserializer<T> getNewCompetitorTrackWithEstimationDataJsonDeserializer();
|
||||
|
||||
+2
@@ -5,5 +5,7 @@ import java.util.Iterator;
|
||||
public interface PersistedElementsIterator<T> extends Iterator<T> {
|
||||
|
||||
long getNumberOfElements();
|
||||
|
||||
PersistedElementsIterator<T> limit(long limit);
|
||||
|
||||
}
|
||||
-6
@@ -38,10 +38,4 @@ public class RaceWithCompleteManeuverCurvePersistenceManager
|
||||
completeManeuverCurveDeserializer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropDb() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+16
-1
@@ -24,6 +24,7 @@ import com.sap.sailing.domain.tracking.Maneuver;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.windestimation.data.CompetitorTrackWithEstimationData;
|
||||
import com.sap.sailing.windestimation.data.ManeuverCategory;
|
||||
import com.sap.sailing.windestimation.data.ManeuverForEstimation;
|
||||
import com.sap.sse.common.Bearing;
|
||||
import com.sap.sse.common.Distance;
|
||||
@@ -150,7 +151,8 @@ public class EstimationDataUtil {
|
||||
List<ManeuverForEstimation> usefulManeuversSortedByTimePoint = new ArrayList<>();
|
||||
for (CompetitorTrackWithEstimationData<ManeuverForEstimation> competitorTrack : competitorTracks) {
|
||||
for (ManeuverForEstimation maneuver : competitorTrack.getElements()) {
|
||||
if (maneuver.isClean() || maneuver.isCleanBefore() || maneuver.isCleanAfter()) {
|
||||
if (maneuver.isClean() && maneuver.getManeuverCategory() == ManeuverCategory.REGULAR
|
||||
|| maneuver.getManeuverCategory() == ManeuverCategory.MARK_PASSING) {
|
||||
usefulManeuversSortedByTimePoint.add(maneuver);
|
||||
}
|
||||
}
|
||||
@@ -160,4 +162,17 @@ public class EstimationDataUtil {
|
||||
return usefulManeuversSortedByTimePoint;
|
||||
}
|
||||
|
||||
public static List<ManeuverForEstimation> getManeuversSortedByTimePoint(
|
||||
List<CompetitorTrackWithEstimationData<ManeuverForEstimation>> competitorTracks) {
|
||||
List<ManeuverForEstimation> maneuversSortedByTimePoint = new ArrayList<>();
|
||||
for (CompetitorTrackWithEstimationData<ManeuverForEstimation> competitorTrack : competitorTracks) {
|
||||
for (ManeuverForEstimation maneuver : competitorTrack.getElements()) {
|
||||
maneuversSortedByTimePoint.add(maneuver);
|
||||
}
|
||||
}
|
||||
Collections.sort(maneuversSortedByTimePoint,
|
||||
(o1, o2) -> o1.getManeuverTimePoint().compareTo(o2.getManeuverTimePoint()));
|
||||
return maneuversSortedByTimePoint;
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -13,14 +13,14 @@ public class ManeuverSequenceGraphBasedWindEstimatorEvaluationRunner {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
WindEstimatorEvaluator<CompleteManeuverCurveWithEstimationData> evaluator = new WindEstimationEvaluatorImpl<>(
|
||||
15, 2, 0.8);
|
||||
20, 2, 0.8);
|
||||
LoggingUtil.logInfo("Connecting to MongoDB");
|
||||
RaceWithCompleteManeuverCurvePersistenceManager persistenceManager = new RaceWithCompleteManeuverCurvePersistenceManager();
|
||||
LoggingUtil.logInfo("Loading polar data");
|
||||
PolarDataService polarService = PolarDataServiceAccessUtil.getPersistedPolarService();
|
||||
LoggingUtil.logInfo("Wind estimator evaluation started...");
|
||||
PersistedElementsIterator<RaceWithEstimationData<CompleteManeuverCurveWithEstimationData>> racesIterator = persistenceManager
|
||||
.getIterator();
|
||||
.getIterator().limit(20);
|
||||
|
||||
WindEstimatorEvaluationResult evaluationResult = evaluator.evaluateWindEstimator(
|
||||
new ManeuverSequenceGraphBasedWindEstimatorFactory(polarService),
|
||||
|
||||
+1
-1
@@ -88,7 +88,7 @@ public class WindEstimationEvaluatorImpl<T> implements WindEstimatorEvaluator<T>
|
||||
}
|
||||
LoggingUtil.logInfo("Evaluating on " + raceWithEstimationData.getRegattaName() + " Race "
|
||||
+ raceWithEstimationData.getRaceName() + " succeeded");
|
||||
result.printEvaluationStatistics(true);
|
||||
result.printEvaluationStatistics(false);
|
||||
return result.getAvgAsSingleResult(minAccuracyPerRaceForCorrectEstimation - 0.00001);
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -238,11 +238,11 @@ public class WindEstimatorEvaluationResult {
|
||||
+ (numberOfCorrectWindDirectionEstimations + numberOfIncorrectWindDirectionEstimations) + " correct)");
|
||||
System.out.println(" Avg. wind course error : "
|
||||
+ formatDegrees(getAvgAbsWindCourseErrorInDegreesOfCorrectWindDirectionEstimations()));
|
||||
System.out.println(" Avg. wind course error of incorrect estimations : "
|
||||
+ formatDegrees(getAvgAbsWindCourseErrorInDegreesOfIncorrectWindDirectionEstimations()));
|
||||
System.out.println(" Avg. wind course error of all estimations : "
|
||||
+ formatDegrees(getAvgAbsWindCourseErrorInDegreesOfCorrectAndIncorrectWindDirectionEstimations()));
|
||||
if (detailed) {
|
||||
System.out.println(" Avg. wind course error of incorrect estimations : "
|
||||
+ formatDegrees(getAvgAbsWindCourseErrorInDegreesOfIncorrectWindDirectionEstimations()));
|
||||
System.out.println(" Avg. wind course error of all estimations : "
|
||||
+ formatDegrees(getAvgAbsWindCourseErrorInDegreesOfCorrectAndIncorrectWindDirectionEstimations()));
|
||||
System.out.println();
|
||||
System.out.println("### Wind speed ###");
|
||||
System.out.println(" Accuracy: " + formatPercentage(getAccuracyOfWindSpeedEstimation()) + " ("
|
||||
|
||||
+23
-22
@@ -3,7 +3,6 @@ package com.sap.sailing.windestimation.maneuverclassifier;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.json.simple.parser.ParseException;
|
||||
@@ -36,14 +35,11 @@ public class ManeuverClassifierTrainer {
|
||||
}
|
||||
|
||||
public void trainClassifier(TrainableSingleManeuverOfflineClassifier classifier) throws Exception {
|
||||
LoggingUtil.logInfo("Connecting to MongoDB");
|
||||
persistenceManager.createIfNotExistsCollectionWithTransformedManeuvers();
|
||||
LoggingUtil.logInfo("Querying train dataset...");
|
||||
Long fixesCountForBoatClass = classifier.getBoatClass() == null ? null
|
||||
: polarService.getFixCountPerBoatClass().get(classifier.getBoatClass());
|
||||
List<ManeuverForEstimation> maneuvers = getSuitableManeuvers(classifier.getManeuverFeatures(),
|
||||
classifier.getBoatClass());
|
||||
LoggingUtil.logInfo("Queried " + maneuvers.size() + " maneuvers");
|
||||
LoggingUtil.logInfo("Using " + maneuvers.size() + " maneuvers");
|
||||
if (maneuvers.size() < MIN_MANEUVERS_COUNT) {
|
||||
LoggingUtil.logInfo("Not enough maneuver data for training. Training aborted!");
|
||||
} else {
|
||||
@@ -74,13 +70,20 @@ public class ManeuverClassifierTrainer {
|
||||
|
||||
private List<ManeuverForEstimation> getSuitableManeuvers(ManeuverFeatures maneuverFeatures, BoatClass boatClass)
|
||||
throws JsonDeserializationException, ParseException {
|
||||
if (boatClass == null && allManeuvers != null) {
|
||||
return allManeuvers;
|
||||
}
|
||||
Pair<String, ManeuverFeatures> key = new Pair<>(boatClass == null ? null : boatClass.getName(),
|
||||
maneuverFeatures);
|
||||
List<ManeuverForEstimation> maneuvers = maneuversPerBoatClass.get(key);
|
||||
if (maneuvers == null) {
|
||||
if (allManeuvers == null) {
|
||||
LoggingUtil.logInfo("Connecting to MongoDB");
|
||||
persistenceManager.createIfNotExistsCollectionWithTransformedManeuvers();
|
||||
LoggingUtil.logInfo("Querying dataset...");
|
||||
allManeuvers = persistenceManager.getAllElements();
|
||||
}
|
||||
LoggingUtil.logInfo("Filtering maneuvers for boat class: " + boatClass.getName());
|
||||
maneuvers = allManeuvers.stream()
|
||||
.filter(maneuver -> MLUtil.isManeuverContainsAllFeatures(maneuver, maneuverFeatures, boatClass))
|
||||
.collect(Collectors.toList());
|
||||
@@ -91,7 +94,8 @@ public class ManeuverClassifierTrainer {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
PolarDataService polarService = PolarDataServiceAccessUtil.getPersistedPolarService();
|
||||
Set<BoatClass> allBoatClasses = polarService.getAllBoatClassesWithPolarSheetsAvailable();
|
||||
ManeuverClassifierTrainer classifierTrainer = new ManeuverClassifierTrainer(
|
||||
new RegularManeuversForEstimationPersistenceManager(), polarService);
|
||||
for (ManeuverFeatures maneuverFeatures : ManeuverFeatures.values()) {
|
||||
LoggingUtil.logInfo(
|
||||
"### Training classifier for all boat classes with maneuver features: " + maneuverFeatures);
|
||||
@@ -99,26 +103,23 @@ public class ManeuverClassifierTrainer {
|
||||
.getAllTrainableClassifierInstances(maneuverFeatures, null);
|
||||
for (TrainableSingleManeuverOfflineClassifier classifier : allTrainableClassifierInstances) {
|
||||
LoggingUtil.logInfo("## Classifier: " + classifier.getClass().getName());
|
||||
ManeuverClassifierTrainer classifierTrainer = new ManeuverClassifierTrainer(
|
||||
new RegularManeuversForEstimationPersistenceManager(), polarService);
|
||||
classifierTrainer.trainClassifier(classifier);
|
||||
}
|
||||
}
|
||||
// Set<BoatClass> allBoatClasses = polarService.getAllBoatClassesWithPolarSheetsAvailable();
|
||||
|
||||
for (ManeuverFeatures maneuverFeatures : ManeuverFeatures.values()) {
|
||||
for (BoatClass boatClass : allBoatClasses) {
|
||||
LoggingUtil.logInfo("### Training classifier for boat class " + boatClass + " with maneuver features: "
|
||||
+ maneuverFeatures);
|
||||
List<TrainableSingleManeuverOfflineClassifier> allTrainableClassifierInstances = ManeuverClassifiersFactory
|
||||
.getAllTrainableClassifierInstances(maneuverFeatures, boatClass);
|
||||
for (TrainableSingleManeuverOfflineClassifier classifier : allTrainableClassifierInstances) {
|
||||
LoggingUtil.logInfo("## Classifier: " + classifier.getClass().getName());
|
||||
ManeuverClassifierTrainer classifierTrainer = new ManeuverClassifierTrainer(
|
||||
new RegularManeuversForEstimationPersistenceManager(), polarService);
|
||||
classifierTrainer.trainClassifier(classifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
// for (ManeuverFeatures maneuverFeatures : ManeuverFeatures.values()) {
|
||||
// for (BoatClass boatClass : allBoatClasses) {
|
||||
// LoggingUtil.logInfo("### Training classifier for boat class " + boatClass + " with maneuver features: "
|
||||
// + maneuverFeatures);
|
||||
// List<TrainableSingleManeuverOfflineClassifier> allTrainableClassifierInstances = ManeuverClassifiersFactory
|
||||
// .getAllTrainableClassifierInstances(maneuverFeatures, boatClass);
|
||||
// for (TrainableSingleManeuverOfflineClassifier classifier : allTrainableClassifierInstances) {
|
||||
// LoggingUtil.logInfo("## Classifier: " + classifier.getClass().getName());
|
||||
// classifierTrainer.trainClassifier(classifier);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
+10
-13
@@ -7,11 +7,7 @@ import java.util.stream.Collectors;
|
||||
import com.sap.sailing.domain.base.BoatClass;
|
||||
import com.sap.sailing.windestimation.maneuverclassifier.impl.ManeuverFeatures;
|
||||
import com.sap.sailing.windestimation.maneuverclassifier.impl.smile.GradientBoostingManeuverClassifier;
|
||||
import com.sap.sailing.windestimation.maneuverclassifier.impl.smile.LDAManeuverClassifier;
|
||||
import com.sap.sailing.windestimation.maneuverclassifier.impl.smile.LogisticRegressionManeuverClassifier;
|
||||
import com.sap.sailing.windestimation.maneuverclassifier.impl.smile.NaiveBayesManeuverClassifier;
|
||||
import com.sap.sailing.windestimation.maneuverclassifier.impl.smile.NeuralNetworkManeuverClassifier;
|
||||
import com.sap.sailing.windestimation.maneuverclassifier.impl.smile.QDAManeuverClassifier;
|
||||
import com.sap.sailing.windestimation.maneuverclassifier.impl.smile.RandomForestManeuverClassifier;
|
||||
import com.sap.sailing.windestimation.maneuverclassifier.impl.smile.SVMManeuverClassifier;
|
||||
|
||||
@@ -25,22 +21,23 @@ public class ManeuverClassifiersFactory {
|
||||
|
||||
public static TrainableSingleManeuverOfflineClassifier getNewClassifierInstance(ManeuverFeatures maneuverFeatures,
|
||||
BoatClass boatClass) {
|
||||
if (boatClass == null) {
|
||||
return new RandomForestManeuverClassifier(maneuverFeatures, null, supportedManeuverTypes);
|
||||
} else {
|
||||
return new SVMManeuverClassifier(maneuverFeatures, boatClass, supportedManeuverTypes);
|
||||
}
|
||||
return new NeuralNetworkManeuverClassifier(maneuverFeatures, null, supportedManeuverTypes);
|
||||
// if (boatClass == null) {
|
||||
// return new RandomForestManeuverClassifier(maneuverFeatures, null, supportedManeuverTypes);
|
||||
// } else {
|
||||
// return new SVMManeuverClassifier(maneuverFeatures, boatClass, supportedManeuverTypes);
|
||||
// }
|
||||
}
|
||||
|
||||
public static List<TrainableSingleManeuverOfflineClassifier> getAllTrainableClassifierInstances(
|
||||
ManeuverFeatures maneuverFeatures, BoatClass boatClass) {
|
||||
List<TrainableSingleManeuverOfflineClassifier> classifiers = new ArrayList<>();
|
||||
classifiers.add(new GradientBoostingManeuverClassifier(maneuverFeatures, boatClass, supportedManeuverTypes));
|
||||
classifiers.add(new LDAManeuverClassifier(maneuverFeatures, boatClass, supportedManeuverTypes));
|
||||
classifiers.add(new LogisticRegressionManeuverClassifier(maneuverFeatures, boatClass, supportedManeuverTypes));
|
||||
classifiers.add(new NaiveBayesManeuverClassifier(maneuverFeatures, boatClass, supportedManeuverTypes));
|
||||
// classifiers.add(new LDAManeuverClassifier(maneuverFeatures, boatClass, supportedManeuverTypes));
|
||||
// classifiers.add(new LogisticRegressionManeuverClassifier(maneuverFeatures, boatClass, supportedManeuverTypes));
|
||||
// classifiers.add(new NaiveBayesManeuverClassifier(maneuverFeatures, boatClass, supportedManeuverTypes));
|
||||
classifiers.add(new NeuralNetworkManeuverClassifier(maneuverFeatures, boatClass, supportedManeuverTypes));
|
||||
classifiers.add(new QDAManeuverClassifier(maneuverFeatures, boatClass, supportedManeuverTypes));
|
||||
// classifiers.add(new QDAManeuverClassifier(maneuverFeatures, boatClass, supportedManeuverTypes));
|
||||
classifiers.add(new RandomForestManeuverClassifier(maneuverFeatures, boatClass, supportedManeuverTypes));
|
||||
classifiers.add(new SVMManeuverClassifier(maneuverFeatures, boatClass, supportedManeuverTypes));
|
||||
List<TrainableSingleManeuverOfflineClassifier> suitableClassifiers = classifiers.stream()
|
||||
|
||||
+4
@@ -26,6 +26,10 @@ public class ManeuverEstimationResult {
|
||||
|
||||
public ManeuverEstimationResult(ManeuverForEstimation maneuver, double[] likelihoodPerManeuverType) {
|
||||
this.maneuver = maneuver;
|
||||
for (int i = 0; i < likelihoodPerManeuverType.length; i++) {
|
||||
likelihoodPerManeuverType[i] += 0.1;
|
||||
}
|
||||
ProbabilityUtil.normalizeLikelihoodArray(likelihoodPerManeuverType);
|
||||
this.likelihoodPerManeuverType = likelihoodPerManeuverType;
|
||||
computeLikelihoodsForPointOfSailAfterManeuver();
|
||||
}
|
||||
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
package com.sap.sailing.windestimation.maneuverclassifier;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.sap.sailing.domain.base.BoatClass;
|
||||
import com.sap.sailing.domain.polars.PolarDataService;
|
||||
import com.sap.sailing.windestimation.data.persistence.PolarDataServiceAccessUtil;
|
||||
import com.sap.sailing.windestimation.maneuverclassifier.impl.ManeuverFeatures;
|
||||
import com.sap.sailing.windestimation.util.LoggingUtil;
|
||||
|
||||
public class PersistedManeuverClassifiersScorePrinter {
|
||||
|
||||
private PersistedManeuverClassifiersScorePrinter() {
|
||||
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
throws MalformedURLException, ClassNotFoundException, IOException, InterruptedException {
|
||||
PolarDataService polarService = PolarDataServiceAccessUtil.getPersistedPolarService();
|
||||
Set<BoatClass> allBoatClasses = polarService.getAllBoatClassesWithPolarSheetsAvailable();
|
||||
List<TrainableSingleManeuverOfflineClassifier> allBoatClassesClassifiers = new ArrayList<>();
|
||||
LoggingUtil.logInfo("### Loading all boat class classifiers:");
|
||||
for (ManeuverFeatures maneuverFeatures : ManeuverFeatures.values()) {
|
||||
List<TrainableSingleManeuverOfflineClassifier> classifiers = ManeuverClassifiersFactory
|
||||
.getAllTrainableClassifierInstances(maneuverFeatures, null);
|
||||
for (TrainableSingleManeuverOfflineClassifier classifier : classifiers) {
|
||||
try {
|
||||
classifier.loadPersistedModel();
|
||||
allBoatClassesClassifiers.add(classifier);
|
||||
} catch (ClassifierPersistenceException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
for (BoatClass boatClass : allBoatClasses) {
|
||||
classifiers = ManeuverClassifiersFactory.getAllTrainableClassifierInstances(maneuverFeatures,
|
||||
boatClass);
|
||||
for (TrainableSingleManeuverOfflineClassifier classifier : classifiers) {
|
||||
try {
|
||||
classifier.loadPersistedModel();
|
||||
allBoatClassesClassifiers.add(classifier);
|
||||
} catch (ClassifierPersistenceException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
StringBuilder str = new StringBuilder("Classifier name \t| Maneuver features \t| Boat class \t| Test score");
|
||||
Collections.sort(allBoatClassesClassifiers, (a, b) -> Double.compare(a.getTestScore(), b.getTestScore()));
|
||||
for (TrainableSingleManeuverOfflineClassifier classifier : allBoatClassesClassifiers) {
|
||||
str.append("\r\n" + classifier.getClass().getSimpleName() + ": \t| " + classifier.getManeuverFeatures()
|
||||
+ " \t| " + classifier.getBoatClass() + " \t| "
|
||||
+ String.format(" %.03f", classifier.getTestScore()));
|
||||
}
|
||||
String outputStr = str.toString();
|
||||
LoggingUtil.logInfo(outputStr);
|
||||
outputStr = outputStr.replaceAll(Pattern.quote(" \t| "), ";");
|
||||
Files.write(Paths.get("classifierScores.csv"), outputStr.getBytes());
|
||||
}
|
||||
|
||||
}
|
||||
+4
@@ -49,6 +49,10 @@ public class MLUtil {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (boatClass == null
|
||||
|| maneuver.getBoatClass() != null && !boatClass.getName().equals(maneuver.getBoatClass().getName())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -6,6 +6,7 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.sap.sailing.domain.base.BoatClass;
|
||||
@@ -56,7 +57,8 @@ public abstract class AbstractSmileManeuverClassifier<T extends SoftClassifier<d
|
||||
this.pca = copy.pca;
|
||||
this.scaler = copy.scaler;
|
||||
this.fixesCountForBoatClass = copy.fixesCountForBoatClass;
|
||||
if (!getBoatClass().equals(copy.getBoatClass())) {
|
||||
if (getBoatClass() != copy.getBoatClass() && getBoatClass() != null && (copy.getBoatClass() == null
|
||||
|| !getBoatClass().getName().equals(copy.getBoatClass().getName()))) {
|
||||
throw new ClassifierPersistenceException("The boat class of the loaded classifier is: "
|
||||
+ copy.getBoatClass() + ". \nExpected: " + getBoatClass());
|
||||
}
|
||||
@@ -64,7 +66,7 @@ public abstract class AbstractSmileManeuverClassifier<T extends SoftClassifier<d
|
||||
throw new ClassifierPersistenceException("The maneuver features of the loaded classifier are: "
|
||||
+ copy.getManeuverFeatures() + ". \nExpected: " + getManeuverFeatures());
|
||||
}
|
||||
if (!getSupportedManeuverTypesMapping().equals(copy.getSupportedManeuverTypesMapping())) {
|
||||
if (!Arrays.equals(getSupportedManeuverTypesMapping(), copy.getSupportedManeuverTypesMapping())) {
|
||||
throw new ClassifierPersistenceException("The supported maneuver types of the loaded classifier are: "
|
||||
+ copy.getSupportedManeuverTypes() + ". \nExpected: " + getSupportedManeuverTypes());
|
||||
}
|
||||
|
||||
+4
-1
@@ -6,6 +6,7 @@ import com.sap.sailing.windestimation.maneuverclassifier.PreprocessingConfig.Pre
|
||||
import com.sap.sailing.windestimation.maneuverclassifier.impl.ManeuverFeatures;
|
||||
|
||||
import smile.classification.SVM;
|
||||
import smile.classification.SVM.Multiclass;
|
||||
import smile.math.kernel.GaussianKernel;
|
||||
|
||||
public class SVMManeuverClassifier extends AbstractSmileManeuverClassifier<SVM<double[]>> {
|
||||
@@ -22,9 +23,11 @@ public class SVMManeuverClassifier extends AbstractSmileManeuverClassifier<SVM<d
|
||||
int featuresCount = x[0].length;
|
||||
double gamma = 1.0 / featuresCount;
|
||||
double sigma = Math.sqrt(0.5 / gamma);
|
||||
SVM<double[]> svm = new SVM<>(new GaussianKernel(sigma), 10.0);
|
||||
SVM<double[]> svm = new SVM<>(new GaussianKernel(sigma), 10.0, getSupportedManeuverTypesCount(),
|
||||
Multiclass.ONE_VS_ALL);
|
||||
svm.learn(x, y);
|
||||
svm.finish();
|
||||
svm.trainPlattScaling(x, y);
|
||||
return svm;
|
||||
}
|
||||
|
||||
|
||||
+23
@@ -17,4 +17,27 @@ public class IntersectedWindRange extends WindRangeForManeuverNode {
|
||||
return violationRange > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
long temp;
|
||||
temp = Double.doubleToLongBits(violationRange);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (!super.equals(obj))
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
IntersectedWindRange other = (IntersectedWindRange) obj;
|
||||
if (Double.doubleToLongBits(violationRange) != Double.doubleToLongBits(other.violationRange))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+1
-7
@@ -6,18 +6,16 @@ import com.sap.sailing.windestimation.maneuverclassifier.ManeuverTypeForClassifi
|
||||
public class ManeuverNode {
|
||||
|
||||
private final WindRangeForManeuverNode validWindRange;
|
||||
private final boolean windRangeToExclude;
|
||||
private double confidence;
|
||||
private final int indexInLevel;
|
||||
private final ManeuverTypeForClassification maneuverType;
|
||||
private final Tack tackAfter;
|
||||
|
||||
public ManeuverNode(ManeuverTypeForClassification maneuverType, Tack tackAfter,
|
||||
WindRangeForManeuverNode validWindRange, boolean windRangeToExclude, double confidence, int indexInLevel) {
|
||||
WindRangeForManeuverNode validWindRange, double confidence, int indexInLevel) {
|
||||
this.maneuverType = maneuverType;
|
||||
this.tackAfter = tackAfter;
|
||||
this.validWindRange = validWindRange;
|
||||
this.windRangeToExclude = windRangeToExclude;
|
||||
this.confidence = confidence;
|
||||
this.indexInLevel = indexInLevel;
|
||||
}
|
||||
@@ -30,10 +28,6 @@ public class ManeuverNode {
|
||||
return validWindRange;
|
||||
}
|
||||
|
||||
public boolean isWindRangeToExclude() {
|
||||
return windRangeToExclude;
|
||||
}
|
||||
|
||||
public double getConfidence() {
|
||||
return confidence;
|
||||
}
|
||||
|
||||
+51
-86
@@ -10,7 +10,6 @@ import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.sap.sailing.domain.base.BoatClass;
|
||||
import com.sap.sailing.domain.common.LegType;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
|
||||
@@ -18,9 +17,9 @@ import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.WindImpl;
|
||||
import com.sap.sailing.domain.tracking.WindWithConfidence;
|
||||
import com.sap.sailing.domain.tracking.impl.WindWithConfidenceImpl;
|
||||
import com.sap.sailing.windestimation.data.FineGrainedManeuverType;
|
||||
import com.sap.sailing.windestimation.data.FineGrainedPointOfSail;
|
||||
import com.sap.sailing.windestimation.data.ManeuverForEstimation;
|
||||
import com.sap.sailing.windestimation.maneuverclassifier.ManeuverTypeForClassification;
|
||||
import com.sap.sailing.windestimation.polarsfitting.SailingStatistics;
|
||||
import com.sap.sse.common.Bearing;
|
||||
import com.sap.sse.common.Speed;
|
||||
@@ -35,7 +34,7 @@ import com.sap.sse.common.impl.DegreeBearingImpl;
|
||||
public class ManeuverNodeBestPathsCalculator {
|
||||
|
||||
protected static final double INTERVAL_FOR_WIND_PATH_DEVIATION_ANALYSIS_IN_SECONDS = 30 * 60;
|
||||
protected static final double MAX_ABS_WIND_COURSE_DEVIATION_TOLERANCE_WITHIN_ANALYSIS_INTERVAL_IN_DEGREES = 45;
|
||||
protected static final double MAX_ABS_WIND_COURSE_DEVIATION_TOLERANCE_WITHIN_ANALYSIS_INTERVAL_IN_DEGREES = 30;
|
||||
|
||||
private ManeuverNodeGraphLevel lastLevel;
|
||||
|
||||
@@ -123,13 +122,14 @@ public class ManeuverNodeBestPathsCalculator {
|
||||
.getPathSailingStatistics(previousLevel.getManeuver().getBoatClass());
|
||||
IntersectedWindRange intersectedWindRangeUntilCurrentNode = bestPreviousNodeInfo.getWindRange()
|
||||
.intersect(currentNode.getValidWindRange());
|
||||
double probability = bestPathsUntilPreviousLevel
|
||||
.getNormalizedProbabilityToNodeFromStart(previousNode)
|
||||
double probability = bestPathsUntilPreviousLevel.getNormalizedProbabilityToNodeFromStart(
|
||||
previousNode) * getPenaltyFactorForTackTransition(currentLevel, previousNode, currentNode)
|
||||
* currentNode.getConfidence()
|
||||
* getPenaltyFactorForTransitionConsideringWindRangeWithinBestPath(
|
||||
intersectedWindRangeUntilCurrentNode, secondsPassedSincePreviousManeuver)
|
||||
* getPenaltyFactorForTransitionConsideringPreviousPathStats(currentLevel, currentNode,
|
||||
previousPathStats, intersectedWindRangeUntilCurrentNode);
|
||||
assert (probability > 0.0001);
|
||||
if (probability > maxProbability) {
|
||||
maxProbability = probability;
|
||||
bestPreviousNode = previousNode;
|
||||
@@ -145,6 +145,33 @@ public class ManeuverNodeBestPathsCalculator {
|
||||
this.lastLevel = currentLevel;
|
||||
}
|
||||
|
||||
protected double getPenaltyFactorForTackTransition(ManeuverNodeGraphLevel currentLevel, ManeuverNode previousNode,
|
||||
ManeuverNode currentNode) {
|
||||
// if (previousNode.getManeuverType() != ManeuverTypeForClassification.OTHER
|
||||
// && currentNode.getManeuverType() != ManeuverTypeForClassification.OTHER) {
|
||||
// if (previousNode.getTackAfter() == currentNode.getTackAfter()) {
|
||||
// return 0.05;
|
||||
// }
|
||||
// } else if (previousNode.getManeuverType() == currentNode.getManeuverType()) {
|
||||
// // maneuver type is OTHER
|
||||
// if (previousNode.getTackAfter() != currentNode.getTackAfter()) {
|
||||
// return 0.05;
|
||||
// }
|
||||
// } else {
|
||||
// // one maneuver is OTHER, other maneuver is tack or jibe
|
||||
// if (previousNode.getManeuverType() == ManeuverTypeForClassification.OTHER) {
|
||||
// if (previousNode.getTackAfter() == currentNode.getTackAfter()) {
|
||||
// return 0.05;
|
||||
// }
|
||||
// } else {
|
||||
// if (previousNode.getTackAfter() != currentNode.getTackAfter()) {
|
||||
// return 0.05;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
return 1;
|
||||
}
|
||||
|
||||
protected double getPenaltyFactorForTransitionConsideringWindRangeWithinBestPath(
|
||||
IntersectedWindRange intersectedWindRangeUntilCurrentNode, double secondsPassedSincePreviousWindRange) {
|
||||
double violationRange = intersectedWindRangeUntilCurrentNode.getViolationRange();
|
||||
@@ -152,18 +179,19 @@ public class ManeuverNodeBestPathsCalculator {
|
||||
if (violationRange == 0) {
|
||||
penaltyFactor = 1.0;
|
||||
} else {
|
||||
violationRange -= secondsPassedSincePreviousWindRange / 3600
|
||||
* MAX_ABS_WIND_COURSE_DEVIATION_TOLERANCE_WITHIN_ANALYSIS_INTERVAL_IN_DEGREES;
|
||||
if (violationRange <= 0) {
|
||||
penaltyFactor = 1 / (1
|
||||
+ (MAX_ABS_WIND_COURSE_DEVIATION_TOLERANCE_WITHIN_ANALYSIS_INTERVAL_IN_DEGREES + violationRange)
|
||||
* 0.01);
|
||||
} else if (violationRange <= MAX_ABS_WIND_COURSE_DEVIATION_TOLERANCE_WITHIN_ANALYSIS_INTERVAL_IN_DEGREES) {
|
||||
penaltyFactor = 1 / (1 + Math.pow((violationRange) / 15, 2));
|
||||
} else {
|
||||
penaltyFactor = 1 / (4 + Math.pow((violationRange) / 5, 2));
|
||||
}
|
||||
// violationRange -= Math.max(1, secondsPassedSincePreviousWindRange / 3600)
|
||||
// * MAX_ABS_WIND_COURSE_DEVIATION_TOLERANCE_WITHIN_ANALYSIS_INTERVAL_IN_DEGREES;
|
||||
// if (violationRange <= 0) {
|
||||
// penaltyFactor = 1 / (1
|
||||
// + (MAX_ABS_WIND_COURSE_DEVIATION_TOLERANCE_WITHIN_ANALYSIS_INTERVAL_IN_DEGREES + violationRange)
|
||||
// * 0.1);
|
||||
// if (violationRange <= MAX_ABS_WIND_COURSE_DEVIATION_TOLERANCE_WITHIN_ANALYSIS_INTERVAL_IN_DEGREES) {
|
||||
// penaltyFactor = 1 / (1 + Math.pow((violationRange) / 15, 2));
|
||||
// } else {
|
||||
penaltyFactor = 1 / (1 + (Math.pow(violationRange, 2)));
|
||||
// }
|
||||
}
|
||||
assert (penaltyFactor > 0.0001);
|
||||
return penaltyFactor;
|
||||
}
|
||||
|
||||
@@ -188,7 +216,9 @@ public class ManeuverNodeBestPathsCalculator {
|
||||
lowestSpeedAndTurningRatePenaltyFactor = getLowestSpeedAndTurningRatePenaltyFactor(previousPathStats,
|
||||
currentLevel, currentNode);
|
||||
}
|
||||
return speedPenaltyFactorBefore * speedPenaltyFactorAfter * lowestSpeedAndTurningRatePenaltyFactor;
|
||||
double finalPenaltyFactor = speedPenaltyFactorBefore * speedPenaltyFactorAfter
|
||||
* lowestSpeedAndTurningRatePenaltyFactor;
|
||||
return finalPenaltyFactor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,78 +242,12 @@ public class ManeuverNodeBestPathsCalculator {
|
||||
|
||||
protected double getLowestSpeedAndTurningRatePenaltyFactor(SailingStatistics averageStatistics,
|
||||
ManeuverNodeGraphLevel currentLevel, ManeuverNode currentNode) {
|
||||
double tackProbabilityBonus = 0;
|
||||
ManeuverForEstimation maneuver = currentLevel.getManeuver();
|
||||
double absDirectionChangeInDegrees = Math.abs(maneuver.getCourseChangeWithinMainCurveInDegrees());
|
||||
double lowestSpeedRatio = (maneuver.getSpeedLossRatio() + maneuver.getLowestSpeedVsExitingSpeedRatio()) / 2.0;
|
||||
double turningRate = maneuver.getMaxTurningRateInDegreesPerSecond();
|
||||
switch (currentNode.getManeuverType()) {
|
||||
case TACK:
|
||||
for (FineGrainedManeuverType otherManeuverType : FineGrainedManeuverType.values()) {
|
||||
if (otherManeuverType != FineGrainedManeuverType.TACK
|
||||
&& otherManeuverType != FineGrainedManeuverType._180_JIBE
|
||||
&& otherManeuverType != FineGrainedManeuverType._180_TACK
|
||||
&& otherManeuverType != FineGrainedManeuverType._360
|
||||
&& averageStatistics.getNumberOfCleanManeuvers(otherManeuverType) > 0) {
|
||||
double lowestSpeedRatioDifference = lowestSpeedRatio
|
||||
- averageStatistics.getAverageSpeedLossForManeuverType(otherManeuverType);
|
||||
double turningRateDifference = turningRate
|
||||
- averageStatistics.getAverageTurningRateForManeuverType(otherManeuverType);
|
||||
if (lowestSpeedRatioDifference > 0 && turningRateDifference < 0) {
|
||||
tackProbabilityBonus -= lowestSpeedRatioDifference * 2 + turningRateDifference / -100;
|
||||
}
|
||||
}
|
||||
}
|
||||
double courseChangeDifference = absDirectionChangeInDegrees
|
||||
- averageStatistics.getAverageAbsCourseChangeInDegreesForManeuverType(FineGrainedManeuverType.JIBE);
|
||||
if (courseChangeDifference < 0) {
|
||||
tackProbabilityBonus -= courseChangeDifference / -180;
|
||||
}
|
||||
break;
|
||||
case JIBE:
|
||||
case OTHER:
|
||||
if (averageStatistics.getNumberOfCleanManeuvers(FineGrainedManeuverType.TACK) > 0) {
|
||||
double lowestSpeedRatioDifference = lowestSpeedRatio
|
||||
- averageStatistics.getAverageSpeedLossForManeuverType(FineGrainedManeuverType.TACK);
|
||||
double turningRateDifference = turningRate
|
||||
- averageStatistics.getAverageTurningRateForManeuverType(FineGrainedManeuverType.TACK);
|
||||
if (lowestSpeedRatioDifference < 0 && turningRateDifference > 0) {
|
||||
tackProbabilityBonus += lowestSpeedRatioDifference * -2 + turningRateDifference / 100;
|
||||
}
|
||||
// courseChangeDifference = absDirectionChangeInDegrees - averageStatistics
|
||||
// .getAverageAbsCourseChangeInDegreesForManeuverType(FineGrainedManeuverType.TACK);
|
||||
// if (courseChangeDifference > 0) {
|
||||
// tackProbabilityBonus += courseChangeDifference / 180;
|
||||
// }
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 1 - Math.abs(tackProbabilityBonus);
|
||||
return 1;
|
||||
}
|
||||
|
||||
protected double getSpeedPenaltyFactorForPointOfSail(SailingStatistics averageStatistics,
|
||||
FineGrainedPointOfSail pointOfSail, double speedAtPointOfSail, BoatClass boatClass) {
|
||||
double lowestAverageSpeedUpwind = averageStatistics.getLowestUpwindAvgSpeed();
|
||||
double upwindProbabilityBonus = 0.0;
|
||||
if (pointOfSail.getLegType() == LegType.UPWIND) {
|
||||
for (FineGrainedPointOfSail otherPointOfSail : FineGrainedPointOfSail.values()) {
|
||||
if (otherPointOfSail.getLegType() == LegType.REACHING
|
||||
|| otherPointOfSail.getLegType() == LegType.DOWNWIND
|
||||
&& averageStatistics.getNumberOfCleanTracks(otherPointOfSail) > 0) {
|
||||
double speedRatio = speedAtPointOfSail
|
||||
/ averageStatistics.getAverageSpeedInKnotsForPointOfSail(otherPointOfSail);
|
||||
if (speedRatio > 1.05) {
|
||||
upwindProbabilityBonus = Math.min(upwindProbabilityBonus, Math.max((1 - speedRatio), -0.8));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (lowestAverageSpeedUpwind != 0) {
|
||||
double speedRatio = lowestAverageSpeedUpwind / speedAtPointOfSail;
|
||||
if (speedRatio > 1.05) {
|
||||
upwindProbabilityBonus = Math.max((1 - speedRatio), -0.8);
|
||||
}
|
||||
}
|
||||
return 1.0 - Math.abs(upwindProbabilityBonus);
|
||||
return 1;
|
||||
}
|
||||
|
||||
public List<Pair<ManeuverNodeGraphLevel, ManeuverNode>> getBestPath(ManeuverNodeGraphLevel lastLevel,
|
||||
@@ -345,7 +309,8 @@ public class ManeuverNodeBestPathsCalculator {
|
||||
.getBestPreviousNodeInfo(currentNode);
|
||||
globalWindRange = globalWindRange == null ? bestPreviousNodeInfo.getWindRange()
|
||||
: globalWindRange.intersect(bestPreviousNodeInfo.getWindRange());
|
||||
if (!globalWindRange.isViolation() && globalWindRange.getAngleTowardStarboard() <= 40) {
|
||||
if (!globalWindRange.isViolation() && globalWindRange.getAngleTowardStarboard() <= 20
|
||||
&& currentNode.getManeuverType() == ManeuverTypeForClassification.OTHER) {
|
||||
DegreeBearingImpl windCourse = new DegreeBearingImpl(globalWindRange.getAvgWindCourse());
|
||||
Speed avgWindSpeed = getAvgWindSpeed(currentLevel.getManeuver(), windCourse);
|
||||
Wind wind = new WindImpl(currentLevel.getManeuver().getManeuverPosition(),
|
||||
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
package com.sap.sailing.windestimation.maneuvergraph.maneuvernode;
|
||||
|
||||
import com.sap.sailing.domain.base.BoatClass;
|
||||
import com.sap.sailing.domain.common.LegType;
|
||||
import com.sap.sailing.windestimation.data.FineGrainedManeuverType;
|
||||
import com.sap.sailing.windestimation.data.FineGrainedPointOfSail;
|
||||
import com.sap.sailing.windestimation.data.ManeuverForEstimation;
|
||||
import com.sap.sailing.windestimation.polarsfitting.SailingStatistics;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Vladislav Chumak (D069712)
|
||||
*
|
||||
*/
|
||||
public class ManeuverNodeBestPathsCalculatorWithGeneralPolars extends ManeuverNodeBestPathsCalculator {
|
||||
|
||||
@Override
|
||||
protected double getLowestSpeedAndTurningRatePenaltyFactor(SailingStatistics averageStatistics,
|
||||
ManeuverNodeGraphLevel currentLevel, ManeuverNode currentNode) {
|
||||
double tackProbabilityBonus = 0;
|
||||
ManeuverForEstimation maneuver = currentLevel.getManeuver();
|
||||
double absDirectionChangeInDegrees = Math.abs(maneuver.getCourseChangeWithinMainCurveInDegrees());
|
||||
double lowestSpeedRatio = (maneuver.getSpeedLossRatio() + maneuver.getLowestSpeedVsExitingSpeedRatio()) / 2.0;
|
||||
double turningRate = maneuver.getMaxTurningRateInDegreesPerSecond();
|
||||
switch (currentNode.getManeuverType()) {
|
||||
case TACK:
|
||||
for (FineGrainedManeuverType otherManeuverType : FineGrainedManeuverType.values()) {
|
||||
if (otherManeuverType != FineGrainedManeuverType.TACK
|
||||
&& otherManeuverType != FineGrainedManeuverType._180_JIBE
|
||||
&& otherManeuverType != FineGrainedManeuverType._180_TACK
|
||||
&& otherManeuverType != FineGrainedManeuverType._360
|
||||
&& averageStatistics.getNumberOfCleanManeuvers(otherManeuverType) > 0) {
|
||||
double lowestSpeedRatioDifference = lowestSpeedRatio
|
||||
- averageStatistics.getAverageSpeedLossForManeuverType(otherManeuverType);
|
||||
double turningRateDifference = turningRate
|
||||
- averageStatistics.getAverageTurningRateForManeuverType(otherManeuverType);
|
||||
if (lowestSpeedRatioDifference > 0 && turningRateDifference < 0) {
|
||||
tackProbabilityBonus = Math.min(tackProbabilityBonus,
|
||||
-1 / (1 + lowestSpeedRatioDifference / 2 + turningRateDifference / -100));
|
||||
}
|
||||
}
|
||||
}
|
||||
double courseChangeDifference = absDirectionChangeInDegrees
|
||||
- averageStatistics.getAverageAbsCourseChangeInDegreesForManeuverType(FineGrainedManeuverType.JIBE);
|
||||
if (courseChangeDifference < 0) {
|
||||
tackProbabilityBonus = (1 + tackProbabilityBonus) * (1 - Math.min(0.1, courseChangeDifference / -360));
|
||||
}
|
||||
break;
|
||||
case JIBE:
|
||||
case OTHER:
|
||||
if (averageStatistics.getNumberOfCleanManeuvers(FineGrainedManeuverType.TACK) > 0) {
|
||||
double lowestSpeedRatioDifference = lowestSpeedRatio
|
||||
- averageStatistics.getAverageSpeedLossForManeuverType(FineGrainedManeuverType.TACK);
|
||||
double turningRateDifference = turningRate
|
||||
- averageStatistics.getAverageTurningRateForManeuverType(FineGrainedManeuverType.TACK);
|
||||
if (lowestSpeedRatioDifference < 0 && turningRateDifference > 0) {
|
||||
tackProbabilityBonus += Math.max(tackProbabilityBonus,
|
||||
1 / (1 + lowestSpeedRatioDifference / -2 + turningRateDifference / 100));
|
||||
}
|
||||
// courseChangeDifference = absDirectionChangeInDegrees - averageStatistics
|
||||
// .getAverageAbsCourseChangeInDegreesForManeuverType(FineGrainedManeuverType.TACK);
|
||||
// if (courseChangeDifference > 0) {
|
||||
// tackProbabilityBonus += courseChangeDifference / 180;
|
||||
// }
|
||||
}
|
||||
break;
|
||||
}
|
||||
double penaltyFactor = 1 - Math.abs(tackProbabilityBonus);
|
||||
return penaltyFactor;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double getSpeedPenaltyFactorForPointOfSail(SailingStatistics averageStatistics,
|
||||
FineGrainedPointOfSail pointOfSail, double speedAtPointOfSail, BoatClass boatClass) {
|
||||
double lowestAverageSpeedUpwind = averageStatistics.getLowestUpwindAvgSpeed();
|
||||
double upwindProbabilityBonus = 0.0;
|
||||
if (pointOfSail.getLegType() == LegType.UPWIND) {
|
||||
for (FineGrainedPointOfSail otherPointOfSail : FineGrainedPointOfSail.values()) {
|
||||
if (otherPointOfSail.getLegType() == LegType.REACHING
|
||||
|| otherPointOfSail.getLegType() == LegType.DOWNWIND
|
||||
&& averageStatistics.getNumberOfCleanTracks(otherPointOfSail) > 0) {
|
||||
double speedRatio = speedAtPointOfSail
|
||||
/ averageStatistics.getAverageSpeedInKnotsForPointOfSail(otherPointOfSail);
|
||||
if (speedRatio > 1.05) {
|
||||
upwindProbabilityBonus = Math.min(upwindProbabilityBonus, Math.max((1 - speedRatio), -0.8));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (lowestAverageSpeedUpwind != 0) {
|
||||
double speedRatio = lowestAverageSpeedUpwind / speedAtPointOfSail;
|
||||
if (speedRatio > 1.05) {
|
||||
upwindProbabilityBonus = Math.max((1 - speedRatio), -0.8);
|
||||
}
|
||||
}
|
||||
return 1.0 - Math.abs(upwindProbabilityBonus);
|
||||
}
|
||||
|
||||
}
|
||||
+23
-31
@@ -18,6 +18,7 @@ import com.sap.sse.common.impl.DegreeBearingImpl;
|
||||
*/
|
||||
public class ManeuverNodeGraphLevel {
|
||||
|
||||
private static final int MIN_UPWIND_ABS_TWA = 20;
|
||||
private final ManeuverForEstimation maneuver;
|
||||
private ManeuverEstimationResult maneuverEstimationResult;
|
||||
|
||||
@@ -51,15 +52,15 @@ public class ManeuverNodeGraphLevel {
|
||||
}
|
||||
|
||||
private void addManeuverNode(ManeuverTypeForClassification maneuverType, Tack tackAfter,
|
||||
WindRangeForManeuverNode windRange, boolean windRangeToExclude, double confidence) {
|
||||
ManeuverNode maneuverNode = new ManeuverNode(maneuverType, tackAfter, windRange, windRangeToExclude,
|
||||
confidence / probabilitiesSum, levelNodes.size() - 1);
|
||||
WindRangeForManeuverNode windRange, double confidence) {
|
||||
ManeuverNode maneuverNode = new ManeuverNode(maneuverType, tackAfter, windRange, confidence / probabilitiesSum,
|
||||
levelNodes.size());
|
||||
levelNodes.add(maneuverNode);
|
||||
}
|
||||
|
||||
private void initNodeJustForSpeedAnalysis() {
|
||||
WindRangeForManeuverNode windRange = new WindRangeForManeuverNode(0, 360);
|
||||
addManeuverNode(ManeuverTypeForClassification.OTHER, null, windRange, false, 1.0);
|
||||
addManeuverNode(ManeuverTypeForClassification.OTHER, null, windRange, 1.0);
|
||||
}
|
||||
|
||||
private void initNodesAsRegular() {
|
||||
@@ -86,15 +87,14 @@ public class ManeuverNodeGraphLevel {
|
||||
|
||||
private void initBearAwayNode() {
|
||||
Bearing invertedCourseBefore = maneuver.getSpeedWithBearingBefore().getBearing().reverse();
|
||||
double angleTowardStarboard = Math.abs(
|
||||
invertedCourseBefore.getDifferenceTo(maneuver.getSpeedWithBearingAfter().getBearing()).getDegrees());
|
||||
// subtract minimal upwind TWA which is 30 deg plus -10 deg for buffer
|
||||
double minUpwindTwa = 20;
|
||||
angleTowardStarboard -= 20;
|
||||
double angleTowardStarboard = invertedCourseBefore
|
||||
.getDifferenceTo(maneuver.getSpeedWithBearingAfter().getBearing()).abs().getDegrees();
|
||||
angleTowardStarboard -= MIN_UPWIND_ABS_TWA;
|
||||
assert (angleTowardStarboard > 0);
|
||||
Bearing from;
|
||||
Tack tackAfter;
|
||||
if (maneuver.getCourseChangeInDegrees() < 0) {
|
||||
from = invertedCourseBefore.add(new DegreeBearingImpl(-minUpwindTwa));
|
||||
from = invertedCourseBefore.add(new DegreeBearingImpl(MIN_UPWIND_ABS_TWA));
|
||||
tackAfter = Tack.PORT;
|
||||
} else {
|
||||
from = maneuver.getSpeedWithBearingAfter().getBearing();
|
||||
@@ -102,58 +102,50 @@ public class ManeuverNodeGraphLevel {
|
||||
}
|
||||
WindRangeForManeuverNode windRange = new WindRangeForManeuverNode(from.getDegrees(), angleTowardStarboard);
|
||||
double confidence = maneuverEstimationResult.getManeuverTypeLikelihood(CoarseGrainedManeuverType.BEAR_AWAY);
|
||||
addManeuverNode(ManeuverTypeForClassification.OTHER, tackAfter, windRange, false, confidence);
|
||||
addManeuverNode(ManeuverTypeForClassification.OTHER, tackAfter, windRange, confidence);
|
||||
}
|
||||
|
||||
private void initHeadUpNode() {
|
||||
Bearing invertedCourseAfter = maneuver.getSpeedWithBearingAfter().getBearing().reverse();
|
||||
double angleTowardStarboard = Math.abs(
|
||||
invertedCourseAfter.getDifferenceTo(maneuver.getSpeedWithBearingBefore().getBearing()).getDegrees());
|
||||
double angleTowardStarboard = invertedCourseAfter
|
||||
.getDifferenceTo(maneuver.getSpeedWithBearingBefore().getBearing()).abs().getDegrees();
|
||||
angleTowardStarboard -= MIN_UPWIND_ABS_TWA;
|
||||
assert (angleTowardStarboard > 0);
|
||||
Bearing from;
|
||||
Tack tackAfter;
|
||||
if (maneuver.getCourseChangeInDegrees() < 0) {
|
||||
from = maneuver.getSpeedWithBearingBefore().getBearing();
|
||||
tackAfter = Tack.STARBOARD;
|
||||
} else {
|
||||
from = invertedCourseAfter;
|
||||
from = invertedCourseAfter.add(new DegreeBearingImpl(MIN_UPWIND_ABS_TWA));
|
||||
tackAfter = Tack.PORT;
|
||||
}
|
||||
WindRangeForManeuverNode windRange = new WindRangeForManeuverNode(from.getDegrees(), angleTowardStarboard);
|
||||
double confidence = maneuverEstimationResult.getManeuverTypeLikelihood(CoarseGrainedManeuverType.HEAD_UP);
|
||||
addManeuverNode(ManeuverTypeForClassification.OTHER, tackAfter, windRange, false, confidence);
|
||||
addManeuverNode(ManeuverTypeForClassification.OTHER, tackAfter, windRange, confidence);
|
||||
}
|
||||
|
||||
private void initJibeNode() {
|
||||
Bearing middleCourse = maneuver.getMiddleCourse();
|
||||
double absCourseChangeDeg = Math.abs(maneuver.getCourseChangeInDegrees());
|
||||
Double optimalJibeDeviationDeg = maneuver.getDeviationFromOptimalJibeAngleInDegrees();
|
||||
double middleAngleRange = 0;
|
||||
if (optimalJibeDeviationDeg != null) {
|
||||
middleAngleRange = Math.max(0, optimalJibeDeviationDeg * 2);
|
||||
}
|
||||
middleAngleRange += absCourseChangeDeg * 0.4;
|
||||
double middleAngleRange = absCourseChangeDeg * 0.5;
|
||||
Bearing from = middleCourse.add(new DegreeBearingImpl(-middleAngleRange / 2.0));
|
||||
WindRangeForManeuverNode windRange = new WindRangeForManeuverNode(from.getDegrees(), middleAngleRange);
|
||||
double confidence = maneuverEstimationResult.getManeuverTypeLikelihood(CoarseGrainedManeuverType.JIBE);
|
||||
Tack tackAfter = maneuver.getCourseChangeWithinMainCurveInDegrees() < 0 ? Tack.STARBOARD : Tack.PORT;
|
||||
addManeuverNode(ManeuverTypeForClassification.JIBE, tackAfter, windRange, false, confidence);
|
||||
addManeuverNode(ManeuverTypeForClassification.JIBE, tackAfter, windRange, confidence);
|
||||
}
|
||||
|
||||
private void initTackNode(double probabilitiesSum) {
|
||||
Bearing middleCourse = maneuver.getMiddleCourse();
|
||||
double absCourseChangeDeg = Math.abs(maneuver.getCourseChangeInDegrees());
|
||||
Double optimalTackDeviationDeg = maneuver.getDeviationFromOptimalTackAngleInDegrees();
|
||||
double middleAngleRange = 0;
|
||||
if (optimalTackDeviationDeg != null) {
|
||||
middleAngleRange = Math.max(0, optimalTackDeviationDeg * 2);
|
||||
}
|
||||
middleAngleRange += absCourseChangeDeg * 0.2;
|
||||
double middleAngleRange = absCourseChangeDeg * 0.4;
|
||||
Bearing from = middleCourse.add(new DegreeBearingImpl(-middleAngleRange / 2.0));
|
||||
from = from.reverse();
|
||||
WindRangeForManeuverNode windRange = new WindRangeForManeuverNode(from.getDegrees(), middleAngleRange);
|
||||
double confidence = maneuverEstimationResult.getManeuverTypeLikelihood(CoarseGrainedManeuverType.TACK);
|
||||
Tack tackAfter = maneuver.getCourseChangeWithinMainCurveInDegrees() < 0 ? Tack.PORT : Tack.STARBOARD;
|
||||
addManeuverNode(ManeuverTypeForClassification.TACK, tackAfter, windRange, false, confidence);
|
||||
addManeuverNode(ManeuverTypeForClassification.TACK, tackAfter, windRange, confidence);
|
||||
}
|
||||
|
||||
private void initNodesAsPenaltyCircle() {
|
||||
@@ -167,8 +159,8 @@ public class ManeuverNodeGraphLevel {
|
||||
from = from.reverse();
|
||||
double angleTowardStarboard = 140;
|
||||
WindRangeForManeuverNode windRange = new WindRangeForManeuverNode(from.getDegrees(), angleTowardStarboard);
|
||||
addManeuverNode(ManeuverTypeForClassification.OTHER, null, windRange, false, 0.7);
|
||||
addManeuverNode(ManeuverTypeForClassification.OTHER, null, windRange, true, 0.3);
|
||||
addManeuverNode(ManeuverTypeForClassification.OTHER, null, windRange, 0.7);
|
||||
addManeuverNode(ManeuverTypeForClassification.OTHER, null, windRange.invert(), 0.3);
|
||||
}
|
||||
|
||||
public ManeuverForEstimation getManeuver() {
|
||||
|
||||
+39
-4
@@ -35,12 +35,19 @@ public class WindRangeForManeuverNode {
|
||||
return angleTowardStarboard;
|
||||
}
|
||||
|
||||
public WindRangeForManeuverNode invert() {
|
||||
double newFromPortside = fromPortside + angleTowardStarboard;
|
||||
if (newFromPortside >= 360) {
|
||||
newFromPortside -= 360;
|
||||
}
|
||||
double newAngleTowardStarboard = 360 - angleTowardStarboard;
|
||||
return new WindRangeForManeuverNode(newFromPortside, newAngleTowardStarboard);
|
||||
}
|
||||
|
||||
public double getAvgWindCourse() {
|
||||
double avgWindCourse = fromPortside + angleTowardStarboard / 2.0;
|
||||
if (avgWindCourse > 360) {
|
||||
avgWindCourse -= 360;
|
||||
} else if (avgWindCourse < 0) {
|
||||
avgWindCourse += 360;
|
||||
}
|
||||
return avgWindCourse;
|
||||
}
|
||||
@@ -88,11 +95,11 @@ public class WindRangeForManeuverNode {
|
||||
// newFromPortside = nextWindRange.angleTowardStarboard -
|
||||
// deviationFromPortsideTowardStarboardInDegrees;
|
||||
// newAngleTowardStarboard = deviationFromPortsideTowardStarboardInDegrees;
|
||||
violationRange = newAngleTowardStarboard;
|
||||
violationRange = deviationFromPortsideTowardStarboardInDegrees;
|
||||
} else {
|
||||
// newFromPortside = angleTowardStarboard - deviationFromPortsideTowardPortsideInDegrees;
|
||||
// newAngleTowardStarboard = deviationFromPortsideTowardPortsideInDegrees;
|
||||
violationRange = newAngleTowardStarboard;
|
||||
violationRange = deviationFromPortsideTowardPortsideInDegrees;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -230,4 +237,32 @@ public class WindRangeForManeuverNode {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
long temp;
|
||||
temp = Double.doubleToLongBits(angleTowardStarboard);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(fromPortside);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
WindRangeForManeuverNode other = (WindRangeForManeuverNode) obj;
|
||||
if (Double.doubleToLongBits(angleTowardStarboard) != Double.doubleToLongBits(other.angleTowardStarboard))
|
||||
return false;
|
||||
if (Double.doubleToLongBits(fromPortside) != Double.doubleToLongBits(other.fromPortside))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.sap.sailing.windestimation.polarsfitting;
|
||||
|
||||
import com.sap.sailing.domain.polars.PolarDataService;
|
||||
|
||||
public class ManeuverPolarsFittingWindEstimation extends PolarsFittingWindEstimation {
|
||||
|
||||
public ManeuverPolarsFittingWindEstimation(PolarDataService polarService) {
|
||||
super(polarService);
|
||||
// TODO Auto-generated constructor stub
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user