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
1.3.9.1