mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-14 08:11:00 +00:00
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:
1 parent
efe9fb6295
commit
d1abac0c8d
2 files changed
+66
-28
No files matched your search
+1
-1
@@ -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");
|
||||
|
||||
+65
-27
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user