1 package tim.prune.data;
3 import tim.prune.config.Config;
6 * Abstract class to hold static calculation functions
7 * for speed (and vertical speed)
9 public abstract class SpeedCalculator
12 * Calculate the horizontal speed value of the track at the specified index
13 * @param inTrack track object
14 * @param inIndex index of point to calculate speed for
15 * @param inValue object in which to place result of calculation
17 public static void calculateSpeed(Track inTrack, int inIndex, SpeedValue inValue)
23 if (inTrack == null || inIndex < 0 || inValue == null)
25 System.err.println("Cannot calculate speed for index " + inIndex);
29 DataPoint point = inTrack.getPoint(inIndex);
30 if (point == null) {return;}
31 boolean pointHasSpeed = false;
32 double speedValue = 0.0;
34 // First, see if point has a speed value already
35 if (point.hasHSpeed()) {
36 speedValue = point.getHSpeed().getValue(Config.getUnitSet().getSpeedUnit());
40 // otherwise, see if we can calculate it from the timestamps
41 if (!pointHasSpeed && point.hasTimestamp() && !point.isWaypoint())
43 double totalRadians = 0.0;
44 int index = inIndex-1;
47 Timestamp earlyStamp = point.getTimestamp();
50 // Count backwards until timestamp earlier than now; total distances back to this point
51 if (!point.getSegmentStart())
55 p = inTrack.getPoint(index);
56 boolean timeOk = p != null && p.hasTimestamp() && p.getTimestamp().isBefore(point.getTimestamp());
57 boolean pValid = timeOk && !p.isWaypoint();
59 totalRadians += DataPoint.calculateRadiansBetween(p, q);
60 earlyStamp = p.getTimestamp();
63 stop = (p == null) || p.getSegmentStart() || hasSufficientTimeDifference(p, point);
65 if (p != null && !p.isWaypoint()) {
71 // Count forwards until timestamp later than now; total distances forward to this point
72 Timestamp lateStamp = point.getTimestamp();
77 p = inTrack.getPoint(index);
78 boolean timeOk = p != null && p.hasTimestamp() && !p.getTimestamp().isBefore(point.getTimestamp());
79 boolean pValid = timeOk && !p.isWaypoint() && !p.getSegmentStart();
81 totalRadians += DataPoint.calculateRadiansBetween(p, q);
82 lateStamp = p.getTimestamp();
85 stop = (p == null) || p.getSegmentStart() || hasSufficientTimeDifference(point, p);
87 if (p != null && !p.isWaypoint()) {
93 // See if we've managed to get a time range of at least a second
94 long milliseconds = lateStamp.getMillisecondsSince(earlyStamp);
95 if (milliseconds >= 1000L)
97 double dist = Distance.convertRadiansToDistance(totalRadians);
98 // Store the value and maintain max and min values
99 speedValue = dist / milliseconds * 1000.0 * 60.0 * 60.0; // convert from per millisec to per hour
100 pointHasSpeed = true;
103 // Did we get a value?
106 inValue.setValue(speedValue);
108 // otherwise, just leave value as invalid
113 * Calculate the vertical speed value of the track at the specified index
114 * @param inTrack track object
115 * @param inIndex index of point to calculate speed for
116 * @param inValue object in which to place the result of calculation
118 public static void calculateVerticalSpeed(Track inTrack, int inIndex, SpeedValue inValue)
120 if (inTrack == null || inIndex < 0 || inValue == null) {
121 System.err.println("Cannot calculate vert speed for index " + inIndex);
124 inValue.setInvalid();
126 DataPoint point = inTrack.getPoint(inIndex);
127 boolean pointHasSpeed = false;
128 double speedValue = 0.0;
130 // First, see if point has a speed value already
131 if (point != null && point.hasVSpeed())
133 speedValue = point.getVSpeed().getValue(Config.getUnitSet().getVerticalSpeedUnit());
134 pointHasSpeed = true;
136 // otherwise, see if we can calculate it from the heights and timestamps
138 && point != null && point.hasTimestamp() && point.hasAltitude() && !point.isWaypoint())
140 int index = inIndex-1;
142 Timestamp earlyStamp = point.getTimestamp();
143 Altitude firstAlt = point.getAltitude();
144 boolean stop = false;
146 // Count backwards until timestamp earlier than now
147 if (!point.getSegmentStart())
151 p = inTrack.getPoint(index);
152 boolean timeOk = p != null && p.hasTimestamp() && p.getTimestamp().isBefore(point.getTimestamp());
153 boolean pValid = timeOk && !p.isWaypoint();
155 earlyStamp = p.getTimestamp();
156 if (p.hasAltitude()) firstAlt = p.getAltitude();
159 stop = (p == null) || p.getSegmentStart() || hasSufficientTimeDifference(p, point);
165 // Count forwards until timestamp later than now
166 Timestamp lateStamp = point.getTimestamp();
167 Altitude lastAlt = point.getAltitude();
171 p = inTrack.getPoint(index);
172 boolean timeOk = p != null && p.hasTimestamp() && !p.getTimestamp().isBefore(point.getTimestamp());
173 boolean pValid = timeOk && !p.isWaypoint() && !p.getSegmentStart();
175 lateStamp = p.getTimestamp();
176 if (p.hasAltitude()) lastAlt = p.getAltitude();
179 stop = (p == null) || p.getSegmentStart() || hasSufficientTimeDifference(point, p);
184 // See if we've managed to get a non-zero time range
185 long milliseconds = lateStamp.getMillisecondsSince(earlyStamp);
186 if (milliseconds >= 1000L)
188 double altDiff = (lastAlt.getMetricValue() - firstAlt.getMetricValue())
189 * Config.getUnitSet().getVerticalSpeedUnit().getMultFactorFromStd();
190 speedValue = altDiff / milliseconds * 1000.0; // units are feet/sec or metres/sec
191 pointHasSpeed = true;
194 // Check whether we got a value from either method
197 inValue.setValue(speedValue);
202 * Check whether the time difference between P1 and P2 is sufficiently large
203 * @param inP1 earlier point
204 * @param inP2 later point
205 * @return true if we can stop looking now, found a point early/late enough
207 private static boolean hasSufficientTimeDifference(DataPoint inP1, DataPoint inP2)
209 if (inP1 == null || inP2 == null)
210 return true; // we have to give up now
211 if (!inP1.hasTimestamp() || !inP2.hasTimestamp())
212 return false; // keep looking
213 final long MIN_TIME_DIFFERENCE_MS = 1000L;
214 return inP2.getTimestamp().getMillisecondsSince(inP1.getTimestamp()) >= MIN_TIME_DIFFERENCE_MS;