astro.cpp
上传用户:center1979
上传日期:2022-07-26
资源大小:50633k
文件大小:23k
源码类别:

OpenGL

开发平台:

Visual C++

  1. // astro.cpp
  2. //
  3. // Copyright (C) 2001-2009, the Celestia Development Team
  4. //
  5. // This program is free software; you can redistribute it and/or
  6. // modify it under the terms of the GNU General Public License
  7. // as published by the Free Software Foundation; either version 2
  8. // of the License, or (at your option) any later version.
  9. #include <cstring>
  10. #include <cmath>
  11. #include <iomanip>
  12. #include <cstdio>
  13. #include <time.h>
  14. #include <celutil/basictypes.h>
  15. #include <celmath/mathlib.h>
  16. #include "celestia.h"
  17. #include "astro.h"
  18. #include <celutil/util.h>
  19. using namespace std;
  20. const double astro::speedOfLight = 299792.458; // km/s
  21. // epoch J2000: 12 UT on 1 Jan 2000
  22. const double astro::J2000 = 2451545.0;
  23. const double astro::G = 6.672e-11; // N m^2 / kg^2
  24. const double astro::SolarMass = 1.989e30;
  25. const double astro::EarthMass = 5.976e24;
  26. const double astro::LunarMass = 7.354e22;
  27. const double astro::SOLAR_IRRADIANCE  = 1367.6;        // Watts / m^2
  28. const double astro::SOLAR_POWER       =    3.8462e26;  // Watts
  29. // Angle between J2000 mean equator and the ecliptic plane.
  30. // 23 deg 26' 21".448 (Seidelmann, _Explanatory Supplement to the
  31. // Astronomical Almanac_ (1992), eqn 3.222-1.
  32. const double astro::J2000Obliquity = degToRad(23.4392911);
  33. static const Quatd ECLIPTIC_TO_EQUATORIAL_ROTATION =
  34.     Quatd::xrotation(-astro::J2000Obliquity);
  35. static const Mat3d ECLIPTIC_TO_EQUATORIAL_MATRIX = ECLIPTIC_TO_EQUATORIAL_ROTATION.toMatrix3();
  36. // Equatorial to galactic coordinate transformation
  37. // North galactic pole at:
  38. // RA 12h 51m 26.282s (192.85958 deg)
  39. // Dec 27 d 07' 42.01" (27.1283361 deg)
  40. // Zero longitude at position angle 122.932
  41. // (J2000 coordinates)
  42. static const double GALACTIC_NODE = 282.85958;
  43. static const double GALACTIC_INCLINATION = 90.0 - 27.1283361;
  44. static const double GALACTIC_LONGITUDE_AT_NODE = 32.932;
  45. static const Quatd EQUATORIAL_TO_GALACTIC_ROTATION =
  46.     Quatd::zrotation(degToRad(GALACTIC_NODE)) *
  47.     Quatd::xrotation(degToRad(GALACTIC_INCLINATION)) *
  48.     Quatd::zrotation(degToRad(-GALACTIC_LONGITUDE_AT_NODE));
  49. static const Mat3d EQUATORIAL_TO_GALACTIC_MATRIX = EQUATORIAL_TO_GALACTIC_ROTATION.toMatrix3();
  50.     
  51. // epoch B1950: 22:09 UT on 21 Dec 1949
  52. #define B1950         2433282.423
  53. // Difference in seconds between Terrestrial Time and International
  54. // Atomic Time
  55. static const double dTA = 32.184;
  56. struct LeapSecondRecord
  57. {
  58.     int seconds;
  59.     double t;
  60. };
  61. // Table of leap second insertions. The leap second always
  62. // appears as the last second of the day immediately prior
  63. // to the date in the table.
  64. static const LeapSecondRecord LeapSeconds[] =
  65. {
  66.     { 10, 2441317.5 }, // 1 Jan 1972
  67.     { 11, 2441499.5 }, // 1 Jul 1972
  68.     { 12, 2441683.5 }, // 1 Jan 1973
  69.     { 13, 2442048.5 }, // 1 Jan 1974
  70.     { 14, 2442413.5 }, // 1 Jan 1975
  71.     { 15, 2442778.5 }, // 1 Jan 1976
  72.     { 16, 2443144.5 }, // 1 Jan 1977
  73.     { 17, 2443509.5 }, // 1 Jan 1978
  74.     { 18, 2443874.5 }, // 1 Jan 1979
  75.     { 19, 2444239.5 }, // 1 Jan 1980
  76.     { 20, 2444786.5 }, // 1 Jul 1981
  77.     { 21, 2445151.5 }, // 1 Jul 1982
  78.     { 22, 2445516.5 }, // 1 Jul 1983
  79.     { 23, 2446247.5 }, // 1 Jul 1985
  80.     { 24, 2447161.5 }, // 1 Jan 1988
  81.     { 25, 2447892.5 }, // 1 Jan 1990
  82.     { 26, 2448257.5 }, // 1 Jan 1991
  83.     { 27, 2448804.5 }, // 1 Jul 1992
  84.     { 28, 2449169.5 }, // 1 Jul 1993
  85.     { 29, 2449534.5 }, // 1 Jul 1994
  86.     { 30, 2450083.5 }, // 1 Jan 1996
  87.     { 31, 2450630.5 }, // 1 Jul 1997
  88.     { 32, 2451179.5 }, // 1 Jan 1999
  89.     { 33, 2453736.5 }, // 1 Jan 2006
  90.     { 34, 2454832.5 }, // 1 Jan 2009
  91. };
  92. #if !defined(__GNUC__) || defined(_WIN32)
  93. static const char* MonthAbbrList[12] =
  94. { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
  95. #endif
  96. static Mat3d equatorialToCelestiald = Mat3d::xrotation(astro::J2000Obliquity);
  97. static Mat3f equatorialToCelestial = Mat3f::xrotation((float) astro::J2000Obliquity);
  98. float astro::lumToAbsMag(float lum)
  99. {
  100.     return (float) (SOLAR_ABSMAG - log(lum) * LN_MAG);
  101. }
  102. // Return the apparent magnitude of a star with lum times solar
  103. // luminosity viewed at lyrs light years
  104. float astro::lumToAppMag(float lum, float lyrs)
  105. {
  106.     return absToAppMag(lumToAbsMag(lum), lyrs);
  107. }
  108. float astro::absMagToLum(float mag)
  109. {
  110.     return (float) exp((SOLAR_ABSMAG - mag) / LN_MAG);
  111. }
  112. float astro::appMagToLum(float mag, float lyrs)
  113. {
  114.     return absMagToLum(appToAbsMag(mag, lyrs));
  115. }
  116. float astro::lightYearsToParsecs(float ly)
  117. {
  118.     return ly / (float) LY_PER_PARSEC;
  119. }
  120. double astro::lightYearsToParsecs(double ly)
  121. {
  122.     return ly / (double) LY_PER_PARSEC;
  123. }
  124. float astro::parsecsToLightYears(float pc)
  125. {
  126.     return pc * (float) LY_PER_PARSEC;
  127. }
  128. double astro::parsecsToLightYears(double pc)
  129. {
  130.     return pc * (double) LY_PER_PARSEC;
  131. }
  132. float astro::lightYearsToKilometers(float ly)
  133. {
  134.     return ly * (float) KM_PER_LY;
  135. }
  136. double astro::lightYearsToKilometers(double ly)
  137. {
  138.     return ly * KM_PER_LY;
  139. }
  140. float astro::kilometersToLightYears(float km)
  141. {
  142.     return km / (float) KM_PER_LY;
  143. }
  144. double astro::kilometersToLightYears(double km)
  145. {
  146.     return km / KM_PER_LY;
  147. }
  148. float astro::lightYearsToAU(float ly)
  149. {
  150.     return ly * (float) AU_PER_LY;
  151. }
  152. double astro::lightYearsToAU(double ly)
  153. {
  154.     return ly * AU_PER_LY;
  155. }
  156. float astro::AUtoKilometers(float au)
  157. {
  158.     return au * (float) KM_PER_AU;
  159. }
  160. double astro::AUtoKilometers(double au)
  161. {
  162.     return au * (double) KM_PER_AU;
  163. }
  164. float astro::kilometersToAU(float km)
  165. {
  166.     return km / (float) KM_PER_AU;
  167. }
  168. double astro::kilometersToAU(double km)
  169. {
  170.     return km / KM_PER_AU;
  171. }
  172. double astro::secondsToJulianDate(double sec)
  173. {
  174.     return sec / 86400.0;
  175. }
  176. double astro::julianDateToSeconds(double jd)
  177. {
  178.     return jd * 86400.0;
  179. }
  180. void astro::decimalToDegMinSec(double angle, int& degrees, int& minutes, double& seconds)
  181. {
  182.     double A, B, C;
  183.     degrees = (int) angle;
  184.     A = angle - (double) degrees;
  185.     B = A * 60.0;
  186.     minutes = (int) B;
  187.     C = B - (double) minutes;
  188.     seconds = C * 60.0;
  189. }
  190. double astro::degMinSecToDecimal(int degrees, int minutes, double seconds)
  191. {
  192.     return (double)degrees + (seconds/60.0 + (double)minutes)/60.0;
  193. }
  194. void astro::decimalToHourMinSec(double angle, int& hours, int& minutes, double& seconds)
  195. {
  196.     double A, B;
  197.     A = angle / 15.0;
  198.     hours = (int) A;
  199.     B = (A - (double) hours) * 60.0;
  200.     minutes = (int) (B);
  201.     seconds = (B - (double) minutes) * 60.0;
  202. }
  203. // Compute the fraction of a sphere which is illuminated and visible
  204. // to a viewer.  The source of illumination is assumed to be at (0, 0, 0)
  205. float astro::sphereIlluminationFraction(Point3d,
  206.                                         Point3d)
  207. {
  208.     return 1.0f;
  209. }
  210. float astro::microLightYearsToKilometers(float ly)
  211. {
  212.     return ly * ((float) KM_PER_LY * 1e-6f);
  213. }
  214. double astro::microLightYearsToKilometers(double ly)
  215. {
  216.     return ly * (KM_PER_LY * 1e-6);
  217. }
  218. float astro::kilometersToMicroLightYears(float km)
  219. {
  220.     return km / ((float) KM_PER_LY * 1e-6f);
  221. }
  222. double astro::kilometersToMicroLightYears(double km)
  223. {
  224.     return km / (KM_PER_LY * 1e-6);
  225. }
  226. float astro::microLightYearsToAU(float ly)
  227. {
  228.     return ly * (float) AU_PER_LY * 1e-6f;
  229. }
  230. double astro::microLightYearsToAU(double ly)
  231. {
  232.     return ly * AU_PER_LY * 1e-6;
  233. }
  234. float astro::AUtoMicroLightYears(float au)
  235. {
  236.     return au / ((float) AU_PER_LY * 1e-6f);
  237. }
  238. double astro::AUtoMicroLightYears(double au)
  239. {
  240.     return au / (AU_PER_LY * 1e-6);
  241. }
  242. // Convert the position in univeral coordinates to a star-centric
  243. // coordinates in units of kilometers.  Note that there are three different
  244. // precisions used here:  star coordinates are stored as floats in units of
  245. // light years, position within a solar system are doubles in units of
  246. // kilometers, and p is highest-precision in units of light years.
  247. Point3d astro::heliocentricPosition(const UniversalCoord& universal,
  248.                                     const Point3f& starPosition)
  249. {
  250.     // Get the offset vector
  251.     Vec3d v = universal - Point3d(starPosition.x * 1e6,
  252.                                   starPosition.y * 1e6,
  253.                                   starPosition.z * 1e6);
  254.     // . . . and convert it to kilometers
  255.     return Point3d(microLightYearsToKilometers(v.x),
  256.                    microLightYearsToKilometers(v.y),
  257.                    microLightYearsToKilometers(v.z));
  258. }
  259. // universalPosition is the inverse operation of heliocentricPosition
  260. UniversalCoord astro::universalPosition(const Point3d& heliocentric,
  261.                                         const Point3f& starPosition)
  262. {
  263.     return UniversalCoord(Point3d(starPosition.x * 1e6,
  264.                                   starPosition.y * 1e6,
  265.                                   starPosition.z * 1e6)) +
  266.         Vec3d(kilometersToMicroLightYears(heliocentric.x),
  267.               kilometersToMicroLightYears(heliocentric.y),
  268.               kilometersToMicroLightYears(heliocentric.z));
  269. }
  270. // universalPosition is the inverse operation of heliocentricPosition
  271. UniversalCoord astro::universalPosition(const Point3d& heliocentric,
  272.                                         const UniversalCoord& starPosition)
  273. {
  274.     return starPosition +
  275.         Vec3d(kilometersToMicroLightYears(heliocentric.x),
  276.               kilometersToMicroLightYears(heliocentric.y),
  277.               kilometersToMicroLightYears(heliocentric.z));
  278. }
  279. // Convert equatorial coordinates to Cartesian celestial (or ecliptical)
  280. // coordinates.
  281. Point3f astro::equatorialToCelestialCart(float ra, float dec, float distance)
  282. {
  283.     double theta = ra / 24.0 * PI * 2 + PI;
  284.     double phi = (dec / 90.0 - 1.0) * PI / 2;
  285.     double x = cos(theta) * sin(phi) * distance;
  286.     double y = cos(phi) * distance;
  287.     double z = -sin(theta) * sin(phi) * distance;
  288.     return (Point3f((float) x, (float) y, (float) z) * equatorialToCelestial);
  289. }
  290. // Convert equatorial coordinates to Cartesian celestial (or ecliptical)
  291. // coordinates.
  292. Point3d astro::equatorialToCelestialCart(double ra, double dec, double distance)
  293. {
  294.     double theta = ra / 24.0 * PI * 2 + PI;
  295.     double phi = (dec / 90.0 - 1.0) * PI / 2;
  296.     double x = cos(theta) * sin(phi) * distance;
  297.     double y = cos(phi) * distance;
  298.     double z = -sin(theta) * sin(phi) * distance;
  299.     return (Point3d(x, y, z) * equatorialToCelestiald);
  300. }
  301. void astro::anomaly(double meanAnomaly, double eccentricity,
  302.                     double& trueAnomaly, double& eccentricAnomaly)
  303. {
  304.     double e, delta, err;
  305.     double tol = 0.00000001745;
  306.     int iterations = 20; // limit while() to maximum of 20 iterations.
  307.     e = meanAnomaly - 2*PI * (int) (meanAnomaly / (2*PI));
  308.     err = 1;
  309.     while(abs(err) > tol && iterations > 0)
  310.     {
  311.         err = e - eccentricity*sin(e) - meanAnomaly;
  312.         delta = err / (1 - eccentricity * cos(e));
  313.         e -= delta;
  314.         iterations--;
  315.     }
  316.     trueAnomaly = 2*atan(sqrt((1+eccentricity)/(1-eccentricity))*tan(e/2));
  317.     eccentricAnomaly = e;
  318. }
  319. /*! Return the angle between the mean ecliptic plane and mean equator at
  320.  *  the specified Julian date.
  321.  */
  322. // TODO: replace this with a better precession model
  323. double astro::meanEclipticObliquity(double jd)
  324. {
  325.     double t, de;
  326.     jd -= 2451545.0;
  327.     t = jd / 36525;
  328.     de = (46.815 * t + 0.0006 * t * t - 0.00181 * t * t * t) / 3600;
  329.     return J2000Obliquity - de;
  330. }
  331. /*! Return a quaternion giving the transformation from the J2000 ecliptic
  332.  *  coordinate system to the J2000 Earth equatorial coordinate system.
  333.  */
  334. Quatd astro::eclipticToEquatorial()
  335. {
  336.     return ECLIPTIC_TO_EQUATORIAL_ROTATION;
  337. }
  338. /*! Rotate a vector in the J2000 ecliptic coordinate system to
  339.  *  the J2000 Earth equatorial coordinate system.
  340.  */
  341. Vec3d astro::eclipticToEquatorial(const Vec3d& v)
  342. {
  343.     return v * ECLIPTIC_TO_EQUATORIAL_MATRIX;
  344. }
  345. /*! Return a quaternion giving the transformation from the J2000 Earth
  346.  *  equatorial coordinate system to the galactic coordinate system.
  347.  */
  348. Quatd astro::equatorialToGalactic()
  349. {
  350.     return EQUATORIAL_TO_GALACTIC_ROTATION;
  351. }
  352. /*! Rotate a vector int the J2000 Earth equatorial coordinate system to
  353.  *  the galactic coordinate system.
  354.  */
  355. Vec3d astro::equatorialToGalactic(const Vec3d& v)
  356. {
  357.     return v * EQUATORIAL_TO_GALACTIC_MATRIX;
  358. }
  359. astro::Date::Date()
  360. {
  361.     year = 0;
  362.     month = 0;
  363.     day = 0;
  364.     hour = 0;
  365.     minute = 0;
  366.     seconds = 0.0;
  367.     wday = 0;
  368.     utc_offset = 0;
  369.     tzname = "UTC";
  370. }
  371. astro::Date::Date(int Y, int M, int D)
  372. {
  373.     year = Y;
  374.     month = M;
  375.     day = D;
  376.     hour = 0;
  377.     minute = 0;
  378.     seconds = 0.0;
  379.     wday = 0;
  380.     utc_offset = 0;
  381.     tzname = "UTC";
  382. }
  383. astro::Date::Date(double jd)
  384. {
  385.     int64 a = (int64) floor(jd + 0.5);
  386.     wday = (a + 1) % 7;
  387.     double c;
  388.     if (a < 2299161)
  389.     {
  390.         c = (double) (a + 1524);
  391.     }
  392.     else
  393.     {
  394.         double b = (double) ((int64) floor((a - 1867216.25) / 36524.25));
  395.         c = a + b - (int64) floor(b / 4) + 1525;
  396.     }
  397.     int64 d = (int64) floor((c - 122.1) / 365.25);
  398.     int64 e = (int64) floor(365.25 * d);
  399.     int64 f = (int64) floor((c - e) / 30.6001);
  400.     double dday = c - e - (int64) floor(30.6001 * f) + ((jd + 0.5) - a);
  401.     // This following used to be 14.0, but gcc was computing it incorrectly, so
  402.     // it was changed to 14
  403.     month = (int) (f - 1 - 12 * (int64) (f / 14));
  404.     year = (int) (d - 4715 - (int64) ((7.0 + month) / 10.0));
  405.     day = (int) dday;
  406.     double dhour = (dday - day) * 24;
  407.     hour = (int) dhour;
  408.     double dminute = (dhour - hour) * 60;
  409.     minute = (int) dminute;
  410.     seconds = (dminute - minute) * 60;
  411.     utc_offset = 0;
  412.     tzname = "UTC";
  413. }
  414. const char* astro::Date::toCStr(Format format) const
  415. {
  416.     static char date[255];
  417.     // MinGW's libraries don't have the tm_gmtoff and tm_zone fields for
  418.     // struct tm.
  419. #if defined(__GNUC__) && !defined(_WIN32)
  420.     struct tm cal_time;
  421.     memset(&cal_time, 0, sizeof(cal_time));
  422.     cal_time.tm_year = year-1900;
  423.     cal_time.tm_mon = month-1;
  424.     cal_time.tm_mday = day;
  425.     cal_time.tm_hour = hour;
  426.     cal_time.tm_min = minute;
  427.     cal_time.tm_sec = (int)seconds;
  428.     cal_time.tm_wday = wday;
  429.     cal_time.tm_gmtoff = utc_offset;
  430. #if defined(TARGET_OS_MAC) || defined(__FreeBSD__)
  431.     // tm_zone is a non-const string field on the Mac and FreeBSD (why?)
  432.     cal_time.tm_zone = const_cast<char*>(tzname.c_str());
  433. #else
  434.     cal_time.tm_zone = tzname.c_str();
  435. #endif
  436.     const char* strftime_format;
  437.     switch(format)
  438.     {
  439.     case Locale:
  440.         strftime_format = "%c";
  441.         break;
  442.     case TZName:
  443.         strftime_format = "%Y %b %d %H:%M:%S %Z";
  444.         break;
  445.     default:
  446.         strftime_format = "%Y %b %d %H:%M:%S %z";
  447.         break;
  448.     }
  449.     strftime(date, sizeof(date), strftime_format, &cal_time);
  450. #else
  451.     switch(format)
  452.     {
  453.     case Locale:
  454.     case TZName:
  455.         snprintf(date, sizeof(date), "%04d %s %02d %02d:%02d:%02d %s", 
  456.                  year, _(MonthAbbrList[month-1]), day, 
  457.                  hour, minute, (int)seconds, tzname.c_str());
  458.         break;
  459.     case UTCOffset:
  460.         {
  461.             int sign = utc_offset < 0 ? -1:1;
  462.             int h_offset = sign * utc_offset / 3600;
  463.             int m_offset = (sign * utc_offset - h_offset * 3600) / 60;
  464.             snprintf(date, sizeof(date), "%04d %s %02d %02d:%02d:%02d %c%02d%02d", 
  465.                     year, _(MonthAbbrList[month-1]), day, 
  466.                     hour, minute, (int)seconds, (sign==1?'+':'-'), h_offset, m_offset);
  467.         }
  468.         break;
  469.     }
  470. #endif
  471.     return date;
  472. }
  473. // Convert a calendar date to a Julian date
  474. astro::Date::operator double() const
  475. {
  476.     int y = year, m = month;
  477.     if (month <= 2)
  478.     {
  479.         y = year - 1;
  480.         m = month + 12;
  481.     }
  482.     // Correct for the lost days in Oct 1582 when the Gregorian calendar
  483.     // replaced the Julian calendar.
  484.     int B = -2;
  485.     if (year > 1582 || (year == 1582 && (month > 10 || (month == 10 && day >= 15))))
  486.     {
  487.         B = y / 400 - y / 100;
  488.     }
  489.     return (floor(365.25 * y) +
  490.             floor(30.6001 * (m + 1)) + B + 1720996.5 +
  491.             day + hour / 24.0 + minute / 1440.0 + seconds / 86400.0);
  492. }
  493. // TODO: need option to parse UTC times (with leap seconds)
  494. bool astro::parseDate(const string& s, astro::Date& date)
  495. {
  496.     int year = 0;
  497.     unsigned int month = 1;
  498.     unsigned int day = 1;
  499.     unsigned int hour = 0;
  500.     unsigned int minute = 0;
  501.     double second = 0.0;
  502.     if (sscanf(s.c_str(), " %d %u %u %u:%u:%lf ",
  503.                &year, &month, &day, &hour, &minute, &second) == 6 ||
  504.         sscanf(s.c_str(), " %d %u %u %u:%u ",
  505.                &year, &month, &day, &hour, &minute) == 5 ||
  506.         sscanf(s.c_str(), " %d %u %u ", &year, &month, &day) == 3)
  507.     {
  508.         if (month < 1 || month > 12)
  509.             return false;
  510.         if (hour > 23 || minute > 59 || second >= 60.0 || second < 0.0)
  511.             return false;
  512.         // Days / month calculation . . .
  513.         int maxDay = 31 - ((0xa50 >> month) & 0x1);
  514.         if (month == 2)
  515.         {
  516.             // Check for a leap year
  517.             if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
  518.                 maxDay = 29;
  519.             else
  520.                 maxDay = 28;
  521.         }
  522.         if (day > (unsigned int) maxDay || day < 1)
  523.             return false;
  524.         date.year = year;
  525.         date.month = month;
  526.         date.day = day;
  527.         date.hour = hour;
  528.         date.minute = minute;
  529.         date.seconds = second;
  530.         return true;
  531.     }
  532.     return false;
  533. }
  534. astro::Date
  535. astro::Date::systemDate()
  536. {
  537.     time_t t = time(NULL);
  538.     struct tm *gmt = gmtime(&t);
  539.     astro::Date d;
  540.     d.year = gmt->tm_year + 1900;
  541.     d.month = gmt->tm_mon + 1;
  542.     d.day = gmt->tm_mday;
  543.     d.hour = gmt->tm_hour;
  544.     d.minute = gmt->tm_min;
  545.     d.seconds = (int) gmt->tm_sec;
  546.     
  547.     return d;
  548. }
  549. ostream& operator<<(ostream& s, const astro::Date d)
  550. {
  551.     s << d.toCStr();
  552.     return s;
  553. }
  554. /********* Time scale conversion functions ***********/
  555. // Convert from Atomic Time to UTC
  556. astro::Date
  557. astro::TAItoUTC(double tai)
  558. {
  559.     unsigned int nRecords = sizeof(LeapSeconds) / sizeof(LeapSeconds[0]);
  560.     double dAT = LeapSeconds[0].seconds;
  561.     /*double dD = 0.0;  Unused*/
  562.     int extraSecs = 0;
  563.     for (unsigned int i = nRecords - 1; i > 0; i--)
  564.     {
  565.         if (tai - secsToDays(LeapSeconds[i].seconds) >= LeapSeconds[i].t)
  566.         {
  567.             dAT = LeapSeconds[i].seconds;
  568.             break;
  569.         }
  570.         else if (tai - secsToDays(LeapSeconds[i - 1].seconds) >= LeapSeconds[i].t)
  571.         {
  572.             dAT = LeapSeconds[i].seconds;
  573.             extraSecs = LeapSeconds[i].seconds - LeapSeconds[i - 1].seconds;
  574.             break;
  575.         }
  576.     }
  577.     Date utcDate(tai - secsToDays(dAT));
  578.     utcDate.seconds += extraSecs;
  579.     return utcDate;
  580. }
  581. // Convert from UTC to Atomic Time
  582. double
  583. astro::UTCtoTAI(const astro::Date& utc)
  584. {
  585.     unsigned int nRecords = sizeof(LeapSeconds) / sizeof(LeapSeconds[0]);
  586.     double dAT = LeapSeconds[0].seconds;
  587.     double utcjd = (double) Date(utc.year, utc.month, utc.day);
  588.     for (unsigned int i = nRecords - 1; i > 0; i--)
  589.     {
  590.         if (utcjd >= LeapSeconds[i].t)
  591.         {
  592.             dAT = LeapSeconds[i].seconds;
  593.             break;
  594.         }
  595.     }
  596.     double tai = utcjd + secsToDays(utc.hour * 3600.0 + utc.minute * 60.0 + utc.seconds + dAT);
  597.     return tai;
  598. }
  599. // Convert from Terrestrial Time to Atomic Time
  600. double
  601. astro::TTtoTAI(double tt)
  602. {
  603.     return tt - secsToDays(dTA);
  604. }
  605. // Convert from Atomic Time to Terrestrial TIme
  606. double
  607. astro::TAItoTT(double tai)
  608. {
  609.     return tai + secsToDays(dTA);
  610. }
  611. // Correction for converting from Terrestrial Time to Barycentric Dynamical
  612. // Time. Constants and algorithm from "Time Routines in CSPICE",
  613. // http://sohowww.nascom.nasa.gov/solarsoft/stereo/gen/exe/icy/doc/time.req
  614. static const double K  = 1.657e-3;
  615. static const double EB = 1.671e-2;
  616. static const double M0 = 6.239996;
  617. static const double M1 = 1.99096871e-7;
  618. // Input is a TDB Julian Date; result is in seconds
  619. double TDBcorrection(double tdb)
  620. {
  621.     // t is seconds from J2000.0
  622.     double t = astro::daysToSecs(tdb - astro::J2000);
  623.     // Approximate calculation of Earth's mean anomaly
  624.     double M = M0 + M1 * t;
  625.     // Compute the eccentric anomaly
  626.     double E = M + EB * sin(M);
  627.     return K * sin(E);
  628. }
  629. // Convert from Terrestrial Time to Barycentric Dynamical Time
  630. double
  631. astro::TTtoTDB(double tt)
  632. {
  633.     return tt + secsToDays(TDBcorrection(tt));
  634. }
  635. // Convert from Barycentric Dynamical Time to Terrestrial Time
  636. double
  637. astro::TDBtoTT(double tdb)
  638. {
  639.     return tdb - secsToDays(TDBcorrection(tdb));
  640. }
  641. // Convert from Coordinated Universal time to Barycentric Dynamical Time
  642. astro::Date
  643. astro::TDBtoUTC(double tdb)
  644. {
  645.     return TAItoUTC(TTtoTAI(TDBtoTT(tdb)));
  646. }
  647. // Convert from Barycentric Dynamical Time to local calendar if possible
  648. // otherwise convert to UTC
  649. astro::Date
  650. astro::TDBtoLocal(double tdb)
  651. {
  652.     double tai = astro::TTtoTAI(astro::TDBtoTT(tdb));
  653.     double jdutc = astro::TAItoJDUTC(tai);
  654.     if (jdutc < 2465442 &&
  655.         jdutc > 2415733)
  656.     {
  657.         time_t time = (int) astro::julianDateToSeconds(jdutc - 2440587.5);
  658.         struct tm *localt = localtime(&time);
  659.         if (localt != NULL)
  660.         {
  661.             astro::Date d;
  662.             d.year = localt->tm_year + 1900;
  663.             d.month = localt->tm_mon + 1;
  664.             d.day = localt->tm_mday;
  665.             d.hour = localt->tm_hour;
  666.             d.minute = localt->tm_min;
  667.             d.seconds = (int) localt->tm_sec;
  668.             d.wday = localt->tm_wday;
  669.         #if defined(__GNUC__) && !defined(_WIN32)
  670.             d.utc_offset = localt->tm_gmtoff;
  671.             d.tzname = localt->tm_zone;
  672.         #else
  673.             {
  674.                 astro::Date utcDate = astro::TAItoUTC(tai);
  675.                 int daydiff = d.day - utcDate.day;
  676.                 int hourdiff;
  677.                 if (daydiff == 0)
  678.                     hourdiff = 0;
  679.                 else if (daydiff > 1 || daydiff == -1)
  680.                     hourdiff = -24;
  681.                 else
  682.                     hourdiff = 24;
  683.                 d.utc_offset = (hourdiff + d.hour - utcDate.hour) * 3600 
  684.                              + (d.minute - utcDate.minute) * 60;
  685.             }
  686.             d.tzname = localt->tm_isdst ? _("DST"): _("STD");
  687.         #endif
  688.             return d;
  689.         }
  690.     }
  691.     return astro::TDBtoUTC(tdb);
  692. }
  693. // Convert from Barycentric Dynamical Time to UTC
  694. double
  695. astro::UTCtoTDB(const astro::Date& utc)
  696. {
  697.     return TTtoTDB(TAItoTT(UTCtoTAI(utc)));
  698. }
  699. // Convert from TAI to Julian Date UTC. The Julian Date UTC functions should
  700. // generally be avoided because there's no provision for dealing with leap
  701. // seconds.
  702. double
  703. astro::JDUTCtoTAI(double utc)
  704. {
  705.     unsigned int nRecords = sizeof(LeapSeconds) / sizeof(LeapSeconds[0]);
  706.     double dAT = LeapSeconds[0].seconds;
  707.     for (unsigned int i = nRecords - 1; i > 0; i--)
  708.     {
  709.         if (utc > LeapSeconds[i].t)
  710.         {
  711.             dAT = LeapSeconds[i].seconds;
  712.             break;
  713.         }
  714.     }
  715.     return utc + secsToDays(dAT);
  716. }
  717. // Convert from Julian Date UTC to TAI
  718. double
  719. astro::TAItoJDUTC(double tai)
  720. {
  721.     unsigned int nRecords = sizeof(LeapSeconds) / sizeof(LeapSeconds[0]);
  722.     double dAT = LeapSeconds[0].seconds;
  723.     for (unsigned int i = nRecords - 1; i > 0; i--)
  724.     {
  725.         if (tai - secsToDays(LeapSeconds[i - 1].seconds) > LeapSeconds[i].t)
  726.         {
  727.             dAT = LeapSeconds[i].seconds;
  728.             break;
  729.         }
  730.     }
  731.     return tai - secsToDays(dAT);
  732. }