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}