]> gitweb.fperrin.net Git - GpsPrune.git/blobdiff - tim/prune/data/SpeedCalculator.java
Version 14, October 2012
[GpsPrune.git] / tim / prune / data / SpeedCalculator.java
diff --git a/tim/prune/data/SpeedCalculator.java b/tim/prune/data/SpeedCalculator.java
new file mode 100644 (file)
index 0000000..d83a7bd
--- /dev/null
@@ -0,0 +1,204 @@
+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 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 (inTrack == null || inIndex < 0 || inValue == null) {
+                       System.err.println("Cannot calculate speed for index " + inIndex);
+                       return;
+               }
+               inValue.setInvalid();
+
+               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
+               // FIXME: How do we know what units this speed is in?  m/s or mph or km/h or what?
+               String speedStr = point.getFieldValue(Field.SPEED);
+               try {
+                       speedValue = Double.parseDouble(speedStr);
+                       pointHasSpeed = true;
+               }
+               catch (Exception e) {} // ignore, leave pointHasSpeed false
+
+               // 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().isAfter(point.getTimestamp());
+                                       boolean pValid = timeOk && !p.isWaypoint();
+                                       if (pValid) {
+                                               totalRadians += DataPoint.calculateRadiansBetween(p, q);
+                                               earlyStamp = p.getTimestamp();
+                                       }
+                                       stop = (p == null) || (p.hasTimestamp() && !p.getTimestamp().isEqual(point.getTimestamp())
+                                               || p.getSegmentStart());
+                                       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.hasTimestamp() && !p.getTimestamp().isEqual(point.getTimestamp())
+                                       || p.getSegmentStart());
+                               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)
+               {
+                       // FIXME: Can we assume m/s or ft/s?
+                       String speedStr = point.getFieldValue(Field.VERTICAL_SPEED);
+                       try {
+                               speedValue = Double.parseDouble(speedStr);
+                               pointHasSpeed = true;
+                       }
+                       catch (Exception e) {} // ignore, leave pointHasSpeed false
+               }
+               // 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().isAfter(point.getTimestamp());
+                                       boolean pValid = timeOk && !p.isWaypoint();
+                                       if (pValid) {
+                                               earlyStamp = p.getTimestamp();
+                                               if (p.hasAltitude()) firstAlt = p.getAltitude();
+                                       }
+                                       stop = (p == null) || (p.hasTimestamp() && !p.getTimestamp().isEqual(point.getTimestamp())
+                                               || p.getSegmentStart());
+                                       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.hasTimestamp() && !p.getTimestamp().isEqual(point.getTimestamp())
+                                       || p.getSegmentStart());
+                               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);
+               }
+       }
+}