diff --git a/java/com.sap.sailing.domain.common/.settings/org.eclipse.core.resources.prefs b/java/com.sap.sailing.domain.common/.settings/org.eclipse.core.resources.prefs
index c111da40d73..ec633bb1915 100755
--- a/java/com.sap.sailing.domain.common/.settings/org.eclipse.core.resources.prefs
+++ b/java/com.sap.sailing.domain.common/.settings/org.eclipse.core.resources.prefs
@@ -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
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/WindImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/WindImpl.java
index 1ae650de809..516686bd0d7 100755
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/WindImpl.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/WindImpl.java
@@ -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.
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/sensordata/BravoSensorDataMetadata.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/sensordata/BravoSensorDataMetadata.java
index ff02c3d87eb..5f8d177bf6e 100644
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/sensordata/BravoSensorDataMetadata.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/sensordata/BravoSensorDataMetadata.java
@@ -14,7 +14,7 @@ public enum BravoSensorDataMetadata {
INSTANCE;
- public static final String RIDE_HEIGHT = "RideHeight";
+ 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";
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/CompactGPSFixImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/AbstractCompactGPSFixImpl.java
similarity index 54%
rename from java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/CompactGPSFixImpl.java
rename to java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/AbstractCompactGPSFixImpl.java
index 16a009c6e86..bfd89ce129e 100755
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/CompactGPSFixImpl.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/AbstractCompactGPSFixImpl.java
@@ -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 {
* {@link #whatIsCached}&{@link #IS_VALIDITY_CACHED} != 0
*/
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 {@link #whatIsCached}&{@link #IS_ESTIMATED_SPEED_CACHED} != 0, this field tells the estimated speed's
- * true "bearing" (true course over ground) in degrees.
- */
- private double cachedEstimatedSpeedBearingInDegrees;
-
- /**
- * When {@link #whatIsCached}&{@link #IS_ESTIMATED_SPEED_CACHED} != 0, 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;
}
-
-
}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/CompactGPSFixMovingImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/AbstractCompactGPSFixMovingImpl.java
similarity index 66%
rename from java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/CompactGPSFixMovingImpl.java
rename to java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/AbstractCompactGPSFixMovingImpl.java
index b48f26dcd05..a23e80dd0a3 100755
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/CompactGPSFixMovingImpl.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/AbstractCompactGPSFixMovingImpl.java
@@ -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
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/AbstractCompactWindImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/AbstractCompactWindImpl.java
new file mode 100755
index 00000000000..81c83d7008e
--- /dev/null
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/AbstractCompactWindImpl.java
@@ -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();
+ }
+}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/CompactPositionHelper.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/CompactPositionHelper.java
new file mode 100755
index 00000000000..b391ca92522
--- /dev/null
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/CompactPositionHelper.java
@@ -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.
+ *
+ * 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.
+ *
+ * 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°.
+ *
+ * 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.
+ *
+ * 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);
+ }
+}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/CompactWindImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/CompactWindImpl.java
deleted file mode 100755
index 0c0035c54e7..00000000000
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/CompactWindImpl.java
+++ /dev/null
@@ -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();
- }
-}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/CompactionNotPossibleException.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/CompactionNotPossibleException.java
new file mode 100755
index 00000000000..43be75fd2d0
--- /dev/null
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/CompactionNotPossibleException.java
@@ -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);
+ }
+
+}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/PreciseCompactGPSFixImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/PreciseCompactGPSFixImpl.java
new file mode 100755
index 00000000000..1cf8940a462
--- /dev/null
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/PreciseCompactGPSFixImpl.java
@@ -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.
+ *
+ * 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 {@link #whatIsCached}&{@link #IS_ESTIMATED_SPEED_CACHED} != 0, this field tells the estimated speed's
+ * true "bearing" (true course over ground) in degrees.
+ */
+ private double cachedEstimatedSpeedBearingInDegrees;
+
+ /**
+ * When {@link #whatIsCached}&{@link #IS_ESTIMATED_SPEED_CACHED} != 0, 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);
+ }
+}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/PreciseCompactGPSFixMovingImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/PreciseCompactGPSFixMovingImpl.java
new file mode 100755
index 00000000000..d6fd25e7f0c
--- /dev/null
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/PreciseCompactGPSFixMovingImpl.java
@@ -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 {@link #whatIsCached}&{@link #IS_ESTIMATED_SPEED_CACHED} != 0, this field tells the estimated speed's
+ * true "bearing" (true course over ground) in degrees.
+ */
+ private double cachedEstimatedSpeedBearingInDegrees;
+
+ /**
+ * When {@link #whatIsCached}&{@link #IS_ESTIMATED_SPEED_CACHED} != 0, 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);
+ }
+}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/PreciseCompactWindImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/PreciseCompactWindImpl.java
new file mode 100755
index 00000000000..12a444c2cb2
--- /dev/null
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/PreciseCompactWindImpl.java
@@ -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();
+ }
+}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixImpl.java
new file mode 100755
index 00000000000..ff6b0931d43
--- /dev/null
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixImpl.java
@@ -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.
+ *
+ * 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 {@link #whatIsCached}&{@link #IS_ESTIMATED_SPEED_CACHED} != 0, 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 {@link #whatIsCached}&{@link #IS_ESTIMATED_SPEED_CACHED} != 0, 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);
+ }
+ }
+}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixMovingImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixMovingImpl.java
new file mode 100755
index 00000000000..625379af503
--- /dev/null
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixMovingImpl.java
@@ -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 {@link #whatIsCached}&{@link #IS_ESTIMATED_SPEED_CACHED} != 0, 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 {@link #whatIsCached}&{@link #IS_ESTIMATED_SPEED_CACHED} != 0, 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);
+ }
+ }
+}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/VeryCompactWindImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/VeryCompactWindImpl.java
new file mode 100755
index 00000000000..1d3b60b3988
--- /dev/null
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/VeryCompactWindImpl.java
@@ -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();
+ }
+}
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreAndLoadTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreAndLoadTest.java
index 2e7f4e17d99..aaafe78372d 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreAndLoadTest.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreAndLoadTest.java
@@ -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) {
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/MockGPSFixMongoServiceFinder.java b/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/MockGPSFixMongoServiceFinder.java
index 241b5fff5f3..b29d32db4b8 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/MockGPSFixMongoServiceFinder.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/MockGPSFixMongoServiceFinder.java
@@ -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;
}
diff --git a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/SwissTimingReplayService.java b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/SwissTimingReplayService.java
index 2553cc2d0d0..40347ac6d11 100755
--- a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/SwissTimingReplayService.java
+++ b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/SwissTimingReplayService.java
@@ -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
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/AbstractTracTracLiveTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/AbstractTracTracLiveTest.java
index e5acc80daf5..4a6fced0e2b 100755
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/AbstractTracTracLiveTest.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/AbstractTracTracLiveTest.java
@@ -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
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/DistanceCacheInvalidationAfterLoadingFinishedTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/DistanceCacheInvalidationAfterLoadingFinishedTest.java
index 290a02d13cc..a9f0cdd75a0 100644
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/DistanceCacheInvalidationAfterLoadingFinishedTest.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/DistanceCacheInvalidationAfterLoadingFinishedTest.java
@@ -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
}
}
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LineAnalysisTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LineAnalysisTest.java
index ce9baf13dfb..5dc92520644 100755
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LineAnalysisTest.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LineAnalysisTest.java
@@ -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) {
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MultipleClassesInRegattaTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MultipleClassesInRegattaTest.java
index 6d0ced1c310..2386c1ebf30 100755
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MultipleClassesInRegattaTest.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MultipleClassesInRegattaTest.java
@@ -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
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/PositionAssert.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/PositionAssert.java
new file mode 100755
index 00000000000..b88c1a4742a
--- /dev/null
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/PositionAssert.java
@@ -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 expectedWinds, Set 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");
+ }
+ }
+ }
+}
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/PositionTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/PositionTest.java
index 1d76669219e..5c7b653a13e 100755
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/PositionTest.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/PositionTest.java
@@ -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
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReachingLegTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReachingLegTest.java
index d77f4c76acb..1014e9c2ea8 100755
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReachingLegTest.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReachingLegTest.java
@@ -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);
}
}
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/StarbordSideOfStartLineRecognitionTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/StarbordSideOfStartLineRecognitionTest.java
index c8a621c5707..911ed8fe8a8 100755
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/StarbordSideOfStartLineRecognitionTest.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/StarbordSideOfStartLineRecognitionTest.java
@@ -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
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackTest.java
index 2f5c7bf7de5..ce1092fcf4e 100644
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackTest.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackTest.java
@@ -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 track;
private GPSFixMovingImpl gpsFix1;
private GPSFixMovingImpl gpsFix2;
@@ -75,7 +77,7 @@ public class TrackTest {
track = new DynamicGPSFixMovingTrackImpl(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
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.
+
+
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.
+
+
February 2017
Wind data in GRIB format can now be imported in the "Wind" sub-tab of the "Tracked Races" tab
diff --git a/java/com.sap.sse.replication.testsupport/src/com/sap/sse/replication/testsupport/AbstractServerReplicationTestSetUp.java b/java/com.sap.sse.replication.testsupport/src/com/sap/sse/replication/testsupport/AbstractServerReplicationTestSetUp.java
index cea1755f607..f95629286a3 100644
--- a/java/com.sap.sse.replication.testsupport/src/com/sap/sse/replication/testsupport/AbstractServerReplicationTestSetUp.java
+++ b/java/com.sap.sse.replication.testsupport/src/com/sap/sse/replication/testsupport/AbstractServerReplicationTestSetUp.java
@@ -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 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
diff --git a/java/com.tractrac.clientmodule/javadoc/allclasses-frame.html b/java/com.tractrac.clientmodule/javadoc/allclasses-frame.html
index 8df91bfc193..566bce0b293 100755
--- a/java/com.tractrac.clientmodule/javadoc/allclasses-frame.html
+++ b/java/com.tractrac.clientmodule/javadoc/allclasses-frame.html
@@ -2,9 +2,9 @@
-
+
All Classes
-
+
@@ -14,6 +14,7 @@
According to the event manager, the visibility of the race can be:
Hidden: the race is not visible
Offline: the race is visible but it is not accessible
- Online: the race is visible and accessible (the replay status has
- been merged with this status)
+ Online: the race is visible and accessible
+
+
+ If we also consider the race tracking time, the race can be:
+
+ Replay: the race was celebrated in the past
+ Live: the race is in an online status ath this moment
+ Coming up: the race will be celebrated in the future
+
+
+ Combining these two dimensions, the possible values for the RaceVisibilityType
+ enum are:
+
+
+ Hidden: if the race has the HIDDEN value in the event manager
+ Offline: if the race has the OFFLINE value in the event manager
+ Replay: if the race has the ONLINE value in the event manager and it is over
+ Live: if the race has the ONLINE value in the event manager and it is online
+ Coming up: if the race has the ONLINE value in the event manager and it is coming up
According to the event manager, the visibility of the race can be:
Hidden: the race is not visible
Offline: the race is visible but it is not accessible
- Online: the race is visible and accessible (the replay status has
- been merged with this status)
+ Online: the race is visible and accessible
+
+
+ If we also consider the race tracking time, the race can be:
+
+ Replay: the race was celebrated in the past
+ Live: the race is in an online status ath this moment
+ Coming up: the race will be celebrated in the future
+
+
+ Combining these two dimensions, the possible values for the RaceVisibilityType
+ enum are:
+
+
+ Hidden: if the race has the HIDDEN value in the event manager
+ Offline: if the race has the OFFLINE value in the event manager
+ Replay: if the race has the ONLINE value in the event manager and it is over
+ Live: if the race has the ONLINE value in the event manager and it is online
+ Coming up: if the race has the ONLINE value in the event manager and it is coming up
getX() - Method in interface com.tractrac.model.lib.api.spatial.ICoordinate
@@ -412,6 +416,10 @@
All the listeners that are used for the consumer application in order to receive
events from the server side have to implemented this interface.
+
isValid() - Method in interface com.tractrac.model.lib.api.spatial.IExtent