altosdroid: Fix offline map messages to match new meanings
[fw/altos] / altoslib / AltosTimeSeries.java
index 30b24d82bcf3503fe6f059e7ae022c512f700994..75225e1558d236ae45bdd34e72509fcce48905ab 100644 (file)
  * General Public License for more details.
  */
 
-package org.altusmetrum.altoslib_11;
+package org.altusmetrum.altoslib_13;
 
 import java.util.*;
 
-public class AltosTimeSeries implements Iterable<AltosTimeValue> {
+public class AltosTimeSeries implements Iterable<AltosTimeValue>, Comparable<AltosTimeSeries> {
        public String                   label;
        public AltosUnits               units;
        ArrayList<AltosTimeValue>       values;
+       boolean                         data_changed;
+
+       public int compareTo(AltosTimeSeries other) {
+               return label.compareTo(other.label);
+       }
 
        public void add(AltosTimeValue tv) {
+               data_changed = true;
                values.add(tv);
        }
 
+       public void erase_values() {
+               data_changed = true;
+               this.values = new ArrayList<AltosTimeValue>();
+       }
+
+       public void clear_changed() {
+               data_changed = false;
+       }
+
+//     public boolean changed() {
+//             return data_changed;
+//     }
+
        public void add(double time, double value) {
                add(new AltosTimeValue(time, value));
        }
@@ -38,7 +57,7 @@ public class AltosTimeSeries implements Iterable<AltosTimeValue> {
                if (v0.time == v1.time)
                        return (v0.value + v1.value) / 2;
 
-               return (v0.value * (v1.time - t) + v1.value * (t - v0.time)) / v1.time - v0.time;
+               return (v0.value * (v1.time - t) + v1.value * (t - v0.time)) / (v1.time - v0.time);
        }
 
        private int after_index(double time) {
@@ -59,12 +78,18 @@ public class AltosTimeSeries implements Iterable<AltosTimeValue> {
        /* Compute a value for an arbitrary time */
        public double value(double time) {
                int after = after_index(time);
-               if (after == 0)
-                       return values.get(0).value;
-               if (after == values.size())
-                       return values.get(after - 1).value;
+               double ret;
 
-               return lerp(values.get(after-1), values.get(after), time);
+               if (after == 0)
+                       ret = values.get(0).value;
+               else if (after == values.size())
+                       ret = values.get(after - 1).value;
+               else {
+                       AltosTimeValue b = values.get(after-1);
+                       AltosTimeValue a = values.get(after);
+                       ret = lerp(b, a, time);
+               }
+               return ret;
        }
 
        /* Find the value just before an arbitrary time */
@@ -85,6 +110,16 @@ public class AltosTimeSeries implements Iterable<AltosTimeValue> {
                return values.get(after).value;
        }
 
+       public double time_of(double value) {
+               double  last = AltosLib.MISSING;
+               for (AltosTimeValue v : values) {
+                       if (v.value >= value)
+                               return v.time;
+                       last = v.time;
+               }
+               return last;
+       }
+
        public int size() {
                return values.size();
        }
@@ -93,24 +128,90 @@ public class AltosTimeSeries implements Iterable<AltosTimeValue> {
                return values.iterator();
        }
 
-       public double max() {
-               double max = AltosLib.MISSING;
+       public AltosTimeValue max() {
+               AltosTimeValue max = null;
+               for (AltosTimeValue tv : values)
+                       if (max == null || tv.value > max.value)
+                               max = tv;
+               return max;
+       }
+
+       public AltosTimeValue max(double start_time, double end_time) {
+               AltosTimeValue max = null;
                for (AltosTimeValue tv : values) {
-                       if (max == AltosLib.MISSING || tv.value > max)
-                               max = tv.value;
+                       if (start_time <= tv.time && tv.time <= end_time)
+                               if (max == null || tv.value > max.value)
+                                       max = tv;
                }
                return max;
        }
 
-       public double min() {
-               double min = AltosLib.MISSING;
+       public AltosTimeValue min() {
+               AltosTimeValue min = null;
+               for (AltosTimeValue tv : values) {
+                       if (min == null || tv.value < min.value)
+                               min = tv;
+               }
+               return min;
+       }
+
+       public AltosTimeValue min(double start_time, double end_time) {
+               AltosTimeValue min = null;
                for (AltosTimeValue tv : values) {
-                       if (min == AltosLib.MISSING || tv.value < min)
-                               min = tv.value;
+                       if (start_time <= tv.time && tv.time <= end_time)
+                               if (min == null || tv.value < min.value)
+                                       min = tv;
                }
                return min;
        }
 
+       public AltosTimeValue first() {
+               if (values.size() > 0)
+                       return values.get(0);
+               return null;
+       }
+
+       public AltosTimeValue last() {
+               if (values.size() > 0)
+                       return values.get(values.size() - 1);
+               return null;
+       }
+
+       public double average() {
+               double total_value = 0;
+               double total_time = 0;
+               AltosTimeValue prev = null;
+               for (AltosTimeValue tv : values) {
+                       if (prev != null) {
+                               total_value += (tv.value + prev.value) / 2 * (tv.time - prev.time);
+                               total_time += (tv.time - prev.time);
+                       }
+                       prev = tv;
+               }
+               if (total_time == 0)
+                       return AltosLib.MISSING;
+               return total_value / total_time;
+       }
+
+       public double average(double start_time, double end_time) {
+               double total_value = 0;
+               double total_time = 0;
+               AltosTimeValue prev = null;
+               for (AltosTimeValue tv : values) {
+                       if (start_time <= tv.time && tv.time <= end_time) {
+                               if (prev != null) {
+                                       total_value += (tv.value + prev.value) / 2 * (tv.time - start_time);
+                                       total_time += (tv.time - start_time);
+                               }
+                               start_time = tv.time;
+                       }
+                       prev = tv;
+               }
+               if (total_time == 0)
+                       return AltosLib.MISSING;
+               return total_value / total_time;
+       }
+
        public AltosTimeSeries integrate(AltosTimeSeries integral) {
                double  value = 0.0;
                double  pvalue = 0.0;
@@ -126,7 +227,6 @@ public class AltosTimeSeries implements Iterable<AltosTimeValue> {
                        }
                        pvalue = v.value;
                        time = v.time;
-//                     System.out.printf("%g %g %g\n", time, v.value, value);
                        integral.add(time, value);
 
                }
@@ -179,14 +279,35 @@ public class AltosTimeSeries implements Iterable<AltosTimeValue> {
 
        }
 
-       private double filter_coeff(double dist, double width) {
-               double ratio = dist / (width / 2);
+       private static double i0(double x) {
+               double  ds = 1, d = 0, s = 0;
+
+               do {
+                       d += 2;
+                       ds = ds * (x * x) / (d * d);
+                       s += ds;
+               } while (ds - 0.2e-8 * s > 0);
+               return s;
+       }
+
+       private static double kaiser(double n, double m, double beta) {
+               double alpha = m / 2;
+               double t = (n - alpha) / alpha;
+
+               if (t > 1 || t < -1)
+                       t = 1;
+               double k = i0 (beta * Math.sqrt (1 - t*t)) / i0(beta);
+               return k;
+       }
 
-               return Math.cos(ratio * Math.PI / 2);
+       private double filter_coeff(double dist, double width) {
+               return kaiser(dist + width/2.0, width, 2 * Math.PI);
        }
 
        public AltosTimeSeries filter(AltosTimeSeries f, double width) {
+
                double  half_width = width/2;
+               int half_point = values.size() / 2;
                for (int i = 0; i < values.size(); i++) {
                        double  center_time = values.get(i).time;
                        double  left_time = center_time - half_width;
@@ -205,9 +326,11 @@ public class AltosTimeSeries implements Iterable<AltosTimeValue> {
                                        double  j_right = j == right ? right_time : values.get(j+1).time;
                                        double  interval = (j_right - j_left) / 2.0;
                                        double  coeff = filter_coeff(j_time - center_time, width) * interval;
+                                       double  value = values.get(j).value;
+                                       double  partial = value * coeff;
 
                                        total_coeff += coeff;
-                                       total_value += coeff * values.get(j).value;
+                                       total_value += partial;
                                }
                        }
                        if (total_coeff != 0.0)