pvt.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2010 Swift Navigation Inc.
00003  * Contact: Henry Hallam <henry@swift-nav.com>
00004  *          Matt Peddie <peddie@alum.mit.edu>
00005  *
00006  * This source is subject to the license found in the file 'LICENSE' which must
00007  * be be distributed together with this source. All other rights reserved.
00008  *
00009  * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
00010  * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
00011  * WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
00012  */
00013 
00014 #include <math.h>
00015 #include <string.h>
00016 #include <stdio.h>
00017 
00018 #include "linear_algebra.h"
00019 #include "coord_system.h"
00020 
00021 #include "pvt.h"
00022 #include "track.h"
00023 
00024 static double vel_solve(double rx_vel[],
00025                         const navigation_measurement_t const nav_meas[GPS_NUM_SATS],
00026                         const u8 n_used,
00027                         const double const G[n_used][4],
00028                         const double const X[4][n_used])
00029 {
00030   /* Velocity Solution
00031    *
00032    * G and X matrices already exist from the position
00033    * solution loop through valid measurements.  Here we form satellite
00034    * velocity and pseudorange rate vectors -- it's the same
00035    * prediction-error least-squares thing, but we do only one step.
00036   */
00037 
00038   double tempvX[n_used];
00039   double pdot_pred;
00040 
00041   for (u8 j = 0; j < n_used; j++) {
00042     /* Calculate predicted pseudorange rates from the satellite velocity
00043      * and the geometry matix G which contains normalised line-of-sight
00044      * vectors to the satellites.
00045      */
00046     pdot_pred = -vector_dot(3, G[j], nav_meas[j].sat_vel);
00047 
00048     /* The residual is due to the user's motion. */
00049     tempvX[j] = nav_meas[j].pseudorange_rate - pdot_pred;
00050   }
00051 
00052   /* Use X to map our pseudorange rate residuals onto the Jacobian update.
00053    *
00054    *   rx_vel[j] = X[j] . tempvX[j]
00055    */
00056   matrix_multiply(4, n_used, 1, (double *) X, (double *) tempvX, (double *) rx_vel);
00057 
00058   /* Return just the receiver clock bias. */
00059   return rx_vel[3];
00060 }
00061 
00062 void compute_dops(const double const H[4][4],
00063                   const double const pos_ecef[3],
00064                   dops_t *dops)
00065 {
00066   double H_pos_diag[3];
00067   double H_ned[3];
00068 
00069   dops->gdop = dops->pdop = dops->tdop = dops->hdop = dops->vdop = 0;
00070 
00071   /* PDOP is the norm of the position elements of tr(H) */
00072   for (u8 i=0; i<3; i++) {
00073     dops->pdop += H[i][i];
00074     /* Also get the trace of H position states for use in HDOP/VDOP
00075      * calculations.
00076      */
00077     H_pos_diag[i] = H[i][i];
00078   }
00079   dops->pdop = sqrt(dops->pdop);
00080 
00081   /* TDOP is like PDOP but for the time state. */
00082   dops->tdop = sqrt(H[3][3]);
00083 
00084   /* Calculate the GDOP -- ||tr(H)|| = sqrt(PDOP^2 + TDOP^2) */
00085   dops->gdop = sqrt(dops->tdop*dops->tdop + dops->pdop*dops->pdop);
00086 
00087   /* HDOP and VDOP are Horizontal and Vertical; we need to rotate the
00088    * PDOP into NED frame and then take the separate components.
00089    */
00090   wgsecef2ned(H_pos_diag, pos_ecef, H_ned);
00091   dops->vdop = sqrt(H_ned[2]*H_ned[2]);
00092   dops->hdop = sqrt(H_ned[0]*H_ned[0] + H_ned[1]*H_ned[1]);
00093 }
00094 
00095 
00096 /* This function is the key to GPS solution, so it's commented
00097  * liberally.  It does a single step of a multi-dimensional
00098  * Newton-Raphson solution for the variables X, Y, Z (in ECEF) plus
00099  * the clock offset for each receiver used to make pseudorange
00100  * measurements.  The steps involved are roughly the following:
00101  *
00102  *     1. account for the Earth's rotation during transmission
00103  *
00104  *     2. Estimate the ECEF position for each satellite measured using
00105  *     the downloaded ephemeris
00106  *
00107  *     3. Compute the Jacobian of pseudorange versus estimated state.
00108  *     There's no explicit differentiation; it's done symbolically
00109  *     first and just coded as a "line of sight" vector.
00110  *
00111  *     4. Get the inverse of the Jacobian times its transpose.  This
00112  *     matrix is normalized to one, but it tells us the direction we
00113  *     must move the state estimate during this step.
00114  *
00115  *     5. Multiply this inverse matrix (H) by the transpose of the
00116  *     Jacobian (to yield X).  This maps the direction of our state
00117  *     error into a direction of pseudorange error.
00118  *
00119  *     6. Multiply this matrix (X) by the error between the estimated
00120  *     (ephemeris) position and the measured pseudoranges.  This
00121  *     yields a vector of corrections to our state estimate.  We apply
00122  *     these to our current estimate and recurse to the next step.
00123  *
00124  *     7. If our corrections are very small, we've arrived at a good
00125  *     enough solution.  Solve for the receiver's velocity (with
00126  *     vel_solve) and do some bookkeeping to pass the solution back
00127  *     out.
00128  */
00129 static double pvt_solve(double rx_state[],
00130                         const u8 n_used,
00131                         const navigation_measurement_t const nav_meas[n_used],
00132                         double H[4][4])
00133 {
00134   double p_pred[n_used];
00135 
00136   /* Vector of prediction errors */
00137   double omp[n_used];
00138 
00139   /* G is a geometry matrix tells us how our pseudoranges relate to
00140    * our state estimates -- it's the Jacobian of d(p_i)/d(x_j) where
00141    * x_j are x, y, z, Δt. */
00142   double G[n_used][4];
00143   double Gtrans[4][n_used];
00144   double GtG[4][4];
00145 
00146   /* H is the square of the Jacobian matrix; it tells us the shape of
00147      our error (or, if you prefer, the direction in which we need to
00148      move to get a better solution) in terms of the receiver state. */
00149 
00150   /* X is just H * Gtrans -- it maps our pseudoranges onto our
00151    * Jacobian update */
00152   double X[4][n_used];
00153 
00154   double tempv[3];
00155   double los[3];
00156   double xk_new[3];
00157   double tempd;
00158   double correction[4];
00159 
00160   for (u8 j=0; j<4; j++) {
00161     correction[j] = 0.0;
00162   }
00163 
00164   for (u8 j = 0; j < n_used; j++) {
00165     /* The satellite positions need to be corrected for earth's
00166      * rotation during the transmission time.  We base this correction
00167      * on the range between our receiver and satellite k */
00168     vector_subtract(3, rx_state, nav_meas[j].sat_pos, tempv);
00169 
00170     /* Magnitude of range vector converted into an approximate time in secs. */
00171     double tau = vector_norm(3, tempv) / NAV_C;
00172     /* Rotation of Earth during transit in radians. */
00173     double wEtau = NAV_OMEGAE_DOT * tau;
00174 
00175     /* Form rotation matrix about Z-axis for Earth's motion which will
00176      * adjust for the satellite's position at time (t-tau).
00177      */
00178     double rotm[3][3];
00179     rotm[0][0] = cos(wEtau);
00180     rotm[0][1] = sin(wEtau);
00181     rotm[0][2] = 0.0;
00182     rotm[1][0] = -sin(wEtau);
00183     rotm[1][1] = cos(wEtau);
00184     rotm[1][2] = 0.0;
00185     rotm[2][0] = 0.0;
00186     rotm[2][1] = 0.0;
00187     rotm[2][2] = 1.0;
00188 
00189     /* Result in xk_new, position of satellite k in ECEF. */
00190     matrix_multiply(3, 3, 1, (double *) rotm,
00191                              (double *) nav_meas[j].sat_pos,
00192                              (double *) xk_new);
00193 
00194     /* Line of sight vector. */
00195     vector_subtract(3, xk_new, rx_state, los);
00196 
00197     /* Predicted range from satellite position and estimated Rx position. */
00198     p_pred[j] = vector_norm(3, los);
00199 
00200     /* omp means "observed minus predicted" range -- this is E, the
00201      * prediction error vector (or innovation vector in Kalman/LS
00202      * filtering terms).
00203      */
00204     omp[j] = nav_meas[j].pseudorange - p_pred[j];
00205 
00206 
00207     /* Construct a geometry matrix.  Each row (satellite) is
00208      * independently normalized into a unit vector.
00209      */
00210     for (u8 i=0; i<3; i++) {
00211       G[j][i] = -los[i] / p_pred[j];
00212     }
00213 
00214     /* Set time covariance to 1. */
00215     G[j][3] = 1;
00216 
00217   } /* End of channel loop. */
00218 
00219   /* Solve for position corrections using batch least-squares.  When
00220    * all-at-once least-squares estimation for a nonlinear problem is
00221    * mixed with numerical iteration (not time-series recursion, but
00222    * iteration on a single set of measurements), it's basically
00223    * Newton's method.  There's a reasonably clear explanation of this
00224    * on Wikipedia's article about GPS.
00225    */
00226 
00227   /* Gt := G^{T} */
00228   matrix_transpose(n_used, 4, (double *) G, (double *) Gtrans);
00229   /* GtG := G^{T} G */
00230   matrix_multiply(4, n_used, 4, (double *) Gtrans, (double *) G, (double *) GtG);
00231   /* H \elem \mathbb{R}^{4 \times 4} := GtG^{-1} */
00232   matrix_inverse(4, (const double *) GtG, (double *) H);
00233   /* X := H * G^{T} */
00234   matrix_multiply(4, 4, n_used, (double *) H, (double *) Gtrans, (double *) X);
00235   /* correction := X * E (= X * omp) */
00236   matrix_multiply(4, n_used, 1, (double *) X, (double *) omp, (double *) correction);
00237 
00238   /* Increment ecef estimate by the new corrections */
00239   for (u8 i=0; i<3; i++) {
00240     rx_state[i] += correction[i];
00241   }
00242 
00243   /* Set the Δt estimates according to this solution */
00244   for (u8 i=3; i<4; i++) {
00245     rx_state[i] = correction[i];
00246   }
00247 
00248   /* Look at the magnintude of the correction to see if
00249    * the solution has converged yet.
00250    */
00251   tempd = vector_norm(3, correction);
00252   if(tempd > 0.001) {
00253     /* The solution has not converged, return a negative value to
00254      * indicate that we should continue iterating.
00255      */
00256     return -tempd;
00257   }
00258 
00259   /* The solution has converged! */
00260 
00261   /* Perform the velocity solution. */
00262   vel_solve(&rx_state[4], nav_meas, n_used, (const double (*)[4]) G, (const double (*)[n_used]) X);
00263 
00264   return tempd;
00265 }
00266 
00267 u8 filter_solution(gnss_solution* soln, dops_t* dops)
00268 {
00269   if (dops->pdop > 50.0)
00270     /* PDOP is too high to yeild a good solution. */
00271     return 1;
00272 
00273   if (soln->pos_llh[2] < -1e3 || soln->pos_llh[2] > 1e8)
00274     /* Altitude is unreasonable. */
00275     return 2;
00276 
00277   /* NOTE: The following condition is required to comply with US export
00278    * regulations. It must not be removed. Any modification to this condition
00279    * is strictly not approved by Swift Navigation Inc. */
00280 
00281   if (soln->pos_llh[2] >= 0.3048*60000 &&
00282       vector_norm(3, soln->vel_ecef) >= 0.514444444*1000)
00283     /* Altitude is greater than 60000' and velocity is greater than 1000kts. */
00284     return 3;
00285 
00286   return 0;
00287 }
00288 
00289 u8 calc_PVT(const u8 n_used,
00290             const navigation_measurement_t const nav_meas[n_used],
00291             gnss_solution *soln,
00292             dops_t *dops)
00293 {
00294   /* Initial state is the center of the Earth with zero velocity and zero
00295    * clock error, if we have some a priori position estimate we could use
00296    * that here to speed convergence a little on the first iteration.
00297    *
00298    *  rx_state format:
00299    *    pos[3], clock error, vel[3], intermediate freq error
00300    */
00301   static double rx_state[8];
00302 
00303   double H[4][4];
00304 
00305   soln->valid = 0;
00306 
00307   soln->n_used = n_used; // Keep track of number of working channels
00308 
00309   /* reset state to zero !? */
00310   for(u8 i=4; i<8; i++) {
00311     rx_state[i] = 0;
00312   }
00313 
00314   double update;
00315   u8 iters;
00316   /* Newton-Raphson iteration. */
00317   for (iters=0; iters<PVT_MAX_ITERATIONS; iters++) {
00318     if ((update = pvt_solve(rx_state, n_used, nav_meas, H)) > 0) {
00319       break;
00320     }
00321   }
00322 
00323   /* Compute various dilution of precision metrics. */
00324   compute_dops((const double(*)[4])H, rx_state, dops);
00325   soln->err_cov[6] = dops->gdop;
00326 
00327   /* Populate error covariances according to layout in definition
00328    * of gnss_solution struct.
00329    */
00330   soln->err_cov[0] = H[0][0];
00331   soln->err_cov[1] = H[0][1];
00332   soln->err_cov[2] = H[0][2];
00333   soln->err_cov[3] = H[1][1];
00334   soln->err_cov[4] = H[1][2];
00335   soln->err_cov[5] = H[2][2];
00336 
00337   if (iters >= PVT_MAX_ITERATIONS) {
00338     printf("Position solution not available after %d iterations, giving up.\n", iters);
00339     /* Reset state if solution fails */
00340     rx_state[0] = 0;
00341     rx_state[1] = 0;
00342     rx_state[2] = 0;
00343     return -1;
00344   }
00345 
00346   /* Save as x, y, z. */
00347   for (u8 i=0; i<3; i++) {
00348     soln->pos_ecef[i] = rx_state[i];
00349     soln->vel_ecef[i] = rx_state[4+i];
00350   }
00351 
00352   wgsecef2ned(soln->vel_ecef, soln->pos_ecef, soln->vel_ned);
00353 
00354   /* Convert to lat, lon, hgt. */
00355   wgsecef2llh(rx_state, soln->pos_llh);
00356 
00357   soln->clock_offset = rx_state[3] / NAV_C;
00358   soln->clock_bias = rx_state[7] / NAV_C;
00359 
00360   /* Implicitly use the first receiver to calculate offset from GPS
00361    * TOW.  Maybe there's a better way to do this?  */
00362   /* TODO: what is this about? */
00363   soln->time = nav_meas[0].tot;
00364   soln->time.tow -= rx_state[3] / NAV_C;
00365   normalize_gps_time(soln->time);
00366 
00367   u8 ret;
00368   if ((ret = filter_solution(soln, dops))) {
00369     memset(soln, 0, sizeof(*soln));
00370     memset(dops, 0, sizeof(*dops));
00371     /* Reset state if solution fails */
00372     rx_state[0] = 0;
00373     rx_state[1] = 0;
00374     rx_state[2] = 0;
00375     printf("Solution filtered: %d\n", ret);
00376     return -1;
00377   }
00378 
00379   soln->valid = 1;
00380   return 0;
00381 }
00382 


enu
Author(s): Mike Purvis
autogenerated on Sun Oct 5 2014 23:44:53