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-2020, 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 /// @file SolarPosition.cpp  Compute solar and lunar positions with a simple algorithm
40 
41 //------------------------------------------------------------------------------------
42 // includes
43 // system
44 // GPSTk
45 #include "CommonTime.hpp"
46 #include "Position.hpp"
47 #include "GNSSconstants.hpp"       // TWO_PI
48 #include "GNSSconstants.hpp"                // DEG_TO_RAD
49 #include "SolarPosition.hpp"
50 #include "YDSTime.hpp"
51 #include "JulianDate.hpp"
52 
53 namespace gpstk {
54 using namespace std;
55 
56 //------------------------------------------------------------------------------------
57 // Compute Greenwich Mean Sidereal Time in degrees
GMST(gpstk::CommonTime t)58 static double GMST(gpstk::CommonTime t)
59 {
60 
61    static const long JulianEpoch=2451545;
62    // days since epoch
63    double days =( static_cast<JulianDate>(t).jd - JulianEpoch);                         // days
64    if(days <= 0.0) days -= 1.0;
65    double Tp = days/36525.0;                                   // dim-less
66 
67       // Compute GMST in radians
68    double G;
69    // seconds (24060s = 6h 41min)
70    //G = 24110.54841 + (8640184.812866 + (0.093104 - 6.2e-6*Tp)*Tp)*Tp;   // sec
71    //G /= 86400.0; // instead, divide the numbers above manually
72    G = 0.279057273264 + 100.0021390378009*Tp        // seconds/86400 = days
73                       + (0.093104 - 6.2e-6*Tp)*Tp*Tp/86400.0;
74    G += static_cast<YDSTime>(t).sod/86400.0;                      // days
75 
76    // put answer between 0 and 360 deg
77    G = fmod(G,1.0);
78    while(G < 0.0) G += 1.0;
79    G *= 360.0;                                                 // degrees
80 
81    return G;
82 }
83 
84 
85 
86 //------------------------------------------------------------------------------------
87 // accuracy is about 1 arcminute, when t is within 2 centuries of 2000.
88 // Ref. Astronomical Almanac pg C24, as presented on USNO web site.
89 // input
90 //    t             epoch of interest
91 // output
92 //    lat,lon,R     latitude, longitude and distance (deg,deg,m in ECEF) of sun at t.
93 //    AR            apparent angular radius of sun as seen at Earth (deg) at t.
SolarPosition(CommonTime t,double & AR)94 Position SolarPosition(CommonTime t, double& AR) throw()
95 {
96    //const double mPerAU = 149598.0e6;
97    double D;     // days since J2000
98    double g,q;   // q is mean longitude of sun, corrected for aberration
99    double L;     // sun's geocentric apparent ecliptic longitude (deg)
100    //double b=0; // sun's geocentric apparent ecliptic latitude (deg)
101    double e;     // mean obliquity of the ecliptic (deg)
102    //double R;   // sun's distance from Earth (m)
103    double RA;    // sun's right ascension (deg)
104    double DEC;   // sun's declination (deg)
105    //double AR;  // sun's apparent angular radius as seen at Earth (deg)
106 
107    D = static_cast<JulianDate>(t).jd - 2451545.0;
108    g = (357.529 + 0.98560028 * D) * DEG_TO_RAD;
109    // AA 1990 has g = (357.528 + 0.9856003 * D) * DEG_TO_RAD;
110    q = 280.459 + 0.98564736 * D;
111    // AA 1990 has q = 280.460 + 0.9856474 * D;
112    L = (q + 1.915 * std::sin(g) + 0.020 * std::sin(2*g)) * DEG_TO_RAD;
113 
114    e = (23.439 - 0.00000036 * D) * DEG_TO_RAD;
115    // AA 1990 has e = (23.439 - 0.0000004 * D) * DEG_TO_RAD;
116    RA = std::atan2(std::cos(e)*std::sin(L),std::cos(L)) * RAD_TO_DEG;
117    DEC = std::asin(std::sin(e)*std::sin(L)) * RAD_TO_DEG;
118 
119    // equation of time = apparent solar time minus mean solar time
120    // = [q-RA (deg)]/(15deg/hr)
121 
122    // compute the hour angle of the vernal equinox = GMST and convert RA to lon
123    double lon = fmod(RA-GMST(t),360.0);
124    if(lon < -180.0) lon += 360.0;
125    if(lon >  180.0) lon -= 360.0;
126 
127    double lat = DEC;
128 
129    // ECEF unit vector in direction Earth to sun
130    double xhat = std::cos(lat*DEG_TO_RAD)*std::cos(lon*DEG_TO_RAD);
131    double yhat = std::cos(lat*DEG_TO_RAD)*std::sin(lon*DEG_TO_RAD);
132    double zhat = std::sin(lat*DEG_TO_RAD);
133 
134    // R in AU
135    double R = 1.00014 - 0.01671 * std::cos(g) - 0.00014 * std::cos(2*g);
136    // apparent angular radius in degrees
137    AR = 0.2666/R;
138    // convert to meters
139    R *= 149598.0e6;
140 
141    Position es;
142    es.setECEF(R*xhat,R*yhat,R*zhat);
143    return es;
144 }
145 
146 //------------------------------------------------------------------------------------
147 // Compute the position (latitude and longitude, in degrees) of the sun
148 // given the day of year and the hour of the day.
149 // Adapted from sunpos by D. Coco 12/15/94
CrudeSolarPosition(CommonTime t,double & lat,double & lon)150 void CrudeSolarPosition(CommonTime t, double& lat, double& lon) throw()
151 {
152    int doy = static_cast<YDSTime>(t).doy;
153    int hod = int(static_cast<YDSTime>(t).sod/3600.0 + 0.5);
154    lat = std::sin(23.5*DEG_TO_RAD)*std::sin(TWO_PI*double(doy-83)/365.25);
155    lat = lat / std::sqrt(1.0-lat*lat);
156    lat = RAD_TO_DEG*std::atan(lat);
157    lon = 180.0 - hod*15.0;
158 }
159 
160 //------------------------------------------------------------------------------------
161 // Consider the sun and the earth as seen from the satellite. Let the sun be a circle
162 // of angular radius r, center in direction s, and the earth be a (larger) circle
163 // of angular radius R, center in direction e. The circles overlap if |e-s| < R+r;
164 // complete overlap if |e-s| < R. Let L == |e-s|.
165 //    What is the area of overlap if R-r < L < R+r ?
166 // Call the two points where the circles intersect p1 and p2. Draw a line from e to s;
167 // call the points where this line intersects the two circles r1 and R1, respectively.
168 // Draw lines from e to s, e to p1, e to p2, s to p1 and s to p2. Call the angle
169 // between e-s and e-p1 alpha, and that between s-e and s-p1, beta.
170 // Draw a rectangle with top and bottom parallel to e-s passing through p1 and p2,
171 // and with sides passing through s and r1. Similarly for e and R1. Note that the
172 // area of intersection lies within the intersection of these two rectangles.
173 // Call the area of the rectangle outside the circles A and B. The height H of the
174 // rectangles is
175 // H = 2Rsin(alpha) = 2rsin(beta)
176 // also L = rcos(beta)+Rcos(alpha)
177 // The area A will be the area of the rectangle
178 //              minus the area of the wedge formed by the angle 2*alpha
179 //              minus the area of the two triangles which meet at s :
180 // A = RH - (2alpha/2pi)*pi*R*R - 2*(1/2)*(H/2)Rcos(alpha)
181 // Similarly
182 // B = rH - (2beta/2pi)*pi*r*r  - 2*(1/2)*(H/2)rcos(beta)
183 // The area of intersection will be the area of the rectangular intersection
184 //                            minus the area A
185 //                            minus the area B
186 // Intersection = H(R+r-L) - A - B
187 //              = HR+Hr-HL -HR+alpha*R*R+(H/2)Rcos(alpha) -Hr+beta*r*r+(H/2)rcos(beta)
188 // Cancel terms, and substitute for L using above equation L = ..
189 //              = -(H/2)rcos(beta)-(H/2)Rcos(alpha)+alpha*R*R+beta*r*r
190 // substitute for H/2
191 //              = -R*R*sin(alpha)cos(alpha)-r*r*sin(beta)cos(beta)+alpha*R*R+beta*r*r
192 // Intersection = R*R*[alpha-sin(alpha)cos(alpha)]+r*r*[beta-sin(beta)cos(beta)]
193 // Solve for alpha and beta in terms of R, r and L using the H and L relations above
194 // (r/R)cos(beta)=(L/R)-cos(alpha)
195 // (r/R)sin(beta)=sin(alpha)
196 // so
197 // (r/R)^2 = (L/R)^2 - (2L/R)cos(alpha) + 1
198 // cos(alpha) = (R/2L)(1+(L/R)^2-(r/R)^2)
199 // cos(beta) = (L/r) - (R/r)cos(alpha)
200 // and 0 <= alpha or beta <= pi
201 //
202 // Rearth    angular radius of the earth as seen at the satellite
203 // Rsun      angular radius of the sun as seen at the satellite
204 // dES       angular distance of the sun from the earth
205 // return    fraction (0 <= f <= 1) of area of sun covered by earth
206 // units only need be consistent
shadowFactor(double Rearth,double Rsun,double dES)207 double shadowFactor(double Rearth, double Rsun, double dES) throw()
208 {
209    if(dES >= Rearth+Rsun) return 0.0;
210    if(dES <= std::fabs(Rearth-Rsun)) return 1.0;
211    double r=Rsun, R=Rearth, L=dES;
212    if(Rsun > Rearth) { r=Rearth; R=Rsun; }
213    double cosalpha = (R/L)*(1.0+(L/R)*(L/R)-(r/R)*(r/R))/2.0;
214    double cosbeta = (L/r) - (R/r)*cosalpha;
215    double sinalpha = ::sqrt(1-cosalpha*cosalpha);
216    double sinbeta = ::sqrt(1-cosbeta*cosbeta);
217    double alpha = ::asin(sinalpha);
218    double beta = ::asin(sinbeta);
219    double shadow = r*r*(beta-sinbeta*cosbeta)+R*R*(alpha-sinalpha*cosalpha);
220    shadow /= ::acos(-1.0)*Rsun*Rsun;
221    return shadow;
222 }
223 
224 //------------------------------------------------------------------------------------
225 // From AA 1990 D46
LunarPosition(CommonTime t,double & AR)226 Position LunarPosition(CommonTime t, double& AR) throw()
227 {
228    // days since J2000
229    double N = static_cast<JulianDate>(t).jd-2451545.0;
230    // centuries since J2000
231    double T = N/36525.0;
232    // ecliptic longitude
233    double lam = DEG_TO_RAD*(218.32 + 481267.883*T
234               + 6.29 * ::sin(DEG_TO_RAD*(134.9+477198.85*T))
235               - 1.27 * ::sin(DEG_TO_RAD*(259.2-413335.38*T))
236               + 0.66 * ::sin(DEG_TO_RAD*(235.7+890534.23*T))
237               + 0.21 * ::sin(DEG_TO_RAD*(269.9+954397.70*T))
238               - 0.19 * ::sin(DEG_TO_RAD*(357.5+ 35999.05*T))
239               - 0.11 * ::sin(DEG_TO_RAD*(259.2+966404.05*T)));
240    // ecliptic latitude
241    double bet = DEG_TO_RAD*(5.13 * ::sin(DEG_TO_RAD*( 93.3+483202.03*T))
242                           + 0.28 * ::sin(DEG_TO_RAD*(228.2+960400.87*T))
243                           - 0.28 * ::sin(DEG_TO_RAD*(318.3+  6003.18*T))
244                           - 0.17 * ::sin(DEG_TO_RAD*(217.6-407332.20*T)));
245    // horizontal parallax
246    double par = DEG_TO_RAD*(0.9508
247               + 0.0518 * ::cos(DEG_TO_RAD*(134.9+477198.85*T))
248               + 0.0095 * ::cos(DEG_TO_RAD*(259.2-413335.38*T))
249               + 0.0078 * ::cos(DEG_TO_RAD*(235.7+890534.23*T))
250               + 0.0028 * ::cos(DEG_TO_RAD*(269.9+954397.70*T)));
251 
252    // obliquity of the ecliptic
253    double eps = (23.439 - 0.00000036 * N) * DEG_TO_RAD;
254 
255    // convert ecliptic lon,lat to geocentric lon,lat
256    double l = ::cos(bet)*::cos(lam);
257    double m = ::cos(eps)*::cos(bet)*::sin(lam) - ::sin(eps)*::sin(bet);
258    double n = ::sin(eps)*::cos(bet)*::sin(lam) + ::cos(eps)*::sin(bet);
259 
260    // convert to right ascension and declination,
261    // (referred to mean equator and equinox of date)
262    double RA = ::atan2(m,l) * RAD_TO_DEG;
263    double DEC = ::asin(n) * RAD_TO_DEG;
264 
265    // compute the hour angle of the vernal equinox = GMST and convert RA to lon
266    double lon = ::fmod(RA-GMST(t),360.0);
267    if(lon < -180.0) lon += 360.0;
268    if(lon >  180.0) lon -= 360.0;
269 
270    double lat = DEC;
271 
272    // apparent semidiameter of moon (in radians)
273    AR = 0.2725 * par;
274    // moon distance in meters
275    double R = 1.0 / ::sin(par);
276    R *= 6378137.0;
277 
278    // ECEF vector in direction Earth to moon
279    double x = R*std::cos(lat*DEG_TO_RAD)*std::cos(lon*DEG_TO_RAD);
280    double y = R*std::cos(lat*DEG_TO_RAD)*std::sin(lon*DEG_TO_RAD);
281    double z = R*std::sin(lat*DEG_TO_RAD);
282 
283    Position EM;
284    EM.setECEF(x,y,z);
285 
286    return EM;
287 }
288 
289 //------------------------------------------------------------------------------------
290 } // end namespace gpstk
291