]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/data/SpeedCalculator.java
Version 19.1, August 2018
[GpsPrune.git] / tim / prune / data / SpeedCalculator.java
1 package tim.prune.data;
2
3 import tim.prune.config.Config;
4
5 /**
6  * Abstract class to hold static calculation functions
7  * for speed (and vertical speed)
8  */
9 public abstract class SpeedCalculator
10 {
11         /**
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
16          */
17         public static void calculateSpeed(Track inTrack, int inIndex, SpeedValue inValue)
18         {
19                 if (inValue != null)
20                 {
21                         inValue.setInvalid();
22                 }
23                 if (inTrack == null || inIndex < 0 || inValue == null)
24                 {
25                         System.err.println("Cannot calculate speed for index " + inIndex);
26                         return;
27                 }
28
29                 DataPoint point = inTrack.getPoint(inIndex);
30                 if (point == null) {return;}
31                 boolean pointHasSpeed = false;
32                 double  speedValue = 0.0;
33
34                 // First, see if point has a speed value already
35                 if (point.hasHSpeed()) {
36                         speedValue = point.getHSpeed().getValue(Config.getUnitSet().getSpeedUnit());
37                         pointHasSpeed = true;
38                 }
39
40                 // otherwise, see if we can calculate it from the timestamps
41                 if (!pointHasSpeed && point.hasTimestamp() && !point.isWaypoint())
42                 {
43                         double totalRadians = 0.0;
44                         int index = inIndex-1;
45                         DataPoint p = null;
46                         DataPoint q = point;
47                         Timestamp earlyStamp = point.getTimestamp();
48                         boolean stop = false;
49
50                         // Count backwards until timestamp earlier than now; total distances back to this point
51                         if (!point.getSegmentStart())
52                         {
53                                 do
54                                 {
55                                         p = inTrack.getPoint(index);
56                                         boolean timeOk = p != null && p.hasTimestamp() && p.getTimestamp().isBefore(point.getTimestamp());
57                                         boolean pValid = timeOk && !p.isWaypoint();
58                                         if (pValid) {
59                                                 totalRadians += DataPoint.calculateRadiansBetween(p, q);
60                                                 earlyStamp = p.getTimestamp();
61                                         }
62
63                                         stop = (p == null) || p.getSegmentStart() || hasSufficientTimeDifference(p, point);
64                                         index--;
65                                         if (p != null && !p.isWaypoint()) {
66                                                 q = p;
67                                         }
68                                 }
69                                 while (!stop);
70                         }
71                         // Count forwards until timestamp later than now; total distances forward to this point
72                         Timestamp lateStamp = point.getTimestamp();
73                         q = point;
74                         index = inIndex+1;
75                         do
76                         {
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();
80                                 if (pValid) {
81                                         totalRadians += DataPoint.calculateRadiansBetween(p, q);
82                                         lateStamp = p.getTimestamp();
83                                 }
84
85                                 stop = (p == null) || p.getSegmentStart() || hasSufficientTimeDifference(point, p);
86                                 index++;
87                                 if (p != null && !p.isWaypoint()) {
88                                         q = p;
89                                 }
90                         }
91                         while (!stop);
92
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)
96                         {
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;
101                         }
102                 }
103                 // Did we get a value?
104                 if (pointHasSpeed)
105                 {
106                         inValue.setValue(speedValue);
107                 }
108                 // otherwise, just leave value as invalid
109         }
110
111
112         /**
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
117          */
118         public static void calculateVerticalSpeed(Track inTrack, int inIndex, SpeedValue inValue)
119         {
120                 if (inTrack == null || inIndex < 0 || inValue == null) {
121                         System.err.println("Cannot calculate vert speed for index " + inIndex);
122                         return;
123                 }
124                 inValue.setInvalid();
125
126                 DataPoint point = inTrack.getPoint(inIndex);
127                 boolean pointHasSpeed = false;
128                 double  speedValue = 0.0;
129
130                 // First, see if point has a speed value already
131                 if (point != null && point.hasVSpeed())
132                 {
133                         speedValue = point.getVSpeed().getValue(Config.getUnitSet().getVerticalSpeedUnit());
134                         pointHasSpeed = true;
135                 }
136                 // otherwise, see if we can calculate it from the heights and timestamps
137                 if (!pointHasSpeed
138                         && point != null && point.hasTimestamp() && point.hasAltitude() && !point.isWaypoint())
139                 {
140                         int index = inIndex-1;
141                         DataPoint p = null;
142                         Timestamp earlyStamp = point.getTimestamp();
143                         Altitude firstAlt = point.getAltitude();
144                         boolean stop = false;
145
146                         // Count backwards until timestamp earlier than now
147                         if (!point.getSegmentStart())
148                         {
149                                 do
150                                 {
151                                         p = inTrack.getPoint(index);
152                                         boolean timeOk = p != null && p.hasTimestamp() && p.getTimestamp().isBefore(point.getTimestamp());
153                                         boolean pValid = timeOk && !p.isWaypoint();
154                                         if (pValid) {
155                                                 earlyStamp = p.getTimestamp();
156                                                 if (p.hasAltitude()) firstAlt = p.getAltitude();
157                                         }
158
159                                         stop = (p == null) || p.getSegmentStart() || hasSufficientTimeDifference(p, point);
160                                         index--;
161                                 }
162                                 while (!stop);
163                         }
164
165                         // Count forwards until timestamp later than now
166                         Timestamp lateStamp = point.getTimestamp();
167                         Altitude lastAlt = point.getAltitude();
168                         index = inIndex+1;
169                         do
170                         {
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();
174                                 if (pValid) {
175                                         lateStamp = p.getTimestamp();
176                                         if (p.hasAltitude()) lastAlt = p.getAltitude();
177                                 }
178
179                                 stop = (p == null) || p.getSegmentStart() || hasSufficientTimeDifference(point, p);
180                                 index++;
181                         }
182                         while (!stop);
183
184                         // See if we've managed to get a non-zero time range
185                         long milliseconds = lateStamp.getMillisecondsSince(earlyStamp);
186                         if (milliseconds >= 1000L)
187                         {
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;
192                         }
193                 }
194                 // Check whether we got a value from either method
195                 if (pointHasSpeed)
196                 {
197                         inValue.setValue(speedValue);
198                 }
199         }
200
201         /**
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
206          */
207         private static boolean hasSufficientTimeDifference(DataPoint inP1, DataPoint inP2)
208         {
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;
215         }
216 }