mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-24 21:13:50 +00:00
Merge branch 'master' into mark-rounding-inference
This commit is contained in:
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
|
||||
|
||||
+39
-3
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -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);
|
||||
|
||||
}
|
||||
+91
-2
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user