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.Matrix; 20 import com.irurueta.algebra.Utils; 21 import com.irurueta.algebra.WrongSizeException; 22 23 import java.io.Serializable; 24 25 /** 26 * Implementation of a Kalman filter. 27 * This class contains the state of a Kalman kilter. 28 * The state of a Kalman filter is updated by 29 * <code>predict</code> and <code>correct</code> functions. 30 * <p> 31 * The source code, notation and formulae below are borrowed from the JKalman 32 * tutorial <a href="http://www.cs.unc.edu/~welch/kalman/">[Welch95]</a>: 33 * <pre> 34 * {@code 35 * x<sub>k</sub>=A*x<sub>k-1</sub>+B*u<sub>k</sub>+w<sub>k</sub> 36 * z<sub>k</sub>=Hx<sub>k</sub>+v<sub>k</sub>, 37 * } 38 * </pre> 39 * <p>where: 40 * <pre> 41 * {@code x<sub>k</sub> (x<sub>k-1</sub>)} - state of the system at the moment k (k-1) 42 * {@code z<sub>k</sub>} - measurement of the system state at the moment k 43 * {@code u<sub>k</sub>} - external control applied at the moment k 44 * {@code w<sub>k</sub>} and {@code v<sub>k</sub>} are normally-distributed process and 45 * measurement noise, respectively: 46 * p(w) ~ N(0,Q) 47 * p(v) ~ N(0,R), 48 * that is, 49 * Q - process noise covariance matrix, constant or variable, 50 * R - measurement noise covariance matrix, constant or variable 51 * </pre> 52 * <p> 53 * In case of standard Kalman filter, all the matrices: A, B, H, Q and R 54 * are initialized once after Kalman structure is allocated via constructor. 55 * However, the same structure and the same functions may be used to simulate 56 * extended Kalman filter by linearizing extended Kalman filter equation in the 57 * current system state neighborhood, in this case A, B, H (and, probably, 58 * Q and R) should be updated on every step. 59 */ 60 public class KalmanFilter implements Serializable { 61 62 /** 63 * Independent process noise variance assumed when no process noise 64 * covariance matrix is provided. 65 * The lower the process variance the smoother the estimated state will 66 * typically be. 67 */ 68 public static final double DEFAULT_PROCESS_NOISE_VARIANCE = 1e-6; 69 70 /** 71 * Independent measurement noise variance assumed when no measurement noise 72 * covariance matrix is provided. 73 */ 74 public static final double DEFAULT_MEASUREMENT_NOISE_VARIANCE = 1e-1; 75 76 /** 77 * Number of measurement vector dimensions (measure parameters). 78 */ 79 private int mp; 80 81 /** 82 * Number of state vector dimensions (dynamic parameters). 83 */ 84 private final int dp; 85 86 /** 87 * Number of control vector dimensions (control parameters). 88 */ 89 private final int cp; 90 91 /** 92 * Predicted state (x'(k)): x(k)=A*x(k-1)+B*u(k) 93 */ 94 private Matrix statePre; 95 96 /** 97 * Corrected state (x(k)): x(k)=x'(k)+K(k)*(z(k)-H*x'(k)) 98 */ 99 private Matrix statePost; 100 101 /** 102 * State transition matrix (A). 103 */ 104 private Matrix transitionMatrix; 105 106 /** 107 * Control matrix (B) (it is not used if there is no control). 108 */ 109 private Matrix controlMatrix; 110 111 /** 112 * Measurement matrix (H). 113 */ 114 private Matrix measurementMatrix; 115 116 /** 117 * Process noise covariance matrix (Q). 118 */ 119 private Matrix processNoiseCov; 120 121 /** 122 * Measurement noise covariance matrix (R). 123 */ 124 private Matrix measurementNoiseCov; 125 126 /** 127 * Priori error estimate covariance matrix (P'(k)): P'(k)=A*P(k-1)*At + Q) 128 */ 129 private Matrix errorCovPre; 130 131 /** 132 * Kalman gain matrix (K(k)): K(k)=P'(k)*Ht*inv(H*P'(k)*Ht+R) 133 */ 134 private Matrix gain; 135 136 /** 137 * Posteriori error estimate covariance matrix (P(k)): P(k)=(I-K(k)*H)*P'(k) 138 */ 139 private Matrix errorCovPost; 140 141 // temporary matrices to be reused to avoid unnecessary re-allocations 142 143 /** 144 * Temporary matrix 1. 145 */ 146 private final Matrix temp1; 147 148 /** 149 * Temporary matrix 2. 150 */ 151 private Matrix temp2; 152 153 /** 154 * Temporary matrix 3. 155 */ 156 private Matrix temp3; 157 158 /** 159 * Temporary matrix 4. 160 */ 161 private Matrix temp4; 162 163 /** 164 * Temporary matrix 5. 165 */ 166 private Matrix temp5; 167 168 /** 169 * Temporary matrix 6. 170 */ 171 private Matrix temp6; 172 173 /** 174 * Temporary matrix 7. 175 */ 176 private final Matrix temp7; 177 178 /** 179 * Temporary matrix 8. 180 */ 181 private Matrix temp8; 182 183 /** 184 * Allocates a Kalman filter and all its matrices and initializes them. 185 * 186 * @param dynamParams number of dynamic parameters (state vector dimensions). 187 * @param measureParams number of measurement parameters (measurement vector 188 * dimensions). 189 * @param controlParams number of control parameters (control vector. 190 * dimensions). If zero, no control parameters are used. If less than zero, 191 * it is assumed that this is equal to the number of dynamic parameters. 192 * @throws IllegalArgumentException if either the number of dynamic or 193 * measurement parameters is zero or negative. 194 * @throws SignalProcessingException if something else fails. 195 */ 196 public KalmanFilter(final int dynamParams, final int measureParams, int controlParams) 197 throws SignalProcessingException { 198 199 if (dynamParams <= 0 || measureParams <= 0) { 200 throw new IllegalArgumentException("Kalman filter: Illegal dimensions"); 201 } 202 203 if (controlParams < 0) { 204 controlParams = dynamParams; 205 } 206 207 // init 208 dp = dynamParams; 209 mp = measureParams; 210 cp = controlParams; 211 212 try { 213 statePre = new Matrix(dp, 1); 214 215 // following variables must be initialized properly in advance 216 statePost = new Matrix(dp, 1); 217 transitionMatrix = Matrix.identity(dp, dp); 218 219 processNoiseCov = Matrix.identity(dp, dp); 220 processNoiseCov.multiplyByScalar(DEFAULT_PROCESS_NOISE_VARIANCE); 221 222 measurementMatrix = Matrix.identity(mp, dp); 223 measurementNoiseCov = Matrix.identity(mp, mp); 224 measurementNoiseCov.multiplyByScalar(DEFAULT_MEASUREMENT_NOISE_VARIANCE); 225 226 errorCovPre = new Matrix(dp, dp); 227 errorCovPost = Matrix.identity(dp, dp); 228 229 gain = new Matrix(dp, mp); 230 231 if (cp > 0) { 232 controlMatrix = new Matrix(dp, cp); 233 } else { 234 // no control parameters 235 controlMatrix = null; 236 } 237 238 temp1 = new Matrix(dp, dp); 239 temp2 = new Matrix(mp, dp); 240 temp3 = new Matrix(mp, mp); 241 temp4 = new Matrix(mp, dp); 242 temp5 = new Matrix(mp, 1); 243 244 if (cp > 0) { 245 temp6 = new Matrix(dp, 1); 246 } 247 248 temp7 = new Matrix(dp, dp); 249 temp8 = new Matrix(dp, mp); 250 } catch (final AlgebraException ex) { 251 throw new SignalProcessingException(ex); 252 } 253 } 254 255 /** 256 * Constructor in case of no control parameters. 257 * 258 * @param dynamParams number of dynamic parameters (state vector dimensions). 259 * @param measureParams number of measurement parameters (measurement vector 260 * dimensions). 261 * @throws IllegalArgumentException if either the number of dynamic or 262 * measurement parameters is zero or negative. 263 * @throws SignalProcessingException if something else fails. 264 */ 265 public KalmanFilter(final int dynamParams, final int measureParams) throws SignalProcessingException { 266 this(dynamParams, measureParams, 0); 267 } 268 269 /** 270 * Estimates subsequent model state without control parameters. 271 * 272 * @return estimated state. 273 * @throws SignalProcessingException if something fails. 274 * @see #predict(Matrix) 275 */ 276 public Matrix predict() throws SignalProcessingException { 277 return predict(null); 278 } 279 280 /** 281 * Estimates subsequent model state. 282 * The function estimates the subsequent stochastic model state by its 283 * current state and stores it at <code>statePre</code>: 284 * <pre> 285 * {@code 286 * x'<sub>k</sub>=A*x<sub>k</sub>+B*u<sub>k</sub> 287 * P'<sub>k</sub>=A*P<sub>k-1</sub>*A<sup>T</sup> + Q, 288 * where 289 * x'<sub>k</sub> is predicted state (statePre), 290 * x<sub>k-1</sub> is corrected state on the previous step (statePost) 291 * (should be initialized somehow in the beginning, zero vector by 292 * default), 293 * u<sub>k</sub> is external control (<code>control</code> parameter), 294 * P'<sub>k</sub> is prior error covariance matrix (error_cov_pre) 295 * P<sub>k-1</sub> is posteriori error covariance matrix on the previous 296 * step (error_cov_post) 297 * (should be initialized somehow in the beginning, identity matrix by 298 * default), 299 * } 300 * </pre> 301 * 302 * @param control control vector (u<sub>k</sub>), should be null if there is 303 * no external control (<code>controlParams</code>=0). If provided and 304 * filter uses control parameters, it must be a 1 column matrix having 305 * cp rows (where cp = number of control parameters), otherwise a 306 * SignalProcessingException will be raised. 307 * @return estimated state as a 1 column matrix having dp rows (where dp = 308 * number of dynamic parameters). 309 * @throws SignalProcessingException if something fails. 310 */ 311 public Matrix predict(final Matrix control) throws SignalProcessingException { 312 try { 313 // (1) Project the state ahead 314 // update the state: x'(k) = A*x(k) 315 transitionMatrix.multiply(statePost, statePre); 316 if (control != null && cp > 0) { 317 // x'(k) = x'(k) + B*u(k) 318 controlMatrix.multiply(control, temp6); 319 statePre.add(temp6); 320 } 321 322 // (2) Project the error covariance ahead 323 // update error covariance matrices: temp1 = A * P(k) 324 transitionMatrix.multiply(errorCovPost, temp1); 325 // P'(k) = temp1 * At + Q 326 transitionMatrix.transpose(temp7); 327 temp1.multiply(temp7); 328 temp1.add(processNoiseCov); 329 errorCovPre = temp1; 330 331 return statePre; 332 } catch (final AlgebraException e) { 333 throw new SignalProcessingException(e); 334 } 335 } 336 337 /** 338 * Adjusts model state. 339 * This method adjusts stochastic model state on the basis of the given 340 * measurement of the model state: 341 * <pre> 342 * {@code 343 * K<sub>k</sub>=P'<sub>k</sub>*H<sup>T</sup>*(H*P'<sub>k</sub>*H<sup>T</sup>+R)<sup>-1</sup> 344 * x<sub>k</sub>=x'<sub>k</sub>+K<sub>k</sub>*(z<sub>k</sub>-H*x'<sub>k</sub>) 345 * P<sub>k</sub>=(I-K<sub>k</sub>*H)*P'<sub>k</sub> 346 * where 347 * z<sub>k</sub> - given measurement (<code>measurement</code> parameter) 348 * K<sub>k</sub> - Kalman "gain" matrix. 349 * } 350 * </pre> 351 * <p> 352 * The function stores adjusted state at <code>statePost</code> and returns 353 * it on output. 354 * 355 * @param measurement matrix containing the measurement vector. Matrix must 356 * have 1 column and mp rows (mp = measurement parameters). 357 * @return adjusted model state. 358 * @throws SignalProcessingException if something fails. 359 */ 360 public Matrix correct(final Matrix measurement) throws SignalProcessingException { 361 try { 362 // (1) compute the Kalman gain 363 // temp2 = H*P'(k) 364 measurementMatrix.multiply(errorCovPre, temp2); 365 366 // temp3 = temp2*Ht + R 367 measurementMatrix.transpose(temp8); 368 temp2.multiply(temp8, temp3); 369 temp3.add(measurementNoiseCov); 370 371 // temp4 = inv(temp3)*temp2 = Kt(k) 372 // which is also equivalent to: 373 // temp4 = temp3.svd().getU().times(temp2) 374 Utils.solve(temp3, temp2, temp4); 375 376 // K(k) 377 temp4.transpose(); 378 gain = temp4; 379 380 // (2) Update estimate with measurement z(k) 381 //temp5 = z(k) - H*x'(k) 382 measurementMatrix.multiply(statePre, temp5); 383 temp5.multiplyByScalar(-1.0); 384 temp5.add(measurement); 385 386 // x(k) = x'(k) + K(k)*temp5 387 gain.multiply(temp5, statePost); 388 statePost.add(statePre); 389 390 // (3) Update the error covariance 391 // P(x) = P'(k) - K(k)*temp2 392 gain.multiply(temp2, errorCovPost); 393 errorCovPost.multiplyByScalar(-1.0); 394 errorCovPost.add(errorCovPre); 395 396 return statePost; 397 } catch (final AlgebraException e) { 398 throw new SignalProcessingException(e); 399 } 400 } 401 402 /** 403 * Obtains the number of measurement vector dimensions (measure parameters). 404 * 405 * @return number of measurement vector dimensions (measure parameters) 406 */ 407 public int getMeasureParameters() { 408 return mp; 409 } 410 411 /** 412 * Sets the number of measurement vector dimensions (measure parameters). 413 * 414 * @param measureParameters number of measurement vector dimensions (measure 415 * parameters). 416 * NOTE: when resetting number of measure parameters, the measurement noise 417 * covariance matrix and the measurement matrix get reset to their default 418 * values having the required new size. Please, make sure those matrices 419 * are reset to their proper values after calling this method. 420 * @throws IllegalArgumentException if provided value is zero or negative. 421 * @throws SignalProcessingException if something else fails 422 */ 423 public void setMeasureParameters(final int measureParameters) throws SignalProcessingException { 424 if (measureParameters <= 0) { 425 throw new IllegalArgumentException(""); 426 } 427 mp = measureParameters; 428 429 try { 430 measurementMatrix = Matrix.identity(mp, dp); 431 measurementNoiseCov = Matrix.identity(mp, mp); 432 measurementNoiseCov.multiplyByScalar(DEFAULT_MEASUREMENT_NOISE_VARIANCE); 433 434 gain = new Matrix(dp, mp); 435 436 temp2 = new Matrix(mp, dp); 437 temp3 = new Matrix(mp, mp); 438 temp4 = new Matrix(mp, dp); 439 temp5 = new Matrix(mp, 1); 440 441 temp8 = new Matrix(dp, mp); 442 } catch (final WrongSizeException e) { 443 throw new SignalProcessingException(e); 444 } 445 } 446 447 /** 448 * Obtains the number of state vector dimensions (dynamic parameters). 449 * 450 * @return number of state vector dimensions (dynamic parameters) 451 */ 452 public int getDynamicParameters() { 453 return dp; 454 } 455 456 /** 457 * Obtains the number of control vector dimensions (control parameters). 458 * 459 * @return number of control vector dimensions (control parameters) 460 */ 461 public int getControlParameters() { 462 return cp; 463 } 464 465 /** 466 * Obtains predicted state (x'(k)): x(k)=A*x(k-1)+B*u(k). 467 * It is a column matrix having 1 column and dp rows, where dp is the 468 * number of dynamic parameters 469 * 470 * @return predicted state 471 */ 472 public Matrix getStatePre() { 473 return statePre; 474 } 475 476 /** 477 * Sets predicted state (x'(k)): x(k)=A*x(k-1)+B*u(k). 478 * Provided matrix must have 1 column and dp rows, where dp is the number 479 * of dynamic parameters set for this Kalman filter instance. 480 * This setter method can be used for initial setup purposes. 481 * 482 * @param statePre new predicted state. 483 * @throws IllegalArgumentException if provided matrix does not have 1 484 * column and dp rows 485 */ 486 public void setStatePre(final Matrix statePre) { 487 if (statePre.getColumns() != 1 || statePre.getRows() != dp) { 488 throw new IllegalArgumentException(); 489 } 490 this.statePre = statePre; 491 } 492 493 /** 494 * Obtains corrected state (x(k)): x(k)=x'(k)+K(k)*(z(k)-H*x'(k)). 495 * It is a column matrix having 1 column and dp rows, where dp is the 496 * number of dynamic parameters 497 * 498 * @return corrected state 499 */ 500 public Matrix getStatePost() { 501 return statePost; 502 } 503 504 /** 505 * Sets corrected state (x(k)): x(k)=x'(k)+K(k)*(z(k)-H*x'(k)). 506 * Provided matrix must have 1 column and dp rows, where dp is the number 507 * of dynamic parameters set for this Kalman filter instance. 508 * This setter method can be used for initial setup purposes. 509 * 510 * @param statePost new corrected state 511 * @throws IllegalArgumentException if provided matrix does not have 1 512 * column and dp rows 513 */ 514 public void setStatePost(final Matrix statePost) { 515 if (statePost.getColumns() != 1 || statePost.getRows() != dp) { 516 throw new IllegalArgumentException(); 517 } 518 this.statePost = statePost; 519 } 520 521 /** 522 * Obtains the state transition matrix (A). 523 * It is a square matrix having dp rows and columns, where dp is equal to 524 * the number of dynamic parameters. 525 * This matrix defines how the system transitions to a new state for a given 526 * previous state. It is used for prediction purposes 527 * 528 * @return state transition matrix 529 */ 530 public Matrix getTransitionMatrix() { 531 return transitionMatrix; 532 } 533 534 /** 535 * Sets the state transition matrix (A). 536 * It must be a square matrix having dp rows and columns, where dp is equal 537 * to the number of dynamic parameters set for this instance. 538 * This matrix defines how the system transitions to a new state for a given 539 * previous state. It is used for prediction purposes. 540 * This setter method can be used for initial setup purposes. 541 * 542 * @param transitionMatrix new state transition matrix 543 * @throws IllegalArgumentException if provided matrix does not have dp rows 544 * and columns 545 */ 546 public void setTransitionMatrix(final Matrix transitionMatrix) { 547 if (transitionMatrix.getRows() != dp || transitionMatrix.getColumns() != dp) { 548 throw new IllegalArgumentException(); 549 } 550 this.transitionMatrix = transitionMatrix; 551 } 552 553 /** 554 * Obtains the control matrix (B) (it is not used if there is no control). 555 * It's a matrix having dp rows and cp columns, where dp is the number of 556 * dynamic parameters and cp is the number of control parameters. 557 * 558 * @return control matrix 559 */ 560 public Matrix getControlMatrix() { 561 return controlMatrix; 562 } 563 564 /** 565 * Sets the control matrix (B) (it is not used if there is no control). 566 * Provided matrix must have dp rows and cp columns, where dp is the number 567 * of dynamic parameters and cp is the number of control parameters set for 568 * this Kalman filter instance. 569 * This setter method can be used for initial setup purposes. 570 * 571 * @param controlMatrix new control matrix to be set, or null if no control 572 * parameters are set 573 * @throws IllegalArgumentException if provided matrix does not have dp 574 * rows and cp columns 575 */ 576 public void setControlMatrix(final Matrix controlMatrix) { 577 if (cp > 0) { 578 if (controlMatrix == null || (controlMatrix.getRows() != dp || controlMatrix.getColumns() != cp)) { 579 throw new IllegalArgumentException(); 580 } 581 } else { 582 // control matrix cannot be set 583 throw new IllegalArgumentException(); 584 } 585 this.controlMatrix = controlMatrix; 586 } 587 588 /** 589 * Obtains measurement matrix (H). 590 * It's a matrix having mp rows and dp columns, where mp is the number 591 * of measurement parameters and dp is the number of dynamic parameters of 592 * the system state. 593 * This matrix relates obtained measures to the actual system state when a 594 * given model is known in advance. If no model is known and measures 595 * directly indicate the system state, then this matrix must be the 596 * identity. 597 * 598 * @return measurement matrix 599 */ 600 public Matrix getMeasurementMatrix() { 601 return measurementMatrix; 602 } 603 604 /** 605 * Sets measurement matrix (H). 606 * Provided matrix must have mp rows and dp columns, where mp is the number 607 * of measurement parameters and dp is the number of dynamic parameters of 608 * the system state. 609 * This matrix relates obtained measures to the actual system state when a 610 * given model is known in advance. If no model is known and measures 611 * directly indicate the system state, then this matrix must be the 612 * identity. 613 * This setter method can be used for initial setup purposes. 614 * 615 * @param measurementMatrix measurement matrix 616 * @throws IllegalArgumentException if provided matrix does not have mp rows 617 * and dp columns. 618 */ 619 public void setMeasurementMatrix(final Matrix measurementMatrix) { 620 if (measurementMatrix.getRows() != mp || measurementMatrix.getColumns() != dp) { 621 throw new IllegalArgumentException(); 622 } 623 this.measurementMatrix = measurementMatrix; 624 } 625 626 /** 627 * Obtains the process noise covariance matrix (Q). 628 * This is a covariance matrix indicating the correlations of the amount of 629 * error in the system state. 630 * It is a square symmetric matrix having dp rows and columns, where dp is 631 * the number of dynamic parameters containing the system state. 632 * 633 * @return the process noise covariance matrix 634 */ 635 public Matrix getProcessNoiseCov() { 636 return processNoiseCov; 637 } 638 639 /** 640 * Sets the process noise covariance matrix (Q). 641 * This is a covariance matrix indicating the correlations of the amount of 642 * error in the system state. 643 * It must be provided a square symmetric matrix having dp rows and columns, 644 * where dp is the number of dynamic parameters containing the system state 645 * for this instance of a Kalman filter. 646 * This setter method can be used for initial setup purposes, however 647 * typically the process noise is difficult to determine. This matrix is 648 * generally constructed intuitively so that un-modelled dynamics and 649 * parameter uncertainties are modeled as process noise generally. If 650 * the process noise is unknown, just leave the default value or provide 651 * a diagonal matrix with the desired level of variance Q, where a low Q 652 * variance indicates confidence that any unknown noise terms and/or 653 * modelling errors are small to negligible, and higher Q allows the tracker 654 * to follow the state despite unknown noise and/or model errors. 655 * 656 * @param processNoiseCov process noise covariance matrix 657 * @throws IllegalArgumentException if provided matrix does not have dp 658 * rows and columns, or it is not symmetric 659 */ 660 public void setProcessNoiseCov(final Matrix processNoiseCov) { 661 if (processNoiseCov.getRows() != dp || processNoiseCov.getColumns() != dp 662 || !Utils.isSymmetric(processNoiseCov)) { 663 throw new IllegalArgumentException(); 664 } 665 666 this.processNoiseCov = processNoiseCov; 667 } 668 669 /** 670 * Obtains the measurement noise covariance matrix (R). 671 * This is a covariance matrix indicating the correlations of the amount of 672 * error in the measures taken from the system. 673 * It is a square symmetric matrix having mp rows and columns, where mp is 674 * the number of measurement parameters. 675 * Typically, this matrix can be easily obtained by processing the 676 * measurements while the output of the system is held constant. In this 677 * case, only noise remains in the data after its mean is removed. 678 * The covariance can be calculated easily from the remaining portion of the 679 * data. 680 * 681 * @return the measurement noise covariance matrix 682 */ 683 public Matrix getMeasurementNoiseCov() { 684 return measurementNoiseCov; 685 } 686 687 /** 688 * Sets the measurement noise covariance matrix (R). 689 * This is a covariance matrix indicating the correlations of the amount of 690 * error in the measures taken from the system. 691 * Provided matrix must be a square symmetric matrix having mp rows and 692 * columns, where mp is the number of measurement parameters. 693 * Typically, this matrix can be easily obtained by processing the 694 * measurements while the output of the system is held constant. In this 695 * case, only noise remains in the data after its mean is removed. 696 * The covariance can be calculated easily from the remaining portion of the 697 * data. 698 * This setter method can be used for initial setup purposes. 699 * 700 * @param measurementNoiseCov new measurement noise covariance matrix 701 * @throws IllegalArgumentException if provided matrix does not have mp 702 * rows and columns, or it is not symmetric 703 */ 704 public void setMeasurementNoiseCov(final Matrix measurementNoiseCov) { 705 if (measurementNoiseCov.getRows() != mp || measurementNoiseCov.getColumns() != mp 706 || !Utils.isSymmetric(measurementNoiseCov)) { 707 throw new IllegalArgumentException(); 708 } 709 710 this.measurementNoiseCov = measurementNoiseCov; 711 } 712 713 /** 714 * Obtains the priori error estimate covariance matrix 715 * (P'(k)): P'(k)=A*P(k-1)*At + Q). 716 * It is a square symmetric matrix having dp rows and columns, where dp 717 * is the number of dynamic parameters of the system state 718 * 719 * @return the priori error estimate covariance matrix 720 */ 721 public Matrix getErrorCovPre() { 722 return errorCovPre; 723 } 724 725 /** 726 * Sets the priori error estimate covariance matrix 727 * (P'(k)): P'(k)=A*P(k-1)*At + Q). 728 * Provided matrix must be square and symmetric having dp rows and columns, 729 * where dp is the number of the dynamic parameters of the system state set 730 * for this Kalman filter instance. 731 * This setter method can be used for initial setup purposes, however this 732 * value will rarely need to be set, and instead the getter method will be 733 * used to obtain the error of the predicted system state once the filter 734 * converges 735 * 736 * @param errorCovPre new priori error estimate covariance matrix 737 * @throws IllegalArgumentException if provided matrix does not have dp rows 738 * and columns, or it is not symmetric 739 */ 740 public void setErrorCovPre(final Matrix errorCovPre) { 741 if (errorCovPre.getRows() != dp || errorCovPre.getColumns() != dp || !Utils.isSymmetric(errorCovPre)) { 742 throw new IllegalArgumentException(); 743 } 744 this.errorCovPre = errorCovPre; 745 } 746 747 /** 748 * Obtains the Kalman gain matrix (K(k)): K(k)=P'(k)*Ht*inv(H*P'(k)*Ht+R). 749 * This matrix is used to correct the predicted state, if the gain values 750 * are small then the filter is accurately tracking the system state and the 751 * prediction error remains small too. 752 * The gain matrix has dp rows and mp columns, where dp is the number of 753 * dynamic parameters and mp is the number of measure parameters. 754 * 755 * @return the Kalman gain matrix 756 */ 757 public Matrix getGain() { 758 return gain; 759 } 760 761 /** 762 * Sets the Kalman gain matrix (K(k)): K(k)=P'(k)*Ht*inv(H*P'(k)*Ht+R). 763 * This matrix is used to correct the predicted state, if the gain values 764 * are small then the filter is accurately tracking the system state and the 765 * prediction error remains small too. 766 * The gain matrix must have dp rows and mp columns, where dp is the number 767 * of dynamic parameters and mp is the number of measure parameters set for 768 * this Kalman filter instance. 769 * This setter method can be used for initial setup purposes, however this 770 * matrix rarely needs to be set, and instead it is better to let the filter 771 * converge to the actual system state. 772 * 773 * @param gain new gain matrix 774 * @throws IllegalArgumentException if provided matrix does not have dp rows 775 * and mp columns 776 */ 777 public void setGain(final Matrix gain) { 778 if (gain.getRows() != dp || gain.getColumns() != mp) { 779 throw new IllegalArgumentException("Wrong matrix size"); 780 } 781 this.gain = gain; 782 } 783 784 /** 785 * Obtains the posteriori error estimate covariance matrix 786 * (P(k)): P(k)=(I-K(k)*H)*P'(k). 787 * It is a square symmetric matrix having dp rows and columns, where dp 788 * is the number of dynamic parameters of the system state 789 * 790 * @return the priori error estimate covariance matrix 791 */ 792 public Matrix getErrorCovPost() { 793 return errorCovPost; 794 } 795 796 /** 797 * Sets the posteriori error estimate covariance matrix 798 * (P(k)): P(k)=(I-K(k)*H)*P'(k). 799 * Provided matrix must be square and symmetric having dp rows and columns, 800 * where dp is the number of the dynamic parameters of the system state set 801 * for this Kalman filter instance. 802 * This setter method can be used for initial setup purposes, however this 803 * value will rarely need to be set, and instead the getter method will be 804 * used to obtain the error of the posteriori system state once the filter 805 * converges 806 * 807 * @param errorCovPost new posteriori error estimate covariance matrix 808 * @throws IllegalArgumentException if provided matrix does not have dp rows 809 * and columns, or it is not symmetric 810 */ 811 public void setErrorCovPost(final Matrix errorCovPost) { 812 if (errorCovPost.getRows() != dp || errorCovPost.getColumns() != dp || !Utils.isSymmetric(errorCovPost)) { 813 throw new IllegalArgumentException(); 814 } 815 this.errorCovPost = errorCovPost; 816 } 817 }