]> gitweb.fperrin.net Git - GpsPrune.git/blobdiff - src/tim/prune/data/SpeedCalculator.java
Moved source into separate src directory due to popular request
[GpsPrune.git] / src / tim / prune / data / SpeedCalculator.java
diff --git a/src/tim/prune/data/SpeedCalculator.java b/src/tim/prune/data/SpeedCalculator.java
new file mode 100644 (file)
index 0000000..6f81d5a
--- /dev/null
@@ -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;
+       }
+}