minikalman/kalman/regular.rs
1//! # Regular Kalman Filter
2
3use crate::kalman::*;
4use crate::matrix::MatrixDataType;
5use crate::prelude::Matrix;
6use core::marker::PhantomData;
7
8/// A builder for a [`RegularKalman`] filter instances.
9#[allow(clippy::type_complexity)]
10pub struct RegularKalmanBuilder<A, X, P, Q, PX, TempP> {
11 _phantom: (
12 PhantomData<A>,
13 PhantomData<X>,
14 PhantomData<P>,
15 PhantomData<Q>,
16 PhantomData<PX>,
17 PhantomData<TempP>,
18 ),
19}
20
21impl<A, X, P, Q, PX, TempP> RegularKalmanBuilder<A, X, P, Q, PX, TempP> {
22 /// Initializes a Kalman filter instance.
23 ///
24 /// ## Arguments
25 /// * `A` - The state transition matrix (`STATES` × `STATES`).
26 /// * `x` - The state vector (`STATES` × `1`).
27 /// * `P` - The state covariance matrix (`STATES` × `STATES`).
28 /// * `Q` - The direct process noise matrix (`STATES` × `STATES`).
29 /// * `predictedX` - The temporary vector for predicted states (`STATES` × `1`).
30 /// * `temp_P` - The temporary vector for P calculation (`STATES` × `STATES`).
31 ///
32 /// ## Example
33 ///
34 /// ```
35 /// # #![allow(non_snake_case)]
36 /// # use minikalman::*;
37 /// use minikalman::regular::RegularKalmanBuilder;
38 /// # const NUM_STATES: usize = 3;
39 /// # const NUM_CONTROLS: usize = 0;
40 /// # const NUM_OBSERVATIONS: usize = 1;
41 /// // System buffers.
42 /// impl_buffer_x!(mut gravity_x, NUM_STATES, f32, 0.0);
43 /// impl_buffer_A!(mut gravity_A, NUM_STATES, f32, 0.0);
44 /// impl_buffer_P!(mut gravity_P, NUM_STATES, f32, 0.0);
45 /// impl_buffer_Q_direct!(mut gravity_Q, NUM_STATES, f32, 0.0);
46 ///
47 /// // Filter temporaries.
48 /// impl_buffer_temp_x!(mut gravity_temp_x, NUM_STATES, f32, 0.0);
49 /// impl_buffer_temp_P!(mut gravity_temp_P, NUM_STATES, f32, 0.0);
50 ///
51 /// let mut filter = RegularKalmanBuilder::new::<NUM_STATES, f32>(
52 /// gravity_A,
53 /// gravity_x,
54 /// gravity_P,
55 /// gravity_Q,
56 /// gravity_temp_x,
57 /// gravity_temp_P,
58 /// );
59 /// ```
60 #[allow(non_snake_case, clippy::too_many_arguments, clippy::new_ret_no_self)]
61 pub fn new<const STATES: usize, T>(
62 A: A,
63 x: X,
64 P: P,
65 Q: Q,
66 predicted_x: PX,
67 temp_P: TempP,
68 ) -> RegularKalman<STATES, T, A, X, P, Q, PX, TempP>
69 where
70 T: MatrixDataType,
71 A: StateTransitionMatrix<STATES, T>,
72 X: StateVectorMut<STATES, T>,
73 P: EstimateCovarianceMatrix<STATES, T>,
74 Q: DirectProcessNoiseCovarianceMatrix<STATES, T>,
75 PX: PredictedStateEstimateVector<STATES, T>,
76 TempP: TemporaryStateMatrix<STATES, T>,
77 {
78 RegularKalman::<STATES, T, _, _, _, _, _, _> {
79 x,
80 A,
81 P,
82 Q,
83 predicted_x,
84 temp_P,
85 _phantom: Default::default(),
86 }
87 }
88}
89
90/// Kalman Filter structure. See [`RegularKalmanBuilder`] for construction.
91#[allow(non_snake_case, unused)]
92pub struct RegularKalman<const STATES: usize, T, A, X, P, Q, PX, TempP> {
93 /// State vector.
94 x: X,
95
96 /// System matrix. In Extended Kalman Filters, the Jacobian of the system matrix.
97 ///
98 /// See also [`P`].
99 A: A,
100
101 /// Estimation covariance matrix.
102 ///
103 /// See also [`A`].
104 P: P,
105
106 /// Direct process noise matrix.
107 ///
108 /// See also [`P`].
109 Q: Q,
110
111 /// x-sized temporary vector.
112 predicted_x: PX,
113
114 /// P-Sized temporary matrix (number of states × number of states).
115 ///
116 /// The backing field for this temporary MAY be aliased with temporary BQ.
117 temp_P: TempP,
118
119 _phantom: PhantomData<T>,
120}
121
122impl<const STATES: usize, T, A, X, P, Q, PX, TempP>
123 RegularKalman<STATES, T, A, X, P, Q, PX, TempP>
124{
125 /// Returns the number of states.
126 pub const fn states(&self) -> usize {
127 STATES
128 }
129}
130
131impl<const STATES: usize, T, A, X, P, Q, PX, TempP> RegularKalman<STATES, T, A, X, P, Q, PX, TempP>
132where
133 X: StateVector<STATES, T>,
134{
135 /// Gets a reference to the state vector x.
136 ///
137 /// The state vector represents the internal state of the system at a given time. It contains
138 /// all the necessary information to describe the system's current situation.
139 #[inline(always)]
140 pub fn state_vector(&self) -> &X {
141 &self.x
142 }
143}
144
145impl<const STATES: usize, T, A, X, P, Q, PX, TempP> RegularKalman<STATES, T, A, X, P, Q, PX, TempP>
146where
147 X: StateVectorMut<STATES, T>,
148{
149 /// Gets a reference to the state vector x.
150 ///
151 /// The state vector represents the internal state of the system at a given time. It contains
152 /// all the necessary information to describe the system's current situation.
153 #[inline(always)]
154 #[doc(alias = "kalman_get_state_vector")]
155 pub fn state_vector_mut(&mut self) -> &mut X {
156 &mut self.x
157 }
158}
159
160impl<const STATES: usize, T, A, X, P, Q, PX, TempP> RegularKalman<STATES, T, A, X, P, Q, PX, TempP>
161where
162 A: StateTransitionMatrix<STATES, T>,
163{
164 /// Gets a reference to the state transition matrix A/F, or its Jacobian
165 ///
166 /// ## (Regular) Kalman Filters
167 /// This matrix describes how the state vector evolves from one time step to the next in the
168 /// absence of control inputs. It defines the relationship between the previous state and the
169 /// current state, accounting for the inherent dynamics of the system.
170 #[inline(always)]
171 #[doc(alias = "system_matrix")]
172 #[doc(alias = "system_jacobian_matrix")]
173 pub fn state_transition(&self) -> &A {
174 &self.A
175 }
176}
177
178impl<const STATES: usize, T, A, X, P, Q, PX, TempP> RegularKalman<STATES, T, A, X, P, Q, PX, TempP>
179where
180 A: StateTransitionMatrixMut<STATES, T>,
181{
182 /// Gets a reference to the state transition matrix A/F, or its Jacobian.
183 ///
184 /// This matrix describes how the state vector evolves from one time step to the next in the
185 /// absence of control inputs. It defines the relationship between the previous state and the
186 /// current state, accounting for the inherent dynamics of the system.
187 #[inline(always)]
188 #[doc(alias = "system_matrix_mut")]
189 #[doc(alias = "system_jacobian_matrix_mut")]
190 #[doc(alias = "kalman_get_state_transition")]
191 pub fn state_transition_mut(&mut self) -> &mut A {
192 &mut self.A
193 }
194}
195
196impl<const STATES: usize, T, A, X, P, Q, PX, TempP> RegularKalman<STATES, T, A, X, P, Q, PX, TempP>
197where
198 P: EstimateCovarianceMatrix<STATES, T>,
199{
200 /// Gets a reference to the system covariance matrix P.
201 ///
202 /// This matrix represents the uncertainty in the state estimate. It quantifies how much the
203 /// state estimate is expected to vary, providing a measure of confidence in the estimate.
204 #[inline(always)]
205 #[doc(alias = "system_covariance")]
206 pub fn estimate_covariance(&self) -> &P {
207 &self.P
208 }
209
210 /// Gets a mutable reference to the system covariance matrix P.
211 ///
212 /// This matrix represents the uncertainty in the state estimate. It quantifies how much the
213 /// state estimate is expected to vary, providing a measure of confidence in the estimate.
214 #[inline(always)]
215 #[doc(alias = "system_covariance_mut")]
216 #[doc(alias = "kalman_get_system_covariance")]
217 pub fn estimate_covariance_mut(&mut self) -> &mut P {
218 &mut self.P
219 }
220}
221
222impl<const STATES: usize, T, A, X, P, Q, PX, TempP>
223 RegularKalman<STATES, T, A, X, P, Q, PX, TempP>
224{
225 /// Performs the time update / prediction step.
226 ///
227 /// This call assumes that the control covariance and variables are already set in the filter structure.
228 ///
229 /// ## Example
230 /// ```
231 /// # #![allow(non_snake_case)]
232 /// # use minikalman::prelude::*;
233 /// use minikalman::regular::{RegularKalmanBuilder, RegularObservationBuilder};
234 /// # const NUM_STATES: usize = 3;
235 /// # const NUM_CONTROLS: usize = 0;
236 /// # const NUM_OBSERVATIONS: usize = 1;
237 /// # // System buffers.
238 /// # impl_buffer_x!(mut gravity_x, NUM_STATES, f32, 0.0);
239 /// # impl_buffer_A!(mut gravity_A, NUM_STATES, f32, 0.0);
240 /// # impl_buffer_P!(mut gravity_P, NUM_STATES, f32, 0.0);
241 /// # impl_buffer_Q_direct!(mut gravity_Q, NUM_STATES, f32, 0.0);
242 /// #
243 /// # // Filter temporaries.
244 /// # impl_buffer_temp_x!(mut gravity_temp_x, NUM_STATES, f32, 0.0);
245 /// # impl_buffer_temp_P!(mut gravity_temp_P, NUM_STATES, f32, 0.0);
246 /// #
247 /// # let mut filter = RegularKalmanBuilder::new::<NUM_STATES, f32>(
248 /// # gravity_A,
249 /// # gravity_x,
250 /// # gravity_P,
251 /// # gravity_Q,
252 /// # gravity_temp_x,
253 /// # gravity_temp_P,
254 /// # );
255 /// #
256 /// # // Observation buffers.
257 /// # impl_buffer_z!(mut gravity_z, NUM_OBSERVATIONS, f32, 0.0);
258 /// # impl_buffer_H!(mut gravity_H, NUM_OBSERVATIONS, NUM_STATES, f32, 0.0);
259 /// # impl_buffer_R!(mut gravity_R, NUM_OBSERVATIONS, f32, 0.0);
260 /// # impl_buffer_y!(mut gravity_y, NUM_OBSERVATIONS, f32, 0.0);
261 /// # impl_buffer_S!(mut gravity_S, NUM_OBSERVATIONS, f32, 0.0);
262 /// # impl_buffer_K!(mut gravity_K, NUM_STATES, NUM_OBSERVATIONS, f32, 0.0);
263 /// #
264 /// # // Observation temporaries.
265 /// # impl_buffer_temp_S_inv!(mut gravity_temp_S_inv, NUM_OBSERVATIONS, f32, 0.0);
266 /// # impl_buffer_temp_HP!(mut gravity_temp_HP, NUM_OBSERVATIONS, NUM_STATES, f32, 0.0);
267 /// # impl_buffer_temp_PHt!(mut gravity_temp_PHt, NUM_STATES, NUM_OBSERVATIONS, f32, 0.0);
268 /// # impl_buffer_temp_KHP!(mut gravity_temp_KHP, NUM_STATES, f32, 0.0);
269 /// #
270 /// # let mut measurement = RegularObservationBuilder::new::<NUM_STATES, NUM_OBSERVATIONS, f32>(
271 /// # gravity_H,
272 /// # gravity_z,
273 /// # gravity_R,
274 /// # gravity_y,
275 /// # gravity_S,
276 /// # gravity_K,
277 /// # gravity_temp_S_inv,
278 /// # gravity_temp_HP,
279 /// # gravity_temp_PHt,
280 /// # gravity_temp_KHP,
281 /// # );
282 /// #
283 /// # const REAL_DISTANCE: &[f32] = &[0.0, 0.0, 0.0];
284 /// # const OBSERVATION_ERROR: &[f32] = &[0.0, 0.0, 0.0];
285 /// #
286 /// for t in 0..REAL_DISTANCE.len() {
287 /// // Prediction.
288 /// filter.predict();
289 ///
290 /// // Measure ...
291 /// let m = REAL_DISTANCE[t] + OBSERVATION_ERROR[t];
292 /// measurement.measurement_vector_mut().apply(|z| z[0] = m);
293 ///
294 /// // Update.
295 /// filter.correct(&mut measurement);
296 /// }
297 /// ```
298 #[doc(alias = "kalman_predict")]
299 pub fn predict(&mut self)
300 where
301 X: StateVectorMut<STATES, T>,
302 A: StateTransitionMatrix<STATES, T>,
303 PX: PredictedStateEstimateVector<STATES, T>,
304 P: EstimateCovarianceMatrix<STATES, T>,
305 Q: DirectProcessNoiseCovarianceMatrix<STATES, T>,
306 TempP: TemporaryStateMatrix<STATES, T>,
307 T: MatrixDataType,
308 {
309 //* Predict next state using system dynamics
310 //* x = A*x
311 self.predict_x();
312
313 //* Predict next covariance using system dynamics and control
314 //* P = A*P*Aᵀ + Q
315 self.predict_P();
316 }
317
318 /// Performs the time update / prediction step.
319 ///
320 /// This call assumes that the control covariance and variables are already set in the filter structure.
321 ///
322 /// ## Arguments
323 /// * `lambda` - The estimation covariance scaling factor (0 < `lambda` <= 1) to forcibly reduce prediction certainty. Smaller values mean larger uncertainty.
324 ///
325 /// ## Tuning Factor (lambda)
326 /// In general, a process noise component is factored into the filter's state estimation
327 /// covariance matrix update. Since it can be difficult to create a correct process noise
328 /// matrix, this function incorporates a scaling factor of 1/λ² into the update process,
329 /// where a value of 1.0 resembles no change.
330 /// Smaller values correspond to a higher uncertainty increase.
331 ///
332 /// ## Example
333 /// ```
334 /// # #![allow(non_snake_case)]
335 /// # use minikalman::prelude::*;
336 /// use minikalman::regular::{RegularKalmanBuilder, RegularObservationBuilder};
337 /// # const NUM_STATES: usize = 3;
338 /// # const NUM_CONTROLS: usize = 0;
339 /// # const NUM_OBSERVATIONS: usize = 1;
340 /// # // System buffers.
341 /// # impl_buffer_x!(mut gravity_x, NUM_STATES, f32, 0.0);
342 /// # impl_buffer_A!(mut gravity_A, NUM_STATES, f32, 0.0);
343 /// # impl_buffer_P!(mut gravity_P, NUM_STATES, f32, 0.0);
344 /// # impl_buffer_Q_direct!(mut gravity_Q, NUM_STATES, f32, 0.0);
345 /// #
346 /// # // Filter temporaries.
347 /// # impl_buffer_temp_x!(mut gravity_temp_x, NUM_STATES, f32, 0.0);
348 /// # impl_buffer_temp_P!(mut gravity_temp_P, NUM_STATES, f32, 0.0);
349 /// #
350 /// # let mut filter = RegularKalmanBuilder::new::<NUM_STATES, f32>(
351 /// # gravity_A,
352 /// # gravity_x,
353 /// # gravity_P,
354 /// # gravity_Q,
355 /// # gravity_temp_x,
356 /// # gravity_temp_P,
357 /// # );
358 /// #
359 /// # // Observation buffers.
360 /// # impl_buffer_z!(mut gravity_z, NUM_OBSERVATIONS, f32, 0.0);
361 /// # impl_buffer_H!(mut gravity_H, NUM_OBSERVATIONS, NUM_STATES, f32, 0.0);
362 /// # impl_buffer_R!(mut gravity_R, NUM_OBSERVATIONS, f32, 0.0);
363 /// # impl_buffer_y!(mut gravity_y, NUM_OBSERVATIONS, f32, 0.0);
364 /// # impl_buffer_S!(mut gravity_S, NUM_OBSERVATIONS, f32, 0.0);
365 /// # impl_buffer_K!(mut gravity_K, NUM_STATES, NUM_OBSERVATIONS, f32, 0.0);
366 /// #
367 /// # // Observation temporaries.
368 /// # impl_buffer_temp_S_inv!(mut gravity_temp_S_inv, NUM_OBSERVATIONS, f32, 0.0);
369 /// # impl_buffer_temp_HP!(mut gravity_temp_HP, NUM_OBSERVATIONS, NUM_STATES, f32, 0.0);
370 /// # impl_buffer_temp_PHt!(mut gravity_temp_PHt, NUM_STATES, NUM_OBSERVATIONS, f32, 0.0);
371 /// # impl_buffer_temp_KHP!(mut gravity_temp_KHP, NUM_STATES, f32, 0.0);
372 /// #
373 /// # let mut measurement = RegularObservationBuilder::new::<NUM_STATES, NUM_OBSERVATIONS, f32>(
374 /// # gravity_H,
375 /// # gravity_z,
376 /// # gravity_R,
377 /// # gravity_y,
378 /// # gravity_S,
379 /// # gravity_K,
380 /// # gravity_temp_S_inv,
381 /// # gravity_temp_HP,
382 /// # gravity_temp_PHt,
383 /// # gravity_temp_KHP,
384 /// # );
385 /// #
386 /// # const REAL_DISTANCE: &[f32] = &[0.0, 0.0, 0.0];
387 /// # const OBSERVATION_ERROR: &[f32] = &[0.0, 0.0, 0.0];
388 /// #
389 /// const LAMBDA: f32 = 0.97;
390 ///
391 /// for t in 0..REAL_DISTANCE.len() {
392 /// // Prediction.
393 /// filter.predict_tuned(LAMBDA);
394 ///
395 /// // Measure ...
396 /// let m = REAL_DISTANCE[t] + OBSERVATION_ERROR[t];
397 /// measurement.measurement_vector_mut().apply(|z| z[0] = m);
398 ///
399 /// // Update.
400 /// filter.correct(&mut measurement);
401 /// }
402 /// ```
403 #[doc(alias = "kalman_predict_tuned")]
404 pub fn predict_tuned(&mut self, lambda: T)
405 where
406 X: StateVectorMut<STATES, T>,
407 A: StateTransitionMatrix<STATES, T>,
408 PX: PredictedStateEstimateVector<STATES, T>,
409 P: EstimateCovarianceMatrix<STATES, T>,
410 Q: DirectProcessNoiseCovarianceMatrix<STATES, T>,
411 TempP: TemporaryStateMatrix<STATES, T>,
412 T: MatrixDataType,
413 {
414 // Predict next state using system dynamics
415 // x = A*x
416 self.predict_x();
417
418 // Predict next covariance using system dynamics and control
419 // P = A*P*Aᵀ * 1/lambda^2 + Q
420 self.predict_P_tuned(lambda);
421 }
422
423 /// Performs the time update / prediction step of only the state vector
424 #[allow(non_snake_case)]
425 #[doc(alias = "kalman_predict_x")]
426 fn predict_x(&mut self)
427 where
428 X: StateVectorMut<STATES, T>,
429 A: StateTransitionMatrix<STATES, T>,
430 PX: PredictedStateEstimateVector<STATES, T>,
431 T: MatrixDataType,
432 {
433 // matrices and vectors
434 let A = self.A.as_matrix();
435 let x = self.x.as_matrix_mut();
436
437 // temporaries
438 let x_predicted = self.predicted_x.as_matrix_mut();
439
440 // Predict next state using system dynamics
441 // x = A*x
442
443 A.mult_rowvector(x, x_predicted);
444 x_predicted.copy(x);
445 }
446
447 #[allow(non_snake_case)]
448 #[doc(alias = "kalman_predict_P")]
449 fn predict_P(&mut self)
450 where
451 A: StateTransitionMatrix<STATES, T>,
452 P: EstimateCovarianceMatrix<STATES, T>,
453 Q: DirectProcessNoiseCovarianceMatrix<STATES, T>,
454 TempP: TemporaryStateMatrix<STATES, T>,
455 T: MatrixDataType,
456 {
457 // matrices and vectors
458 let A = self.A.as_matrix();
459 let P = self.P.as_matrix_mut();
460 let Q = self.Q.as_matrix();
461
462 // temporaries
463 let P_temp = self.temp_P.as_matrix_mut();
464
465 // Predict next covariance using system dynamics (without control)
466
467 // P = A*P*Aᵀ + Q
468 A.mult(P, P_temp); // temp = A*P
469 P_temp.mult_transb(A, P); // P = temp*Aᵀ
470 Q.add_inplace_b(P); // P = P + Q
471 }
472
473 #[allow(non_snake_case)]
474 #[doc(alias = "kalman_predict_Q")]
475 fn predict_P_tuned(&mut self, lambda: T)
476 where
477 A: StateTransitionMatrix<STATES, T>,
478 P: EstimateCovarianceMatrix<STATES, T>,
479 Q: DirectProcessNoiseCovarianceMatrix<STATES, T>,
480 TempP: TemporaryStateMatrix<STATES, T>,
481 T: MatrixDataType,
482 {
483 // matrices and vectors
484 let A = self.A.as_matrix();
485 let P = self.P.as_matrix_mut();
486 let Q = self.Q.as_matrix();
487
488 // temporaries
489 let P_temp = self.temp_P.as_matrix_mut();
490
491 // Predict next covariance using system dynamics (without control)
492 // P = A*P*Aᵀ * 1/lambda^2
493
494 // lambda = 1/lambda^2
495 let factor = lambda.mul(lambda).recip(); // TODO: This should be precalculated, e.g. using set_lambda(...);
496
497 // P = A*P*A' * 1/(lambda^2) + Q
498 A.mult(P, P_temp); // temp = A*P
499 P_temp.multscale_transb(A, factor, P); // P = temp*A' * 1/(lambda^2)
500 Q.add_inplace_b(P); // P = P + Q
501 }
502
503 /// Applies a control input.
504 #[inline(always)]
505 pub fn control<I>(&mut self, control: &mut I)
506 where
507 P: EstimateCovarianceMatrix<STATES, T>,
508 X: StateVectorMut<STATES, T>,
509 T: MatrixDataType,
510 I: KalmanFilterControlApplyToFilter<STATES, T>,
511 {
512 control.apply_to(&mut self.x, &mut self.P)
513 }
514
515 /// Performs the measurement update step.
516 ///
517 /// ## Arguments
518 /// * `measurement` - The measurement.
519 ///
520 /// ## Example
521 /// ```
522 /// # #![allow(non_snake_case)]
523 /// # use minikalman::prelude::*;
524 /// use minikalman::regular::{RegularKalmanBuilder, RegularObservationBuilder};
525 /// # const NUM_STATES: usize = 3;
526 /// # const NUM_CONTROLS: usize = 0;
527 /// # const NUM_OBSERVATIONS: usize = 1;
528 /// # // System buffers.
529 /// # impl_buffer_x!(mut gravity_x, NUM_STATES, f32, 0.0);
530 /// # impl_buffer_A!(mut gravity_A, NUM_STATES, f32, 0.0);
531 /// # impl_buffer_P!(mut gravity_P, NUM_STATES, f32, 0.0);
532 /// # impl_buffer_Q_direct!(mut gravity_Q, NUM_STATES, f32, 0.0);
533 /// #
534 /// # // Filter temporaries.
535 /// # impl_buffer_temp_x!(mut gravity_temp_x, NUM_STATES, f32, 0.0);
536 /// # impl_buffer_temp_P!(mut gravity_temp_P, NUM_STATES, f32, 0.0);
537 /// #
538 /// # let mut filter = RegularKalmanBuilder::new::<NUM_STATES, f32>(
539 /// # gravity_A,
540 /// # gravity_x,
541 /// # gravity_P,
542 /// # gravity_Q,
543 /// # gravity_temp_x,
544 /// # gravity_temp_P,
545 /// # );
546 /// #
547 /// # // Observation buffers.
548 /// # impl_buffer_z!(mut gravity_z, NUM_OBSERVATIONS, f32, 0.0);
549 /// # impl_buffer_H!(mut gravity_H, NUM_OBSERVATIONS, NUM_STATES, f32, 0.0);
550 /// # impl_buffer_R!(mut gravity_R, NUM_OBSERVATIONS, f32, 0.0);
551 /// # impl_buffer_y!(mut gravity_y, NUM_OBSERVATIONS, f32, 0.0);
552 /// # impl_buffer_S!(mut gravity_S, NUM_OBSERVATIONS, f32, 0.0);
553 /// # impl_buffer_K!(mut gravity_K, NUM_STATES, NUM_OBSERVATIONS, f32, 0.0);
554 /// #
555 /// # // Observation temporaries.
556 /// # impl_buffer_temp_S_inv!(mut gravity_temp_S_inv, NUM_OBSERVATIONS, f32, 0.0);
557 /// # impl_buffer_temp_HP!(mut gravity_temp_HP, NUM_OBSERVATIONS, NUM_STATES, f32, 0.0);
558 /// # impl_buffer_temp_PHt!(mut gravity_temp_PHt, NUM_STATES, NUM_OBSERVATIONS, f32, 0.0);
559 /// # impl_buffer_temp_KHP!(mut gravity_temp_KHP, NUM_STATES, f32, 0.0);
560 /// #
561 /// # let mut measurement = RegularObservationBuilder::new::<NUM_STATES, NUM_OBSERVATIONS, f32>(
562 /// # gravity_H,
563 /// # gravity_z,
564 /// # gravity_R,
565 /// # gravity_y,
566 /// # gravity_S,
567 /// # gravity_K,
568 /// # gravity_temp_S_inv,
569 /// # gravity_temp_HP,
570 /// # gravity_temp_PHt,
571 /// # gravity_temp_KHP,
572 /// # );
573 /// #
574 /// # const REAL_DISTANCE: &[f32] = &[0.0, 0.0, 0.0];
575 /// # const OBSERVATION_ERROR: &[f32] = &[0.0, 0.0, 0.0];
576 /// #
577 /// for t in 0..REAL_DISTANCE.len() {
578 /// // Prediction.
579 /// filter.predict();
580 ///
581 /// // Measure ...
582 /// let m = REAL_DISTANCE[t] + OBSERVATION_ERROR[t];
583 /// measurement.measurement_vector_mut().apply(|z| z[0] = m);
584 ///
585 /// // Update.
586 /// filter.correct(&mut measurement);
587 /// }
588 /// ```
589 pub fn correct<M>(&mut self, measurement: &mut M)
590 where
591 P: EstimateCovarianceMatrix<STATES, T>,
592 X: StateVectorMut<STATES, T>,
593 T: MatrixDataType,
594 M: KalmanFilterObservationCorrectFilter<STATES, T>,
595 {
596 measurement.correct(&mut self.x, &mut self.P);
597 }
598}
599
600impl<const STATES: usize, T, A, X, P, Q, PX, TempP> KalmanFilterNumStates<STATES>
601 for RegularKalman<STATES, T, A, X, P, Q, PX, TempP>
602{
603}
604
605impl<const STATES: usize, T, A, X, P, Q, PX, TempP> KalmanFilterStateVector<STATES, T>
606 for RegularKalman<STATES, T, A, X, P, Q, PX, TempP>
607where
608 X: StateVector<STATES, T>,
609{
610 type StateVector = X;
611
612 #[inline(always)]
613 fn state_vector(&self) -> &Self::StateVector {
614 self.state_vector()
615 }
616}
617
618impl<const STATES: usize, T, A, X, P, Q, PX, TempP> KalmanFilterStateVectorMut<STATES, T>
619 for RegularKalman<STATES, T, A, X, P, Q, PX, TempP>
620where
621 X: StateVectorMut<STATES, T>,
622{
623 type StateVectorMut = X;
624
625 #[inline(always)]
626 fn state_vector_mut(&mut self) -> &mut Self::StateVectorMut {
627 self.state_vector_mut()
628 }
629}
630
631impl<const STATES: usize, T, A, X, P, Q, PX, TempP> KalmanFilterStateTransition<STATES, T>
632 for RegularKalman<STATES, T, A, X, P, Q, PX, TempP>
633where
634 A: StateTransitionMatrix<STATES, T>,
635{
636 type StateTransitionMatrix = A;
637
638 #[inline(always)]
639 fn state_transition(&self) -> &Self::StateTransitionMatrix {
640 self.state_transition()
641 }
642}
643
644impl<const STATES: usize, T, A, X, P, Q, PX, TempP> KalmanFilterStateTransitionMut<STATES, T>
645 for RegularKalman<STATES, T, A, X, P, Q, PX, TempP>
646where
647 A: StateTransitionMatrixMut<STATES, T>,
648{
649 type StateTransitionMatrixMut = A;
650
651 #[inline(always)]
652 fn state_transition_mut(&mut self) -> &mut Self::StateTransitionMatrixMut {
653 self.state_transition_mut()
654 }
655}
656
657impl<const STATES: usize, T, A, X, P, Q, PX, TempP> KalmanFilterEstimateCovariance<STATES, T>
658 for RegularKalman<STATES, T, A, X, P, Q, PX, TempP>
659where
660 P: EstimateCovarianceMatrix<STATES, T>,
661{
662 type EstimateCovarianceMatrix = P;
663
664 #[inline(always)]
665 fn estimate_covariance(&self) -> &Self::EstimateCovarianceMatrix {
666 self.estimate_covariance()
667 }
668}
669
670impl<const STATES: usize, T, A, X, P, Q, PX, TempP> KalmanFilterEstimateCovarianceMut<STATES, T>
671 for RegularKalman<STATES, T, A, X, P, Q, PX, TempP>
672where
673 P: EstimateCovarianceMatrix<STATES, T>,
674{
675 type EstimateCovarianceMatrixMut = P;
676
677 #[inline(always)]
678 fn estimate_covariance_mut(&mut self) -> &mut Self::EstimateCovarianceMatrixMut {
679 self.estimate_covariance_mut()
680 }
681}
682
683impl<const STATES: usize, T, A, X, P, Q, PX, TempP> RegularKalman<STATES, T, A, X, P, Q, PX, TempP>
684where
685 Q: DirectProcessNoiseCovarianceMatrix<STATES, T>,
686{
687 /// Gets a reference to the direct process noise matrix Q.
688 ///
689 /// This matrix represents the process noise covariance. It quantifies the uncertainty
690 /// introduced by the model dynamics and process variations, providing a measure of
691 /// how much the true state is expected to deviate from the predicted state due to
692 /// inherent system noise and external influences.
693 #[inline(always)]
694 pub fn direct_process_noise(&self) -> &Q {
695 &self.Q
696 }
697}
698
699impl<const STATES: usize, T, A, X, P, Q, PX, TempP> RegularKalman<STATES, T, A, X, P, Q, PX, TempP>
700where
701 Q: DirectProcessNoiseCovarianceMatrixMut<STATES, T>,
702{
703 /// Gets a mutable reference to the direct process noise matrix Q.
704 ///
705 /// This matrix represents the process noise covariance. It quantifies the uncertainty
706 /// introduced by the model dynamics and process variations, providing a measure of
707 /// how much the true state is expected to deviate from the predicted state due to
708 /// inherent system noise and external influences.
709 #[inline(always)]
710 pub fn direct_process_noise_mut(&mut self) -> &mut Q {
711 &mut self.Q
712 }
713}
714
715impl<const STATES: usize, T, A, X, P, Q, PX, TempP> KalmanFilterPredict<STATES, T>
716 for RegularKalman<STATES, T, A, X, P, Q, PX, TempP>
717where
718 X: StateVectorMut<STATES, T>,
719 A: StateTransitionMatrix<STATES, T>,
720 PX: PredictedStateEstimateVector<STATES, T>,
721 P: EstimateCovarianceMatrix<STATES, T>,
722 Q: DirectProcessNoiseCovarianceMatrix<STATES, T>,
723 TempP: TemporaryStateMatrix<STATES, T>,
724 T: MatrixDataType,
725{
726 #[inline(always)]
727 fn predict(&mut self) {
728 self.predict()
729 }
730}
731
732impl<const STATES: usize, T, A, X, P, Q, PX, TempP> KalmanFilterUpdate<STATES, T>
733 for RegularKalman<STATES, T, A, X, P, Q, PX, TempP>
734where
735 P: EstimateCovarianceMatrix<STATES, T>,
736 X: StateVectorMut<STATES, T>,
737 T: MatrixDataType,
738{
739 #[inline(always)]
740 fn correct<M>(&mut self, measurement: &mut M)
741 where
742 M: KalmanFilterObservationCorrectFilter<STATES, T>,
743 {
744 self.correct(measurement)
745 }
746}
747
748impl<const STATES: usize, T, A, X, P, Q, PX, TempP> KalmanFilterApplyControl<STATES, T>
749 for RegularKalman<STATES, T, A, X, P, Q, PX, TempP>
750where
751 P: EstimateCovarianceMatrix<STATES, T>,
752 X: StateVectorMut<STATES, T>,
753 T: MatrixDataType,
754{
755 #[inline(always)]
756 fn control<I>(&mut self, control: &mut I)
757 where
758 I: KalmanFilterControlApplyToFilter<STATES, T>,
759 {
760 self.control(control)
761 }
762}
763
764#[cfg(test)]
765mod tests {
766 use super::*;
767 use crate::prelude::{AsMatrix, AsMatrixMut, MatrixMut};
768 use crate::test_dummies::make_dummy_filter;
769
770 fn trait_impl<const STATES: usize, T, K>(mut filter: K) -> K
771 where
772 K: KalmanFilter<STATES, T> + KalmanFilterStateTransitionMut<STATES, T>,
773 {
774 assert_eq!(filter.states(), STATES);
775
776 let test_fn = || 42;
777
778 let mut temp = 0;
779 let mut test_fn_mut = || {
780 temp += 0;
781 42
782 };
783
784 let _vec = filter.state_vector();
785 let _vec = filter.state_vector_mut();
786 let _ = filter.state_vector().as_matrix().inspect(|_vec| test_fn());
787 let _ = filter
788 .state_vector_mut()
789 .as_matrix()
790 .inspect(|_vec| test_fn_mut());
791 filter
792 .state_vector_mut()
793 .as_matrix_mut()
794 .apply(|_vec| test_fn());
795 filter
796 .state_vector_mut()
797 .as_matrix_mut()
798 .apply(|_vec| test_fn_mut());
799
800 let _mat = filter.state_transition();
801 let _mat = filter.state_transition_mut();
802 let _ = filter
803 .state_transition()
804 .as_matrix()
805 .inspect(|_mat| test_fn());
806 let _ = filter
807 .state_transition_mut()
808 .as_matrix()
809 .inspect(|_mat| test_fn_mut());
810 filter
811 .state_transition_mut()
812 .as_matrix_mut()
813 .apply(|_mat| test_fn());
814 filter
815 .state_transition_mut()
816 .as_matrix_mut()
817 .apply(|_mat| test_fn_mut());
818
819 let _mat = filter.estimate_covariance();
820 let _mat = filter.estimate_covariance_mut();
821 let _ = filter
822 .estimate_covariance()
823 .as_matrix()
824 .inspect(|_mat| test_fn());
825 let _ = filter
826 .estimate_covariance_mut()
827 .as_matrix()
828 .inspect(|_mat| test_fn_mut());
829 filter
830 .estimate_covariance_mut()
831 .as_matrix_mut()
832 .apply(|_mat| test_fn());
833 filter
834 .estimate_covariance_mut()
835 .as_matrix_mut()
836 .apply(|_mat| test_fn_mut());
837
838 filter.predict();
839
840 filter
841 }
842
843 #[test]
844 fn builder_simple() {
845 let filter = make_dummy_filter();
846
847 let mut filter = trait_impl(filter);
848 assert_eq!(filter.states(), 3);
849
850 let test_fn = || 42;
851
852 let mut temp = 0;
853 let mut test_fn_mut = || {
854 temp += 0;
855 42
856 };
857
858 let _vec = filter.state_vector();
859 let _vec = filter.state_vector_mut();
860 let _ = filter.state_vector().as_matrix().inspect(|_vec| test_fn());
861 let _ = filter
862 .state_vector_mut()
863 .as_matrix()
864 .inspect(|_vec| test_fn_mut());
865 filter
866 .state_vector_mut()
867 .as_matrix_mut()
868 .apply(|_vec| test_fn());
869 filter
870 .state_vector_mut()
871 .as_matrix_mut()
872 .apply(|_vec| test_fn_mut());
873
874 let _mat = filter.state_transition();
875 let _mat = filter.state_transition_mut();
876 let _ = filter
877 .state_transition()
878 .as_matrix()
879 .inspect(|_mat| test_fn());
880 let _ = filter
881 .state_transition_mut()
882 .as_matrix()
883 .inspect(|_mat| test_fn_mut());
884 filter
885 .state_transition_mut()
886 .as_matrix_mut()
887 .apply(|_mat| test_fn());
888 filter
889 .state_transition_mut()
890 .as_matrix_mut()
891 .apply(|_mat| test_fn_mut());
892
893 let _mat = filter.estimate_covariance();
894 let _mat = filter.estimate_covariance_mut();
895 let _ = filter
896 .estimate_covariance()
897 .as_matrix()
898 .inspect(|_mat| test_fn());
899 let _ = filter
900 .estimate_covariance_mut()
901 .as_matrix()
902 .inspect(|_mat| test_fn_mut());
903 filter
904 .estimate_covariance_mut()
905 .as_matrix_mut()
906 .apply(|_mat| test_fn());
907 filter
908 .estimate_covariance_mut()
909 .as_matrix_mut()
910 .apply(|_mat| test_fn_mut());
911
912 filter.predict();
913 }
914
915 #[test]
916 #[cfg(feature = "alloc")]
917 fn it_works() {
918 use crate::prelude::*;
919 use assert_float_eq::*;
920
921 let mut example = crate::test_filter::create_test_filter(1.0);
922
923 // The estimate covariance still is scalar.
924 assert!(example
925 .filter
926 .estimate_covariance()
927 .inspect(|mat| (0..3).into_iter().all(|i| { mat.get_at(i, i) == 0.1 })));
928
929 // Since our initial state is zero, any number of prediction steps keeps the filter unchanged.
930 for _ in 0..10 {
931 example.filter.predict();
932 }
933
934 // All states are zero.
935 assert!(example
936 .filter
937 .state_vector()
938 .as_ref()
939 .iter()
940 .all(|&x| x == 0.0));
941
942 // The estimate covariance has changed.
943 example.filter.estimate_covariance().inspect(|mat| {
944 assert_f32_near!(mat.get_at(0, 0), 260.1);
945 assert_f32_near!(mat.get_at(1, 1), 10.1);
946 assert_f32_near!(mat.get_at(2, 2), 0.1);
947 });
948
949 // The measurement is zero.
950 example
951 .measurement
952 .measurement_vector_mut()
953 .set_at(0, 0, 0.0);
954
955 // Apply a measurement of the unchanged state.
956 example.filter.correct(&mut example.measurement);
957
958 // All states are still zero.
959 assert!(example
960 .filter
961 .state_vector()
962 .as_ref()
963 .iter()
964 .all(|&x| x == 0.0));
965
966 // The estimate covariance has improved.
967 example.filter.estimate_covariance().inspect(|mat| {
968 assert!(mat.get_at(0, 0) < 1.0);
969 assert!(mat.get_at(1, 1) < 0.2);
970 assert!(mat.get_at(2, 2) < 0.01);
971 });
972
973 // Set an input.
974 example.control.control_vector_mut().set_at(0, 0, 1.0);
975
976 // Predict and apply an input.
977 example.filter.predict();
978 example.filter.control(&mut example.control);
979
980 // All states are still zero.
981 example.filter.state_vector().inspect(|vec| {
982 assert_eq!(
983 vec.get_at(0, 0),
984 0.5,
985 "incorrect position after control input"
986 );
987 assert_eq!(
988 vec.get_at(1, 0),
989 1.0,
990 "incorrect velocity after control input"
991 );
992 assert_eq!(
993 vec.get_at(2, 0),
994 1.0,
995 "incorrect acceleration after control input"
996 );
997 });
998
999 // Predict without input.
1000 example.filter.predict();
1001
1002 // All states are still zero.
1003 example.filter.state_vector().inspect(|vec| {
1004 assert_eq!(vec.get_at(0, 0), 2.0, "incorrect position");
1005 assert_eq!(vec.get_at(1, 0), 2.0, "incorrect velocity");
1006 assert_eq!(vec.get_at(2, 0), 1.0, "incorrect acceleration");
1007 });
1008
1009 // The estimate covariance has worsened.
1010 example.filter.estimate_covariance().inspect(|mat| {
1011 assert!(mat.get_at(0, 0) > 6.2);
1012 assert!(mat.get_at(1, 1) > 4.2);
1013 assert!(mat.get_at(2, 2) > 1.0);
1014 });
1015
1016 // Set a new measurement
1017 example.measurement.measurement_vector_mut().apply(|vec| {
1018 vec.set_at(0, 0, 2.0);
1019 vec.set_at(1, 0, (2.0 + 2.0 + 1.0) / 3.0);
1020 });
1021
1022 // Apply a measurement of the state.
1023 example.filter.correct(&mut example.measurement);
1024
1025 // The estimate covariance has improved.
1026 example.filter.estimate_covariance().inspect(|mat| {
1027 assert!(mat.get_at(0, 0) < 1.0);
1028 assert!(mat.get_at(1, 1) < 1.0);
1029 assert!(mat.get_at(2, 2) < 0.4);
1030 });
1031 }
1032}