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, 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 /**
41  * @file PRSolutionLegacy.cpp
42  * Autonomous pseudorange navigation solution, including RAIM algorithm
43  */
44 
45 #include "MathBase.hpp"
46 #include "PRSolutionLegacy.hpp"
47 #include "EphemerisRange.hpp"
48 #include "GPSEllipsoid.hpp"
49 #include "GPSWeekSecond.hpp"
50 
51 //#define DEBUG_PRINT_WARNINGS
52 
53 // ----------------------------------------------------------------------------
54 // Combinations.hpp
55 // Find all the Combinations of n things taken k at a time.
56 
57 #include "Exception.hpp"
58 
59 /// Class Combinations will compute C(n,k), all the Combinations of n things
60 /// taken k at a time (where k <= n).
61 /// Let n 'things' be indexed by i (i=0...n-1), e.g. stored in an array of length n.
62 /// This class computes C(n,k) as sets of k indexes into the 'things' array.
63 /// These indexes are accessible via member functions Selection() or isSelected().
64 /// Next() computes the next combination until there are no more (when it returns -1).
65 
66 class Combinations
67 {
68 
69 public:
70 
71       /// Default constructor
Combinations(void)72    Combinations(void)
73       throw()
74    {
75       nc = n = k = 0;
76       Index = NULL;
77    }
78 
79       /// Constructor for C(n,k) = Combinations of n things taken k at a time (k <= n)
80       /// @throw Exception on invalid input (k>n).
Combinations(int N,int K)81    Combinations(int N, int K)
82    {
83       try
84       {
85          init(N,K);
86       }
87       catch(gpstk::Exception& e)
88       {
89          GPSTK_RETHROW(e);
90       }
91    }
92 
93       /// copy constructor
Combinations(const Combinations & right)94    Combinations(const Combinations& right)
95       throw()
96    {
97       *this = right;
98    }
99 
100       /// destructor
~Combinations(void)101    ~Combinations(void)
102    {
103       if (Index)
104          delete[] Index;
105       Index = NULL;
106    }
107 
108       /// Assignment operator.
operator =(const Combinations & right)109    Combinations& operator=(const Combinations& right)
110       throw()
111    {
112       this->~Combinations();
113       init(right.n,right.k);
114       nc = right.nc;
115       for (int j=0; j<k; j++)
116       {
117          Index[j] = right.Index[j];
118       }
119       return *this;
120    }
121 
122       /// Compute the next combination, returning the number of Combinations computed
123       /// so far; if there are no more Combinations, return -1.
124    int Next(void) throw();
125 
126       /// Return index i (0 <= i < n) of jth selection (0 <= j < k);
127       /// if j is out of range, return -1.
Selection(int j)128    int Selection(int j)
129       throw()
130    {
131       if (j < 0 || j >= k)
132          return -1;
133       return Index[j];
134    }
135 
136       /// Return true if the given index j (0 <= j < n) is
137       /// currently selected (i.e. if j = Selection(i) for some i)
isSelected(int j)138    bool isSelected(int j)
139       throw()
140    {
141       for (int i=0; i<k; i++)
142       {
143          if (Index[i] == j)
144             return true;
145       }
146       return false;
147    }
148 
149 private:
150 
151       /// The initialization routine used by constructors.
152       /// @throw Exception on invalid input (k>n or either n or k < 0).
153    void init(int N, int K);
154 
155       /// Recursive function to increment Index[j].
156    int Increment(int j) throw();
157 
158    int nc;         ///< number of Combinations computed so far
159    int k,n;        ///< Combinations of n things taken k at a time, given at c'tor
160    int* Index;     ///< Index[j] = index of jth selection (j=0...k-1; I[j]=0...n-1)
161 
162 };
163 
164 // ----------------------------------------------------------------------------
165 
Next(void)166 int Combinations::Next(void)
167    throw()
168 {
169    if (k < 1)
170       return -1;
171    if (Increment(k-1) != -1)
172       return ++nc;
173    return -1;
174 }
175 
Increment(int j)176 int Combinations::Increment(int j)
177    throw()
178 {
179    if (Index[j] < n-k+j) {        // can this index be incremented?
180       Index[j]++;
181       for (int m=j+1; m<k; m++)
182       {
183          Index[m]=Index[m-1]+1;
184       }
185       return 0;
186    }
187       // is this the last index?
188    if (j-1 < 0) return -1;
189       // increment the next lower index
190    return Increment(j-1);
191 }
192 
init(int N,int K)193 void Combinations::init(int N, int K)
194 {
195    if (K > N || N < 0 || K < 0)
196    {
197       gpstk::Exception e("Combinations(n,k) must have k <= n, with n,k >= 0");
198       GPSTK_THROW(e);
199    }
200 
201    if (K > 0)
202    {
203       Index = new int[K];
204       if (!Index)
205       {
206          gpstk::Exception e("Could not allocate");
207          GPSTK_THROW(e);
208       }
209    }
210    else
211       Index = NULL;
212 
213    nc = 0;
214    k = K;
215    n = N;
216    for (int j=0; j<k; j++)
217    {
218       Index[j]=j;
219    }
220 }
221 
222 // ----------------------------------------------------------------------------
223 
224 using namespace std;
225 using namespace gpstk;
226 
227 namespace gpstk
228 {
RAIMCompute(const CommonTime & Tr,vector<SatID> & Satellite,const vector<double> & Pseudorange,const XvtStore<SatID> & Eph,TropModel * pTropModel)229    int PRSolutionLegacy::RAIMCompute(const CommonTime& Tr,
230                                      vector<SatID>& Satellite,
231                                      const vector<double>& Pseudorange,
232                                      const XvtStore<SatID>& Eph,
233                                      TropModel *pTropModel)
234    {
235       try
236       {
237          int iret,N,Nreject,MinSV,stage;
238          size_t i, j;
239          vector<bool> UseSat, UseSave;
240          vector<int> GoodIndexes;
241          // Use these to save the 'best' solution within the loop.
242          int BestNIter=0;
243          double BestRMS=0.0, BestSL=0.0, BestConv=0.0;
244          Vector<double> BestSol(3,0.0);
245          vector<bool> BestUse;
246          BestRMS = -1.0;      // this marks the 'Best' set as unused.
247          Matrix<double> BestCovariance;
248 
249          // ----------------------------------------------------------------
250          // initialize
251 
252          Valid = false;
253 
254          bool hasGlonass = false;
255          bool hasOther = false;
256          Vector<int> satSystems(Satellite.size());
257          //Check if we have a mixed system
258          for ( i = 0; i < Satellite.size(); ++i)
259          {
260             satSystems[i] = ((Satellite[i].system == SatelliteSystem::Glonass) ? (1) : (0) );
261 
262             if (satSystems[i] == 1) hasGlonass = true;
263             else hasOther = true;
264          }
265 
266          // Save the input solution
267          // (for use in rejection when ResidualCriterion is false).
268          if (!hasGlonass && Solution.size() != 4)
269          {
270             Solution.resize(4);
271          }
272          else if (hasGlonass && hasOther && Solution.size() != 5)
273          {
274             Solution.resize(5);
275          }
276          Solution = 0.0;
277          APrioriSolution = Solution;
278 
279          // ----------------------------------------------------------------
280          // fill the SVP matrix, and use it for every solution
281          // NB this routine can set Satellite[.]=negative when no ephemeris
282 
283          i = PrepareAutonomousSolution(Tr, Satellite, Pseudorange, Eph, SVP,
284              Debug?pDebugStream:0);
285          if(Debug && pDebugStream) {
286             *pDebugStream << "In RAIMCompute after PAS(): Satellites:";
287             for(j=0; j<Satellite.size(); j++) {
288                RinexSatID rs(::abs(Satellite[j].id), Satellite[j].system);
289                *pDebugStream << " " << (Satellite[j].id < 0 ? "-":"") << rs;
290             }
291             *pDebugStream << endl;
292             *pDebugStream << " SVP matrix("
293                << SVP.rows() << "," << SVP.cols() << ")" << endl;
294             *pDebugStream << fixed << setw(16) << setprecision(3) << SVP << endl;
295          }
296          if (i) return i;  // return is 0 (ok) or -4 (no ephemeris)
297 
298          // count how many good satellites we have
299          // Initialize UseSat based on Satellite, and build GoodIndexes.
300          // UseSat is used to mark good sats (true) and those to ignore (false).
301          // UseSave saves the original so it can be reused for each solution.
302          // Let GoodIndexes be all the indexes of Satellites that are good:
303          // UseSat[GoodIndexes[.]] == true by definition
304 
305          for (N=0,i=0; i<Satellite.size(); i++)
306          {
307             if (Satellite[i].id > 0)
308             {
309                N++;
310                UseSat.push_back(true);
311                GoodIndexes.push_back(i);
312             }
313             else
314             {
315                UseSat.push_back(false);
316             }
317          }
318          UseSave = UseSat;
319 
320          // don't even try if there are not 4 good satellites
321 
322          if (!hasGlonass && N < 4)
323             return -3;
324          else if (hasGlonass && hasOther && N < 5)
325             return -3;
326 
327          // minimum number of sats needed for algorithm
328          MinSV = 5;   // this would be RAIM
329          // ( not really RAIM || not RAIM at all - just one solution)
330          if (!ResidualCriterion || NSatsReject==0)
331             MinSV=4;
332 
333          // how many satellites can RAIM reject, if we have to?
334          // default is -1, meaning as many as possible
335          Nreject = NSatsReject;
336          if (Nreject == -1 || Nreject > N-MinSV)
337             Nreject=N-MinSV;
338 
339          // ----------------------------------------------------------------
340          // now compute the solution, first with all the data. If this fails,
341          // reject 1 satellite at a time and try again, then 2, etc.
342 
343          // Slopes for each satellite are computed (cf. the RAIM algorithm)
344          Vector<double> Slopes(Pseudorange.size());
345 
346          // Residuals stores the post-fit data residuals.
347          Vector<double> Residuals(Satellite.size(),0.0);
348 
349          // stage is the number of satellites to reject.
350          stage = 0;
351 
352          do{
353             // compute all the Combinations of N satellites taken stage at a time
354             Combinations Combo(N,stage);
355 
356             // compute a solution for each combination of marked satellites
357             do{
358                // Mark the satellites for this combination
359                UseSat = UseSave;
360                for (i = 0; i < GoodIndexes.size(); i++)
361                {
362                   if (Combo.isSelected(i))
363                      UseSat[i]=false;
364                }
365                // ----------------------------------------------------------------
366                // Compute a solution given the data; ignore ranges for marked
367                // satellites. Fill Vector 'Slopes' with slopes for each unmarked
368                // satellite.
369                // Return 0  ok
370                //       -1  failed to converge
371                //       -2  singular problem
372                //       -3  not enough good data
373                NIterations = MaxNIterations;             // pass limits in
374                Convergence = ConvergenceLimit;
375                iret = AutonomousPRSolution(Tr, UseSat, SVP, pTropModel, Algebraic,
376                   NIterations, Convergence, Solution, Covariance, Residuals, Slopes,
377                   pDebugStream, ((hasGlonass && hasOther)?(&satSystems):(NULL)) );
378 
379                // ----------------------------------------------------------------
380                // Compute RMS residual...
381                if (!ResidualCriterion)
382                {
383                   // when 'distance from a priori' is the criterion.
384                   Vector<double> D=Solution-APrioriSolution;
385                   RMSResidual = RMS(D);
386                }
387                else
388                {
389                   // and in the usual case
390                   RMSResidual = RMS(Residuals);
391                }
392                // ... and find the maximum slope
393                MaxSlope = 0.0;
394                if (iret == 0)
395                {
396                   for (i=0; i<UseSat.size(); i++)
397                   {
398                      if (UseSat[i] && Slopes(i)>MaxSlope)
399                         MaxSlope=Slopes[i];
400                   }
401                }
402                // ----------------------------------------------------------------
403                // print solution with diagnostic information
404                if (Debug && pDebugStream)
405                {
406                   GPSWeekSecond weeksec(Tr);
407                   *pDebugStream << "RPS " << setw(2) << stage
408                      << " " << setw(4) << weeksec.week
409                      << " " << fixed << setw(10) << setprecision(3) << weeksec.sow
410                      << " " << setw(2) << N-stage << setprecision(6)
411                      << " " << setw(16) << Solution(0)
412                      << " " << setw(16) << Solution(1)
413                      << " " << setw(16) << Solution(2)
414                      << " " << setw(14) << Solution(3);
415                   if (hasGlonass && hasOther)
416                      *pDebugStream << " " << setw(16) << Solution(4);
417                   *pDebugStream << " " << setw(12) << RMSResidual
418                      << " " << fixed << setw(5) << setprecision(1) << MaxSlope
419                      << " " << NIterations
420                      << " " << scientific << setw(8) << setprecision(2)<< Convergence;
421                      // print the SatID for good sats
422                   for (i=0; i<UseSat.size(); i++)
423                   {
424                      if (UseSat[i])
425                         *pDebugStream << " " << setw(3)<< Satellite[i].id;
426                      else
427                         *pDebugStream << " " << setw(3) << -::abs(Satellite[i].id);
428                   }
429                      // also print the return value
430                   *pDebugStream << " (" << iret << ")" << endl;
431                }// end debug print
432 
433                // ----------------------------------------------------------------
434                // deal with the results of AutonomousPRSolution()
435                if (iret)
436                {     // failure for this combination
437                   RMSResidual = 0.0;
438                   Solution = 0.0;
439                }
440                else
441                {  // success
442                      // save 'best' solution for later
443                   if (BestRMS < 0.0 || RMSResidual < BestRMS)
444                   {
445                      BestRMS = RMSResidual;
446                      BestSol = Solution;
447                      BestUse = UseSat;
448                      BestSL = MaxSlope;
449                      BestConv = Convergence;
450                      BestNIter = NIterations;
451                   }
452                      // quit immediately?
453                   if ((stage==0 || ReturnAtOnce) && RMSResidual < RMSLimit)
454                      break;
455                }
456 
457                // get the next Combinations and repeat
458             } while(Combo.Next() != -1);
459 
460             // end of the stage
461             if (BestRMS > 0.0 && BestRMS < RMSLimit)
462             {     // success
463                iret=0;
464                break;
465             }
466 
467             // go to next stage
468             stage++;
469             if (stage > Nreject)
470                break;
471 
472          } while(iret == 0);        // end loop over stages
473 
474          // ----------------------------------------------------------------
475          // copy out the best solution and return
476          Convergence = BestConv;
477          NIterations = BestNIter;
478          RMSResidual = BestRMS;
479          Solution = BestSol;
480          MaxSlope = BestSL;
481          Covariance =BestCovariance;
482          for (Nsvs=0,i=0; i<BestUse.size(); i++)
483          {
484             if (!BestUse[i])
485                Satellite[i].id = -::abs(Satellite[i].id);
486             else
487                Nsvs++;
488          }
489 
490          if (iret==0 && BestSL > SlopeLimit)
491             iret=1;
492          if (iret==0 && BestSL > SlopeLimit/2.0 && Nsvs == 5)
493             iret=1;
494          if (iret>=0 && BestRMS >= RMSLimit)
495             iret=2;
496 
497          if (iret==0)
498             Valid=true;
499 
500          return iret;
501       }
502       catch(Exception& e)
503       {
504          GPSTK_RETHROW(e);
505       }
506    }  // end PRSolutionLegacy::RAIMCompute()
507 
508 ///-------------------------------------------------------------------------///
509 ///----------------------PrepareAutonomousSolution--------------------------///
510 ///-------------------------------------------------------------------------///
511 
PrepareAutonomousSolution(const CommonTime & Tr,vector<SatID> & Satellite,const vector<double> & Pseudorange,const XvtStore<SatID> & Eph,Matrix<double> & SVP,ostream * pDebugStream)512    int PRSolutionLegacy::PrepareAutonomousSolution(const CommonTime& Tr,
513                                                    vector<SatID>& Satellite,
514                                                    const vector<double>& Pseudorange,
515                                                    const XvtStore<SatID>& Eph,
516                                                    Matrix<double>& SVP,
517                                                    ostream *pDebugStream)
518       throw()
519    {
520       int i,j,nsvs,N=Satellite.size();
521       CommonTime tx;                // transmit time
522       Xvt PVT;
523 
524       if (N <= 0)
525          return 0;
526       SVP = Matrix<double>(N,4);
527       SVP = 0.0;
528 
529       for (nsvs=0,i=0; i<N; i++)
530       {
531             // skip marked satellites
532          if (Satellite[i].id <= 0) continue;
533 
534             // test the system
535             if(Satellite[i].system == SatelliteSystem::GPS)
536                ;
537             else {
538                Satellite[i].id = -::abs(Satellite[i].id);
539                if(pDebugStream) *pDebugStream
540                   << "Warning: Ignoring satellite (system) " << Satellite[i];
541                continue;
542             }
543 
544 
545             // first estimate of transmit time
546          tx = Tr;
547          tx -= Pseudorange[i]/C_MPS;
548             // get ephemeris range, etc
549          try
550          {
551             PVT = Eph.getXvt(Satellite[i], tx);
552          }
553          catch(InvalidRequest& e)
554          {
555             ///Negate SatID because getXvt failed.
556             #ifdef DEBUG_PRINT_WARNINGS
557                cout << "Eph.getXvt(Satellite[" << i << "], tx); threw InvalidRequest:" << endl;
558                cout << e << endl << endl;
559             #endif
560             Satellite[i].id = -::abs(Satellite[i].id);
561                if(pDebugStream) *pDebugStream
562                   << "Warning: PRSolutionLegacy ignores satellite (ephemeris) "
563                   << Satellite[i] << endl;
564             continue;
565          }
566 
567             // update transmit time and get ephemeris range again
568          tx -= PVT.clkbias + PVT.relcorr;     // clk+rel
569          try
570          {
571             PVT = Eph.getXvt(Satellite[i], tx);
572          }
573          catch(InvalidRequest& e)
574          {
575             ///Negate SatID because getXvt failed.
576             Satellite[i].id = -::abs(Satellite[i].id);
577             continue;
578          }
579 
580             // SVP = {SV position at transmit time}, raw range + clk + rel
581          for (j=0; j<3; j++)
582          {
583             SVP(i,j) = PVT.x[j];
584          }
585          SVP(i,3) = Pseudorange[i] + C_MPS * (PVT.clkbias + PVT.relcorr);
586          nsvs++;
587       }
588 
589       if (nsvs == 0)
590          return -4;
591       return 0;
592    } // end PrepareAutonomousPRSolution
593 
594 ///-------------------------------------------------------------------------///
595 ///-------------------------------------------------------------------------///
596 
AlgebraicSolution(Matrix<double> & A,Vector<double> & Q,Vector<double> & X,Vector<double> & R)597    int PRSolutionLegacy::AlgebraicSolution(Matrix<double>& A,
598                                            Vector<double>& Q,
599                                            Vector<double>& X,
600                                            Vector<double>& R)
601    {
602        try
603        {
604          int N=A.rows();
605          Matrix<double> AT=transpose(A);
606          Matrix<double> B=AT,C(4,4);
607 
608          C = AT * A;
609          // invert
610          try
611          {
612             //double big,small;
613             //condNum(C,big,small);
614             //if (small < 1.e-15 || big/small > 1.e15) return -2;
615             C = inverseSVD(C);
616          }
617          catch(SingularMatrixException& sme)
618          {
619             return -2;
620          }
621 
622          B = C * AT;
623 
624          Vector<double> One(N,1.0),V(4),U(4);
625          double E,F,G,d,lam;
626 
627          U = B * One;
628          V = B * Q;
629          E = Minkowski(U,U);
630          F = Minkowski(U,V) - 1.0;
631          G = Minkowski(V,V);
632          d = F*F-E*G;
633             // avoid imaginary solutions: what does this really mean?
634          if (d < 0.0)
635             d=0.0;
636          d = SQRT(d);
637 
638             // first solution ...
639          lam = (-F+d)/E;
640          X = lam*U + V;
641          X(3) = -X(3);
642             // ... and its residual
643          R(0) = A(0,3)-X(3) - RSS(X(0)-A(0,0), X(1)-A(0,1), X(2)-A(0,2));
644 
645             // second solution ...
646          lam = (-F-d)/E;
647          X = lam*U + V;
648          X(3) = -X(3);
649             // ... and its residual
650          R(1) = A(0,3)-X(3) - RSS(X(0)-A(0,0), X(1)-A(0,1), X(2)-A(0,2));
651 
652             // pick the right solution
653          if (ABS(R(1)) > ABS(R(0)))
654          {
655             lam = (-F+d)/E;
656             X = lam*U + V;
657             X(3) = -X(3);
658          }
659 
660             // compute the residuals
661          for (int i=0; i<N; i++)
662          {
663             R(i) = A(i,3)-X(3) - RSS(X(0)-A(i,0), X(1)-A(i,1), X(2)-A(i,2));
664          }
665 
666          return 0;
667 
668       }
669       catch(Exception& e)
670       {
671          GPSTK_RETHROW(e);
672       }
673    }  // end PRSolutionLegacy::AlgebraicSolution
674 
675 ///-------------------------------------------------------------------------///
676 ///--------------------------AutonomousPRSolution---------------------------///
677 ///-------------------------------------------------------------------------///
678 
AutonomousPRSolution(const CommonTime & T,const vector<bool> & Use,const Matrix<double> SVP,TropModel * pTropModel,const bool Algebraic,int & n_iterate,double & converge,Vector<double> & Sol,Matrix<double> & Cov,Vector<double> & Resid,Vector<double> & Slope,ostream * pDebugStream,Vector<int> * satSystems)679    int PRSolutionLegacy::AutonomousPRSolution(const CommonTime& T,
680                                              const vector<bool>& Use,
681                                              const Matrix<double> SVP,
682                                              TropModel *pTropModel,
683                                              const bool Algebraic,
684                                              int& n_iterate,
685                                              double& converge,
686                                              Vector<double>& Sol,
687                                              Matrix<double>& Cov,
688                                              Vector<double>& Resid,
689                                              Vector<double>& Slope,
690                                              ostream *pDebugStream,
691                                              Vector<int>* satSystems)   ///Change
692    {
693       if (!pTropModel)
694       {
695          Exception e("Undefined tropospheric model");
696          GPSTK_THROW(e);
697       }
698 
699       int size;
700       if (satSystems != NULL)
701          size = 5;
702       else
703          size = 4;
704 
705       try
706       {
707          int iret,j,n,N;
708          size_t i;
709          double rho,wt,svxyz[3];
710          GPSEllipsoid ell;               // WGS84?
711 
712          //if (pDebugStream)
713          //   *pDebugStream << "Enter APRS " << n_iterate << " "
714          //                << scientific << setprecision(3) << converge << endl;
715 
716             // find the number of good satellites
717          for (N=0,i=0; i<Use.size(); i++)
718          {
719             if (Use[i])
720                N++;
721          }
722          if ((satSystems != NULL) && N < 5)
723             return -3;
724          else if (N < 4)
725             return -3;
726 
727             // define for computation
728          Vector<double> CRange(N),dX(size);
729          Matrix<double> P(N,size),PT,G(size,N),PG(N,N);
730          Xvt SV,RX;
731 
732          Sol.resize(size);
733          Cov.resize(size,size);
734          Resid.resize(N);
735          Slope.resize(Use.size());
736 
737             // define for algebraic solution
738          Vector<double> U(size),Q(N);
739          Matrix<double> A(P);
740             // and for linearized least squares
741          int niter_limit = (n_iterate < 2 ? 2 : n_iterate);
742          double conv_limit = converge;
743 
744             // prepare for iteration loop
745          Sol = 0.0;                                // initial guess: center of earth
746          n_iterate = 0;
747          converge = 0.0;
748 
749             // iteration loop
750             // do at least twice (even for algebraic solution) so that
751             // trop model gets evaluated
752           //bool applied_trop;
753           do {
754             //applied_trop = true;
755 
756 
757                // current estimate of position solution
758             for(i=0; i<3; i++) RX.x[i]=Sol(i);
759 
760                // loop over satellites, computing partials matrix
761             for (n=0,i=0; i<Use.size(); i++)
762             {
763                   // ignore marked satellites
764                if (!Use[i])
765                   continue;
766 
767                   // time of flight (sec)
768                if (n_iterate == 0)
769                   rho = 0.070;             // initial guess: 70ms
770                else
771                   rho = RSS(SVP(i,0)-Sol(0), SVP(i,1)-Sol(1), SVP(i,2)-Sol(2))
772                             / ell.c();
773 
774                   // correct for earth rotation
775                wt = ell.angVelocity()*rho;             // radians
776                svxyz[0] =  ::cos(wt)*SVP(i,0) + ::sin(wt)*SVP(i,1);
777                svxyz[1] = -::sin(wt)*SVP(i,0) + ::cos(wt)*SVP(i,1);
778                svxyz[2] = SVP(i,2);
779 
780                   // corrected pseudorange (m)
781                CRange(n) = SVP(i,3);
782 
783                   // correct for troposphere (but not on the first iteration)
784                if (n_iterate > 0)
785                {
786                   SV.x[0] = svxyz[0];
787                   SV.x[1] = svxyz[1];
788                   SV.x[2] = svxyz[2];
789                   // must test RX for reasonableness to avoid corrupting TropModel
790                   Position R(RX),S(SV);
791                   double tc=R.getHeight(), elev = R.elevation(SV);
792                   if (elev < 0.0 || tc > 10000.0 || tc < -1000) {
793                      tc=0.0;
794                      //applied_trop = false;
795                   }
796                   else tc = pTropModel->correction(R,S,T);
797                   //if(pDebugStream) *pDebugStream << "Trop " << i << " "
798                   //   << fixed << setprecision(3) << tc << endl;
799                   CRange(n) -= tc;
800 
801                }
802 
803                   // geometric range
804                rho = RSS(svxyz[0]-Sol(0),svxyz[1]-Sol(1),svxyz[2]-Sol(2));
805 
806                   // partials matrix
807                P(n,0) = (Sol(0)-svxyz[0])/rho;           // x direction cosine
808                P(n,1) = (Sol(1)-svxyz[1])/rho;           // y direction cosine
809                P(n,2) = (Sol(2)-svxyz[2])/rho;           // z direction cosine
810                P(n,3) = 1.0;
811                if (satSystems != NULL)
812                   P(n,4) = (*satSystems)[i]; // 1 if GLONASS, 0 if GPS
813 
814                   // data vector: corrected range residual
815                Resid(n) = CRange(n) - rho - Sol(3);
816                if (satSystems != NULL && (*satSystems)[i] == 1)
817                {
818                   Resid(n) -= Sol(4);
819                }
820 
821                   // TD: allow weight matrix = measurement covariance
822                // P *= MCov;
823                // Resid *= MCov;
824 
825                   // compute intermediate quantities for algebraic solution
826                if (satSystems == NULL && Algebraic)
827                {
828                   U(0) = A(n,0) = svxyz[0];
829                   U(1) = A(n,1) = svxyz[1];
830                   U(2) = A(n,2) = svxyz[2];
831                   U(3) = A(n,3) = CRange(n);
832                   U(4) = A(n,4) = P(n,4);
833                   Q(n) = 0.5 * Minkowski(U,U);
834                }
835 
836                n++;        // n is number of good satellites - used for Slope
837             }  // end loop over satellites
838 
839                // compute information matrix = inverse covariance matrix
840             PT = transpose(P);
841             Cov = PT * P;
842 
843                // invert using SVD
844             //double big,small;
845             //condNum(PT*P,big,small);
846             //if (small < 1.e-15 || big/small > 1.e15)
847             //   return -2;
848             try
849             {
850                Cov = inverseSVD(Cov);
851             }
852             //try { Cov = inverseLUD(Cov); }
853             catch(SingularMatrixException& sme)
854             {
855                return -2;
856             }
857 
858                // generalized inverse
859             G = Cov * PT;
860             PG = P * G;                         // used for Slope
861 
862             n_iterate++;                        // increment number iterations
863 
864                // ----------------- algebraic solution -----------------------
865             if (satSystems == NULL && Algebraic)
866             {
867                iret = PRSolutionLegacy::AlgebraicSolution(A,Q,Sol,Resid);
868                if (iret)
869                   return iret;                     // (singular)
870                if (n_iterate > 1)
871                {                       // need 2, for trop
872                   iret = 0;
873                   break;
874                }
875             }
876                // ----------------- linearized least squares solution --------
877             else
878             {
879                dX = G * Resid;
880                Sol += dX;
881                   // test for convergence
882                converge = norm(dX);
883                   // success: quit
884                if (n_iterate > 1 && converge < conv_limit)
885                {
886                   iret = 0;
887                   break;
888                }
889                   // failure: quit
890                if (n_iterate >= niter_limit || converge > 1.e10)
891                {
892                   iret = -1;
893                   break;
894                }
895             }
896 
897          }
898 
899         while(1);    // end iteration loop
900 
901          //if(!applied_trop && pDebugStream)
902             //*pDebugStream << "Warning - trop correction not applied at time "
903                //<< T.printf("%4F %10.3g\n");
904 
905 
906 
907             // compute slopes
908          Slope = 0.0;
909          if (iret == 0)
910          {
911             for (j=0,i=0; i<Use.size(); i++)
912             {
913                if (!Use[i])
914                   continue;
915                for (int k=0; k<4; k++)
916                {
917                   Slope(i) += G(k,j)*G(k,j);
918                }
919                Slope(i) = SQRT(Slope(i)*double(n-4)/(1.0-PG(j,j)));
920                j++;
921             }
922          }
923 
924          return iret;
925       }
926       catch(Exception& e)
927       {
928          GPSTK_RETHROW(e);
929       }
930    } // end PRSolutionLegacy::AutonomousPRSolution
931 
932 ///-------------------------------------------------------------------------///
933 ///-------------------------------------------------------------------------///
934 
935 } // namespace gpstk
936