resolved the deep maneuver analysis recursion by setting strict time bounds for start/end of maneuver in case of recursions

This commit is contained in:
Axel Uhl committed 2014-05-16 00:57:01 +02:00
1 parent efe9fb6295
commit d1abac0c8d
2 files changed
+66 -28

No files matched your search

@@ -59,7 +59,7 @@ public class ManeuverAnalysisQingdao2014Test extends AbstractManeuverDetectionTe
* a penalty although between the jibe and the tack there is a mark passing
*/
@Test
public void testManeuversForFindelCriticalDetection() throws ParseException, NoWindException {
public void testManeuversForAlinghiCriticalDetection() throws ParseException, NoWindException {
Competitor competitor = getCompetitorByName("Alinghi");
assertNotNull(competitor);
Date fromDate = dateFormat.parse("05/01/2014-09:07:00");
@@ -5,6 +5,7 @@ import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.Date;
@@ -1927,7 +1928,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
}
if (extendedTo != null) {
try {
List<Maneuver> extendedResultForCache = detectManeuvers(competitor, extendedFrom, extendedTo);
List<Maneuver> extendedResultForCache = detectManeuvers(competitor, extendedFrom, extendedTo, /* ignoreMarkPassings */ false);
result = new Triple<TimePoint, TimePoint, List<Maneuver>>(extendedFrom, extendedTo,
extendedResultForCache);
} catch (NoWindException ex) {
@@ -1949,19 +1950,26 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
}
/**
* @param ignoreMarkPassings
* When <code>true</code>, no {@link ManeuverType#MARK_PASSING} maneuvers will be identified, and the
* fact that a mark passing would split up what else may be a penalty circle is ignored. This is helpful
* for recursive calls, e.g., after identifying a tack and a jibe around a mark passing and trying to
* identify for the time before and after the mark passing which maneuvers exist on which side of the
* passing.
* @return a valid but possibly empty list
* @see #detectManeuvers(Competitor, List)
* @see #detectManeuvers(Competitor, List, boolean, TimePoint, TimePoint)
*/
private List<Maneuver> detectManeuvers(Competitor competitor, TimePoint from, TimePoint to) throws NoWindException {
private List<Maneuver> detectManeuvers(Competitor competitor, TimePoint from, TimePoint to, boolean ignoreMarkPassings) throws NoWindException {
return detectManeuvers(
competitor, approximate(competitor, getRace().getBoatClass().getMaximumDistanceForCourseApproximation(),
from, to));
from, to), ignoreMarkPassings, from, to);
}
/**
* Tries to detect maneuvers on the <code>competitor</code>'s track based on a number of approximating fixes. The
* fixes contain bearing information, but this is not the bearing leading to the next approximation fix but the
* bearing the boat had at the time of the approximating fix which is taken from the original track.<p>
* bearing the boat had at the time of the approximating fix which is taken from the original track.
* <p>
*
* The time period assumed for a maneuver duration is taken from the
* {@link BoatClass#getApproximateManeuverDurationInMilliseconds() boat class}. If no maneuver is detected, an empty
@@ -1972,10 +1980,28 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
* timely distance than {@link #getApproximateManeuverDurationInMilliseconds()} (including single course changes
* that have no surrounding other course changes to group) are grouped into one {@link Maneuver}.
*
* @param ignoreMarkPassings
* When <code>true</code>, no {@link ManeuverType#MARK_PASSING} maneuvers will be identified, and the
* fact that a mark passing would split up what else may be a penalty circle is ignored. This is helpful
* for recursive calls, e.g., after identifying a tack and a jibe around a mark passing and trying to
* identify for the time before and after the mark passing which maneuvers exist on which side of the
* passing.
* @param earliestManeuverStart
* maneuver start will not be before this time point; if a maneuver is found whose time point is at or
* after this time point, no matter how close it is, its start regarding speed and course into the
* maneuver and the leg before the maneuver is not taken from an earlier time point, even if half the
* maneuver duration before the maneuver time point were before this time point.
* @param latestManeuverEnd
* maneuver end will not be after this time point; if a maneuver is found whose time point is at or
* before this time point, no matter how close it is, its end regarding speed and course out of the
* maneuver and the leg after the maneuver is not taken from a later time point, even if half the
* maneuver duration after the maneuver time point were after this time point.
*
* @return an empty list if no maneuver is detected for <code>competitor</code> between <code>from</code> and
* <code>to</code>, or else the list of maneuvers detected.
*/
private List<Maneuver> detectManeuvers(Competitor competitor, List<GPSFixMoving> approximatingFixesToAnalyze)
private List<Maneuver> detectManeuvers(Competitor competitor, List<GPSFixMoving> approximatingFixesToAnalyze,
boolean ignoreMarkPassings, TimePoint earliestManeuverStart, TimePoint latestManeuverEnd)
throws NoWindException {
List<Maneuver> result = new ArrayList<Maneuver>();
if (approximatingFixesToAnalyze.size() > 2) {
@@ -2004,7 +2030,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
// start new list
List<Maneuver> maneuvers = groupChangesInSameDirectionIntoManeuvers(competitor,
speedWithBearingOnApproximationAtBeginningOfUnidirectionalCourseChanges,
courseChangeSequenceInSameDirection);
courseChangeSequenceInSameDirection, ignoreMarkPassings, earliestManeuverStart, latestManeuverEnd);
result.addAll(maneuvers);
courseChangeSequenceInSameDirection.clear();
speedWithBearingOnApproximationAtBeginningOfUnidirectionalCourseChanges = speedWithBearingOnApproximationFromPreviousToCurrent;
@@ -2017,7 +2043,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
if (!courseChangeSequenceInSameDirection.isEmpty()) {
result.addAll(groupChangesInSameDirectionIntoManeuvers(competitor,
speedWithBearingOnApproximationAtBeginningOfUnidirectionalCourseChanges,
courseChangeSequenceInSameDirection));
courseChangeSequenceInSameDirection, ignoreMarkPassings, earliestManeuverStart, latestManeuverEnd));
}
}
return result;
@@ -2068,18 +2094,34 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
* direction which may, e.g., represent a penalty circle or a mark rounding maneuver. As the maneuver's time point,
* the average time point of the course changes that went into the maneuver construction is used.
* <p>
*
* @param speedWithBearingOnApproximationAtBeginning
* the speed/bearing before the first approximating fix passed in
* <code>courseChangeSequenceInSameDirection</code>
* @param courseChangeSequenceInSameDirection
* all expected to have equal {@link CourseChange#to()} values
* @param ignoreMarkPassings
* When <code>true</code>, no {@link ManeuverType#MARK_PASSING} maneuvers will be identified, and the
* fact that a mark passing would split up what else may be a penalty circle is ignored. This is helpful
* for recursive calls, e.g., after identifying a tack and a jibe around a mark passing and trying to
* identify for the time before and after the mark passing which maneuvers exist on which side of the
* passing.
* @param earliestManeuverStart
* maneuver start will not be before this time point; if a maneuver is found whose time point is at or
* after this time point, no matter how close it is, its start regarding speed and course into the
* maneuver and the leg before the maneuver is not taken from an earlier time point, even if half the
* maneuver duration before the maneuver time point were before this time point.
* @param latestManeuverEnd
* maneuver end will not be after this time point; if a maneuver is found whose time point is at or
* before this time point, no matter how close it is, its end regarding speed and course out of the
* maneuver and the leg after the maneuver is not taken from a later time point, even if half the
* maneuver duration after the maneuver time point were after this time point.
*
* @return a non-<code>null</code> list
*/
private List<Maneuver> groupChangesInSameDirectionIntoManeuvers(Competitor competitor,
SpeedWithBearing speedWithBearingOnApproximationAtBeginning,
List<Pair<GPSFixMoving, CourseChange>> courseChangeSequenceInSameDirection) throws NoWindException {
List<Pair<GPSFixMoving, CourseChange>> courseChangeSequenceInSameDirection, boolean ignoreMarkPassings,
TimePoint earliestManeuverStart, TimePoint latestManeuverEnd) throws NoWindException {
List<Maneuver> result = new ArrayList<Maneuver>();
List<Pair<GPSFixMoving, CourseChange>> group = new ArrayList<Pair<GPSFixMoving, CourseChange>>();
if (!courseChangeSequenceInSameDirection.isEmpty()) {
@@ -2110,7 +2152,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
// if next is more then approximate maneuver duration later or further apart than two hull lengths,
// turn the current group into a maneuver and add to result
Util.addAll(createManeuverFromGroupOfCourseChanges(competitor, beforeGroupOnApproximation,
group, afterCurrentCourseChange, totalCourseChangeInDegrees, totalMilliseconds), result);
group, afterCurrentCourseChange, totalCourseChangeInDegrees, totalMilliseconds, earliestManeuverStart, latestManeuverEnd), result);
group.clear();
totalCourseChangeInDegrees = 0.0;
totalMilliseconds = 0l;
@@ -2126,7 +2168,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
} while (iter.hasNext());
if (!group.isEmpty()) {
Util.addAll(createManeuverFromGroupOfCourseChanges(competitor, beforeGroupOnApproximation, group,
afterCurrentCourseChange, totalCourseChangeInDegrees, totalMilliseconds), result);
afterCurrentCourseChange, totalCourseChangeInDegrees, totalMilliseconds, earliestManeuverStart, latestManeuverEnd), result);
}
}
return result;
@@ -2135,19 +2177,15 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
private Iterable<Maneuver> createManeuverFromGroupOfCourseChanges(Competitor competitor,
SpeedWithBearing speedWithBearingOnApproximationAtBeginning, List<Pair<GPSFixMoving, CourseChange>> group,
SpeedWithBearing speedWithBearingOnApproximationAtEnd, double totalCourseChangeInDegrees,
long totalMilliseconds) throws NoWindException {
long totalMilliseconds, TimePoint earliestManeuverStart, TimePoint latestManeuverEnd) throws NoWindException {
List<Maneuver> result = new ArrayList<>();
MillisecondsTimePoint timePointBeforeManeuver = new MillisecondsTimePoint(group.get(0).getA().getTimePoint()
.asMillis()
- getApproximateManeuverDurationInMilliseconds() / 2);
MillisecondsTimePoint timePointAfterManeuver = new MillisecondsTimePoint(group.get(group.size() - 1).getA()
.getTimePoint().asMillis()
+ getApproximateManeuverDurationInMilliseconds() / 2);
TimePoint maneuverTimePoint = computeManeuverTimepoint(competitor, timePointBeforeManeuver,
timePointAfterManeuver);
TimePoint timePointBeforeManeuver = Collections.max(Arrays.asList(new MillisecondsTimePoint(group.get(0).getA().getTimePoint()
.asMillis() - getApproximateManeuverDurationInMilliseconds() / 2), earliestManeuverStart));
TimePoint timePointAfterManeuver = Collections.min(Arrays.asList(new MillisecondsTimePoint(group.get(group.size() - 1).getA()
.getTimePoint().asMillis() + getApproximateManeuverDurationInMilliseconds() / 2), latestManeuverEnd));
TimePoint maneuverTimePoint = computeManeuverTimepoint(competitor, timePointBeforeManeuver, timePointAfterManeuver);
final GPSFixTrack<Competitor, GPSFixMoving> competitorTrack = getTrack(competitor);
Position maneuverPosition = competitorTrack
.getEstimatedPosition(maneuverTimePoint, /* extrapolate */false);
Position maneuverPosition = competitorTrack.getEstimatedPosition(maneuverTimePoint, /* extrapolate */false);
Tack tackAfterManeuver = getTack(maneuverPosition, timePointAfterManeuver,
speedWithBearingOnApproximationAtEnd.getBearing());
ManeuverType maneuverType;
@@ -2196,8 +2234,8 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
// kiwi drop.
// Therefore, we recursively detect the maneuvers for the segment before and the segment after the
// mark passing and add the results to our result.
result.addAll(detectManeuvers(competitor, timePointBeforeManeuver, markPassingTimePoint.minus(1)));
result.addAll(detectManeuvers(competitor, markPassingTimePoint.plus(1), timePointAfterManeuver));
result.addAll(detectManeuvers(competitor, timePointBeforeManeuver, markPassingTimePoint.minus(1), /* ignoreMarkPassings */ true));
result.addAll(detectManeuvers(competitor, markPassingTimePoint.plus(1), timePointAfterManeuver, /* ignoreMarkPassings */ true));
} else {
// Either there was no mark passing, or the mark passing was not accompanied by a tack or a jibe
if (tacked && jibed && markPassingTimePoint == null) {
@@ -2241,8 +2279,8 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
* Computes the maneuver time point as the time point along between maneuver start and end where the competitor's
* track has greatest change in course.
*/
private TimePoint computeManeuverTimepoint(Competitor competitor, MillisecondsTimePoint timePointBeforeManeuver,
MillisecondsTimePoint timePointAfterManeuver) {
private TimePoint computeManeuverTimepoint(Competitor competitor, TimePoint timePointBeforeManeuver,
TimePoint timePointAfterManeuver) {
TimePoint result = timePointBeforeManeuver;
GPSFixTrack<Competitor, GPSFixMoving> track = getTrack(competitor);
GPSFixMoving lastFix = null;