1 //==============================================================================
2 //
3 //  This file is part of GPSTk, the GPS Toolkit.
4 //
5 //  The GPSTk is free software; you can redistribute it and/or modify
6 //  it under the terms of the GNU Lesser General Public License as published
7 //  by the Free Software Foundation; either version 3.0 of the License, or
8 //  any later version.
9 //
10 //  The GPSTk is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU Lesser General Public License for more details.
14 //
15 //  You should have received a copy of the GNU Lesser General Public
16 //  License along with GPSTk; if not, write to the Free Software Foundation,
17 //  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
18 //
19 //  This software was developed by Applied Research Laboratories at the
20 //  University of Texas at Austin.
21 //  Copyright 2004-2020, The Board of Regents of The University of Texas System
22 //
23 //==============================================================================
24 
25 //==============================================================================
26 //
27 //  This software was developed by Applied Research Laboratories at the
28 //  University of Texas at Austin, under contract to an agency or agencies
29 //  within the U.S. Department of Defense. The U.S. Government retains all
30 //  rights to use, duplicate, distribute, disclose, or release this software.
31 //
32 //  Pursuant to DoD Directive 523024
33 //
34 //  DISTRIBUTION STATEMENT A: This software has been approved for public
35 //                            release, distribution is unlimited.
36 //
37 //==============================================================================
38 
39 //------------------------------------------------------------------------------------
40 #include "SRIFilter.hpp"
41 #include "RobustStats.hpp"
42 #include "StringUtils.hpp"
43 
44 //------------------------------------------------------------------------------------
45 // TD
46 using namespace std;
47 
48 namespace gpstk
49 {
50 
51 using namespace StringUtils;
52 
53 //------------------------------------------------------------------------------------
54 // empty constructor
SRIFilter(void)55 SRIFilter::SRIFilter(void)
56    throw()
57 {
58    defaults();
59 }
60 
61 //------------------------------------------------------------------------------------
62 // constructor given the dimension N.
SRIFilter(const unsigned int N)63 SRIFilter::SRIFilter(const unsigned int N)
64    throw()
65 {
66    defaults();
67    R = Matrix<double>(N,N,0.0);
68    Z = Vector<double>(N,0.0);
69    names = Namelist(N);
70 }
71 
72 //------------------------------------------------------------------------------------
73 // constructor given a Namelist, its dimension determines the SRI dimension.
SRIFilter(const Namelist & NL)74 SRIFilter::SRIFilter(const Namelist& NL)
75    throw()
76 {
77    defaults();
78    if(NL.size() <= 0) return;
79    R = Matrix<double>(NL.size(),NL.size(),0.0);
80    Z = Vector<double>(NL.size(),0.0);
81    names = NL;
82 }
83 
84 //------------------------------------------------------------------------------------
85 // explicit constructor - throw if the dimensions are inconsistent.
SRIFilter(const Matrix<double> & Rin,const Vector<double> & Zin,const Namelist & NLin)86 SRIFilter::SRIFilter(const Matrix<double>& Rin,
87                      const Vector<double>& Zin,
88                      const Namelist& NLin)
89 {
90    defaults();
91    if(Rin.rows() != Rin.cols() ||
92       Rin.rows() != Zin.size() ||
93       Rin.rows() != NLin.size()) {
94       MatrixException me("Invalid input dimensions: R is "
95          + asString<int>(Rin.rows()) + "x"
96          + asString<int>(Rin.cols()) + ", Z has length "
97          + asString<int>(Zin.size()) + ", and NL has length "
98          + asString<int>(NLin.size())
99          );
100       GPSTK_THROW(me);
101    }
102    R = Rin;
103    Z = Zin;
104    names = NLin;
105 }
106 
107 //------------------------------------------------------------------------------------
108 // operator=
operator =(const SRIFilter & right)109 SRIFilter& SRIFilter::operator=(const SRIFilter& right)
110    throw()
111 {
112    R = right.R;
113    Z = right.Z;
114    names = right.names;
115    //valid = right.valid;
116    return *this;
117 }
118 
119 //------------------------------------------------------------------------------------
120 // SRIF (Kalman) measurement update, or least squares update
121 // Returns unwhitened residuals in D
measurementUpdate(const Matrix<double> & H,Vector<double> & D,const Matrix<double> & CM)122 void SRIFilter::measurementUpdate(const Matrix<double>& H, Vector<double>& D,
123                                   const Matrix<double>& CM)
124 {
125    if(H.cols() != R.cols() || H.rows() != D.size() ||
126       (&CM != &SRINullMatrix && (CM.rows() != D.size() || CM.cols() != D.size())) )
127    {
128       string msg("\nInvalid input dimensions:\n  SRI is ");
129       msg += asString<int>(R.rows()) + "x"
130           + asString<int>(R.cols()) + ",\n  Partials is "
131           + asString<int>(H.rows()) + "x"
132           + asString<int>(H.cols()) + ",\n  Data has length "
133           + asString<int>(D.size());
134       if(&CM != &SRINullMatrix) msg += ",\n  and Cov is "
135           + asString<int>(CM.rows()) + "x"
136           + asString<int>(CM.cols());
137 
138       MatrixException me(msg);
139       GPSTK_THROW(me);
140    }
141    try {
142          // whiten partials and data
143       Matrix<double> P(H);
144       Matrix<double> CHL;
145       if(&CM != &SRINullMatrix) {
146          CHL = lowerCholesky(CM);
147          Matrix<double> L(inverseLT(CHL));
148          P = L * P;
149          D = L * D;
150       }
151 
152          // update *this with the whitened information
153       SrifMU(R, Z, P, D);
154 
155          // un-whiten residuals
156       if(&CM != &SRINullMatrix)           // same if above creates CHL
157          D = CHL * D;
158    }
159    catch(MatrixException& me) { GPSTK_RETHROW(me); }
160    catch(VectorException& ve) { GPSTK_RETHROW(ve); }
161 }
162 
163 //------------------------------------------------------------------------------------
164 // SRIF (Kalman) measurement update, or least squares update -- SparseMatrix version
165 // Returns unwhitened residuals in D
measurementUpdate(const SparseMatrix<double> & H,Vector<double> & D,const SparseMatrix<double> & CM)166 void SRIFilter::measurementUpdate(const SparseMatrix<double>& H, Vector<double>& D,
167                                   const SparseMatrix<double>& CM)
168 {
169    if(H.cols() != R.cols() || H.rows() != D.size() ||
170       (&CM != &SRINullSparseMatrix &&
171          (CM.rows() != D.size() || CM.cols() != D.size())) )
172    {
173       string msg("\nInvalid input dimensions:\n  SRI is ");
174       msg += asString<int>(R.rows()) + "x"
175           + asString<int>(R.cols()) + ",\n  Partials is "
176           + asString<int>(H.rows()) + "x"
177           + asString<int>(H.cols()) + ",\n  Data has length "
178           + asString<int>(D.size());
179       if(&CM != &SRINullSparseMatrix) msg += ",\n  and Cov is "
180           + asString<int>(CM.rows()) + "x"
181           + asString<int>(CM.cols());
182 
183       MatrixException me(msg);
184       GPSTK_THROW(me);
185    }
186    try {
187       SparseMatrix<double> A(H || D);
188       SparseMatrix<double> CHL;
189          // whiten partials and data
190       if(&CM != &SRINullSparseMatrix) {
191          CHL = lowerCholesky(CM);
192          SparseMatrix<double> L(inverseLT(CHL));
193          A = L * A;
194       }
195 
196          // update *this with the whitened information
197       SrifMU(R, Z, A);
198 
199          // copy out D and un-whiten residuals
200       D = Vector<double>(A.colCopy(A.cols()-1));
201       if(&CM != &SRINullSparseMatrix) {      // same if above creates CHL
202          D = CHL * D;
203       }
204    }
205    catch(MatrixException& me) { GPSTK_RETHROW(me); }
206    catch(VectorException& ve) { GPSTK_RETHROW(ve); }
207 }
208 
209 //------------------------------------------------------------------------------------
210 // SRIF (Kalman) time update see SrifTU for doc.
timeUpdate(Matrix<double> & PhiInv,Matrix<double> & Rw,Matrix<double> & G,Vector<double> & Zw,Matrix<double> & Rwx)211 void SRIFilter::timeUpdate(Matrix<double>& PhiInv,
212                            Matrix<double>& Rw,
213                            Matrix<double>& G,
214                            Vector<double>& Zw,
215                            Matrix<double>& Rwx)
216 {
217    try { SrifTU(R, Z, PhiInv, Rw, G, Zw, Rwx); }
218    catch(MatrixException& me) { GPSTK_RETHROW(me); }
219 }
220 
221 //------------------------------------------------------------------------------------
222 // SRIF (Kalman) smoother update see SrifSU for doc.
smootherUpdate(Matrix<double> & Phi,Matrix<double> & Rw,Matrix<double> & G,Vector<double> & Zw,Matrix<double> & Rwx)223 void SRIFilter::smootherUpdate(Matrix<double>& Phi,
224                                Matrix<double>& Rw,
225                                Matrix<double>& G,
226                                Vector<double>& Zw,
227                                Matrix<double>& Rwx)
228 {
229    try { SrifSU(R, Z, Phi, Rw, G, Zw, Rwx); }
230    catch(MatrixException& me) { GPSTK_RETHROW(me); }
231 }
232 
233 //------------------------------------------------------------------------------------
DMsmootherUpdate(Matrix<double> & P,Vector<double> & X,Matrix<double> & Phinv,Matrix<double> & Rw,Matrix<double> & G,Vector<double> & Zw,Matrix<double> & Rwx)234 void SRIFilter::DMsmootherUpdate(Matrix<double>& P,
235                                  Vector<double>& X,
236                                  Matrix<double>& Phinv,
237                                  Matrix<double>& Rw,
238                                  Matrix<double>& G,
239                                  Vector<double>& Zw,
240                                  Matrix<double>& Rwx)
241 {
242    try { SrifSU_DM(P, X, Phinv, Rw, G, Zw, Rwx); }
243    catch(MatrixException& me) { GPSTK_RETHROW(me); }
244 }
245 
246 //------------------------------------------------------------------------------------
247 // reset the computation, i.e. remove all stored information
zeroAll(void)248 void SRIFilter::zeroAll(void)
249 {
250    SRI::zeroAll();
251 }
252 
253 //------------------------------------------------------------------------------------
254 // reset the computation, i.e. remove all stored information, and
255 // optionally change the dimension. If N is not input, the
256 // dimension is not changed.
257 // @param N new SRIFilter dimension (optional).
Reset(const int N)258 void SRIFilter::Reset(const int N)
259 {
260    if(N > 0 && N != (int)R.rows()) {
261       R.resize(N,N,0.0);
262       Z.resize(N,0.0);
263    }
264    else
265       SRI::zeroAll(N);
266 }
267 
268 //------------------------------------------------------------------------------------
269 // private beyond this
270 //------------------------------------------------------------------------------------
271 
272 //------------------------------------------------------------------------------------
273 // Kalman time update.
274 // This routine uses the Householder transformation to propagate the SRIFilter
275 // state and covariance through a time step.
276 // Input:
277 // R       a priori square root information (SRI) matrix (an n by n
278 //            upper triangular matrix)
279 // Z       a priori SRIF state vector, of length n (state is X, Z = R*X).
280 // PhiInv  Inverse of state transition matrix, an n by n matrix.
281 //            PhiInv is destroyed on output.
282 // Rw      a priori square root information matrix for the process
283 //            noise, an ns by ns upper triangular matrix
284 // G       The n by ns matrix associated with process noise.  The
285 //            process noise covariance is G*Q*transpose(G) where inverse(Q)
286 //            is transpose(Rw)*Rw. G is destroyed on output.
287 // Zw      a priori 'state' associated with the process noise,
288 //            a vector with ns elements.  Usually set to zero by
289 //            the calling routine (for unbiased process noise).
290 // Rwx     An ns by n matrix which is set to zero by this routine
291 //            but is used for output.
292 //
293 // Output:
294 //    The updated square root information matrix and SRIF state (R,Z) and
295 // the matrices which are used in smoothing: Rw, Zw, Rwx.
296 // Note that PhiInv and G are trashed, and that Rw and Zw are modified.
297 //
298 // Return values:
299 //    SrifTU returns void, but throws exceptions if the input matrices
300 // or vectors have incompatible dimensions or incorrect types.
301 //
302 // Method:
303 //    This SRIF time update method treats the process noise and mapping
304 // information as a separate data equation, and applies a Householder
305 // transformation to the (appended) equations to solve for an updated
306 // state.  Thus there is another 'state' variable associated with
307 // whatever state variables have process noise.  The matrix G relates
308 // the process noise variables to the regular state variables, and
309 // appears in the term GQG(trans) of the covariance.  If all n state
310 // variables have process noise, then ns=n and G is an n by n matrix.
311 // Since some (or all) of the state variables may not have process
312 // noise, ns may be zero.  [Bierman ftnt pg 122 seems to indicate that
313 // variables with zero process noise can be handled by ns=n & setting a
314 // column of G=0.  But note that the case of the matrix G=0 is the
315 // same as ns=0, because the first ns columns would be zero below the
316 // diagonal in that case anyway, so the HH transformation would be
317 // null.]
318 //    For startup, all of the a priori information and state arrays may
319 // be zero.  That is, "no information" would imply that R and Z are zero,
320 // as well as Rw and Zw.  A priori information (covariance) and state
321 // are handled by setting P = inverse(R)*transpose(inverse((R)), Z = R*X.
322 //    There are three ways to handle non-zero process noise covariance.
323 // (1) If Q is the (known) a priori process noise covariance Q, then
324 // set Q=Rw(-1)*Rw(-T), and G=1.
325 // (2) Transform process noise covariance matrix to UDU form, Q=UDU^T,
326 // then set G=U  and Rw = (D)^-1/2.
327 // (3) Take the sqrt of process noise covariance matrix Q, then set
328 // G=this sqrt and Rw = 1.  [2 and 3 have been tested.]
329 //    The routine applies a Householder transformation to a large
330 // matrix formed by concatenation of the input matricies.  Two preliminary
331 // steps are to form Rd = R*PhiInv (stored in PhiInv) and -Rd*G (stored in
332 // G) by matrix multiplication, and to set Rwx to the zero matrix.
333 // Then the Householder transformation is applied to the following
334 // matrix, dimensions are shown in ():
335 //       _  (ns)   (n)   (1)  _          _                  _
336 // (ns) |    Rw     0     Zw   |   ==>  |   Rw   Rwx   Zw    |
337 // (n)  |  -Rd*G   Rd     Z    |   ==>  |   0     R    Z     | .
338 //       -                    -          -                  -
339 // The SRI matricies R and Rw remain upper triangular.
340 //
341 //    For the programmer:  after Rwx is set to zero, G is made into
342 // -Rd*G and PhiInv is made into R*PhiInv, the transformation is applied
343 // to the matrix:
344 //       _   (ns)   (n)   (1) _
345 // (ns) |    Rw    Rwx    Zw   |
346 // (n)  |     G    PhiInv  Z   |
347 //       -                    -
348 // then the (upper triangular) matrix R is copied out of PhiInv into R.
349 // -------------------------------------------------------------------
350 //    The matrix Rwx is related to the sensitivity of the state
351 // estimate to the unmodeled parameters in Zw.  The sensitivity matrix
352 // is          Sen = -inverse(Rw)*Rwx,
353 // where perturbation in model X =
354 //             Sen * diagonal(a priori sigmas of parameter uncertainties).
355 // -------------------------------------------------------------------
356 //    The quantities Rw, Rwx and Zw on output are to be saved and used
357 // in the sqrt information fixed interval smoother (SRIS), during the
358 // backward filter process.
359 // -------------------------------------------------------------------
360 // Ref: Bierman, G.J. "Factorization Methods for Discrete Sequential
361 //      Estimation," Academic Press, 1977, pg 121.
362 // -------------------------------------------------------------------
363 template <class T>
SrifTU(Matrix<T> & R,Vector<T> & Z,Matrix<T> & PhiInv,Matrix<T> & Rw,Matrix<T> & G,Vector<T> & Zw,Matrix<T> & Rwx)364 void SRIFilter::SrifTU(Matrix<T>& R,
365                        Vector<T>& Z,
366                        Matrix<T>& PhiInv,
367                        Matrix<T>& Rw,
368                        Matrix<T>& G,
369                        Vector<T>& Zw,
370                        Matrix<T>& Rwx)
371 {
372    const T EPS=-T(1.e-200);
373    unsigned int n=R.rows(),ns=Rw.rows();
374    unsigned int i,j,k;
375    T sum, beta, delta, dum;
376 
377    if(PhiInv.rows() < n || PhiInv.cols() < n ||
378       G.rows() < n || G.cols() < ns ||
379       R.cols() != n ||
380       Rwx.rows() < ns || Rwx.cols() < n ||
381       Z.size() < n || Zw.size() < ns) {
382       MatrixException me("Invalid input dimensions:\n  R is "
383          + asString<int>(R.rows()) + "x"
384          + asString<int>(R.cols()) + ", Z has length "
385          + asString<int>(Z.size()) + "\n  PhiInv is "
386          + asString<int>(PhiInv.rows()) + "x"
387          + asString<int>(PhiInv.cols()) + "\n  Rw is "
388          + asString<int>(Rw.rows()) + "x"
389          + asString<int>(Rw.cols()) + "\n  G is "
390          + asString<int>(G.rows()) + "x"
391          + asString<int>(G.cols()) + "\n  Zw has length "
392          + asString<int>(Zw.size()) + "\n  Rwx is "
393          + asString<int>(Rwx.rows()) + "x"
394          + asString<int>(Rwx.cols())
395          );
396       GPSTK_THROW(me);
397    }
398 
399    try {
400       // initialize
401       Rwx = T(0);
402       PhiInv = R * PhiInv;                   // set PhiInv = Rd = R*PhiInv
403       G = -PhiInv * G;
404       // fixed Matrix problem - unary minus should not return an l-value
405       //G = -(PhiInv * G);                     // set G = -Rd*G
406 
407       // temp
408       //Matrix <T> A;
409       //A = (Rw || Rwx || Zw) && (G || PhiInv || Z);
410       //cout << "SrifTU - :\n" << fixed << setw(10) << setprecision(5) << A << endl;
411 
412       //---------------------------------------------------------------
413       for(j=0; j<ns; j++) {               // loop over first ns columns
414          sum = T(0);
415          for(i=0; i<n; i++)               // rows of -Rd*G
416             sum += G(i,j)*G(i,j);
417          dum = Rw(j,j);
418          sum += dum*dum;
419          sum = (dum > T(0) ? -T(1) : T(1)) * ::sqrt(sum);
420          delta = dum - sum;
421          Rw(j,j) = sum;
422 
423          beta = sum * delta;
424          if(beta > EPS) continue;
425          beta = T(1)/beta;
426 
427             // apply jth Householder transformation
428             // to submatrix below and right of (j,j)
429          if(j+1 < ns) {                   // apply to G
430             for(k=j+1; k<ns; k++) {       // columns to right of diagonal
431                sum = delta * Rw(j,k);
432                for(i=0; i<n; i++)         // rows of G
433                   sum += G(i,j)*G(i,k);
434                if(sum == T(0)) continue;
435                sum *= beta;
436                Rw(j,k) += sum*delta;
437                for(i=0; i<n; i++)         // rows of G again
438                   G(i,k) += sum * G(i,j);
439             }
440          }
441 
442             // apply jth Householder transformation
443             // to Rwx and PhiInv
444          for(k=0; k<n; k++) {             // columns of Rwx and PhiInv
445             sum = delta * Rwx(j,k);
446             for(i=0; i<n; i++)            // rows of PhiInv and G
447                sum += PhiInv(i,k) * G(i,j);
448             if(sum == T(0)) continue;
449             sum *= beta;
450             Rwx(j,k) += sum*delta;
451             for(i=0; i<n; i++)            // rows of PhiInv and G
452                PhiInv(i,k) += sum * G(i,j);
453          }                                // end loop over columns of Rwx and PhiInv
454 
455             // apply jth Householder transformation
456             // to Zw and Z
457          sum = delta * Zw(j);
458          for(i=0; i<n; i++)               // rows of G and elements of Z
459             sum += Z(i) * G(i,j);
460          if(sum == T(0)) continue;
461          sum *= beta;
462          Zw(j) += sum * delta;
463          for(i=0; i<n; i++)               // rows of G and elements of Z
464             Z(i) += sum * G(i,j);
465       }                                   // end loop over first ns columns
466 
467       //---------------------------------------------------------------
468       for(j=0; j<n; j++) {                // loop over columns of Rwx and PhiInv
469          sum = T(0);
470          for(i=j+1; i<n; i++)             // rows of PhiInv
471             sum += PhiInv(i,j)*PhiInv(i,j);
472          dum = PhiInv(j,j);
473          sum += dum*dum;
474          sum = (dum > T(0) ? -T(1) : T(1)) * ::sqrt(sum);
475          delta = dum - sum;
476          PhiInv(j,j) = sum;
477          beta = sum*delta;
478          if(beta > EPS) continue;
479          beta = T(1)/beta;
480 
481             // apply jth Householder transformation to columns of PhiInv on row j
482          for(k=j+1; k<n; k++) {           // columns of PhiInv
483             sum = delta * PhiInv(j,k);
484             for(i=j+1; i<n; i++)
485                sum += PhiInv(i,j)*PhiInv(i,k);
486             if(sum == T(0)) continue;
487             sum *= beta;
488             PhiInv(j,k) += sum*delta;
489             for(i=j+1; i<n; i++)
490                PhiInv(i,k) += sum * PhiInv(i,j);
491          }
492 
493             // apply jth Householder transformation to Z
494          sum = delta *Z(j);
495          for(i=j+1; i<n; i++)
496             sum += Z(i) * PhiInv(i,j);
497          if(sum == T(0)) continue;
498          sum *= beta;
499          Z(j) += sum*delta;
500          for(i=j+1; i<n; i++)
501             Z(i) += sum * PhiInv(i,j);
502       }                                   // end loop over cols of Rwx and PhiInv
503 
504       // temp
505       //A = (Rw || Rwx || Zw) && (G || PhiInv || Z);
506       //cout << "SrifTU + :\n" << fixed << setw(10) << setprecision(5) << A << endl;
507 
508          // copy transformed R out of PhiInv
509       for(j=0; j<n; j++)
510          for(i=0; i<=j; i++)
511             R(i,j) = PhiInv(i,j);
512    }
513    catch(MatrixException& me) { GPSTK_RETHROW(me); }
514 }  // end SrifTU
515 
516 //------------------------------------------------------------------------------------
517 // Kalman smoother update.
518 // This routine uses the Householder transformation to propagate the SRIF
519 // state and covariance through a smoother (backward filter) step.
520 // Input:
521 // R     A priori square root information (SRI) matrix (an N by N
522 //          upper triangular matrix)
523 // z     a priori SRIF state vector, an N vector (state is x, z = R*x).
524 // Phi   State transition matrix, an N by N matrix. Phi is destroyed on output.
525 // Rw    A priori square root information matrix for the process
526 //          noise, an Ns by Ns upper triangular matrix (which has
527 //          Ns(Ns+1)/2 elements).
528 // G     The N by Ns matrix associated with process noise.  The
529 //          process noise covariance is GQGtrans where Qinverse
530 //          is Rw(trans)*Rw. G is destroyed on output.
531 // Zw    A priori 'state' associated with the process noise,
532 //          a vector with Ns elements. Zw is destroyed on output.
533 // Rwx   An Ns by N matrix. Rwx is destroyed on output.
534 //
535 // The inputs Rw,Zw,Rwx are the output of the SRIF time update, and these and
536 // Phi and G are associated with the same timestep.
537 //
538 // Output:
539 //    The updated square root information matrix and SRIF smoothed state (R,z).
540 // All other inputs are trashed.
541 //
542 // Return values:
543 //    SrifSU returns void, but throws exceptions if the input matrices
544 // or vectors have incompatible dimensions or incorrect types.
545 //
546 // Method:
547 //    The fixed interval square root information smoother (SRIS) is
548 // composed of two Kalman filters, one identical with the square root
549 // information filter (SRIF), the other similar but operating on the
550 // data in reverse order and combining the current (smoothed) state
551 // with elements output by the SRIF in its forward run and saved.
552 // Thus a smoother is composed of a forward filter which saves all of
553 // its output, followed by a backward filter which makes use of that
554 // saved information.
555 //    This form of the SRIF backward filter algorithm is equivalent to the
556 // Dyer-McReynolds SRIS algorithm, which uses less computer resources, but
557 // propagates the state and covariance rather than the SRI (R,z). (As always,
558 // at any point the state X and covariance P are related to the SRI by
559 // X = R^-1 * z , P = R^-1 * R^-T.)
560 //    For startup of the backward filter, the state after the final
561 // measurement update of the SRIF is given another time update, the
562 // output of which is identified with the a priori values for the
563 // backward filter.  Backward filtering proceeds from there, the N+1st
564 // point, toward the first point.
565 //
566 //    In this implementation of the backward filter, the Householder
567 // transformation is applied to the following matrix
568 // (dimensions are shown in ()):
569 //
570 //       _  (Ns)     (N)      (1) _          _                  _
571 // (Ns) |  Rw+Rwx*G  Rwx*Phi  Zw   |   ==>  |   Rw   Rwx   Zw    |
572 // (N)  |  R*G       R*Phi    z    |   ==>  |   0     R    z     | .
573 //       -                        -          -                  -
574 // The SRI matricies R and Rw remain upper triangular.
575 //
576 //    For the programmer: First create an NsXNs matrix A, then
577 // Rw+Rwx*G -> A, Rwx*Phi -> Rwx, R*Phi -> Phi, and R*G -> G, and
578 // the transformation is applied to the matrix:
579 //
580 //       _ (Ns)   (N)  (1) _
581 // (Ns) |   A    Rwx   Zw   |
582 // (N)  |   G    Phi   z    |
583 //       -                 -
584 // then the (upper triangular) matrix R is copied out of Phi into R.
585 //
586 // Ref: Bierman, G.J. "Factorization Methods for Discrete Sequential
587 //      Estimation," Academic Press, 1977, pg 216.
588 template <class T>
SrifSU(Matrix<T> & R,Vector<T> & Z,Matrix<T> & Phi,Matrix<T> & Rw,Matrix<T> & G,Vector<T> & Zw,Matrix<T> & Rwx)589 void SRIFilter::SrifSU(Matrix<T>& R,
590                        Vector<T>& Z,
591                        Matrix<T>& Phi,
592                        Matrix<T>& Rw,
593                        Matrix<T>& G,
594                        Vector<T>& Zw,
595                        Matrix<T>& Rwx)
596 {
597    unsigned int N=R.rows(),Ns=Rw.rows();
598 
599    if(Phi.rows() < N || Phi.cols() < N ||
600       G.rows() < N || G.cols() < Ns ||
601       R.cols() != N ||
602       Rwx.rows() < Ns || Rwx.cols() < N ||
603       Z.size() < N || Zw.size() < Ns) {
604       MatrixException me("Invalid input dimensions:\n  R is "
605          + asString<int>(R.rows()) + "x"
606          + asString<int>(R.cols()) + ", Z has length "
607          + asString<int>(Z.size()) + "\n  Phi is "
608          + asString<int>(Phi.rows()) + "x"
609          + asString<int>(Phi.cols()) + "\n  Rw is "
610          + asString<int>(Rw.rows()) + "x"
611          + asString<int>(Rw.cols()) + "\n  G is "
612          + asString<int>(G.rows()) + "x"
613          + asString<int>(G.cols()) + "\n  Zw has length "
614          + asString<int>(Zw.size()) + "\n  Rwx is "
615          + asString<int>(Rwx.rows()) + "x"
616          + asString<int>(Rwx.cols())
617          );
618       GPSTK_THROW(me);
619    }
620 
621    const T EPS=-T(1.e-200);
622    size_t i, j, k;
623    T sum, beta, delta, diag;
624 
625 try {
626       // Rw+Rwx*G -> A
627    Matrix<T> A;
628    A = Rw + Rwx*G;
629    Rwx = Rwx * Phi;
630    Phi = R * Phi;
631    G = R * G;
632 
633          //-----------------------------------------
634          // HouseHolder Transformation
635 
636          // Loop over first Ns columns
637    for(j=0; j<Ns; j++) {                  // columns of A
638       sum = T(0);
639       for(i=j+1; i<Ns; i++) {             // rows i below diagonal in A
640          sum += A(i,j) * A(i,j);
641       }
642       for(i=0; i<N; i++) {                // all rows i in G
643          sum += G(i,j) * G(i,j);
644       }
645 
646       diag = A(j,j);
647       sum += diag*diag;
648       sum = (diag > T(0) ? -T(1) : T(1)) * ::sqrt(sum);
649       delta = diag - sum;
650       A(j,j) = sum;
651       beta = sum*delta;
652       if(beta > EPS) continue;
653       beta = T(1)/beta;
654 
655             // apply jth HH trans to submatrix below and right of j,j
656       for(k=j+1; k<Ns; k++) {                // cols to right of diag
657          sum = delta * A(j,k);
658          for(i=j+1; i<Ns; i++) {             // rows of A below diagonal
659             sum += A(i,j)*A(i,k);
660          }
661          for(i=0; i<N; i++) {                // all rows of G
662             sum += G(i,j)*G(i,k);
663          }
664          if(sum == T(0)) continue;
665          sum *= beta;
666             //------------------------------------------
667          A(j,k) += sum*delta;
668 
669          for(i=j+1; i<Ns; i++) {             // rows of A > j (same loops again)
670             A(i,k) += sum * A(i,j);
671          }
672          for(i=0; i<N; i++) {                // all rows of G (again)
673             G(i,k) += sum * G(i,j);
674          }
675       }
676 
677             // apply jth HH trans to Rwx and Phi sub-matrices
678       for(k=0; k<N; k++) {                // all columns of Rwx / Phi
679          sum = delta * Rwx(j,k);
680          for(i=j+1; i<Ns; i++) {          // rows of Rwx below j
681             sum += A(i,j) * Rwx(i,k);
682          }
683          for(i=0; i<N; i++) {             // all rows of Phi
684             sum += G(i,j) * Phi(i,k);
685          }
686          if(sum == T(0)) continue;
687          sum *= beta;
688          Rwx(j,k) += sum*delta;
689          for(i=j+1; i<Ns; i++) {          // rows of Rwx below j (again)
690             Rwx(i,k) += sum * A(i,j);
691          }
692          for(i=0; i<N; i++) {             // all rows of Phi (again)
693             Phi(i,k) += sum * G(i,j);
694          }
695       }
696 
697             // apply jth HH trans to Zw and Z
698       sum = delta * Zw(j);
699       for(i=j+1; i<Ns; i++) {             // rows (elements) of Zw below j
700          sum += A(i,j) * Zw(i);
701       }
702       for(i=0; i<N; i++) {                // all rows (elements) of Z
703          sum += Z(i) * G(i,j);
704       }
705       if(sum == T(0)) continue;
706       sum *= beta;
707       Zw(j) += sum*delta;
708       for(i=j+1; i<Ns; i++) {             // rows of Zw below j (again)
709          Zw(i) += sum * A(i,j);
710       }
711       for(i=0; i<N; i++) {                // all rows of Z (again)
712          Z(i) += sum * G(i,j);
713       }
714    }
715 
716          // Loop over columns past the Ns block: all of Rwx and Phi
717    for(j=0; j<N; j++) {
718       sum = T(0);
719       for(i=j+1; i<N; i++) {              // rows of Phi at and below j
720          sum += Phi(i,j) * Phi(i,j);
721       }
722       diag = Phi(j,j);
723       sum += diag*diag;
724       sum = (diag > T(0) ? -T(1) : T(1)) * ::sqrt(sum);
725       delta = diag - sum;
726       Phi(j,j) = sum;
727       beta = sum*delta;
728       if(beta > EPS) continue;
729       beta = T(1)/beta;
730 
731                // apply HH trans to Phi sub-block below and right of j,j
732       for(k=j+1; k<N; k++) {                 // columns k > j
733          sum = delta * Phi(j,k);
734          for(i=j+1; i<N; i++) {              // rows below j
735             sum += Phi(i,j) * Phi(i,k);
736          }
737          if(sum == T(0)) continue;
738          sum *= beta;
739          Phi(j,k) += sum*delta;
740          for(i=j+1; i<N; i++) {              // rows below j (again)
741             Phi(i,k) += sum * Phi(i,j);
742          }
743       }
744                // Now apply to the Z column
745       sum = delta * Z(j);
746       for(i=j+1; i<N; i++) {                 // rows of Z below j
747          sum += Z(i) * Phi(i,j);
748       }
749       if(sum == T(0)) continue;
750       sum *= beta;
751       Z(j) += sum*delta;
752       for(i=j+1; i<N; i++) {                 // rows of Z below j (again)
753          Z(i) += sum * Phi(i,j);
754       }
755    }
756       //------------------------------
757       // Transformation finished
758 
759       //-------------------------------------
760       // copy transformed R out of Phi into R
761    R = T(0);
762    for(j=0; j<N; j++) {
763       for(i=0; i<=j; i++) {
764          R(i,j) = Phi(i,j);
765       }
766    }
767 }
768 catch(Exception& e) { GPSTK_RETHROW(e); }
769 }  // end SrifSU
770 
771 //------------------------------------------------------------------------------
772 // Covariance/State version of the Kalman smoother update (Dyer-McReynolds).
773 // This routine implements the Dyer-McReynolds form of the state and covariance
774 // recursions which constitute the backward filter of the Square Root
775 // Information Smoother.
776 //
777 // Input: (assume N and Ns are greater than zero)
778 //		Vector X(N)				A priori state, derived from SRI (R*X=Z)
779 // 	Matrix P(N,N)			A priori covariance, derived from SRI (P=R^-1*R^-T)
780 // 	Matrix Rw(Ns,Ns)		Process noise covariance (UT), output of SRIF TU
781 // 	Matrix Rwx(Ns,N)		PN 'cross term', output of SRIF TU
782 // 	Vector Zw(Ns)			Process noise state, output of SRIF TU
783 // 	Matrix Phinv(N,N)		Inverse of state transition, saved at SRIF TU
784 // 	Matrix G(N,Ns)			Noise coupling matrix, saved at SRIF TU
785 // Output:
786 // 	Updated X and P. The other inputs are trashed.
787 //
788 // Method:
789 // 	The fixed interval square root information smoother (SRIS) is
790 // composed of two Kalman filters, one identical with the square root
791 // information filter (SRIF), the other similar but operating on the
792 // data in reverse order and combining the current (smoothed) state
793 // with elements output by the SRIF in its forward run and saved.
794 // Thus a smoother is composed of a forward filter which saves all of
795 // its output, followed by a backward filter which makes use of that
796 // saved information.
797 // 	This form of the SRIS algorithm is equivalent to the SRIS backward
798 // filter Householder transformation algorithm, but uses less computer
799 // resources. It is not necessary to update both the state and the
800 // covariance, although doing both at once is less expensive than
801 // doing them separately. (This routine does both.)
802 // 	For startup of the backward filter, the state after the final
803 // measurement update of the SRIF is given another time update, the
804 // output of which is identified with the a priori values for the
805 // backward filter.  Backward filtering proceeds from there, the N+1st
806 // point, toward the first point.
807 //
808 // Ref: Bierman, G.J. "Factorization Methods for Discrete Sequential
809 //      Estimation," Academic Press, 1977, pg 216.
810 template <class T>
SrifSU_DM(Matrix<T> & P,Vector<T> & X,Matrix<T> & Phinv,Matrix<T> & Rw,Matrix<T> & G,Vector<T> & Zw,Matrix<T> & Rwx)811 void SRIFilter::SrifSU_DM(Matrix<T>& P,
812                           Vector<T>& X,
813                           Matrix<T>& Phinv,
814                           Matrix<T>& Rw,
815                           Matrix<T>& G,
816                           Vector<T>& Zw,
817                           Matrix<T>& Rwx)
818 {
819    unsigned int N=P.rows(),Ns=Rw.rows();
820 
821    if(P.cols() != P.rows() ||
822       X.size() != N ||
823       Rwx.cols() != N ||
824       Zw.size() != Ns ||
825       Rwx.rows() != Ns || Rwx.cols() != N ||
826       Phinv.rows() != N || Phinv.cols() != N ||
827       G.rows() != N || G.cols() != Ns ) {
828       MatrixException me("Invalid input dimensions:\n  P is "
829          + asString<int>(P.rows()) + "x"
830          + asString<int>(P.cols()) + ", X has length "
831          + asString<int>(X.size()) + "\n  Phinv is "
832          + asString<int>(Phinv.rows()) + "x"
833          + asString<int>(Phinv.cols()) + "\n  Rw is "
834          + asString<int>(Rw.rows()) + "x"
835          + asString<int>(Rw.cols()) + "\n  G is "
836          + asString<int>(G.rows()) + "x"
837          + asString<int>(G.cols()) + "\n  Zw has length "
838          + asString<int>(Zw.size()) + "\n  Rwx is "
839          + asString<int>(Rwx.rows()) + "x"
840          + asString<int>(Rwx.cols())
841          );
842       GPSTK_THROW(me);
843    }
844 
845 try {
846    G = G * inverseLUD(Rw);
847    Matrix<T> F;
848    F = ident<T>(N) + G*Rwx;
849    // update X
850    Vector<T> C;
851    C = F*X - G*Zw;
852    X = Phinv * C;
853    // update P
854    P = F*P*transpose(F) + G*transpose(G);
855    P = Phinv*P*transpose(Phinv);
856 }
857 catch(Exception& e) { GPSTK_RETHROW(e); }
858 } // end SrifSU_DM
859 
860 // Modification for case with control vector: Xj+1 = Phi*Xj + Gwj + u
861 template <class T>
DMsmootherUpdateWithControl(Matrix<double> & P,Vector<double> & X,Matrix<double> & Phinv,Matrix<double> & Rw,Matrix<double> & G,Vector<double> & Zw,Matrix<double> & Rwx,Vector<double> & U)862 void DMsmootherUpdateWithControl(Matrix<double>& P,
863                                  Vector<double>& X,
864                                  Matrix<double>& Phinv,
865                                  Matrix<double>& Rw,
866                                  Matrix<double>& G,
867                                  Vector<double>& Zw,
868                                  Matrix<double>& Rwx,
869                                  Vector<double>& U)
870 {
871    unsigned int N=P.rows(),Ns=Rw.rows();
872 
873    if(P.cols() != P.rows() ||
874       X.size() != N ||
875       Rwx.cols() != N ||
876       Zw.size() != Ns ||
877       Rwx.rows() != Ns || Rwx.cols() != N ||
878       Phinv.rows() != N || Phinv.cols() != N ||
879       G.rows() != N || G.cols() != Ns ||
880       U.size() != N) {
881       MatrixException me("Invalid input dimensions:\n  P is "
882          + asString<int>(P.rows()) + "x"
883          + asString<int>(P.cols()) + ", X has length "
884          + asString<int>(X.size()) + "\n  Phinv is "
885          + asString<int>(Phinv.rows()) + "x"
886          + asString<int>(Phinv.cols()) + "\n  Rw is "
887          + asString<int>(Rw.rows()) + "x"
888          + asString<int>(Rw.cols()) + "\n  G is "
889          + asString<int>(G.rows()) + "x"
890          + asString<int>(G.cols()) + "\n  Zw has length "
891          + asString<int>(Zw.size()) + "\n  Rwx is "
892          + asString<int>(Rwx.rows()) + "x"
893          + asString<int>(Rwx.cols()) + "\n  U has length "
894          + asString<int>(U.size())
895          );
896       GPSTK_THROW(me);
897    }
898 
899 try {
900    G = G * inverseLUD(Rw);
901    Matrix<T> F;
902    F = ident<T>(N) + G*Rwx;
903    // update X
904    Vector<T> C;
905    C = F*X - G*Zw - U;
906    X = Phinv * C;
907    // update P
908    P = F*P*transpose(F) + G*transpose(G);
909    P = Phinv*P*transpose(Phinv);
910    P += outer(U,U);
911 }
912 catch(Exception& e) { GPSTK_RETHROW(e); }
913 } // end DMsmootherUpdateWithControl
914 
915 //------------------------------------------------------------------------------------
916 } // end namespace gpstk
917