bug6068: introduced four new @Statistic methods for wind axis-based XTE

This commit is contained in:
Axel Uhl
2024-11-08 17:26:54 +01:00
parent e5e86e0a34
commit ea51331950
8 changed files with 122 additions and 22 deletions
@@ -253,6 +253,10 @@ XTE=Cross-Track Error, signed (- = left, + = right)
AbsoluteXTE=Absolute Cross-Track Error, unsigned
XTERelativeSigned=Relative Cross-Track Error, signed (-1 = 1/2 leg length left, 0 = middle, +1 = 1/2 leg length right)
XTERelativeUnsigned=Relative Cross-Track Error, unsigned (0 = middle, 1 = 1/2 leg length out)
XTEToWindAxisUnsigned=Cross-Track Error to Wind Axis, unsigned
XTEToWindAxisSigned=Cross-Track Error to Wind Axis, signed (- = left, + = right)
XTEToWindAxisRelativeUnsigned=Relative Cross-Track Error to Wind Axis, unsigned (0 = middle, 1 = 1/2 leg length out)
XTEToWindAxisRelativeSigned=Relative Cross-Track Error to Wind Axis, signed (-1 = 1/2 leg length left, 0 = middle, +1 = 1/2 leg length right)
Day=Day
CompetitorDay=Competitor Day
CompetitorDaySailingDomainRetrieverChain=Competitor-Day for race
@@ -256,6 +256,10 @@ XTE=Absolute Querabweichung, vorzeichenbehaftet (- = links, + = rechts)
AbsoluteXTE=Absolute Querabweichung, ohne Vorzeichen
XTERelativeSigned=Relative Querabweichung, mit Vorzeichen (-1 = 1/2 Schenkellänge links, 0 = Mitte, +1 = 1/2 Schenkellänge rechts)
XTERelativeUnsigned=Relative Querabweichung, ohne Vorzeichen (0 = Mitte, 1 = ganz aussen)
XTEToWindAxisUnsigned=Querabweichung zur Windachse, ohne Vorzeichen
XTEToWindAxisSigned=Querabweichung zur Windachse, vorzeichenbehaftet (- = links, + = rechts)
XTEToWindAxisRelativeUnsigned=Relative Querabweichung zur Windachse, mit Vorzeichen (-1 = 1/2 Schenkellänge links, 0 = Mitte, +1 = 1/2 Schenkellänge rechts)
XTEToWindAxisRelativeSigned=Relative Querabweichung zur Windachse, ohne Vorzeichen (0 = Mitte, 1 = ganz aussen)
Day=Tag
CompetitorDay=Teilnehmer-Tag
CompetitorDaySailingDomainRetrieverChain=Teilnehmer-Tag für Wettfahrt
@@ -42,6 +42,36 @@ public interface HasGPSFixContext {
@Statistic(messageKey = "XTERelativeUnsigned", resultDecimals = 2)
Double getRelativeXTEUnsigned();
/**
* Instead of projecting onto the course middle line connecting start and end of the leg,
* projects onto the wind axis, attached to the leg's end waypoint.
*/
@Statistic(messageKey = "XTEToWindAxisUnsigned", resultDecimals = 2)
Distance getXTEToWindAxisUnsigned();
/**
* Instead of projecting onto the course middle line connecting start and end of the leg,
* projects onto the wind axis, attached to the leg's end waypoint.
*/
@Statistic(messageKey = "XTEToWindAxisSigned", resultDecimals = 2)
Distance getXTEToWindAxisSigned();
/**
* Instead of projecting onto the course middle line connecting start and end of the leg,
* projects onto the wind axis, attached to the leg's end waypoint. That distance is divided
* by half the leg length.
*/
@Statistic(messageKey = "XTEToWindAxisRelativeUnsigned", resultDecimals = 2)
Double getXTEToWindAxisRelativeUnsigned();
/**
* Instead of projecting onto the course middle line connecting start and end of the leg,
* projects onto the wind axis, attached to the leg's end waypoint. That distance is divided
* by half the leg length.
*/
@Statistic(messageKey = "XTEToWindAxisRelativeSigned", resultDecimals = 2)
Double getXTEToWindAxisRelativeSigned();
@Statistic(messageKey = "SmoothedSpeed", resultDecimals = 2)
Speed getSmoothedSpeed();
@@ -149,4 +149,24 @@ public class GPSFixWithContext implements HasGPSFixContext {
final double fractionSailed = Math.max(0, legWindwardDistance.add(windwardDistanceToGo.scale(-1)).divide(legWindwardDistance));
return (int) Math.min(10, 1+10*fractionSailed);
}
@Override
public Distance getXTEToWindAxisUnsigned() {
return getTrackedLegOfCompetitorContext().getTrackedLegOfCompetitor().getUnsignedCrossTrackErrorToWindAxis(getTimePoint());
}
@Override
public Distance getXTEToWindAxisSigned() {
return getTrackedLegOfCompetitorContext().getTrackedLegOfCompetitor().getSignedCrossTrackErrorToWindAxis(getTimePoint());
}
@Override
public Double getXTEToWindAxisRelativeUnsigned() {
return getRelativeXTE(TrackedLegOfCompetitor::getUnsignedCrossTrackErrorToWindAxis);
}
@Override
public Double getXTEToWindAxisRelativeSigned() {
return getRelativeXTE(TrackedLegOfCompetitor::getSignedCrossTrackErrorToWindAxis);
}
}
@@ -80,6 +80,19 @@ public interface TrackedLeg extends Serializable {
*/
Distance getSignedCrossTrackError(Position p, TimePoint timePoint);
/**
* Returns the (unsigned) distance of <code>p</code> to this leg's wind axis attached to the leg's end
* waypoint at <code>timePoint</code>.
*/
Distance getUnsignedCrossTrackErrorToWindAxis(Position p, TimePoint timePoint);
/**
* Returns the (unsigned) distance of <code>p</code> to this leg's wind axis attached to the leg's end
* waypoint at <code>timePoint</code>. Negative distances mean "left" of the
* wind axis, looking in the direction of the leg, positive distances mean "right."
*/
Distance getSignedCrossTrackErrorToWindAxis(Position p, TimePoint timePoint);
/**
* Must be called when the start and finish waypoint of this leg may have changed.
*/
@@ -325,6 +325,18 @@ public interface TrackedLegOfCompetitor extends Serializable {
* @return {@code null} if the competitor has not started or already finished the leg
*/
Distance getSignedCrossTrackError(TimePoint timePoint);
/**
* Instead of projecting onto the course middle line connecting start and end of the leg,
* projects onto the wind axis, attached to the leg's end waypoint.
*/
Distance getUnsignedCrossTrackErrorToWindAxis(TimePoint timePoint);
/**
* Instead of projecting onto the course middle line connecting start and end of the leg,
* projects onto the wind axis, attached to the leg's end waypoint.
*/
Distance getSignedCrossTrackErrorToWindAxis(TimePoint timePoint);
TrackedLeg getTrackedLeg();
@@ -393,16 +393,30 @@ public class TrackedLegImpl implements TrackedLeg {
@Override
public Distance getAbsoluteCrossTrackError(Position p, TimePoint timePoint) {
final Position approximatePosition = getTrackedRace().getApproximatePosition(getLeg().getFrom(), timePoint);
final Position approximateLegFromPosition = getTrackedRace().getApproximatePosition(getLeg().getFrom(), timePoint);
final Bearing legBearing = getLegBearing(timePoint);
return approximatePosition==null || legBearing==null ? null : p.absoluteCrossTrackError(approximatePosition, legBearing);
return approximateLegFromPosition==null || legBearing==null ? null : p.absoluteCrossTrackError(approximateLegFromPosition, legBearing);
}
@Override
public Distance getSignedCrossTrackError(Position p, TimePoint timePoint) {
final Position approximatePosition = getTrackedRace().getApproximatePosition(getLeg().getFrom(), timePoint);
final Position approximateLegFromPosition = getTrackedRace().getApproximatePosition(getLeg().getFrom(), timePoint);
final Bearing legBearing = getLegBearing(timePoint);
return approximatePosition==null || legBearing==null ? null : p.crossTrackError(approximatePosition, legBearing);
return approximateLegFromPosition==null || legBearing==null ? null : p.crossTrackError(approximateLegFromPosition, legBearing);
}
@Override
public Distance getUnsignedCrossTrackErrorToWindAxis(Position p, TimePoint timePoint) {
final Position approximateLegToPosition = getTrackedRace().getApproximatePosition(getLeg().getTo(), timePoint);
final Bearing windAxis = getWind(p, timePoint, getTrackedRace().getWindSourcesToExclude()).getFrom(); // the "from" wind direction, not "to"
return approximateLegToPosition==null || windAxis==null ? null : p.absoluteCrossTrackError(approximateLegToPosition, windAxis);
}
@Override
public Distance getSignedCrossTrackErrorToWindAxis(Position p, TimePoint timePoint) {
final Position approximateLegToPosition = getTrackedRace().getApproximatePosition(getLeg().getTo(), timePoint);
final Bearing windAxis = getWind(p, timePoint, getTrackedRace().getWindSourcesToExclude()).getFrom(); // the "from" wind direction, not "to"
return approximateLegToPosition==null || windAxis==null ? null : p.crossTrackError(approximateLegToPosition, windAxis);
}
@Override
@@ -26,6 +26,7 @@ import com.sap.sailing.domain.tracking.BravoFixTrack;
import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.Maneuver;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedLeg;
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache;
import com.sap.sailing.domain.tracking.WindPositionMode;
@@ -543,7 +544,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
@Override
public Distance getAverageAbsoluteCrossTrackError(TimePoint timePoint, boolean waitForLatestAnalysis) {
final Distance result;
MarkPassing legStart = getMarkPassingForLegStart();
final MarkPassing legStart = getMarkPassingForLegStart();
if (legStart != null) {
final TimePoint to = getTimePointNotAfterFinishingOfLeg(timePoint);
if (to != null) {
@@ -575,15 +576,14 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
}
return result;
}
@Override
public Distance getAbsoluteCrossTrackError(TimePoint timePoint) {
private Distance getSomeCrossTrackError(TimePoint timePoint, BiFunction<TrackedLeg, Position, Distance> crossTrackCalculatorAtTimePoint) {
final Distance result;
final GPSFixTrack<Competitor, GPSFixMoving> track = getTrackedRace().getTrack(getCompetitor());
if (track != null) {
final Position estimatedPosition = track.getEstimatedPosition(timePoint, /* extrapolate */ true);
if (estimatedPosition != null) {
result = getTrackedLeg().getAbsoluteCrossTrackError(estimatedPosition, timePoint);
result = crossTrackCalculatorAtTimePoint.apply(getTrackedLeg(), estimatedPosition);
} else {
result = null;
}
@@ -593,21 +593,24 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
return result;
}
@Override
public Distance getAbsoluteCrossTrackError(TimePoint timePoint) {
return getSomeCrossTrackError(timePoint, (trackedLeg, estimatedPosition)->getTrackedLeg().getAbsoluteCrossTrackError(estimatedPosition, timePoint));
}
@Override
public Distance getSignedCrossTrackError(TimePoint timePoint) {
final Distance result;
final GPSFixTrack<Competitor, GPSFixMoving> track = getTrackedRace().getTrack(getCompetitor());
if (track != null) {
final Position estimatedPosition = track.getEstimatedPosition(timePoint, /* extrapolate */ true);
if (estimatedPosition != null) {
result = getTrackedLeg().getSignedCrossTrackError(estimatedPosition, timePoint);
} else {
result = null;
}
} else {
result = null;
}
return result;
return getSomeCrossTrackError(timePoint, (trackedLeg, estimatedPosition)->getTrackedLeg().getSignedCrossTrackError(estimatedPosition, timePoint));
}
@Override
public Distance getUnsignedCrossTrackErrorToWindAxis(TimePoint timePoint) {
return getSomeCrossTrackError(timePoint, (trackedLeg, estimatedPosition)->getTrackedLeg().getUnsignedCrossTrackErrorToWindAxis(estimatedPosition, timePoint));
}
@Override
public Distance getSignedCrossTrackErrorToWindAxis(TimePoint timePoint) {
return getSomeCrossTrackError(timePoint, (trackedLeg, estimatedPosition)->getTrackedLeg().getSignedCrossTrackErrorToWindAxis(estimatedPosition, timePoint));
}
@Override