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 }