1 /* 2 * Copyright (C) 2015 Alberto Irurueta Carro (alberto@irurueta.com) 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.irurueta.numerical.signal.processing; 17 18 import com.irurueta.algebra.AlgebraException; 19 import com.irurueta.algebra.ArrayUtils; 20 import com.irurueta.algebra.Matrix; 21 22 /** 23 * Estimates noise covariance matrix for a given set of measures. 24 * Covariance matrix is updated each time that measures are added. 25 * This class also computes average values of samples to estimate the bias 26 * on given noise samples. 27 * This class should only be used for samples obtained while system state is 28 * held constant. 29 */ 30 public class MeasurementNoiseCovarianceEstimator { 31 32 /** 33 * Number of measurement vector dimensions (measure parameters). 34 */ 35 private final int mp; 36 37 /** 38 * Estimated measurement noise covariance matrix. 39 */ 40 private final Matrix measurementNoiseCov; 41 42 /** 43 * Estimated sample average. 44 */ 45 private final double[] sampleAverage; 46 47 /** 48 * Number of samples used for estimation. 49 */ 50 private long sampleCount; 51 52 /** 53 * A sample after removing its mean. This is used internally, and it is 54 * kept as an instance variable for reuse purposes. 55 */ 56 private final double[] sampleNoMean; 57 58 /** 59 * A sample expressed in matrix form. This is used internally, and it is kept 60 * as an instance variable for reuse purposes. 61 */ 62 private final Matrix sampleMatrix; 63 64 /** 65 * The transposed sample matrix. This is used internally, and it is kept as 66 * an instance variable for reuse purposes. 67 */ 68 private final Matrix transposedSampleMatrix; 69 70 /** 71 * A covariance matrix for a single sample. This is used internally, and it 72 * is kept as an instance variable for reuse purposes. 73 */ 74 private final Matrix singleCov; 75 76 /** 77 * Constructor. 78 * 79 * @param measureParams number of measurement parameters for each sample 80 * (i.e. when sampling 3D acceleration samples, this value must be 3, since 81 * each sample contains acceleration values for x, y, and z axes). 82 * @throws IllegalArgumentException if provided number of measure parameters 83 * is less than 1. 84 * @throws SignalProcessingException if something fails. 85 */ 86 public MeasurementNoiseCovarianceEstimator(final int measureParams) throws SignalProcessingException { 87 88 if (measureParams < 1) { 89 throw new IllegalArgumentException("Measure parameters must be greater than zero"); 90 } 91 92 try { 93 mp = measureParams; 94 measurementNoiseCov = new Matrix(mp, mp); 95 sampleAverage = new double[mp]; 96 97 sampleNoMean = new double[mp]; 98 sampleMatrix = new Matrix(mp, 1); 99 transposedSampleMatrix = new Matrix(1, mp); 100 101 singleCov = new Matrix(mp, mp); 102 } catch (final AlgebraException ex) { 103 throw new SignalProcessingException(ex); 104 } 105 } 106 107 /** 108 * Updates currently estimated covariance matrix by adding provided sample 109 * data. 110 * 111 * @param sample sample to be added to update covariance matrix. 112 * @return covariance matrix after update. 113 * @throws IllegalArgumentException if provided sample length is not equal 114 * to the number of measure parameters set for this instance. 115 * @throws SignalProcessingException if something fails. 116 */ 117 public Matrix update(final double[] sample) throws SignalProcessingException { 118 119 if (sample.length != mp) { 120 throw new IllegalArgumentException("wrong sample size"); 121 } 122 123 final var nextCount = sampleCount + 1; 124 125 // update sample average 126 for (int i = 0; i < mp; i++) { 127 sampleAverage[i] = (sampleAverage[i] * sampleCount + sample[i]) / nextCount; 128 } 129 130 // compute sample without mean 131 ArrayUtils.subtract(sample, sampleAverage, sampleNoMean); 132 133 // copy sample without mean into matrix form 134 sampleMatrix.setSubmatrix(0, 0, mp - 1, 0, sampleNoMean); 135 // compute transpose of matrix form 136 sampleMatrix.transpose(transposedSampleMatrix); 137 138 try { 139 // compute covariance matrix for a single sample 140 sampleMatrix.multiply(transposedSampleMatrix, singleCov); 141 142 // update covariance matrix 143 measurementNoiseCov.multiplyByScalar(sampleCount); 144 measurementNoiseCov.add(singleCov); 145 measurementNoiseCov.multiplyByScalar(1.0 / nextCount); 146 } catch (final AlgebraException ex) { 147 throw new SignalProcessingException(ex); 148 } 149 150 sampleCount = nextCount; 151 152 return measurementNoiseCov; 153 } 154 155 /** 156 * Obtains the number of measurement vector dimensions (measure parameters). 157 * 158 * @return number of measurement vector dimensions (measure parameters). 159 */ 160 public int getMeasureParams() { 161 return mp; 162 } 163 164 /** 165 * Obtains estimated measurement noise covariance matrix. 166 * 167 * @return estimated measurement noise covariance matrix. 168 */ 169 public Matrix getMeasurementNoiseCov() { 170 return measurementNoiseCov; 171 } 172 173 /** 174 * Obtains estimated sample average. 175 * 176 * @return estimated sample average. 177 */ 178 public double[] getSampleAverage() { 179 return sampleAverage; 180 } 181 182 /** 183 * Obtains number of samples used for estimation. 184 * 185 * @return number of samples used for estimation. 186 */ 187 public long getSampleCount() { 188 return sampleCount; 189 } 190 }