package net.seninp.grammarviz.tinker; /* * To change this template, choose Tools | Templates * and open the template in the editor. */ /** * * @author s. conversy from n. roussel c++ version */ class LowPassFilter { double y, a, s; boolean initialized; void setAlpha(double alpha) throws Exception { if (alpha <= 0.0 || alpha > 1.0) { throw new Exception("alpha should be in (0.0., 1.0]"); } a = alpha; } public LowPassFilter(double alpha) throws Exception { init(alpha, 0); } public LowPassFilter(double alpha, double initval) throws Exception { init(alpha, initval); } private void init(double alpha, double initval) throws Exception { y = s = initval; setAlpha(alpha); initialized = false; } public double filter(double value) { double result; if (initialized) { result = a * value + (1.0 - a) * s; } else { result = value; initialized = true; } y = value; s = result; return result; } public double filterWithAlpha(double value, double alpha) throws Exception { setAlpha(alpha); return filter(value); } public boolean hasLastRawValue() { return initialized; } public double lastRawValue() { return y; } }; public class OneEuroFilter { double freq; double mincutoff; double beta_; double dcutoff; LowPassFilter x; LowPassFilter dx; double lasttime; static double UndefinedTime = -1; double alpha(double cutoff) { double te = 1.0 / freq; double tau = 1.0 / (2 * Math.PI * cutoff); return 1.0 / (1.0 + tau / te); } void setFrequency(double f) throws Exception { if (f <= 0) { throw new Exception("freq should be >0"); } freq = f; } void setMinCutoff(double mc) throws Exception { if (mc <= 0) { throw new Exception("mincutoff should be >0"); } mincutoff = mc; } void setBeta(double b) { beta_ = b; } void setDerivateCutoff(double dc) throws Exception { if (dc <= 0) { throw new Exception("dcutoff should be >0"); } dcutoff = dc; } public OneEuroFilter(double freq) throws Exception { init(freq, 1.0, 0.0, 1.0); } public OneEuroFilter(double freq, double mincutoff) throws Exception { init(freq, mincutoff, 0.0, 1.0); } public OneEuroFilter(double freq, double mincutoff, double beta_) throws Exception { init(freq, mincutoff, beta_, 1.0); } public OneEuroFilter(double freq, double mincutoff, double beta_, double dcutoff) throws Exception { init(freq, mincutoff, beta_, dcutoff); } private void init(double freq, double mincutoff, double beta_, double dcutoff) throws Exception { setFrequency(freq); setMinCutoff(mincutoff); setBeta(beta_); setDerivateCutoff(dcutoff); x = new LowPassFilter(alpha(mincutoff)); dx = new LowPassFilter(alpha(dcutoff)); lasttime = UndefinedTime; } double filter(double value) throws Exception { return filter(value, UndefinedTime); } double filter(double value, double timestamp) throws Exception { // update the sampling frequency based on timestamps if (lasttime != UndefinedTime && timestamp != UndefinedTime) { freq = 1.0 / (timestamp - lasttime); } lasttime = timestamp; // estimate the current variation per second double dvalue = x.hasLastRawValue() ? (value - x.lastRawValue()) * freq : 0.0; // FIXME: 0.0 or // value? double edvalue = dx.filterWithAlpha(dvalue, alpha(dcutoff)); // use it to update the cutoff frequency double cutoff = mincutoff + beta_ * Math.abs(edvalue); // filter the given value return x.filterWithAlpha(value, alpha(cutoff)); } public static void main(String[] args) throws Exception { // randSeed(); double duration = 10.0; // seconds double frequency = 120; // Hz double mincutoff = 1.0; // FIXME double beta = 1.0; // FIXME double dcutoff = 1.0; // this one should be ok System.out.print("#SRC OneEuroFilter.java" + "\n" + "#CFG {'beta': " + beta + ", 'freq': " + frequency + ", 'dcutoff': " + dcutoff + ", 'mincutoff': " + mincutoff + "}" + "\n" + "#LOG timestamp, signal, noisy, filtered" + "\n"); OneEuroFilter f = new OneEuroFilter(frequency, mincutoff, beta, dcutoff); for (double timestamp = 0.0; timestamp < duration; timestamp += 1.0 / frequency) { double signal = Math.sin(timestamp); double noisy = signal + (Math.random() - 0.5) / 5.0; double filtered = f.filter(noisy, timestamp); System.out.println("" + timestamp + ", " + signal + ", " + noisy + ", " + filtered); } } }