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