added Track.getInterpolatedValue, preparing for re-use across NMEA and Igtimi connector

Change-Id: Ia956d8ca0d9e40f275ade9f2c3764887e0de195a
This commit is contained in:
Axel Uhl committed 2017-04-21 17:18:41 +02:00
1 parent 1026fcf044
commit 82da2ccb51
5 files changed
+102 -9

No files matched your search

@@ -17,6 +17,8 @@ import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor;
import com.sap.sailing.domain.common.abstractlog.NotRevokableException;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util.Function;
import com.sap.sse.common.scalablevalue.ScalableValue;
/**
* Wrapper for a {@link RaceLog} which will ignore all calls trying to add an {@link RaceLogEvent}. All other
@@ -250,4 +252,10 @@ public class NoAddingRaceLogWrapper implements RaceLog {
public void revokeEvent(AbstractLogEventAuthor author, RaceLogEvent toRevoke) throws NotRevokableException {
innerRaceLog.revokeEvent(author, toRevoke);
}
@Override
public <InternalType, ValueType> ValueType getInterpolatedValue(TimePoint timePoint,
Function<RaceLogEvent, ScalableValue<InternalType, ValueType>> converter) {
return innerRaceLog.getInterpolatedValue(timePoint, converter);
}
}
@@ -4,9 +4,12 @@ import java.io.Serializable;
import java.util.Iterator;
import java.util.concurrent.locks.ReadWriteLock;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Timed;
import com.sap.sse.common.Util.Function;
import com.sap.sse.common.scalablevalue.ScalableValue;
/**
* A track records {@link Timed} items for an object of type <code>ItemType</code>. It allows clients to ask for a value
@@ -117,6 +120,27 @@ public interface Track<FixType extends Timed> extends Serializable {
*/
FixType getLastRawFix();
/**
* Interpolates an aspect of the fixes in this track for a given {@code timePoint}. If {@code timePoint} matches
* exactly a fix in this track, that fix is used. If this track is empty, {@code null} is returned. If the
* {@code timePoint} is after the last fix of this track, the last fix is used; if before the first fix, the first
* fix is used.
* <p>
*
* The fix(es) are converted to {@link ScalableValue}s using the {@code converter} which gives callers a choice
* which aspect of the fixes to project and interpolate. If more than one value results because two fixes (one
* before, one after) are used, linear interpolation based on the fixes' time points takes place.
* <p>
*
* Example: for a track of {@link GPSFixMoving} fixes the course over ground shall be determined for a given time
* point. The call would look like this:
* {@code getInterpolatedValue(timePoint, f->new ScalableBearing(f.getSpeed().getBearing()))}
*
* @return the projected interpolated value, typed by what the {@link ScalableValue#divide(double)} method returns.
*/
<InternalType, ValueType> ValueType getInterpolatedValue(TimePoint timePoint,
Function<FixType, ScalableValue<InternalType, ValueType>> converter);
/**
* Returns an iterator starting at the first fix after <code>startingAt</code> (or "at or after" in case
* <code>inclusive</code> is <code>true</code>). The fixes returned by the iterator are the smoothened fixes (see
@@ -184,4 +208,5 @@ public interface Track<FixType extends Timed> extends Serializable {
* more than one raw fix in the track
*/
Duration getAverageIntervalBetweenRawFixes();
}
@@ -10,6 +10,9 @@ import com.sap.sailing.domain.tracking.Track;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Timed;
import com.sap.sse.common.Util.Function;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.common.scalablevalue.ScalableValue;
import com.sap.sse.concurrent.LockUtil;
import com.sap.sse.concurrent.NamedReentrantReadWriteLock;
import com.sap.sse.util.impl.ArrayListNavigableSet;
@@ -251,6 +254,47 @@ public class TrackImpl<FixType extends Timed> implements Track<FixType> {
}
}
private Pair<FixType, FixType> getSurroundingFixes(TimePoint timePoint) {
FixType left = getLastFixAtOrBefore(timePoint);
FixType right = getFirstFixAtOrAfter(timePoint);
com.sap.sse.common.Util.Pair<FixType, FixType> result = new com.sap.sse.common.Util.Pair<>(left, right);
return result;
}
private <V, T> T timeBasedAverage(TimePoint timePoint, ScalableValue<V, T> value1, TimePoint timePoint1, ScalableValue<V, T> value2, TimePoint timePoint2) {
final T acc;
if (timePoint1.equals(timePoint2)) {
acc = value1.add(value2).divide(2);
} else {
long timeDiff1 = Math.abs(timePoint1.asMillis() - timePoint.asMillis());
long timeDiff2 = Math.abs(timePoint2.asMillis() - timePoint.asMillis());
acc = value1.multiply(timeDiff2).add(value2.multiply(timeDiff1)).divide(timeDiff1 + timeDiff2);
}
return acc;
}
@Override
public <InternalType, ValueType> ValueType getInterpolatedValue(TimePoint timePoint, Function<FixType, ScalableValue<InternalType, ValueType>> converter) {
final ValueType aws;
Pair<FixType, FixType> awsPair = getSurroundingFixes(timePoint);
if (awsPair.getA() == null) {
if (awsPair.getB() == null) {
aws = null;
} else {
aws = converter.get(awsPair.getB()).divide(1);
}
} else {
if (awsPair.getB() == null) {
aws = converter.get(awsPair.getA()).divide(1);
} else {
aws = timeBasedAverage(timePoint,
converter.get(awsPair.getA()), awsPair.getA().getTimePoint(),
converter.get(awsPair.getB()), awsPair.getB().getTimePoint());
}
}
return aws;
}
@Override
public Iterator<FixType> getFixesIterator(TimePoint startingAt, boolean inclusive) {
assertReadLock();