SunEarthSatGeometry.cpp

Go to the documentation of this file.
00001 #pragma ident "$Id: SunEarthSatGeometry.cpp 3140 2012-06-18 15:03:02Z susancummins $"
00002 
00010 //============================================================================
00011 //
00012 //  This file is part of GPSTk, the GPS Toolkit.
00013 //
00014 //  The GPSTk is free software; you can redistribute it and/or modify
00015 //  it under the terms of the GNU Lesser General Public License as published
00016 //  by the Free Software Foundation; either version 2.1 of the License, or
00017 //  any later version.
00018 //
00019 //  The GPSTk is distributed in the hope that it will be useful,
00020 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00021 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00022 //  GNU Lesser General Public License for more details.
00023 //
00024 //  You should have received a copy of the GNU Lesser General Public
00025 //  License along with GPSTk; if not, write to the Free Software Foundation,
00026 //  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
00027 //
00028 //  Copyright 2004, The University of Texas at Austin
00029 //
00030 //============================================================================
00031 
00032 //============================================================================
00033 //
00034 //This software developed by Applied Research Laboratories at the University of
00035 //Texas at Austin, under contract to an agency or agencies within the U.S.
00036 //Department of Defense. The U.S. Government retains all rights to use,
00037 //duplicate, distribute, disclose, or release this software.
00038 //
00039 //Pursuant to DoD Directive 523024
00040 //
00041 // DISTRIBUTION STATEMENT A: This software has been approved for public
00042 //                           release, distribution is unlimited.
00043 //
00044 //=============================================================================
00045 
00046 // GPSTk includes
00047 #include "StringUtils.hpp"          // asString
00048 #include "geometry.hpp"             // DEG_TO_RAD
00049 #include "GNSSconstants.hpp"    // TWO_PI
00050 // geomatics
00051 #include "SunEarthSatGeometry.hpp"
00052 #include "SolarPosition.hpp"
00053 
00054 using namespace std;
00055 
00056 namespace gpstk {
00057 
00058 // -----------------------------------------------------------------------------------
00059 // Given a Position, compute unit (ECEF) vectors in the Up, East and North directions
00060 // at that position. Use geodetic coordinates, i.e. 'up' is perpendicular to the
00061 // geoid. Return the vectors in the form of a 3x3 Matrix<double>, this is in fact the
00062 // rotation matrix that will take an ECEF vector into an 'up-east-north' vector.
00063 Matrix<double> UpEastNorth(Position& P, bool geocentric) throw(Exception)
00064 {
00065 try {
00066    Matrix<double> R = NorthEastUp(P,geocentric);
00067    for(int i=0; i<3; i++) { double r=R(0,i); R(0,i)=R(2,i); R(2,i)=r; }
00068    return R;
00069 }
00070 catch(Exception& e) { GPSTK_RETHROW(e); }
00071 catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
00072 catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
00073 }
00074 
00075 // -----------------------------------------------------------------------------------
00076 // Same as UpEastNorth, but using geocentric coordinates, so that the -Up
00077 // direction will meet the center of Earth.
00078 Matrix<double> UpEastNorthGeocentric(Position& P) throw(Exception)
00079 {
00080    try { return UpEastNorth(P, true); }
00081    catch(Exception& e) { GPSTK_RETHROW(e); }
00082 }
00083 
00084 // -----------------------------------------------------------------------------------
00085 // Same as UpEastNorth(), but with rows re-ordered.
00086 Matrix<double> NorthEastUp(Position& P, bool geocentric) throw(Exception)
00087 {
00088 try {
00089    Matrix<double> R(3,3);
00090    P.transformTo(geocentric ? Position::Geodetic : Position::Geocentric);
00091 
00092    double lat = (geocentric ? P.getGeodeticLatitude() : P.getGeocentricLatitude())
00093       * DEG_TO_RAD;                                        // rad N
00094    double lon = P.getLongitude() * DEG_TO_RAD;             // rad E
00095    double ca = ::cos(lat);
00096    double sa = ::sin(lat);
00097    double co = ::cos(lon);
00098    double so = ::sin(lon);
00099 
00100    // This is the rotation matrix which will
00101    // transform  X=(x,y,z) into (R*X)(north,east,up)
00102    R(0,0) = -sa*co;  R(0,1) = -sa*so;  R(0,2) = ca;
00103    R(1,0) =    -so;  R(1,1) =     co;  R(1,2) = 0.;
00104    R(2,0) =  ca*co;  R(2,1) =  ca*so;  R(2,2) = sa;
00105 
00106    // The rows of R are also the unit vectors, in ECEF, of north,east,up;
00107    //  R = (N && E && U) = transpose(N || E || U).
00108    //Vector<double> N = R.rowCopy(0);
00109    //Vector<double> E = R.rowCopy(1);
00110    //Vector<double> U = R.rowCopy(2);
00111 
00112    return R;
00113 }
00114 catch(Exception& e) { GPSTK_RETHROW(e); }
00115 catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
00116 catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
00117 }
00118 
00119 // -----------------------------------------------------------------------------------
00120 // Same as UpEastNorthGeocentric(), but with rows re-ordered.
00121 Matrix<double> NorthEastUpGeocentric(Position& P) throw(Exception)
00122 {
00123    try { return NorthEastUp(P, true); }
00124    catch(Exception& e) { GPSTK_RETHROW(e); }
00125 }
00126 
00127 // -----------------------------------------------------------------------------------
00128 // Generate a 3x3 rotation Matrix, for direct rotations about one axis
00129 // (for XYZ, axis=123), given the rotation angle in radians;
00130 // @param angle in radians.
00131 // @param axis 1,2,3 as rotation about X,Y,Z.
00132 // @return Rotation matrix (3x3).
00133 // @throw InvalidInput if axis is anything other than 1, 2 or 3.
00134 Matrix<double> SingleAxisRotation(double angle, const int axis)
00135    throw(Exception)
00136 {
00137 try {
00138    if(axis < 1 || axis > 3) {
00139       Exception e(string("Invalid axis (1,2,3 <=> X,Y,Z): ")
00140                          + StringUtils::asString(axis));
00141       GPSTK_THROW(e);
00142    }
00143    Matrix<double> R(3,3,0.0);
00144 
00145    int i1=axis-1;                      // axis = 1 : 0,1,2
00146    int i2=i1+1; if(i2 == 3) i2=0;      // axis = 2 : 1,2,0
00147    int i3=i2+1; if(i3 == 3) i3=0;      // axis = 3 : 2,0,1
00148 
00149    R(i1,i1) = 1.0;
00150    R(i2,i2) = R(i3,i3) = ::cos(angle);
00151    R(i3,i2) = -(R(i2,i3) = ::sin(angle));
00152 
00153    return R;
00154 }
00155 catch(Exception& e) { GPSTK_RETHROW(e); }
00156 catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
00157 catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
00158 }
00159 
00160 //------------------------------------------------------------------------------------
00161 // Consider the sun and the earth as seen from the satellite. Let the sun be a circle
00162 // of angular radius r, center in direction s, and the earth be a (larger) circle
00163 // of angular radius R, center in direction e. The circles overlap if |e-s| < R+r;
00164 // complete overlap if |e-s| < R-r. The satellite is in penumbra if R-r < |e-s| < R+r,// it is in umbra if |e-s| < R-r.
00165 //    Let L == |e-s|. What is the area of overlap in penumbra : R-r < L < R+r ?
00166 // Call the two points where the circles intersect p1 and p2. Draw a line from e to s;
00167 // call the points where this line intersects the two circles r1 and R1, respectively.
00168 // Draw lines from e to s, e to p1, e to p2, s to p1 and s to p2. Call the angle
00169 // between e-s and e-p1 alpha, and that between s-e and s-p1, beta.
00170 // Draw a rectangle with top and bottom parallel to e-s passing through p1 and p2,
00171 // and with sides passing through s and r1. Similarly for e and R1. Note that the
00172 // area of intersection lies within the intersection of these two rectangles.
00173 // Call the area of the rectangle outside the circles A and B. The height H of the
00174 // rectangles is
00175 // H = 2Rsin(alpha) = 2rsin(beta)
00176 // also L = rcos(beta)+Rcos(alpha)
00177 // The area A will be the area of the rectangle
00178 //              minus the area of the wedge formed by the angle 2*alpha
00179 //              minus the area of the two triangles which meet at s :
00180 // A = RH - (2alpha/2pi)*pi*R*R - 2*(1/2)*(H/2)Rcos(alpha)
00181 // Similarly
00182 // B = rH - (2beta/2pi)*pi*r*r  - 2*(1/2)*(H/2)rcos(beta)
00183 // The area of intersection will be the area of the rectangular intersection
00184 //                            minus the area A
00185 //                            minus the area B
00186 // Intersection = H(R+r-L) - A - B
00187 //              = HR+Hr-HL -HR+alpha*R*R+(H/2)Rcos(alpha) -Hr+beta*r*r+(H/2)rcos(beta)
00188 // Cancel terms, and substitute for L using above equation L = ..
00189 //              = -(H/2)rcos(beta)-(H/2)Rcos(alpha)+alpha*R*R+beta*r*r
00190 // substitute for H/2
00191 //              = -R*R*sin(alpha)cos(alpha)-r*r*sin(beta)cos(beta)+alpha*R*R+beta*r*r
00192 // Intersection = R*R*[alpha-sin(alpha)cos(alpha)]+r*r*[beta-sin(beta)cos(beta)]
00193 // Solve for alpha and beta in terms of R, r and L using the H and L relations above
00194 // (r/R)cos(beta)=(L/R)-cos(alpha)
00195 // (r/R)sin(beta)=sin(alpha)
00196 // so
00197 // (r/R)^2 = (L/R)^2 - (2L/R)cos(alpha) + 1
00198 // cos(alpha) = (R/2L)(1+(L/R)^2-(r/R)^2)
00199 // cos(beta) = (L/r) - (R/r)cos(alpha)
00200 // and 0 <= alpha or beta <= pi
00201 //
00202 // AngRadEarth    angular radius of the earth as seen at the satellite
00203 // AngRadSun      angular radius of the sun as seen at the satellite
00204 // AngSeparation  angular distance of the sun from the earth
00205 // return         fraction (0 <= f <= 1) of area of sun covered by earth
00206 // units only need be consistent
00207 double ShadowFactor(double AngRadEarth, double AngRadSun, double AngSeparation)
00208 {
00209 try {
00210    if(AngSeparation >= AngRadEarth+AngRadSun) return 0.0;
00211    if(AngSeparation <= fabs(AngRadEarth-AngRadSun)) return 1.0;
00212    double r=AngRadSun, R=AngRadEarth, L=AngSeparation;
00213    if(AngRadSun > AngRadEarth) { r=AngRadEarth; R=AngRadSun; }
00214    double cosalpha = (R/L)*(1.0+(L/R)*(L/R)-(r/R)*(r/R))/2.0;
00215    double cosbeta = (L/r) - (R/r)*cosalpha;
00216    double sinalpha = ::sqrt(1-cosalpha*cosalpha);
00217    double sinbeta = ::sqrt(1-cosbeta*cosbeta);
00218    double alpha = ::asin(sinalpha);
00219    double beta = ::asin(sinbeta);
00220    double shadow = r*r*(beta-sinbeta*cosbeta)+R*R*(alpha-sinalpha*cosalpha);
00221    shadow /= ::acos(-1.0)*AngRadSun*AngRadSun;
00222    return shadow;
00223 }
00224 catch(Exception& e) { GPSTK_RETHROW(e); }
00225 catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
00226 catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
00227 }
00228 
00229 // -----------------------------------------------------------------------------------
00230 // Compute the satellite attitude, given the time and the satellite position SV.
00231 // If the SolarSystem is valid, use it; otherwise use SolarPosition.
00232 // See two versions of SatelliteAttitude() for the user interface.
00233 // Return a 3x3 Matrix which contains, as rows, the unit (ECEF) vectors X,Y,Z in the
00234 // body frame of the satellite, namely
00235 //    Z = along the boresight (i.e. towards Earth center),
00236 //    Y = perpendicular to both Z and the satellite-sun direction, and
00237 //    X = completing the orthonormal triad. X will generally point toward the sun.
00238 // Thus this rotation matrix R * (ECEF XYZ vector) = components in body frame, and
00239 // R.transpose() * (sat. body. frame vector) = ECEF XYZ components.
00240 // Also return the shadow factor = fraction of sun's area not visible to satellite.
00241 Matrix<double> doSatAtt(const CommonTime& tt, const Position& SV,
00242                         const SolarSystem& SSEph, const EarthOrientation& EO,
00243                         double& sf)
00244    throw(Exception)
00245 {
00246    try {
00247       int i;
00248       double d,svrange,DistSun,AngRadSun,AngRadEarth,AngSeparation;
00249       Position X,Y,Z,T,S,Sun;
00250       Matrix<double> R(3,3);
00251 
00252       // Z points from satellite to Earth center - along the antenna boresight
00253       Z = SV;
00254       Z.transformTo(Position::Cartesian);
00255       svrange = Z.mag();
00256       d = -1.0/Z.mag();
00257       Z = d * Z;                                // reverse and normalize Z
00258 
00259       // get the Sun's position
00260       if(SSEph.JPLNumber() > -1) {
00261          //SolarSystem& mySSEph=const_cast<SolarSystem&>(SSEph);
00262          Sun = const_cast<SolarSystem&>(SSEph).WGS84Position(SolarSystem::Sun,tt,EO);
00263       }
00264       else {
00265          double AR;
00266          Sun = SolarPosition(tt, AR);
00267       }
00268       DistSun = Sun.radius();
00269 
00270       // apparent angular radius of sun = 0.2666/distance in AU (deg)
00271       AngRadSun = 0.2666/(DistSun/149598.0e6);
00272       AngRadSun *= DEG_TO_RAD;
00273 
00274       // angular radius of earth at sat
00275       AngRadEarth = ::asin(6378137.0/svrange);
00276 
00277       // T points from satellite to sun
00278       T = Sun;                                  // vector earth to sun
00279       T.transformTo(Position::Cartesian);
00280       S = SV;
00281       S.transformTo(Position::Cartesian);
00282       T = T - S;                                // sat to sun=(E to sun)-(E to sat)
00283       d = 1.0/T.mag();
00284       T = d * T;                                // normalize T
00285 
00286       AngSeparation = ::acos(Z.dot(T));         // apparent angular distance, earth
00287                                                 // to sun, as seen at satellite
00288       // is satellite in eclipse?
00289       try { sf = ShadowFactor(AngRadEarth, AngRadSun, AngSeparation); }
00290       catch(Exception& e) { GPSTK_RETHROW(e); }
00291 
00292       // Y is perpendicular to Z and T, such that ...
00293       Y = Z.cross(T);
00294       d = 1.0/Y.mag();
00295       Y = d * Y;                                // normalize Y
00296 
00297       // ... X points generally in the direction of the sun
00298       X = Y.cross(Z);                           // X will be unit vector
00299       if(X.dot(T) < 0) {                        // need to reverse X, hence Y also
00300          X = -1.0 * X;
00301          Y = -1.0 * Y;
00302       }
00303 
00304       // fill the matrix and return it
00305       for(i=0; i<3; i++) {
00306          R(0,i) = X[i];
00307          R(1,i) = Y[i];
00308          R(2,i) = Z[i];
00309       }
00310 
00311       return R;
00312    }
00313    catch(Exception& e) { GPSTK_RETHROW(e); }
00314    catch(exception& e) {Exception E("std except: "+string(e.what())); GPSTK_THROW(E);}
00315    catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
00316 }
00317 
00318 // -----------------------------------------------------------------------------------
00319 // Version without JPL SolarSystem ephemeris - uses SolarPosition
00320 Matrix<double> SatelliteAttitude(const CommonTime& tt, const Position& SV, double& sf)
00321    throw(Exception)
00322 {
00323    SolarSystem ssdummy;
00324    EarthOrientation eodum;
00325    return doSatAtt(tt,SV,ssdummy,eodum,sf);
00326 }
00327 
00328 // -----------------------------------------------------------------------------------
00329 // Version with JPL SolarSystem ephemeris. Throw if the SolarSystem is not valid
00330 Matrix<double> SatelliteAttitude(const CommonTime& tt, const Position& SV,
00331                                  const SolarSystem& SSEph, const EarthOrientation& EO,
00332                                  double& sf)
00333    throw(Exception)
00334 {
00335    if(SSEph.JPLNumber() == -1 || SSEph.startTime()-tt > 1.e-8
00336                               || tt - SSEph.endTime() > 1.e-8) {
00337       Exception e("Solar system ephemeris invalid");
00338       GPSTK_THROW(e);
00339    }
00340 
00341    return doSatAtt(tt,SV,SSEph,EO,sf);
00342 }
00343 
00344 // -----------------------------------------------------------------------------------
00345 //------------------------------------------------------------------------------------
00346 // Compute the azimuth and nadir angle, in the satellite body frame,
00347 // of receiver Position RX as seen at the satellite Position SV. The nadir angle
00348 // is measured from the Z axis, which points to Earth center, and azimuth is
00349 // measured from the X axis.
00350 // @param Position SV          Satellite position
00351 // @param Position RX          Receiver position
00352 // @param Matrix<double> Rot   Rotation matrix (3,3), output of SatelliteAttitude
00353 // @param double nadir         Output nadir angle in degrees
00354 // @param double azimuth       Output azimuth angle in degrees
00355 void SatelliteNadirAzimuthAngles(const Position& SV,
00356                                  const Position& RX,
00357                                  const Matrix<double>& Rot,
00358                                  double& nadir,
00359                                  double& azimuth)
00360    throw(Exception)
00361 {
00362    try {
00363       if(Rot.rows() != 3 || Rot.cols() != 3) {
00364          Exception e("Rotation matrix invalid");
00365          GPSTK_THROW(e);
00366       }
00367 
00368       double d;
00369       Position RmS;
00370       // RmS points from satellite to receiver
00371       RmS = RX - SV;
00372       RmS.transformTo(Position::Cartesian);
00373       d = RmS.mag();
00374       if(d == 0.0) {
00375          Exception e("Satellite and Receiver Positions identical");
00376          GPSTK_THROW(e);
00377       }
00378       RmS = (1.0/d) * RmS;
00379 
00380       Vector<double> XYZ(3),Body(3);
00381       XYZ(0) = RmS.X();
00382       XYZ(1) = RmS.Y();
00383       XYZ(2) = RmS.Z();
00384       Body = Rot * XYZ;
00385 
00386       nadir = ::acos(Body(2)) * RAD_TO_DEG;
00387       azimuth = ::atan2(Body(1),Body(0)) * RAD_TO_DEG;
00388       if(azimuth < 0.0) azimuth += 360.;
00389    }
00390    catch(Exception& e) { GPSTK_RETHROW(e); }
00391    catch(exception& e) {Exception E("std except: "+string(e.what())); GPSTK_THROW(E);}
00392    catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
00393 }
00394 
00395 // -----------------------------------------------------------------------------------
00396 }  // end namespace
00397 

Generated on Wed May 22 03:31:14 2013 for GPS ToolKit Software Library by  doxygen 1.3.9.1