mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-19 12:15:28 +00:00
added more tests and adjusted the test certificate to make it injective
This commit is contained in:
@@ -159,7 +159,7 @@
|
|||||||
530.3,
|
530.3,
|
||||||
516.8,
|
516.8,
|
||||||
510.9,
|
510.9,
|
||||||
508.9
|
506.9
|
||||||
],
|
],
|
||||||
"R75":[
|
"R75":[
|
||||||
697.1,
|
697.1,
|
||||||
|
|||||||
+17
-4
@@ -70,6 +70,16 @@ public class TestORCPerformanceCurve {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void assertEquals(String message, double a, double b, double accuracy) {
|
||||||
|
try {
|
||||||
|
Assert.assertEquals(message, a, b, accuracy);
|
||||||
|
} catch (AssertionError e) {
|
||||||
|
if (collectErrors) {
|
||||||
|
collector.addError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void initialize() throws IOException, ParseException {
|
public static void initialize() throws IOException, ParseException {
|
||||||
List<ORCPerformanceCurveLeg> legs = new ArrayList<>();
|
List<ORCPerformanceCurveLeg> legs = new ArrayList<>();
|
||||||
@@ -253,15 +263,18 @@ public class TestORCPerformanceCurve {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAllowancesAndImpliedWindForSpecificBins() throws FunctionEvaluationException {
|
public void testAllowancesAndImpliedWindForSpecificBins() throws FunctionEvaluationException, MaxIterationsExceededException {
|
||||||
|
final double highAccuracy = 0.01;
|
||||||
|
final double allowanceAccuracy = 0.1;
|
||||||
final Distance ONE_NAUTICAL_MILE = new NauticalMileDistance(1.0);
|
final Distance ONE_NAUTICAL_MILE = new NauticalMileDistance(1.0);
|
||||||
final ORCCertificate certificateWithSpecificBins = importerWithSpecificBins.getCertificateById("GRA00073GR317");
|
final ORCCertificate certificateWithSpecificBins = importerWithSpecificBins.getCertificateById("GRA00073GR317");
|
||||||
for (final Bearing twa : certificateWithSpecificBins.getTrueWindAngles()) {
|
for (final Bearing twa : certificateWithSpecificBins.getTrueWindAngles()) {
|
||||||
final ORCPerformanceCurveCourse singleLegOneMileCourseWithTwa = createSingleLegCourseWithTwa(twa);
|
final ORCPerformanceCurveCourse singleLegOneMileCourseWithTwa = createSingleLegCourseWithTwa(twa);
|
||||||
final ORCPerformanceCurve performanceCurveSpecificBins = new ORCPerformanceCurveImpl(certificateWithSpecificBins, singleLegOneMileCourseWithTwa);
|
final ORCPerformanceCurve performanceCurveSpecificBins = new ORCPerformanceCurveImpl(certificateWithSpecificBins, singleLegOneMileCourseWithTwa);
|
||||||
for (final Speed tws : certificateWithSpecificBins.getTrueWindSpeeds()) {
|
for (final Speed tws : certificateWithSpecificBins.getTrueWindSpeeds()) {
|
||||||
Assert.assertEquals(certificateWithSpecificBins.getVelocityPredictionPerTrueWindSpeedAndAngle().get(tws).get(twa).getDuration(ONE_NAUTICAL_MILE),
|
final Duration duration = certificateWithSpecificBins.getVelocityPredictionPerTrueWindSpeedAndAngle().get(tws).get(twa).getDuration(ONE_NAUTICAL_MILE);
|
||||||
performanceCurveSpecificBins.getAllowancePerCourse(tws));
|
assertEquals("mismatch for twa "+twa+", tws "+tws, duration.asSeconds(), performanceCurveSpecificBins.getAllowancePerCourse(tws).asSeconds(), allowanceAccuracy);
|
||||||
|
assertEquals("mismatch for twa "+twa+", tws "+tws, tws.getKnots(), performanceCurveSpecificBins.getImpliedWind(duration).getKnots(), highAccuracy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -284,7 +297,7 @@ public class TestORCPerformanceCurve {
|
|||||||
list.add(new ORCPerformanceCurveLegImpl(new NauticalMileDistance(1), new DegreeBearingImpl(180)));
|
list.add(new ORCPerformanceCurveLegImpl(new NauticalMileDistance(1), new DegreeBearingImpl(180)));
|
||||||
ORCPerformanceCurveCourse complexCourse = new ORCPerformanceCurveCourseImpl(list);
|
ORCPerformanceCurveCourse complexCourse = new ORCPerformanceCurveCourseImpl(list);
|
||||||
|
|
||||||
double accuracy = 0.0001;
|
final double accuracy = 0.0001;
|
||||||
ORCCertificate certificateMoana = importerLocal.getCertificateById("GER140772GER5549");
|
ORCCertificate certificateMoana = importerLocal.getCertificateById("GER140772GER5549");
|
||||||
ORCCertificate certificateMilan = importerLocal.getCertificateById("GER166844GER7323");
|
ORCCertificate certificateMilan = importerLocal.getCertificateById("GER166844GER7323");
|
||||||
ORCCertificate certificateTutima = importerLocal.getCertificateById("GER140618GER5609");
|
ORCCertificate certificateTutima = importerLocal.getCertificateById("GER140618GER5609");
|
||||||
|
|||||||
+18
-6
@@ -71,6 +71,16 @@ public class ORCPerformanceCurveImpl implements Serializable, ORCPerformanceCurv
|
|||||||
* allowance in sec/nm.
|
* allowance in sec/nm.
|
||||||
*/
|
*/
|
||||||
private final UnivariateDifferentiableFunction functionImpliedWindInKnotsToAverageSpeedInKnotsForCourse;
|
private final UnivariateDifferentiableFunction functionImpliedWindInKnotsToAverageSpeedInKnotsForCourse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link ORCCertificate#getTrueWindAngles()} from the certificate
|
||||||
|
*/
|
||||||
|
private final Bearing[] trueWindAngles;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link ORCCertificate#getTrueWindSpeeds()} from the certificate
|
||||||
|
*/
|
||||||
|
private final Speed[] trueWindSpeeds;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accepts the simplified polar data, one "column" for each of the defined true wind speeds, where each column is a
|
* Accepts the simplified polar data, one "column" for each of the defined true wind speeds, where each column is a
|
||||||
@@ -79,6 +89,8 @@ public class ORCPerformanceCurveImpl implements Serializable, ORCPerformanceCurv
|
|||||||
*/
|
*/
|
||||||
public ORCPerformanceCurveImpl(ORCCertificate certificate, ORCPerformanceCurveCourse course) throws FunctionEvaluationException {
|
public ORCPerformanceCurveImpl(ORCCertificate certificate, ORCPerformanceCurveCourse course) throws FunctionEvaluationException {
|
||||||
this.course = course;
|
this.course = course;
|
||||||
|
this.trueWindAngles = certificate.getTrueWindAngles();
|
||||||
|
this.trueWindSpeeds = certificate.getTrueWindSpeeds();
|
||||||
functionImpliedWindInKnotsToAverageSpeedInKnotsForCourse = createPerformanceCurve(certificate);
|
functionImpliedWindInKnotsToAverageSpeedInKnotsForCourse = createPerformanceCurve(certificate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,14 +263,14 @@ public class ORCPerformanceCurveImpl implements Serializable, ORCPerformanceCurv
|
|||||||
@Override
|
@Override
|
||||||
public Speed getImpliedWind(Duration durationToCompleteCourse) throws MaxIterationsExceededException, FunctionEvaluationException{
|
public Speed getImpliedWind(Duration durationToCompleteCourse) throws MaxIterationsExceededException, FunctionEvaluationException{
|
||||||
final Speed averageSpeedOnCourse = getCourse().getTotalLength().inTime(durationToCompleteCourse);
|
final Speed averageSpeedOnCourse = getCourse().getTotalLength().inTime(durationToCompleteCourse);
|
||||||
final double[] predictedSpeedsInKnotsForTotalCourseByTrueWindSpeed = Arrays.stream(ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS).mapToDouble
|
final double[] predictedSpeedsInKnotsForTotalCourseByTrueWindSpeed = Arrays.stream(trueWindSpeeds).mapToDouble
|
||||||
(tws->{ return functionImpliedWindInKnotsToAverageSpeedInKnotsForCourse.value(tws.getKnots()); }).toArray();
|
(tws->{ return functionImpliedWindInKnotsToAverageSpeedInKnotsForCourse.value(tws.getKnots()); }).toArray();
|
||||||
final Speed result;
|
final Speed result;
|
||||||
// Corner cases for Allowance > Allowance(20kt) or Allowance < Allowance(6kt)
|
// Corner cases for Allowance > Allowance(20kt) or Allowance < Allowance(6kt)
|
||||||
if (averageSpeedOnCourse.getKnots() >= predictedSpeedsInKnotsForTotalCourseByTrueWindSpeed[predictedSpeedsInKnotsForTotalCourseByTrueWindSpeed.length-1]) {
|
if (averageSpeedOnCourse.getKnots() >= predictedSpeedsInKnotsForTotalCourseByTrueWindSpeed[predictedSpeedsInKnotsForTotalCourseByTrueWindSpeed.length-1]) {
|
||||||
result = ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS[ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS.length-1];
|
result = trueWindSpeeds[trueWindSpeeds.length-1];
|
||||||
} else if (averageSpeedOnCourse.equals(Speed.NULL) || averageSpeedOnCourse.getKnots() <= predictedSpeedsInKnotsForTotalCourseByTrueWindSpeed[0]) {
|
} else if (averageSpeedOnCourse.equals(Speed.NULL) || averageSpeedOnCourse.getKnots() <= predictedSpeedsInKnotsForTotalCourseByTrueWindSpeed[0]) {
|
||||||
result = ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS[0];
|
result = trueWindSpeeds[0];
|
||||||
} else {
|
} else {
|
||||||
// find the polynomial splined function that produces the durationToCompleteCourse within its validity range
|
// find the polynomial splined function that produces the durationToCompleteCourse within its validity range
|
||||||
int i = 1; // skip the auxiliary spline segment from (0.0, 0.0) to (6.0, ...)
|
int i = 1; // skip the auxiliary spline segment from (0.0, 0.0) to (6.0, ...)
|
||||||
@@ -272,7 +284,7 @@ public class ORCPerformanceCurveImpl implements Serializable, ORCPerformanceCurv
|
|||||||
final UnivariateDifferentiableFunction targetZeroFunction = FunctionUtils.add(functionImpliedWindInKnotsToAverageSpeedInKnotsForCourse,
|
final UnivariateDifferentiableFunction targetZeroFunction = FunctionUtils.add(functionImpliedWindInKnotsToAverageSpeedInKnotsForCourse,
|
||||||
FunctionUtils.multiply((UnivariateDifferentiableFunction) averageSpeedInKnots,
|
FunctionUtils.multiply((UnivariateDifferentiableFunction) averageSpeedInKnots,
|
||||||
(UnivariateDifferentiableFunction) new Constant(-1)));
|
(UnivariateDifferentiableFunction) new Constant(-1)));
|
||||||
final double impliedWindSpeedInKnots = newtonSolver.solve(1000, targetZeroFunction, 10 /* knots of true wind speed */);
|
final double impliedWindSpeedInKnots = newtonSolver.solve(1000, targetZeroFunction, 12 /* knots of true wind speed */);
|
||||||
result = new KnotSpeedImpl(impliedWindSpeedInKnots);
|
result = new KnotSpeedImpl(impliedWindSpeedInKnots);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -296,7 +308,7 @@ public class ORCPerformanceCurveImpl implements Serializable, ORCPerformanceCurv
|
|||||||
Bearing trueWindAngle) throws FunctionEvaluationException, IllegalArgumentException {
|
Bearing trueWindAngle) throws FunctionEvaluationException, IllegalArgumentException {
|
||||||
return getLagrangeSpeedPredictionForTrueWindSpeedAndAngle(twaAllowances, beatAngles,
|
return getLagrangeSpeedPredictionForTrueWindSpeedAndAngle(twaAllowances, beatAngles,
|
||||||
beatVMGPredictionPerTrueWindSpeed, runAngles, runVMGPredictionPerTrueWindSpeed, trueWindSpeed,
|
beatVMGPredictionPerTrueWindSpeed, runAngles, runVMGPredictionPerTrueWindSpeed, trueWindSpeed,
|
||||||
trueWindAngle, ORCCertificate.ALLOWANCES_TRUE_WIND_ANGLES);
|
trueWindAngle, trueWindAngles);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Speed getLagrangeSpeedPredictionForTrueWindSpeedAndAngle(Map<Speed, Map<Bearing, Speed>> twaAllowances,
|
public Speed getLagrangeSpeedPredictionForTrueWindSpeedAndAngle(Map<Speed, Map<Bearing, Speed>> twaAllowances,
|
||||||
@@ -341,7 +353,7 @@ public class ORCPerformanceCurveImpl implements Serializable, ORCPerformanceCurv
|
|||||||
*/
|
*/
|
||||||
private LinkedHashMap<Speed, Duration> getAllowancesPerTrueWindSpeedsForCourse() throws ArgumentOutsideDomainException {
|
private LinkedHashMap<Speed, Duration> getAllowancesPerTrueWindSpeedsForCourse() throws ArgumentOutsideDomainException {
|
||||||
final LinkedHashMap<Speed, Duration> result = new LinkedHashMap<>();
|
final LinkedHashMap<Speed, Duration> result = new LinkedHashMap<>();
|
||||||
for (final Speed tws : ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS) {
|
for (final Speed tws : trueWindSpeeds) {
|
||||||
result.put(tws, getAllowancePerCourse(tws));
|
result.put(tws, getAllowancePerCourse(tws));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
Reference in New Issue
Block a user