altoslib: Do data analysis on raw values rather than AltosState
[fw/altos] / altoslib / AltosTimeSeries.java
1 /*
2  * Copyright © 2017 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  */
14
15 package org.altusmetrum.altoslib_11;
16
17 import java.util.*;
18
19 public class AltosTimeSeries implements Iterable<AltosTimeValue> {
20         public String           label;
21         public AltosUnits       units;
22         List<AltosTimeValue>    values;
23
24         public void add(AltosTimeValue tv) {
25                 values.add(tv);
26         }
27
28         public void add(double time, double value) {
29                 add(new AltosTimeValue(time, value));
30         }
31
32         public AltosTimeValue get(int i) {
33                 return values.get(i);
34         }
35
36         public int size() {
37                 return values.size();
38         }
39
40         public Iterator<AltosTimeValue> iterator() {
41                 return values.iterator();
42         }
43
44         public double max() {
45                 double max = AltosLib.MISSING;
46                 for (AltosTimeValue tv : values) {
47                         if (max == AltosLib.MISSING || tv.value > max)
48                                 max = tv.value;
49                 }
50                 return max;
51         }
52
53         public double min() {
54                 double min = AltosLib.MISSING;
55                 for (AltosTimeValue tv : values) {
56                         if (min == AltosLib.MISSING || tv.value < min)
57                                 min = tv.value;
58                 }
59                 return min;
60         }
61
62         public AltosTimeSeries integrate(AltosTimeSeries integral) {
63                 double  value = 0.0;
64                 double  pvalue = 0.0;
65                 double  time = 0.0;
66                 boolean start = true;
67
68                 for (AltosTimeValue v : values) {
69                         if (start) {
70                                 value = 0.0;
71                                 start = false;
72                         } else {
73                                 value += (pvalue + v.value) / 2.0 * (v.time - time);
74                         }
75                         pvalue = v.value;
76                         time = v.time;
77 //                      System.out.printf("%g %g %g\n", time, v.value, value);
78                         integral.add(time, value);
79
80                 }
81                 return integral;
82         }
83
84         public AltosTimeSeries differentiate(AltosTimeSeries diff) {
85                 double value = 0.0;
86                 double time = 0.0;
87                 boolean start = true;
88
89                 for (AltosTimeValue v: values) {
90                         if (start) {
91                                 value = v.value;
92                                 time = v.time;
93                                 start = false;
94                         } else {
95                                 double  dx = v.time - time;
96                                 double  dy = v.value - value;
97
98                                 if (dx != 0)
99                                         diff.add(time, dy/dx);
100
101                                 time = v.time;
102                                 value = v.value;
103                         }
104                 }
105                 return diff;
106         }
107
108         private int find_left(int i, double dt) {
109                 int j;
110                 double t = values.get(i).time - dt;
111                 for (j = i; j >= 0; j--)        {
112                         if (values.get(j).time < t)
113                                 break;
114                 }
115                 return j + 1;
116
117         }
118
119         private int find_right(int i, double dt) {
120                 int j;
121                 double t = values.get(i).time + dt;
122                 for (j = i; j < values.size(); j++) {
123                         if (values.get(j).time > t)
124                                 break;
125                 }
126                 return j - 1;
127
128         }
129
130         private double filter_coeff(double dist, double width) {
131                 double ratio = dist / (width / 2);
132
133                 return Math.cos(ratio * Math.PI / 2);
134         }
135
136         public AltosTimeSeries filter(AltosTimeSeries f, double width) {
137                 double  half_width = width/2;
138                 for (int i = 0; i < values.size(); i++) {
139                         double  center_time = values.get(i).time;
140                         double  left_time = center_time - half_width;
141                         double  right_time = center_time + half_width;
142                         double  total_coeff = 0.0;
143                         double  total_value = 0.0;
144
145                         int     left = find_left(i, half_width);
146                         int     right = find_right(i, half_width);
147                         for (int j = left; j <= right; j++) {
148                                 double  j_time = values.get(j).time;
149
150                                 if (left_time <= j_time && j_time <= right_time) {
151                                         double  coeff = filter_coeff(j_time - center_time, width);
152                                         total_coeff += coeff;
153                                         total_value += coeff * values.get(j).value;
154                                 }
155                         }
156                         if (total_coeff != 0.0)
157                                 f.add(center_time, total_value / total_coeff);
158                 }
159                 return f;
160         }
161
162         public AltosTimeSeries(String label, AltosUnits units) {
163                 this.label = label;
164                 this.units = units;
165                 this.values = new ArrayList<AltosTimeValue>();
166         }
167 }