shkolnick-kun преди 1 година
родител
ревизия
e00153ff5a
променени са 1 файла, в които са добавени 38 реда и са изтрити 23 реда
  1. 38 23
      doc/C-Manual.md

+ 38 - 23
doc/C-Manual.md

@@ -238,8 +238,8 @@ kfMemorySt kf_memory =
 };
 
 /*This is EKF structure definition*/
-/*                                           fx  jfx  hx  jhx zrf nx  nz   memory */
-yaflEKFBaseSt kf = YAFL_EKF_BASE_INITIALIZER(fx, jfx, hx, jhx, 0, NX, NZ, kf_memory);
+/*                                           fx  jfx  hx  jhx zrf nx  nz  rff  qff  memory */
+yaflEKFBaseSt kf = YAFL_EKF_BASE_INITIALIZER(fx, jfx, hx, jhx, 0, NX, NZ, 0.0, 0.0, kf_memory);
 
 /*
 Arguments of initializer macro:
@@ -250,6 +250,8 @@ jhx    - measurement Jacobian function pointer
 zrf    - measurement Residual function pointer (needed to calculate the distance between forecast and measurement vectors)
 nx     - the dimension of state vector
 nz     - the dimension of measurement vector
+rff    - measurement noice covatiance forgetting factor
+qff    - process noice covatiance forgetting factor
 memory - the name of a memory structure.
 */
 ```
@@ -300,7 +302,7 @@ Filter algorithms and data structures are declared in [src/yafl.h](./src/yafl.h)
 To power the filtering code we need some math functions which are declared in [src/yafl_math](./src/yafl_math.h).
 
 Functions declared in [src/yafl.h](./src/yafl.h) and [src/yafl_math](./src/yafl_math.h) return result code, defined in `yaflStatusEn`.
-If everything is OK then `YAFL_ST_OK = 0` is returned. If something went wrong the result code is actually a bit mask. 
+If everything is OK then `YAFL_ST_OK = 0` is returned. If something went wrong the result code is actually a bit mask.
 Here is the bit field list:
 * Warning bit fields:
   * `YAFL_ST_MSK_REGULARIZED`  - we had to do regularization to maintain the filter integrity
@@ -341,7 +343,7 @@ To disthiguish between warinings and errors `YAFL_ST_ERR_THR` is used, e.g.:
 We prefer using static memory allocation so filter structure initializers look like this:
 ```C
 /*yaflKalmanBaseSt is for internal usage only, so our code axemple is about EKF*/
-yaflEKFBaseSt kf = YAFL_EKF_BASE_INITIALIZER(fx, jfx, hx, jhx, zrf, nx, nz, memory);
+yaflEKFBaseSt kf = YAFL_EKF_BASE_INITIALIZER(fx, jfx, hx, jhx, zrf, nx, nz, rff, qff, memory);
 ```
 where:
 * `fx`     - state transition function pointer
@@ -351,6 +353,8 @@ where:
 * `zrf`    - measurement Residual function pointer (needed to calculate the distance between forecast and measurement vectors)
 * `nx`     - the dimension of state vector
 * `nz`     - the dimension of measurement vector
+* `rff`    - measurement noice covatiance forgetting factor
+* `qff`    - process noice covatiance forgetting factor
 * `memory` - the name of the memory structure.
 
 The filter control block does not store an data it has only pointers to data storage, so we must have som separate data storage called `memory`.
@@ -367,12 +371,19 @@ The function `zrf` must have the prototype which must be similar to:
 
 it is utilized to compute the error vectors as there are some cases when we can't use the Euclid distance.
 
-As you can see all functions have `yaflKalmanBaseSt * self` as their first parameter this is done to enable passing additional data to these functions. 
+As you can see all functions have `yaflKalmanBaseSt * self` as their first parameter this is done to enable passing additional data to these functions.
 So all these functions are **"methods"** of some **"class"** which has `yaflKalmanBaseSt` as its **"basic class"**. As you can see we are using **OOP** approach here.
 
 Now let's talk about memory. We use **mixins** to declare filter memory pools so users can put more fields at their filter memory pools.
 This also enables us to do nested **mixin** macros calls while keeping memory pool strustures flat.
 
+### Notes on Q and R adjustments
+We used [this paper](https://arxiv.org/pdf/1702.00884.pdf) to implement optional Q and R adaptive adjustments.
+Here are som notes on our implementation:
+* All filters in this lib have the optional measurement noice covariance adjustment which can be enabled by setting `rff` to a small positive number e.g. `1e-4`.
+* All EKF filters in this lib have the optional process noice covariance adjustment which can be enabled by setting `qff` to a small positive number e.g. `1e-4`.
+* None of UKF filters have the optional process noice covariance adjustment as it leads to filters instability.
+
 ### EKF stuff
 The basic type for all **EKF** control blocks is `yaflEKFBaseSt`. It is initialized with `YAFL_EKF_BASE_INITIALIZER` macro.
 In the example above we gave the initial explanation of its work. Now let's go deeper.
@@ -584,7 +595,7 @@ someMemorySt memory =
 #### Basic EKF variants
 Basic **EKF** control block can be initialized with:
 ```C
-yaflEKFBaseSt kf = YAFL_EKF_BASE_INITIALIZER(fx, jfx, hx, jhx, zrf, nx, nz, memory);
+yaflEKFBaseSt kf = YAFL_EKF_BASE_INITIALIZER(fx, jfx, hx, jhx, zrf, nx, nz, rff, qff, memory);
 ```
 
 Predict macros are:
@@ -628,7 +639,7 @@ The memory mixin used is `YAFL_EKF_BASE_MEMORY_MIXIN`.
 
 The initilizer macro is:
 ```C
-YAFL_EKF_ADAPTIVE_INITIALIZER(fx, jfx, hx, jhx, zrf, nx, nz, memory)
+YAFL_EKF_ADAPTIVE_INITIALIZER(fx, jfx, hx, jhx, zrf, nx, nz, rff, qff, chi2, memory)
 ```
 
 As you can see it takes the same parameter list as `YAFL_EKF_BASE_INITIALIZER`. the `chi2` field is set to `scipy.stats.chi2.ppf(0.999, 1)`
@@ -673,7 +684,7 @@ The noise model is Normal with Poisson outliers.
 See:
 
 A.V. Chernodarov, G.I. Djandjgava and A.P. Rogalev (1999).
-Monitoring and adaptive robust protection of the integrity of air data 
+Monitoring and adaptive robust protection of the integrity of air data
 inertial satellite navigation systems for maneuverable aircraft.
 
 In: Proceedings of the RTO SCI International Conference on Integrated Navigation Systems,
@@ -683,12 +694,13 @@ held at "Electropribor", St. Petersburg, pp. 2111-10, RTO-MP-43 , NeuiIly-sur-Se
 yaflFloat psi(yaflKalmanBaseSt * self, yaflFloat normalized_error)
 {
     (void)self;
-    if (3.0 >= normalized_error)
+    yaflFloat abs_ne = fabs(normalized_error);
+    if (3.0 >= abs_ne)
     {
         return normalized_error;
     }
 
-    if (6.0 >= normalized_error)
+    if (6.0 >= abs_ne)
     {
         /*Small glitch*/
         return normalized_error / 3.0;
@@ -701,12 +713,13 @@ yaflFloat psi(yaflKalmanBaseSt * self, yaflFloat normalized_error)
 yaflFloat psi_dot(yaflKalmanBaseSt * self, yaflFloat normalized_error)
 {
     (void)self;
-    if (3.0 >= normalized_error)
+    yaflFloat abs_ne = fabs(normalized_error);
+    if (3.0 >= abs_ne)
     {
         return 1.0;
     }
 
-    if (6.0 >= normalized_error)
+    if (6.0 >= abs_ne)
     {
         /*Small glitch*/
         return 1.0 / 3.0;
@@ -734,7 +747,7 @@ The memory mixin used is `YAFL_EKF_BASE_MEMORY_MIXIN`.
 
 The initilizer macro is:
 ```C
-YAFL_EKF_ROBUST_INITIALIZER(fx, jfx, hx, jhx, zrf, g, gdot,  nx, nz, memory)
+YAFL_EKF_ROBUST_INITIALIZER(fx, jfx, hx, jhx, zrf, g, gdot, nx, nz, rff, qff, memory)
 ```
 
 Note that we have two additional arguments:
@@ -743,7 +756,7 @@ Note that we have two additional arguments:
 
 The example for `YAFL_EKF_ROBUST_INITIALIZER` call is:
 ```C
-yaflEKFRobustSt kf = YAFL_EKF_ROBUST_INITIALIZER(fx, jfx, hx, jhx, zrf, psi, psi_dot,  nx, nz, memory);
+yaflEKFRobustSt kf = YAFL_EKF_ROBUST_INITIALIZER(fx, jfx, hx, jhx, zrf, psi, psi_dot, nx, nz, rff, qff, memory);
 ```
 
 Predict macros are:
@@ -786,7 +799,7 @@ The memory mixin used is `YAFL_EKF_BASE_MEMORY_MIXIN`.
 
 The initilizer macro is:
 ```C
-YAFL_EKF_ADAPTIVE_ROBUST_INITIALIZER(fx, jfx, hx, jhx, zrf, g, gdot,  nx, nz, memory)
+YAFL_EKF_ADAPTIVE_ROBUST_INITIALIZER(fx, jfx, hx, jhx, zrf, g, gdot, nx, nz, rff, qff, chi2, memory)
 ```
 
 As you can see it takes the same parameter list as `YAFL_EKF_ROBUST_INITIALIZER`. the `chi2` field is set to `scipy.stats.chi2.ppf(0.997, 1)`
@@ -823,7 +836,7 @@ The basic type for all **UKF** control blocks is `yaflUKFBaseSt`.
 The memory mixin used is `YAFL_UKF_BASE_MEMORY_MIXIN` which is similar to other memory mixins in YAFL.
 
 ```C
-YAFL_UKF_BASE_INITIALIZER(points, points_methods, fx, xmf, xrf, hx, zmf, zrf, nx, nz, memory)
+YAFL_UKF_BASE_INITIALIZER(points, points_methods, fx, xmf, xrf, hx, zmf, zrf, nx, nz, rff, memory)
 ```
 It is quite similar to **EKF** base initializer but there are no Jacobian function pointers and there are som new parameters:
 * `points`         - a pointer to sigma point generator object
@@ -832,6 +845,8 @@ It is quite similar to **EKF** base initializer but there are no Jacobian functi
 * `xrf`            - a pointer to a state residual function
 * `zmf`            - a ponter to a measurement mean function
 
+**Note that we don't have `qff` parameter since `Q` adjustment leads to numerical instability of Unscented filter variants!**
+
 #### Siama points generators
 Sigma point generator is runtime extension to `yaflUKFBaseSt` data type.
 We desided to add such runtime extension to **UKF** types because user may
@@ -1115,8 +1130,8 @@ extern yaflStatusEn  hx(yaflKalmanBaseSt * self, yaflFloat * y, yaflFloat * x)
 extern yaflStatusEn zmf(yaflKalmanBaseSt * self, yaflFloat *result, yaflFloat *sigmas_z);
 extern yaflStatusEn zrf(yaflKalmanBaseSt * self, yaflFloat *result, yaflFloat *a, yaflFloat *b)
 
-/*                                         points   points_metods    fx  xmf  xrf  hx  zmf  zrf  nx  nz   memory */
-yaflEKFBaseSt kf = YAFL_UKF_BASE_INITIALIZER(sp, yafl_ukf_merwe_spm, fx, xmf, xrf, hx, zmf, zrf, NX, NX, ukf_memory);
+/*                                         points   points_metods    fx  xmf  xrf  hx  zmf  zrf  nx  nz  rff  memory */
+yaflEKFBaseSt kf = YAFL_UKF_BASE_INITIALIZER(sp, yafl_ukf_merwe_spm, fx, xmf, xrf, hx, zmf, zrf, NX, NX, 0.0, ukf_memory);
 
 ```
 
@@ -1201,7 +1216,7 @@ The memory mixin used is `YAFL_UKF_BASE_MEMORY_MIXIN` which is similar to other
 
 The initializer used is:
 ```C
-yaflEKFBaseSt kf = YAFL_UKF_ROBUST_INITIALIZER(points, points_methods, fx, xmf, xrf, hx, zmf, zrf, g, gdot, nx, nz, memory);
+yaflEKFBaseSt kf = YAFL_UKF_ROBUST_INITIALIZER(points, points_methods, fx, xmf, xrf, hx, zmf, zrf, g, gdot, nx, nz, rff, memory);
 ```
 
 where:
@@ -1223,8 +1238,8 @@ extern yaflFloat psi(yaflKalmanBaseSt * self, yaflFloat normalized_error);
 extern yaflFloat psi_dot(yaflKalmanBaseSt * self, yaflFloat normalized_error);
 
 /*The filter is initiated differently from basic UKF:*/
-/*                                           points   points_metods    fx  xmf  xrf  hx  zmf  zrf   g     gdot   nx  nz   memory */
-yaflEKFBaseSt kf = YAFL_UKF_ROBUST_INITIALIZER(sp, yafl_ukf_merwe_spm, fx, xmf, xrf, hx, zmf, zrf, psi, psi_dot, NX, NX, ukf_memory);
+/*                                           points   points_metods    fx  xmf  xrf  hx  zmf  zrf   g     gdot   nx  nz  rff  memory */
+yaflEKFBaseSt kf = YAFL_UKF_ROBUST_INITIALIZER(sp, yafl_ukf_merwe_spm, fx, xmf, xrf, hx, zmf, zrf, psi, psi_dot, NX, NX, 0.0, ukf_memory);
 ```
 
 #### Adaptive robust Bierman UKF
@@ -1271,8 +1286,8 @@ The memory mixin used is `YAFL_UKF_MEMORY_MIXIN` which is similar to other memor
 
 The initializer used is: `YAFL_UKF_INITIALIZER`
 ```C
-/*                                                  points   points_metods    fx  xmf  xrf  hx  zmf  zrf  nx  nz  chi2    memory */
-yaflEKFBaseSt kf = YAFL_UKF_FULL_ADAPTIVE_INITIALIZER(sp, yafl_ukf_merwe_spm, fx, xmf, xrf, hx, zmf, zrf, NX, NX, chi2, ukf_memory);
+/*                                                  points   points_metods    fx  xmf  xrf  hx  zmf  zrf  nx  nz  rff, chi2    memory */
+yaflEKFBaseSt kf = YAFL_UKF_FULL_ADAPTIVE_INITIALIZER(sp, yafl_ukf_merwe_spm, fx, xmf, xrf, hx, zmf, zrf, NX, NX, rff, chi2, ukf_memory);
 ```
 where `chi2` must be set to `scipy.stats.chi2.ppf(1.0 - some_small_number, NZ)` as we can not predict `chi2` for specific `nz` at compile time using C-preprocessor.