Merge branch 'master' into bug3985

This commit is contained in:
Kai Boernert committed 2017-03-14 09:35:00 +01:00
commit e92116be9d
244 files changed
+3042 -893

No files matched your search

@@ -2,4 +2,4 @@ eclipse.preferences.version=1
encoding//src/com/sap/sailing/domain/common/AbstractBearing.java=UTF-8
encoding//src/com/sap/sailing/domain/common/confidence/impl/ConfidenceBasedAveragerImpl.java=utf-8
encoding//src/com/sap/sailing/domain/common/impl/AbstractSpeedWithAbstractBearingImpl.java=UTF-8
encoding//src/com/sap/sailing/domain/common/tracking/impl/CompactGPSFixMovingImpl.java=UTF-8
encoding//src/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixMovingImpl.java=UTF-8
@@ -4,6 +4,6 @@ public enum ScoringSchemeType {
// make sure to also add new ones to ScoringSchemeTypeFormatter
LOW_POINT, HIGH_POINT, HIGH_POINT_ESS_OVERALL,HIGH_POINT_ESS_OVERALL_12, HIGH_POINT_LAST_BREAKS_TIE, HIGH_POINT_FIRST_GETS_TEN,
HIGH_POINT_FIRST_GETS_ONE, LOW_POINT_WINNER_GETS_ZERO, HIGH_POINT_WINNER_GETS_SIX, HIGH_POINT_WINNER_GETS_FIVE, HIGH_POINT_WINNER_GETS_EIGHT,
HIGH_POINT_WINNER_GETS_EIGHT_AND_INTERPOLATION, HIGH_POINT_FIRST_GETS_TEN_OR_EIGHT, HIGH_POINT_FIRST_GETS_TWELVE_OR_EIGHT, LOW_POINT_WITH_ELIMINATIONS_AND_ROUNDS_WINNER_GETS_07,
HIGH_POINT_WINNER_GETS_EIGHT_AND_INTERPOLATION, HIGH_POINT_FIRST_GETS_TEN_OR_EIGHT, HIGH_POINT_FIRST_GETS_TWELVE_OR_EIGHT, HIGH_POINT_FIRST_GETS_TWELVE_OR_EIGHT_2017, LOW_POINT_WITH_ELIMINATIONS_AND_ROUNDS_WINNER_GETS_07,
LOW_POINT_LEAGUE_OVERALL, HIGH_POINT_MATCH_RACING, LOW_POINT_TIE_BREAK_BASED_ON_LAST_SERIES_ONLY
}
@@ -33,7 +33,7 @@ public class WindImpl extends KnotSpeedWithBearingImpl implements Wind {
}
public static int hashCode(double latDeg, double lngDeg, long timePointAsMillis) {
return (31 * (int) (timePointAsMillis & Integer.MAX_VALUE)) ^ (int) (latDeg*lngDeg);
return (31 * (int) (timePointAsMillis & Integer.MAX_VALUE)) ^ (int) (Math.round(latDeg)*Math.round(lngDeg));
}
/**
* Wind hash is determined based on time point and position only to speed this up a little.
@@ -14,10 +14,11 @@ public enum BravoSensorDataMetadata {
INSTANCE;
private static final String RIDE_HEIGHT_PORT_HULL = "RideHeightPortHull";
private static final String RIDE_HEIGHT_STBD_HULL = "RideHeightStbdHull";
private static final String HEEL = "Heel";
private static final String PITCH = "ImuSensor_Pitch";
public static final String RIDE_HEIGHT = "RideHeight";
public static final String RIDE_HEIGHT_PORT_HULL = "RideHeightPortHull";
public static final String RIDE_HEIGHT_STBD_HULL = "RideHeightStbdHull";
public static final String HEEL = "Heel";
public static final String PITCH = "ImuSensor_Pitch";
private final int HEADER_COLUMN_OFFSET = 3;
@@ -1,11 +1,6 @@
package com.sap.sailing.domain.common.tracking.impl;
import com.sap.sailing.domain.common.AbstractBearing;
import com.sap.sailing.domain.common.AbstractPosition;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.impl.AbstractSpeedWithAbstractBearingImpl;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.AbstractTimePoint;
@@ -21,9 +16,9 @@ import com.sap.sse.common.impl.AbstractTimePoint;
* @author Axel Uhl (d043530)
*
*/
public class CompactGPSFixImpl extends AbstractGPSFixImpl {
private static final long serialVersionUID = 8167588584536992501L;
public abstract class AbstractCompactGPSFixImpl extends AbstractGPSFixImpl {
private static final long serialVersionUID = 3943572107870245437L;
/**
* Bit mask for {@link #whatIsCached}, telling whether validity is currently cached
*/
@@ -39,9 +34,7 @@ public class CompactGPSFixImpl extends AbstractGPSFixImpl {
* <code>{@link #whatIsCached}&amp;{@link #IS_VALIDITY_CACHED} != 0</code>
*/
private static final byte VALIDITY = 1<<2;
private final double latDeg;
private final double lngDeg;
private final long timePointAsMillis;
/**
@@ -52,32 +45,6 @@ public class CompactGPSFixImpl extends AbstractGPSFixImpl {
*/
private byte whatIsCached = 0;
/**
* When <code>{@link #whatIsCached}&amp;{@link #IS_ESTIMATED_SPEED_CACHED} != 0</code>, this field tells the estimated speed's
* true "bearing" (true course over ground) in degrees.
*/
private double cachedEstimatedSpeedBearingInDegrees;
/**
* When <code>{@link #whatIsCached}&amp;{@link #IS_ESTIMATED_SPEED_CACHED} != 0</code>, this field tells the estimated speed
* in knots.
*/
private double cachedEstimatedSpeedInKnots;
public class CompactPosition extends AbstractPosition {
private static final long serialVersionUID = 5621506820766614178L;
@Override
public double getLatDeg() {
return latDeg;
}
@Override
public double getLngDeg() {
return lngDeg;
}
}
private class CompactTimePoint extends AbstractTimePoint implements TimePoint {
private static final long serialVersionUID = -2470922642359937437L;
@@ -87,37 +54,12 @@ public class CompactGPSFixImpl extends AbstractGPSFixImpl {
}
}
private class CompactEstimatedSpeedBearing extends AbstractBearing {
private static final long serialVersionUID = 8549231429037883121L;
@Override
public double getDegrees() {
return cachedEstimatedSpeedBearingInDegrees;
}
}
private class CompactEstimatedSpeed extends AbstractSpeedWithAbstractBearingImpl {
private static final long serialVersionUID = -5871855443391817248L;
@Override
public Bearing getBearing() {
return new CompactEstimatedSpeedBearing();
}
@Override
public double getKnots() {
return cachedEstimatedSpeedInKnots;
}
}
public CompactGPSFixImpl(Position position, TimePoint timePoint) {
latDeg = position.getLatDeg();
lngDeg = position.getLngDeg();
public AbstractCompactGPSFixImpl(TimePoint timePoint) {
timePointAsMillis = timePoint==null?-1:timePoint.asMillis();
}
public CompactGPSFixImpl(GPSFix gpsFix) {
this(gpsFix.getPosition(), gpsFix.getTimePoint());
public AbstractCompactGPSFixImpl(GPSFix gpsFix) {
this(gpsFix.getTimePoint());
}
@Override
@@ -125,11 +67,6 @@ public class CompactGPSFixImpl extends AbstractGPSFixImpl {
return getTimePoint() + ": " + getPosition();
}
@Override
public Position getPosition() {
return new CompactPosition();
}
@Override
public TimePoint getTimePoint() {
return new CompactTimePoint();
@@ -166,12 +103,6 @@ public class CompactGPSFixImpl extends AbstractGPSFixImpl {
return (whatIsCached & IS_ESTIMATED_SPEED_CACHED) != 0;
}
@Override
public SpeedWithBearing getCachedEstimatedSpeed() {
assert isEstimatedSpeedCached();
return new CompactEstimatedSpeed();
}
@Override
public void invalidateEstimatedSpeedCache() {
whatIsCached &= ~IS_ESTIMATED_SPEED_CACHED;
@@ -179,10 +110,6 @@ public class CompactGPSFixImpl extends AbstractGPSFixImpl {
@Override
public void cacheEstimatedSpeed(SpeedWithBearing estimatedSpeed) {
cachedEstimatedSpeedBearingInDegrees = estimatedSpeed.getBearing().getDegrees();
cachedEstimatedSpeedInKnots = estimatedSpeed.getKnots();
whatIsCached |= IS_ESTIMATED_SPEED_CACHED;
}
}
@@ -1,6 +1,5 @@
package com.sap.sailing.domain.common.tracking.impl;
import com.sap.sailing.domain.common.AbstractBearing;
import com.sap.sailing.domain.common.AbstractSpeedImpl;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.CourseChange;
@@ -21,23 +20,14 @@ import com.sap.sse.common.TimePoint;
* @author Axel Uhl (d043530)
*
*/
public class CompactGPSFixMovingImpl extends CompactGPSFixImpl implements GPSFixMoving {
private static final long serialVersionUID = 761582024504236533L;
private final double knotSpeed;
private final double degBearing;
private class CompactSpeedWithBearing extends AbstractSpeedImpl implements SpeedWithBearing {
public abstract class AbstractCompactGPSFixMovingImpl extends AbstractCompactGPSFixImpl implements GPSFixMoving {
private static final long serialVersionUID = 3977983319207618335L;
protected abstract class AbstractCompactSpeedWithBearing extends AbstractSpeedImpl implements SpeedWithBearing {
private static final long serialVersionUID = 1802065090733146728L;
@Override
public double getKnots() {
return knotSpeed;
}
@Override
public Bearing getBearing() {
return new CompactBearing();
}
public abstract double getKnots();
@Override
public Position travelTo(Position pos, TimePoint from, TimePoint to) {
@@ -81,33 +71,12 @@ public class CompactGPSFixMovingImpl extends CompactGPSFixImpl implements GPSFix
}
}
private class CompactBearing extends AbstractBearing {
private static final long serialVersionUID = -6474909210513108635L;
@Override
public double getDegrees() {
return degBearing;
}
@Override
public double getRadians() {
return degBearing / 180. * Math.PI;
}
public AbstractCompactGPSFixMovingImpl(TimePoint timePoint) {
super(timePoint);
}
public CompactGPSFixMovingImpl(Position position, TimePoint timePoint, SpeedWithBearing speed) {
super(position, timePoint);
knotSpeed = speed.getKnots();
degBearing = speed.getBearing().getDegrees();
}
public CompactGPSFixMovingImpl(GPSFixMoving gpsFixMoving) {
this(gpsFixMoving.getPosition(), gpsFixMoving.getTimePoint(), gpsFixMoving.getSpeed());
}
@Override
public SpeedWithBearing getSpeed() {
return new CompactSpeedWithBearing();
public AbstractCompactGPSFixMovingImpl(GPSFixMoving gpsFixMoving) throws CompactionNotPossibleException {
this(gpsFixMoving.getTimePoint());
}
@Override
@@ -0,0 +1,129 @@
package com.sap.sailing.domain.common.tracking.impl;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.impl.AbstractSpeedWithAbstractBearingImpl;
import com.sap.sailing.domain.common.impl.WindImpl;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.AbstractTimePoint;
public abstract class AbstractCompactWindImpl extends AbstractSpeedWithAbstractBearingImpl implements Wind {
private static final long serialVersionUID = -5059956032663387929L;
/**
* bit mask for {@link #flags}, telling the bit encoding whether the position is {@code null}.
*/
private static final byte POSITION_IS_NULL = 1<<0;
/**
* bit mask for {@link #flags}, telling the bit encoding whether the bearing is {@code null}.
*/
private static final byte BEARING_IS_NULL = 1<<1;
/**
* bit mask for {@link #flags}, telling the bit encoding whether the time point is {@code null}.
*/
private static final byte TIME_POINT_IS_NULL = 1<<2;
/**
* See the bit mask values such as {@link #POSITION_IS_NULL}, {@link #BEARING_IS_NULL} and {@link #TIME_POINT_IS_NULL}.
*/
private final byte flags;
private final long timePointAsMillis;
private class CompactTimePoint extends AbstractTimePoint implements TimePoint {
private static final long serialVersionUID = -2470922642359937437L;
@Override
public long asMillis() {
return timePointAsMillis;
}
}
public AbstractCompactWindImpl(Wind wind) {
final boolean bearingIsNull;
final boolean positionIsNull;
final boolean timePointIsNull;
bearingIsNull = wind.getBearing() == null;
positionIsNull = wind.getPosition() == null;
if (wind.getTimePoint() == null) {
timePointIsNull = true;
this.timePointAsMillis = 0;
} else {
timePointIsNull = false;
this.timePointAsMillis = wind.getTimePoint().asMillis();
}
flags = (byte) ((byte) (bearingIsNull ? BEARING_IS_NULL : (byte) 0) | (positionIsNull ? POSITION_IS_NULL : (byte) 0) | (timePointIsNull ? TIME_POINT_IS_NULL : (byte) 0));
}
@Override
public Position getPosition() {
if ((flags & POSITION_IS_NULL) != 0) {
return null;
} else {
return getCompactPosition();
}
}
protected abstract Position getCompactPosition();
@Override
public TimePoint getTimePoint() {
if ((flags & TIME_POINT_IS_NULL) != 0) {
return null;
} else {
return new CompactTimePoint();
}
}
@Override
public Bearing getBearing() {
if ((flags & BEARING_IS_NULL) != 0) {
return null;
} else {
return getCompactBearing();
}
}
protected abstract Bearing getCompactBearing();
/**
* Make abstract to force implementing subclasses to implement this method.
*/
@Override
public abstract double getKnots();
@Override
public Bearing getFrom() {
if (getBearing() == null) {
return null;
} else {
return getBearing().reverse();
}
}
@Override
public int hashCode() {
return WindImpl.hashCode(getPosition().getLatDeg(), getPosition().getLngDeg(), (flags&TIME_POINT_IS_NULL) != 0?0:timePointAsMillis);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (!(obj instanceof Wind))
return false;
Wind other = (Wind) obj;
return Util.equalsWithNull(getPosition(), other.getPosition()) && Util.equalsWithNull(getTimePoint(), other.getTimePoint());
}
@Override
public String toString() {
return ""+getTimePoint()+"@"+getPosition()+": "+getKnots()+"kn from "+getFrom();
}
}
@@ -0,0 +1,75 @@
package com.sap.sailing.domain.common.tracking.impl;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Speed;
/**
* A utility class that "compact" fix implementations such as {@link VeryCompactGPSFixImpl} etc. can use
* to obtain representations of latitudes, longitudes, knot speeds and degree bearings marshalled in
* types that sacrifice very little accuracy from a real-world perspective, yet use a data type for
* encoding that requires fewer bytes than a {@code double} value.<p>
*
* Latitude and longitude values are represented using a signed {@code int} value, scaling their range (-90°..+90° and
* -180°..+180°, respectively over the full {@code int} range. This gives a resolution of 4.6mm for latitudes,
* and at the equator of 9.3mm for longitudes.<p>
*
* Bearings / courses over ground are represented as a signed {@code short} value, mapping the range of -360°..+360° to the
* value range of the {@code short} datatype. This results in a resolution of 0.01°.<p>
*
* Speed values are represented in knots in their compact form; lossy compaction uses a signed {@code short} value for
* the knot speeds, assuming a range of -500kts..+500kts, spreading this range over the value range of the {@code short}
* datatype. This results in a resolution of 0.015kts.<p>
*
* If an attempt is made to obtain a compact form of a knot speed or bearing where the value if out of the range described above,
* a {@link CompactionNotPossibleException} will be thrown. Callers should then transparently resort to the precise
* compaction counterparts for the respective type.
*
* @author Axel Uhl (d043530)
*
*/
public class CompactPositionHelper {
private static final double LAT_SCALE = 90. / (double) (1 << 31); // int
private static final double LNG_SCALE = 180. / (double) (1 << 31); // int
private static final double KNOT_SPEED_SCALE = 500. /* knots; what that we track would ever be faster? */ / (double) (1<<15); // short
private static final double DEGREE_BEARING_SCALE = 360. / (double) (1<<15); // short
// Positions
public static double getLatDeg(int latDegScaled) {
return LAT_SCALE * latDegScaled;
}
public static double getLngDeg(int lngDegScaled) {
return LNG_SCALE * lngDegScaled;
}
public static int getLngDegScaled(Position position) {
return (int) (position.getLngDeg() / LNG_SCALE);
}
public static int getLatDegScaled(Position position) {
return (int) (position.getLatDeg() / LAT_SCALE);
}
// Speeds / Bearings
public static double getKnotSpeed(short knotSpeedScaled) {
return KNOT_SPEED_SCALE * knotSpeedScaled;
}
public static double getDegreeBearing(short degreeBearingScaled) {
return DEGREE_BEARING_SCALE * degreeBearingScaled;
}
public static short getKnotSpeedScaled(Speed speed) throws CompactionNotPossibleException {
final double knotSpeedScaled = speed.getKnots() / KNOT_SPEED_SCALE;
if (knotSpeedScaled > Short.MAX_VALUE || knotSpeedScaled < Short.MIN_VALUE) {
throw new CompactionNotPossibleException("Speed "+speed+" cannot be compacted; "+speed.getKnots()+" does not fit into a signed short value");
}
return (short) knotSpeedScaled;
}
public static short getDegreeBearingScaled(Bearing bearing) {
return (short) (bearing.getDegrees() / DEGREE_BEARING_SCALE);
}
}
@@ -1,161 +0,0 @@
package com.sap.sailing.domain.common.tracking.impl;
import com.sap.sailing.domain.common.AbstractBearing;
import com.sap.sailing.domain.common.AbstractPosition;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.impl.AbstractSpeedWithAbstractBearingImpl;
import com.sap.sailing.domain.common.impl.WindImpl;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.AbstractTimePoint;
public class CompactWindImpl extends AbstractSpeedWithAbstractBearingImpl implements Wind {
private static final long serialVersionUID = -5059956032663387929L;
private final double latDeg;
private final double lngDeg;
private final boolean positionIsNull;
private final boolean bearingIsNull;
private final boolean timePointIsNull;
private final double speedInKnots;
private final double degBearing;
private final long timePointAsMillis;
public class CompactPosition extends AbstractPosition {
private static final long serialVersionUID = 5621506820766614178L;
@Override
public double getLatDeg() {
return latDeg;
}
@Override
public double getLngDeg() {
return lngDeg;
}
@Override
public boolean equals(Object o) {
return this==o || o instanceof Position && getLatDeg() == ((Position) o).getLatDeg() && getLngDeg() == ((Position) o).getLngDeg();
}
}
private class CompactBearing extends AbstractBearing {
private static final long serialVersionUID = -6474909210513108635L;
@Override
public double getDegrees() {
return degBearing;
}
@Override
public double getRadians() {
return degBearing / 180. * Math.PI;
}
@Override
public boolean equals(Object object) {
return this==object || object instanceof Bearing && getDegrees() == ((Bearing) object).getDegrees();
}
}
private class CompactTimePoint extends AbstractTimePoint implements TimePoint {
private static final long serialVersionUID = -2470922642359937437L;
@Override
public long asMillis() {
return timePointAsMillis;
}
}
public CompactWindImpl(Wind wind) {
if (wind.getBearing() == null) {
bearingIsNull = true;
degBearing = 0;
} else {
this.degBearing = wind.getBearing().getDegrees();
bearingIsNull = false;
}
if (wind.getPosition() == null) {
positionIsNull = true;
this.latDeg = 0;
this.lngDeg = 0;
} else {
this.latDeg = wind.getPosition().getLatDeg();
this.lngDeg = wind.getPosition().getLngDeg();
positionIsNull = false;
}
this.speedInKnots = wind.getKnots();
if (wind.getTimePoint() == null) {
timePointIsNull = true;
this.timePointAsMillis = 0;
} else {
timePointIsNull = false;
this.timePointAsMillis = wind.getTimePoint().asMillis();
}
}
@Override
public Position getPosition() {
if (positionIsNull) {
return null;
} else {
return new CompactPosition();
}
}
@Override
public TimePoint getTimePoint() {
if (timePointIsNull) {
return null;
} else {
return new CompactTimePoint();
}
}
@Override
public Bearing getBearing() {
if (bearingIsNull) {
return null;
} else {
return new CompactBearing();
}
}
@Override
public double getKnots() {
return speedInKnots;
}
@Override
public Bearing getFrom() {
if (getBearing() == null) {
return null;
} else {
return getBearing().reverse();
}
}
@Override
public int hashCode() {
return WindImpl.hashCode(latDeg, lngDeg, timePointIsNull?0:timePointAsMillis);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (!(obj instanceof Wind))
return false;
Wind other = (Wind) obj;
return Util.equalsWithNull(getPosition(), other.getPosition()) && Util.equalsWithNull(getTimePoint(), other.getTimePoint());
}
@Override
public String toString() {
return ""+getTimePoint()+"@"+getPosition()+": "+getKnots()+"kn from "+getFrom();
}
}
@@ -0,0 +1,37 @@
package com.sap.sailing.domain.common.tracking.impl;
/**
* Throws when an attempt is made to turn a fix of some sort into a more compact form, probably
* slightly reducing accuracy and/or reducing value ranges, such as for the speeds possible, where
* the fix presented cannot be turned into its compact form without exceeding a range. This would
* lead to an overflow or wrap-around and cannot be tolerated. Clients need to handle this exception
* by skipping compaction and using the original fix instead.
*
* @author Axel Uhl (d043530)
*
*/
public class CompactionNotPossibleException extends Exception {
private static final long serialVersionUID = -723044264487667717L;
public CompactionNotPossibleException() {
super();
}
public CompactionNotPossibleException(String message, Throwable cause, boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public CompactionNotPossibleException(String message, Throwable cause) {
super(message, cause);
}
public CompactionNotPossibleException(String message) {
super(message);
}
public CompactionNotPossibleException(Throwable cause) {
super(cause);
}
}
@@ -0,0 +1,105 @@
package com.sap.sailing.domain.common.tracking.impl;
import com.sap.sailing.domain.common.AbstractBearing;
import com.sap.sailing.domain.common.AbstractPosition;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.impl.AbstractSpeedWithAbstractBearingImpl;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sse.common.TimePoint;
/**
* A compact representation of a GPS fix which collects all primitive-typed attributes in one object to avoid
* memory overhead otherwise created by using too many individual fine-grained objects.<p>
*
* Objects of this type are assumed to be contained in at most one {@link DynamicGPSFixTrackImpl}. It is
* therefore permissible to cache information about validity to speed up the otherwise expensive
* {@link DynamicGPSFixTrackImpl#isValid(PartialNavigableSetView, GPSFix)} computation.
*
* @author Axel Uhl (d043530)
*
*/
public class PreciseCompactGPSFixImpl extends AbstractCompactGPSFixImpl {
private static final long serialVersionUID = 8167588584536992501L;
private final double latDeg;
private final double lngDeg;
/**
* When <code>{@link #whatIsCached}&amp;{@link #IS_ESTIMATED_SPEED_CACHED} != 0</code>, this field tells the estimated speed's
* true "bearing" (true course over ground) in degrees.
*/
private double cachedEstimatedSpeedBearingInDegrees;
/**
* When <code>{@link #whatIsCached}&amp;{@link #IS_ESTIMATED_SPEED_CACHED} != 0</code>, this field tells the estimated speed
* in knots.
*/
private double cachedEstimatedSpeedInKnots;
public class PreciseCompactPosition extends AbstractPosition {
private static final long serialVersionUID = 5621506820766614178L;
@Override
public double getLatDeg() {
return latDeg;
}
@Override
public double getLngDeg() {
return lngDeg;
}
}
private class PreciseCompactEstimatedSpeedBearing extends AbstractBearing {
private static final long serialVersionUID = 8549231429037883121L;
@Override
public double getDegrees() {
return cachedEstimatedSpeedBearingInDegrees;
}
}
private class PreciseCompactEstimatedSpeed extends AbstractSpeedWithAbstractBearingImpl {
private static final long serialVersionUID = -5871855443391817248L;
@Override
public Bearing getBearing() {
return new PreciseCompactEstimatedSpeedBearing();
}
@Override
public double getKnots() {
return cachedEstimatedSpeedInKnots;
}
}
public PreciseCompactGPSFixImpl(Position position, TimePoint timePoint) {
super(timePoint);
latDeg = position.getLatDeg();
lngDeg = position.getLngDeg();
}
public PreciseCompactGPSFixImpl(GPSFix gpsFix) {
this(gpsFix.getPosition(), gpsFix.getTimePoint());
}
@Override
public Position getPosition() {
return new PreciseCompactPosition();
}
@Override
public SpeedWithBearing getCachedEstimatedSpeed() {
assert isEstimatedSpeedCached();
return new PreciseCompactEstimatedSpeed();
}
@Override
public void cacheEstimatedSpeed(SpeedWithBearing estimatedSpeed) {
cachedEstimatedSpeedBearingInDegrees = estimatedSpeed.getBearing().getDegrees();
cachedEstimatedSpeedInKnots = estimatedSpeed.getKnots();
super.cacheEstimatedSpeed(estimatedSpeed);
}
}
@@ -0,0 +1,135 @@
package com.sap.sailing.domain.common.tracking.impl;
import com.sap.sailing.domain.common.AbstractBearing;
import com.sap.sailing.domain.common.AbstractPosition;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.impl.AbstractSpeedWithAbstractBearingImpl;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sse.common.TimePoint;
/**
* A memory-conserving representation of a {@link GPSFixMoving} object that produces the fine-grained
* objects for {@link Position}, {@link SpeedWithBearing}, {@link Bearing} and {@link TimePoint} dynamically
* as thin wrappers around this object which holds all elementary attributes required. This saves several
* object references and object headers.
*
* @author Axel Uhl (d043530)
*
*/
public class PreciseCompactGPSFixMovingImpl extends AbstractCompactGPSFixMovingImpl {
private static final long serialVersionUID = 761582024504236533L;
private final double latDeg;
private final double lngDeg;
private final double knotSpeed;
private final double degBearing;
/**
* When <code>{@link #whatIsCached}&amp;{@link #IS_ESTIMATED_SPEED_CACHED} != 0</code>, this field tells the estimated speed's
* true "bearing" (true course over ground) in degrees.
*/
private double cachedEstimatedSpeedBearingInDegrees;
/**
* When <code>{@link #whatIsCached}&amp;{@link #IS_ESTIMATED_SPEED_CACHED} != 0</code>, this field tells the estimated speed
* in knots.
*/
private double cachedEstimatedSpeedInKnots;
private class PreciseCompactSpeedWithBearing extends AbstractCompactSpeedWithBearing implements SpeedWithBearing {
private static final long serialVersionUID = 1802065090733146728L;
@Override
public double getKnots() {
return knotSpeed;
}
@Override
public Bearing getBearing() {
return new PreciseCompactBearing();
}
}
private class PreciseCompactBearing extends AbstractBearing {
private static final long serialVersionUID = -6474909210513108635L;
@Override
public double getDegrees() {
return degBearing;
}
}
public class PreciseCompactPosition extends AbstractPosition {
private static final long serialVersionUID = 5621506820766614178L;
@Override
public double getLatDeg() {
return latDeg;
}
@Override
public double getLngDeg() {
return lngDeg;
}
}
private class PreciseCompactEstimatedSpeedBearing extends AbstractBearing {
private static final long serialVersionUID = 8549231429037883121L;
@Override
public double getDegrees() {
return cachedEstimatedSpeedBearingInDegrees;
}
}
private class PreciseCompactEstimatedSpeed extends AbstractSpeedWithAbstractBearingImpl {
private static final long serialVersionUID = -5871855443391817248L;
@Override
public Bearing getBearing() {
return new PreciseCompactEstimatedSpeedBearing();
}
@Override
public double getKnots() {
return cachedEstimatedSpeedInKnots;
}
}
public PreciseCompactGPSFixMovingImpl(Position position, TimePoint timePoint, SpeedWithBearing speed) {
super(timePoint);
latDeg = position.getLatDeg();
lngDeg = position.getLngDeg();
knotSpeed = speed.getKnots();
degBearing = speed.getBearing().getDegrees();
}
public PreciseCompactGPSFixMovingImpl(GPSFixMoving gpsFixMoving) {
this(gpsFixMoving.getPosition(), gpsFixMoving.getTimePoint(), gpsFixMoving.getSpeed());
}
@Override
public SpeedWithBearing getSpeed() {
return new PreciseCompactSpeedWithBearing();
}
@Override
public Position getPosition() {
return new PreciseCompactPosition();
}
@Override
public SpeedWithBearing getCachedEstimatedSpeed() {
assert isEstimatedSpeedCached();
return new PreciseCompactEstimatedSpeed();
}
@Override
public void cacheEstimatedSpeed(SpeedWithBearing estimatedSpeed) {
cachedEstimatedSpeedBearingInDegrees = estimatedSpeed.getBearing().getDegrees();
cachedEstimatedSpeedInKnots = estimatedSpeed.getKnots();
super.cacheEstimatedSpeed(estimatedSpeed);
}
}
@@ -0,0 +1,81 @@
package com.sap.sailing.domain.common.tracking.impl;
import com.sap.sailing.domain.common.AbstractBearing;
import com.sap.sailing.domain.common.AbstractPosition;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Wind;
/**
* Keeps the precise values of lat/lng and speed/course.
*
* @author Axel Uhl (d043530)
*
*/
public class PreciseCompactWindImpl extends AbstractCompactWindImpl {
private static final long serialVersionUID = -5059956032663387929L;
private final double latDeg;
private final double lngDeg;
private final double speedInKnots;
private final double degBearing;
public class CompactPosition extends AbstractPosition {
private static final long serialVersionUID = 5621506820766614178L;
@Override
public double getLatDeg() {
return latDeg;
}
@Override
public double getLngDeg() {
return lngDeg;
}
@Override
public boolean equals(Object o) {
return this==o || o instanceof Position && getLatDeg() == ((Position) o).getLatDeg() && getLngDeg() == ((Position) o).getLngDeg();
}
}
private class CompactBearing extends AbstractBearing {
private static final long serialVersionUID = -6474909210513108635L;
@Override
public double getDegrees() {
return degBearing;
}
}
public PreciseCompactWindImpl(Wind wind) {
super(wind);
if (wind.getBearing() == null) {
degBearing = 0;
} else {
this.degBearing = wind.getBearing().getDegrees();
}
if (wind.getPosition() == null) {
this.latDeg = 0;
this.lngDeg = 0;
} else {
this.latDeg = wind.getPosition().getLatDeg();
this.lngDeg = wind.getPosition().getLngDeg();
}
this.speedInKnots = wind.getKnots();
}
@Override
public double getKnots() {
return speedInKnots;
}
@Override
protected Position getCompactPosition() {
return new CompactPosition();
}
@Override
protected Bearing getCompactBearing() {
return new CompactBearing();
}
}
@@ -0,0 +1,128 @@
package com.sap.sailing.domain.common.tracking.impl;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sap.sailing.domain.common.AbstractBearing;
import com.sap.sailing.domain.common.AbstractPosition;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.impl.AbstractSpeedWithAbstractBearingImpl;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sse.common.TimePoint;
/**
* A compact representation of a GPS fix which collects all primitive-typed attributes in one object to avoid
* memory overhead otherwise created by using too many individual fine-grained objects.<p>
*
* Objects of this type are assumed to be contained in at most one {@link DynamicGPSFixTrackImpl}. It is
* therefore permissible to cache information about validity to speed up the otherwise expensive
* {@link DynamicGPSFixTrackImpl#isValid(PartialNavigableSetView, GPSFix)} computation.
*
* @author Axel Uhl (d043530)
*
*/
public class VeryCompactGPSFixImpl extends AbstractCompactGPSFixImpl {
private static final long serialVersionUID = 3943572107870245437L;
private static final Logger logger = Logger.getLogger(VeryCompactGPSFixImpl.class.getName());
/**
* See {@link CompactPositionHelper}
*/
private final int latDegScaled;
/**
* See {@link CompactPositionHelper}
*/
private final int lngDegScaled;
/**
* When <code>{@link #whatIsCached}&amp;{@link #IS_ESTIMATED_SPEED_CACHED} != 0</code>, this field tells the estimated speed's
* true "bearing" (true course over ground) in degrees, scaled into a short value using {@link CompactPositionHelper}.
*/
private short cachedEstimatedSpeedBearingInDegreesScaled;
/**
* When <code>{@link #whatIsCached}&amp;{@link #IS_ESTIMATED_SPEED_CACHED} != 0</code>, this field tells the estimated speed
* in knots, scaled into a short value using {@link CompactPositionHelper}.
*/
private short cachedEstimatedSpeedInKnotsScaled;
public class CompactPosition extends AbstractPosition {
private static final long serialVersionUID = 5621506820766614178L;
@Override
public double getLatDeg() {
return CompactPositionHelper.getLatDeg(latDegScaled);
}
@Override
public double getLngDeg() {
return CompactPositionHelper.getLngDeg(lngDegScaled);
}
}
private class VeryCompactEstimatedSpeedBearing extends AbstractBearing {
private static final long serialVersionUID = 8549231429037883121L;
@Override
public double getDegrees() {
return CompactPositionHelper.getDegreeBearing(cachedEstimatedSpeedBearingInDegreesScaled);
}
}
private class VeryCompactEstimatedSpeed extends AbstractSpeedWithAbstractBearingImpl {
private static final long serialVersionUID = -5871855443391817248L;
@Override
public Bearing getBearing() {
return new VeryCompactEstimatedSpeedBearing();
}
@Override
public double getKnots() {
return CompactPositionHelper.getKnotSpeed(cachedEstimatedSpeedInKnotsScaled);
}
}
public VeryCompactGPSFixImpl(Position position, TimePoint timePoint) {
super(timePoint);
latDegScaled = CompactPositionHelper.getLatDegScaled(position);
lngDegScaled = CompactPositionHelper.getLngDegScaled(position);
}
public VeryCompactGPSFixImpl(GPSFix gpsFix) {
this(gpsFix.getPosition(), gpsFix.getTimePoint());
}
@Override
public Position getPosition() {
return new CompactPosition();
}
@Override
public SpeedWithBearing getCachedEstimatedSpeed() {
assert isEstimatedSpeedCached();
return new VeryCompactEstimatedSpeed();
}
/**
* Under rare circumstances, caching the speed may fail for the compact representation of a GPS fix.
* This can happen if the speed estimated exceeds the range that can be represented in this compact
* form which is less than in the original form where the speed amount is represented as a {@code double}
* value. In this case, a warning message is logged with level {@link Level#FINER}, and the speed remains
* uncached.
*/
@Override
public void cacheEstimatedSpeed(SpeedWithBearing estimatedSpeed) {
try {
cachedEstimatedSpeedInKnotsScaled = CompactPositionHelper.getKnotSpeedScaled(estimatedSpeed);
cachedEstimatedSpeedBearingInDegreesScaled = CompactPositionHelper.getDegreeBearingScaled(estimatedSpeed.getBearing());
super.cacheEstimatedSpeed(estimatedSpeed);
} catch (CompactionNotPossibleException e) {
logger.log(Level.FINER, "Cannot cache estimated speed "+estimatedSpeed+" in compact fix:", e);
}
}
}
@@ -0,0 +1,171 @@
package com.sap.sailing.domain.common.tracking.impl;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sap.sailing.domain.common.AbstractBearing;
import com.sap.sailing.domain.common.AbstractPosition;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.impl.AbstractSpeedWithAbstractBearingImpl;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sse.common.TimePoint;
/**
* A memory-conserving representation of a {@link GPSFixMoving} object that produces the fine-grained
* objects for {@link Position}, {@link SpeedWithBearing}, {@link Bearing} and {@link TimePoint} dynamically
* as thin wrappers around this object which holds all elementary attributes required. This saves several
* object references and object headers.
*
* @author Axel Uhl (d043530)
*
*/
public class VeryCompactGPSFixMovingImpl extends AbstractCompactGPSFixMovingImpl {
private static final long serialVersionUID = 3977983319207618335L;
private static final Logger logger = Logger.getLogger(VeryCompactGPSFixMovingImpl.class.getName());
/**
* See {@link CompactPositionHelper}
*/
private final int latDegScaled;
/**
* See {@link CompactPositionHelper}
*/
private final int lngDegScaled;
/**
* See {@link CompactPositionHelper}
*/
private final short speedInKnotsScaled;
/**
* See {@link CompactPositionHelper}
*/
private final short degreeBearingScaled;
/**
* When <code>{@link #whatIsCached}&amp;{@link #IS_ESTIMATED_SPEED_CACHED} != 0</code>, this field tells the estimated speed's
* true "bearing" (true course over ground) in degrees, scaled into a short value using {@link CompactPositionHelper}.
*/
private short cachedEstimatedSpeedBearingInDegreesScaled;
/**
* When <code>{@link #whatIsCached}&amp;{@link #IS_ESTIMATED_SPEED_CACHED} != 0</code>, this field tells the estimated speed
* in knots, scaled into a short value using {@link CompactPositionHelper}.
*/
private short cachedEstimatedSpeedInKnotsScaled;
public class VeryCompactPosition extends AbstractPosition {
private static final long serialVersionUID = 5621506820766614178L;
@Override
public double getLatDeg() {
return CompactPositionHelper.getLatDeg(latDegScaled);
}
@Override
public double getLngDeg() {
return CompactPositionHelper.getLngDeg(lngDegScaled);
}
}
private class VeryCompactSpeedWithBearing extends AbstractCompactSpeedWithBearing {
private static final long serialVersionUID = 1802065090733146728L;
@Override
public double getKnots() {
return CompactPositionHelper.getKnotSpeed(speedInKnotsScaled);
}
@Override
public Bearing getBearing() {
return new VeryCompactBearing();
}
}
private class VeryCompactBearing extends AbstractBearing {
private static final long serialVersionUID = 8167886382067060570L;
@Override
public double getDegrees() {
return CompactPositionHelper.getDegreeBearing(degreeBearingScaled);
}
}
private class VeryCompactEstimatedSpeedBearing extends AbstractBearing {
private static final long serialVersionUID = 8549231429037883121L;
@Override
public double getDegrees() {
return CompactPositionHelper.getDegreeBearing(cachedEstimatedSpeedBearingInDegreesScaled);
}
}
private class VeryCompactEstimatedSpeed extends AbstractSpeedWithAbstractBearingImpl {
private static final long serialVersionUID = -5871855443391817248L;
@Override
public Bearing getBearing() {
return new VeryCompactEstimatedSpeedBearing();
}
@Override
public double getKnots() {
return CompactPositionHelper.getKnotSpeed(cachedEstimatedSpeedInKnotsScaled);
}
}
public VeryCompactGPSFixMovingImpl(Position position, TimePoint timePoint, SpeedWithBearing speed) throws CompactionNotPossibleException {
super(timePoint);
latDegScaled = CompactPositionHelper.getLatDegScaled(position);
lngDegScaled = CompactPositionHelper.getLngDegScaled(position);
if (speed == null) {
speedInKnotsScaled = 0;
degreeBearingScaled = 0;
} else {
speedInKnotsScaled = CompactPositionHelper.getKnotSpeedScaled(speed);
degreeBearingScaled = CompactPositionHelper.getDegreeBearingScaled(speed.getBearing());
}
}
public VeryCompactGPSFixMovingImpl(GPSFixMoving gpsFixMoving) throws CompactionNotPossibleException {
this(gpsFixMoving.getPosition(), gpsFixMoving.getTimePoint(), gpsFixMoving.getSpeed());
}
@Override
public SpeedWithBearing getSpeed() {
return new VeryCompactSpeedWithBearing();
}
@Override
public Position getPosition() {
return new VeryCompactPosition();
}
@Override
public SpeedWithBearing getCachedEstimatedSpeed() {
assert isEstimatedSpeedCached();
return new VeryCompactEstimatedSpeed();
}
/**
* Under rare circumstances, caching the speed may fail for the compact representation of a GPS fix.
* This can happen if the speed estimated exceeds the range that can be represented in this compact
* form which is less than in the original form where the speed amount is represented as a {@code double}
* value. In this case, a warning message is logged with level {@link Level#FINER}, and the speed remains
* uncached.
*/
@Override
public void cacheEstimatedSpeed(SpeedWithBearing estimatedSpeed) {
try {
cachedEstimatedSpeedInKnotsScaled = CompactPositionHelper.getKnotSpeedScaled(estimatedSpeed);
cachedEstimatedSpeedBearingInDegreesScaled = CompactPositionHelper.getDegreeBearingScaled(estimatedSpeed.getBearing());
super.cacheEstimatedSpeed(estimatedSpeed);
} catch (CompactionNotPossibleException e) {
logger.log(Level.FINER, "Cannot cache estimated speed "+estimatedSpeed+" in compact fix:", e);
}
}
}
@@ -0,0 +1,92 @@
package com.sap.sailing.domain.common.tracking.impl;
import com.sap.sailing.domain.common.AbstractBearing;
import com.sap.sailing.domain.common.AbstractPosition;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Wind;
/**
* Uses scaled, rounded internal representations of the values for lat/lng and speed/course. See
* {@link CompactPositionHelper} for the details of how value ranges and accuracies are affected.
*
* @author Axel Uhl (d043530)
*
*/
public class VeryCompactWindImpl extends AbstractCompactWindImpl {
private static final long serialVersionUID = -5059956032663387929L;
/**
* See {@link CompactPositionHelper}
*/
private final int latDegScaled;
/**
* See {@link CompactPositionHelper}
*/
private final int lngDegScaled;
/**
* See {@link CompactPositionHelper}
*/
private final short speedInKnotsScaled;
/**
* See {@link CompactPositionHelper}
*/
private final short degreeBearingScaled;
public class VeryCompactPosition extends AbstractPosition {
private static final long serialVersionUID = 5621506820766614178L;
@Override
public double getLatDeg() {
return CompactPositionHelper.getLatDeg(latDegScaled);
}
@Override
public double getLngDeg() {
return CompactPositionHelper.getLngDeg(lngDegScaled);
}
}
private class VeryCompactBearing extends AbstractBearing {
private static final long serialVersionUID = -6474909210513108635L;
@Override
public double getDegrees() {
return CompactPositionHelper.getDegreeBearing(degreeBearingScaled);
}
}
public VeryCompactWindImpl(Wind wind) throws CompactionNotPossibleException {
super(wind);
if (wind.getBearing() == null) {
degreeBearingScaled = 0;
} else {
this.degreeBearingScaled = CompactPositionHelper.getDegreeBearingScaled(wind.getBearing());
}
if (wind.getPosition() == null) {
this.latDegScaled = 0;
this.lngDegScaled = 0;
} else {
this.latDegScaled = CompactPositionHelper.getLatDegScaled(wind.getPosition());
this.lngDegScaled = CompactPositionHelper.getLngDegScaled(wind.getPosition());
}
this.speedInKnotsScaled = CompactPositionHelper.getKnotSpeedScaled(wind);
}
@Override
public double getKnots() {
return CompactPositionHelper.getKnotSpeed(speedInKnotsScaled);
}
@Override
protected Position getCompactPosition() {
return new VeryCompactPosition();
}
@Override
protected Bearing getCompactBearing() {
return new VeryCompactBearing();
}
}
@@ -566,8 +566,7 @@ public class SensorFixStoreAndLoadTest {
public static final String TRACK_NAME = "TestFixTrack";
public TestFixTrackImpl(ItemType trackedItem, String trackName) {
super(trackedItem, trackName, TestFixImpl.COLUMNS,
TRACK_NAME + " for " + trackedItem);
super(trackedItem, trackName, TRACK_NAME + " for " + trackedItem);
}
public Double getTextValue(TimePoint timePoint) {
@@ -4,8 +4,8 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import com.sap.sailing.domain.common.tracking.impl.CompactGPSFixImpl;
import com.sap.sailing.domain.common.tracking.impl.CompactGPSFixMovingImpl;
import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixImpl;
import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixMovingImpl;
import com.sap.sailing.domain.common.tracking.impl.DoubleVectorFixImpl;
import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
@@ -26,9 +26,9 @@ public class MockGPSFixMongoServiceFinder extends AbstractTypeBasedServiceFinder
@Override
public FixMongoHandler<?> findService(String fixType) {
if (fixType.equals(GPSFixMovingImpl.class.getName()) ||
fixType.equals(CompactGPSFixMovingImpl.class.getName())) return movingHandler;
fixType.equals(VeryCompactGPSFixMovingImpl.class.getName())) return movingHandler;
if (fixType.equals(GPSFixImpl.class.getName()) ||
fixType.equals(CompactGPSFixImpl.class.getName())) return handler;
fixType.equals(VeryCompactGPSFixImpl.class.getName())) return handler;
if (fixType.equals(DoubleVectorFixImpl.class.getName())) return doubleVectorFixHandler;
return null;
}
@@ -35,14 +35,10 @@ public interface SwissTimingReplayService {
* @param link
* the URL without the implicit "http://" prefix, as obtained, e.g., from
* {@link SwissTimingReplayRace#getLink()}.
* @param raceName TODO
* @param raceID
* the SwissTiming ID for the race
* @param trackerManager TODO
* @param useInternalMarkPassingAlgorithm
* use our own instead of the SwissTiming-provided mark rounding / split times
* @param raceLogStore TODO
* @param regattaLogStore TODO
* @param boatClassNameName
* only required if {@code regattaToAddTo} is {@code null}; used for the retrieval/creation of a default
* regatta
@@ -25,6 +25,7 @@ import com.sap.sailing.domain.tractracadapter.Receiver;
import com.sap.sailing.domain.tractracadapter.TracTracConnectionConstants;
import com.sap.sailing.domain.tractracadapter.TracTracControlPoint;
import com.sap.sailing.domain.tractracadapter.impl.ControlPointAdapter;
import com.sap.sse.common.Duration;
import com.sap.sse.common.Util.Pair;
import com.tractrac.model.lib.api.ModelLocator;
import com.tractrac.model.lib.api.event.CreateModelException;
@@ -36,6 +37,7 @@ import com.tractrac.subscription.lib.api.IRaceSubscriber;
import com.tractrac.subscription.lib.api.ISubscriberFactory;
import com.tractrac.subscription.lib.api.SubscriberInitializationException;
import com.tractrac.subscription.lib.api.SubscriptionLocator;
import com.tractrac.util.lib.api.exceptions.TimeOutException;
/**
* Subclassing tests have to call {@link #addListenersForStoredDataAndStartController(Iterable)} to kick off
@@ -65,6 +67,7 @@ public abstract class AbstractTracTracLiveTest extends StoredTrackBasedTest {
* Default set-up for an STG training session in Weymouth, 2011
* @throws SubscriberInitializationException
* @throws CreateModelException
* @throws TimeOutException
*/
@Before
public void setUp() throws MalformedURLException, IOException, InterruptedException, URISyntaxException, SubscriberInitializationException, ParseException, CreateModelException {
@@ -95,26 +98,30 @@ public abstract class AbstractTracTracLiveTest extends StoredTrackBasedTest {
protected void setUp(URL paramUrl, URI liveUri, URI storedUri) throws FileNotFoundException, MalformedURLException, URISyntaxException, SubscriberInitializationException, CreateModelException {
// Read event data from configuration file
final IRace race = ModelLocator.getEventFactory().createRace(new URI(paramUrl.toString()));
this.race = race;
ISubscriberFactory subscriberFactory = SubscriptionLocator.getSusbcriberFactory();
if (storedUri == null) {
eventSubscriber = subscriberFactory.createEventSubscriber(race.getEvent());
raceSubscriber = subscriberFactory.createRaceSubscriber(race);
} else {
eventSubscriber = subscriberFactory.createEventSubscriber(race.getEvent(), liveUri, storedUri);
raceSubscriber = subscriberFactory.createRaceSubscriber(race, liveUri, storedUri);
}
assertNotNull(race);
// Initialize data controller using live and stored data sources
if (storedUri != null && storedUri.toString().startsWith("file:")) {
try {
URI oldStoredUri = storedUri;
storedUri = new URI(storedUri.toString().replaceFirst("file:/([^/])", "file:////$1"));
logger.info("Replaced storedUri "+oldStoredUri+" by "+storedUri);
} catch (URISyntaxException e) {
e.printStackTrace();
try {
final IRace race = ModelLocator.getEventFactory().createRace(new URI(paramUrl.toString()), (int) /* timeout in milliseconds */ Duration.ONE_MINUTE.asMillis());
this.race = race;
ISubscriberFactory subscriberFactory = SubscriptionLocator.getSusbcriberFactory();
if (storedUri == null) {
eventSubscriber = subscriberFactory.createEventSubscriber(race.getEvent());
raceSubscriber = subscriberFactory.createRaceSubscriber(race);
} else {
eventSubscriber = subscriberFactory.createEventSubscriber(race.getEvent(), liveUri, storedUri);
raceSubscriber = subscriberFactory.createRaceSubscriber(race, liveUri, storedUri);
}
assertNotNull(race);
// Initialize data controller using live and stored data sources
if (storedUri != null && storedUri.toString().startsWith("file:")) {
try {
URI oldStoredUri = storedUri;
storedUri = new URI(storedUri.toString().replaceFirst("file:/([^/])", "file:////$1"));
logger.info("Replaced storedUri "+oldStoredUri+" by "+storedUri);
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
} catch (TimeOutException te) {
throw new RuntimeException(te);
}
// test cases need to start the thread calling addListenersForStoredDataAndStartController
// after adding their listeners
@@ -57,6 +57,6 @@ public class DistanceCacheInvalidationAfterLoadingFinishedTest extends TrackBase
final Distance distance = speed.travel(Duration.ONE_SECOND);
trackedRace.recordFix(competitor, new GPSFixMovingImpl(startPos.translateGreatCircle(bearing, distance), now.plus(Duration.ONE_SECOND), speed));
trackedRace.onStatusChanged(tdl, new TrackedRaceStatusImpl(TrackedRaceStatusEnum.TRACKING, /* progress */ 1.0));
assertEquals(distance.getMeters(), trackedRace.getDistanceTraveled(competitor, now.plus(Duration.ONE_SECOND.times(2))).getMeters(), 0.000001); // ask 1s after the second fix
assertEquals(distance.getMeters(), trackedRace.getDistanceTraveled(competitor, now.plus(Duration.ONE_SECOND.times(2))).getMeters(), 0.01); // ask 1s after the second fix
}
}
@@ -61,6 +61,7 @@ import com.sap.sailing.domain.leaderboard.impl.HighPoint;
import com.sap.sailing.domain.leaderboard.impl.HighPointExtremeSailingSeriesOverall;
import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets10LastBreaksTie;
import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets10Or8AndLastBreaksTie;
import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets12Or8AndLastBreaksTie2017;
import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl;
import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl;
@@ -1454,6 +1455,44 @@ public class LeaderboardScoringAndRankingTest extends LeaderboardScoringAndRanki
assertEquals(new Double(1), leaderboardHighPointESSOverall.getNetPoints(competitors[14], later));
assertEquals(new Double(1), leaderboardHighPointESSOverall.getNetPoints(competitors[15], later));
}
@Test
public void testESS2017WithCompetitorsBeingDSQScoringTwoLessThanLastCompetingCompetitor() throws NoWindException {
Competitor[] competitors = createCompetitors(8).toArray(new Competitor[0]);
TimePoint now = MillisecondsTimePoint.now();
TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000);
FlexibleLeaderboard leaderboardHighPoint12LastBreaksTie2017 = new FlexibleLeaderboardImpl("Test ESS Highpoint",
new ThresholdBasedResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]),
new HighPointFirstGets12Or8AndLastBreaksTie2017(), null);
RaceColumn raceColumn = leaderboardHighPoint12LastBreaksTie2017
.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(competitors)), "R1", /* medalRace */
false);
// test basic scores with best getting 12 and worst getting 5 in a 12 point scheme
assertTrue(leaderboardHighPoint12LastBreaksTie2017.getScoringScheme().getScoreComparator(/* nullScoresAreBetter */ false).compare(
leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[0], later),
leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[3], later)) < 0); // c0 better than c3
assertEquals(8, leaderboardHighPoint12LastBreaksTie2017.getCompetitorsFromBestToWorst(later).size());
assertEquals(new Double(12), leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[0], later));
assertEquals(new Double(5), leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[7], later));
// now let's assume that the first competitor is DSQ so it gets 4 points
leaderboardHighPoint12LastBreaksTie2017.getScoreCorrection().setMaxPointsReason(competitors[0], raceColumn, MaxPointsReason.DSQ);
assertEquals(8, leaderboardHighPoint12LastBreaksTie2017.getCompetitorsFromBestToWorst(later).size());
assertEquals(new Double(12), leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[1], later));
assertEquals(new Double(4), leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[0], later));
assertEquals(new Double(6), leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[7], later));
leaderboardHighPoint12LastBreaksTie2017.getScoreCorrection().setMaxPointsReason(competitors[0], raceColumn, MaxPointsReason.NONE);
assertEquals(new Double(12), leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[0], later));
assertEquals(new Double(5), leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[7], later));
leaderboardHighPoint12LastBreaksTie2017.getScoreCorrection().setMaxPointsReason(competitors[0], raceColumn, MaxPointsReason.DNS);
assertEquals(new Double(12), leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[1], later));
assertEquals(new Double(5), leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[0], later));
assertEquals(new Double(6), leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[7], later));
// two competitors not started leading to tie
leaderboardHighPoint12LastBreaksTie2017.getScoreCorrection().setMaxPointsReason(competitors[1], raceColumn, MaxPointsReason.DNS);
assertEquals(new Double(12), leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[2], later));
assertEquals(new Double(6), leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[1], later));
assertEquals(new Double(6), leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[0], later));
}
@Test
public void testOverallLeaderboardWithESSHighPointScoring() throws NoWindException {
@@ -1538,6 +1577,7 @@ public class LeaderboardScoringAndRankingTest extends LeaderboardScoringAndRanki
assertEquals(c[0], rankedCompetitors.get(0));
assertEquals(c[2], rankedCompetitors.get(1));
}
@Test
public void testHighPointScoringEightWithInterpolationWhenFleetNotComplete() throws NoWindException {
int competitorsCount = 21;
@@ -74,7 +74,7 @@ public class LineAnalysisTest extends TrackBasedTest {
final Distance distanceBehindLine = new MeterDistance(10);
final Position behindMiddleOfLine = lineMiddle.translateGreatCircle(awayFromCourseSideOrthogonalToLine, distanceBehindLine);
trackedRace.getTrack(competitor).add(new GPSFixMovingImpl(behindMiddleOfLine, now, new KnotSpeedWithBearingImpl(5, new DegreeBearingImpl(45))));
assertEquals(distanceBehindLine.getMeters(), trackedRace.getDistanceToStartLine(competitor, now).getMeters(), 0.000001);
assertEquals(distanceBehindLine.getMeters(), trackedRace.getDistanceToStartLine(competitor, now).getMeters(), 0.01);
}
@Test
@@ -90,7 +90,7 @@ public class LineAnalysisTest extends TrackBasedTest {
final Position behindOutsideOfLine = outsideOfLine.translateGreatCircle(awayFromCourseSideOrthogonalToLine, distanceBehindLine);
trackedRace.getTrack(competitor).add(new GPSFixMovingImpl(behindOutsideOfLine, now, new KnotSpeedWithBearingImpl(5, new DegreeBearingImpl(45))));
// the distance is that of the hypotenuse formed by the two cathesuses of 10m and 30m length, respectively:
assertEquals(Math.sqrt(10.*10.+30.*30.), trackedRace.getDistanceToStartLine(competitor, now).getMeters(), 0.001);
assertEquals(Math.sqrt(10.*10.+30.*30.), trackedRace.getDistanceToStartLine(competitor, now).getMeters(), 0.01);
}
private Mark getMark(String name) {
@@ -96,7 +96,7 @@ public class MultipleClassesInRegattaTest {
/* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/
false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, domainFactory,
tracTracUsername, tracTracPassword, TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY,
/* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ true, /* preferReplayIfAvailable */ false);
/* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ true, /* preferReplayIfAvailable */ false, /* timeoutInMillis */ -1);
}
@After
@@ -0,0 +1,77 @@
package com.sap.sailing.domain.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.util.Set;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
/**
* Contains equality assertions for "compact" fixes, allowing for deltas to be specified, assuming
* that the "compact" version of a fix may vary slightly from its original.
*
* @author Axel Uhl (d043530)
*
*/
public class PositionAssert {
public static void assertPositionEquals(Position p1, Position p2, double degreeDelta) {
assertEquals(p1.getLatDeg(), p2.getLatDeg(), degreeDelta);
assertEquals(p1.getLngDeg(), p2.getLngDeg(), degreeDelta);
}
public static void assertGPSFixEquals(GPSFixMoving f1, GPSFixMoving f2, double positionDegreeDelta, double bearingDegreeDelta, double knotDelta) {
assertGPSFixEquals((GPSFix) f1, (GPSFix) f2, positionDegreeDelta);
assertSpeedEquals(f1.getSpeed(), f2.getSpeed(), bearingDegreeDelta, knotDelta);
}
public static void assertBearingEquals(Bearing b1, Bearing b2, double bearingDegreeDelta) {
assertEquals(b1.getDegrees(), b2.getDegrees(), bearingDegreeDelta);
}
public static void assertGPSFixEquals(GPSFix f1, GPSFix f2, double positionDegreeDelta) {
assertPositionEquals(f1.getPosition(), f2.getPosition(), positionDegreeDelta);
assertEquals(f1.getTimePoint(), f2.getTimePoint());
}
public static void assertWindEquals(Wind w1, Wind w2, double posDegDelta, double bearingDegreeDelta, double knotSpeedDelta) {
assertEquals(w1.getTimePoint(), w2.getTimePoint());
assertPositionEquals(w1.getPosition(), w2.getPosition(), posDegDelta);
assertBearingEquals(w1.getBearing(), w2.getBearing(), bearingDegreeDelta);
assertSpeedEquals(w1, w2, bearingDegreeDelta, knotSpeedDelta);
}
public static void assertSpeedEquals(Speed s1, Speed s2, double knotSpeedDelta) {
assertEquals(s1.getKnots(), s2.getKnots(), knotSpeedDelta);
}
public static void assertSpeedEquals(SpeedWithBearing s1, SpeedWithBearing s2, double bearingDegreeDelta, double knotSpeedDelta) {
assertBearingEquals(s1.getBearing(), s2.getBearing(), bearingDegreeDelta);
assertSpeedEquals((Speed) s1, (Speed) s2, knotSpeedDelta);
}
public static void assertWindEquals(Set<Wind> expectedWinds, Set<Wind> actualWinds, double posDegDelta,
double bearingDegreeDelta, double knotSpeedDelta) {
for (final Wind expectedWind : expectedWinds) {
boolean found = false;
for (final Wind actualWind : actualWinds) {
try {
assertWindEquals(expectedWind, actualWind, posDegDelta, bearingDegreeDelta, knotSpeedDelta);
found = true;
break;
} catch (AssertionError e) {
// not equals
}
}
if (!found) {
fail("Expected to find "+expectedWind+" in "+actualWinds+" but didn't");
}
}
}
}
@@ -16,8 +16,11 @@ import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.impl.NauticalMileDistance;
import com.sap.sailing.domain.common.tracking.impl.CompactGPSFixImpl;
import com.sap.sailing.domain.common.tracking.impl.CompactGPSFixMovingImpl;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixImpl;
import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixMovingImpl;
import com.sap.sailing.domain.common.tracking.impl.CompactionNotPossibleException;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class PositionTest {
@@ -32,20 +35,40 @@ public class PositionTest {
@Test
public void testEqualityBetweenCompactAndVerbosePosition() {
Position p1 = new DegreePosition(49.2, 008.3);
CompactGPSFixImpl compactFix = new CompactGPSFixImpl(p1, MillisecondsTimePoint.now());
VeryCompactGPSFixImpl compactFix = new VeryCompactGPSFixImpl(p1, MillisecondsTimePoint.now());
assertNotSame(p1, compactFix.getPosition());
assertEquals(p1, compactFix.getPosition());
PositionAssert.assertPositionEquals(p1, compactFix.getPosition(), 0.000001);
}
@Test
public void testEqualityBetweenCompactAndVerboseSpeedWithBearing() {
public void testCompactFixRange() {
final TimePoint now = MillisecondsTimePoint.now();
assertCompactFixHasEqualLatLng(now, 0., 0.);
assertCompactFixHasEqualLatLng(now, 90., 0.);
assertCompactFixHasEqualLatLng(now, -90., 0.);
assertCompactFixHasEqualLatLng(now, 0., 180.);
assertCompactFixHasEqualLatLng(now, 0., -180.);
assertCompactFixHasEqualLatLng(now, 45., 45.);
assertCompactFixHasEqualLatLng(now, 0., -45.);
assertCompactFixHasEqualLatLng(now, -45., 45.);
assertCompactFixHasEqualLatLng(now, 0., 45.);
}
private void assertCompactFixHasEqualLatLng(final TimePoint now, final double latDeg, final double lngDeg) {
GPSFix fix = new VeryCompactGPSFixImpl(new DegreePosition(latDeg, lngDeg), now);
assertEquals(latDeg, fix.getPosition().getLatDeg(), 0.0000001);
assertEquals(lngDeg, fix.getPosition().getLngDeg(), 0.0000001);
}
@Test
public void testEqualityBetweenCompactAndVerboseSpeedWithBearing() throws CompactionNotPossibleException {
Position p1 = new DegreePosition(49.2, 008.3);
SpeedWithBearing swb = new KnotSpeedWithBearingImpl(12, new DegreeBearingImpl(123));
CompactGPSFixMovingImpl compactFix = new CompactGPSFixMovingImpl(p1, MillisecondsTimePoint.now(), swb);
VeryCompactGPSFixMovingImpl compactFix = new VeryCompactGPSFixMovingImpl(p1, MillisecondsTimePoint.now(), swb);
assertNotSame(p1, compactFix.getPosition());
assertEquals(p1, compactFix.getPosition());
PositionAssert.assertPositionEquals(p1, compactFix.getPosition(), 0.000001);
assertNotSame(swb, compactFix.getSpeed());
assertEquals(swb, compactFix.getSpeed());
PositionAssert.assertSpeedEquals(swb, compactFix.getSpeed(), /* bearing deg delta */ 0.1, /* knot speed delta */ 0.1);
}
@Test
@@ -338,7 +338,7 @@ public class ReachingLegTest extends TrackBasedTest {
Distance plattnersDistanceToLeader = getTrackedRace().getWindwardDistanceToCompetitorFarthestAhead(plattner, timePointToConsider, WindPositionMode.LEG_MIDDLE);
assertEquals(0., schomaekersDistanceToLeader.getMeters(), 0.00001);
// distance to leading Schomaeker expected to be the entire upwind distance plus the offset distance plus half the downwind
assertEquals(distanceOfReachingLeg.getMeters()+windwardDistanceOfUpwindLeg.getMeters()*1.5, plattnersDistanceToLeader.getMeters(), 0.001);
assertEquals(distanceOfReachingLeg.getMeters()+windwardDistanceOfUpwindLeg.getMeters()*1.5, plattnersDistanceToLeader.getMeters(), 0.01);
}
@Test
@@ -373,9 +373,9 @@ public class ReachingLegTest extends TrackBasedTest {
.getEstimatedPosition(timePointToConsider, /* extrapolate */false)
.getDistance(
getTrackedRace().getTrack(plattner).getEstimatedPosition(timePointToConsider, /* extrapolate */
false)).getMeters(), 0.00001);
false)).getMeters(), 0.01);
// however, projected onto the leg their distance should be 0
Distance plattnersDistanceToLeader = getTrackedRace().getWindwardDistanceToCompetitorFarthestAhead(plattner, timePointToConsider, WindPositionMode.LEG_MIDDLE);
assertEquals(0., plattnersDistanceToLeader.getMeters(), 0.00001);
assertEquals(0., plattnersDistanceToLeader.getMeters(), 0.01);
}
}
@@ -141,7 +141,7 @@ public class StarbordSideOfStartLineRecognitionTest {
MockedTrackedRaceImpl trackedRace = createTrackedRaceWithMarkPositions(startPortPosition, startStarboardPosition, windwardPosition);
Position p = trackedRace.getOrCreateTrack(trackedRace.getStartLine(now).getStarboardMarkWhileApproachingLine()).
getEstimatedPosition(now, /* extrapolate */ false);
assertEquals(startStarboardPosition, p);
PositionAssert.assertPositionEquals(startStarboardPosition, p, 0.0000001);
}
@Test
@@ -43,7 +43,8 @@ import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.impl.MeterPerSecondSpeedWithDegreeBearingImpl;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.impl.CompactGPSFixMovingImpl;
import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixMovingImpl;
import com.sap.sailing.domain.common.tracking.impl.CompactionNotPossibleException;
import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
@@ -63,6 +64,7 @@ import com.sap.sse.common.Util.Pair;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class TrackTest {
private static final int MILLIS_BETWEEN_FIXES = 3000000;
private DynamicGPSFixTrack<Boat, GPSFixMoving> track;
private GPSFixMovingImpl gpsFix1;
private GPSFixMovingImpl gpsFix2;
@@ -75,7 +77,7 @@ public class TrackTest {
track = new DynamicGPSFixMovingTrackImpl<Boat>(new BoatImpl("MyFirstBoat", new BoatClassImpl("505", /* typicallyStartsUpwind */
true), null), /* millisecondsOverWhichToAverage */5000, /* no smoothening */null);
TimePoint now1 = MillisecondsTimePoint.now();
TimePoint now2 = addMillisToTimepoint(now1, 3);
TimePoint now2 = addMillisToTimepoint(now1, MILLIS_BETWEEN_FIXES);
DegreePosition position1 = new DegreePosition(1, 2);
DegreePosition position2 = new DegreePosition(1, 3);
gpsFix1 = new GPSFixMovingImpl(
@@ -84,19 +86,19 @@ public class TrackTest {
new DegreeBearingImpl(90)));
gpsFix2 = new GPSFixMovingImpl(position2, now2, new KnotSpeedWithBearingImpl(position1.getDistance(position2)
.inTime(now2.asMillis() - gpsFix1.getTimePoint().asMillis()).getKnots(), new DegreeBearingImpl(90)));
TimePoint now3 = addMillisToTimepoint(now2, 3);
TimePoint now3 = addMillisToTimepoint(now2, MILLIS_BETWEEN_FIXES);
Position position3 = new DegreePosition(1, 4);
gpsFix3 = new GPSFixMovingImpl(
position3, now3, new KnotSpeedWithBearingImpl(position2.getDistance(position3)
.inTime(now3.asMillis() - gpsFix2.getTimePoint().asMillis()).getKnots(),
new DegreeBearingImpl(0)));
TimePoint now4 = addMillisToTimepoint(now3, 3);
TimePoint now4 = addMillisToTimepoint(now3, MILLIS_BETWEEN_FIXES);
Position position4 = new DegreePosition(3, 4);
gpsFix4 = new GPSFixMovingImpl(
position4, now4, new KnotSpeedWithBearingImpl(position3.getDistance(position4)
.inTime(now4.asMillis() - gpsFix3.getTimePoint().asMillis()).getKnots(),
new DegreeBearingImpl(0)));
TimePoint now5 = addMillisToTimepoint(now4, 3);
TimePoint now5 = addMillisToTimepoint(now4, MILLIS_BETWEEN_FIXES);
Position position5 = new DegreePosition(5, 4);
gpsFix5 = new GPSFixMovingImpl(position5, now5, new KnotSpeedWithBearingImpl(position4.getDistance(position5)
.inTime(now5.asMillis() - gpsFix4.getTimePoint().asMillis()).getKnots(), new DegreeBearingImpl(0)));
@@ -106,6 +108,7 @@ public class TrackTest {
track.addGPSFix(gpsFix4);
track.addGPSFix(gpsFix5);
}
/**
* Tests for a incorrect method of estimating Positions
*/
@@ -128,7 +131,6 @@ public class TrackTest {
assertEquals(45, track.getEstimatedPosition(t1.plus(15), true).getLatDeg(), 0.00000001);
assertEquals(60, track.getEstimatedPosition(t1.plus(20), true).getLatDeg(), 0.00000001);
assertEquals(90, track.getEstimatedPosition(t1.plus(30), true).getLatDeg(), 0.00000001);
} finally {
track.unlockAfterRead();
}
@@ -221,7 +223,7 @@ public class TrackTest {
now = nextNow;
}
assertEquals(speed.travel(start, now.minus(NUMBER_OF_FIXES_AT_SAME_POSITION * 1000)).getMeters(),
track.getDistanceTraveled(start, now).getMeters(), 0.00000001);
track.getDistanceTraveled(start, now).getMeters(), 0.01);
}
private void addFixesWithSamePositionButProgressingTime(DynamicGPSFixTrack<Object, GPSFixMoving> track,
@@ -320,7 +322,7 @@ public class TrackTest {
cacheBarrier.await(20, TimeUnit.SECONDS);
cacheDone.await(20, TimeUnit.SECONDS);
testDone.await();
assertEquals(2., maxSpeed[0], 0.001);
assertEquals(2., maxSpeed[0], 0.1);
}
@Test
@@ -366,12 +368,12 @@ public class TrackTest {
1, new DegreeBearingImpl(123)));
track.addGPSFix(fix3);
assertEquals(2., track.getMaximumSpeedOverGround(new MillisecondsTimePoint(0), new MillisecondsTimePoint(7200000)).
getB().getKnots(), 0.001); // produces a cache entry that ends
getB().getKnots(), 0.01); // produces a cache entry that ends
GPSFixMoving fix4 = new GPSFixMovingImpl(new DegreePosition(0, 0), new MillisecondsTimePoint(10800000), new KnotSpeedWithBearingImpl(
1, new DegreeBearingImpl(123)));
track.addGPSFix(fix4);
assertEquals(2., track.getMaximumSpeedOverGround(new MillisecondsTimePoint(0), new MillisecondsTimePoint(10800000)).
getB().getKnots(), 0.001);
getB().getKnots(), 0.01);
}
@Test
@@ -383,7 +385,7 @@ public class TrackTest {
2, new DegreeBearingImpl(123)));
track.addGPSFix(fast);
assertEquals(2., track.getMaximumSpeedOverGround(new MillisecondsTimePoint(0), new MillisecondsTimePoint(10800000)).
getB().getKnots(), 0.001);
getB().getKnots(), 0.01);
track.addGPSFix(slow);
track.lockForRead();
try {
@@ -392,7 +394,7 @@ public class TrackTest {
track.unlockAfterRead();
}
assertEquals(1., track.getMaximumSpeedOverGround(new MillisecondsTimePoint(0), new MillisecondsTimePoint(10800000)).
getB().getKnots(), 0.001);
getB().getKnots(), 0.01);
}
@Test
@@ -405,7 +407,7 @@ public class TrackTest {
2, new DegreeBearingImpl(123)));
track.addGPSFix(fix2);
assertEquals(2., track.getMaximumSpeedOverGround(new MillisecondsTimePoint(0), new MillisecondsTimePoint(7200000)).
getB().getKnots(), 0.001); // produces a cache entry that ends
getB().getKnots(), 0.01); // produces a cache entry that ends
GPSFixMoving fix3 = new GPSFixMovingImpl(new DegreePosition(0, 0), new MillisecondsTimePoint(7200000), new KnotSpeedWithBearingImpl(
1, new DegreeBearingImpl(123)));
track.addGPSFix(fix3);
@@ -413,7 +415,7 @@ public class TrackTest {
1, new DegreeBearingImpl(123)));
track.addGPSFix(fix4);
assertEquals(2., track.getMaximumSpeedOverGround(new MillisecondsTimePoint(0), new MillisecondsTimePoint(10800000)).
getB().getKnots(), 0.001);
getB().getKnots(), 0.01);
}
/**
@@ -462,7 +464,7 @@ public class TrackTest {
track.addGPSFix(f2);
track.addGPSFix(f3);
SpeedWithBearing average = track.getEstimatedSpeed(t2);
assertEquals(0, average.getBearing().getDegrees(), 0.00001);
PositionAssert.assertBearingEquals(new DegreeBearingImpl(0), average.getBearing(), 0.1);
}
@Test
@@ -477,7 +479,7 @@ public class TrackTest {
track.addGPSFix(f1);
track.addGPSFix(f2);
track.addGPSFix(f3);
assertEquals(f2, track.getFirstFixAtOrAfter(f2.getTimePoint())); // expect the fix to still be valid, but only its provided speed shall be ignored
PositionAssert.assertGPSFixEquals(f2, track.getFirstFixAtOrAfter(f2.getTimePoint()), /* pos deg delta */ 0.00001, /* bearing deg delta */ 0.01, /* knot delta */ 0.01); // expect the fix to still be valid, but only its provided speed shall be ignored
}
@Test
@@ -492,7 +494,7 @@ public class TrackTest {
track.addGPSFix(f1);
track.addGPSFix(f2);
track.addGPSFix(f3);
assertEquals(f3, track.getFirstFixAtOrAfter(f2.getTimePoint())); // expect the f2 fix to be invalid
PositionAssert.assertGPSFixEquals(f3, track.getFirstFixAtOrAfter(f2.getTimePoint()), /* pos deg delta */ 0.00001, /* bearing deg delta */ 0.01, /* knot delta */ 0.01); // expect the fix to still be valid, but only its provided speed shall be ignored
}
@Test
@@ -508,7 +510,7 @@ public class TrackTest {
track.addGPSFix(f2);
track.addGPSFix(f3);
// expect the f2 fix to be invalid because provided speed is too different
assertEquals(0, f3.getPosition().getDistance(track.getFirstFixAtOrAfter(f2.getTimePoint()).getPosition()).getMeters(), 0.001);
assertEquals(0, f3.getPosition().getDistance(track.getFirstFixAtOrAfter(f2.getTimePoint()).getPosition()).getMeters(), 0.01);
}
@Test
@@ -523,9 +525,9 @@ public class TrackTest {
track.addGPSFix(f1);
track.addGPSFix(f2);
track.addGPSFix(f3);
assertEquals(f2, track.getFirstFixAtOrAfter(f2.getTimePoint())); // expect the fix to still be valid, but only its provided speed shall be ignored
PositionAssert.assertGPSFixEquals(f2, track.getFirstFixAtOrAfter(f2.getTimePoint()), /* pos deg delta */ 0.00001, /* bearing deg delta */ 0.01, /* knot delta */ 0.01); // expect the fix to still be valid, but only its provided speed shall be ignored
SpeedWithBearing average = track.getEstimatedSpeed(t2);
assertEquals(1, average.getKnots(), 0.001);
assertEquals(1, average.getKnots(), 0.01);
}
@Test
@@ -541,7 +543,7 @@ public class TrackTest {
track.addGPSFix(f2);
track.addGPSFix(f3);
SpeedWithBearing average = track.getRawEstimatedSpeed(t2);
assertEquals(0, average.getBearing().getDegrees(), 0.00001);
PositionAssert.assertBearingEquals(new DegreeBearingImpl(0), average.getBearing(), /* deg delta */ 0.1);
}
/**
@@ -560,14 +562,14 @@ public class TrackTest {
Iterator<GPSFixMoving> subsetIter = subset.iterator();
// start iteration
GPSFixMoving firstOfSubset = subsetIter.next();
assertEquals(gpsFix2, firstOfSubset);
PositionAssert.assertGPSFixEquals(gpsFix2, firstOfSubset, /* pos deg delta */ 0.00001, /* bearing deg delta */ 0.01, /* knot delta */ 0.01);
// now add a fix:
TimePoint now6 = addMillisToTimepoint(gpsFix5.getTimePoint(), 3);
track.addGPSFix(new GPSFixMovingImpl(
new DegreePosition(6, 5), now6, new KnotSpeedWithBearingImpl(2, new DegreeBearingImpl(0))));
try {
GPSFixMoving secondOfSubset = subsetIter.next();
assertEquals(gpsFix3, secondOfSubset);
PositionAssert.assertGPSFixEquals(gpsFix3, secondOfSubset, /* pos deg delta */ 0.000001, /* bearing deg delta */ 0.01, /* knot delta */ 0.01);
fail("adding a fix interferes with iteration over subSet of track's fixes");
} catch (ConcurrentModificationException e) {
// this is what we expected
@@ -675,7 +677,7 @@ public class TrackTest {
TimePoint afterTimePoint = new MillisecondsTimePoint(millis + (millis-lastMillis));
Position extrapolatedPosition = track.getEstimatedPosition(afterTimePoint, true);
assertEquals(0.5, fix.getPosition().getDistance(extrapolatedPosition).getMeters()
/ lastFix.getPosition().getDistance(extrapolatedPosition).getMeters(), 0.00001);
/ lastFix.getPosition().getDistance(extrapolatedPosition).getMeters(), 0.01);
}
@Test
@@ -792,7 +794,7 @@ public class TrackTest {
bearing = new DegreeBearingImpl(bearing.getDegrees() + 1);
}
invalidationCalls.clear();
assertEquals(speed.getMetersPerSecond()*(steps-1), track.getDistanceTraveled(now, start).getMeters(), 0.01);
assertEquals(speed.getMetersPerSecond()*(steps-1), track.getDistanceTraveled(now, start).getMeters(), 0.02);
final com.sap.sse.common.Util.Pair<TimePoint, com.sap.sse.common.Util.Pair<TimePoint, Distance>> fullIntervalCacheEntry = distanceCache.getEarliestFromAndDistanceAtOrAfterFrom(now, start);
assertNotNull(fullIntervalCacheEntry); // no more entry for "to"-value start in cache
assertEquals(start, fullIntervalCacheEntry.getA());
@@ -808,7 +810,7 @@ public class TrackTest {
invalidationCalls.clear();
final TimePoint timePointOfLastOriginalFix = track.getLastRawFix().getTimePoint();
assertEquals(speed.getMetersPerSecond() * (steps - 1),
track.getDistanceTraveled(now, timePointOfLastOriginalFix).getMeters(), 0.01);
track.getDistanceTraveled(now, timePointOfLastOriginalFix).getMeters(), 0.02);
final com.sap.sse.common.Util.Pair<TimePoint, com.sap.sse.common.Util.Pair<TimePoint, Distance>> newFullIntervalCacheEntry = distanceCache
.getEarliestFromAndDistanceAtOrAfterFrom(now, timePointOfLastOriginalFix);
assertNotNull(newFullIntervalCacheEntry); // no more entry for "to"-value start in cache
@@ -861,7 +863,7 @@ public class TrackTest {
track.unlockAfterRead();
}
GPSFix polishedLastFix2 = track.getLastFixBefore(new MillisecondsTimePoint(Long.MAX_VALUE)); // get the last smoothened fix...
assertEquals(fix, polishedLastFix2);
PositionAssert.assertGPSFixEquals(fix, polishedLastFix2, /* pos deg delta */ 0.000001);
assertEquals(speed.getMetersPerSecond()*steps, track.getDistanceTraveled(now, start).getMeters(), 0.01);
}
@@ -1003,7 +1005,7 @@ public class TrackTest {
final TimePoint stripTo = start.minus(2*timeBetweenFixesInMillis);
assertEquals(speed.getMetersPerSecond()*(steps-3),
track.getDistanceTraveled(stripFrom, stripTo).getMeters(), 0.01);
assertEquals(speed.getMetersPerSecond()*(steps-1), track.getDistanceTraveled(now, start).getMeters(), 0.01);
assertEquals(speed.getMetersPerSecond()*(steps-1), track.getDistanceTraveled(now, start).getMeters(), 0.02);
assertTrue(invalidationCalls.isEmpty());
// expect a cache entry exactly for the strip's boundaries
com.sap.sse.common.Util.Pair<TimePoint, com.sap.sse.common.Util.Pair<TimePoint, Distance>> stripCacheEntry = distanceCache.getEarliestFromAndDistanceAtOrAfterFrom(stripFrom, stripTo);
@@ -1054,8 +1056,7 @@ public class TrackTest {
assertEquals(gpsFix2.getPosition().getLatDeg(), estimatedPosNew.getLatDeg(), 0.5);
assertEquals(gpsFix2.getPosition().getLngDeg(), estimatedPosNew.getLngDeg(), 0.5);
SpeedWithBearing estimatedSpeedNew = track.getEstimatedSpeed(normalFixesTime);
assertEquals(estimatedSpeed.getKnots(), estimatedSpeedNew.getKnots(), 0.001);
assertEquals(estimatedSpeed.getBearing().getDegrees(), estimatedSpeedNew.getBearing().getDegrees(), 0.001);
PositionAssert.assertSpeedEquals(estimatedSpeed, estimatedSpeedNew, /* bearing deg delta */ 0.1, /* knot speed delta */ 0.1);
}
@Test
@@ -1262,21 +1263,21 @@ public class TrackTest {
@Test
public void testFrequency() {
assertEquals(3, track.getAverageIntervalBetweenFixes().asMillis());
assertEquals(3, track.getAverageIntervalBetweenRawFixes().asMillis());
assertEquals(MILLIS_BETWEEN_FIXES, track.getAverageIntervalBetweenFixes().asMillis());
assertEquals(MILLIS_BETWEEN_FIXES, track.getAverageIntervalBetweenRawFixes().asMillis());
}
@Test
public void testEstimatedSpeedCaching() {
public void testEstimatedSpeedCaching() throws CompactionNotPossibleException {
GPSFixMoving originalFix = new GPSFixMovingImpl(new DegreePosition(12, 34), MillisecondsTimePoint.now(),
new KnotSpeedWithBearingImpl(9, new DegreeBearingImpl(123)));
GPSFixMoving fix = new CompactGPSFixMovingImpl(originalFix);
GPSFixMoving fix = new VeryCompactGPSFixMovingImpl(originalFix);
assertFalse(fix.isEstimatedSpeedCached());
final KnotSpeedWithBearingImpl estimatedSpeed = new KnotSpeedWithBearingImpl(9.1, new DegreeBearingImpl(124));
fix.cacheEstimatedSpeed(estimatedSpeed);
assertTrue(fix.isEstimatedSpeedCached());
SpeedWithBearing cachedEstimatedSpeed = fix.getCachedEstimatedSpeed();
assertEquals(estimatedSpeed, cachedEstimatedSpeed);
PositionAssert.assertSpeedEquals(estimatedSpeed, cachedEstimatedSpeed, /* bearing deg delta */ 0.1, /* knot speed delta */ 0.1);
fix.invalidateEstimatedSpeedCache();
assertFalse(fix.isEstimatedSpeedCached());
}
@@ -1287,8 +1288,9 @@ public class TrackTest {
assertFalse(compactFix3.isEstimatedSpeedCached());
SpeedWithBearing fix3EstimatedSpeed = track.getEstimatedSpeed(gpsFix3.getTimePoint());
assertTrue(compactFix3.isEstimatedSpeedCached());
assertEquals(fix3EstimatedSpeed, compactFix3.getCachedEstimatedSpeed());
assertEquals(fix3EstimatedSpeed, track.getEstimatedSpeed(gpsFix3.getTimePoint())); // fetch again from the cache
PositionAssert.assertSpeedEquals(fix3EstimatedSpeed, compactFix3.getCachedEstimatedSpeed(), /* bearing deg delta */ 0.1, /* knot speed delta */ 0.1);
PositionAssert.assertSpeedEquals(fix3EstimatedSpeed, track.getEstimatedSpeed(gpsFix3.getTimePoint()),
/* bearing deg delta */ 0.1, /* knot speed delta */ 0.1); // fetch again from the cache
// assuming that all test fixes are within a few milliseconds and the averaging interval is much larger than that,
// adding a single fix in the middle should invalidate the cache
track.add(new GPSFixMovingImpl(gpsFix3.getPosition(), gpsFix3.getTimePoint().plus(1), gpsFix3.getSpeed()));
@@ -68,7 +68,9 @@ public class UnicodeCharactersInCompetitorNamesTest {
/* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l,
/* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/
false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, domainFactory,
"tracTest", "tracTest", "", "", /* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ true, /* preferReplayIfAvailable */ false), RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS);
"tracTest", "tracTest", "", "", /* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ true,
/* preferReplayIfAvailable */ false, /* timeoutInMillis */ (int) RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS),
RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS);
Iterable<Competitor> competitors = fourtyninerYellow_2.getRaceHandle().getRace().getCompetitors();
for (Competitor competitor : competitors) {
@@ -186,9 +186,9 @@ public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest
};
Wind estimatedWindDirection = track.getAveragedWind(/* position */ null, checkTime);
assertNotNull(estimatedWindDirection);
assertEquals(185., estimatedWindDirection.getBearing().getDegrees(), 0.00000001);
assertEquals(185., estimatedWindDirection.getBearing().getDegrees(), 0.1);
assertFalse(cachedFixes.isEmpty());
assertEquals(185., cachedFixes.values().iterator().next().getObject().getBearing().getDegrees(), 0.00000001);
assertEquals(185., cachedFixes.values().iterator().next().getObject().getBearing().getDegrees(), 0.1);
// now invert leg's type by moving the top mark along the wind from the leeward gate:
Iterator<Waypoint> waypointsIter = getTrackedRace().getRace().getCourse().getWaypoints().iterator();
Waypoint leewardMark = waypointsIter.next();
@@ -202,7 +202,7 @@ public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest
assertEquals(LegType.DOWNWIND, firstLeg.getLegType(fixTime));
Wind estimatedWindDirectionDownwind = track.getAveragedWind(/* position */ null, checkTime);
assertNotNull(estimatedWindDirectionDownwind);
assertEquals(185., estimatedWindDirectionDownwind.getBearing().getDegrees(), 0.00000001);
assertEquals(185., estimatedWindDirectionDownwind.getBearing().getDegrees(), 0.1);
}
@Test
@@ -229,15 +229,15 @@ public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest
};
Wind estimatedWindDirection = track.getAveragedWind(/* position */ null, checkTime);
assertNotNull(estimatedWindDirection);
assertEquals(185., estimatedWindDirection.getBearing().getDegrees(), 0.00000001);
assertEquals(185., estimatedWindDirection.getBearing().getDegrees(), 0.1);
assertFalse(cachedFixes.isEmpty());
assertEquals(185., cachedFixes.values().iterator().next().getObject().getBearing().getDegrees(), 0.00000001);
assertEquals(185., cachedFixes.values().iterator().next().getObject().getBearing().getDegrees(), 0.1);
// now clear set of cached fixes, ask again and ensure nothing is cached again:
cachedFixes.clear();
Wind estimatedWindDirectionCached = track.getAveragedWind(/* position */ null, checkTime);
assertTrue(cachedFixes.isEmpty());
assertNotNull(estimatedWindDirectionCached);
assertEquals(185., estimatedWindDirectionCached.getBearing().getDegrees(), 0.00000001);
assertEquals(185., estimatedWindDirectionCached.getBearing().getDegrees(), 0.1);
// now add a GPS fix and make sure the cache is invalidated by adding it inside the averaging interval
now = new MillisecondsTimePoint(checkTime.asMillis() + track.getMillisecondsOverWhichToAverageWind()/2);
setBearingForCompetitor(competitors.get(0), now, 330);
@@ -257,7 +257,7 @@ public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest
setBearingForCompetitor(competitors.get(0), now, 320);
setBearingForCompetitor(competitors.get(1), now, 50);
Wind estimatedWindDirection = getTrackedRace().getEstimatedWindDirection(now);
assertEquals(185., estimatedWindDirection.getBearing().getDegrees(), 0.00000001);
assertEquals(185., estimatedWindDirection.getBearing().getDegrees(), 0.1);
}
@Test
@@ -268,7 +268,7 @@ public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest
setBearingForCompetitor(competitors.get(1), now, 50);
setBearingForCompetitor(competitors.get(2), new MillisecondsTimePoint(0), 100); // this shouldn't disturb the estimation because it's too old
Wind estimatedWindDirection = getTrackedRace().getEstimatedWindDirection(now);
assertEquals(185., estimatedWindDirection.getBearing().getDegrees(), 0.00000001);
assertEquals(185., estimatedWindDirection.getBearing().getDegrees(), 0.1);
}
@Test
@@ -280,7 +280,7 @@ public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest
setBearingForCompetitor(competitors.get(1), now, 50);
setBearingForCompetitor(competitors.get(2), markPassingTimePoint, 100); // this shouldn't disturb the estimation because it's too old
Wind estimatedWindDirection = getTrackedRace().getEstimatedWindDirection(now);
assertEquals(185., estimatedWindDirection.getBearing().getDegrees(), 0.00000001);
assertEquals(185., estimatedWindDirection.getBearing().getDegrees(), 0.1);
}
@Test
@@ -318,7 +318,7 @@ public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest
setBearingForCompetitor(competitors.get(2), now, 135);
setBearingForCompetitor(competitors.get(3), now, 220); // on the same tack, should give no read-out
Wind estimatedWindDirection = getTrackedRace().getEstimatedWindDirection(now);
assertEquals(177.5, estimatedWindDirection.getBearing().getDegrees(), 0.00000001);
assertEquals(177.5, estimatedWindDirection.getBearing().getDegrees(), 0.1);
}
@Test
@@ -43,10 +43,12 @@ import com.sap.sailing.domain.common.confidence.Weigher;
import com.sap.sailing.domain.common.confidence.impl.PositionAndTimePointWeigher;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.impl.WindImpl;
import com.sap.sailing.domain.common.tracking.impl.CompactWindImpl;
import com.sap.sailing.domain.common.tracking.impl.VeryCompactWindImpl;
import com.sap.sailing.domain.common.tracking.impl.CompactionNotPossibleException;
import com.sap.sailing.domain.confidence.ConfidenceBasedWindAverager;
import com.sap.sailing.domain.confidence.ConfidenceFactory;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
@@ -86,7 +88,7 @@ public class WindTest {
track.add(wind1);
track.add(wind2);
Wind average = track.getAveragedWind(pos, middle);
assertEquals(0., average.getBearing().getDifferenceTo(new DegreeBearingImpl(0)).getDegrees(), 0.0001);
PositionAssert.assertBearingEquals(new DegreeBearingImpl(0), average.getBearing().getDifferenceTo(new DegreeBearingImpl(0)), 0.01);
}
@Test
@@ -109,14 +111,14 @@ public class WindTest {
for (Wind wind : track.getFixes()) {
actualWind.add(wind);
}
assertEquals(expectedWind, actualWind);
PositionAssert.assertWindEquals(expectedWind, actualWind, /* pos deg delta */ 0.000001, /* bearing deg delta */ 0.1, /* knot speed delta */ 0.1);
} finally {
track.unlockAfterRead();
}
}
@Test
public void testWindEquality() {
public void testWindEquality() throws CompactionNotPossibleException {
Position p1 = new DegreePosition(1, 2);
Position p2 = new DegreePosition(4, 5);
TimePoint t1 = MillisecondsTimePoint.now();
@@ -124,8 +126,8 @@ public class WindTest {
SpeedWithBearing s1 = new KnotSpeedWithBearingImpl(12, new DegreeBearingImpl(3));
SpeedWithBearing s2 = new KnotSpeedWithBearingImpl(22, new DegreeBearingImpl(123));
Wind w1 = new WindImpl(p1, t1, s1);
Wind w2 = new CompactWindImpl(w1);
assertEquals(w1, w2);
Wind w2 = new VeryCompactWindImpl(w1);
PositionAssert.assertWindEquals(w1, w2, /* pos deg delta */ 0.000001, /* bearing deg delta */ 0.1, /* knot speed delta */ 0.1);
assertEquals(w1.hashCode(), w2.hashCode());
Wind w3 = new WindImpl(p2, t2, s2);
assertFalse(w1.equals(w3));
@@ -163,9 +165,9 @@ public class WindTest {
track.add(wind7);
// interval does bearely reach 20's burst because 0 has 0 length and 1000..30000 has 29000 length
assertEquals(20, track.getAveragedWind(pos, new MillisecondsTimePoint(1)).getKnots(), 0.00000001);
PositionAssert.assertSpeedEquals(new KnotSpeedImpl(20), track.getAveragedWind(pos, new MillisecondsTimePoint(1)), 0.02);
// interval uses the two fixes to the left (0, 1000)=1000 and three to the right (2000, 10000, 30000)=28000
assertEquals(20, track.getAveragedWind(pos, new MillisecondsTimePoint(1001)).getKnots(), 0.00000001);
PositionAssert.assertSpeedEquals(new KnotSpeedImpl(20), track.getAveragedWind(pos, new MillisecondsTimePoint(1001)), 0.02);
// in the middle of the "hole", fetches (0, 1000, 2000, 10000)=10000 and (30000, 40000)=10000, so 20000ms worth of wind
final double averageFor20000 = track.getAveragedWind(pos, new MillisecondsTimePoint(20000)).getKnots();
// value is hard to predict exactly because time difference-based confidences rate fixes closer to 20000ms higher than those further away
@@ -183,8 +185,7 @@ public class WindTest {
Wind wind = new WindImpl(pos, new MillisecondsTimePoint(0), new KnotSpeedWithBearingImpl(10, new DegreeBearingImpl(123)));
track.add(wind);
Wind estimate = track.getAveragedWind(pos, new MillisecondsTimePoint(0));
assertEquals(10, estimate.getKnots(), 0.000000001);
assertEquals(123, estimate.getBearing().getDegrees(), 0.000000001);
PositionAssert.assertSpeedEquals(new KnotSpeedWithBearingImpl(10, new DegreeBearingImpl(123)), estimate, /* deg bearing delta */ 0.1, /* knot speed delta */ 0.01);
}
@Test
@@ -195,8 +196,8 @@ public class WindTest {
track.add(wind);
// we only have one measurement; this should be extrapolated because it's our best guess
Wind estimate = track.getAveragedWind(pos, new MillisecondsTimePoint(1000));
assertEquals(10, estimate.getKnots(), 0.000000001);
assertEquals(123, estimate.getBearing().getDegrees(), 0.000000001);
PositionAssert.assertSpeedEquals(new KnotSpeedImpl(10), estimate, 0.01);
PositionAssert.assertBearingEquals(new DegreeBearingImpl(123), estimate.getBearing(), 0.01);
}
@Test
@@ -208,8 +209,7 @@ public class WindTest {
// we only have one measurement; this should be extrapolated because it's our best guess even if
// the last measurement was longer ago than our smoothening interval
Wind estimate = track.getAveragedWind(pos, new MillisecondsTimePoint(AVERAGING_INTERVAL_MILLIS+1000));
assertEquals(10, estimate.getKnots(), 0.000000001);
assertEquals(123, estimate.getBearing().getDegrees(), 0.000000001);
PositionAssert.assertSpeedEquals(new KnotSpeedWithBearingImpl(10, new DegreeBearingImpl(123)), estimate, /* deg bearing delta */ 0.1, /* knot speed delta */ 0.01);
}
@Test
@@ -221,8 +221,7 @@ public class WindTest {
Wind wind2 = new WindImpl(pos, new MillisecondsTimePoint(1000), new KnotSpeedWithBearingImpl(20, new DegreeBearingImpl(100)));
track.add(wind2);
Wind estimate = track.getAveragedWind(pos, new MillisecondsTimePoint(500));
assertEquals(15, estimate.getKnots(), 0.000000001);
assertEquals(100, estimate.getBearing().getDegrees(), 0.00000001);
PositionAssert.assertSpeedEquals(new KnotSpeedWithBearingImpl(15, new DegreeBearingImpl(100)), estimate, /* deg bearing delta */ 0.1, /* knot speed delta */ 0.01);
}
@Test
@@ -234,8 +233,7 @@ public class WindTest {
Wind wind2 = new WindImpl(pos, new MillisecondsTimePoint(1000), new KnotSpeedWithBearingImpl(10, new DegreeBearingImpl(100)));
track.add(wind2);
Wind estimate = track.getAveragedWind(pos, new MillisecondsTimePoint(2000));
assertEquals(10, estimate.getKnots(), 0.000000001);
assertEquals(105, estimate.getBearing().getDegrees(), 0.6); // some tolerance needed because of time-based confidence
PositionAssert.assertSpeedEquals(new KnotSpeedWithBearingImpl(10, new DegreeBearingImpl(105)), estimate, /* deg bearing delta, some tolerance needed because of time-based confidence */ 0.6, /* knot speed delta */ 0.01);
}
@Test
@@ -266,7 +264,7 @@ public class WindTest {
TimePoint timePoint = new MillisecondsTimePoint(df.parse("2009-07-11T17:31:38").getTime());
Wind result = track.getAveragedWind(null, timePoint);
// expectation: take two from left (because they are closer than AVERAGING_INTERVAL_MILLIS apart), one from right side:
assertEquals((wind1.getKnots() + wind2.getKnots() + wind3.getKnots()) / 3, result.getKnots(), 0.000000001);
assertEquals((wind1.getKnots() + wind2.getKnots() + wind3.getKnots()) / 3, result.getKnots(), 0.01);
assertEquals(80., result.getBearing().getDegrees(), 5);
}
@@ -24,6 +24,7 @@ import java.util.concurrent.TimeUnit;
import org.junit.Test;
import com.sap.sse.common.Duration;
import com.tractrac.model.lib.api.ModelLocator;
import com.tractrac.model.lib.api.data.IControlPassing;
import com.tractrac.model.lib.api.data.IControlPassings;
@@ -66,7 +67,7 @@ public class JorgesTracTracParallelLoadingTest {
threads.add(new Thread(()->{
try {
// Download the parameters file and load the race
IRace race = ModelLocator.getEventFactory().createRace(new URI(paramsURIs.get(index)));
IRace race = ModelLocator.getEventFactory().createRace(new URI(paramsURIs.get(index)), (int) /* timeout in milliseconds */ Duration.ONE_MINUTE.asMillis());
// Create the subscriptions
SubscriptionLogger subscriptionLogger = new SubscriptionLogger(race);
@@ -1,10 +1,9 @@
package com.sap.sailing.domain.tracking.impl;
import static org.junit.Assert.assertEquals;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.test.PositionAssert;
import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
@@ -22,7 +21,7 @@ public abstract class PositionInterpolationAndExtrapolationTest<FixType extends
}
protected void assertPos(Position p, boolean extrapolate) {
assertEquals(p, getTrack().getEstimatedPosition(now, extrapolate));
PositionAssert.assertPositionEquals(p, getTrack().getEstimatedPosition(now, extrapolate), /* deg delta */ 0.0005);
}
protected GPSFixTrack<Object, FixType> getTrack() {
@@ -7,6 +7,7 @@ import java.util.Map;
import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.regattalog.RegattaLogStore;
import com.sap.sailing.domain.tracking.RaceTracker;
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
import com.sap.sailing.domain.tracking.impl.AbstractRaceTrackingConnectivityParametersHandler;
import com.sap.sailing.domain.tractracadapter.DomainFactory;
@@ -88,7 +89,8 @@ public class TracTracConnectivityParamsHandler extends AbstractRaceTrackingConne
map.get(TRAC_TRAC_PASSWORD)==null?null:map.get(TRAC_TRAC_PASSWORD).toString(),
map.get(RACE_STATUS)==null?null:map.get(RACE_STATUS).toString(),
map.get(RACE_VISIBILITY)==null?null:map.get(RACE_VISIBILITY).toString(), isTrackWind(map),
isCorrectWindDirectionByMagneticDeclination(map), /* preferReplayIfAvailable */ true);
isCorrectWindDirectionByMagneticDeclination(map), /* preferReplayIfAvailable */ true,
/* default timeout for obtaining IRace object from params URL */ (int) RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS);
}
@Override
@@ -256,12 +256,13 @@ public interface DomainFactory {
* {@code File}) then if this parameter is {@code true} the race will be loaded from the replay file
* instead of the {@code storedURI}/{@code liveURI} specified. This is particularly useful for restoring
* races if since the last connection the race was migrated to a replay file format.
* @param timeoutInMillis TODO
*/
RaceTrackingConnectivityParameters createTrackingConnectivityParameters(URL paramURL, URI liveURI, URI storedURI,
URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis,
Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
String tracTracUsername, String tracTracPassword, String raceStatus, String raceVisibility, boolean trackWind, boolean correctWindDirectionByMagneticDeclination,
boolean preferReplayIfAvailable) throws Exception;
boolean preferReplayIfAvailable, int timeoutInMillis) throws Exception;
/**
* Removes all knowledge about <code>tractracRace</code> which includes removing it from the race cache, from the
* {@link com.sap.sailing.domain.base.Regatta} and, if a {@link TrackedRace} for the corresponding
@@ -48,13 +48,12 @@ public interface TracTracAdapter {
* @param timeoutInMilliseconds
* if the race definition is not received for the race within this time, the race tracker for that race
* is stopped; use -1 to wait forever
* @param trackWind TODO
* @param correctWindDirectionByMagneticDeclination TODO
*/
RaceHandle addTracTracRace(TrackerManager trackerManager, URL paramURL, URI liveURI, URI storedURI,
URI courseDesignUpdateURI, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
long timeoutInMilliseconds, String tracTracUsername, String tracTracPassword, String raceStatus,
String raceVisibility, boolean trackWind, boolean correctWindDirectionByMagneticDeclination) throws MalformedURLException, FileNotFoundException, URISyntaxException, Exception;
String raceVisibility, boolean trackWind, boolean correctWindDirectionByMagneticDeclination, int timeoutInMillis)
throws MalformedURLException, FileNotFoundException, URISyntaxException, Exception;
/**
* Same as {@link #addTracTracRace(URL, URI, URI, WindStore, long)}, only that start and end of tracking are
@@ -12,12 +12,12 @@ import java.util.logging.Logger;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.RaceTracker;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRegatta;
import com.sap.sailing.domain.tractracadapter.DomainFactory;
import com.sap.sailing.domain.tractracadapter.LoadingQueueDoneCallBack;
import com.sap.sailing.domain.tractracadapter.Receiver;
import com.sap.sse.common.Duration;
import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Triple;
import com.tractrac.model.lib.api.event.IEvent;
@@ -35,7 +35,15 @@ import com.tractrac.subscription.lib.api.IRaceSubscriber;
*/
public abstract class AbstractReceiverWithQueue<A, B, C> implements Runnable, Receiver {
private static Logger logger = Logger.getLogger(AbstractReceiverWithQueue.class.getName());
/**
* The timeout after which to re-try looking for the race definition to have appeared in
* {@link #getTrackedRace(IRace)} in case an infinite timeout (-1) was specified. During this
* short break, {@link #getTrackedRace(IRace)} will check for {@link #hasBeenStoppedPreemptively()}
* and will stop trying to look for the race if this received was stopped preemptively.
*/
private static final long RETRY_TIMEOUT_IN_MILLIS = Duration.ONE_MINUTE.asMillis();
private final LinkedBlockingDeque<Util.Triple<A, B, C>> queue;
private final DomainFactory domainFactory;
private final IEvent tractracEvent;
@@ -53,6 +61,14 @@ public abstract class AbstractReceiverWithQueue<A, B, C> implements Runnable, Re
*/
private boolean receivedEventDuringTimeout;
/**
* Set by {@link #stopPreemptively()}. Invocations of {@link #handleEvent(Triple)} waiting for something
* should wait repeatedly with some timeout, such as, say, one minute, and keep re-trying until what they
* are waiting for has appeared, or this flag has been set in which case they can abort and assume they
* received a stop event.
*/
private boolean stoppedPreemptively;
public AbstractReceiverWithQueue(DomainFactory domainFactory, IEvent tractracEvent,
DynamicTrackedRegatta trackedRegatta, Simulator simulator, IEventSubscriber eventSubscriber, IRaceSubscriber raceSubscriber,
long timeoutInMilliseconds) {
@@ -97,6 +113,11 @@ public abstract class AbstractReceiverWithQueue<A, B, C> implements Runnable, Re
// mark the end and hence terminate the thread by adding a null/null/null event to the queue
queue.clear();
stopAfterProcessingQueuedEvents();
stoppedPreemptively = true;
}
protected boolean hasBeenStoppedPreemptively() {
return stoppedPreemptively;
}
@Override
@@ -195,15 +216,19 @@ public abstract class AbstractReceiverWithQueue<A, B, C> implements Runnable, Re
/**
* Tries to find a {@link TrackedRace} for <code>race</code> in the {@link com.sap.sailing.domain.base.Regatta}
* corresponding to {@link #tractracEvent}, as keyed by the {@link #domainFactory}. Waits for
* {@link RaceTracker#TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS} milliseconds for the
* {@link RaceDefinition} to show up. If it doesn't, <code>null</code> is returned. If the {@link RaceDefinition}
* for <code>race</code> is not found in the {@link com.sap.sailing.domain.base.Regatta}, <code>null</code> is
* returned. If the {@link TrackedRace} for <code>race</code> isn't found in the {@link TrackedRegatta},
* <code>null</code> is returned, too.
* {@link #timeoutInMilliseconds} milliseconds for the {@link RaceDefinition} to show up, or infinitely if
* {@link #timeoutInMilliseconds} is -1, except this receiver is {@link #hasBeenStoppedPreemptively() stopped
* preemptively}. If the race doesn't show up under these boundary conditions, <code>null</code> is returned. If the
* {@link RaceDefinition} for <code>race</code> is not found in the {@link com.sap.sailing.domain.base.Regatta},
* <code>null</code> is returned. If the {@link TrackedRace} for <code>race</code> isn't found in the
* {@link TrackedRegatta}, <code>null</code> is returned, too.
*/
protected DynamicTrackedRace getTrackedRace(IRace race) {
DynamicTrackedRace result = null;
RaceDefinition raceDefinition = getDomainFactory().getAndWaitForRaceDefinition(race.getId(), timeoutInMilliseconds);
final long effectiveTimeoutInMilliseconds = timeoutInMilliseconds == -1 ? RETRY_TIMEOUT_IN_MILLIS : timeoutInMilliseconds;
RaceDefinition raceDefinition;
while ((raceDefinition = getDomainFactory().getAndWaitForRaceDefinition(race.getId(), effectiveTimeoutInMilliseconds)) == null &&
timeoutInMilliseconds == -1 && !hasBeenStoppedPreemptively());
if (raceDefinition != null) {
com.sap.sailing.domain.base.Regatta domainRegatta = trackedRegatta.getRegatta();
if (domainRegatta.getRaceByName(raceDefinition.getName()) != null) {
@@ -537,9 +537,16 @@ public class DomainFactoryImpl implements DomainFactory {
raceCache.put(raceId, raceDefinition);
raceCache.notifyAll();
} else {
logger.warning("Not adding race " + raceDefinition + " to regatta " + trackedRegatta.getRegatta()
final String reasonForNotAddingRaceToRegatta = "Not adding race " + raceDefinition + " to regatta " + trackedRegatta.getRegatta()
+ " because boat class " + raceDefinition.getBoatClass()
+ " doesn't match regatta's boat class " + trackedRegatta.getRegatta().getBoatClass());
+ " doesn't match regatta's boat class " + trackedRegatta.getRegatta().getBoatClass();
logger.warning(reasonForNotAddingRaceToRegatta);
try {
raceDefinitionSetToUpdate.raceNotLoaded(reasonForNotAddingRaceToRegatta);
} catch (Exception e) {
logger.log(Level.INFO, "Something else went wrong while trying to notify the RaceDefinition set that the race "+
raceDefinition+" could not be added to the the regatta "+trackedRegatta.getRegatta(), e);
}
}
} else {
logger.info("Found existing tracked race for race "+raceName+" with ID "+raceId);
@@ -688,11 +695,11 @@ public class DomainFactoryImpl implements DomainFactory {
URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking,
long delayToLiveInMillis, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore,
RegattaLogStore regattaLogStore, String tracTracUsername, String tracTracPassword, String raceStatus,
String raceVisibility, boolean trackWind, boolean correctWindDirectionByMagneticDeclination, boolean preferReplayIfAvailable) throws Exception {
String raceVisibility, boolean trackWind, boolean correctWindDirectionByMagneticDeclination, boolean preferReplayIfAvailable, int timeoutInMillis) throws Exception {
return new RaceTrackingConnectivityParametersImpl(paramURL, liveURI, storedURI, courseDesignUpdateURI,
startOfTracking, endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore,
regattaLogStore, this, tracTracUsername, tracTracPassword, raceStatus, raceVisibility, trackWind, correctWindDirectionByMagneticDeclination,
preferReplayIfAvailable);
preferReplayIfAvailable, timeoutInMillis);
}
@Override
@@ -5,6 +5,7 @@ import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
@@ -54,17 +55,24 @@ public class RaceTrackingConnectivityParametersImpl extends AbstractRaceTracking
* {@code File}) then if this parameter is {@code true} the race will be loaded from the replay file
* instead of the {@code storedURI}/{@code liveURI} specified. This is particularly useful for restoring
* races if since the last connection the race was migrated to a replay file format.
* @param timeoutInMillis
* -1 means no timeout; otherwise, this is the timeout for waiting for the {@link IRace} to be obtained
* from the {@code paramURL} document. A {@link TimeoutException} will result if the timeout applied.
*/
public RaceTrackingConnectivityParametersImpl(URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI,
TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis,
Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm,
RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, DomainFactory domainFactory,
String tracTracUsername, String tracTracPassword, String raceStatus, String raceVisibility,
boolean trackWind, boolean correctWindDirectionByMagneticDeclination, boolean preferReplayIfAvailable)
boolean trackWind, boolean correctWindDirectionByMagneticDeclination, boolean preferReplayIfAvailable, int timeoutInMillis)
throws Exception {
super(trackWind, correctWindDirectionByMagneticDeclination);
this.paramURL = paramURL;
this.tractracRace = ModelLocator.getEventFactory().createRace(new URI(paramURL.toString()));
if (timeoutInMillis == -1) {
this.tractracRace = ModelLocator.getEventFactory().createRace(new URI(paramURL.toString()));
} else {
this.tractracRace = ModelLocator.getEventFactory().createRace(new URI(paramURL.toString()), timeoutInMillis);
}
if (preferReplayIfAvailable && isReplayRace(tractracRace) &&
(!Util.equalsWithNull(liveURI, tractracRace.getLiveURI()) || !Util.equalsWithNull(storedURI, tractracRace.getStoredURI()))) {
logger.info("Replay format available and preferred for race " + tractracRace.getName()
@@ -49,7 +49,7 @@ public class TracTracAdapterImpl implements TracTracAdapter {
public RaceHandle addTracTracRace(TrackerManager trackerManager, URL paramURL, URI liveURI, URI storedURI,
URI courseDesignUpdateURI, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
long timeoutInMilliseconds, String tracTracUsername, String tracTracPassword, String raceStatus,
String raceVisibility, boolean trackWind, boolean correctWindDirectionByMagneticDeclination) throws Exception {
String raceVisibility, boolean trackWind, boolean correctWindDirectionByMagneticDeclination, int timeoutInMillis) throws Exception {
return trackerManager.addRace(
/* regattaToAddTo */null,
getTracTracDomainFactory().createTrackingConnectivityParameters(paramURL, liveURI, storedURI,
@@ -57,7 +57,7 @@ public class TracTracAdapterImpl implements TracTracAdapter {
/* startOfTracking */null,
/* endOfTracking */null, delayToLiveInMillis, /* offsetToStartTimeOfSimulatedRace */null, /* ignoreTracTracMarkPassings */ false,
raceLogStore, regattaLogStore, tracTracUsername, tracTracPassword, raceStatus, raceVisibility, trackWind, correctWindDirectionByMagneticDeclination,
/* preferReplayIfAvailable */ false),
/* preferReplayIfAvailable */ false, timeoutInMillis),
timeoutInMilliseconds);
}
@@ -73,7 +73,8 @@ public class TracTracAdapterImpl implements TracTracAdapter {
getTracTracDomainFactory().createTrackingConnectivityParameters(paramURL, liveURI, storedURI,
courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis,
offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, tracTracUsername, tracTracPassword,
raceStatus, raceVisibility, trackWind, correctWindDirectionByMagneticDeclination, /* preferReplayIfAvailable */ false), timeoutInMilliseconds);
raceStatus, raceVisibility, trackWind, correctWindDirectionByMagneticDeclination, /* preferReplayIfAvailable */ false,
(int) timeoutInMilliseconds), timeoutInMilliseconds);
}
@Override
@@ -647,6 +647,16 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl
notifyRaceCreationListeners();
}
/**
* When this tracker is informed that the race cannot be loaded, e.g., because its boat class does
* not match the regatta's boat class, the tracker will {@link #stop} preemptively.
*/
@Override
public void raceNotLoaded(String reason) throws MalformedURLException, IOException, InterruptedException {
logger.severe("Race for tracker "+this+" with ID "+getID()+" did not load: "+reason+". Stopping tracker.");
stop(/* preemptive */ true);
}
@Override
public void gotLiveDataEvent(ILiveDataEvent liveDataEvent) {
logger.info("Status change in tracker "+getID()+" for race(s) "+getRace()+": "+liveDataEvent);
@@ -43,6 +43,7 @@ import com.sap.sailing.domain.leaderboard.impl.HighPointExtremeSailingSeriesOver
import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets10LastBreaksTie;
import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets10Or8AndLastBreaksTie;
import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets12Or8AndLastBreaksTie;
import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets12Or8AndLastBreaksTie2017;
import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets1LastBreaksTie;
import com.sap.sailing.domain.leaderboard.impl.HighPointLastBreaksTie;
import com.sap.sailing.domain.leaderboard.impl.HighPointWinnerGetsEight;
@@ -124,6 +125,8 @@ public class DomainFactoryImpl extends SharedDomainFactoryImpl implements Domain
return new HighPointFirstGets10Or8AndLastBreaksTie();
case HIGH_POINT_FIRST_GETS_TWELVE_OR_EIGHT:
return new HighPointFirstGets12Or8AndLastBreaksTie();
case HIGH_POINT_FIRST_GETS_TWELVE_OR_EIGHT_2017:
return new HighPointFirstGets12Or8AndLastBreaksTie2017();
case LOW_POINT_WITH_ELIMINATIONS_AND_ROUNDS_WINNER_GETS_07:
return new LowPointWithEliminationsAndRoundsWinnerGets07();
case LOW_POINT_LEAGUE_OVERALL:
@@ -1,6 +1,8 @@
package com.sap.sailing.domain.leaderboard;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.leaderboard.impl.AbstractSimpleLeaderboardImpl;
import com.sap.sse.common.TimePoint;
/**
* Computing the competitors can be a bit expensive, particularly if the fleet is large and there may be suppressed
@@ -23,4 +25,5 @@ import com.sap.sailing.domain.leaderboard.impl.AbstractSimpleLeaderboardImpl;
*/
public interface NumberOfCompetitorsInLeaderboardFetcher {
int getNumberOfCompetitorsInLeaderboard();
int getNumberOfCompetitorsWithoutMaxPointReason(RaceColumn column, TimePoint timePoint);
}
@@ -66,9 +66,12 @@ public interface ScoringScheme extends Serializable {
* otherwise, this parameter tells the number of competitors in the same race as <code>competitor</code>,
* not in the entire <code>raceColumn</code> (those may be more in case of split fleets). The scoring scheme
* may use this number, if available, to infer a penalty score.
* @param timePoint
* an optional timePoint that may help the scheme to determine the penalty related to a certain point in time only.
*/
Double getPenaltyScore(RaceColumn raceColumn, Competitor competitor, MaxPointsReason maxPointsReason,
Integer numberOfCompetitorsInRace, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher);
Integer numberOfCompetitorsInRace, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher,
TimePoint timePoint);
/**
* @param competitor1Scores scores of the first competitor, in the order of race columns in the leaderboard
@@ -252,6 +252,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
*/
public class NumberOfCompetitorsFetcherImpl implements NumberOfCompetitorsInLeaderboardFetcher {
private int numberOfCompetitors = -1;
private int numberOfCompetitorsWithoutMaxPointReason = -1;
@Override
public int getNumberOfCompetitorsInLeaderboard() {
@@ -260,6 +261,18 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
}
return numberOfCompetitors;
}
@Override
public int getNumberOfCompetitorsWithoutMaxPointReason(RaceColumn column, TimePoint timePoint) {
if (numberOfCompetitorsWithoutMaxPointReason == -1) {
numberOfCompetitorsWithoutMaxPointReason = 0;
for (Competitor competitor : getCompetitors()) {
MaxPointsReason maxPointReason = getScoreCorrection().getMaxPointsReason(competitor, column, timePoint);
numberOfCompetitorsWithoutMaxPointReason += maxPointReason == MaxPointsReason.NONE ? 1 : 0;
}
}
return numberOfCompetitorsWithoutMaxPointReason;
}
}
/**
@@ -64,7 +64,7 @@ public class HighPoint extends AbstractScoringSchemeImpl {
@Override
public Double getPenaltyScore(RaceColumn raceColumn, Competitor competitor, MaxPointsReason maxPointsReason, Integer numberOfCompetitorsInRace,
NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher) {
NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher, TimePoint timePoint) {
return 0.0;
}
@@ -0,0 +1,61 @@
package com.sap.sailing.domain.leaderboard.impl;
import com.sap.sailing.domain.abstractlog.race.scoring.RaceLogAdditionalScoringInformationEvent;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetcher;
import com.sap.sse.common.TimePoint;
/**
* {@link HighPointFirstGetsFixedScore} scheme that in most cases applies
* 12 points for the winner. Iff for one race column a {@link RaceLogAdditionalScoringInformationEvent}
* is found then 8 points are applied for the winner.
*
* From Phil, Race Director, on 12.09.2014: "If there is a tie in the regatta score between two or more boats at any time, the tie
* shall be broken in favour of the boat that has won the most races. If a tie still remains, it shall be broken in
* favour of the boat that had the better place at the last race sailed."
*
* 13.1.2 A Boat that did not start, did not finish, or retired shall receive 1 point less than the last boat to complete the race.
* 13.1.3 A Boat that is disqualified from a race, including OCS, shall receive 2 points less than the last boat to complete the race.
*
* @author Simon Marcel Pamies
*/
public class HighPointFirstGets12Or8AndLastBreaksTie2017 extends AbstractHighPointFirstGetsFixedOr8AndLastBreaksTie {
private static final long serialVersionUID = 1L;
public HighPointFirstGets12Or8AndLastBreaksTie2017() {
super(12.0);
}
@Override
public ScoringSchemeType getType() {
return ScoringSchemeType.HIGH_POINT_FIRST_GETS_TWELVE_OR_EIGHT_2017;
}
@Override
public Double getPenaltyScore(RaceColumn raceColumn, Competitor competitor, MaxPointsReason maxPointsReason,
Integer numberOfCompetitorsInRace, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher, TimePoint timePoint) {
Double result;
/**
* 1. Get all competitors without a max point reason.
* 2. This list of competitors is assumed to be all competitors having successfully taken part in the race.
* 3. The score of the last competitor will be 12-size(competitorList)
*
* XXX How about two competitors not finishing a race? They will get the same score. NOR does not state
* anything on how to handle such a situation except that it implicates that this will lead to a tie. No
* update from OC Sport on that question so far (Mar 2017).
*/
int competitorCountWithoutMaxPointReason = numberOfCompetitorsInLeaderboardFetcher.getNumberOfCompetitorsWithoutMaxPointReason(raceColumn, timePoint);
if (maxPointsReason == MaxPointsReason.DNS || maxPointsReason == MaxPointsReason.DNF || maxPointsReason == MaxPointsReason.RET) {
result = (12 - competitorCountWithoutMaxPointReason) * 1.0;
} else if (maxPointsReason == MaxPointsReason.DSQ || maxPointsReason == MaxPointsReason.OCS) {
result = (12 - competitorCountWithoutMaxPointReason - 1) * 1.0;
} else {
result = 0.0;
}
return result;
}
}
@@ -24,7 +24,7 @@ public class HighPointFirstGets1LastBreaksTie extends HighPointFirstGetsFixedSco
@Override
public Double getPenaltyScore(RaceColumn raceColumn, Competitor competitor, MaxPointsReason maxPointsReason, Integer numberOfCompetitorsInRace,
NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher) {
NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher, TimePoint timePoint) {
return -1.0;
}
@@ -57,7 +57,7 @@ public class LowPoint extends AbstractScoringSchemeImpl {
@Override
public Double getPenaltyScore(RaceColumn raceColumn, Competitor competitor, MaxPointsReason maxPointsReason,
Integer numberOfCompetitorsInRace, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher) {
Integer numberOfCompetitorsInRace, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher, TimePoint timePoint) {
Double result;
if (numberOfCompetitorsInRace == null || raceColumn.hasSplitFleetContiguousScoring()) {
result = (double) (numberOfCompetitorsInLeaderboardFetcher.getNumberOfCompetitorsInLeaderboard()+1);
@@ -330,7 +330,7 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
}
}
private AnnotatedMaxPointsReason getAnnotatedMaxPointsReason(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) {
protected AnnotatedMaxPointsReason getAnnotatedMaxPointsReason(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) {
MaxPointsReason maxPointsReason = maxPointsReasons.get(raceColumn.getKey(competitor));
boolean maxPointsReasonExistsButIsNotApplicableForTimePoint;
if (maxPointsReason == null) {
@@ -390,7 +390,7 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
if (correctedNonMaxedScore == null) {
result = scoringScheme.getPenaltyScore(raceColumn, competitor, maxPointsReason.getMaxPointsReason(),
getNumberOfCompetitorsInRace(raceColumn, competitor, numberOfCompetitorsInLeaderboardFetcher),
numberOfCompetitorsInLeaderboardFetcher);
numberOfCompetitorsInLeaderboardFetcher, timePoint);
} else {
result = correctedNonMaxedScore;
}
@@ -18,4 +18,11 @@ import com.sap.sailing.domain.base.RaceDefinition;
@FunctionalInterface
public interface DynamicRaceDefinitionSet {
void addRaceDefinition(RaceDefinition race, DynamicTrackedRace trackedRace);
/**
* Called when the race has not or can not be loaded. A {@code reason} may be provided or may be left {@code null}.
* This default implementation does nothing. Implementing classes may choose, e.g., to stop all further efforts
* trying to track the race.
*/
default void raceNotLoaded(String reason) throws Exception {}
}
@@ -9,8 +9,8 @@ import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
/**
* Centerpiece of a tracking adapter. A tracker is responsible for receiving tracking data for one or more
* {@link RaceDefinition races} that are {@link Regatta#getAllRaces() part of} a common {@link #getRegatta() Event}. Some
* Centerpiece of a tracking adapter. A tracker is responsible for receiving tracking data for one
* {@link RaceDefinition race} that is {@link Regatta#getAllRaces() part of} a common {@link #getRegatta() Event}. Some
* tracker architectures may not be able to deliver all data for the {@link RaceDefinition} when created or started.
* Therefore, {@link #getRace()} may return <code>null</code> if the race information hasn't been received by the
* tracker yet. Through the {@link RaceHandle} returned by {@link #getRaceHandle()} it is also possible to perform a
@@ -43,7 +43,7 @@ public interface RaceTracker {
static long TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS = 60000;
/**
* Stops tracking the races.
* Stops tracking the race.
*
* @param preemptive
* if <code>false</code>, the tracker will continue to process data already received but will stop
@@ -55,7 +55,7 @@ public interface RaceTracker {
com.sap.sailing.domain.base.Regatta getRegatta();
/**
* Returns the races being tracked by this tracker, in a "volatile" way, meaning that if another thread belonging to
* Returns the race being tracked by this tracker, in a "volatile" way, meaning that if another thread belonging to
* this tracker has established the {@link RaceDefinition} for this tracker, other threads will immediately see this
* object. Non-blocking call that returns <code>null</code> if the {@link RaceDefinition} hasn't been created yet,
* e.g., because the course definition hasn't been received yet or the listener for receiving course information
@@ -25,7 +25,13 @@ public interface RaceTrackingConnectivityParameters {
* {@link Regatta} is provided, this will first look up a regatta for the race from
* {@link TrackedRegattaRegistry#getRememberedRegattaForRace(java.io.Serializable)} and if not found will look up or
* create a default regatta based on race data such as an event name and the boat class.
* @param timeoutInMilliseconds TODO
*
* @param timeoutInMilliseconds
* gives the tracker a possibility to abort tracking the race after so many milliseconds of
* unsuccessfully waiting for the connection to be established. Support is optional for
* implementations, and there is no exact specification what must have happened before
* this timeout in order for tracking to continue. So, consider this as a "hint" to the
* tracker.
*/
RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore,
RaceLogResolver raceLogResolver, long timeoutInMilliseconds) throws Exception;
@@ -16,13 +16,6 @@ import com.sap.sse.common.WithID;
public interface SensorFixTrack<ItemType extends WithID & Serializable, FixT extends SensorFix>
extends MappedTrack<ItemType, FixT> {
/**
* Value names to be used when reading values from {@link SensorFix}es by calling {@link SensorFix#get(String)}.
*
* @return the value names that fixes contained in this track support
*/
Iterable<String> getValueNames();
void addListener(SensorFixTrackListener<ItemType, FixT> listener);
void removeListener(SensorFixTrackListener<ItemType, FixT> listener);
@@ -3,7 +3,6 @@ package com.sap.sailing.domain.tracking.impl;
import java.io.Serializable;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.sensordata.BravoSensorDataMetadata;
import com.sap.sailing.domain.common.tracking.BravoFix;
import com.sap.sailing.domain.tracking.BravoFixTrack;
import com.sap.sailing.domain.tracking.DynamicBravoFixTrack;
@@ -28,8 +27,7 @@ public class BravoFixTrackImpl<ItemType extends WithID & Serializable> extends S
* the name of the track by which it can be obtained from the {@link TrackedRace}.
*/
public BravoFixTrackImpl(ItemType trackedItem, String trackName) {
super(trackedItem, trackName, BravoSensorDataMetadata.INSTANCE.getFileColumns(),
BravoFixTrack.TRACK_NAME + " for " + trackedItem);
super(trackedItem, trackName, BravoFixTrack.TRACK_NAME + " for " + trackedItem);
}
@Override
@@ -3,6 +3,8 @@ package com.sap.sailing.domain.tracking.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.NavigableSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence;
import com.sap.sailing.domain.base.SpeedWithConfidence;
@@ -13,6 +15,7 @@ import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.confidence.BearingWithConfidence;
import com.sap.sailing.domain.common.confidence.BearingWithConfidenceCluster;
import com.sap.sailing.domain.common.confidence.ConfidenceBasedAverager;
@@ -22,23 +25,62 @@ import com.sap.sailing.domain.common.confidence.Weigher;
import com.sap.sailing.domain.common.confidence.impl.BearingWithConfidenceImpl;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.impl.CompactGPSFixMovingImpl;
import com.sap.sailing.domain.common.tracking.impl.CompactPositionHelper;
import com.sap.sailing.domain.common.tracking.impl.CompactionNotPossibleException;
import com.sap.sailing.domain.common.tracking.impl.PreciseCompactGPSFixMovingImpl;
import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixMovingImpl;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sse.common.TimePoint;
public class DynamicGPSFixMovingTrackImpl<ItemType> extends GPSFixTrackImpl<ItemType, GPSFixMoving> implements DynamicGPSFixTrack<ItemType, GPSFixMoving> {
private static final Logger logger = Logger.getLogger(DynamicGPSFixMovingTrackImpl.class.getName());
private static final long serialVersionUID = 9111448573301259784L;
private static final double MAX_SPEED_FACTOR_COMPARED_TO_MEASURED_SPEED_FOR_FILTERING = 2;
/**
* Uses lossy compaction of fixes. See {@link CompactPositionHelper}. Use {@link #DynamicGPSFixMovingTrackImpl(Object, long, boolean)}
* to configure lossless compaction.
*/
public DynamicGPSFixMovingTrackImpl(ItemType trackedItem, long millisecondsOverWhichToAverage) {
super(trackedItem, millisecondsOverWhichToAverage);
this(trackedItem, millisecondsOverWhichToAverage, /* losslessCompaction */ false);
}
/**
* @param maxSpeedForSmoothening pass <code>null</code> if you don't want speed-based smoothening
* Uses lossy compaction. Use {@link #DynamicGPSFixMovingTrackImpl(Object, long, Speed, boolean)} to configure
* lossless compaction.
*
* @param maxSpeedForSmoothening
* pass <code>null</code> if you don't want speed-based smoothening
*/
public DynamicGPSFixMovingTrackImpl(ItemType trackedItem, long millisecondsOverWhichToAverage, Speed maxSpeedForSmoothening) {
super(trackedItem, millisecondsOverWhichToAverage, maxSpeedForSmoothening);
this(trackedItem, millisecondsOverWhichToAverage, maxSpeedForSmoothening, /* losslessCompaction */ false);
}
/**
* @param maxSpeedForSmoothening
* pass <code>null</code> if you don't want speed-based smoothening
* @param losslessCompaction
* If {@code true}, {@link Wind} fixes will be compacted in a way that they remain precisely equal to
* their original, without rounding or range limitations different from those of the original
* {@link Wind} fix. Lossy compaction, in contrast, may use a more compact form that, however, has
* stricter limits on ranges and precisions. See {@link CompactPositionHelper} for details on lossy
* compaction.
*/
public DynamicGPSFixMovingTrackImpl(ItemType trackedItem, long millisecondsOverWhichToAverage, Speed maxSpeedForSmoothening, boolean losslessCompaction) {
super(trackedItem, millisecondsOverWhichToAverage, maxSpeedForSmoothening, losslessCompaction);
}
/**
* @param losslessCompaction
* If {@code true}, {@link Wind} fixes will be compacted in a way that they remain precisely equal to
* their original, without rounding or range limitations different from those of the original
* {@link Wind} fix. Lossy compaction, in contrast, may use a more compact form that, however, has
* stricter limits on ranges and precisions. See {@link CompactPositionHelper} for details on lossy
* compaction.
*/
public DynamicGPSFixMovingTrackImpl(ItemType trackedItem, long millisecondsOverWhichToAverage, boolean losslessCompaction) {
super(trackedItem, millisecondsOverWhichToAverage, losslessCompaction);
}
/**
@@ -57,7 +99,14 @@ public class DynamicGPSFixMovingTrackImpl<ItemType> extends GPSFixTrackImpl<Item
@Override
public boolean add(GPSFixMoving fix, boolean replace) {
return super.add(new CompactGPSFixMovingImpl(fix), replace);
GPSFixMoving compactFix;
try {
compactFix = isLosslessCompaction() ? new PreciseCompactGPSFixMovingImpl(fix) : new VeryCompactGPSFixMovingImpl(fix);
} catch (CompactionNotPossibleException e) {
logger.log(Level.FINE, "Couldn't compact fix "+fix+". Using losslessly-compacted fix instead.", e);
compactFix = new PreciseCompactGPSFixMovingImpl(fix);
}
return super.add(compactFix, replace);
}
/**
@@ -1,14 +1,23 @@
package com.sap.sailing.domain.tracking.impl;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.impl.CompactGPSFixImpl;
import com.sap.sailing.domain.common.tracking.impl.CompactPositionHelper;
import com.sap.sailing.domain.common.tracking.impl.PreciseCompactGPSFixImpl;
import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixImpl;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
public class DynamicGPSFixTrackImpl<ItemType> extends GPSFixTrackImpl<ItemType, GPSFix> implements DynamicGPSFixTrack<ItemType, GPSFix> {
private static final long serialVersionUID = 4035953954507697564L;
/**
* By default uses lossy compaction. See {@link CompactPositionHelper}.
*/
public DynamicGPSFixTrackImpl(ItemType trackedItem, long millisecondsOverWhichToAverage) {
super(trackedItem, millisecondsOverWhichToAverage);
this(trackedItem, millisecondsOverWhichToAverage, /* losslessCompaction */ false);
}
public DynamicGPSFixTrackImpl(ItemType trackedItem, long millisecondsOverWhichToAverage, boolean losslessCompaction) {
super(trackedItem, millisecondsOverWhichToAverage, losslessCompaction);
}
public void addGPSFix(GPSFix gpsFix) {
@@ -22,7 +31,8 @@ public class DynamicGPSFixTrackImpl<ItemType> extends GPSFixTrackImpl<ItemType,
@Override
public boolean add(GPSFix fix, boolean replace) {
return super.add(new CompactGPSFixImpl(fix), replace);
return super.add(
isLosslessCompaction() ? new PreciseCompactGPSFixImpl(fix) : new VeryCompactGPSFixImpl(fix), replace);
}
@Override
@@ -22,6 +22,7 @@ import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.TrackedRaceStatusEnum;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.confidence.BearingWithConfidence;
import com.sap.sailing.domain.common.confidence.BearingWithConfidenceCluster;
import com.sap.sailing.domain.common.confidence.ConfidenceBasedAverager;
@@ -34,6 +35,7 @@ import com.sap.sailing.domain.common.impl.NauticalMileDistance;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.WithValidityCache;
import com.sap.sailing.domain.common.tracking.impl.CompactPositionHelper;
import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.GPSTrackListener;
import com.sap.sailing.domain.tracking.TrackedRace;
@@ -45,7 +47,7 @@ import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sse.common.impl.TimeRangeImpl;
import com.sap.sse.util.impl.ArrayListNavigableSet;
public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends MappedTrackImpl<ItemType, FixType>
public abstract class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends MappedTrackImpl<ItemType, FixType>
implements GPSFixTrack<ItemType, FixType> {
private static final Logger logger = Logger.getLogger(GPSFixTrackImpl.class.getName());
private static final long serialVersionUID = 2115321069242514378L;
@@ -121,8 +123,25 @@ public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends MappedTra
private int estimatedSpeedCacheHits;
private int estimatedSpeedCacheMisses;
public GPSFixTrackImpl(ItemType trackedItem, long millisecondsOverWhichToAverage) {
this(trackedItem, millisecondsOverWhichToAverage, DEFAULT_MAX_SPEED_FOR_SMOOTHING);
/**
* If {@code true}, {@link Wind} fixes will be compacted in a way that they remain precisely
* equal to their original, without rounding or range limitations different from those of the
* original {@link Wind} fix. Lossy compaction, in contrast, may use a more compact form that,
* however, has stricter limits on ranges and precisions. See {@link CompactPositionHelper} for
* details on lossy compaction.
*/
private final boolean losslessCompaction;
/**
* @param losslessCompaction
* If {@code true}, {@link Wind} fixes will be compacted in a way that they remain precisely equal to
* their original, without rounding or range limitations different from those of the original
* {@link Wind} fix. Lossy compaction, in contrast, may use a more compact form that, however, has
* stricter limits on ranges and precisions. See {@link CompactPositionHelper} for details on lossy
* compaction.
*/
protected GPSFixTrackImpl(ItemType trackedItem, long millisecondsOverWhichToAverage, boolean losslessCompaction) {
this(trackedItem, millisecondsOverWhichToAverage, DEFAULT_MAX_SPEED_FOR_SMOOTHING, losslessCompaction);
}
/**
@@ -130,7 +149,7 @@ public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends MappedTra
* if <code>null</code>, any fix speed will be accepted as long as fix-provided speed (if any) and
* inferred speed can be matched.
*/
public GPSFixTrackImpl(ItemType trackedItem, long millisecondsOverWhichToAverage, Speed maxSpeedForSmoothening) {
protected GPSFixTrackImpl(ItemType trackedItem, long millisecondsOverWhichToAverage, Speed maxSpeedForSmoothening, boolean losslessCompaction) {
super(trackedItem, /* nameForReadWriteLock */ GPSFixTrackImpl.class.getSimpleName()
+ (trackedItem == null ? "" : (" for " + trackedItem.toString())));
this.millisecondsOverWhichToAverage = millisecondsOverWhichToAverage;
@@ -139,8 +158,13 @@ public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends MappedTra
this.distanceCache = new DistanceCache(trackedItem==null?"null":trackedItem.toString());
this.maxSpeedCache = createMaxSpeedCache();
this.validityCachingSuspended = false;
this.losslessCompaction = losslessCompaction;
}
protected boolean isLosslessCompaction() {
return losslessCompaction;
}
@Override
public void suspendValidityCaching() {
validityCachingSuspended = true;
@@ -21,21 +21,17 @@ public class SensorFixTrackImpl<ItemType extends WithID & Serializable, FixT ext
private static final long serialVersionUID = 6383421895429843002L;
private final Iterable<String> valueNames;
private final String trackName;
private final TrackListenerCollection<ItemType, FixT, SensorFixTrackListener<ItemType, FixT>> listeners;
/**
* @param trackedItem the item this track is mapped to
* @param trackName the name of the track by which it can be obtained from the {@link TrackedRace}.
* @param valueNames the name of the values that can be obtained by fixes contained in the track
* @param nameForReadWriteLock the name to use for the lock object that is used internally
*/
public SensorFixTrackImpl(ItemType trackedItem, String trackName, Iterable<String> valueNames,
String nameForReadWriteLock) {
public SensorFixTrackImpl(ItemType trackedItem, String trackName, String nameForReadWriteLock) {
super(trackedItem, nameForReadWriteLock);
this.trackName = trackName;
this.valueNames = valueNames;
this.listeners = new TrackListenerCollection<>();
}
@@ -57,11 +53,6 @@ public class SensorFixTrackImpl<ItemType extends WithID & Serializable, FixT ext
listeners.getListeners().forEach(notification);
}
@Override
public Iterable<String> getValueNames() {
return valueNames;
}
@Override
public void addListener(SensorFixTrackListener<ItemType, FixT> listener) {
this.listeners.addListener(listener);
@@ -19,7 +19,10 @@ import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.confidence.impl.PositionAndTimePointWeigher;
import com.sap.sailing.domain.common.tracking.impl.CompactWindImpl;
import com.sap.sailing.domain.common.tracking.impl.CompactPositionHelper;
import com.sap.sailing.domain.common.tracking.impl.CompactionNotPossibleException;
import com.sap.sailing.domain.common.tracking.impl.PreciseCompactWindImpl;
import com.sap.sailing.domain.common.tracking.impl.VeryCompactWindImpl;
import com.sap.sailing.domain.confidence.ConfidenceBasedWindAverager;
import com.sap.sailing.domain.confidence.ConfidenceFactory;
import com.sap.sailing.domain.tracking.WindListener;
@@ -56,11 +59,23 @@ public class WindTrackImpl extends TrackImpl<Wind> implements WindTrack {
*/
private transient Set<WindListener> listeners;
/**
* If {@code true}, {@link Wind} fixes will be compacted in a way that they remain precisely
* equal to their original, without rounding or range limitations different from those of the
* original {@link Wind} fix. Lossy compaction, in contrast, may use a more compact form that,
* however, has stricter limits on ranges and precisions. See {@link CompactPositionHelper} for
* details on lossy compaction.
*/
private final boolean losslessCompaction;
public WindTrackImpl(long millisecondsOverWhichToAverage, boolean useSpeed, String nameForReadWriteLock) {
this(millisecondsOverWhichToAverage, DEFAULT_BASE_CONFIDENCE, useSpeed, nameForReadWriteLock);
}
/**
* Uses lossy compaction. See {@link CompactPositionHelper}. Use {@link #WindTrackImpl(long, double, boolean, String, boolean)}
* to select lossless compaction.
*
* @param baseConfidence
* the confidence to attribute to the raw wind fixes in this track
* @param useSpeed
@@ -69,11 +84,29 @@ public class WindTrackImpl extends TrackImpl<Wind> implements WindTrack {
* value for the speed
*/
public WindTrackImpl(long millisecondsOverWhichToAverage, double baseConfidence, boolean useSpeed, String nameForReadWriteLock) {
this(millisecondsOverWhichToAverage, baseConfidence, useSpeed, nameForReadWriteLock, /* losslessCompaction */ false);
}
/**
* Uses lossy compaction. See {@link CompactPositionHelper}.
*
* @param baseConfidence
* the confidence to attribute to the raw wind fixes in this track
* @param useSpeed
* whether the wind speed described by the fixes in this track are usable at all; example for an unusable
* wind speed would be that of an estimation that only estimates the wind direction and uses some default
* value for the speed
* @param losslessCompaction
* whether or not to use lossless compaction; see {@link CompactPositionHelper} for details on lossy
* compaction
*/
public WindTrackImpl(long millisecondsOverWhichToAverage, double baseConfidence, boolean useSpeed, String nameForReadWriteLock, boolean losslessCompaction) {
super(new ArrayListNavigableSet<Timed>(WindComparator.INSTANCE), nameForReadWriteLock);
this.baseConfidence = baseConfidence;
this.millisecondsOverWhichToAverage = millisecondsOverWhichToAverage;
listeners = new HashSet<WindListener>();
this.useSpeed = useSpeed;
this.losslessCompaction = losslessCompaction;
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
@@ -114,12 +147,23 @@ public class WindTrackImpl extends TrackImpl<Wind> implements WindTrack {
@Override
public boolean add(Wind wind, boolean replace) {
final CompactWindImpl compactWind = new CompactWindImpl(wind);
final Wind compactWind = compactify(wind);
final boolean result = super.add(compactWind, replace);
notifyListenersAboutReceive(compactWind);
return result;
}
private Wind compactify(Wind wind) {
Wind compactWind;
try {
compactWind = losslessCompaction ? new PreciseCompactWindImpl(wind) : new VeryCompactWindImpl(wind);
} catch (CompactionNotPossibleException e) {
logger.log(Level.FINE, "Couldn't compact wind fix "+wind+". Using lossless compactification instead.", e);
compactWind = new PreciseCompactWindImpl(wind);
}
return compactWind;
}
private void notifyListenersAboutReceive(Wind wind) {
synchronized (listeners) {
for (WindListener listener : listeners) {
@@ -420,7 +464,8 @@ public class WindTrackImpl extends TrackImpl<Wind> implements WindTrack {
public void remove(Wind wind) {
lockForWrite();
try {
getInternalRawFixes().remove(wind);
// map through "compactify" to account for possible rounding / accuracy differences and still achieve "equals"
getInternalRawFixes().remove(compactify(wind));
} finally {
unlockAfterWrite();
}
@@ -1,5 +1,7 @@
package com.google.gwt.user.client.rpc.core.com.sap.sailing.domain.common.tracking.impl;
import java.util.logging.Logger;
import com.google.gwt.user.client.rpc.CustomFieldSerializer;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.SerializationStreamReader;
@@ -8,46 +10,54 @@ import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.WindImpl;
import com.sap.sailing.domain.common.tracking.impl.CompactWindImpl.CompactPosition;
import com.sap.sailing.domain.common.tracking.impl.VeryCompactWindImpl.VeryCompactPosition;
import com.sap.sailing.domain.common.tracking.impl.CompactionNotPossibleException;
public final class CompactWindImpl {
public static final class CompactPosition_CustomFieldSerializer extends CustomFieldSerializer<CompactPosition> {
private static final Logger logger = Logger.getLogger(CompactWindImpl.class.getName());
public static final class CompactPosition_CustomFieldSerializer extends CustomFieldSerializer<VeryCompactPosition> {
@Override
public boolean hasCustomInstantiateInstance() {
return true;
}
@Override
public CompactPosition instantiateInstance(SerializationStreamReader streamReader)
public VeryCompactPosition instantiateInstance(SerializationStreamReader streamReader)
throws SerializationException {
return instantiate(streamReader);
}
public static CompactPosition instantiate(SerializationStreamReader streamReader) throws SerializationException {
public static VeryCompactPosition instantiate(SerializationStreamReader streamReader) throws SerializationException {
final double latDeg = streamReader.readDouble();
final double lngDeg = streamReader.readDouble();
return (CompactPosition) new com.sap.sailing.domain.common.tracking.impl.CompactWindImpl(new WindImpl(
new DegreePosition(latDeg, lngDeg), /* timePoint */null, new KnotSpeedWithBearingImpl(0,
new DegreeBearingImpl(0)))).getPosition();
try {
return (VeryCompactPosition) new com.sap.sailing.domain.common.tracking.impl.VeryCompactWindImpl(new WindImpl(
new DegreePosition(latDeg, lngDeg), /* timePoint */null, new KnotSpeedWithBearingImpl(0,
new DegreeBearingImpl(0)))).getPosition();
} catch (CompactionNotPossibleException e) {
logger.severe("Internal error: Cannot de-serialize compact position: "+e.getMessage()+"; throwing runtime exception");
throw new RuntimeException(e);
}
}
@Override
public void deserializeInstance(SerializationStreamReader streamReader, CompactPosition instance)
public void deserializeInstance(SerializationStreamReader streamReader, VeryCompactPosition instance)
throws SerializationException {
deserialize(streamReader, instance);
}
public static void deserialize(SerializationStreamReader streamReader, CompactPosition instance) {
public static void deserialize(SerializationStreamReader streamReader, VeryCompactPosition instance) {
// handled by instantiate
}
@Override
public void serializeInstance(SerializationStreamWriter streamWriter, CompactPosition instance)
public void serializeInstance(SerializationStreamWriter streamWriter, VeryCompactPosition instance)
throws SerializationException {
serialize(streamWriter, instance);
}
public static void serialize(SerializationStreamWriter streamWriter, CompactPosition instance)
public static void serialize(SerializationStreamWriter streamWriter, VeryCompactPosition instance)
throws SerializationException {
streamWriter.writeDouble(instance.getLatDeg());
streamWriter.writeDouble(instance.getLngDeg());
@@ -0,0 +1,52 @@
package com.google.gwt.user.client.rpc.core.com.sap.sailing.domain.common.tracking.impl;
import com.google.gwt.user.client.rpc.CustomFieldSerializer;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.SerializationStreamReader;
import com.google.gwt.user.client.rpc.SerializationStreamWriter;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.tracking.impl.PreciseCompactGPSFixImpl.PreciseCompactPosition;
public final class PreciseCompactGPSFixImpl {
public static final class PreciseCompactPosition_CustomFieldSerializer extends CustomFieldSerializer<PreciseCompactPosition> {
@Override
public boolean hasCustomInstantiateInstance() {
return true;
}
@Override
public PreciseCompactPosition instantiateInstance(SerializationStreamReader streamReader)
throws SerializationException {
return instantiate(streamReader);
}
public static PreciseCompactPosition instantiate(SerializationStreamReader streamReader) throws SerializationException {
final double latDeg = streamReader.readDouble();
final double lngDeg = streamReader.readDouble();
return (PreciseCompactPosition) new com.sap.sailing.domain.common.tracking.impl.PreciseCompactGPSFixImpl(
new DegreePosition(latDeg, lngDeg), /* timePoint */null).getPosition();
}
@Override
public void deserializeInstance(SerializationStreamReader streamReader, PreciseCompactPosition instance)
throws SerializationException {
deserialize(streamReader, instance);
}
public static void deserialize(SerializationStreamReader streamReader, PreciseCompactPosition instance) {
// handled by instantiate
}
@Override
public void serializeInstance(SerializationStreamWriter streamWriter, PreciseCompactPosition instance)
throws SerializationException {
serialize(streamWriter, instance);
}
public static void serialize(SerializationStreamWriter streamWriter, PreciseCompactPosition instance)
throws SerializationException {
streamWriter.writeDouble(instance.getLatDeg());
streamWriter.writeDouble(instance.getLngDeg());
}
}
}
@@ -5,9 +5,9 @@ import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.SerializationStreamReader;
import com.google.gwt.user.client.rpc.SerializationStreamWriter;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.tracking.impl.CompactGPSFixImpl.CompactPosition;
import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixImpl.CompactPosition;
public final class CompactGPSFixImpl {
public final class VeryCompactGPSFixImpl {
public static final class CompactPosition_CustomFieldSerializer extends CustomFieldSerializer<CompactPosition> {
@Override
public boolean hasCustomInstantiateInstance() {
@@ -23,7 +23,7 @@ public final class CompactGPSFixImpl {
public static CompactPosition instantiate(SerializationStreamReader streamReader) throws SerializationException {
final double latDeg = streamReader.readDouble();
final double lngDeg = streamReader.readDouble();
return (CompactPosition) new com.sap.sailing.domain.common.tracking.impl.CompactGPSFixImpl(
return (CompactPosition) new com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixImpl(
new DegreePosition(latDeg, lngDeg), /* timePoint */null).getPosition();
}
@@ -0,0 +1,64 @@
package com.google.gwt.user.client.rpc.core.com.sap.sailing.domain.common.tracking.impl;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.google.gwt.user.client.rpc.CustomFieldSerializer;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.SerializationStreamReader;
import com.google.gwt.user.client.rpc.SerializationStreamWriter;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.tracking.impl.CompactionNotPossibleException;
import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixMovingImpl.VeryCompactPosition;
public final class VeryCompactGPSFixMovingImpl {
private static final Logger logger = Logger.getLogger(VeryCompactGPSFixMovingImpl.class.getName());
public static final class VeryCompactPosition_CustomFieldSerializer extends CustomFieldSerializer<VeryCompactPosition> {
@Override
public boolean hasCustomInstantiateInstance() {
return true;
}
@Override
public VeryCompactPosition instantiateInstance(SerializationStreamReader streamReader)
throws SerializationException {
return instantiate(streamReader);
}
public static VeryCompactPosition instantiate(SerializationStreamReader streamReader) throws SerializationException {
final double latDeg = streamReader.readDouble();
final double lngDeg = streamReader.readDouble();
try {
return (VeryCompactPosition) new com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixMovingImpl(
new DegreePosition(latDeg, lngDeg), /* timePoint */null, /* speed with bearing */ null).getPosition();
} catch (CompactionNotPossibleException e) {
logger.log(Level.SEVERE, "Internal error: an object that was a very compact position and was serialized "+
"couldn't be de-serialized again as such an object", e);
throw new RuntimeException(e);
}
}
@Override
public void deserializeInstance(SerializationStreamReader streamReader, VeryCompactPosition instance)
throws SerializationException {
deserialize(streamReader, instance);
}
public static void deserialize(SerializationStreamReader streamReader, VeryCompactPosition instance) {
// handled by instantiate
}
@Override
public void serializeInstance(SerializationStreamWriter streamWriter, VeryCompactPosition instance)
throws SerializationException {
serialize(streamWriter, instance);
}
public static void serialize(SerializationStreamWriter streamWriter, VeryCompactPosition instance)
throws SerializationException {
streamWriter.writeDouble(instance.getLatDeg());
streamWriter.writeDouble(instance.getLngDeg());
}
}
}
@@ -0,0 +1,66 @@
package com.google.gwt.user.client.rpc.core.com.sap.sailing.domain.common.tracking.impl;
import java.util.logging.Logger;
import com.google.gwt.user.client.rpc.CustomFieldSerializer;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.SerializationStreamReader;
import com.google.gwt.user.client.rpc.SerializationStreamWriter;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.WindImpl;
import com.sap.sailing.domain.common.tracking.impl.VeryCompactWindImpl.VeryCompactPosition;
import com.sap.sailing.domain.common.tracking.impl.CompactionNotPossibleException;
public final class VeryCompactWindImpl {
private static final Logger logger = Logger.getLogger(VeryCompactWindImpl.class.getName());
public static final class VeryCompactPosition_CustomFieldSerializer extends CustomFieldSerializer<VeryCompactPosition> {
@Override
public boolean hasCustomInstantiateInstance() {
return true;
}
@Override
public VeryCompactPosition instantiateInstance(SerializationStreamReader streamReader)
throws SerializationException {
return instantiate(streamReader);
}
public static VeryCompactPosition instantiate(SerializationStreamReader streamReader) throws SerializationException {
final double latDeg = streamReader.readDouble();
final double lngDeg = streamReader.readDouble();
try {
return (VeryCompactPosition) new com.sap.sailing.domain.common.tracking.impl.VeryCompactWindImpl(new WindImpl(
new DegreePosition(latDeg, lngDeg), /* timePoint */null, new KnotSpeedWithBearingImpl(0,
new DegreeBearingImpl(0)))).getPosition();
} catch (CompactionNotPossibleException e) {
logger.severe("Internal error: Cannot de-serialize compact position: "+e.getMessage()+"; throwing runtime exception");
throw new RuntimeException(e);
}
}
@Override
public void deserializeInstance(SerializationStreamReader streamReader, VeryCompactPosition instance)
throws SerializationException {
deserialize(streamReader, instance);
}
public static void deserialize(SerializationStreamReader streamReader, VeryCompactPosition instance) {
// handled by instantiate
}
@Override
public void serializeInstance(SerializationStreamWriter streamWriter, VeryCompactPosition instance)
throws SerializationException {
serialize(streamWriter, instance);
}
public static void serialize(SerializationStreamWriter streamWriter, VeryCompactPosition instance)
throws SerializationException {
streamWriter.writeDouble(instance.getLatDeg());
streamWriter.writeDouble(instance.getLngDeg());
}
}
}
@@ -435,6 +435,8 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
String scoringSchemeHighPointEssOverall12Description();
String scoringSchemeHighPointFirstGetsTwelveOrEight();
String scoringSchemeHighPointFirstGetsTwelveOrEightDescription();
String scoringSchemeHighPointFirstGetsTwelveOrEight2017();
String scoringSchemeHighPointFirstGetsTwelveOrEight2017Description();
String scoringSystem();
String createFlexibleLeaderboard();
String createRegattaLeaderboard();
@@ -452,6 +452,8 @@ scoringSchemeHighPointFirstGetsTenOrEight=High Point, winner gets 10 points (or
scoringSchemeHighPointFirstGetsTenOrEightDescription=Score in points. Winner scores 10 points, 2nd - 9 points, .... A tie is broken in favour of the competitor having won most races, if tie remains then tie is broken by the rank in the last race scored.
scoringSchemeHighPointFirstGetsTwelveOrEight=High Point, winner gets 12 points (or 8)
scoringSchemeHighPointFirstGetsTwelveOrEightDescription=Score in points. Winner scores 12 points, 2nd - 11 points, .... A tie is broken in favour of the competitor with the highest score, if tie remains then tie is broken by the rank in the last race scored.
scoringSchemeHighPointFirstGetsTwelveOrEight2017=High Point ESS 2017, winner gets 12 points (or 8)
scoringSchemeHighPointFirstGetsTwelveOrEight2017Description=Score in points. Winner scores 12 points, 2nd - 11 points, .... A tie is broken in favour of the competitor with the highest score, if tie remains then tie is broken by the rank in the last race scored.
scoringSystem=Scoring system
createFlexibleLeaderboard=Create flexible leaderboard
createRegattaLeaderboard=Create regatta leaderboard
@@ -1475,6 +1475,8 @@ confirmLosingCompetitorEditsWhenTogglingLogBasedView=Damit werden alle aktuellen
removalOfMarkDisabledMayBeUsedInRaces=Löschung deaktiviert, Bahnmarke wird in folgenden anderen Wettfahrten benutzt: {0}
scoringSchemeHighPointFirstGetsTwelveOrEight=High-Point System 12 Punkte
scoringSchemeHighPointFirstGetsTwelveOrEightDescription=Scores als Punkte. Der Gewinner bekommt 12 Punkte.
scoringSchemeHighPointFirstGetsTwelveOrEight2017=High-Point System 12 Punkte ESS2017
scoringSchemeHighPointFirstGetsTwelveOrEight2017Description=Scores als Punkte. Der Gewinner bekommt 12 Punkte.
scoringSchemeHighPointEssOverall12=High Point System ESS Gesamtwertung (12)
scoringSchemeHighPointEssOverall12Description=High Point System, der Gewinner eines Acts erhält 12 Punkte, der Zweite 11 und so weiter.
pleaseCreateAtLeastOneMappingBy=Bitte erstellen Sie mindestens eine Zuordnung, indem Sie zunächst einen Track auf der linken Seite und anschließend einen Teilnehmer oder eine Tonne auf der rechten Seite auswählen
@@ -450,6 +450,8 @@ scoringSchemeHighPointFirstGetsTenOrEight=高得点方式、1 位が 10 点 (ま
scoringSchemeHighPointFirstGetsTenOrEightDescription=得点数によるスコアです。1 位が 10 点、2 位が 9 点などと得点します。同点となった場合は、より多くのレースで勝者となった競技者を上位とします。それでも同順位となる場合には、最終レースでの得点でのランクで順位を決定します。
scoringSchemeHighPointFirstGetsTwelveOrEight=高得点方式、1 位が 12 点 (または 8 点)
scoringSchemeHighPointFirstGetsTwelveOrEightDescription=得点数によるスコアです。1 位が 12 点、2 位が 11 点などと得点します。同点となった場合は、(各競技者のレース得点を高い方から比較して) 最多得点の競技者を上位とします。それでも同順位となる場合には、最終レースでの得点でのランクで順位を決定します。
scoringSchemeHighPointFirstGetsTwelveOrEight2017=High Point ESS 2017, winner gets 12 points (or 8)
scoringSchemeHighPointFirstGetsTwelveOrEight2017Description=Score in points. Winner scores 12 points, 2nd - 11 points, .... A tie is broken in favour of the competitor with the highest score, if tie remains then tie is broken by the rank in the last race scored.
scoringSystem=得点方式
createFlexibleLeaderboard=フレキシブルリーダーボード登録
createRegattaLeaderboard=レガッタリーダーボード登録
@@ -450,6 +450,8 @@ scoringSchemeHighPointFirstGetsTenOrEight=Высокий балл, победи
scoringSchemeHighPointFirstGetsTenOrEightDescription=Оценка в баллах. Победитель получает 10 баллов, 2-й — 9 баллов, .... Равенство разрешается в пользу участника, имеющего больше побед в гонках. Если равенство сохраняется, оно разрешается по рангу в последней гонке.
scoringSchemeHighPointFirstGetsTwelveOrEight=Высокий балл, победитель получает 12 баллов (или 8)
scoringSchemeHighPointFirstGetsTwelveOrEightDescription=Оценка в баллах. Победитель получает 12 баллов, 2-й — 11 баллов, .... Равенство разрешается в пользу участника с наивысшей оценкой. Если равенство сохраняется, оно разрешается по рангу в последней гонке.
scoringSchemeHighPointFirstGetsTwelveOrEight2017=High Point ESS 2017, winner gets 12 points (or 8)
scoringSchemeHighPointFirstGetsTwelveOrEight2017Description=Score in points. Winner scores 12 points, 2nd - 11 points, .... A tie is broken in favour of the competitor with the highest score, if tie remains then tie is broken by the rank in the last race scored.
scoringSystem=Система оценок
createFlexibleLeaderboard=Создать гибкую таблицу лидеров
createRegattaLeaderboard=Создать таблицу лидеров регаты
@@ -450,6 +450,8 @@ scoringSchemeHighPointFirstGetsTenOrEight=高分,获胜者得 10 分(或 8
scoringSchemeHighPointFirstGetsTenOrEightDescription=得分。 获胜者得 10 分、第 2 名 9 分、.... 以赢得最多比赛轮次的参赛队取得胜利,如果仍有平局,则以最后一个比赛轮次的名次打破平局。
scoringSchemeHighPointFirstGetsTwelveOrEight=高分,获胜者得 12 分(或 8 分)
scoringSchemeHighPointFirstGetsTwelveOrEightDescription=得分。获胜者得 12 分、第 2 名 11 分、.... 得分最高的参赛队取得胜利,如果仍有平局,则以最后一个比赛轮次的名次打破平局。
scoringSchemeHighPointFirstGetsTwelveOrEight2017=High Point ESS 2017, winner gets 12 points (or 8)
scoringSchemeHighPointFirstGetsTwelveOrEight2017Description=Score in points. Winner scores 12 points, 2nd - 11 points, .... A tie is broken in favour of the competitor with the highest score, if tie remains then tie is broken by the rank in the last race scored.
scoringSystem=记分系统
createFlexibleLeaderboard=创建灵活的积分榜
createRegattaLeaderboard=创建比赛积分榜
@@ -36,6 +36,8 @@ public class ScoringSchemeTypeFormatter {
return stringMessages.scoringSchemeHighPointFirstGetsTenOrEight();
case HIGH_POINT_FIRST_GETS_TWELVE_OR_EIGHT:
return stringMessages.scoringSchemeHighPointFirstGetsTwelveOrEight();
case HIGH_POINT_FIRST_GETS_TWELVE_OR_EIGHT_2017:
return stringMessages.scoringSchemeHighPointFirstGetsTwelveOrEight2017();
case LOW_POINT_WITH_ELIMINATIONS_AND_ROUNDS_WINNER_GETS_07:
return stringMessages.scoringSchemeLowPointWithEliminationsAndRoundsWinnerGets07();
case LOW_POINT_LEAGUE_OVERALL:
@@ -52,6 +54,8 @@ public class ScoringSchemeTypeFormatter {
return stringMessages.scoringSchemeHighPointFirstGetsTenOrEightDescription();
case HIGH_POINT_FIRST_GETS_TWELVE_OR_EIGHT:
return stringMessages.scoringSchemeHighPointFirstGetsTwelveOrEightDescription();
case HIGH_POINT_FIRST_GETS_TWELVE_OR_EIGHT_2017:
return stringMessages.scoringSchemeHighPointFirstGetsTwelveOrEight2017Description();
case HIGH_POINT_ESS_OVERALL:
return stringMessages.scoringSchemeHighPointEssOverallDescription();
case HIGH_POINT_ESS_OVERALL_12:
@@ -35,6 +35,7 @@ import com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.test.AbstractTracTracLiveTest;
import com.sap.sailing.domain.test.PositionAssert;
import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
@@ -124,9 +125,9 @@ public class TestStoringAndRetrievingWindTracksTest extends AbstractTracTracLive
result.lockForRead();
try {
for (Wind wind : result.getRawFixes()) {
assertEquals(pos, wind.getPosition());
assertEquals(10., wind.getKnots(), 0.000000000001);
assertEquals(myBearingDeg, wind.getBearing().getDegrees(), 0.000000001);
PositionAssert.assertPositionEquals(pos, wind.getPosition(), /* deg delta */ 0.000001);
assertEquals(10., wind.getKnots(), 0.01);
assertEquals(myBearingDeg, wind.getBearing().getDegrees(), 0.01);
myBearingDeg += 1.1;
}
} finally {
@@ -12,6 +12,7 @@ import java.util.Set;
import org.junit.Test;
import com.mongodb.MongoException;
import com.sap.sailing.domain.tracking.RaceTracker;
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
import com.sap.sailing.domain.tractracadapter.DomainFactory;
import com.sap.sailing.domain.tractracadapter.TracTracConnectionConstants;
@@ -47,7 +48,8 @@ public class TracTracConnectivityParamsLoadAndStoreTest extends AbstractConnecti
paramURL, /* live URI */ null, storedURI, courseDesignUpdateURI, startOfTracking, endOfTracking,
delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm,
/* raceLogStore */ null, /* regattaLogStore */ null, DomainFactory.INSTANCE, tracTracUsername, tracTracPassword,
raceStatus, raceVisibility, trackWind, correctWindDirectionByMagneticDeclination, /* preferReplayIfAvailable */ false);
raceStatus, raceVisibility, trackWind, correctWindDirectionByMagneticDeclination, /* preferReplayIfAvailable */ false,
/* timeoutInMillis */ (int) RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS);
// store
mongoObjectFactory.addConnectivityParametersForRaceToRestore(tracTracParams);
// load
@@ -20,7 +20,7 @@ public abstract class HostPage extends PageObject {
/**
* </p>The default timeout of 60 seconds for the initialization of the page object.</p>
*/
protected static final int DEFAULT_PAGE_LOAD_TIMEOUT = 60;
protected static final int DEFAULT_PAGE_LOAD_TIMEOUT = 120;
public static final String getGWTCodeServer() {
String codeServer = System.getProperty(GWT_CODE_SERVER_PARAMETER_NAME);
@@ -1,6 +1,7 @@
package com.sap.sailing.server.gateway.jaxrs.api;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
@@ -9,6 +10,8 @@ import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
@@ -33,6 +36,7 @@ import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.RegattaName;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.Tack;
@@ -44,6 +48,7 @@ import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.GPSFixTrack;
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.TrackedRace;
@@ -868,6 +873,102 @@ public class RegattasResource extends AbstractSailingServerResource {
return response;
}
/**
* @return the actual or anticipated start order for the race identified by {@coode regattaName} and {@code raceName}.
* Those competitors for which a start mark passing is already known are sorted by those start mark passings. All other
* boats are ordered by their geometrical distance from the start line or the windward distance from the start mark
* if the start for some reason is defined by a single mark. See {@link #compareDistanceFromStartLine(Competitor, Competitor)}.
*/
@GET
@Produces("application/json;charset=UTF-8")
@Path("{regattaname}/races/{racename}/startorder")
public Response getStartOrder(@PathParam("regattaname") String regattaName, @PathParam("racename") String raceName) {
Response response;
Regatta regatta = findRegattaByName(regattaName);
if (regatta == null) {
response = Response.status(Status.NOT_FOUND)
.entity("Could not find a regatta with name '" + StringEscapeUtils.escapeHtml(regattaName) + "'.").type(MediaType.TEXT_PLAIN)
.build();
} else {
RaceDefinition race = findRaceByName(regatta, raceName);
if (race == null) {
response = Response.status(Status.NOT_FOUND)
.entity("Could not find a race with name '" + StringEscapeUtils.escapeHtml(raceName) + "'.").type(MediaType.TEXT_PLAIN)
.build();
} else {
final TrackedRace trackedRace = findTrackedRace(regattaName, raceName);
Course course = trackedRace.getRace().getCourse();
final List<Competitor> competitors = new ArrayList<>();
final Waypoint firstWaypoint = course.getFirstWaypoint();
final TimePoint timeToUseForStart;
if (trackedRace.getStartOfRace() != null) {
timeToUseForStart = trackedRace.getStartOfRace();
} else {
timeToUseForStart = MillisecondsTimePoint.now();
}
if (firstWaypoint != null) {
final List<Position> startWaypointMarkPositions =
StreamSupport.stream(firstWaypoint.getMarks().spliterator(), /* parallel */ false).
map(mark->trackedRace.getTrack(mark)).filter(markTrack->markTrack!=null).
map(markTrack->markTrack.getEstimatedPosition(timeToUseForStart, /* extrapolate */ false)).
collect(Collectors.toList());
Util.addAll(trackedRace.getRace().getCompetitors(), competitors);
competitors.sort((a, b)->{
final MarkPassing aStartMarkPassing = trackedRace.getMarkPassing(a, firstWaypoint);
final MarkPassing bStartMarkPassing = trackedRace.getMarkPassing(b, firstWaypoint);
final int result;
if (aStartMarkPassing == null) {
if (bStartMarkPassing == null) {
result = compareDistanceFromStartLine(trackedRace, startWaypointMarkPositions, timeToUseForStart, a, b);
} else {
result = 1; // b consider less than a because it has a start mark passing and therefore is assumed to have started before a
}
} else if (bStartMarkPassing == null) {
result = -1; // a consider less than b because it has a start mark passing and therefore is assumed to have started before b
} else {
// both have a start mark passing; compare time points
result = aStartMarkPassing.getTimePoint().compareTo(bStartMarkPassing.getTimePoint());
}
return result;
});
}
CompetitorJsonSerializer serializer = new CompetitorJsonSerializer();
JSONArray result = new JSONArray();
for (final Competitor c : competitors) {
JSONObject jsonCompetitor = serializer.serialize(c);
result.add(jsonCompetitor);
}
String json = result.toJSONString();
return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
}
}
return response;
}
private int compareDistanceFromStartLine(TrackedRace trackedRace, Iterable<Position> startWaypointMarkPositions,
TimePoint timeToUseForStart, Competitor a, Competitor b) {
final Position aPos = trackedRace.getTrack(a).getEstimatedPosition(timeToUseForStart, /* extrapolate */ true);
final Position bPos = trackedRace.getTrack(b).getEstimatedPosition(timeToUseForStart, /* extrapolate */ true);
final Distance aDist = aPos==null?null:getDistanceFromStartWaypoint(aPos, startWaypointMarkPositions);
final Distance bDist = bPos==null?null:getDistanceFromStartWaypoint(bPos, startWaypointMarkPositions);
return Comparator.<Distance>nullsLast(Comparator.naturalOrder()).compare(aDist, bDist);
}
private Distance getDistanceFromStartWaypoint(Position pos, Iterable<Position> startWaypointMarkPositions) {
final Distance result;
if (Util.isEmpty(startWaypointMarkPositions)) {
result = null;
} else if (Util.size(startWaypointMarkPositions) == 1) {
// single mark start; strange, but possible:
result = startWaypointMarkPositions.iterator().next().getDistance(pos);
} else {
final Position first = Util.get(startWaypointMarkPositions, 0);
final Position second = Util.get(startWaypointMarkPositions, 1);
result = pos.getDistanceToLine(first, second).abs();
}
return result;
}
@GET
@Produces("application/json;charset=UTF-8")
@Path("{regattaname}/races")
@@ -1,7 +1,6 @@
package com.sap.sailing.server.replication.test;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
@@ -57,6 +56,7 @@ import com.sap.sailing.domain.persistence.PersistenceFactory;
import com.sap.sailing.domain.persistence.media.MediaDBFactory;
import com.sap.sailing.domain.racelog.tracking.EmptySensorFixStore;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.test.PositionAssert;
import com.sap.sailing.domain.test.TrackBasedTest;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.TrackedRace;
@@ -158,7 +158,7 @@ public class InitialLoadReplicationObjectIdentityTest extends AbstractServerRepl
WindTrack masterRCWindFixes = masterTrackedRace.getOrCreateWindTrack(new WindSourceImpl(WindSourceType.RACECOMMITTEE));
masterRCWindFixes.lockForRead();
try {
assertEquals(masterWindFix, masterRCWindFixes.getRawFixes().iterator().next());
PositionAssert.assertWindEquals(masterWindFix, masterRCWindFixes.getRawFixes().iterator().next(), /* pos deg delta */ 0.0000001, /* bearing deg delta */ 0.01, /* knot speed delta */ 0.01);
} finally {
masterRCWindFixes.unlockAfterRead();
}
@@ -233,7 +233,7 @@ public class InitialLoadReplicationObjectIdentityTest extends AbstractServerRepl
try {
Iterator<Wind> replicaRCWindFixesIter = replicaRCWindFixes.getRawFixes().iterator();
replicaRCWindFixesIter.next();
assertEquals(replicaWindFix, replicaRCWindFixesIter.next());
PositionAssert.assertWindEquals(replicaWindFix, replicaRCWindFixesIter.next(), /* pos deg delta */ 0.0000001, /* bearing deg delta */ 0.01, /* knot speed delta */ 0.02);
} finally {
replicaRCWindFixes.unlockAfterRead();
}
@@ -20,6 +20,7 @@ import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.test.AbstractTracTracLiveTest;
import com.sap.sailing.domain.tracking.RaceHandle;
import com.sap.sailing.domain.tracking.RaceTracker;
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.server.RacingEventService;
@@ -72,7 +73,7 @@ public class TrackRaceBoatCompetitorMetadataReplicationTest extends AbstractServ
startOfTracking, endOfTracking, /* delayToLiveInMillis */
0l, /* offsetToStartTimeOfSimulatedRace */null, /*ignoreTracTracMarkPassings*/ false, EmptyRaceLogStore.INSTANCE,
EmptyRegattaLogStore.INSTANCE, tracTracUsername, tracTracPassword, "", "", /* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ false,
/* preferReplayIfAvailable */ false);
/* preferReplayIfAvailable */ false, /* timeoutInMillis */ (int) RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS);
}
private void startTracking() throws Exception, InterruptedException {
@@ -31,6 +31,7 @@ import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.test.AbstractTracTracLiveTest;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.RaceHandle;
import com.sap.sailing.domain.tracking.RaceTracker;
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.server.RacingEventService;
@@ -90,7 +91,7 @@ public class TrackRaceReplicationTest extends AbstractServerReplicationTest {
startOfTracking, endOfTracking, /* delayToLiveInMillis */
0l, /* offsetToStartTimeOfSimulatedRace */null, /*ignoreTracTracMarkPassings*/ false, EmptyRaceLogStore.INSTANCE,
EmptyRegattaLogStore.INSTANCE, tracTracUsername, tracTracPassword, "", "", /* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ false,
/* preferReplayIfAvailable */ false);
/* preferReplayIfAvailable */ false, /* timeoutInMillis */ (int) RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS);
}
private void startTracking() throws Exception, InterruptedException {
@@ -44,6 +44,7 @@ import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.persistence.MongoWindStore;
import com.sap.sailing.domain.persistence.MongoWindStoreFactory;
import com.sap.sailing.domain.persistence.PersistenceFactory;
import com.sap.sailing.domain.test.PositionAssert;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.GPSFixTrack;
@@ -130,7 +131,7 @@ public class TrackedRaceContentsReplicationTest extends AbstractServerReplicatio
competitorTrack.lockForRead();
try {
assertEquals(1, Util.size(competitorTrack.getRawFixes()));
assertEquals(fix, competitorTrack.getRawFixes().iterator().next());
PositionAssert.assertGPSFixEquals(fix, competitorTrack.getRawFixes().iterator().next(), /* pos deg delta */ 0.0000001, /* bearing deg delta */ 0.01, /* knot speed delta */ 0.01);
assertNotSame(fix, competitorTrack.getRawFixes().iterator().next());
} finally {
competitorTrack.unlockAfterRead();
@@ -151,7 +152,7 @@ public class TrackedRaceContentsReplicationTest extends AbstractServerReplicatio
replicaMarkTrack.lockForRead();
try {
assertEquals(1, Util.size(replicaMarkTrack.getRawFixes()));
assertEquals(replicaMarkTrack.getRawFixes().iterator().next(), fix);
PositionAssert.assertGPSFixEquals(replicaMarkTrack.getRawFixes().iterator().next(), fix, /* pos deg delta */ 0.0000001);
assertNotSame(fix, replicaMarkTrack.getRawFixes().iterator().next());
} finally {
replicaMarkTrack.unlockAfterRead();
@@ -174,7 +175,7 @@ public class TrackedRaceContentsReplicationTest extends AbstractServerReplicatio
try {
assertEquals(1, Util.size(replicaWindTrack.getRawFixes()));
Wind replicaWind = replicaWindTrack.getRawFixes().iterator().next();
assertEquals(wind, replicaWind);
PositionAssert.assertWindEquals(wind, replicaWind, /* pos deg delta */ 0.0000001, /* bearing deg delta */ 0.02, /* knot speed delta */ 0.02);
assertNotSame(wind, replicaWind);
} finally {
replicaWindTrack.unlockAfterRead();
@@ -37,6 +37,8 @@ import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.persistence.MongoWindStoreFactory;
import com.sap.sailing.domain.persistence.PersistenceFactory;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.test.PositionAssert;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.GPSFixTrack;
@@ -157,7 +159,7 @@ public class TrackedRaceWithGPSFixStoreContentsReplicationTest extends AbstractS
competitorTrack.lockForRead();
try {
assertEquals(1, Util.size(competitorTrack.getRawFixes()));
assertEquals(fix, competitorTrack.getRawFixes().iterator().next());
PositionAssert.assertGPSFixEquals(fix, competitorTrack.getRawFixes().iterator().next(), /* pos deg delta */ 0.0000001, /* bearing deg delta */ 0.01, /* knot speed delta */ 0.01);
assertNotSame(fix, competitorTrack.getRawFixes().iterator().next());
} finally {
competitorTrack.unlockAfterRead();
@@ -21,6 +21,7 @@ import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.tracking.RaceHandle;
import com.sap.sailing.domain.tracking.RaceListener;
import com.sap.sailing.domain.tracking.RaceTracker;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRegatta;
import com.sap.sailing.domain.tractracadapter.TracTracConnectionConstants;
@@ -73,7 +74,8 @@ public class RaceTrackerTest {
service, paramUrl, liveUri, storedUri, courseDesignUpdateUri, EmptyRaceLogStore.INSTANCE,
EmptyRegattaLogStore.INSTANCE, /* timeoutInMilliseconds */60000, tracTracUsername, tracTracPassword,
TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY,
/* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ false);
/* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ false,
/* timeoutInMillis */ (int) RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS);
logger.info("Calling raceHandle.getRaces()");
RaceDefinition race = raceHandle.getRace(); // wait for RaceDefinition to be completely wired in Regatta
logger.info("Obtained race: "+race);
@@ -133,7 +135,7 @@ public class RaceTrackerTest {
EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, /* timeoutInMilliseconds */60000,
tracTracUsername, tracTracPassword, TracTracConnectionConstants.ONLINE_STATUS,
TracTracConnectionConstants.ONLINE_VISIBILITY, /* trackWind */ false,
/* correctWindDirectionByMagneticDeclination */ false);
/* correctWindDirectionByMagneticDeclination */ false, /* timeoutInMillis */ (int) RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS);
TrackedRegatta newTrackedRegatta = myRaceHandle.getTrackedRegatta();
assertNotSame(oldTrackedRegatta, newTrackedRegatta);
TrackedRace newTrackedRace = getTrackedRace(newTrackedRegatta);
@@ -162,7 +164,7 @@ public class RaceTrackerTest {
EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, /* timeoutInMilliseconds */60000,
tracTracUsername, tracTracPassword, TracTracConnectionConstants.ONLINE_STATUS,
TracTracConnectionConstants.ONLINE_VISIBILITY, /* trackWind */ false,
/* correctWindDirectionByMagneticDeclination */ false);
/* correctWindDirectionByMagneticDeclination */ false, /* timeoutInMillis */ (int) RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS);
TrackedRegatta newTrackedEvent = myRaceHandle.getTrackedRegatta();
TrackedRace newTrackedRace = getTrackedRace(newTrackedEvent);
// expecting a new tracked race to be created when starting over with tracking
@@ -42,6 +42,7 @@ import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.WindImpl;
import com.sap.sailing.domain.common.impl.WindSourceImpl;
import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.test.PositionAssert;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
@@ -146,8 +147,22 @@ public class WindByRaceLogTest {
try {
windTrack.lockForRead();
assertTrue(Util.contains(windTrack.getFixes(), wind1));
assertTrue(Util.contains(windTrack.getFixes(), wind2));
boolean foundWind1 = false;
boolean foundWind2 = false;
for (Wind w : windTrack.getFixes()) {
try {
PositionAssert.assertWindEquals(w, wind1, /* posDegDelta */ 0.000001, /* bearingDegreeDelta */ 0.01, /* knotSpeedDelta */ 0.01);
foundWind1 = true;
} catch (AssertionError e) {
}
try {
PositionAssert.assertWindEquals(w, wind2, /* posDegDelta */ 0.000001, /* bearingDegreeDelta */ 0.01, /* knotSpeedDelta */ 0.01);
foundWind2 = true;
} catch (AssertionError e) {
}
}
assertTrue(foundWind1);
assertTrue(foundWind2);
} finally {
windTrack.unlockAfterRead();
}
@@ -172,7 +187,15 @@ public class WindByRaceLogTest {
try {
windTrack.lockForRead();
assertTrue(Util.contains(windTrack.getFixes(), wind1));
boolean foundWind1 = false;
for (Wind w : windTrack.getFixes()) {
try {
PositionAssert.assertWindEquals(w, wind1, /* posDegDelta */ 0.000001, /* bearingDegreeDelta */ 0.01, /* knotSpeedDelta */ 0.01);
foundWind1 = true;
} catch (AssertionError e) {
}
}
assertTrue(foundWind1);
} finally {
windTrack.unlockAfterRead();
}
@@ -0,0 +1,10 @@
#------------------------------------------------------------
#-- File created 08/03/2017 at 05:38:19
#------------------------------------------------------------
jjlDATE jjlTIME Epoch RideHeight RideHeightPortHull RideHeightStbdHull Heel Trim ImuSensor_GyroX ImuSensor_GyroY ImuSensor_GyroZ ImuSensor_Pitch ImuSensor_Roll ImuSensor_Yaw ImuSensor_LinearAccX ImuSensor_LinearAccY ImuSensor_LinearAccZ Hb_Z Dn_Z Db_Z LKF_ride_hgh LKF_ride_hgh_Position LKF_ride_hgh_Velocity LKF_ride_hgh_Acceleration LKF_ride_hgh_PositionError LKF_ride_hgh_VelocityError LKF_ride_hgh_AccelerationError Gps_Gga_PosFixTime Gps_Gga_Lat Gps_Gga_Lon Gps_Gga_QI Gps_Gga_HDOP Gps_Gga_AntHeight Gps_Vtg_TMG Gps_Vtg_SOGKnots Gps_Rmc_MagVar Gps_EastVelocity Gps_NorthVelocity Gps_UpVelocity BravoNet_Node0x0A0_Ch0 BravoNet_Node0x0A0_Voltage BravoNet_Node0x0A0_Temperature BravoNet_Node0x0A0_Current SyncTimeDiff SyncTimeSync SyncTimeUpdateCount SyncTimePercentageToSync ES_OnTimeSync
20170308.000000 53819.390491 1488951499390.000000 0.313796 -0.714744 -0.853481 -1.500000 3.200000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 -0.100172 -0.314127 -0.330172 0.016091 -0.314174 -0.004288 -0.000000 0.121613 1.403056 0.062733 53819.200000 2337.887200 5815.964540 0.000000 0.000000 0.000000 0.000000 0.000000 1.300000 0.000000 0.000000 0.000000 -0.001831 24.295210 21.217340 0.015640 542266653.468207 0.000000 1.000000 0.000000 0.000000
20170308.000000 53819.487567 1488951499481.000000 0.313992 -0.714548 -0.853286 -1.500000 3.200000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 -0.100172 -0.314127 -0.330172 0.016090 -0.314261 -0.002298 0.000000 0.097345 0.648185 0.049813 53819.400000 2337.887200 5815.964540 0.000000 0.000000 0.000000 0.000000 0.000000 1.300000 0.000000 0.000000 0.000000 -0.002441 24.295210 21.000116 0.023460 0.000077 1.000000 1.000000 0.000000 1.000000
20170308.000000 53819.585786 1488951499572.000000 0.314025 -0.714515 -0.853253 -1.500000 3.200000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 -0.099696 -0.313652 -0.329696 0.019540 -0.313953 -0.000227 0.000001 0.086096 0.423595 0.043597 53819.400000 2337.887200 5815.964540 0.000000 0.000000 0.000000 0.000000 0.000000 1.300000 0.000000 0.000000 0.000000 -0.002441 24.295210 20.782894 0.046921 0.000077 1.000000 1.000000 0.000000 1.000000
20170308.000000 53819.685771 1488951499682.000000 0.313923 -0.714617 -0.853355 -1.500000 3.200000 -0.086588 -0.214116 0.175599 -0.000000 0.000000 0.000000 -0.258920 0.519435 -0.087402 -0.099696 -0.313652 -0.329696 0.019270 -0.313730 0.000672 0.000839 0.075893 0.287746 0.038563 53819.600000 2337.887200 5815.964540 0.000000 0.000000 0.000000 0.000000 0.000000 1.300000 0.000000 0.000000 0.000000 -0.002441 24.253178 21.000116 0.046921 -0.000199 1.000000 1.000000 0.000000 1.000000
20170308.000000 53819.782123 1488951499772.000000 0.313831 -0.714709 -0.853447 -1.500000 3.200000 0.013137 -0.213877 0.120009 -1.060399 -3.133662 0.001262 -0.108944 0.029720 0.070589 -0.099696 -0.313652 -0.329696 0.015927 -0.313576 0.001633 0.003961 0.069401 0.222243 0.035510 53819.600000 2337.887200 5815.964540 0.000000 0.000000 0.000000 0.000000 0.000000 1.300000 0.000000 0.000000 0.000000 -0.002441 24.295210 21.000116 0.046921 -0.000199 1.000000 1.000000 0.000000 1.000000
20170308.000000 53819.880156 1488951499863.000000 0.314961 -0.768407 -0.798152 -0.321583 2.797351 0.072364 -0.168356 0.209327 -1.091322 -3.133510 0.016635 -0.068841 0.000188 0.184953 -0.099696 -0.324611 -0.329696 0.016254 -0.318301 -0.005391 0.019517 0.064999 0.178037 0.033119 53819.800000 2337.887200 5815.964540 0.000000 0.000000 0.000000 0.000000 0.000000 1.300000 0.000000 0.000000 0.000000 -0.002441 24.295210 21.000116 0.031281 -0.000724 1.000000 1.000000 0.000000 1.000000
@@ -1,7 +1,7 @@
#------------------------------------------------------------
#-- File created 09/04/2016 at 23:26:40
#------------------------------------------------------------
jjlDATE jjlTIME Epoch RideHeight RideHeightPortHull RideHeightStbdHull Heel Trim ImuSensor_GyroX ImuSensor_GyroY ImuSensor_GyroZ ImuSensor_Pitch ImuSensor_Roll ImuSensor_Yaw ImuSensor_LinearAccX ImuSensor_LinearAccY ImuSensor_LinearAccZ Hb_Z Dn_Z Db_Z LKF_ride_hgh LKF_ride_hgh_Position LKF_ride_hgh_Velocity LKF_ride_hgh_Acceleration LKF_ride_hgh_PositionError LKF_ride_hgh_VelocityError LKF_ride_hgh_AccelerationError Gps_Gga_PosFixTime Gps_Gga_Lat Gps_Gga_Lon Gps_Gga_QI Gps_Gga_HDOP Gps_Gga_AntHeight Gps_Vtg_TMG Gps_Vtg_SOGKnots Gps_Rmc_MagVar Gps_EastVelocity Gps_NorthVelocity Gps_UpVelocity BravoNet_Node0x0A0_Ch0 BravoNet_Node0x0A0_Voltage BravoNet_Node0x0A0_Temperature BravoNet_Node0x0A0_Current
jjlDATE jjlTIME Epoch RideHeight RideHeightPortHull RideHeightStbdHull Heel Trim ImuSensor_GyroX ImuSensor_GyroY ImuSensor_GyroZ ImuSensor_Pitch ImuSensor_Roll ImuSensor_Yaw ImuSensor_LinearAccX ImuSensor_LinearAccY ImuSensor_LinearAccZ Hb_Z Dn_Z Db_Z LKF_ride_hgh LKF_ride_hgh_Position LKF_ride_hgh_Velocity LKF_ride_hgh_Acceleration LKF_ride_hgh_PositionError LKF_ride_hgh_VelocityError LKF_ride_hgh_AccelerationError Gps_Gga_PosFixTime Gps_Gga_Lat Gps_Gga_Lon Gps_Gga_QI Gps_Gga_HDOP Gps_Gga_AntHeight Gps_Vtg_TMG Gps_Vtg_SOGKnots Gps_Rmc_MagVar Gps_EastVelocity Gps_NorthVelocity Gps_UpVelocity BravoNet_Node0x0A0_Ch0 BravoNet_Node0x0A0_Voltage BravoNet_Node0x0A0_Temperature BravoNet_Node0x0A0_Current
20160409.000000 232640.908740 1460244399905.000000 0.757862 0.650044 0.865680 2.095203 -10.572519 0.014012 0.080612 0.075319 -10.572519 -2.095203 358.804353 -0.021935 0.017397 0.000239 -0.673131 -0.757548 -0.743131 0.013623 -0.757862 -0.004228 -0.018140 0.074238 0.272712 0.037974 212640.800000 5232.276310 1325.488310 1.000000 1.100000 80.800000 306.100000 0.370000 3.200000 -0.170000 0.130000 -0.000000 0.733032 24.043011 36.640236 0.054741
20160409.000000 232641.008763 1460244399995.000000 0.758014 0.650158 0.865870 2.095937 -10.571562 0.032092 0.217810 -0.023014 -10.571683 -2.096830 358.798479 -0.028412 0.021632 -0.027246 -0.673131 -0.757548 -0.743131 0.019586 -0.758014 -0.005095 -0.018096 0.069231 0.211765 0.035063 212640.800000 5232.276310 1325.488310 1.000000 1.100000 80.800000 306.100000 0.370000 3.200000 -0.150000 0.110000 -0.000000 0.733032 24.000978 35.988564 0.054741
20160409.000000 232641.105742 1460244400089.000000 0.758203 0.650319 0.866087 2.096466 -10.570188 0.073738 0.193435 0.107095 -10.570447 -2.097253 358.792882 -0.042638 0.045676 -0.010817 -0.673131 -0.757547 -0.743131 0.019374 -0.758203 -0.006152 -0.018839 0.064587 0.170564 0.032760 212641.000000 5232.276260 1325.488390 1.000000 1.100000 80.800000 306.100000 0.300000 3.200000 -0.150000 0.110000 -0.000000 0.733032 24.043011 35.988564 0.054741
@@ -48,6 +48,11 @@ public class BravoDataImportTest {
testImport(ImportData.FILE_UNDEFINED_RACE_BRAVO);
}
@Test
public void testFileImportNewFormat() throws FormatNotSupportedException, IOException {
testImport(ImportData.FILE_NEW_BRAVO_FORMAT);
}
@Test
public void testDefaultHeaderNoDataImport() throws FormatNotSupportedException, IOException {
testImport(ImportData.DUMMY_DEFAULT_HEADER_NO_DATA);
@@ -64,7 +69,6 @@ public class BravoDataImportTest {
}
private void testImport(ImportData importData) throws FormatNotSupportedException, IOException {
bravoDataImporter.importFixes(importData.getInputStream(), (fixes, device) -> {
for (DoubleVectorFix fix : fixes) {
callbackCallCount++;
@@ -85,6 +89,12 @@ public class BravoDataImportTest {
return getClass().getResourceAsStream("/Undefined Race - BRAVO.txt");
}
},
FILE_NEW_BRAVO_FORMAT(6, 1) {
@Override
protected InputStream getInputStream() {
return getClass().getResourceAsStream("/FMU_out_new.txt");
}
},
DUMMY_DEFAULT_HEADER_NO_DATA(0, 0) {
@Override
protected InputStream getInputStream() {
@@ -6,8 +6,10 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
@@ -142,10 +144,12 @@ public class BravoDataImporterImpl implements DoubleVectorFixImporter {
String header = headerTokens[j];
colIndicesInFile.put(header, j);
}
List<String> columnsInFix = metadata.getFileColumns();
if (colIndicesInFile.size() != columnsInFix.size() || !colIndicesInFile.keySet().containsAll(columnsInFix)) {
LOG.log(Level.SEVERE, "Missing headers");
throw new RuntimeException("Missing headers in import files");
List<String> requiredColumnsInFix = metadata.getTrackColumns();
if (!colIndicesInFile.keySet().containsAll(requiredColumnsInFix)) {
final Set<String> missingColumns = new HashSet<>(requiredColumnsInFix);
missingColumns.removeAll(colIndicesInFile.keySet());
LOG.log(Level.SEVERE, "Missing headers: "+missingColumns);
throw new RuntimeException("Missing headers "+missingColumns+" in import files");
}
return colIndicesInFile;
}
@@ -31,8 +31,8 @@ import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.impl.MasterDataImportObjectCreationCountImpl;
import com.sap.sailing.domain.common.tracking.impl.CompactGPSFixImpl;
import com.sap.sailing.domain.common.tracking.impl.CompactGPSFixMovingImpl;
import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixImpl;
import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixMovingImpl;
import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
@@ -415,12 +415,12 @@ public class ImportMasterDataOperation extends
DeviceIdentifier device = entry.getKey();
final Collection<Timed> fixesToAddAsBatch = new ArrayList<>(BATCH_SIZE_FOR_IMPORTING_FIXES);
for (Timed fixToAdd : entry.getValue()) {
if (fixToAdd instanceof CompactGPSFixMovingImpl) {
CompactGPSFixMovingImpl gpsFix = (CompactGPSFixMovingImpl) fixToAdd;
if (fixToAdd instanceof VeryCompactGPSFixMovingImpl) {
VeryCompactGPSFixMovingImpl gpsFix = (VeryCompactGPSFixMovingImpl) fixToAdd;
fixToAdd = new GPSFixMovingImpl(gpsFix.getPosition(), fixToAdd.getTimePoint(),
((CompactGPSFixMovingImpl) fixToAdd).getSpeed());
} else if (fixToAdd instanceof CompactGPSFixImpl) {
CompactGPSFixImpl gpsFix = (CompactGPSFixImpl) fixToAdd;
((VeryCompactGPSFixMovingImpl) fixToAdd).getSpeed());
} else if (fixToAdd instanceof VeryCompactGPSFixImpl) {
VeryCompactGPSFixImpl gpsFix = (VeryCompactGPSFixImpl) fixToAdd;
fixToAdd = new GPSFixImpl(gpsFix.getPosition(), fixToAdd.getTimePoint());
}
fixesToAddAsBatch.add(fixToAdd);
+18 -1
View File
@@ -23,7 +23,24 @@
<h2 class="releaseHeadline">Release Notes - Administration Console</h2>
<div class="innerContent">
<h2 class="articleSubheadline">Febrbuary 2017</h2>
<h2 class="articleSubheadline">March 2017</h2>
<ul class="bulletList">
<li>A more compact internal storage format for GPS and Wind fixes is now being used. Instead of a full
64-bit "double" value for all components such at latitude, longitude, speed over ground or course over
ground, "smaller" data types such as "int" and "short" are now employed. The reduction of accuracy
that this brings about is negligible given the accuracy of the tracking systems used. For example,
the latitude values are encoded such that while covering the full range from -90° to +90°, the
resolution is at 4.6mm. Similarly, speeds are represented in their compact form in such a way
that speeds up to 500kts can be represented at a resolution of 0.015kts.
</li>
<li>When a race cannot be loaded successfully through the TracTrac connector, e.g., because its boat
class does not match the regatta's boat class, the tracker is now stopped, and a SEVERE log message
is written, giving the reason for the failure to load the race. This gives administrators an opportunity
to correct any errors in the underlying data and try again. Previously, tracking such a
was not possible without a server re-start because the tracker continued to exist.
</li>
</ul>
<h2 class="articleSubheadline">February 2017</h2>
<ul class="bulletList">
<li>Wind data in GRIB format can now be imported in the "Wind" sub-tab of the "Tracked Races" tab
in the <tt>AdminConsole</tt>. When one or more races are being tracked, with a tracking time
@@ -0,0 +1,164 @@
package com.sap.sailing.xmlexport;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.SpeedWithConfidence;
import com.sap.sailing.domain.base.impl.SpeedWithConfidenceImpl;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
import com.sap.sailing.domain.common.impl.WindSourceImpl;
import com.sap.sailing.domain.common.sensordata.BravoSensorDataMetadata;
import com.sap.sailing.domain.common.tracking.BravoFix;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.tracking.BravoFixTrack;
import com.sap.sailing.domain.tracking.DynamicSensorFixTrack;
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.WindWithConfidence;
import com.sap.sailing.server.RacingEventService;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
public class FoilingData {
private final HttpServletRequest req;
private final HttpServletResponse res;
private final RacingEventService service;
private final boolean useProvidedLeaderboard;
public FoilingData(HttpServletRequest req, HttpServletResponse res, RacingEventService service) {
this.req = req;
this.res = res;
this.service = service;
this.useProvidedLeaderboard = false;
}
public String getAttribute(String name) {
return this.req.getParameter(name);
}
public RacingEventService getService() {
return service;
}
public Leaderboard getLeaderboard() throws IOException, ServletException {
Leaderboard leaderboard = null;
if (!useProvidedLeaderboard) {
final String leaderboardName = getAttribute("name");
if (leaderboardName == null) {
throw new ServletException("Use the name= parameter to specify the leaderboard");
}
leaderboard = getService().getLeaderboardByName(leaderboardName);
if (leaderboard == null) {
throw new ServletException("Leaderboard " + leaderboardName + " not found.");
}
}
return leaderboard;
}
public void perform() throws Exception {
StringBuffer result = new StringBuffer();
result.append("TimePoint").append(";");
result.append("RegattaName").append(";");
result.append("RaceName").append(";");
result.append("LegIndexStartingAtZero").append(";");
result.append("LegType").append(";");
result.append("CompetitorName").append(";");
result.append("CompetitorSailId").append(";");
result.append("CompetitorUUID").append(";");
result.append("SpeedOverGroundInKnots").append(";");
result.append("RideHeightInMeter").append(";");
result.append("RideHeightPortInMeter").append(";");
result.append("RideHeightStarboardInMeter").append(";");
result.append("Heel").append(";");
result.append("Pitch").append(";");
result.append("AveragedWindSpeedInKnots").append(";");
result.append("WindDirectionInDegrees").append(";");
result.append("CompetitorBearingInDegrees").append(";");
result.append("BearingDifferenceBetweenCompetitorAndWindInDegrees").append("\n");
Leaderboard leaderboard = getLeaderboard();
for (RaceColumn raceColumn : leaderboard.getRaceColumns()) {
for (Fleet fleet : raceColumn.getFleets()) {
TrackedRace trackedRace = raceColumn.getTrackedRace(fleet);
if (trackedRace != null) {
final List<Competitor> allCompetitors = trackedRace.getCompetitorsFromBestToWorst(/*timePoint*/trackedRace.getEndOfRace());
for (Competitor competitor : allCompetitors) {
DynamicSensorFixTrack<Competitor, BravoFix> sensorTrack = trackedRace.getSensorTrack(competitor, BravoFixTrack.TRACK_NAME);
final Duration samplingInterval = Duration.ONE_SECOND;
final TimePoint fromTimePoint = trackedRace.getStartOfRace();
if (sensorTrack != null && fromTimePoint != null) {
final List<WindSource> windSourcesToDeliver = new ArrayList<WindSource>();
final WindSource windSource = new WindSourceImpl(WindSourceType.COMBINED);
windSourcesToDeliver.add(windSource);
final WindTrack windTrack = trackedRace.getOrCreateWindTrack(windSource);
final TimePoint toTimePoint = trackedRace.getEndOfRace();
TimePoint timePointToConsider = fromTimePoint;
int attemptedNumberOfFixes = 0;
while (toTimePoint == null ? (attemptedNumberOfFixes<5) : (timePointToConsider.compareTo(toTimePoint) < 0)) {
BravoFix bravoFixAtTimepoint = sensorTrack.getFirstFixAtOrAfter(timePointToConsider);
if (bravoFixAtTimepoint != null) {
SpeedWithBearing speedOfCompetitor = trackedRace.getTrack(competitor).getEstimatedSpeed(timePointToConsider);
double rideHeight = bravoFixAtTimepoint.get(BravoSensorDataMetadata.RIDE_HEIGHT);
double rideHeightPort = bravoFixAtTimepoint.get(BravoSensorDataMetadata.RIDE_HEIGHT_PORT_HULL);
double rideHeightStarboard = bravoFixAtTimepoint.get(BravoSensorDataMetadata.RIDE_HEIGHT_STBD_HULL);
double heel = bravoFixAtTimepoint.get(BravoSensorDataMetadata.HEEL);
double pitch = bravoFixAtTimepoint.get(BravoSensorDataMetadata.PITCH);
WindWithConfidence<com.sap.sse.common.Util.Pair<Position, TimePoint>> windFix = windTrack.getAveragedWindWithConfidence(null, timePointToConsider);
SpeedWithConfidence<TimePoint> windFixSpeed = new SpeedWithConfidenceImpl<TimePoint>(new KnotSpeedImpl(windFix.getObject().getKnots()), windFix.getConfidence(), timePointToConsider);
Bearing windFixBearing = windFix.getObject().getBearing();
TrackedLegOfCompetitor leg = trackedRace.getTrackedLeg(competitor, timePointToConsider);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-M-dd HH:mm:ss");
result.append(simpleDateFormat.format(timePointToConsider.asDate())).append(";");
result.append(trackedRace.getRaceIdentifier().getRegattaName()).append(";");
result.append(trackedRace.getRaceIdentifier().getRaceName()).append(";");
if (leg != null) {
result.append(trackedRace.getRace().getCourse().getLegs().indexOf(leg.getLeg())).append(";");
result.append(leg.getTrackedLeg().getLegType(timePointToConsider).name()).append(";");
} else {
result.append("-1").append(";");
result.append("UNKNOWN").append(";");
}
result.append(competitor.getName()).append(";");
result.append(competitor.getBoat().getSailID()).append(";");
result.append(competitor.getId()).append(";");
result.append(speedOfCompetitor != null ? speedOfCompetitor.getKnots() : "0.0").append(";");
result.append(rideHeight).append(";");
result.append(rideHeightPort).append(";");
result.append(rideHeightStarboard).append(";");
result.append(heel).append(";");
result.append(pitch).append(";");
result.append(windFixSpeed.getObject().getKnots()).append(";");
result.append(windFixBearing.reverse().getDegrees()).append(";");
result.append(speedOfCompetitor != null ? speedOfCompetitor.getBearing().getDegrees() : "0.0").append(";");
result.append(speedOfCompetitor != null ? speedOfCompetitor.getBearing().getDifferenceTo(windFixBearing.reverse()).getDegrees() : "0.0").append("\n");
}
timePointToConsider = timePointToConsider.plus(samplingInterval);
attemptedNumberOfFixes++;
}
}
}
}
}
}
res.setContentType("text/csv");
res.addHeader("Content-Disposition", "attachment; filename=" + leaderboard.getName() + ".csv");
res.getOutputStream().write(result.toString().getBytes("UTF-8"));
res.getOutputStream().flush();
}
}
@@ -20,6 +20,10 @@ public class XmlExportServlet extends SailingServerHttpServlet {
final LeaderboardData leaderboardData = new LeaderboardData(req, res, getService());
leaderboardData.perform();
return;
} else if ("foiling".equalsIgnoreCase(action)) {
final FoilingData foilingData = new FoilingData(req, res, getService());
foilingData.perform();
return;
}
throw new ServletException("Unknown domain " + action);
}
@@ -19,6 +19,7 @@ import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -131,7 +132,7 @@ public abstract class AbstractServerReplicationTestSetUp<ReplicableInterface ext
ReplicableImpl master, ReplicableImpl replica) throws Exception {
logger.info("basicSetUp for test class "+getClass().getName());
persistenceSetUp(dropDB);
String exchangeName = "test-sapsailinganalytics-exchange";
String exchangeName = "test-sapsailinganalytics-exchange-"+new Random().nextInt();
String exchangeHost = "localhost";
if (System.getenv(Activator.ENV_VAR_NAME_REPLICATION_HOST) != null) {
exchangeHost = System.getenv(Activator.ENV_VAR_NAME_REPLICATION_HOST);
@@ -188,7 +189,7 @@ public abstract class AbstractServerReplicationTestSetUp<ReplicableInterface ext
}
if (masterDescriptor != null) {
logger.info("before stopConnection...");
masterDescriptor.stopConnection();
masterDescriptor.stopConnection(/* deleteExchange */ true);
}
try {
if (initialLoadTestServerThread != null) {
@@ -59,7 +59,12 @@ public interface ReplicationMasterDescriptor {
String getExchangeName();
void stopConnection();
/**
* @param deleteExchange
* Only to be used by the master itself when no longer delivering messages to the exchange, or to tear
* down after a test
*/
void stopConnection(boolean deleteExchange);
String getMessagingHostname();
@@ -225,12 +225,15 @@ public class ReplicationMasterDescriptorImpl implements ReplicationMasterDescrip
}
@Override
public synchronized void stopConnection() {
public synchronized void stopConnection(boolean deleteExchange) {
try {
if (consumer != null) {
// make sure to remove queue in order to avoid any exchanges filling it with messages
consumer.getChannel().queueUnbind(queueName, exchangeName, "");
consumer.getChannel().queueDelete(queueName);
if (deleteExchange) {
consumer.getChannel().exchangeDelete(exchangeName);
}
consumer.getChannel().getConnection().close(/* timeout in millis */ 1000);
}
} catch (Exception ex) {
@@ -239,7 +242,7 @@ public class ReplicationMasterDescriptorImpl implements ReplicationMasterDescrip
logger.log(Level.SEVERE, "Exception while closing replication channel consumer", ex);
}
}
/**
* @return 0 means use default port
*/
@@ -400,7 +400,7 @@ public class ReplicationReceiver implements Runnable {
}
logger.info("Signaled Replicator thread to stop asap.");
stopped = true;
master.stopConnection();
master.stopConnection(/* deleteExchange */ false);
notifyAll(); // notify those waiting for stopped
}
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: TracTrac Client module
Bundle-SymbolicName: com.tractrac.clientmodule
Bundle-Version: 3.4.1
Bundle-Version: 3.6.0
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Bundle-ClassPath: .,
lib/TracAPI.jar
+61 -2
View File
@@ -16,6 +16,65 @@ It contains also some files:
- test.sh -> script that compiles the code in the src folder, creates the test.jar library and execute the code of the example.
- Manifest.txt -> manifest used to create the test.jar file
********************************************
************* TracAPI 3.6.0 ****************
********************************************
This is a final version. It change the signature of a couple of methods, breaking the
backward compatibility. These changes are:
- The IEventFactory.createRace(URI) doesn't throw the TimeOutException.
- The IEventFactory.createRace(URI, URI, URI) doesn't throw the TimeOutException.
This version provides a new JavaDoc version.
Release date: 08/03/2017
Build number: 13498
1) Features
- Removing the TimeOutException of some methods of the IEventFactory (Requested by Axel Uhl, 08/03/2017)
********************************************
************* TracAPI 3.5.0 ****************
********************************************
This is a final version. It fixes bugs in the implementation and it adds some new features.
These features add methods to the API breaking the backward compatibility. These changes are:
- The IRaceCompetitor interface changes the return type of the getStatusTime method
from Date to long.
- The methods IEventFactory.createRace throw now a TimeOutException when there is a timeout
downloading the parameters file.
This version provides a new JavaDoc version.
Release date: 07/03/2017
Build number: 13490
1) Features
- Updating the return type of the IRaceCompetitor.getStatusTime from Date to long to
be consistent with the API (Requested by Jorge Piera, 13/02/2017)
- Adding the methods ISegment.getExtent(), the IRoute.getExtent() and IRace.getExtent()
that return the extent of the object (Requested by Jorge Piera, 23/02/2017)
- Adding an extra parameter to the IEventFactory.createRace methods to indicate
the timeout used to download the parameters file. The IEventFactory also includes a method
getDefaultTimeOut that returns the default timeout (Requested by Axel Uhl, 06/03/2017)
********************************************
************* TracAPI 3.4.1 ****************
********************************************
This is a final version. Only fixes bugs in the implementation
Release date: 20/02/2017
Build number: 13415
1) Bugs
- TracAPI doesn't send all data when the races are retrieved in parallel.
It is an error in the thread that executes the callback messages that in some
internal circumstances, doesn't execute the callback (Reported by Axel Uhl,
13/02/2017)
********************************************
************* TracAPI 3.4.0 ****************
********************************************
@@ -33,9 +92,9 @@ Build number: 13339
1) Features
- Adding the method IRace.getDataSource that returns the datasource used by the race
to load data (Requested by Axel Ulh, 25/01/2017)
to load data (Requested by Axel Uhl, 25/01/2017)
- Adding the method IRacesListener.dataSourceChanged that sends a notification
every time that the datasource of a race changes (Requested by Axel Ulh, 25/01/2017)
every time that the datasource of a race changes (Requested by Axel Uhl, 25/01/2017)
2) Bugs
@@ -2,9 +2,9 @@
<!-- NewPage -->
<html lang="en">
<head>
<!-- Generated by javadoc (1.8.0_91) on Tue Jan 17 09:51:12 UTC 2017 -->
<!-- Generated by javadoc (1.8.0_111) on Wed Mar 08 07:35:44 UTC 2017 -->
<title>All Classes</title>
<meta name="date" content="2017-01-17">
<meta name="date" content="2017-03-08">
<link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
<script type="text/javascript" src="script.js"></script>
</head>
@@ -14,6 +14,7 @@
<ul>
<li><a href="com/tractrac/model/lib/api/attachment/AbstractAttachable.html" title="class in com.tractrac.model.lib.api.attachment" target="classFrame">AbstractAttachable</a></li>
<li><a href="com/tractrac/model/lib/api/event/CreateModelException.html" title="class in com.tractrac.model.lib.api.event" target="classFrame">CreateModelException</a></li>
<li><a href="com/tractrac/model/lib/api/event/DataSource.html" title="enum in com.tractrac.model.lib.api.event" target="classFrame">DataSource</a></li>
<li><a href="com/tractrac/model/lib/api/event/EventType.html" title="enum in com.tractrac.model.lib.api.event" target="classFrame">EventType</a></li>
<li><a href="com/tractrac/model/lib/api/attachment/IAttachable.html" title="interface in com.tractrac.model.lib.api.attachment" target="classFrame"><span class="interfaceName">IAttachable</span></a></li>
<li><a href="com/tractrac/model/lib/api/attachment/IAttachmentKey.html" title="interface in com.tractrac.model.lib.api.attachment" target="classFrame"><span class="interfaceName">IAttachmentKey</span></a></li>
@@ -37,6 +38,7 @@
<li><a href="com/tractrac/model/lib/api/event/IEventFactory.html" title="interface in com.tractrac.model.lib.api.event" target="classFrame"><span class="interfaceName">IEventFactory</span></a></li>
<li><a href="com/tractrac/subscription/lib/api/event/IEventMessageListener.html" title="interface in com.tractrac.subscription.lib.api.event" target="classFrame"><span class="interfaceName">IEventMessageListener</span></a></li>
<li><a href="com/tractrac/subscription/lib/api/IEventSubscriber.html" title="interface in com.tractrac.subscription.lib.api" target="classFrame"><span class="interfaceName">IEventSubscriber</span></a></li>
<li><a href="com/tractrac/model/lib/api/spatial/IExtent.html" title="interface in com.tractrac.model.lib.api.spatial" target="classFrame"><span class="interfaceName">IExtent</span></a></li>
<li><a href="com/tractrac/model/lib/api/spatial/IGeoCoordinate.html" title="interface in com.tractrac.model.lib.api.spatial" target="classFrame"><span class="interfaceName">IGeoCoordinate</span></a></li>
<li><a href="com/tractrac/model/lib/api/IIdentifiable.html" title="interface in com.tractrac.model.lib.api" target="classFrame"><span class="interfaceName">IIdentifiable</span></a></li>
<li><a href="com/tractrac/subscription/lib/api/event/ILiveDataEvent.html" title="interface in com.tractrac.subscription.lib.api.event" target="classFrame"><span class="interfaceName">ILiveDataEvent</span></a></li>
@@ -2,9 +2,9 @@
<!-- NewPage -->
<html lang="en">
<head>
<!-- Generated by javadoc (1.8.0_91) on Tue Jan 17 09:51:12 UTC 2017 -->
<!-- Generated by javadoc (1.8.0_111) on Wed Mar 08 07:35:44 UTC 2017 -->
<title>All Classes</title>
<meta name="date" content="2017-01-17">
<meta name="date" content="2017-03-08">
<link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
<script type="text/javascript" src="script.js"></script>
</head>
@@ -14,6 +14,7 @@
<ul>
<li><a href="com/tractrac/model/lib/api/attachment/AbstractAttachable.html" title="class in com.tractrac.model.lib.api.attachment">AbstractAttachable</a></li>
<li><a href="com/tractrac/model/lib/api/event/CreateModelException.html" title="class in com.tractrac.model.lib.api.event">CreateModelException</a></li>
<li><a href="com/tractrac/model/lib/api/event/DataSource.html" title="enum in com.tractrac.model.lib.api.event">DataSource</a></li>
<li><a href="com/tractrac/model/lib/api/event/EventType.html" title="enum in com.tractrac.model.lib.api.event">EventType</a></li>
<li><a href="com/tractrac/model/lib/api/attachment/IAttachable.html" title="interface in com.tractrac.model.lib.api.attachment"><span class="interfaceName">IAttachable</span></a></li>
<li><a href="com/tractrac/model/lib/api/attachment/IAttachmentKey.html" title="interface in com.tractrac.model.lib.api.attachment"><span class="interfaceName">IAttachmentKey</span></a></li>
@@ -37,6 +38,7 @@
<li><a href="com/tractrac/model/lib/api/event/IEventFactory.html" title="interface in com.tractrac.model.lib.api.event"><span class="interfaceName">IEventFactory</span></a></li>
<li><a href="com/tractrac/subscription/lib/api/event/IEventMessageListener.html" title="interface in com.tractrac.subscription.lib.api.event"><span class="interfaceName">IEventMessageListener</span></a></li>
<li><a href="com/tractrac/subscription/lib/api/IEventSubscriber.html" title="interface in com.tractrac.subscription.lib.api"><span class="interfaceName">IEventSubscriber</span></a></li>
<li><a href="com/tractrac/model/lib/api/spatial/IExtent.html" title="interface in com.tractrac.model.lib.api.spatial"><span class="interfaceName">IExtent</span></a></li>
<li><a href="com/tractrac/model/lib/api/spatial/IGeoCoordinate.html" title="interface in com.tractrac.model.lib.api.spatial"><span class="interfaceName">IGeoCoordinate</span></a></li>
<li><a href="com/tractrac/model/lib/api/IIdentifiable.html" title="interface in com.tractrac.model.lib.api"><span class="interfaceName">IIdentifiable</span></a></li>
<li><a href="com/tractrac/subscription/lib/api/event/ILiveDataEvent.html" title="interface in com.tractrac.subscription.lib.api.event"><span class="interfaceName">ILiveDataEvent</span></a></li>
Loaded 100 of 244 files, more files were not shown because too many files have changed in this diff. Show more