SphericalHarmonicGravity.cpp

Go to the documentation of this file.
00001 #pragma ident "$Id: SphericalHarmonicGravity.cpp 2457 2010-08-18 14:20:12Z coandrei $"
00002 
00008 //============================================================================
00009 //
00010 //  This file is part of GPSTk, the GPS Toolkit.
00011 //
00012 //  The GPSTk is free software; you can redistribute it and/or modify
00013 //  it under the terms of the GNU Lesser General Public License as published
00014 //  by the Free Software Foundation; either version 2.1 of the License, or
00015 //  any later version.
00016 //
00017 //  The GPSTk is distributed in the hope that it will be useful,
00018 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00019 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020 //  GNU Lesser General Public License for more details.
00021 //
00022 //  You should have received a copy of the GNU Lesser General Public
00023 //  License along with GPSTk; if not, write to the Free Software Foundation,
00024 //  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00025 //
00026 //  Wei Yan - Chinese Academy of Sciences . 2009, 2010
00027 //
00028 //============================================================================
00029 
00030 
00031 
00032 #include "SphericalHarmonicGravity.hpp"
00033 #include "ASConstant.hpp"
00034 #include "IERS.hpp"
00035 #include "ReferenceFrames.hpp"
00036 #include "SpecialFunctions.hpp"
00037 
00038 namespace gpstk
00039 {
00040       /* Constructor.
00041        * @param n Desired degree.
00042        * @param m Desired order.
00043        */
00044    SphericalHarmonicGravity::SphericalHarmonicGravity(int n, int m)
00045       : desiredDegree(n),
00046         desiredOrder(m),
00047         correctSolidTide(false),
00048         correctOceanTide(false),
00049         correctPoleTide(false)
00050    {
00051       const int size = desiredDegree;
00052 
00053       V.resize( size + 3, size + 3, 0.0);
00054       W.resize( size + 3, size + 3, 0.0);
00055 
00056       //Sn0.resize(gmData.maxDegree, 0.0);
00057 
00058    }
00059 
00060    
00061       /* Evaluates the two harmonic functions V and W.
00062        * @param r ECI position vector.
00063        * @param E ECI to ECEF transformation matrix.
00064        */
00065    void SphericalHarmonicGravity::computeVW(Vector<double> r, Matrix<double> E)
00066    {   
00067       // dimension should be checked here
00068       // I'll do it latter...
00069       if((r.size()!=3) || (E.rows()!=3) || (E.cols()!=3))
00070       {
00071          Exception e("Wrong input for computeVW");
00072          GPSTK_THROW(e);
00073       }
00074 
00075       // Rotate from ECI to ECEF
00076       Vector<double> r_bf = E * r; 
00077 
00078       const double R_ref = gmData.refDistance;
00079 
00080       // Auxiliary quantities
00081       double r_sqr =  dot(r_bf, r_bf);
00082       double rho   =  R_ref * R_ref / r_sqr;
00083 
00084       // Normalized coordinates
00085       double x0 = R_ref * r_bf(0) / r_sqr;          
00086       double y0 = R_ref * r_bf(1) / r_sqr;   
00087       double z0 = R_ref * r_bf(2) / r_sqr;
00088 
00089 
00090       //
00091       // Evaluate harmonic functions 
00092       //   V_nm = (R_ref/r)^(n+1) * P_nm(sin(phi)) * cos(m*lambda)
00093       // and 
00094       //   W_nm = (R_ref/r)^(n+1) * P_nm(sin(phi)) * sin(m*lambda)
00095       // up to degree and order n_max+1
00096       //
00097 
00098       // Calculate zonal terms V(n,0); set W(n,0)=0.0
00099       V[0][0] = R_ref / std::sqrt(r_sqr);
00100       W[0][0] = 0.0;
00101 
00102       V[1][0] = z0 * V[0][0];
00103       W[1][0] = 0.0;
00104 
00105       for(int n = 2; n <= (desiredDegree+2); n++) 
00106       {
00107          V[n][0] = ((2*n - 1) * z0 * V[n-1][0] - (n - 1) * rho * V[n-2][0]) /n;
00108          W[n][0] = 0.0;
00109       }
00110 
00111       // Calculate tesseral and sectorial terms
00112       for (int m = 1; m <= (desiredOrder+2); m++) 
00113       {
00114          // Calculate V(m,m) .. V(n_max+1,m)
00115 
00116          V[m][m] = (2 * m - 1) * ( x0 * V[m-1][m-1] - y0 * W[m-1][m-1] );
00117          W[m][m] = (2 * m - 1) * ( x0 * W[m-1][m-1] + y0 * V[m-1][m-1] );
00118 
00119          if (m <= (desiredDegree+1) ) 
00120          {
00121             V[m+1][m] = (2 * m + 1) * z0 * V[m][m];
00122             W[m+1][m] = (2 * m + 1) * z0 * W[m][m];
00123          }
00124 
00125          for (int n = (m+2); n <= (desiredDegree+2); n++) 
00126          {
00127             V[n][m] = ((2*n-1)*z0*V[n-1][m] - (n+m-1)*rho*V[n-2][m]) / (n-m);
00128             W[n][m] = ((2*n-1)*z0*W[n-1][m] - (n+m-1)*rho*W[n-2][m]) / (n-m);
00129          }
00130 
00131       }  // End 'for (int m = 1; m <= (desiredOrder + 2); m++) '
00132 
00133    }  // End of method 'SphericalHarmonicGravity::computeVW()'
00134 
00135 
00136       /* Computes the acceleration due to gravity in m/s^2.
00137        * @param r ECI position vector.
00138        * @param E ECI to ECEF transformation matrix.
00139        * @return ECI acceleration in m/s^2.
00140        */
00141    Vector<double> SphericalHarmonicGravity::gravity(Vector<double> r, Matrix<double> E)
00142    {
00143       // dimension should be checked here
00144       // I'll do it latter...
00145       if((r.size()!=3) || (E.rows()!=3) || (E.cols()!=3))
00146       {
00147          Exception e("Wrong input for computeVW");
00148          GPSTK_THROW(e);
00149       }
00150 
00151       Matrix<double> CS = gmData.unnormalizedCS;
00152 
00153    
00154       // Calculate accelerations ax,ay,az
00155       double ax(0.0), ay(0.0), az(0.0);
00156 
00157       for (int m = 0; m <= desiredOrder; m++)
00158       {
00159          for (int n = m; n <= desiredDegree; n++)
00160          {
00161             if (m==0) 
00162             {
00163                double C = CS[n][0];               // = C_n,0
00164 
00165                ax -=       C * V[n+1][1];
00166                ay -=       C * W[n+1][1];
00167                az -= (n+1)*C * V[n+1][0];
00168             }
00169             else 
00170             {
00171                double C = CS[n][m];   // = C_n,m
00172                double S = CS[m-1][n]; // = S_n,m
00173                double Fac = 0.5 * (n-m+1) * (n-m+2);
00174                
00175                ax += 0.5*(-C*V[n+1][m+1] - S*W[n+1][m+1]) + Fac*(C*V[n+1][m-1] + S*W[n+1][m-1]);
00176                ay += 0.5*(-C*W[n+1][m+1] + S*V[n+1][m+1]) + Fac*(-C*W[n+1][m-1] + S*V[n+1][m-1]);
00177                az += (n-m+1)*(-C*V[n+1][m] - S*W[n+1][m]);
00178             }
00179 
00180          }  // End of 'for (int n = m; n <= (desiredDegree+1) ; n++)'
00181 
00182       }  // End of 'for (int m = 0; m <= (desiredOrder+1); m++)'
00183 
00184       // Body-fixed acceleration
00185       Vector<double> a_bf(3,0.0);
00186       a_bf(0) = ax;
00187       a_bf(1) = ay;
00188       a_bf(2) = az;
00189 
00190       a_bf = a_bf * ( gmData.GM / (gmData.refDistance * gmData.refDistance) );
00191 
00192       // Inertial acceleration
00193       Matrix<double> Etrans = transpose(E);
00194       Vector<double> out = Etrans * a_bf;            // this line may be wrong  matrix * vector
00195 
00196       return out;
00197 
00198    }  // End of method 'SphericalHarmonicGravity::gravity'
00199 
00200 
00201       /* Computes the partial derivative of gravity with respect to position.
00202        * @return ECI gravity gradient matrix.
00203        * @param r ECI position vector.
00204        * @param E ECI to ECEF transformation matrix.
00205        */
00206    Matrix<double> SphericalHarmonicGravity::gravityGradient(gpstk::Vector<double> r, gpstk::Matrix<double> E)
00207    {
00208       // dimension should be checked here
00209       // I'll do it latter...
00210       if((r.size()!=3) || (E.rows()!=3) || (E.cols()!=3))
00211       {
00212          Exception e("Wrong input for gravityGradient");
00213          GPSTK_THROW(e);
00214       }
00215 
00216       Matrix<double> CS = gmData.unnormalizedCS;
00217 
00218    
00219       double xx = 0.0;     
00220       double xy = 0.0;
00221       double xz = 0.0;
00222       double yy = 0.0;
00223       double yz = 0.0;
00224       double zz = 0.0;
00225 
00226       Matrix<double> out(3, 3, 0.0);
00227 
00228       for (int m = 0; m <= desiredOrder; m++) 
00229       {
00230          for (int n = m; n <= desiredDegree; n++) 
00231          {
00232             double Fac = (n-m+2)*(n-m+1);
00233             
00234             double C = CS[n][m];
00235             double S = (m==0) ? 0.0 : CS[m-1][n];   // yan changed
00236             //S = (m==0)?Sn0(n):CS[m-1][n];   // yan changed
00237 
00238             zz += Fac*(C*V[n+2][m] + S*W[n+2][m]);
00239 
00240             if (m==0) 
00241             {
00242                C = CS[n][0];   // = C_n,0
00243 
00244                Fac = (n+2)*(n+1);
00245                xx += 0.5 * (C*V[n+2][2] - Fac*C*V[n+2][0]);
00246                xy += 0.5 * C * W[n+2][2];
00247                
00248                Fac = n + 1;
00249                xz += Fac * C * V[n+2][1];
00250                yz += Fac * C * W[n+2][1];
00251             }
00252             if (m > 0)
00253             {
00254                C = CS[n][m];
00255                S = CS[m-1][n];
00256                
00257                double f1 = 0.5*(n-m+1);
00258                double f2 = (n-m+3)*(n-m+2)*f1;
00259 
00260                xz += f1*(C*V[n+2][m+1]+S*W[n+2][m+1])-f2*(C*V[n+2][m-1]+S*W[n+2][m-1]);
00261                yz += f1*(C*W[n+2][m+1]-S*V[n+2][m+1])+f2*(C*W[n+2][m-1]-S*V[n+2][m-1]);         //* bug in JAT, I fix it
00262           
00263                if (m == 1)
00264                {
00265                   Fac = (n+1)*n;
00266                   xx += 0.25*(C*V[n+2][3]+S*W[n+2][3]-Fac*(3.0*C*V[n+2][1]+S*W[n+2][1]));
00267                   xy += 0.25*(C*W[n+2][3]-S*V[n+2][3]-Fac*(C*W[n+2][1]+S*V[n+2][1]));
00268                }
00269                if (m > 1) 
00270                {
00271                   f1 = 2.0*(n-m+2)*(n-m+1);
00272                   f2 = (n-m+4)*(n-m+3)*f1*0.5;
00273                   xx += 0.25*(C*V[n+2][m+2]+S*W[n+2][m+2]-f1*(C*V[n+2][m]+S*W[n+2][m])+f2*(C*V[n+2][m-2]+S*W[n+2][m-2]));
00274 
00275                   xy += 0.25*(C*W[n+2][m+2]-S*V[n+2][m+2]+f2*(-C*W[n+2][m-2]+S*V[n+2][m-2]));
00276                }
00277             }
00278          }
00279          yy = -xx - zz;
00280          
00281          out(0,0) = xx;
00282          out(0,1) = xy;
00283          out(0,2) = xz;
00284          out(1,0) = xy;
00285          out(1,1) = yy;
00286          out(1,2) = yz;
00287          out(2,0) = xz;
00288          out(2,1) = yz;
00289          out(2,2) = zz;
00290 
00291       }  // for (int m = 0; m <= desiredOrder; m++) 
00292 
00293       const double R_ref = gmData.refDistance;
00294       out = out * (gmData.GM / (R_ref * R_ref * R_ref));
00295 
00296       // Rotate to ECI
00297       Matrix<double> Etrans = transpose(E);
00298       out = Etrans*(out*E);
00299 
00300       return out;         // the result should be checked
00301 
00302    }  // End of 'SphericalHarmonicGravity::gravityGradient()'
00303 
00304    
00305       
00312    void SphericalHarmonicGravity::doCompute(UTCTime utc, EarthBody& rb, Spacecraft& sc)
00313    {
00314 
00315       Matrix<double> C2T = ReferenceFrames::J2kToECEFMatrix(utc);
00316 
00317       /*
00318       // debuging
00319       -0.96093274494562253,0.27678089792921495,0.00077086494829907383
00320       -0.27678077751454710,-0.96093305341706370,0.00026086203590256260
00321       0.00081295123707397028,3.7310272463024317e-005,0.99999966885906000
00322       
00323       C2T(0,0) = -0.96093274494562253;
00324       C2T(0,1) = 0.27678089792921495;
00325       C2T(0,2) = 0.00077086494829907383;
00326 
00327       C2T(1,0) = -0.27678077751454710;
00328       C2T(1,1) = -0.96093305341706370;
00329       C2T(1,2) = 0.00026086203590256260;
00330 
00331       C2T(2,0) = 0.00081295123707397028;
00332       C2T(2,1) = 3.7310272463024317e-005;
00333       C2T(2,2) = 0.99999966885906000;*/
00334       
00335       // corrcet earth tides
00336       correctCSTides(utc, correctSolidTide, correctOceanTide, correctPoleTide);
00337 
00338       // Evaluate harmonic functions
00339       computeVW(sc.R(), C2T);         // update VM
00340 
00341       // a
00342       a = gravity(sc.R(), C2T);
00343       
00344       // da_dr
00345       da_dr = gravityGradient(sc.R(), C2T);
00346       
00347       //da_dv
00348       da_dv.resize(3,3,0.0);
00349       
00350       //da_dp
00351       
00352    }
00353 
00354    // Correct tides to coefficients 
00355    void SphericalHarmonicGravity::correctCSTides(UTCTime t,bool solidFlag,bool oceanFlag,bool poleFlag)
00356    {
00357       // copy CS
00358       Matrix<double> CS = gmData.unnormalizedCS;
00359       Vector<double> Sn0(CS.rows(),0.0);
00360 
00361       // 
00362       double mjd = t.MJD();
00363       double leapYears = (mjd-gmData.refMJD)/365.25;
00364 
00365       double detC20 = normFactor(2,0)*leapYears*gmData.dotC20;
00366       double detC21 = normFactor(2,1)*leapYears*gmData.dotC21;
00367       double detS21 = normFactor(2,1)*leapYears*gmData.dotS21;
00368 
00369       CS(2,0) += detC20;
00370       CS(2,1) += detC21;
00371       CS(0,2) += detS21;
00372       
00373       // correct solid tide
00374       if(solidFlag)
00375       {
00376          // C20 C21 C22 C30 C31 C32 C33 C40 C41 C42
00377          double dc[10] = {0.0};
00378          double ds[10] = {0.0};
00379          solidTide.getSolidTide(t.mjdUTC(),dc,ds);
00380 
00381          // c
00382          CS(2,0) += normFactor(2,0)*dc[0];
00383          CS(2,1) += normFactor(2,1)*dc[1];
00384          CS(2,2) += normFactor(2,2)*dc[2];
00385          CS(3,0) += normFactor(3,0)*dc[3];
00386          CS(3,1) += normFactor(3,1)*dc[4];
00387          CS(3,2) += normFactor(3,2)*dc[5];
00388          CS(3,3) += normFactor(3,3)*dc[6];
00389          CS(4,0) += normFactor(4,0)*dc[7];
00390          CS(4,1) += normFactor(4,1)*dc[8];
00391          CS(4,2) += normFactor(4,2)*dc[9];
00393          Sn0(2)  += normFactor(2,0)*ds[0];   // s20
00394          CS(0,2) += normFactor(2,1)*ds[1];
00395          CS(1,2) += normFactor(2,2)*ds[2];
00396          Sn0(3)  += normFactor(3,0)*ds[3];   // s30
00397          CS(0,3) += normFactor(3,1)*ds[4];
00398          CS(1,3) += normFactor(3,2)*ds[5];
00399          CS(2,3) += normFactor(3,3)*ds[6];   
00400          Sn0(4)  += normFactor(4,0)*ds[7];   // s40
00401          CS(0,4) += normFactor(4,1)*ds[8];
00402          CS(1,4) += normFactor(4,2)*ds[9];
00403 
00404       }
00405       
00406       // correct ocean tide
00407       if(oceanFlag)
00408       {
00409          // C20 C21 C22 C30 C31 C32 C33 C40 C41 C42 C43 C44
00410          double dc[12] = {0.0};
00411          double ds[12] = {0.0};
00412          oceanTide.getOceanTide(t.mjdUTC(),dc,ds);
00413          
00414          // c
00415          CS(2,0) += normFactor(2,0)*dc[0];
00416          CS(2,1) += normFactor(2,1)*dc[1];
00417          CS(2,2) += normFactor(2,2)*dc[2];
00418          CS(3,0) += normFactor(3,0)*dc[3];
00419          CS(3,1) += normFactor(3,1)*dc[4];
00420          CS(3,2) += normFactor(3,2)*dc[5];
00421          CS(3,3) += normFactor(3,3)*dc[6];
00422          CS(4,0) += normFactor(4,0)*dc[7];
00423          CS(4,1) += normFactor(4,1)*dc[8];
00424          CS(4,2) += normFactor(4,2)*dc[9];
00425          CS(4,3) += normFactor(4,3)*dc[10];
00426          CS(4,4) += normFactor(4,4)*dc[11];
00427 
00428 
00430          Sn0(2)  += normFactor(2,0)*ds[0];   // s20
00431          CS(0,2) += normFactor(2,1)*ds[1];
00432          CS(1,2) += normFactor(2,2)*ds[2];
00433          Sn0(3)  += normFactor(3,0)*ds[3];   // s30
00434          CS(0,3) += normFactor(3,1)*ds[4];
00435          CS(1,3) += normFactor(3,2)*ds[5];
00436          CS(2,3) += normFactor(3,3)*ds[6];
00437          Sn0(4)  += normFactor(4,0)*ds[7];   // s40
00438          CS(1,4) += normFactor(4,1)*ds[8];
00439          CS(2,4) += normFactor(4,2)*ds[9];
00440          CS(3,4) += normFactor(4,1)*ds[10];
00441          CS(4,4) += normFactor(4,2)*ds[11];
00442 
00443       }
00444       
00445       // correct pole tide
00446       if(poleFlag)
00447       {
00448          double dC21=0.0;
00449          double dS21=0.0;
00450          poleTide.getPoleTide(t.mjdUTC(),dC21,dS21);
00451 
00452          CS(2,1) += normFactor(2,1)*dC21;
00453          CS(0,2) += normFactor(2,1)*dS21;
00454       }
00455 
00456    }  // End of method 'SphericalHarmonicGravity::correctCSTides()'
00457 
00458 
00459    double SphericalHarmonicGravity::normFactor(int n, int m) 
00460    {
00461       // The input should be n >= m >= 0
00462 
00463       double fac(1.0);
00464       for(int i = (n-m+1); i <= (n+m); i++)
00465       {
00466          fac = fac * double(i);
00467       }
00468 
00469       double delta  = (m == 0) ? 1.0 : 0.0;
00470 
00471       double num = (2.0 * n + 1.0) * (2.0 - delta);
00472 
00473       // We should make sure fac!=0, but it won't happen on the case,
00474       // so we just skip handling it
00475       double out = std::sqrt(num/fac);                  
00476       
00477       return out;
00478 
00479    }  // End of method 'SphericalHarmonicGravity::normFactor())
00480 
00481    void SphericalHarmonicGravity::test()
00482    {
00483 
00484       Vector<double> r(3,0.0);
00485       Matrix<double> E(3,3,0.0);
00486 
00487       r(0) = 6525.919e3;
00488       r(1) = 1710.416e3;
00489       r(2) = 2508.886e3;
00490 
00491       E = ident<double>(3);
00492 
00493       computeVW(r, E);         // update VM
00494 
00495       // a
00496       Vector<double> a = gravity(r, E);
00497 
00498       Matrix<double> da_dr = gravityGradient(r, E);
00499 
00500       cout<<setprecision(12)<<a<<endl;
00501       cout<<da_dr<<endl;
00502 
00503       int aaa = 0;
00504 
00505    }  // End of method 'SphericalHarmonicGravity::test()'
00506 
00507 }  // End of namespace 'gpstk'
00508 

Generated on Tue May 22 03:31:01 2012 for GPS ToolKit Software Library by  doxygen 1.3.9.1