2 * Copyright © 2017 Keith Packard <keithp@keithp.com>
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.
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.
15 package org.altusmetrum.altoslib_12;
19 public class AltosTimeSeries implements Iterable<AltosTimeValue>, Comparable<AltosTimeSeries> {
21 public AltosUnits units;
22 ArrayList<AltosTimeValue> values;
24 public int compareTo(AltosTimeSeries other) {
25 return label.compareTo(other.label);
28 public void add(AltosTimeValue tv) {
32 public void add(double time, double value) {
33 add(new AltosTimeValue(time, value));
36 public AltosTimeValue get(int i) {
40 private double lerp(AltosTimeValue v0, AltosTimeValue v1, double t) {
42 if (v0.time == v1.time)
43 return (v0.value + v1.value) / 2;
45 return (v0.value * (v1.time - t) + v1.value * (t - v0.time)) / (v1.time - v0.time);
48 private int after_index(double time) {
50 int hi = values.size() - 1;
53 int mid = (lo + hi) / 2;
55 if (values.get(mid).time < time)
63 /* Compute a value for an arbitrary time */
64 public double value(double time) {
65 int after = after_index(time);
69 ret = values.get(0).value;
70 else if (after == values.size())
71 ret = values.get(after - 1).value;
73 AltosTimeValue b = values.get(after-1);
74 AltosTimeValue a = values.get(after);
75 ret = lerp(b, a, time);
80 /* Find the value just before an arbitrary time */
81 public double value_before(double time) {
82 int after = after_index(time);
85 return values.get(0).value;
86 return values.get(after-1).value;
89 /* Find the value just after an arbitrary time */
90 public double value_after(double time) {
91 int after = after_index(time);
93 if (after == values.size())
94 return values.get(after-1).value;
95 return values.get(after).value;
98 public double time_of(double value) {
99 double last = AltosLib.MISSING;
100 for (AltosTimeValue v : values) {
101 if (v.value >= value)
109 return values.size();
112 public Iterator<AltosTimeValue> iterator() {
113 return values.iterator();
116 public AltosTimeValue max() {
117 AltosTimeValue max = null;
118 for (AltosTimeValue tv : values)
119 if (max == null || tv.value > max.value)
124 public AltosTimeValue max(double start_time, double end_time) {
125 AltosTimeValue max = null;
126 for (AltosTimeValue tv : values) {
127 if (start_time <= tv.time && tv.time <= end_time)
128 if (max == null || tv.value > max.value)
134 public AltosTimeValue min() {
135 AltosTimeValue min = null;
136 for (AltosTimeValue tv : values) {
137 if (min == null || tv.value < min.value)
143 public AltosTimeValue min(double start_time, double end_time) {
144 AltosTimeValue min = null;
145 for (AltosTimeValue tv : values) {
146 if (start_time <= tv.time && tv.time <= end_time)
147 if (min == null || tv.value < min.value)
153 public AltosTimeValue first() {
154 return values.get(0);
157 public AltosTimeValue last() {
158 return values.get(values.size() - 1);
161 public double average() {
162 double total_value = 0;
163 double total_time = 0;
164 AltosTimeValue prev = null;
165 for (AltosTimeValue tv : values) {
167 total_value += (tv.value + prev.value) / 2 * (tv.time - prev.time);
168 total_time += (tv.time - prev.time);
173 return AltosLib.MISSING;
174 return total_value / total_time;
177 public double average(double start_time, double end_time) {
178 double total_value = 0;
179 double total_time = 0;
180 AltosTimeValue prev = null;
181 for (AltosTimeValue tv : values) {
182 if (start_time <= tv.time && tv.time <= end_time) {
184 total_value += (tv.value + prev.value) / 2 * (tv.time - start_time);
185 total_time += (tv.time - start_time);
187 start_time = tv.time;
192 return AltosLib.MISSING;
193 return total_value / total_time;
196 public AltosTimeSeries integrate(AltosTimeSeries integral) {
200 boolean start = true;
202 for (AltosTimeValue v : values) {
207 value += (pvalue + v.value) / 2.0 * (v.time - time);
211 integral.add(time, value);
217 public AltosTimeSeries differentiate(AltosTimeSeries diff) {
220 boolean start = true;
222 for (AltosTimeValue v: values) {
228 double dx = v.time - time;
229 double dy = v.value - value;
232 diff.add(time, dy/dx);
241 private int find_left(int i, double dt) {
243 double t = values.get(i).time - dt;
244 for (j = i; j >= 0; j--) {
245 if (values.get(j).time < t)
252 private int find_right(int i, double dt) {
254 double t = values.get(i).time + dt;
255 for (j = i; j < values.size(); j++) {
256 if (values.get(j).time > t)
263 private double filter_coeff(double dist, double width) {
264 double ratio = dist / (width / 2);
266 return Math.cos(ratio * Math.PI / 2);
269 public AltosTimeSeries filter(AltosTimeSeries f, double width) {
270 double half_width = width/2;
271 for (int i = 0; i < values.size(); i++) {
272 double center_time = values.get(i).time;
273 double left_time = center_time - half_width;
274 double right_time = center_time + half_width;
275 double total_coeff = 0.0;
276 double total_value = 0.0;
278 int left = find_left(i, half_width);
279 int right = find_right(i, half_width);
281 for (int j = left; j <= right; j++) {
282 double j_time = values.get(j).time;
284 if (left_time <= j_time && j_time <= right_time) {
285 double j_left = j == left ? left_time : values.get(j-1).time;
286 double j_right = j == right ? right_time : values.get(j+1).time;
287 double interval = (j_right - j_left) / 2.0;
288 double coeff = filter_coeff(j_time - center_time, width) * interval;
289 double value = values.get(j).value;
290 double partial = value * coeff;
292 total_coeff += coeff;
293 total_value += partial;
296 if (total_coeff != 0.0)
297 f.add(center_time, total_value / total_coeff);
302 public AltosTimeSeries(String label, AltosUnits units) {
305 this.values = new ArrayList<AltosTimeValue>();