X-Git-Url: http://gitweb.fperrin.net/?p=GpsPrune.git;a=blobdiff_plain;f=src%2Ftim%2Fprune%2Fdata%2FSpeedCalculator.java;fp=src%2Ftim%2Fprune%2Fdata%2FSpeedCalculator.java;h=6f81d5a32187d60b43d0a686deb6e8a3e59571ba;hp=0000000000000000000000000000000000000000;hb=ce6f2161b8596f7018d6a76bff79bc9e571f35fd;hpb=2d8cb72e84d5cc1089ce77baf1e34ea3ea2f8465 diff --git a/src/tim/prune/data/SpeedCalculator.java b/src/tim/prune/data/SpeedCalculator.java new file mode 100644 index 0000000..6f81d5a --- /dev/null +++ b/src/tim/prune/data/SpeedCalculator.java @@ -0,0 +1,216 @@ +package tim.prune.data; + +import tim.prune.config.Config; + +/** + * Abstract class to hold static calculation functions + * for speed (and vertical speed) + */ +public abstract class SpeedCalculator +{ + /** + * Calculate the horizontal speed value of the track at the specified index + * @param inTrack track object + * @param inIndex index of point to calculate speed for + * @param inValue object in which to place result of calculation + */ + public static void calculateSpeed(Track inTrack, int inIndex, SpeedValue inValue) + { + if (inValue != null) + { + inValue.setInvalid(); + } + if (inTrack == null || inIndex < 0 || inValue == null) + { + System.err.println("Cannot calculate speed for index " + inIndex); + return; + } + + DataPoint point = inTrack.getPoint(inIndex); + if (point == null) {return;} + boolean pointHasSpeed = false; + double speedValue = 0.0; + + // First, see if point has a speed value already + if (point.hasHSpeed()) { + speedValue = point.getHSpeed().getValue(Config.getUnitSet().getSpeedUnit()); + pointHasSpeed = true; + } + + // otherwise, see if we can calculate it from the timestamps + if (!pointHasSpeed && point.hasTimestamp() && !point.isWaypoint()) + { + double totalRadians = 0.0; + int index = inIndex-1; + DataPoint p = null; + DataPoint q = point; + Timestamp earlyStamp = point.getTimestamp(); + boolean stop = false; + + // Count backwards until timestamp earlier than now; total distances back to this point + if (!point.getSegmentStart()) + { + do + { + p = inTrack.getPoint(index); + boolean timeOk = p != null && p.hasTimestamp() && p.getTimestamp().isBefore(point.getTimestamp()); + boolean pValid = timeOk && !p.isWaypoint(); + if (pValid) { + totalRadians += DataPoint.calculateRadiansBetween(p, q); + earlyStamp = p.getTimestamp(); + } + + stop = (p == null) || p.getSegmentStart() || hasSufficientTimeDifference(p, point); + index--; + if (p != null && !p.isWaypoint()) { + q = p; + } + } + while (!stop); + } + // Count forwards until timestamp later than now; total distances forward to this point + Timestamp lateStamp = point.getTimestamp(); + q = point; + index = inIndex+1; + do + { + p = inTrack.getPoint(index); + boolean timeOk = p != null && p.hasTimestamp() && !p.getTimestamp().isBefore(point.getTimestamp()); + boolean pValid = timeOk && !p.isWaypoint() && !p.getSegmentStart(); + if (pValid) { + totalRadians += DataPoint.calculateRadiansBetween(p, q); + lateStamp = p.getTimestamp(); + } + + stop = (p == null) || p.getSegmentStart() || hasSufficientTimeDifference(point, p); + index++; + if (p != null && !p.isWaypoint()) { + q = p; + } + } + while (!stop); + + // See if we've managed to get a time range of at least a second + long milliseconds = lateStamp.getMillisecondsSince(earlyStamp); + if (milliseconds >= 1000L) + { + double dist = Distance.convertRadiansToDistance(totalRadians); + // Store the value and maintain max and min values + speedValue = dist / milliseconds * 1000.0 * 60.0 * 60.0; // convert from per millisec to per hour + pointHasSpeed = true; + } + } + // Did we get a value? + if (pointHasSpeed) + { + inValue.setValue(speedValue); + } + // otherwise, just leave value as invalid + } + + + /** + * Calculate the vertical speed value of the track at the specified index + * @param inTrack track object + * @param inIndex index of point to calculate speed for + * @param inValue object in which to place the result of calculation + */ + public static void calculateVerticalSpeed(Track inTrack, int inIndex, SpeedValue inValue) + { + if (inTrack == null || inIndex < 0 || inValue == null) { + System.err.println("Cannot calculate vert speed for index " + inIndex); + return; + } + inValue.setInvalid(); + + DataPoint point = inTrack.getPoint(inIndex); + boolean pointHasSpeed = false; + double speedValue = 0.0; + + // First, see if point has a speed value already + if (point != null && point.hasVSpeed()) + { + speedValue = point.getVSpeed().getValue(Config.getUnitSet().getVerticalSpeedUnit()); + pointHasSpeed = true; + } + // otherwise, see if we can calculate it from the heights and timestamps + if (!pointHasSpeed + && point != null && point.hasTimestamp() && point.hasAltitude() && !point.isWaypoint()) + { + int index = inIndex-1; + DataPoint p = null; + Timestamp earlyStamp = point.getTimestamp(); + Altitude firstAlt = point.getAltitude(); + boolean stop = false; + + // Count backwards until timestamp earlier than now + if (!point.getSegmentStart()) + { + do + { + p = inTrack.getPoint(index); + boolean timeOk = p != null && p.hasTimestamp() && p.getTimestamp().isBefore(point.getTimestamp()); + boolean pValid = timeOk && !p.isWaypoint(); + if (pValid) { + earlyStamp = p.getTimestamp(); + if (p.hasAltitude()) firstAlt = p.getAltitude(); + } + + stop = (p == null) || p.getSegmentStart() || hasSufficientTimeDifference(p, point); + index--; + } + while (!stop); + } + + // Count forwards until timestamp later than now + Timestamp lateStamp = point.getTimestamp(); + Altitude lastAlt = point.getAltitude(); + index = inIndex+1; + do + { + p = inTrack.getPoint(index); + boolean timeOk = p != null && p.hasTimestamp() && !p.getTimestamp().isBefore(point.getTimestamp()); + boolean pValid = timeOk && !p.isWaypoint() && !p.getSegmentStart(); + if (pValid) { + lateStamp = p.getTimestamp(); + if (p.hasAltitude()) lastAlt = p.getAltitude(); + } + + stop = (p == null) || p.getSegmentStart() || hasSufficientTimeDifference(point, p); + index++; + } + while (!stop); + + // See if we've managed to get a non-zero time range + long milliseconds = lateStamp.getMillisecondsSince(earlyStamp); + if (milliseconds >= 1000L) + { + double altDiff = (lastAlt.getMetricValue() - firstAlt.getMetricValue()) + * Config.getUnitSet().getVerticalSpeedUnit().getMultFactorFromStd(); + speedValue = altDiff / milliseconds * 1000.0; // units are feet/sec or metres/sec + pointHasSpeed = true; + } + } + // Check whether we got a value from either method + if (pointHasSpeed) + { + inValue.setValue(speedValue); + } + } + + /** + * Check whether the time difference between P1 and P2 is sufficiently large + * @param inP1 earlier point + * @param inP2 later point + * @return true if we can stop looking now, found a point early/late enough + */ + private static boolean hasSufficientTimeDifference(DataPoint inP1, DataPoint inP2) + { + if (inP1 == null || inP2 == null) + return true; // we have to give up now + if (!inP1.hasTimestamp() || !inP2.hasTimestamp()) + return false; // keep looking + final long MIN_TIME_DIFFERENCE_MS = 1000L; + return inP2.getTimestamp().getMillisecondsSince(inP1.getTimestamp()) >= MIN_TIME_DIFFERENCE_MS; + } +}