00001 #pragma ident "$Id: SolarPosition.cpp 2293 2010-02-12 18:14:16Z btolman $" 00002 00007 // ====================================================================== 00008 // This software was developed by Applied Research Laboratories, The 00009 // University of Texas at Austin under contract to an agency or agencies 00010 // within the U.S. Department of Defense. The U.S. Government retains all 00011 // rights to use, duplicate, distribute, disclose, or release this 00012 // software. 00013 // 00014 // Copyright 2008 The University of Texas at Austin 00015 // ====================================================================== 00016 00017 //------------------------------------------------------------------------------------ 00018 // includes 00019 // system 00020 // GPSTk 00021 #include "DayTime.hpp" 00022 #include "Position.hpp" 00023 #include "icd_200_constants.hpp" // TWO_PI 00024 #include "geometry.hpp" // DEG_TO_RAD 00025 00026 #include "SolarPosition.hpp" 00027 00028 using namespace std; 00029 00030 //------------------------------------------------------------------------------------ 00031 // Compute Greenwich Mean Sidereal Time in degrees 00032 static double GMST(gpstk::DayTime t) 00033 { 00034 static const long JulianEpoch=2451545; 00035 // days since epoch 00036 double days = t.JD() - JulianEpoch; // days 00037 if(days <= 0.0) days -= 1.0; 00038 double Tp = days/36525.0; // dim-less 00039 00040 // Compute GMST in radians 00041 double G; 00042 // seconds (24060s = 6h 41min) 00043 //G = 24110.54841 + (8640184.812866 + (0.093104 - 6.2e-6*Tp)*Tp)*Tp; // sec 00044 //G /= 86400.0; // instead, divide the numbers above manually 00045 G = 0.279057273264 + 100.0021390378009*Tp // seconds/86400 = days 00046 + (0.093104 - 6.2e-6*Tp)*Tp*Tp/86400.0; 00047 G += t.secOfDay()/86400.0; // days 00048 00049 // put answer between 0 and 360 deg 00050 G = fmod(G,1.0); 00051 while(G < 0.0) G += 1.0; 00052 G *= 360.0; // degrees 00053 00054 return G; 00055 } 00056 00057 namespace gpstk { 00058 00059 //------------------------------------------------------------------------------------ 00060 // accuracy is about 1 arcminute, when t is within 2 centuries of 2000. 00061 // Ref. Astronomical Almanac pg C24, as presented on USNO web site. 00062 // input 00063 // t epoch of interest 00064 // output 00065 // lat,lon,R latitude, longitude and distance (deg,deg,m in ECEF) of sun at t. 00066 // AR apparent angular radius of sun as seen at Earth (deg) at t. 00067 Position SolarPosition(DayTime t, double& AR) throw() 00068 { 00069 //const double mPerAU = 149598.0e6; 00070 double D; // days since J2000 00071 double g,q; // q is mean longitude of sun, corrected for aberration 00072 double L; // sun's geocentric apparent ecliptic longitude (deg) 00073 //double b=0; // sun's geocentric apparent ecliptic latitude (deg) 00074 double e; // mean obliquity of the ecliptic (deg) 00075 //double R; // sun's distance from Earth (m) 00076 double RA; // sun's right ascension (deg) 00077 double DEC; // sun's declination (deg) 00078 //double AR; // sun's apparent angular radius as seen at Earth (deg) 00079 00080 D = t.JD() - 2451545.0; 00081 g = (357.529 + 0.98560028 * D) * DEG_TO_RAD; 00082 // AA 1990 has g = (357.528 + 0.9856003 * D) * DEG_TO_RAD; 00083 q = 280.459 + 0.98564736 * D; 00084 // AA 1990 has q = 280.460 + 0.9856474 * D; 00085 L = (q + 1.915 * sin(g) + 0.020 * sin(2*g)) * DEG_TO_RAD; 00086 00087 e = (23.439 - 0.00000036 * D) * DEG_TO_RAD; 00088 // AA 1990 has e = (23.439 - 0.0000004 * D) * DEG_TO_RAD; 00089 RA = atan2(cos(e)*sin(L),cos(L)) * RAD_TO_DEG; 00090 DEC = asin(sin(e)*sin(L)) * RAD_TO_DEG; 00091 00092 // equation of time = apparent solar time minus mean solar time 00093 // = [q-RA (deg)]/(15deg/hr) 00094 00095 // compute the hour angle of the vernal equinox = GMST and convert RA to lon 00096 double lon = fmod(RA-GMST(t),360.0); 00097 if(lon < -180.0) lon += 360.0; 00098 if(lon > 180.0) lon -= 360.0; 00099 00100 double lat = DEC; 00101 00102 // ECEF unit vector in direction Earth to sun 00103 double xhat = cos(lat*DEG_TO_RAD)*cos(lon*DEG_TO_RAD); 00104 double yhat = cos(lat*DEG_TO_RAD)*sin(lon*DEG_TO_RAD); 00105 double zhat = sin(lat*DEG_TO_RAD); 00106 00107 // R in AU 00108 double R = 1.00014 - 0.01671 * cos(g) - 0.00014 * cos(2*g); 00109 // apparent angular radius in degrees 00110 AR = 0.2666/R; 00111 // convert to meters 00112 R *= 149598.0e6; 00113 00114 Position ES; 00115 ES.setECEF(R*xhat,R*yhat,R*zhat); 00116 return ES; 00117 } 00118 00119 //------------------------------------------------------------------------------------ 00120 // Compute the position (latitude and longitude, in degrees) of the sun 00121 // given the day of year and the hour of the day. 00122 // Adapted from sunpos by D. Coco 12/15/94 00123 void CrudeSolarPosition(DayTime t, double& lat, double& lon) throw() 00124 { 00125 int doy = t.DOY(); 00126 int hod = int(t.secOfDay()/3600.0 + 0.5); 00127 lat = sin(23.5*DEG_TO_RAD)*sin(TWO_PI*double(doy-83)/365.25); 00128 lat = lat / sqrt(1.0-lat*lat); 00129 lat = RAD_TO_DEG*atan(lat); 00130 lon = 180.0 - hod*15.0; 00131 } 00132 00133 //------------------------------------------------------------------------------------ 00134 // Consider the sun and the earth as seen from the satellite. Let the sun be a circle 00135 // of angular radius r, center in direction s, and the earth be a (larger) circle 00136 // of angular radius R, center in direction e. The circles overlap if |e-s| < R+r; 00137 // complete overlap if |e-s| < R. Let L == |e-s|. 00138 // What is the area of overlap if R-r < L < R+r ? 00139 // Call the two points where the circles intersect p1 and p2. Draw a line from e to s; 00140 // call the points where this line intersects the two circles r1 and R1, respectively. 00141 // Draw lines from e to s, e to p1, e to p2, s to p1 and s to p2. Call the angle 00142 // between e-s and e-p1 alpha, and that between s-e and s-p1, beta. 00143 // Draw a rectangle with top and bottom parallel to e-s passing through p1 and p2, 00144 // and with sides passing through s and r1. Similarly for e and R1. Note that the 00145 // area of intersection lies within the intersection of these two rectangles. 00146 // Call the area of the rectangle outside the circles A and B. The height H of the 00147 // rectangles is 00148 // H = 2Rsin(alpha) = 2rsin(beta) 00149 // also L = rcos(beta)+Rcos(alpha) 00150 // The area A will be the area of the rectangle 00151 // minus the area of the wedge formed by the angle 2*alpha 00152 // minus the area of the two triangles which meet at s : 00153 // A = RH - (2alpha/2pi)*pi*R*R - 2*(1/2)*(H/2)Rcos(alpha) 00154 // Similarly 00155 // B = rH - (2beta/2pi)*pi*r*r - 2*(1/2)*(H/2)rcos(beta) 00156 // The area of intersection will be the area of the rectangular intersection 00157 // minus the area A 00158 // minus the area B 00159 // Intersection = H(R+r-L) - A - B 00160 // = HR+Hr-HL -HR+alpha*R*R+(H/2)Rcos(alpha) -Hr+beta*r*r+(H/2)rcos(beta) 00161 // Cancel terms, and substitute for L using above equation L = .. 00162 // = -(H/2)rcos(beta)-(H/2)Rcos(alpha)+alpha*R*R+beta*r*r 00163 // substitute for H/2 00164 // = -R*R*sin(alpha)cos(alpha)-r*r*sin(beta)cos(beta)+alpha*R*R+beta*r*r 00165 // Intersection = R*R*[alpha-sin(alpha)cos(alpha)]+r*r*[beta-sin(beta)cos(beta)] 00166 // Solve for alpha and beta in terms of R, r and L using the H and L relations above 00167 // (r/R)cos(beta)=(L/R)-cos(alpha) 00168 // (r/R)sin(beta)=sin(alpha) 00169 // so 00170 // (r/R)^2 = (L/R)^2 - (2L/R)cos(alpha) + 1 00171 // cos(alpha) = (R/2L)(1+(L/R)^2-(r/R)^2) 00172 // cos(beta) = (L/r) - (R/r)cos(alpha) 00173 // and 0 <= alpha or beta <= pi 00174 // 00175 // Rearth angular radius of the earth as seen at the satellite 00176 // Rsun angular radius of the sun as seen at the satellite 00177 // dES angular distance of the sun from the earth 00178 // return fraction (0 <= f <= 1) of area of sun covered by earth 00179 // units only need be consistent 00180 double shadowFactor(double Rearth, double Rsun, double dES) throw() 00181 { 00182 if(dES >= Rearth+Rsun) return 0.0; 00183 if(dES <= fabs(Rearth-Rsun)) return 1.0; 00184 double r=Rsun, R=Rearth, L=dES; 00185 if(Rsun > Rearth) { r=Rearth; R=Rsun; } 00186 double cosalpha = (R/L)*(1.0+(L/R)*(L/R)-(r/R)*(r/R))/2.0; 00187 double cosbeta = (L/r) - (R/r)*cosalpha; 00188 double sinalpha = ::sqrt(1-cosalpha*cosalpha); 00189 double sinbeta = ::sqrt(1-cosbeta*cosbeta); 00190 double alpha = ::asin(sinalpha); 00191 double beta = ::asin(sinbeta); 00192 double shadow = r*r*(beta-sinbeta*cosbeta)+R*R*(alpha-sinalpha*cosalpha); 00193 shadow /= ::acos(-1.0)*Rsun*Rsun; 00194 return shadow; 00195 } 00196 00197 //------------------------------------------------------------------------------------ 00198 // From AA 1990 D46 00199 Position LunarPosition(DayTime t, double& AR) throw() 00200 { 00201 // days since J2000 00202 double N = t.JD()-2451545.0; 00203 // centuries since J2000 00204 double T = N/36525.0; 00205 // ecliptic longitude 00206 double lam = DEG_TO_RAD*(218.32 + 481267.883*T 00207 + 6.29 * ::sin(DEG_TO_RAD*(134.9+477198.85*T)) 00208 - 1.27 * ::sin(DEG_TO_RAD*(259.2-413335.38*T)) 00209 + 0.66 * ::sin(DEG_TO_RAD*(235.7+890534.23*T)) 00210 + 0.21 * ::sin(DEG_TO_RAD*(269.9+954397.70*T)) 00211 - 0.19 * ::sin(DEG_TO_RAD*(357.5+ 35999.05*T)) 00212 - 0.11 * ::sin(DEG_TO_RAD*(259.2+966404.05*T))); 00213 // ecliptic latitude 00214 double bet = DEG_TO_RAD*(5.13 * ::sin(DEG_TO_RAD*( 93.3+483202.03*T)) 00215 + 0.28 * ::sin(DEG_TO_RAD*(228.2+960400.87*T)) 00216 - 0.28 * ::sin(DEG_TO_RAD*(318.3+ 6003.18*T)) 00217 - 0.17 * ::sin(DEG_TO_RAD*(217.6-407332.20*T))); 00218 // horizontal parallax 00219 double par = DEG_TO_RAD*(0.9508 00220 + 0.0518 * ::cos(DEG_TO_RAD*(134.9+477198.85*T)) 00221 + 0.0095 * ::cos(DEG_TO_RAD*(259.2-413335.38*T)) 00222 + 0.0078 * ::cos(DEG_TO_RAD*(235.7+890534.23*T)) 00223 + 0.0028 * ::cos(DEG_TO_RAD*(269.9+954397.70*T))); 00224 00225 // obliquity of the ecliptic 00226 double eps = (23.439 - 0.00000036 * N) * DEG_TO_RAD; 00227 00228 // convert ecliptic lon,lat to geocentric lon,lat 00229 double l = ::cos(bet)*::cos(lam); 00230 double m = ::cos(eps)*::cos(bet)*::sin(lam) - ::sin(eps)*::sin(bet); 00231 double n = ::sin(eps)*::cos(bet)*::sin(lam) + ::cos(eps)*::sin(bet); 00232 00233 // convert to right ascension and declination, 00234 // (referred to mean equator and equinox of date) 00235 double RA = atan2(m,l) * RAD_TO_DEG; 00236 double DEC = asin(n) * RAD_TO_DEG; 00237 00238 // compute the hour angle of the vernal equinox = GMST and convert RA to lon 00239 double lon = fmod(RA-GMST(t),360.0); 00240 if(lon < -180.0) lon += 360.0; 00241 if(lon > 180.0) lon -= 360.0; 00242 00243 double lat = DEC; 00244 00245 // apparent semidiameter of moon (in radians) 00246 AR = 0.2725 * par; 00247 // moon distance in meters 00248 double R = 1.0 / ::sin(par); 00249 R *= 6378137.0; 00250 00251 // ECEF vector in direction Earth to moon 00252 double x = R*cos(lat*DEG_TO_RAD)*cos(lon*DEG_TO_RAD); 00253 double y = R*cos(lat*DEG_TO_RAD)*sin(lon*DEG_TO_RAD); 00254 double z = R*sin(lat*DEG_TO_RAD); 00255 00256 Position EM; 00257 EM.setECEF(x,y,z); 00258 00259 return EM; 00260 } 00261 00262 //------------------------------------------------------------------------------------ 00263 } // end namespace gpstk
1.3.9.1