diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPerformanceCurve.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPerformanceCurve.java index fb14298930d..d9d4d4dcfd8 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPerformanceCurve.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPerformanceCurve.java @@ -36,7 +36,7 @@ public class TestORCPerformanceCurve { private final boolean collectErrors = true; private static final String RESOURCES = "resources/orc/"; - private static ORCPerformanceCurveCourse course; + private static ORCPerformanceCurveCourse course1; private static ORCCertificateImporter importer; @Rule @@ -61,7 +61,7 @@ public class TestORCPerformanceCurve { legs.add(new ORCPerformanceCurveLegImpl(new NauticalMileDistance(1.03), new DegreeBearingImpl(15))); legs.add(new ORCPerformanceCurveLegImpl(new NauticalMileDistance(1.03), new DegreeBearingImpl(165))); legs.add(new ORCPerformanceCurveLegImpl(new NauticalMileDistance(1.17), new DegreeBearingImpl(180))); - course = new ORCPerformanceCurveCourseImpl(legs); + course1 = new ORCPerformanceCurveCourseImpl(legs); // Local File: File fileGER = new File(RESOURCES + "GER2019.json"); @@ -75,10 +75,10 @@ public class TestORCPerformanceCurve { public void testLagrangeInterpolation() throws FunctionEvaluationException { Double accuracy = 0.21; ORCCertificateImpl certificate = (ORCCertificateImpl) importer.getCertificate("GER 5549"); - ORCPerformanceCurveImpl performanceCurve = (ORCPerformanceCurveImpl) certificate.getPerformanceCurve(course); + ORCPerformanceCurveImpl performanceCurve = (ORCPerformanceCurveImpl) certificate.getPerformanceCurve(course1); Double value = performanceCurve.getDurationPerNauticalMileAtTrueWindAngleAndSpeed(new KnotSpeedImpl(6), new DegreeBearingImpl(60)).asSeconds(); - assertNotNull(course); + assertNotNull(course1); assertNotNull(performanceCurve); assertEquals(value, performanceCurve.getLagrangeAllowancePerTrueWindSpeedAndAngle(new KnotSpeedImpl( 6),new DegreeBearingImpl( 60)).asSeconds(), 0.000001); @@ -98,7 +98,7 @@ public class TestORCPerformanceCurve { assertEquals(345.0, performanceCurve.getLagrangeAllowancePerTrueWindSpeedAndAngle(new KnotSpeedImpl(16),new DegreeBearingImpl(98.3)).asSeconds(), accuracy); assertEquals(321.7, performanceCurve.getLagrangeAllowancePerTrueWindSpeedAndAngle(new KnotSpeedImpl(20),new DegreeBearingImpl(98.3)).asSeconds(), accuracy); - // TESTS of the corner areas of the polars + // TESTS of the corner areas of the polar assertEquals(588.1, performanceCurve.getLagrangeAllowancePerTrueWindSpeedAndAngle(new KnotSpeedImpl( 6),new DegreeBearingImpl(138.7)).asSeconds(), accuracy); assertEquals(468.4, performanceCurve.getLagrangeAllowancePerTrueWindSpeedAndAngle(new KnotSpeedImpl( 8),new DegreeBearingImpl(138.7)).asSeconds(), accuracy); assertEquals(413.8, performanceCurve.getLagrangeAllowancePerTrueWindSpeedAndAngle(new KnotSpeedImpl(10),new DegreeBearingImpl(138.7)).asSeconds(), accuracy); @@ -137,7 +137,7 @@ public class TestORCPerformanceCurve { public void testPerformanceCurveInvertation() throws MaxIterationsExceededException, FunctionEvaluationException { Double accuracy = 0.1; ORCCertificateImpl certificate = (ORCCertificateImpl) importer.getCertificate("GER 5549"); - ORCPerformanceCurveImpl performanceCurve = (ORCPerformanceCurveImpl) certificate.getPerformanceCurve(course); + ORCPerformanceCurveImpl performanceCurve = (ORCPerformanceCurveImpl) certificate.getPerformanceCurve(course1); assertEquals( 11.5, performanceCurve.getImpliedWind(performanceCurve.getAllowancePerCourse(new KnotSpeedImpl( 11.5))).getKnots(), accuracy); assertEquals(17.23, performanceCurve.getImpliedWind(performanceCurve.getAllowancePerCourse(new KnotSpeedImpl(17.23))).getKnots(), accuracy); @@ -154,19 +154,19 @@ public class TestORCPerformanceCurve { @Test public void testImpliedWindSimple() throws MaxIterationsExceededException, FunctionEvaluationException { double accuracy = 0.000001; - double courseLengthInNauticalMiles = course.getTotalLength().getNauticalMiles(); + double courseLengthInNauticalMiles = course1.getTotalLength().getNauticalMiles(); ORCCertificate certificateMoana = importer.getCertificate("GER 5549"); ORCCertificate certificateMilan = importer.getCertificate("GER 7323"); ORCCertificate certificateTutima = importer.getCertificate("GER 5609"); ORCCertificate certificateBank = importer.getCertificate("GER 5555"); ORCCertificate certificateHaspa = importer.getCertificate("GER 6300"); ORCCertificate certificateHalbtrocken = importer.getCertificate("GER 5564"); - ORCPerformanceCurve performanceCurveMoana = certificateMoana.getPerformanceCurve(course); - ORCPerformanceCurve performanceCurveMilan = certificateMilan.getPerformanceCurve(course); - ORCPerformanceCurve performanceCurveTutima = certificateTutima.getPerformanceCurve(course); - ORCPerformanceCurve performanceCurveBank = certificateBank.getPerformanceCurve(course); - ORCPerformanceCurve performanceCurveHaspa = certificateHaspa.getPerformanceCurve(course); - ORCPerformanceCurve performanceCurveHalbtrocken = certificateHalbtrocken.getPerformanceCurve(course); + ORCPerformanceCurve performanceCurveMoana = certificateMoana.getPerformanceCurve(course1); + ORCPerformanceCurve performanceCurveMilan = certificateMilan.getPerformanceCurve(course1); + ORCPerformanceCurve performanceCurveTutima = certificateTutima.getPerformanceCurve(course1); + ORCPerformanceCurve performanceCurveBank = certificateBank.getPerformanceCurve(course1); + ORCPerformanceCurve performanceCurveHaspa = certificateHaspa.getPerformanceCurve(course1); + ORCPerformanceCurve performanceCurveHalbtrocken = certificateHalbtrocken.getPerformanceCurve(course1); assertNotNull(performanceCurveMoana); assertNotNull(performanceCurveMilan); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/ORCCertificateImporter.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/ORCCertificateImporter.java index c9bcf8c7914..016cf2bd17d 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/ORCCertificateImporter.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/ORCCertificateImporter.java @@ -2,6 +2,7 @@ package com.sap.sailing.domain.orc; import java.util.Map; +import com.sap.sailing.domain.base.Competitor; /** * Extracts ORC-Certificates from different sources and different formats. Returns ORC-Certificate objects for given @@ -13,15 +14,28 @@ import java.util.Map; public interface ORCCertificateImporter { /** - * Returns an ORCCertificate object to a given sail number. If there is no certificate for the given id, the method - * returns {@link null}. - **/ - //TODO Better input specification on identification of certificate. + * Creates an {@link ORCCertificate} object to a given sail number. + * + * @param sailnumber + * as specified for the boat of the {@link Competitor) in the ORC files. The sailnumber commonly consists + * out of an alphabetical national code (e.g. 'GER', 'GBR', 'SUI', ...) or an alphanumerical class code (e.g. 'F40', + * 'X41', ...) and a numerical identification code. + * @return {@code null}, if there isn't any data for the given sailnumber. Otherwise an {@link ORCCertificate}. + */ ORCCertificate getCertificate(String sailnumber); /** - * Returns a map of ORCCertificate objects to a given array of sail numbers. - **/ + * Creates an {@link Map} of {@link ORCCertificate} objects to the given sail numbers. + *

+ * Same functionality as {@code getCertificate} for a single sailnumber, just for multiple elements. + * + * @param sailnumbers + * represents an array of sailnumbers as specified for the boat of the {@link Competitor) in the ORC + * files. The sailnumber commonly consists out of an alphabetical national code (e.g. 'GER', 'GBR', 'SUI', ...) or + * an alphanumerical class code (e.g. 'F40', 'X41', ...) and a numerical identification code. + * @return {@link Map} of {@link ORCCertificate} values and the sailnumber parameters as keys. The value equals + * {@code null}, if there isn't any data for the given sailnumber. + */ Map getCertificates(String[] sailnumbers); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/ORCPerformanceCurve.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/ORCPerformanceCurve.java index bf0ec8d19c5..0b3b1a3a03c 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/ORCPerformanceCurve.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/ORCPerformanceCurve.java @@ -8,8 +8,6 @@ import com.sap.sailing.domain.base.Competitor; import com.sap.sse.common.Duration; import com.sap.sse.common.Speed; -// TODO Finish Comments for API - /** * For a {@link Competitor} and the {@link ORCPerformanceCurveCourse} which the competitor sailed until the creation of * an instance of an implementing class, this instance represents a so called "Perfromance Curve". This Curve is @@ -18,13 +16,13 @@ import com.sap.sse.common.Speed; * course. For a given windspeed the performance curve returns the allowance for the boat or in simpler words: how long * should the boat need for a nautical mile when sailing 100% performance. * - * @author Daniel Lisunkin (i505543) + * @author Daniel Lisunkin (i505543)˝ * */ public interface ORCPerformanceCurve { /** - * + * TODO Comment * * @param timePerNauticalMile equals the average duration the boat needed to conquer one mile on the sailed subcourse, => totalTime / course.getTotalLength() * @return @@ -33,7 +31,7 @@ public interface ORCPerformanceCurve { * @throws MaxIterationsExceededException * @throws FunctionEvaluationException */ - Speed getImpliedWind(Duration timePerNauticalMile) throws ArgumentOutsideDomainException, MaxIterationsExceededException, FunctionEvaluationException; + Speed getImpliedWind(Duration timePerNauticalMile) throws MaxIterationsExceededException, FunctionEvaluationException; /** * TODO getComment done @@ -46,6 +44,7 @@ public interface ORCPerformanceCurve { Duration getCalculatedTime(ORCPerformanceCurve referenceBoat, Duration sailedDurationPerNauticalMile); /** + * TODO Comment * * @param impliedWind * @return diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveImpl.java index 2ec9de8cbc2..9d9014f0c4a 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveImpl.java @@ -1,9 +1,7 @@ package com.sap.sailing.domain.orc.impl; -import java.awt.List; import java.io.Serializable; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -134,24 +132,23 @@ public class ORCPerformanceCurveImpl implements Serializable, ORCPerformanceCurv } private Map createAllowancePerLeg(ORCPerformanceCurveLeg leg) throws FunctionEvaluationException { - Map result = new HashMap<>(); - Double twa = leg.getTwa().getDegrees(); - - - for (Speed entry : ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS) { + final Map result = new HashMap<>(); + final double twaDeg = leg.getTwa().getDegrees(); + final double twaRad = leg.getTwa().getRadians(); + for (final Speed entry : ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS) { // Case switching on TWA (0. TWA == 0; 1. TWA < Beat; 2. Beat < TWA < Gybe; 3. Gybe < TWA; 4. TWA == 180) - if (twa < beatAngles.get(entry).getDegrees()) { - // Case 0 & 1 - result = beatVMG * distance / cos(TWA) - result.put(entry, Duration.ONE_SECOND.times(getDurationPerNauticalMileAtTrueWindAngleAndSpeed(entry, beatAngles.get(entry)).asSeconds() * leg.getLength().getNauticalMiles() * Math.abs(Math.cos(Math.toRadians(twa))))); - } else if (twa > gybeAngles.get(entry).getDegrees()) { - // Case 3 & 4 - result = runVMG * distance / cos(TWA) - result.put(entry, Duration.ONE_SECOND.times(getDurationPerNauticalMileAtTrueWindAngleAndSpeed(entry, gybeAngles.get(entry)).asSeconds() * leg.getLength().getNauticalMiles() * Math.abs(Math.cos(Math.toRadians(twa))))); + // TODO (so ? beatAngles : gybeAngles) + if (twaDeg <= beatAngles.get(entry).getDegrees()) { + // Case 0 & 1 - result = beatVMG * distance * cos(TWA) + result.put(entry, Duration.ONE_SECOND.times(getDurationPerNauticalMileAtTrueWindAngleAndSpeed(entry, beatAngles.get(entry)).asSeconds() * leg.getLength().getNauticalMiles())); + } else if (twaDeg >= gybeAngles.get(entry).getDegrees()) { + // Case 3 & 4 - result = runVMG * distance * cos(TWA) + result.put(entry, Duration.ONE_SECOND.times(getDurationPerNauticalMileAtTrueWindAngleAndSpeed(entry, gybeAngles.get(entry)).asSeconds() * leg.getLength().getNauticalMiles())); } else { // Case 2 - result is given through the laGrange Interpolation, between the Beat and Gybe Angles result.put(entry, getLagrangeAllowancePerTrueWindSpeedAndAngle(entry, leg.getTwa()).times(leg.getLength().getNauticalMiles())); } } - return result; } @@ -243,26 +240,27 @@ public class ORCPerformanceCurveImpl implements Serializable, ORCPerformanceCurv private double[][] createPolarsPerTrueWindSpeed(Speed trueWindSpeed) { ArrayList resultWindAngles = new ArrayList<>(); ArrayList resultAllowances = new ArrayList<>(); - Bearing beatAngle = beatAngles.get(trueWindSpeed); Bearing gybeAngle = gybeAngles.get(trueWindSpeed); + final Bearing TWO_DEGREES = new DegreeBearingImpl(2); + final Bearing MINUS_TWO_DEGREES = new DegreeBearingImpl(-2); - resultWindAngles.add(beatAngle.add(new DegreeBearingImpl(-2)).getDegrees()); - resultAllowances.add(getDurationPerNauticalMileAtTrueWindAngleAndSpeed(trueWindSpeed, beatAngle).divide(Math.cos(Math.toRadians(beatAngle.add(new DegreeBearingImpl(-2)).getDegrees()))).asSeconds()); + resultWindAngles.add(beatAngle.add(MINUS_TWO_DEGREES).getDegrees()); + resultAllowances.add(getDurationPerNauticalMileAtTrueWindAngleAndSpeed(trueWindSpeed, beatAngle).divide(Math.cos(beatAngle.add(MINUS_TWO_DEGREES).getRadians())).asSeconds()); resultWindAngles.add(beatAngle.getDegrees()); - resultAllowances.add(getDurationPerNauticalMileAtTrueWindAngleAndSpeed(trueWindSpeed, beatAngle).asSeconds()); + resultAllowances.add(getDurationPerNauticalMileAtTrueWindAngleAndSpeed(trueWindSpeed, beatAngle).divide(Math.cos(beatAngle.getRadians())).asSeconds()); - for(Bearing twa : ORCCertificateImpl.ALLOWANCES_TRUE_WIND_ANGLES) { - if(twa.compareTo(beatAngle) > 0 && twa.compareTo(gybeAngle) < 0) { + for (Bearing twa : ORCCertificateImpl.ALLOWANCES_TRUE_WIND_ANGLES) { + if (twa.compareTo(beatAngle) > 0 && twa.compareTo(gybeAngle) < 0) { resultWindAngles.add(twa.getDegrees()); resultAllowances.add(getDurationPerNauticalMileAtTrueWindAngleAndSpeed(trueWindSpeed, twa).asSeconds()); } } resultWindAngles.add(gybeAngle.getDegrees()); - resultAllowances.add(getDurationPerNauticalMileAtTrueWindAngleAndSpeed(trueWindSpeed, gybeAngle).asSeconds()); - resultWindAngles.add(gybeAngle.add(new DegreeBearingImpl(2)).getDegrees()); - resultAllowances.add(getDurationPerNauticalMileAtTrueWindAngleAndSpeed(trueWindSpeed, gybeAngle).divide(Math.abs(Math.cos(Math.PI - Math.toRadians(gybeAngle.add(new DegreeBearingImpl(2)).getDegrees())))).asSeconds()); + resultAllowances.add(getDurationPerNauticalMileAtTrueWindAngleAndSpeed(trueWindSpeed, gybeAngle).divide(Math.cos(Math.PI - gybeAngle.getRadians())).asSeconds()); + resultWindAngles.add(gybeAngle.add(TWO_DEGREES).getDegrees()); + resultAllowances.add(getDurationPerNauticalMileAtTrueWindAngleAndSpeed(trueWindSpeed, gybeAngle).divide(Math.cos(Math.PI - Math.toRadians(gybeAngle.add(TWO_DEGREES).getDegrees()))).asSeconds()); return new double[][] {resultWindAngles.stream().mapToDouble(d -> d).toArray(), resultAllowances.stream().mapToDouble(d -> d).toArray()}; }