Merge branch 'master' into mark-rounding-inference

This commit is contained in:
Nick Klose committed 2014-01-06 22:56:10 +01:00
commit a0517dfa76
66 files changed
+1321 -2336

No files matched your search

@@ -359,6 +359,14 @@ public interface Leaderboard extends Named {
* races attached to this leaderboard; the fix where the maximum speed was achieved, and the speed value
*/
Pair<GPSFixMoving, Speed> getMaximumSpeedOverGround(Competitor competitor, TimePoint timePoint);
/**
* @return <code>null</code> if no tracked race is available in this leaderboard, the competitor hasn't started
* a single race at <code>timePoint</code> or if the competitor has not finished one of the races. This method
* computes the average speed starting with the first mark passing until <code>timePoint</code> or if earlier then
* the timePoint of the last mark passing.
*/
Speed getAverageSpeedOverGround(Competitor competitor, TimePoint timePoint);
/**
* @param legType the leg type for which to add up the times sailed
@@ -851,6 +851,37 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
return result;
}
@Override
public Speed getAverageSpeedOverGround(Competitor competitor, TimePoint timePoint) {
Speed result = null;
for (TrackedRace trackedRace : getTrackedRaces()) {
if (Util.contains(trackedRace.getRace().getCompetitors(), competitor)) {
NavigableSet<MarkPassing> markPassings = trackedRace.getMarkPassings(competitor);
if (!markPassings.isEmpty()) {
TimePoint from = markPassings.first().getTimePoint();
TimePoint to;
if (timePoint.after(markPassings.last().getTimePoint()) &&
markPassings.last().getWaypoint() == trackedRace.getRace().getCourse().getLastWaypoint()) {
// stop counting when competitor finished the race
to = markPassings.last().getTimePoint();
} else {
if (markPassings.last().getWaypoint() != trackedRace.getRace().getCourse().getLastWaypoint() &&
timePoint.after(markPassings.last().getTimePoint())) {
result = null;
break;
}
to = timePoint;
}
Distance distanceTraveled = trackedRace.getDistanceTraveled(competitor, timePoint);
if (distanceTraveled != null) {
result = distanceTraveled.inTime(to.asMillis()-from.asMillis());
}
}
}
}
return result;
}
@Override
public Long getTotalTimeSailedInLegTypeInMilliseconds(Competitor competitor, LegType legType, TimePoint timePoint) throws NoWindException {
Long result = null;
@@ -918,9 +949,9 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
} else {
if (trackedRace.getEndOfTracking() != null
&& timePoint.after(trackedRace.getEndOfTracking())) {
result = null; // race not finished until end of tracking; no reasonable value can be
// computed for competitor
break;
result = null; // race not finished until end of tracking; no reasonable value can be
// computed for competitor
break;
} else {
to = timePoint;
}
@@ -950,6 +981,11 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
} else {
result = result.add(distanceSailedInRace);
}
} else {
// if competitor has not finished one single race in the whole
// series then we can not return a meaningful value for all
// all races
return null;
}
}
}
@@ -71,7 +71,8 @@ public interface TrackedLegOfCompetitor extends Serializable {
/**
* @return <code>null</code> if the competitor hasn't started this leg yet, otherwise the fix where the maximum speed was
* achieved and the speed value
* achieved and the speed value. In case you provide <code>timepoint</code> that is greater than the time point of the
* end of this leg the provided value will be ignored and replaced by the timepoint of the end of the leg.
*/
Pair<GPSFixMoving, Speed> getMaximumSpeedOverGround(TimePoint timePoint);
@@ -174,6 +175,11 @@ public interface TrackedLegOfCompetitor extends Serializable {
*/
Distance getWindwardDistanceToOverallLeader(TimePoint timePoint) throws NoWindException;
/**
* Computes the average cross track error for this leg. If you provide this method with a {@link TimePoint} greater
* than the time the mark passing of the leg end mark has occurred then the time point of the mark passing
* of the leg end mark will be taken into account.
*/
Distance getAverageCrossTrackError(TimePoint timePoint, boolean waitForLatestAnalysis) throws NoWindException;
/**
@@ -11,6 +11,7 @@ import com.sap.sailing.domain.base.Leg;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Sideline;
import com.sap.sailing.domain.base.SpeedWithConfidence;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.impl.DouglasPeucker;
import com.sap.sailing.domain.common.Distance;
@@ -604,7 +605,13 @@ public interface TrackedRace extends Serializable {
/**
* The estimated speed of the competitor at the time point of the given seconds before the start of race.
*/
Speed getSpeed(Competitor competitor, double secondsBeforeRaceStart);
Speed getSpeed(Competitor competitor, double secondsBeforeRaceStart);
/**
* The speed of the competitor when crossing the start line. It will return null if there are no recorded
* mark passings for this competitor (competitor did not yet or never pass the start line).
*/
Speed getSpeedWhenCrossingStartLine(Competitor competitor);
/**
* Start time received by the tracking infrastructure. To determine real start time use {@link #getStartOfRace()}.
@@ -614,5 +621,16 @@ public interface TrackedRace extends Serializable {
LineLengthAndAdvantage getStartLine(TimePoint at);
LineLengthAndAdvantage getFinishLine(TimePoint at);
/**
* Length of course if there are mark passings for competitors.
*/
Distance getCourseLength();
/**
* The average wind speed with confidence for this race. It uses the timepoint of the race end as
* a reference point.
*/
SpeedWithConfidence<TimePoint> getAverageWindSpeedWithConfidence(long resolutionInMillis);
}
@@ -39,10 +39,12 @@ import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Sideline;
import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence;
import com.sap.sailing.domain.base.SpeedWithConfidence;
import com.sap.sailing.domain.base.Timed;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.impl.BearingWithConfidenceImpl;
import com.sap.sailing.domain.base.impl.DouglasPeucker;
import com.sap.sailing.domain.base.impl.SpeedWithConfidenceImpl;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.CourseChange;
import com.sap.sailing.domain.common.Distance;
@@ -63,8 +65,10 @@ import com.sap.sailing.domain.common.TrackedRaceStatusEnum;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.impl.CentralAngleDistance;
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.common.impl.NauticalMileDistance;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.common.impl.Util.Triple;
@@ -769,6 +773,12 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
&& timePoint.compareTo(markPassings.last().getTimePoint()) > 0) {
// competitor has finished race; use time point of crossing the finish line
end = markPassings.last().getTimePoint();
} else if (markPassings.last().getWaypoint() != getRace().getCourse().getLastWaypoint() &&
timePoint.after(markPassings.last().getTimePoint())) {
// if competitor has not finished the race (and the requested timepoint
// is after the last mark passing) then we can not tell anything about
// the distance traveled - this is in line with AbstractSimpleLeaderboardImpl#getTotalTimeSailedInMilliseconds
return null;
}
return getTrack(competitor).getDistanceTraveled(markPassings.first().getTimePoint(), end);
}
@@ -1810,7 +1820,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
}
private Triple<TimePoint, TimePoint, List<Maneuver>> computeManeuvers(Competitor competitor) throws NoWindException {
logger.fine("computeManeuvers(" + competitor.getName() + ") called in tracked race " + this);
logger.finest("computeManeuvers(" + competitor.getName() + ") called in tracked race " + this);
long startedAt = System.currentTimeMillis();
// compute the maneuvers for competitor
Triple<TimePoint, TimePoint, List<Maneuver>> result = null;
@@ -1865,7 +1875,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
}
} // else competitor has no fixes to consider; remove any maneuver cache entry
} // else competitor hasn't started yet; remove any maneuver cache entry
logger.fine("computeManeuvers(" + competitor.getName() + ") called in tracked race " + this + " took "
logger.finest("computeManeuvers(" + competitor.getName() + ") called in tracked race " + this + " took "
+ (System.currentTimeMillis() - startedAt) + "ms");
return result;
}
@@ -2571,4 +2581,83 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
return getLineLengthAndAdvantage(at, getRace().getCourse().getLastWaypoint());
}
@Override
public SpeedWithConfidence<TimePoint> getAverageWindSpeedWithConfidence(long resolutionInMillis) {
SpeedWithConfidence<TimePoint> result = null;
if (getEndOfRace() != null) {
TimePoint fromTimePoint = getStartOfRace();
TimePoint toTimePoint = getEndOfRace();
List<WindSource> windSourcesToDeliver = new ArrayList<WindSource>();
WindSourceImpl windSource = new WindSourceImpl(WindSourceType.COMBINED);
windSourcesToDeliver.add(windSource);
double sumWindSpeed = 0.0;
double sumWindSpeedConfidence = 0.0;
int speedCounter = 0;
int numberOfFixes = (int) ((toTimePoint.asMillis() - fromTimePoint.asMillis()) / resolutionInMillis);
WindTrack windTrack = getOrCreateWindTrack(windSource);
TimePoint timePoint = fromTimePoint;
for (int i = 0; i < numberOfFixes && toTimePoint != null && timePoint.compareTo(toTimePoint) < 0; i++) {
WindWithConfidence<Pair<Position, TimePoint>> averagedWindWithConfidence = windTrack
.getAveragedWindWithConfidence(null, timePoint);
if (averagedWindWithConfidence != null) {
double windSpeedinKnots = averagedWindWithConfidence.getObject().getKnots();
double confidence = averagedWindWithConfidence.getConfidence();
sumWindSpeed += windSpeedinKnots;
sumWindSpeedConfidence += confidence;
speedCounter++;
}
timePoint = new MillisecondsTimePoint(timePoint.asMillis() + resolutionInMillis);
}
if (speedCounter > 0) {
Speed averageWindSpeed = new KnotSpeedImpl(sumWindSpeed / speedCounter);
double averageWindSpeedConfidence = sumWindSpeedConfidence / speedCounter;
result = new SpeedWithConfidenceImpl<TimePoint>(averageWindSpeed, averageWindSpeedConfidence, toTimePoint);
}
}
return result;
}
@Override
public Distance getCourseLength() {
List<Leg> legs = getRace().getCourse().getLegs();
Distance raceDistance = new NauticalMileDistance(0);
for (Leg leg : legs) {
Waypoint from = leg.getFrom();
Iterable<MarkPassing> markPassings = getMarkPassingsInOrder(from);
Iterator<MarkPassing> markPassingsIterator = markPassings.iterator();
if (!markPassingsIterator.hasNext()) {
return null;
}
MarkPassing firstPassing = markPassingsIterator.next();
TimePoint timePointOfFirstPassing = firstPassing.getTimePoint();
Waypoint to = leg.getTo();
Position fromPos = getApproximatePosition(from, timePointOfFirstPassing);
Position toPos = getApproximatePosition(to, timePointOfFirstPassing);
Distance legDistance = fromPos.getDistance(toPos);
raceDistance = raceDistance.add(legDistance);
}
return raceDistance;
}
@Override
public Speed getSpeedWhenCrossingStartLine(Competitor competitor) {
NavigableSet<MarkPassing> competitorMarkPassings = getMarkPassings(competitor);
Speed competitorSpeedWhenPassingStart = null;
lockForRead(competitorMarkPassings);
try {
if (!competitorMarkPassings.isEmpty()) {
TimePoint competitorStartTime = competitorMarkPassings.first().getTimePoint();
competitorSpeedWhenPassingStart = getTrack(competitor).getEstimatedSpeed(
competitorStartTime);
}
} finally {
unlockAfterRead(competitorMarkPassings);
}
return competitorSpeedWhenPassingStart;
}
}