diff --git a/src/userguide/java/org/apache/commons/math3/userguide/filter/ConstantVoltageExample.java b/src/userguide/java/org/apache/commons/math3/userguide/filter/ConstantVoltageExample.java new file mode 100644 index 000000000..94180d5b7 --- /dev/null +++ b/src/userguide/java/org/apache/commons/math3/userguide/filter/ConstantVoltageExample.java @@ -0,0 +1,241 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You 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 org.apache.commons.math3.userguide.filter; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.JComponent; +import javax.swing.JPanel; + +import org.apache.commons.math3.filter.DefaultMeasurementModel; +import org.apache.commons.math3.filter.DefaultProcessModel; +import org.apache.commons.math3.filter.KalmanFilter; +import org.apache.commons.math3.filter.MeasurementModel; +import org.apache.commons.math3.filter.ProcessModel; +import org.apache.commons.math3.linear.Array2DRowRealMatrix; +import org.apache.commons.math3.linear.ArrayRealVector; +import org.apache.commons.math3.linear.RealMatrix; +import org.apache.commons.math3.linear.RealVector; +import org.apache.commons.math3.random.RandomGenerator; +import org.apache.commons.math3.random.Well19937c; +import org.apache.commons.math3.userguide.ExampleUtils; +import org.apache.commons.math3.userguide.ExampleUtils.ExampleFrame; + +import com.xeiam.xchart.Chart; +import com.xeiam.xchart.ChartBuilder; +import com.xeiam.xchart.Series; +import com.xeiam.xchart.SeriesLineStyle; +import com.xeiam.xchart.SeriesMarker; +import com.xeiam.xchart.XChartPanel; +import com.xeiam.xchart.StyleManager.ChartType; +import com.xeiam.xchart.StyleManager.LegendPosition; + +public class ConstantVoltageExample { + + public static class VoltMeter { + + private final double initialVoltage; + private final double processNoise; + private final double measurementNoise; + private final RandomGenerator rng; + + private double voltage; + + public VoltMeter(double voltage, double processNoise, double measurementNoise, int seed) { + this.initialVoltage = voltage; + this.voltage = voltage; + this.processNoise = processNoise; + this.measurementNoise = measurementNoise; + rng = new Well19937c(seed); + } + + /** + * Returns the real voltage without any measurement noise. + * + * @return the real voltage + */ + public double getVoltage() { + return voltage; + } + + public double getMeasuredVoltage() { + return getVoltage() + rng.nextGaussian() * measurementNoise; + } + + public void step() { + // we apply only the process noise + voltage = initialVoltage + rng.nextGaussian() * processNoise; + } + } + + /** constant voltage test */ + public static void constantVoltageTest(Chart chart1, Chart chart2) { + + final double voltage = 1.25d; + final double measurementNoise = 0.1d; // measurement noise (V) - std dev + final double processNoise = 1e-5d; + + final VoltMeter voltMeter = new VoltMeter(voltage, processNoise, measurementNoise, 2); + + // the state transition matrix -> constant + final RealMatrix A = new Array2DRowRealMatrix(new double[] { 1d }); + + // the control matrix -> no control input + final RealMatrix B = new Array2DRowRealMatrix(new double[] { 0d }); + + // the measurement matrix -> we measure the voltage directly + final RealMatrix H = new Array2DRowRealMatrix(new double[] { 1d }); + + // the initial state vector -> slightly wrong + final RealVector x0 = new ArrayRealVector(new double[] { 1.45 }); + + // the process covariance matrix + final RealMatrix Q = new Array2DRowRealMatrix(new double[] { processNoise * processNoise }); + + // the initial error covariance -> assume a large error at the beginning + final RealMatrix P0 = new Array2DRowRealMatrix(new double[] { 1 }); + + // the measurement covariance matrix -> put the "real" variance + RealMatrix R = new Array2DRowRealMatrix(new double[] { measurementNoise * measurementNoise }); + + final ProcessModel pm = new DefaultProcessModel(A, B, Q, x0, P0); + final MeasurementModel mm = new DefaultMeasurementModel(H, R); + final KalmanFilter filter = new KalmanFilter(pm, mm); + + final List xAxis = new ArrayList(); + final List realVoltageSeries = new ArrayList(); + final List measuredVoltageSeries = new ArrayList(); + final List kalmanVoltageSeries = new ArrayList(); + + final List covSeries = new ArrayList(); + + for (int i = 0; i < 200; i++) { + xAxis.add(i); + + voltMeter.step(); + + realVoltageSeries.add(voltMeter.getVoltage()); + + // get the measured voltage from the volt meter + final double measuredVoltage = voltMeter.getMeasuredVoltage(); + measuredVoltageSeries.add(measuredVoltage); + + filter.predict(); + filter.correct(new double[] { measuredVoltage }); + + kalmanVoltageSeries.add(filter.getStateEstimation()[0]); + + covSeries.add(filter.getErrorCovariance()[0][0]); + } + + chart1.setYAxisTitle("Voltage"); + chart1.setXAxisTitle("Iteration"); + + Series dataset = chart1.addSeries("real", xAxis, realVoltageSeries); + dataset.setMarker(SeriesMarker.NONE); + + dataset = chart1.addSeries("measured", xAxis, measuredVoltageSeries); + dataset.setLineStyle(SeriesLineStyle.DOT_DOT); + dataset.setMarker(SeriesMarker.NONE); + + dataset = chart1.addSeries("filtered", xAxis, kalmanVoltageSeries); + dataset.setLineColor(Color.red); + dataset.setLineStyle(SeriesLineStyle.DASH_DASH); + dataset.setMarker(SeriesMarker.NONE); + + // Error covariance chart + + chart2.setYAxisTitle("(Voltage)²"); + chart2.setXAxisTitle("Iteration"); + + dataset = chart2.addSeries("cov", xAxis, covSeries); + dataset.setLineColor(Color.black); + dataset.setLineStyle(SeriesLineStyle.SOLID); + dataset.setMarker(SeriesMarker.NONE); + + } + + public static Chart createChart(String title, int width, int height, + LegendPosition position, boolean legendVisible) { + Chart chart = new ChartBuilder().width(width).height(height).build(); + + // Customize Chart + chart.setChartTitle(title); + chart.getStyleManager().setChartTitleVisible(true); + chart.getStyleManager().setChartTitleFont(new Font("Arial", Font.PLAIN, 10)); + chart.getStyleManager().setLegendPosition(position); + chart.getStyleManager().setLegendVisible(legendVisible); + chart.getStyleManager().setLegendFont(new Font("Arial", Font.PLAIN, 10)); + chart.getStyleManager().setLegendPadding(6); + chart.getStyleManager().setLegendSeriesLineLength(10); + chart.getStyleManager().setAxisTickLabelsFont(new Font("Arial", Font.PLAIN, 9)); + + chart.getStyleManager().setChartBackgroundColor(Color.white); + chart.getStyleManager().setChartPadding(4); + + chart.getStyleManager().setChartType(ChartType.Line); + return chart; + } + + public static JComponent createComponent() { + JComponent container = new JPanel(); + container.setLayout(new BoxLayout(container, BoxLayout.LINE_AXIS)); + + Chart chart1 = createChart("Filter", 550, 450, LegendPosition.InsideNE, true); + Chart chart2 = createChart("Error Covariance", 450, 450, LegendPosition.InsideNE, false); + + constantVoltageTest(chart1, chart2); + + container.add(new XChartPanel(chart1)); + container.add(new XChartPanel(chart2)); + + container.setBorder(BorderFactory.createLineBorder(Color.black, 1)); + return container; + } + + @SuppressWarnings("serial") + public static class Display extends ExampleFrame { + + private JComponent container; + + public Display() { + setTitle("Commons-Math: Kalman Filter example"); + setSize(1100, 700); + + container = new JPanel(); + + JComponent comp = createComponent(); + container.add(comp); + + add(container); + } + + @Override + public Component getMainPanel() { + return container; + } + } + + public static void main(String[] args) { + ExampleUtils.showExampleFrame(new Display()); + } + +}