diff options
| author | Bdale Garbee <bdale@gag.com> | 2017-08-12 00:59:03 -0400 | 
|---|---|---|
| committer | Bdale Garbee <bdale@gag.com> | 2017-08-12 00:59:03 -0400 | 
| commit | 59c6167b9f1e9de30455af1632e9a0b65d64ad63 (patch) | |
| tree | d27e4b3df53300081aa6ac0a30820c58a1c968ef /altoslib/AltosTimeSeries.java | |
| parent | 41eedf88751910ea9c0a299444fbac769edb8427 (diff) | |
| parent | fccfa54bb3b746cecfcdc1fd497cf736bbfe3ef3 (diff) | |
Merge branch 'branch-1.8' into debian
Diffstat (limited to 'altoslib/AltosTimeSeries.java')
| -rw-r--r-- | altoslib/AltosTimeSeries.java | 307 | 
1 files changed, 307 insertions, 0 deletions
| diff --git a/altoslib/AltosTimeSeries.java b/altoslib/AltosTimeSeries.java new file mode 100644 index 00000000..b3c432fc --- /dev/null +++ b/altoslib/AltosTimeSeries.java @@ -0,0 +1,307 @@ +/* + * Copyright © 2017 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + */ + +package org.altusmetrum.altoslib_12; + +import java.util.*; + +public class AltosTimeSeries implements Iterable<AltosTimeValue>, Comparable<AltosTimeSeries> { +	public String			label; +	public AltosUnits		units; +	ArrayList<AltosTimeValue>	values; + +	public int compareTo(AltosTimeSeries other) { +		return label.compareTo(other.label); +	} + +	public void add(AltosTimeValue tv) { +		values.add(tv); +	} + +	public void add(double time, double value) { +		add(new AltosTimeValue(time, value)); +	} + +	public AltosTimeValue get(int i) { +		return values.get(i); +	} + +	private double lerp(AltosTimeValue v0, AltosTimeValue v1, double t) { +		/* degenerate case */ +		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); +	} + +	private int after_index(double time) { +		int	lo = 0; +		int	hi = values.size() - 1; + +		while (lo <= hi) { +			int mid = (lo + hi) / 2; + +			if (values.get(mid).time < time) +				lo = mid + 1; +			else +				hi = mid - 1; +		} +		return lo; +	} + +	/* Compute a value for an arbitrary time */ +	public double value(double time) { +		int after = after_index(time); +		double ret; + +		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 */ +	public double value_before(double time) { +		int after = after_index(time); + +		if (after == 0) +			return values.get(0).value; +		return values.get(after-1).value; +	} + +	/* Find the value just after an arbitrary time */ +	public double value_after(double time) { +		int after = after_index(time); + +		if (after == values.size()) +			return values.get(after-1).value; +		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(); +	} + +	public Iterator<AltosTimeValue> iterator() { +		return values.iterator(); +	} + +	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 (start_time <= tv.time && tv.time <= end_time) +				if (max == null || tv.value > max.value) +					max = tv; +		} +		return max; +	} + +	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 (start_time <= tv.time && tv.time <= end_time) +				if (min == null || tv.value < min.value) +					min = tv; +		} +		return min; +	} + +	public AltosTimeValue first() { +		return values.get(0); +	} + +	public AltosTimeValue last() { +		return values.get(values.size() - 1); +	} + +	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; +		double 	time = 0.0; +		boolean start = true; + +		for (AltosTimeValue v : values) { +			if (start) { +				value = 0.0; +				start = false; +			} else { +				value += (pvalue + v.value) / 2.0 * (v.time - time); +			} +			pvalue = v.value; +			time = v.time; +			integral.add(time, value); + +		} +		return integral; +	} + +	public AltosTimeSeries differentiate(AltosTimeSeries diff) { +		double value = 0.0; +		double time = 0.0; +		boolean start = true; + +		for (AltosTimeValue v: values) { +			if (start) { +				value = v.value; +				time = v.time; +				start = false; +			} else { +				double	dx = v.time - time; +				double	dy = v.value - value; + +				if (dx != 0) +					diff.add(time, dy/dx); + +				time = v.time; +				value = v.value; +			} +		} +		return diff; +	} + +	private int find_left(int i, double dt) { +		int j; +		double t = values.get(i).time - dt; +		for (j = i; j >= 0; j--)	{ +			if (values.get(j).time < t) +				break; +		} +		return j + 1; + +	} + +	private int find_right(int i, double dt) { +		int j; +		double t = values.get(i).time + dt; +		for (j = i; j < values.size(); j++) { +			if (values.get(j).time > t) +				break; +		} +		return j - 1; + +	} + +	private double filter_coeff(double dist, double width) { +		double ratio = dist / (width / 2); + +		return Math.cos(ratio * Math.PI / 2); +	} + +	public AltosTimeSeries filter(AltosTimeSeries f, double width) { +		double	half_width = width/2; +		for (int i = 0; i < values.size(); i++) { +			double	center_time = values.get(i).time; +			double	left_time = center_time - half_width; +			double	right_time = center_time + half_width; +			double	total_coeff = 0.0; +			double	total_value = 0.0; + +			int	left = find_left(i, half_width); +			int	right = find_right(i, half_width); + +			for (int j = left; j <= right; j++) { +				double	j_time = values.get(j).time; + +				if (left_time <= j_time && j_time <= right_time) { +					double	j_left = j == left ? left_time : values.get(j-1).time; +					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 += partial; +				} +			} +			if (total_coeff != 0.0) +				f.add(center_time, total_value / total_coeff); +		} +		return f; +	} + +	public AltosTimeSeries(String label, AltosUnits units) { +		this.label = label; +		this.units = units; +		this.values = new ArrayList<AltosTimeValue>(); +	} +} | 
