/**
* Copyright 2012 Kevin J. Jones (http://www.kevinjjones.co.uk)
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package uk.co.kevinjjones.model;
import java.text.ParseException;
import org.apache.commons.math3.analysis.polynomials.PolynomialFunction;
import org.apache.commons.math3.fitting.CurveFitter;
import org.apache.commons.math3.optim.nonlinear.vector.jacobian.LevenbergMarquardtOptimizer;
/**
* Wheel virtual stream
*/
public class SmoothStream implements ROStream {
private ROStream _base;
private double[] _curve=null;
public SmoothStream(ROStream base) {
_base = base;
}
@Override
public void setMeta(String id, String value) {
_base.setMeta(id, value);
}
@Override
public String getMeta(String id) {
return _base.getMeta(id);
}
@Override
public String name() {
return _base.name();
}
@Override
public String description() {
return _base.description();
}
@Override
public String axis() {
return _base.axis();
}
@Override
public String units() {
return _base.units();
}
@Override
public int size() {
return _base.hashCode();
}
@Override
public String getString(int position) {
return _base.getString(position);
}
@Override
public long getTick(int position) throws ParseException {
return _base.getTick(position);
}
@Override
public double getNumeric(int position) throws NumberFormatException {
if (_curve == null) {
curveFit();
}
return _curve[position];
}
@Override
public String[] getStringSet() {
return _base.getStringSet();
}
@Override
public Double[] toArray() throws NumberFormatException {
return _base.toArray();
}
private void curveFit() {
if (_curve == null) {
Double[] data=toArray();
int points = data.length;
int blockSize = 8;
_curve=new double[points];
int idx = 0;
while (idx<points) {
// Skip zeros, they curve fit badly
if (data[idx]==0) {
_curve[idx]=0;
idx++;
} else {
// Consume a block
int bcount=0;
while (idx+bcount<points && bcount<blockSize) {
if (data[idx+bcount]==0) {
break;
}
bcount++;
}
// Skip short blocks
if (bcount<5) {
for (int i=0; i<bcount; i++) {
_curve[idx+i]=data[idx+i];
}
idx+=bcount;
} else {
// Try to fit this
CurveFitter fitter = new CurveFitter(new LevenbergMarquardtOptimizer());
double[] init=new double[4];
for (int i=0; i<bcount; i++) {
fitter.addObservedPoint(i,data[idx+i]);
}
final double[] best = fitter.fit(new PolynomialFunction.Parametric(), init);
final PolynomialFunction fitted = new PolynomialFunction(best);
for (int i=0; i<bcount; i++) {
_curve[idx+i] = fitted.value(i);
// Negative fits upset the graphing
if (_curve[idx+i]<0) {
_curve[idx+i] = 0;
}
}
idx+= bcount;
}
}
}
}
}
}