/*******************************************************************************
* Gaggle is Copyright 2010 by Geeksville Industries LLC, a California limited liability corporation.
*
* Gaggle is distributed under a dual license. We've chosen this approach because within Gaggle we've used a number
* of components that Geeksville Industries LLC might reuse for commercial products. Gaggle can be distributed under
* either of the two licenses listed below.
*
* 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.
*
* Commercial Distribution License
* If you would like to distribute Gaggle (or portions thereof) under a license other than
* the "GNU General Public License, version 2", contact Geeksville Industries. Geeksville Industries reserves
* the right to release Gaggle source code under a commercial license of its choice.
*
* GNU Public License, version 2
* All other distribution of Gaggle must conform to the terms of the GNU Public License, version 2. The full
* text of this license is included in the Gaggle source, see assets/manual/gpl-2.0.txt.
******************************************************************************/
package com.geeksville.util;
import java.util.ArrayDeque;
import java.util.Queue;
public class LinearRegression {
static class Sample {
public long x;
public float y;
public float normx;
public Sample(long x, float y) {
this.x = x;
this.y = y;
this.normx = 0; //will be recalculated
}
}
private Queue<Sample> samples = new ArrayDeque<Sample>();
private long xspan = 2 * 1000; // typically milliseconds
public float getXspan() {
return xspan;
}
// / We will keep only the most recent yspan interval around
public void setXspan(long xspan) {
this.xspan = xspan;
}
public void clearSamples() {
samples.clear();
}
public void addSample(long x, float y) {
//shouldn't happen, but don't let it pollute the sample pool if somehow this triggers
if(Float.isNaN(y))
{
assert(false);
return;
}
synchronized (samples) {
{
Sample s = new Sample(x, y);
samples.add(s);
}
// Cull old entries
long oldest = x - xspan;
while (samples.peek().x < oldest) {
Sample s = samples.remove();
}
}
}
public double getSlope() {
synchronized (samples) {
///have some reasonable amount of samples
if(samples.size() < 2)
return 0;
long basex = samples.peek().x;
double sumx = 0;
double sumy = 0;
for (Sample s : samples)
{
//arithmetic on raw timestamp (in ms) is fairly dangerous, reduce
//down to a normalized form (offset from the first item)
s.normx = (float) (s.x - basex);
sumx += s.normx;
sumy += s.y;
}
double xbar = sumx / samples.size();
double ybar = sumy / samples.size();
double xxbar = 0.0, xybar = 0.0;
for (Sample s : samples) {
xxbar += (s.normx - xbar) * (s.normx - xbar);
xybar += (s.normx - xbar) * (s.y - ybar);
}
if(xxbar > 0.0001)
{
double beta1 = xybar / xxbar;
return beta1;
}
else
{
return 0;
}
}
}
}