Merge branch 'master' of /home/trac/git

This commit is contained in:
Generic Wiki User
2018-03-11 14:50:02 +00:00
10 changed files with 152 additions and 58 deletions
@@ -1,5 +1,6 @@
package com.sap.sailing.domain.maneuverdetection.impl;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sse.common.TimePoint;
@@ -14,12 +15,15 @@ public class ManeuverCurveBoundaryExtension {
private final TimePoint extensionTimePoint;
private final SpeedWithBearing speedWithBearingAtExtensionTimePoint;
private final double courseChangeInDegreesWithinExtensionArea;
private final Speed lowestSpeedWithinExtensionArea;
public ManeuverCurveBoundaryExtension(TimePoint extensionTimePoint, SpeedWithBearing speedWithBearingAtExtensionTimePoint,
double totalCourseChangeInDegreesExtension) {
public ManeuverCurveBoundaryExtension(TimePoint extensionTimePoint,
SpeedWithBearing speedWithBearingAtExtensionTimePoint, double totalCourseChangeInDegreesExtension,
Speed lowestSpeedWithinExtensionArea) {
this.extensionTimePoint = extensionTimePoint;
this.speedWithBearingAtExtensionTimePoint = speedWithBearingAtExtensionTimePoint;
this.courseChangeInDegreesWithinExtensionArea = totalCourseChangeInDegreesExtension;
this.lowestSpeedWithinExtensionArea = lowestSpeedWithinExtensionArea;
}
/**
@@ -50,4 +54,11 @@ public class ManeuverCurveBoundaryExtension {
public double getCourseChangeInDegreesWithinExtensionArea() {
return courseChangeInDegreesWithinExtensionArea;
}
/**
* Gets lowest speed within extension area.
*/
public Speed getLowestSpeedWithinExtensionArea() {
return lowestSpeedWithinExtensionArea;
}
}
@@ -1,5 +1,6 @@
package com.sap.sailing.domain.maneuverdetection.impl;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.tracking.ManeuverCurveBoundaries;
import com.sap.sailing.domain.tracking.impl.ManeuverCurveBoundariesImpl;
@@ -17,8 +18,9 @@ public class ManeuverCurveDetails extends ManeuverCurveBoundariesImpl {
public ManeuverCurveDetails(TimePoint timePointBefore, TimePoint timePointAfter, TimePoint timePoint,
SpeedWithBearing speedWithBearingBefore, SpeedWithBearing speedWithBearingAfter,
double directionChangeInDegrees, double maxAngularVelocityInDegreesPerSecond) {
super(timePointBefore, timePointAfter, speedWithBearingBefore, speedWithBearingAfter, directionChangeInDegrees);
double directionChangeInDegrees, double maxAngularVelocityInDegreesPerSecond, Speed lowestSpeed) {
super(timePointBefore, timePointAfter, speedWithBearingBefore, speedWithBearingAfter, directionChangeInDegrees,
lowestSpeed);
this.timePoint = timePoint;
this.maxAngularVelocityInDegreesPerSecond = maxAngularVelocityInDegreesPerSecond;
}
@@ -34,7 +36,7 @@ public class ManeuverCurveDetails extends ManeuverCurveBoundariesImpl {
}
/**
* The maximal angular velocity recorded within the curve which was recorded at {@link #getTimePoint()}.
* Gets the maximal angular velocity recorded within the curve which was recorded at {@link #getTimePoint()}.
*
* @return The maximal angular velocity in degrees per second
*/
@@ -42,8 +44,8 @@ public class ManeuverCurveDetails extends ManeuverCurveBoundariesImpl {
return maxAngularVelocityInDegreesPerSecond;
}
public ManeuverCurveBoundaries extractEnteringAndExistingDetailsOnly() {
return new ManeuverCurveBoundariesImpl(getTimePointBefore(), getTimePointAfter(),
getSpeedWithBearingBefore(), getSpeedWithBearingAfter(), getDirectionChangeInDegrees());
public ManeuverCurveBoundaries extractCurveBoundariesOnly() {
return new ManeuverCurveBoundariesImpl(getTimePointBefore(), getTimePointAfter(), getSpeedWithBearingBefore(),
getSpeedWithBearingAfter(), getDirectionChangeInDegrees(), getLowestSpeed());
}
}
@@ -1,5 +1,6 @@
package com.sap.sailing.domain.maneuverdetection.impl;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.tracking.SpeedWithBearingStepsIterable;
import com.sap.sse.common.TimePoint;
@@ -13,15 +14,16 @@ import com.sap.sse.common.TimePoint;
public class ManeuverCurveDetailsWithBearingSteps extends ManeuverCurveDetails {
private final SpeedWithBearingStepsIterable speedWithBearingSteps;
public ManeuverCurveDetailsWithBearingSteps(TimePoint timePointBefore, TimePoint timePointAfter,
TimePoint timePoint, SpeedWithBearing speedWithBearingBefore, SpeedWithBearing speedWithBearingAfter,
double directionChangeInDegrees, double maxAngularVelocityInDegreesPerSecond, SpeedWithBearingStepsIterable speedWithBearingSteps) {
double directionChangeInDegrees, double maxAngularVelocityInDegreesPerSecond, Speed lowestSpeed,
SpeedWithBearingStepsIterable speedWithBearingSteps) {
super(timePointBefore, timePointAfter, timePoint, speedWithBearingBefore, speedWithBearingAfter,
directionChangeInDegrees, maxAngularVelocityInDegreesPerSecond);
directionChangeInDegrees, maxAngularVelocityInDegreesPerSecond, lowestSpeed);
this.speedWithBearingSteps = speedWithBearingSteps;
}
/**
* Gets the list of bearing steps which was used for computation of curve details.
*
@@ -19,6 +19,7 @@ import com.sap.sailing.domain.common.ManeuverType;
import com.sap.sailing.domain.common.NauticalSide;
import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.Position;
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.Wind;
@@ -433,8 +434,8 @@ public class ManeuverDetectorImpl implements ManeuverDetector {
sideToWhichWaypointWasPassed = maneuverDirection;
// produce an additional mark passing maneuver; continue to analyze to catch jibe sets and kiwi drops
maneuvers.add(new MarkPassingManeuverImpl(ManeuverType.MARK_PASSING, tackAfterManeuver, markPassingPosition,
maneuverLoss, markPassingTimePoint, maneuverMainCurveDetails.extractEnteringAndExistingDetailsOnly(),
maneuverDetails.extractEnteringAndExistingDetailsOnly(),
maneuverLoss, markPassingTimePoint, maneuverMainCurveDetails.extractCurveBoundariesOnly(),
maneuverDetails.extractCurveBoundariesOnly(),
maneuverMainCurveDetails.getMaxAngularVelocityInDegreesPerSecond(), waypointPassed,
sideToWhichWaypointWasPassed));
} else {
@@ -502,14 +503,14 @@ public class ManeuverDetectorImpl implements ManeuverDetector {
refinedPenaltyDetails = computeManeuverDetails(refinedPenaltyMainCurveDetails,
maneuverDetails.getTimePointBefore(), firstPenaltyCircleCompletedAt);
}
maneuverLoss = getManeuverLoss(maneuverDetails.getTimePointBefore(),
maneuverDetails.getTimePoint(), firstPenaltyCircleCompletedAt);
maneuverLoss = getManeuverLoss(maneuverDetails.getTimePointBefore(), maneuverDetails.getTimePoint(),
firstPenaltyCircleCompletedAt);
Position penaltyPosition = competitorTrack.getEstimatedPosition(refinedPenaltyDetails.getTimePoint(),
/* extrapolate */ false);
final Maneuver maneuver = new ManeuverWithStableSpeedAndCourseBoundariesImpl(maneuverType,
tackAfterManeuver, penaltyPosition, maneuverLoss, refinedPenaltyDetails.getTimePoint(),
refinedPenaltyMainCurveDetails.extractEnteringAndExistingDetailsOnly(),
refinedPenaltyDetails.extractEnteringAndExistingDetailsOnly(),
refinedPenaltyMainCurveDetails.extractCurveBoundariesOnly(),
refinedPenaltyDetails.extractCurveBoundariesOnly(),
refinedPenaltyMainCurveDetails.getMaxAngularVelocityInDegreesPerSecond());
maneuvers.add(maneuver);
// after we've "consumed" one tack and one jibe, recursively find more maneuvers if tacks and/or jibes
@@ -523,12 +524,12 @@ public class ManeuverDetectorImpl implements ManeuverDetector {
final Maneuver maneuver;
if (numberOfTacks > 0 || numberOfJibes > 0) {
maneuverType = numberOfTacks > 0 ? ManeuverType.TACK : ManeuverType.JIBE;
maneuverLoss = getManeuverLoss(maneuverDetails.getTimePointBefore(),
maneuverDetails.getTimePoint(), maneuverDetails.getTimePointAfter());
maneuverLoss = getManeuverLoss(maneuverDetails.getTimePointBefore(), maneuverDetails.getTimePoint(),
maneuverDetails.getTimePointAfter());
maneuver = new ManeuverWithStableSpeedAndCourseBoundariesImpl(maneuverType, tackAfterManeuver,
maneuverPosition, maneuverLoss, maneuverDetails.getTimePoint(),
maneuverMainCurveDetails.extractEnteringAndExistingDetailsOnly(),
maneuverDetails.extractEnteringAndExistingDetailsOnly(),
maneuverMainCurveDetails.extractCurveBoundariesOnly(),
maneuverDetails.extractCurveBoundariesOnly(),
maneuverMainCurveDetails.getMaxAngularVelocityInDegreesPerSecond());
} else if (wind != null) {
// heading up or bearing away
@@ -543,18 +544,18 @@ public class ManeuverDetectorImpl implements ManeuverDetector {
// either HEAD_UP or BEAR_AWAY
maneuver = new ManeuverWithMainCurveBoundariesImpl(maneuverType, tackAfterManeuver,
maneuverPosition, maneuverLoss, maneuverDetails.getTimePoint(),
maneuverMainCurveDetails.extractEnteringAndExistingDetailsOnly(),
maneuverDetails.extractEnteringAndExistingDetailsOnly(),
maneuverMainCurveDetails.extractCurveBoundariesOnly(),
maneuverDetails.extractCurveBoundariesOnly(),
maneuverMainCurveDetails.getMaxAngularVelocityInDegreesPerSecond());
} else {
// no wind information; marking as UNKNOWN
maneuverType = ManeuverType.UNKNOWN;
maneuverLoss = getManeuverLoss(maneuverDetails.getTimePointBefore(),
maneuverDetails.getTimePoint(), maneuverDetails.getTimePointAfter());
maneuver = new ManeuverWithStableSpeedAndCourseBoundariesImpl(maneuverType, tackAfterManeuver, maneuverPosition,
maneuverLoss, maneuverDetails.getTimePoint(),
maneuverMainCurveDetails.extractEnteringAndExistingDetailsOnly(),
maneuverDetails.extractEnteringAndExistingDetailsOnly(),
maneuverLoss = getManeuverLoss(maneuverDetails.getTimePointBefore(), maneuverDetails.getTimePoint(),
maneuverDetails.getTimePointAfter());
maneuver = new ManeuverWithStableSpeedAndCourseBoundariesImpl(maneuverType, tackAfterManeuver,
maneuverPosition, maneuverLoss, maneuverDetails.getTimePoint(),
maneuverMainCurveDetails.extractCurveBoundariesOnly(),
maneuverDetails.extractCurveBoundariesOnly(),
maneuverMainCurveDetails.getMaxAngularVelocityInDegreesPerSecond());
}
maneuvers.add(maneuver);
@@ -582,21 +583,29 @@ public class ManeuverDetectorImpl implements ManeuverDetector {
if (speedWhenSpeedStartedToDrop != null) {
SpeedWithBearing speedAfterManeuver = track.getEstimatedSpeed(timePointWhenSpeedLevelledOffAfterManeuver);
if (speedAfterManeuver != null) {
// For upwind/downwind legs, find the mean course between inbound and outbound course and project actual and
// For upwind/downwind legs, find the mean course between inbound and outbound course and project actual
// and
// extrapolated positions onto it:
Bearing middleManeuverAngle = speedWhenSpeedStartedToDrop.getBearing().middle(speedAfterManeuver.getBearing());
// extrapolate maximum speed before maneuver to time point of maximum speed after maneuver and project resulting position
// onto the average maneuver course; compare to the projected position actually reached at the time point of maximum speed after
Bearing middleManeuverAngle = speedWhenSpeedStartedToDrop.getBearing()
.middle(speedAfterManeuver.getBearing());
// extrapolate maximum speed before maneuver to time point of maximum speed after maneuver and project
// resulting position
// onto the average maneuver course; compare to the projected position actually reached at the time
// point of maximum speed after
// maneuver:
Position positionWhenSpeedStartedToDrop = track.getEstimatedPosition(timePointWhenSpeedStartedToDrop, /* extrapolate */ false);
Position extrapolatedPositionAtTimePointOfMaxSpeedAfterManeuver =
speedWhenSpeedStartedToDrop.travelTo(positionWhenSpeedStartedToDrop, timePointWhenSpeedStartedToDrop, timePointWhenSpeedLevelledOffAfterManeuver);
Position actualPositionAtTimePointOfMaxSpeedAfterManeuver = track.getEstimatedPosition(timePointWhenSpeedLevelledOffAfterManeuver, /* extrapolate */ false);
Position projectedExtrapolatedPositionAtTimePointOfMaxSpeedAfterManeuver =
extrapolatedPositionAtTimePointOfMaxSpeedAfterManeuver.projectToLineThrough(positionWhenSpeedStartedToDrop, middleManeuverAngle);
Position projectedActualPositionAtTimePointOfMaxSpeedAfterManeuver =
actualPositionAtTimePointOfMaxSpeedAfterManeuver.projectToLineThrough(positionWhenSpeedStartedToDrop, middleManeuverAngle);
result = projectedActualPositionAtTimePointOfMaxSpeedAfterManeuver.getDistance(projectedExtrapolatedPositionAtTimePointOfMaxSpeedAfterManeuver);
Position positionWhenSpeedStartedToDrop = track.getEstimatedPosition(timePointWhenSpeedStartedToDrop,
/* extrapolate */ false);
Position extrapolatedPositionAtTimePointOfMaxSpeedAfterManeuver = speedWhenSpeedStartedToDrop.travelTo(
positionWhenSpeedStartedToDrop, timePointWhenSpeedStartedToDrop,
timePointWhenSpeedLevelledOffAfterManeuver);
Position actualPositionAtTimePointOfMaxSpeedAfterManeuver = track
.getEstimatedPosition(timePointWhenSpeedLevelledOffAfterManeuver, /* extrapolate */ false);
Position projectedExtrapolatedPositionAtTimePointOfMaxSpeedAfterManeuver = extrapolatedPositionAtTimePointOfMaxSpeedAfterManeuver
.projectToLineThrough(positionWhenSpeedStartedToDrop, middleManeuverAngle);
Position projectedActualPositionAtTimePointOfMaxSpeedAfterManeuver = actualPositionAtTimePointOfMaxSpeedAfterManeuver
.projectToLineThrough(positionWhenSpeedStartedToDrop, middleManeuverAngle);
result = projectedActualPositionAtTimePointOfMaxSpeedAfterManeuver
.getDistance(projectedExtrapolatedPositionAtTimePointOfMaxSpeedAfterManeuver);
} else {
result = null;
}
@@ -706,7 +715,7 @@ public class ManeuverDetectorImpl implements ManeuverDetector {
maneuverMainCurveDetails.getSpeedWithBearingAfter(),
maneuverMainCurveDetails.getDirectionChangeInDegrees(),
maneuverMainCurveDetails.getMaxAngularVelocityInDegreesPerSecond(),
maneuverMainCurveSpeedWithBearingSteps);
maneuverMainCurveDetails.getLowestSpeed(), maneuverMainCurveSpeedWithBearingSteps);
}
/**
@@ -754,11 +763,20 @@ public class ManeuverDetectorImpl implements ManeuverDetector {
double totalCourseChangeInDegrees = beforeManeuverSectionExtension.getCourseChangeInDegreesWithinExtensionArea()
+ maneuverMainCurveDetails.getDirectionChangeInDegrees()
+ afterManeuverSectionExtension.getCourseChangeInDegreesWithinExtensionArea();
Speed lowestSpeed = maneuverMainCurveDetails.getLowestSpeed();
if (lowestSpeed == null || beforeManeuverSectionExtension.getLowestSpeedWithinExtensionArea() != null
&& lowestSpeed.compareTo(beforeManeuverSectionExtension.getLowestSpeedWithinExtensionArea()) > 0) {
lowestSpeed = beforeManeuverSectionExtension.getLowestSpeedWithinExtensionArea();
}
if (lowestSpeed == null || afterManeuverSectionExtension.getLowestSpeedWithinExtensionArea() != null
&& lowestSpeed.compareTo(afterManeuverSectionExtension.getLowestSpeedWithinExtensionArea()) > 0) {
lowestSpeed = afterManeuverSectionExtension.getLowestSpeedWithinExtensionArea();
}
return new ManeuverCurveDetails(beforeManeuverSectionExtension.getExtensionTimePoint(),
afterManeuverSectionExtension.getExtensionTimePoint(), maneuverMainCurveDetails.getTimePoint(),
beforeManeuverSectionExtension.getSpeedWithBearingAtExtensionTimePoint(),
afterManeuverSectionExtension.getSpeedWithBearingAtExtensionTimePoint(), totalCourseChangeInDegrees,
maneuverMainCurveDetails.getMaxAngularVelocityInDegreesPerSecond());
maneuverMainCurveDetails.getMaxAngularVelocityInDegreesPerSecond(), lowestSpeed);
}
/**
@@ -807,6 +825,7 @@ public class ManeuverDetectorImpl implements ManeuverDetector {
}
TimePoint stableBearingAnalysisUntil = maneuverStart == null ? maneuverMainCurveDetails.getTimePointBefore()
: maneuverStart.getExtensionTimePoint();
Speed lowestSpeed = maneuverStart == null ? null : maneuverStart.getLowestSpeedWithinExtensionArea();
double courseChangeSinceManeuverMainCurveInDegrees = maneuverStart == null ? 0
: maneuverStart.getCourseChangeInDegreesWithinExtensionArea();
stepsToAnalyze = getSpeedWithBearingStepsWithinTimeRange(stepsToAnalyze, earliestTimePointForSpeedTrendAnalysis,
@@ -818,14 +837,19 @@ public class ManeuverDetectorImpl implements ManeuverDetector {
maneuverStart = stableBearingExtension;
courseChangeSinceManeuverMainCurveInDegrees += stableBearingExtension
.getCourseChangeInDegreesWithinExtensionArea();
if (lowestSpeed == null
|| lowestSpeed.compareTo(stableBearingExtension.getLowestSpeedWithinExtensionArea()) > 0) {
lowestSpeed = stableBearingExtension.getLowestSpeedWithinExtensionArea();
}
}
return maneuverStart != null
? new ManeuverCurveBoundaryExtension(maneuverStart.getExtensionTimePoint(),
maneuverStart.getSpeedWithBearingAtExtensionTimePoint(),
courseChangeSinceManeuverMainCurveInDegrees
+ maneuverStart.getCourseChangeInDegreesWithinExtensionArea())
+ maneuverStart.getCourseChangeInDegreesWithinExtensionArea(),
lowestSpeed)
: new ManeuverCurveBoundaryExtension(maneuverMainCurveDetails.getTimePointBefore(),
maneuverMainCurveDetails.getSpeedWithBearingBefore(), 0);
maneuverMainCurveDetails.getSpeedWithBearingBefore(), 0, null);
}
private boolean isCourseChangeLimitExceededForCurveExtension(
@@ -887,6 +911,7 @@ public class ManeuverDetectorImpl implements ManeuverDetector {
}
TimePoint stableBearingAnalysisFrom = maneuverEnd == null ? maneuverMainCurveDetails.getTimePointAfter()
: maneuverEnd.getExtensionTimePoint();
Speed lowestSpeed = maneuverEnd == null ? null : maneuverEnd.getLowestSpeedWithinExtensionArea();
double courseChangeSinceManeuverMainCurveInDegrees = maneuverEnd == null ? 0
: maneuverEnd.getCourseChangeInDegreesWithinExtensionArea();
stepsToAnalyze = getSpeedWithBearingStepsWithinTimeRange(stepsToAnalyze, stableBearingAnalysisFrom,
@@ -898,11 +923,17 @@ public class ManeuverDetectorImpl implements ManeuverDetector {
maneuverEnd = stableBearingExtension;
courseChangeSinceManeuverMainCurveInDegrees += stableBearingExtension
.getCourseChangeInDegreesWithinExtensionArea();
if (lowestSpeed == null
|| lowestSpeed.compareTo(stableBearingExtension.getLowestSpeedWithinExtensionArea()) > 0) {
lowestSpeed = stableBearingExtension.getLowestSpeedWithinExtensionArea();
}
}
return maneuverEnd != null ? new ManeuverCurveBoundaryExtension(maneuverEnd.getExtensionTimePoint(),
maneuverEnd.getSpeedWithBearingAtExtensionTimePoint(), courseChangeSinceManeuverMainCurveInDegrees)
return maneuverEnd != null
? new ManeuverCurveBoundaryExtension(maneuverEnd.getExtensionTimePoint(),
maneuverEnd.getSpeedWithBearingAtExtensionTimePoint(),
courseChangeSinceManeuverMainCurveInDegrees, lowestSpeed)
: new ManeuverCurveBoundaryExtension(maneuverMainCurveDetails.getTimePointAfter(),
maneuverMainCurveDetails.getSpeedWithBearingAfter(), 0);
maneuverMainCurveDetails.getSpeedWithBearingAfter(), 0, null);
}
protected Duration getMaxDurationForAfterManeuverSectionExtension(Duration approximateManeuverDuration) {
@@ -947,6 +978,7 @@ public class ManeuverDetectorImpl implements ManeuverDetector {
SpeedWithBearingStep stepWithMaxSpeed = null;
double courseChangeSinceMainCurveBeforeSpeedMaximumInDegrees = 0;
double courseChangeAfterStepWithSpeedMaximum = 0;
Speed lowestSpeed = null;
for (SpeedWithBearingStep speedWithBearingStep : finalStepsToAnalyze) {
courseChangeAfterStepWithSpeedMaximum += speedWithBearingStep.getCourseChangeInDegrees();
@@ -963,6 +995,9 @@ public class ManeuverDetectorImpl implements ManeuverDetector {
courseChangeSinceMainCurveBeforeSpeedMaximumInDegrees += courseChangeAfterStepWithSpeedMaximum;
courseChangeAfterStepWithSpeedMaximum = 0;
}
if (lowestSpeed == null || lowestSpeed.compareTo(speedWithBearingStep.getSpeedWithBearing()) > 0) {
lowestSpeed = speedWithBearingStep.getSpeedWithBearing();
}
}
previousSpeedInKnots = speedInKnots;
}
@@ -974,7 +1009,8 @@ public class ManeuverDetectorImpl implements ManeuverDetector {
}
return stepWithMaxSpeed == null ? null
: new ManeuverCurveBoundaryExtension(stepWithMaxSpeed.getTimePoint(),
stepWithMaxSpeed.getSpeedWithBearing(), courseChangeSinceMainCurveBeforeSpeedMaximumInDegrees);
stepWithMaxSpeed.getSpeedWithBearing(), courseChangeSinceMainCurveBeforeSpeedMaximumInDegrees,
lowestSpeed);
}
private Iterable<SpeedWithBearingStep> cloneAndReverseIterable(SpeedWithBearingStepsIterable stepsToAnalyze) {
@@ -1014,6 +1050,7 @@ public class ManeuverDetectorImpl implements ManeuverDetector {
SpeedWithBearingStep previousStep = null;
SpeedWithBearingStep stepUntilStableBearing = null;
double courseChangeUntilStepWithStableBearingInDegrees = 0;
Speed lowestSpeed = null;
for (SpeedWithBearingStep currentStep : finalStepsToAnalyze) {
if (previousStep != null) {
@@ -1024,6 +1061,9 @@ public class ManeuverDetectorImpl implements ManeuverDetector {
break;
}
}
if (lowestSpeed == null || lowestSpeed.compareTo(currentStep.getSpeedWithBearing()) > 0) {
lowestSpeed = currentStep.getSpeedWithBearing();
}
courseChangeUntilStepWithStableBearingInDegrees += currentStep.getCourseChangeInDegrees();
previousStep = currentStep;
}
@@ -1032,7 +1072,8 @@ public class ManeuverDetectorImpl implements ManeuverDetector {
}
return stepUntilStableBearing == null ? null
: new ManeuverCurveBoundaryExtension(stepUntilStableBearing.getTimePoint(),
stepUntilStableBearing.getSpeedWithBearing(), courseChangeUntilStepWithStableBearingInDegrees);
stepUntilStableBearing.getSpeedWithBearing(), courseChangeUntilStepWithStableBearingInDegrees,
lowestSpeed);
}
/**
@@ -1058,6 +1099,7 @@ public class ManeuverDetectorImpl implements ManeuverDetector {
double maxCourseChangeInDegrees = 0;
double currentCourseChangeInDegrees = 0;
double maxAngularVelocityInDegreesPerSecond = 0;
Speed lowestSpeed = null;
TimePoint maneuverTimePoint = null;
TimePoint previousTimePoint = null;
// Refine the time point before and after maneuver by checking whether the total course changed before maneuver
@@ -1079,7 +1121,7 @@ public class ManeuverDetectorImpl implements ManeuverDetector {
refinedTimePointAfterManeuver = timePoint;
refinedSpeedWithBearingAfterManeuver = entry.getSpeedWithBearing();
}
// Check whether the course change is performed in the target direction of maneuver. If yes, check consider
// Check whether the course change is performed in the target direction of maneuver. If yes, consider
// the step to locate the maneuver time point with the highest angular velocity within main curve.
if (0 < currentCourseChangeInDegrees * totalCourseChangeSignum) {
if (maxAngularVelocityInDegreesPerSecond < entry.getAngularVelocityInDegreesPerSecond()) {
@@ -1088,6 +1130,9 @@ public class ManeuverDetectorImpl implements ManeuverDetector {
maneuverTimePoint = previousTimePoint.plus(durationFromPreviousStep.divide(2.0));
}
}
if (lowestSpeed == null || lowestSpeed.compareTo(entry.getSpeedWithBearing()) > 0) {
lowestSpeed = entry.getSpeedWithBearing();
}
// If the direction sign does not match, or the angular velocity at the beginning of the curve is nearly
// zero => cut the bearing step from the left
if (0 >= currentCourseChangeInDegrees * totalCourseChangeSignum
@@ -1102,6 +1147,7 @@ public class ManeuverDetectorImpl implements ManeuverDetector {
angularVelocityMinimumReachedAtMainCurveBeginning = false;
maneuverTimePoint = null;
maxAngularVelocityInDegreesPerSecond = 0;
lowestSpeed = entry.getSpeedWithBearing();
} else {
angularVelocityMinimumReachedAtMainCurveBeginning = true;
}
@@ -1121,7 +1167,7 @@ public class ManeuverDetectorImpl implements ManeuverDetector {
ManeuverCurveDetails maneuverEnteringAndExitingDetails = new ManeuverCurveDetails(
refinedTimePointBeforeManeuver, refinedTimePointAfterManeuver, maneuverTimePoint,
refinedSpeedWithBearingBeforeManeuver, refinedSpeedWithBearingAfterManeuver, maxCourseChangeInDegrees,
maxAngularVelocityInDegreesPerSecond);
maxAngularVelocityInDegreesPerSecond, lowestSpeed);
return maneuverEnteringAndExitingDetails;
}
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.tracking;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.ManeuverType;
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.tracking.GPSFix;
@@ -81,7 +82,7 @@ public interface Maneuver extends GPSFix {
* Gets time points and speeds with bearings before and after the maneuver, such that the speed and course before
* and after the maneuver are considered as stable.
*
* @return Entering and existing details of maneuver section, with stable speed and bearing before and after that
* @return Entering and exiting details of maneuver section, with stable speed and bearing before and after that
* section
* @see Maneuver
*/
@@ -93,7 +94,7 @@ public interface Maneuver extends GPSFix {
* {@link #getManeuverCurveWithStableSpeedAndCourseBoundaries()}. The former is considered for HEAD_UP and BEAR_AWAY
* maneuvers, whereas the latter is considered for the remainder.
*
* @return Entering and existing details of maneuver
* @return Entering and exiting details of maneuver
*/
ManeuverCurveBoundaries getManeuverBoundaries();
@@ -135,4 +136,9 @@ public interface Maneuver extends GPSFix {
@Statistic(messageKey = "DirectionChange", resultDecimals = 2, ordinal = 2)
double getDirectionChangeInDegrees();
/**
* Gets lowest speed recorded within {@link #getManeuverBoundaries()}.
*/
Speed getLowestSpeed();
}
@@ -1,5 +1,6 @@
package com.sap.sailing.domain.tracking;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sse.common.TimePoint;
@@ -48,4 +49,11 @@ public interface ManeuverCurveBoundaries {
*/
double getDirectionChangeInDegrees();
/**
* Gets the lowest speed sailed within the maneuver curve.
*
* @return The lowest speed within maneuver curve
*/
Speed getLowestSpeed();
}
@@ -1,5 +1,6 @@
package com.sap.sailing.domain.tracking.impl;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.tracking.ManeuverCurveBoundaries;
import com.sap.sse.common.TimePoint;
@@ -14,15 +15,17 @@ public class ManeuverCurveBoundariesImpl implements ManeuverCurveBoundaries {
private final SpeedWithBearing speedWithBearingBefore;
private final SpeedWithBearing speedWithBearingAfter;
private final double directionChangeInDegrees;
private final Speed lowestSpeed;
public ManeuverCurveBoundariesImpl(TimePoint timePointBefore, TimePoint timePointAfter,
SpeedWithBearing speedWithBearingBefore, SpeedWithBearing speedWithBearingAfter,
double directionChangeInDegrees) {
double directionChangeInDegrees, Speed lowestSpeed) {
this.timePointBefore = timePointBefore;
this.timePointAfter = timePointAfter;
this.speedWithBearingBefore = speedWithBearingBefore;
this.speedWithBearingAfter = speedWithBearingAfter;
this.directionChangeInDegrees = directionChangeInDegrees;
this.lowestSpeed = lowestSpeed;
}
@Override
@@ -50,10 +53,16 @@ public class ManeuverCurveBoundariesImpl implements ManeuverCurveBoundaries {
return directionChangeInDegrees;
}
@Override
public Speed getLowestSpeed() {
return lowestSpeed;
}
@Override
public String toString() {
return "Starting at time point " + timePointBefore + ", ending at time point " + timePointAfter
+ ". Speed before curve " + speedWithBearingBefore + " speed after curve " + speedWithBearingAfter
+ ". Course changed by " + directionChangeInDegrees + "deg.";
+ ". Lowest speed within curve: " + lowestSpeed + ". Course changed by " + directionChangeInDegrees
+ "deg.";
}
}
@@ -3,6 +3,7 @@ package com.sap.sailing.domain.tracking.impl;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.ManeuverType;
import com.sap.sailing.domain.common.Position;
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.tracking.impl.AbstractGPSFixImpl;
@@ -89,6 +90,11 @@ public abstract class ManeuverImpl extends AbstractGPSFixImpl implements Maneuve
return getManeuverBoundaries().getSpeedWithBearingAfter();
}
@Override
public Speed getLowestSpeed() {
return getManeuverBoundaries().getLowestSpeed();
}
@Override
public String toString() {
return super.toString() + " " + type + " on new tack " + newTack + " on position " + position
@@ -20,6 +20,7 @@ public class ManeuverCurveBoundariesJsonSerializer implements JsonSerializer<Man
public static final String COG_AFTER_IN_TRUE_DEGREES = "cogAfterInTrueDegrees";
public static final String DIRECTION_CHANGE_IN_DEGREES = "directionChangeInDegrees";
public static final String DURATION_IN_MILLIS = "durationInMillis";
public static final String LOWEST_SPEED_IN_KNOTS = "lowestSpeedInKnots";
@Override
public JSONObject serialize(ManeuverCurveBoundaries curveBoundaries) {
@@ -36,6 +37,7 @@ public class ManeuverCurveBoundariesJsonSerializer implements JsonSerializer<Man
: curveBoundaries.getSpeedWithBearingAfter().getBearing().getDegrees());
result.put(DIRECTION_CHANGE_IN_DEGREES, curveBoundaries.getDirectionChangeInDegrees());
result.put(DURATION_IN_MILLIS, curveBoundaries.getTimePointBefore().until(curveBoundaries.getTimePointAfter()).asMillis());
result.put(LOWEST_SPEED_IN_KNOTS, curveBoundaries.getLowestSpeed().getKnots());
return result;
}
@@ -16,6 +16,7 @@ public class ManeuverJsonSerializer implements JsonSerializer<Maneuver> {
public static final String MANEUVER_LOSS = "maneuverLoss";
public static final String POSITION_AND_TIME = "positionAndTime";
public static final String MAX_ANGULAR_VELOCITY_IN_DEGREES_PER_SECOND = "maxAngularVelocityInDegreesPerSecond";
public static final String LOWEST_SPEED_IN_KNOTS = "lowestSpeedInKnots";
private final GPSFixJsonSerializer gpsFixSerializer;
private final DistanceJsonSerializer distanceSerializer;
@@ -44,6 +45,7 @@ public class ManeuverJsonSerializer implements JsonSerializer<Maneuver> {
maneuver.getManeuverLoss() == null ? null : distanceSerializer.serialize(maneuver.getManeuverLoss()));
result.put(POSITION_AND_TIME, gpsFixSerializer.serialize(maneuver));
result.put(MAX_ANGULAR_VELOCITY_IN_DEGREES_PER_SECOND, maneuver.getMaxAngularVelocityInDegreesPerSecond());
result.put(LOWEST_SPEED_IN_KNOTS, maneuver.getLowestSpeed().getKnots());
return result;
}
}