From 0943f058c9f8a05021479c55eb2d774e2ff154c6 Mon Sep 17 00:00:00 2001 From: Don Cross Date: Tue, 15 Mar 2022 20:48:02 -0400 Subject: [PATCH] Fixed #165 - expose sidereal time function. There was already an internal function for calculating Greenwich Apparent Sidereal Time (GAST). By request, I have exposed this function for outside users. Added a minimal unit test to verify the function is callable and returns the correct result for one case. This function is already exhaustively tested by unit tests that verify other functions that already called this function when it was internal, so minimal testing is sufficient in this case. --- README.md | 2 +- demo/browser/astronomy.browser.js | 34 +++++- demo/nodejs/astronomy.js | 34 +++++- demo/nodejs/calendar/astronomy.ts | 28 +++++ demo/python/astronomy.py | 47 ++++++-- generate/ctest.c | 24 ++++ generate/dotnet/csharp_test/csharp_test.cs | 19 ++++ generate/template/astronomy.c | 44 ++++++-- generate/template/astronomy.cs | 39 +++++-- generate/template/astronomy.py | 47 ++++++-- generate/template/astronomy.ts | 28 +++++ generate/test.js | 16 +++ generate/test.py | 15 +++ source/c/README.md | 24 ++++ source/c/astronomy.c | 44 ++++++-- source/c/astronomy.h | 1 + source/csharp/README.md | 26 +++++ source/csharp/astronomy.cs | 39 +++++-- source/js/README.md | 28 +++++ source/js/astronomy.browser.js | 34 +++++- source/js/astronomy.browser.min.js | 122 ++++++++++----------- source/js/astronomy.d.ts | 24 ++++ source/js/astronomy.js | 34 +++++- source/js/astronomy.min.js | 18 +-- source/js/astronomy.ts | 28 +++++ source/js/esm/astronomy.js | 27 +++++ source/python/README.md | 29 +++++ source/python/astronomy.py | 47 ++++++-- 28 files changed, 760 insertions(+), 142 deletions(-) diff --git a/README.md b/README.md index 8f31eb9d..d2c876d1 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,7 @@ of complexity. So I decided to create Astronomy Engine with the following engine - Support JavaScript, C, C#, and Python with the same algorithms, and verify them to produce identical results. - No external dependencies! The code must not require anything outside the standard library for each language. -- Minified JavaScript code less than 120K. (The current size is 108817 bytes.) +- Minified JavaScript code less than 120K. (The current size is 108883 bytes.) - Accuracy always within 1 arcminute of results from NOVAS. - It would be well documented, relatively easy to use, and support a wide variety of common use cases. diff --git a/demo/browser/astronomy.browser.js b/demo/browser/astronomy.browser.js index 3d0f5ef8..032b36d8 100644 --- a/demo/browser/astronomy.browser.js +++ b/demo/browser/astronomy.browser.js @@ -34,9 +34,9 @@ */ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); -exports.GeoVector = exports.HelioDistance = exports.HelioVector = exports.JupiterMoons = exports.JupiterMoonsInfo = exports.GeoEmbState = exports.GeoMoonState = exports.EclipticGeoMoon = exports.GeoMoon = exports.Ecliptic = exports.ObserverGravity = exports.VectorObserver = exports.ObserverState = exports.ObserverVector = exports.Equator = exports.SunPosition = exports.Observer = exports.Horizon = exports.EclipticCoordinates = exports.HorizontalCoordinates = exports.MakeRotation = exports.RotationMatrix = exports.EquatorialCoordinates = exports.Spherical = exports.StateVector = exports.Vector = exports.Libration = exports.LibrationInfo = exports.CalcMoonCount = exports.MakeTime = exports.AstroTime = exports.SetDeltaTFunction = exports.DeltaT_JplHorizons = exports.DeltaT_EspenakMeeus = exports.Body = exports.AngleBetween = exports.MassProduct = exports.CALLISTO_RADIUS_KM = exports.GANYMEDE_RADIUS_KM = exports.EUROPA_RADIUS_KM = exports.IO_RADIUS_KM = exports.JUPITER_MEAN_RADIUS_KM = exports.JUPITER_POLAR_RADIUS_KM = exports.JUPITER_EQUATORIAL_RADIUS_KM = exports.RAD2HOUR = exports.RAD2DEG = exports.HOUR2RAD = exports.DEG2RAD = exports.KM_PER_AU = exports.C_AUDAY = void 0; -exports.Rotation_HOR_EQJ = exports.Rotation_HOR_EQD = exports.Rotation_EQD_HOR = exports.Rotation_EQD_EQJ = exports.Rotation_EQJ_EQD = exports.Rotation_ECL_EQJ = exports.Rotation_EQJ_ECL = exports.RotateState = exports.RotateVector = exports.InverseRefraction = exports.Refraction = exports.VectorFromHorizon = exports.HorizonFromVector = exports.SphereFromVector = exports.EquatorFromVector = exports.VectorFromSphere = exports.Pivot = exports.IdentityMatrix = exports.CombineRotation = exports.InverseRotation = exports.NextPlanetApsis = exports.SearchPlanetApsis = exports.NextLunarApsis = exports.SearchLunarApsis = exports.Apsis = exports.SearchPeakMagnitude = exports.SearchMaxElongation = exports.Elongation = exports.ElongationEvent = exports.Seasons = exports.SeasonInfo = exports.SearchHourAngle = exports.HourAngleEvent = exports.SearchAltitude = exports.SearchRiseSet = exports.NextMoonQuarter = exports.SearchMoonQuarter = exports.MoonQuarter = exports.SearchMoonPhase = exports.MoonPhase = exports.SearchRelativeLongitude = exports.Illumination = exports.IlluminationInfo = exports.EclipticLongitude = exports.AngleFromSun = exports.PairLongitude = exports.SearchSunLongitude = exports.Search = exports.HelioState = exports.BaryState = void 0; -exports.LagrangePointFast = exports.LagrangePoint = exports.RotationAxis = exports.AxisInfo = exports.NextMoonNode = exports.SearchMoonNode = exports.NodeEventInfo = exports.NodeEventKind = exports.NextTransit = exports.SearchTransit = exports.TransitInfo = exports.NextLocalSolarEclipse = exports.SearchLocalSolarEclipse = exports.LocalSolarEclipseInfo = exports.EclipseEvent = exports.NextGlobalSolarEclipse = exports.SearchGlobalSolarEclipse = exports.NextLunarEclipse = exports.GlobalSolarEclipseInfo = exports.SearchLunarEclipse = exports.LunarEclipseInfo = exports.Constellation = exports.ConstellationInfo = exports.Rotation_GAL_EQJ = exports.Rotation_EQJ_GAL = exports.Rotation_HOR_ECL = exports.Rotation_ECL_HOR = exports.Rotation_ECL_EQD = exports.Rotation_EQD_ECL = exports.Rotation_EQJ_HOR = void 0; +exports.HelioDistance = exports.HelioVector = exports.JupiterMoons = exports.JupiterMoonsInfo = exports.GeoEmbState = exports.GeoMoonState = exports.EclipticGeoMoon = exports.GeoMoon = exports.Ecliptic = exports.ObserverGravity = exports.VectorObserver = exports.ObserverState = exports.ObserverVector = exports.Equator = exports.SunPosition = exports.Observer = exports.Horizon = exports.EclipticCoordinates = exports.HorizontalCoordinates = exports.MakeRotation = exports.RotationMatrix = exports.EquatorialCoordinates = exports.Spherical = exports.StateVector = exports.Vector = exports.SiderealTime = exports.Libration = exports.LibrationInfo = exports.CalcMoonCount = exports.MakeTime = exports.AstroTime = exports.SetDeltaTFunction = exports.DeltaT_JplHorizons = exports.DeltaT_EspenakMeeus = exports.Body = exports.AngleBetween = exports.MassProduct = exports.CALLISTO_RADIUS_KM = exports.GANYMEDE_RADIUS_KM = exports.EUROPA_RADIUS_KM = exports.IO_RADIUS_KM = exports.JUPITER_MEAN_RADIUS_KM = exports.JUPITER_POLAR_RADIUS_KM = exports.JUPITER_EQUATORIAL_RADIUS_KM = exports.RAD2HOUR = exports.RAD2DEG = exports.HOUR2RAD = exports.DEG2RAD = exports.KM_PER_AU = exports.C_AUDAY = void 0; +exports.Rotation_HOR_EQD = exports.Rotation_EQD_HOR = exports.Rotation_EQD_EQJ = exports.Rotation_EQJ_EQD = exports.Rotation_ECL_EQJ = exports.Rotation_EQJ_ECL = exports.RotateState = exports.RotateVector = exports.InverseRefraction = exports.Refraction = exports.VectorFromHorizon = exports.HorizonFromVector = exports.SphereFromVector = exports.EquatorFromVector = exports.VectorFromSphere = exports.Pivot = exports.IdentityMatrix = exports.CombineRotation = exports.InverseRotation = exports.NextPlanetApsis = exports.SearchPlanetApsis = exports.NextLunarApsis = exports.SearchLunarApsis = exports.Apsis = exports.SearchPeakMagnitude = exports.SearchMaxElongation = exports.Elongation = exports.ElongationEvent = exports.Seasons = exports.SeasonInfo = exports.SearchHourAngle = exports.HourAngleEvent = exports.SearchAltitude = exports.SearchRiseSet = exports.NextMoonQuarter = exports.SearchMoonQuarter = exports.MoonQuarter = exports.SearchMoonPhase = exports.MoonPhase = exports.SearchRelativeLongitude = exports.Illumination = exports.IlluminationInfo = exports.EclipticLongitude = exports.AngleFromSun = exports.PairLongitude = exports.SearchSunLongitude = exports.Search = exports.HelioState = exports.BaryState = exports.GeoVector = void 0; +exports.LagrangePointFast = exports.LagrangePoint = exports.RotationAxis = exports.AxisInfo = exports.NextMoonNode = exports.SearchMoonNode = exports.NodeEventInfo = exports.NodeEventKind = exports.NextTransit = exports.SearchTransit = exports.TransitInfo = exports.NextLocalSolarEclipse = exports.SearchLocalSolarEclipse = exports.LocalSolarEclipseInfo = exports.EclipseEvent = exports.NextGlobalSolarEclipse = exports.SearchGlobalSolarEclipse = exports.NextLunarEclipse = exports.GlobalSolarEclipseInfo = exports.SearchLunarEclipse = exports.LunarEclipseInfo = exports.Constellation = exports.ConstellationInfo = exports.Rotation_GAL_EQJ = exports.Rotation_EQJ_GAL = exports.Rotation_HOR_ECL = exports.Rotation_ECL_HOR = exports.Rotation_ECL_EQD = exports.Rotation_EQD_ECL = exports.Rotation_EQJ_HOR = exports.Rotation_HOR_EQJ = void 0; /** * @brief The speed of light in AU/day. */ @@ -1849,6 +1849,34 @@ function sidereal_time(time) { } return sidereal_time_cache.st; // return sidereal hours in the half-open range [0, 24). } +/** + * @brief Calculates Greenwich Apparent Sidereal Time (GAST). + * + * Given a date and time, this function calculates the rotation of the + * Earth, represented by the equatorial angle of the Greenwich prime meridian + * with respect to distant stars (not the Sun, which moves relative to background + * stars by almost one degree per day). + * This angle is called Greenwich Apparent Sidereal Time (GAST). + * GAST is measured in sidereal hours in the half-open range [0, 24). + * When GAST = 0, it means the prime meridian is aligned with the of-date equinox, + * corrected at that time for precession and nutation of the Earth's axis. + * In this context, the "equinox" is the direction in space where the Earth's + * orbital plane (the ecliptic) intersects with the plane of the Earth's equator, + * at the location on the Earth's orbit of the (seasonal) March equinox. + * As the Earth rotates, GAST increases from 0 up to 24 sidereal hours, + * then starts over at 0. + * To convert to degrees, multiply the return value by 15. + * + * @param {FlexibleDateTime} date + * The date and time for which to find GAST. + * + * @returns {number} + */ +function SiderealTime(date) { + const time = MakeTime(date); + return sidereal_time(time); +} +exports.SiderealTime = SiderealTime; function inverse_terra(ovec, st) { // Convert from AU to kilometers const x = ovec[0] * exports.KM_PER_AU; diff --git a/demo/nodejs/astronomy.js b/demo/nodejs/astronomy.js index f27ede14..498dd34a 100644 --- a/demo/nodejs/astronomy.js +++ b/demo/nodejs/astronomy.js @@ -33,9 +33,9 @@ */ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); -exports.GeoVector = exports.HelioDistance = exports.HelioVector = exports.JupiterMoons = exports.JupiterMoonsInfo = exports.GeoEmbState = exports.GeoMoonState = exports.EclipticGeoMoon = exports.GeoMoon = exports.Ecliptic = exports.ObserverGravity = exports.VectorObserver = exports.ObserverState = exports.ObserverVector = exports.Equator = exports.SunPosition = exports.Observer = exports.Horizon = exports.EclipticCoordinates = exports.HorizontalCoordinates = exports.MakeRotation = exports.RotationMatrix = exports.EquatorialCoordinates = exports.Spherical = exports.StateVector = exports.Vector = exports.Libration = exports.LibrationInfo = exports.CalcMoonCount = exports.MakeTime = exports.AstroTime = exports.SetDeltaTFunction = exports.DeltaT_JplHorizons = exports.DeltaT_EspenakMeeus = exports.Body = exports.AngleBetween = exports.MassProduct = exports.CALLISTO_RADIUS_KM = exports.GANYMEDE_RADIUS_KM = exports.EUROPA_RADIUS_KM = exports.IO_RADIUS_KM = exports.JUPITER_MEAN_RADIUS_KM = exports.JUPITER_POLAR_RADIUS_KM = exports.JUPITER_EQUATORIAL_RADIUS_KM = exports.RAD2HOUR = exports.RAD2DEG = exports.HOUR2RAD = exports.DEG2RAD = exports.KM_PER_AU = exports.C_AUDAY = void 0; -exports.Rotation_HOR_EQJ = exports.Rotation_HOR_EQD = exports.Rotation_EQD_HOR = exports.Rotation_EQD_EQJ = exports.Rotation_EQJ_EQD = exports.Rotation_ECL_EQJ = exports.Rotation_EQJ_ECL = exports.RotateState = exports.RotateVector = exports.InverseRefraction = exports.Refraction = exports.VectorFromHorizon = exports.HorizonFromVector = exports.SphereFromVector = exports.EquatorFromVector = exports.VectorFromSphere = exports.Pivot = exports.IdentityMatrix = exports.CombineRotation = exports.InverseRotation = exports.NextPlanetApsis = exports.SearchPlanetApsis = exports.NextLunarApsis = exports.SearchLunarApsis = exports.Apsis = exports.SearchPeakMagnitude = exports.SearchMaxElongation = exports.Elongation = exports.ElongationEvent = exports.Seasons = exports.SeasonInfo = exports.SearchHourAngle = exports.HourAngleEvent = exports.SearchAltitude = exports.SearchRiseSet = exports.NextMoonQuarter = exports.SearchMoonQuarter = exports.MoonQuarter = exports.SearchMoonPhase = exports.MoonPhase = exports.SearchRelativeLongitude = exports.Illumination = exports.IlluminationInfo = exports.EclipticLongitude = exports.AngleFromSun = exports.PairLongitude = exports.SearchSunLongitude = exports.Search = exports.HelioState = exports.BaryState = void 0; -exports.LagrangePointFast = exports.LagrangePoint = exports.RotationAxis = exports.AxisInfo = exports.NextMoonNode = exports.SearchMoonNode = exports.NodeEventInfo = exports.NodeEventKind = exports.NextTransit = exports.SearchTransit = exports.TransitInfo = exports.NextLocalSolarEclipse = exports.SearchLocalSolarEclipse = exports.LocalSolarEclipseInfo = exports.EclipseEvent = exports.NextGlobalSolarEclipse = exports.SearchGlobalSolarEclipse = exports.NextLunarEclipse = exports.GlobalSolarEclipseInfo = exports.SearchLunarEclipse = exports.LunarEclipseInfo = exports.Constellation = exports.ConstellationInfo = exports.Rotation_GAL_EQJ = exports.Rotation_EQJ_GAL = exports.Rotation_HOR_ECL = exports.Rotation_ECL_HOR = exports.Rotation_ECL_EQD = exports.Rotation_EQD_ECL = exports.Rotation_EQJ_HOR = void 0; +exports.HelioDistance = exports.HelioVector = exports.JupiterMoons = exports.JupiterMoonsInfo = exports.GeoEmbState = exports.GeoMoonState = exports.EclipticGeoMoon = exports.GeoMoon = exports.Ecliptic = exports.ObserverGravity = exports.VectorObserver = exports.ObserverState = exports.ObserverVector = exports.Equator = exports.SunPosition = exports.Observer = exports.Horizon = exports.EclipticCoordinates = exports.HorizontalCoordinates = exports.MakeRotation = exports.RotationMatrix = exports.EquatorialCoordinates = exports.Spherical = exports.StateVector = exports.Vector = exports.SiderealTime = exports.Libration = exports.LibrationInfo = exports.CalcMoonCount = exports.MakeTime = exports.AstroTime = exports.SetDeltaTFunction = exports.DeltaT_JplHorizons = exports.DeltaT_EspenakMeeus = exports.Body = exports.AngleBetween = exports.MassProduct = exports.CALLISTO_RADIUS_KM = exports.GANYMEDE_RADIUS_KM = exports.EUROPA_RADIUS_KM = exports.IO_RADIUS_KM = exports.JUPITER_MEAN_RADIUS_KM = exports.JUPITER_POLAR_RADIUS_KM = exports.JUPITER_EQUATORIAL_RADIUS_KM = exports.RAD2HOUR = exports.RAD2DEG = exports.HOUR2RAD = exports.DEG2RAD = exports.KM_PER_AU = exports.C_AUDAY = void 0; +exports.Rotation_HOR_EQD = exports.Rotation_EQD_HOR = exports.Rotation_EQD_EQJ = exports.Rotation_EQJ_EQD = exports.Rotation_ECL_EQJ = exports.Rotation_EQJ_ECL = exports.RotateState = exports.RotateVector = exports.InverseRefraction = exports.Refraction = exports.VectorFromHorizon = exports.HorizonFromVector = exports.SphereFromVector = exports.EquatorFromVector = exports.VectorFromSphere = exports.Pivot = exports.IdentityMatrix = exports.CombineRotation = exports.InverseRotation = exports.NextPlanetApsis = exports.SearchPlanetApsis = exports.NextLunarApsis = exports.SearchLunarApsis = exports.Apsis = exports.SearchPeakMagnitude = exports.SearchMaxElongation = exports.Elongation = exports.ElongationEvent = exports.Seasons = exports.SeasonInfo = exports.SearchHourAngle = exports.HourAngleEvent = exports.SearchAltitude = exports.SearchRiseSet = exports.NextMoonQuarter = exports.SearchMoonQuarter = exports.MoonQuarter = exports.SearchMoonPhase = exports.MoonPhase = exports.SearchRelativeLongitude = exports.Illumination = exports.IlluminationInfo = exports.EclipticLongitude = exports.AngleFromSun = exports.PairLongitude = exports.SearchSunLongitude = exports.Search = exports.HelioState = exports.BaryState = exports.GeoVector = void 0; +exports.LagrangePointFast = exports.LagrangePoint = exports.RotationAxis = exports.AxisInfo = exports.NextMoonNode = exports.SearchMoonNode = exports.NodeEventInfo = exports.NodeEventKind = exports.NextTransit = exports.SearchTransit = exports.TransitInfo = exports.NextLocalSolarEclipse = exports.SearchLocalSolarEclipse = exports.LocalSolarEclipseInfo = exports.EclipseEvent = exports.NextGlobalSolarEclipse = exports.SearchGlobalSolarEclipse = exports.NextLunarEclipse = exports.GlobalSolarEclipseInfo = exports.SearchLunarEclipse = exports.LunarEclipseInfo = exports.Constellation = exports.ConstellationInfo = exports.Rotation_GAL_EQJ = exports.Rotation_EQJ_GAL = exports.Rotation_HOR_ECL = exports.Rotation_ECL_HOR = exports.Rotation_ECL_EQD = exports.Rotation_EQD_ECL = exports.Rotation_EQJ_HOR = exports.Rotation_HOR_EQJ = void 0; /** * @brief The speed of light in AU/day. */ @@ -1848,6 +1848,34 @@ function sidereal_time(time) { } return sidereal_time_cache.st; // return sidereal hours in the half-open range [0, 24). } +/** + * @brief Calculates Greenwich Apparent Sidereal Time (GAST). + * + * Given a date and time, this function calculates the rotation of the + * Earth, represented by the equatorial angle of the Greenwich prime meridian + * with respect to distant stars (not the Sun, which moves relative to background + * stars by almost one degree per day). + * This angle is called Greenwich Apparent Sidereal Time (GAST). + * GAST is measured in sidereal hours in the half-open range [0, 24). + * When GAST = 0, it means the prime meridian is aligned with the of-date equinox, + * corrected at that time for precession and nutation of the Earth's axis. + * In this context, the "equinox" is the direction in space where the Earth's + * orbital plane (the ecliptic) intersects with the plane of the Earth's equator, + * at the location on the Earth's orbit of the (seasonal) March equinox. + * As the Earth rotates, GAST increases from 0 up to 24 sidereal hours, + * then starts over at 0. + * To convert to degrees, multiply the return value by 15. + * + * @param {FlexibleDateTime} date + * The date and time for which to find GAST. + * + * @returns {number} + */ +function SiderealTime(date) { + const time = MakeTime(date); + return sidereal_time(time); +} +exports.SiderealTime = SiderealTime; function inverse_terra(ovec, st) { // Convert from AU to kilometers const x = ovec[0] * exports.KM_PER_AU; diff --git a/demo/nodejs/calendar/astronomy.ts b/demo/nodejs/calendar/astronomy.ts index 298c818c..43990850 100644 --- a/demo/nodejs/calendar/astronomy.ts +++ b/demo/nodejs/calendar/astronomy.ts @@ -1997,6 +1997,34 @@ function sidereal_time(time: AstroTime): number { // calculates Greenwi return sidereal_time_cache.st; // return sidereal hours in the half-open range [0, 24). } +/** + * @brief Calculates Greenwich Apparent Sidereal Time (GAST). + * + * Given a date and time, this function calculates the rotation of the + * Earth, represented by the equatorial angle of the Greenwich prime meridian + * with respect to distant stars (not the Sun, which moves relative to background + * stars by almost one degree per day). + * This angle is called Greenwich Apparent Sidereal Time (GAST). + * GAST is measured in sidereal hours in the half-open range [0, 24). + * When GAST = 0, it means the prime meridian is aligned with the of-date equinox, + * corrected at that time for precession and nutation of the Earth's axis. + * In this context, the "equinox" is the direction in space where the Earth's + * orbital plane (the ecliptic) intersects with the plane of the Earth's equator, + * at the location on the Earth's orbit of the (seasonal) March equinox. + * As the Earth rotates, GAST increases from 0 up to 24 sidereal hours, + * then starts over at 0. + * To convert to degrees, multiply the return value by 15. + * + * @param {FlexibleDateTime} date + * The date and time for which to find GAST. + * + * @returns {number} + */ +export function SiderealTime(date: FlexibleDateTime): number { + const time = MakeTime(date); + return sidereal_time(time); +} + function inverse_terra(ovec: ArrayVector, st: number): Observer { // Convert from AU to kilometers const x = ovec[0] * KM_PER_AU; diff --git a/demo/python/astronomy.py b/demo/python/astronomy.py index 7d68627b..4aeef96b 100644 --- a/demo/python/astronomy.py +++ b/demo/python/astronomy.py @@ -1618,7 +1618,36 @@ def _era(time): # Earth Rotation Angle theta += 360.0 return theta -def _sidereal_time(time): +def SiderealTime(time): + """Calculates Greenwich Apparent Sidereal Time (GAST). + + Given a date and time, this function calculates the rotation of the + Earth, represented by the equatorial angle of the Greenwich prime meridian + with respect to distant stars (not the Sun, which moves relative to background + stars by almost one degree per day). + This angle is called Greenwich Apparent Sidereal Time (GAST). + GAST is measured in sidereal hours in the half-open range [0, 24). + When GAST = 0, it means the prime meridian is aligned with the of-date equinox, + corrected at that time for precession and nutation of the Earth's axis. + In this context, the "equinox" is the direction in space where the Earth's + orbital plane (the ecliptic) intersects with the plane of the Earth's equator, + at the location on the Earth's orbit of the (seasonal) March equinox. + As the Earth rotates, GAST increases from 0 up to 24 sidereal hours, + then starts over at 0. + To convert to degrees, multiply the return value by 15. + + Parameters + ---------- + time : Time + The date and time for which to find GAST. + As an optimization, this function caches the sideral time value in `time`, + unless it has already been cached, in which case the cached value is reused. + + Returns + ------- + float + GAST expressed in sidereal hours. + """ if time._st is None: t = time.tt / 36525.0 eqeq = 15.0 * time._etilt().ee # Replace with eqeq=0 to get GMST instead of GAST (if we ever need it) @@ -1718,7 +1747,7 @@ def _terra(observer, st): return _terra_posvel(observer, st)[0:3] def _geo_pos(time, observer): - gast = _sidereal_time(time) + gast = SiderealTime(time) pos1 = _terra(observer, gast) pos2 = _nutation(pos1, time, _PrecessDir.Into2000) outpos = _precession(pos2, time, _PrecessDir.Into2000) @@ -4646,7 +4675,7 @@ def ObserverVector(time, observer, ofdate): An equatorial vector from the center of the Earth to the specified location on (or near) the Earth's surface. """ - gast = _sidereal_time(time) + gast = SiderealTime(time) ovec = _terra(observer, gast) if not ofdate: ovec = _nutation(ovec, time, _PrecessDir.Into2000) @@ -4688,7 +4717,7 @@ def ObserverState(time, observer, ofdate): StateVector An equatorial position vector and velocity vector relative to the center of the Earth. """ - gast = _sidereal_time(time) + gast = SiderealTime(time) ovec = _terra_posvel(observer, gast) state = StateVector( ovec[0], ovec[1], ovec[2], @@ -4726,7 +4755,7 @@ def VectorObserver(vector, ofdate): The geographic latitude, longitude, and elevation above sea level that corresponds to the given equatorial vector. """ - gast = _sidereal_time(vector.t) + gast = SiderealTime(vector.t) ovec = [vector.x, vector.y, vector.z] if not ofdate: ovec = _precession(ovec, vector.t, _PrecessDir.From2000) @@ -4905,7 +4934,7 @@ def Horizon(time, observer, ra, dec, refraction): # Multiply sidereal hours by -15 to convert to degrees and flip eastward # rotation of the Earth to westward apparent movement of objects with time. - angle = -15.0 * _sidereal_time(time) + angle = -15.0 * SiderealTime(time) uz = _spin(angle, uze) un = _spin(angle, une) uw = _spin(angle, uwe) @@ -6090,7 +6119,7 @@ def SearchHourAngle(body, observer, hourAngle, startTime): while True: iter_count += 1 # Calculate Greenwich Apparent Sidereal Time (GAST) at the given time. - gast = _sidereal_time(time) + gast = SiderealTime(time) ofdate = Equator(body, time, observer, True, True) # Calculate the adjustment needed in sidereal time to bring @@ -7225,7 +7254,7 @@ def Rotation_EQD_HOR(time, observer): uze = [coslat * coslon, coslat * sinlon, sinlat] une = [-sinlat * coslon, -sinlat * sinlon, coslat] uwe = [sinlon, -coslon, 0.0] - spin_angle = -15.0 * _sidereal_time(time) + spin_angle = -15.0 * SiderealTime(time) uz = _spin(spin_angle, uze) un = _spin(spin_angle, une) uw = _spin(spin_angle, uwe) @@ -8454,7 +8483,7 @@ def _GeoidIntersect(shadow): latitude = math.degrees(math.atan(pz / proj)) # Adjust longitude for Earth's rotation at the given UT. - gast = _sidereal_time(peak) + gast = SiderealTime(peak) longitude = math.fmod(math.degrees(math.atan2(py, px)) - (15*gast), 360.0) if longitude <= -180.0: longitude += 360.0 diff --git a/generate/ctest.c b/generate/ctest.c index c905b0e1..92b3fd14 100644 --- a/generate/ctest.c +++ b/generate/ctest.c @@ -211,6 +211,7 @@ static int Twilight(void); static int LibrationTest(void); static int DE405_Check(void); static int AxisTest(void); +static int SiderealTimeTest(void); #if PERFORMANCE_TESTS static int MapPerformanceTest(void); @@ -260,6 +261,7 @@ static unit_test_t UnitTests[] = {"riseset", RiseSet}, {"rotation", RotationTest}, {"seasons", SeasonsTest}, + {"sidereal", SiderealTimeTest}, {"time", Test_AstroTime}, {"topostate", TopoStateTest}, {"transit", Transit}, @@ -6179,3 +6181,25 @@ fail: } /*-----------------------------------------------------------------------------------------------------------*/ + +static int SiderealTimeTest(void) +{ + int error; + astro_time_t time; + double gast, diff; + const double correct = 140.975528 / 15; /* https://eco.mtk.nao.ac.jp/cgi-bin/koyomi/cande/gst_en.cgi */ + + time = Astronomy_MakeTime(2022, 3, 15, 21, 50, 0.0); + gast = Astronomy_SiderealTime(&time); + diff = 3.6e+6 * ABS(gast - correct); /* calculate error in milliseconds */ + printf("C SiderealTimeTest: gast=%0.10lf, correct=%0.10lf, diff=%0.3lf milliseconds.\n", gast, correct, diff); + if (diff > 0.263) + FAIL("C SiderealTimeTest: EXCESSIVE ERROR\n"); + + printf("C SiderealTimeTest: PASS\n"); + error = 0; +fail: + return error; +} + +/*-----------------------------------------------------------------------------------------------------------*/ diff --git a/generate/dotnet/csharp_test/csharp_test.cs b/generate/dotnet/csharp_test/csharp_test.cs index 1e86712f..f30c2fb3 100644 --- a/generate/dotnet/csharp_test/csharp_test.cs +++ b/generate/dotnet/csharp_test/csharp_test.cs @@ -55,6 +55,7 @@ namespace csharp_test new Test("riseset", RiseSetTest), new Test("rotation", RotationTest), new Test("seasons", SeasonsTest), + new Test("sidereal", SiderealTimeTest), new Test("transit", TransitTest), new Test("astro_check", AstroCheck), new Test("barystate", BaryStateTest), @@ -3516,5 +3517,23 @@ namespace csharp_test } //----------------------------------------------------------------------------------------- + + static int SiderealTimeTest() + { + const double correct = 140.975528 / 15; // https://eco.mtk.nao.ac.jp/cgi-bin/koyomi/cande/gst_en.cgi + var time = new AstroTime(2022, 3, 15, 21, 50, 0); + double gast = Astronomy.SiderealTime(time); + double diff = 3.6e+6 * abs(gast - correct); // calculate error in milliseconds + Console.WriteLine($"C# SiderealTimeTest: gast={gast:F10}, correct={correct:F10}, diff={diff:F3} milliseconds."); + if (diff > 0.263) + { + Console.WriteLine("C# SiderealTimeTest: EXCESSIVE ERROR"); + return 1; + } + Console.WriteLine("C# SiderealTimeTest: PASS"); + return 0; + } + + //----------------------------------------------------------------------------------------- } } diff --git a/generate/template/astronomy.c b/generate/template/astronomy.c index b6fa43d5..a46d445a 100644 --- a/generate/template/astronomy.c +++ b/generate/template/astronomy.c @@ -1481,7 +1481,33 @@ static double era(double ut) /* Earth Rotation Angle */ return theta; } -static double sidereal_time(astro_time_t *time) +/** + * @brief Calculates Greenwich Apparent Sidereal Time (GAST). + * + * Given a date and time, this function calculates the rotation of the + * Earth, represented by the equatorial angle of the Greenwich prime meridian + * with respect to distant stars (not the Sun, which moves relative to background + * stars by almost one degree per day). + * This angle is called Greenwich Apparent Sidereal Time (GAST). + * GAST is measured in sidereal hours in the half-open range [0, 24). + * When GAST = 0, it means the prime meridian is aligned with the of-date equinox, + * corrected at that time for precession and nutation of the Earth's axis. + * In this context, the "equinox" is the direction in space where the Earth's + * orbital plane (the ecliptic) intersects with the plane of the Earth's equator, + * at the location on the Earth's orbit of the (seasonal) March equinox. + * As the Earth rotates, GAST increases from 0 up to 24 sidereal hours, + * then starts over at 0. + * To convert to degrees, multiply the return value by 15. + * + * @param time + * The date and time for which to find GAST. + * The parameter is passed by address because it can be modified by the call: + * As an optimization, this function caches the sideral time value in `time`, + * unless it has already been cached, in which case the cached value is reused. + * + * @returns {number} + */ +double Astronomy_SiderealTime(astro_time_t *time) { if (isnan(time->st)) { @@ -1612,7 +1638,7 @@ static void geo_pos(astro_time_t *time, astro_observer_t observer, double pos[3] double gast; double pos1[3], pos2[3]; - gast = sidereal_time(time); + gast = Astronomy_SiderealTime(time); terra(observer, gast, pos1, NULL); nutation(pos1, time, INTO_2000, pos2); precession(pos2, *time, INTO_2000, pos); @@ -3812,7 +3838,7 @@ astro_vector_t Astronomy_ObserverVector( astro_vector_t vec; double gast, pos[3], temp[3]; - gast = sidereal_time(time); + gast = Astronomy_SiderealTime(time); terra(observer, gast, pos, NULL); switch (equdate) @@ -3889,7 +3915,7 @@ astro_state_vector_t Astronomy_ObserverState( astro_state_vector_t state; double gast, pos[3], vel[3], postemp[3], veltemp[3]; - gast = sidereal_time(time); + gast = Astronomy_SiderealTime(time); terra(observer, gast, pos, vel); switch (equdate) @@ -3956,7 +3982,7 @@ astro_observer_t Astronomy_VectorObserver( double pos1[3]; double pos2[3]; - gast = sidereal_time(&vector->t); + gast = Astronomy_SiderealTime(&vector->t); pos1[0] = vector->x; pos1[1] = vector->y; pos1[2] = vector->z; @@ -4111,7 +4137,7 @@ astro_horizon_t Astronomy_Horizon( rotation of the Earth to westward apparent movement of objects with time. */ - spin_angle = -15.0 * sidereal_time(time); + spin_angle = -15.0 * Astronomy_SiderealTime(time); spin(spin_angle, uze, uz); spin(spin_angle, une, un); spin(spin_angle, uwe, uw); @@ -5466,7 +5492,7 @@ astro_hour_angle_t Astronomy_SearchHourAngle( ++iter; /* Calculate Greenwich Apparent Sidereal Time (GAST) at the given time. */ - gast = sidereal_time(&time); + gast = Astronomy_SiderealTime(&time); /* Obtain equatorial coordinates of date for the body. */ ofdate = Astronomy_Equator(body, &time, observer, EQUATOR_OF_DATE, ABERRATION); @@ -7476,7 +7502,7 @@ astro_rotation_t Astronomy_Rotation_EQD_HOR(astro_time_t *time, astro_observer_t uwe[1] = -coslon; uwe[2] = 0.0; - spin_angle = -15.0 * sidereal_time(time); + spin_angle = -15.0 * Astronomy_SiderealTime(time); spin(spin_angle, uze, uz); spin(spin_angle, une, un); spin(spin_angle, uwe, uw); @@ -8396,7 +8422,7 @@ static astro_global_solar_eclipse_t GeoidIntersect(shadow_t shadow) eclipse.latitude = RAD2DEG * atan(pz / proj); /* Adjust longitude for Earth's rotation at the given UT. */ - gast = sidereal_time(&eclipse.peak); + gast = Astronomy_SiderealTime(&eclipse.peak); eclipse.longitude = fmod((RAD2DEG*atan2(py, px)) - (15*gast), 360.0); if (eclipse.longitude <= -180.0) eclipse.longitude += 360.0; diff --git a/generate/template/astronomy.cs b/generate/template/astronomy.cs index 5936ed9b..b87f4c6b 100644 --- a/generate/template/astronomy.cs +++ b/generate/template/astronomy.cs @@ -3257,7 +3257,32 @@ $ASTRO_IAU_DATA() return theta; } - private static double sidereal_time(AstroTime time) + /// + /// Calculates Greenwich Apparent Sidereal Time (GAST). + /// + /// + /// Given a date and time, this function calculates the rotation of the + /// Earth, represented by the equatorial angle of the Greenwich prime meridian + /// with respect to distant stars (not the Sun, which moves relative to background + /// stars by almost one degree per day). + /// This angle is called Greenwich Apparent Sidereal Time (GAST). + /// GAST is measured in sidereal hours in the half-open range [0, 24). + /// When GAST = 0, it means the prime meridian is aligned with the of-date equinox, + /// corrected at that time for precession and nutation of the Earth's axis. + /// In this context, the "equinox" is the direction in space where the Earth's + /// orbital plane (the ecliptic) intersects with the plane of the Earth's equator, + /// at the location on the Earth's orbit of the (seasonal) March equinox. + /// As the Earth rotates, GAST increases from 0 up to 24 sidereal hours, + /// then starts over at 0. + /// To convert to degrees, multiply the return value by 15. + /// + /// + /// The date and time for which to find GAST. + /// As an optimization, this function caches the sideral time value in `time`, + /// unless it has already been cached, in which case the cached value is reused. + /// + /// GAST in sidereal hours. + public static double SiderealTime(AstroTime time) { if (double.IsNaN(time.st)) { @@ -3348,7 +3373,7 @@ $ASTRO_IAU_DATA() private static StateVector terra(Observer observer, AstroTime time) { - double st = sidereal_time(time); + double st = SiderealTime(time); double df = 1.0 - 0.003352819697896; /* flattening of the Earth */ double df2 = df * df; double phi = observer.latitude * DEG2RAD; @@ -4381,7 +4406,7 @@ $ASTRO_IAU_DATA() AstroVector vector, EquatorEpoch equdate) { - double gast = sidereal_time(vector.t); + double gast = SiderealTime(vector.t); if (equdate == EquatorEpoch.J2000) vector = gyration(vector, vector.t, PrecessDirection.From2000); return inverse_terra(vector, gast); @@ -4498,7 +4523,7 @@ $ASTRO_IAU_DATA() // the Earth's axis to yield corrected unit vectors uz, un, uw. // Multiply sidereal hours by -15 to convert to degrees and flip eastward // rotation of the Earth to westward apparent movement of objects with time. - double angle = -15.0 * sidereal_time(time); + double angle = -15.0 * SiderealTime(time); AstroVector uz = spin(angle, uze); AstroVector un = spin(angle, une); AstroVector uw = spin(angle, uwe); @@ -5418,7 +5443,7 @@ $ASTRO_IAU_DATA() ++iter; /* Calculate Greenwich Apparent Sidereal Time (GAST) at the given time. */ - double gast = sidereal_time(time); + double gast = SiderealTime(time); /* Obtain equatorial coordinates of date for the body. */ Equatorial ofdate = Equator(body, time, observer, EquatorEpoch.OfDate, Aberration.Corrected); @@ -6498,7 +6523,7 @@ $ASTRO_IAU_DATA() eclipse.latitude = RAD2DEG * Math.Atan(pz / proj); /* Adjust longitude for Earth's rotation at the given UT. */ - double gast = sidereal_time(eclipse.peak); + double gast = SiderealTime(eclipse.peak); eclipse.longitude = ((RAD2DEG*Math.Atan2(py, px)) - (15*gast)) % 360.0; if (eclipse.longitude <= -180.0) eclipse.longitude += 360.0; @@ -8397,7 +8422,7 @@ $ASTRO_IAU_DATA() // Multiply sidereal hours by -15 to convert to degrees and flip eastward // rotation of the Earth to westward apparent movement of objects with time. - double angle = -15.0 * sidereal_time(time); + double angle = -15.0 * SiderealTime(time); AstroVector uz = spin(angle, uze); AstroVector un = spin(angle, une); AstroVector uw = spin(angle, uwe); diff --git a/generate/template/astronomy.py b/generate/template/astronomy.py index 91cd23b3..eda2dc69 100644 --- a/generate/template/astronomy.py +++ b/generate/template/astronomy.py @@ -1082,7 +1082,36 @@ def _era(time): # Earth Rotation Angle theta += 360.0 return theta -def _sidereal_time(time): +def SiderealTime(time): + """Calculates Greenwich Apparent Sidereal Time (GAST). + + Given a date and time, this function calculates the rotation of the + Earth, represented by the equatorial angle of the Greenwich prime meridian + with respect to distant stars (not the Sun, which moves relative to background + stars by almost one degree per day). + This angle is called Greenwich Apparent Sidereal Time (GAST). + GAST is measured in sidereal hours in the half-open range [0, 24). + When GAST = 0, it means the prime meridian is aligned with the of-date equinox, + corrected at that time for precession and nutation of the Earth's axis. + In this context, the "equinox" is the direction in space where the Earth's + orbital plane (the ecliptic) intersects with the plane of the Earth's equator, + at the location on the Earth's orbit of the (seasonal) March equinox. + As the Earth rotates, GAST increases from 0 up to 24 sidereal hours, + then starts over at 0. + To convert to degrees, multiply the return value by 15. + + Parameters + ---------- + time : Time + The date and time for which to find GAST. + As an optimization, this function caches the sideral time value in `time`, + unless it has already been cached, in which case the cached value is reused. + + Returns + ------- + float + GAST expressed in sidereal hours. + """ if time._st is None: t = time.tt / 36525.0 eqeq = 15.0 * time._etilt().ee # Replace with eqeq=0 to get GMST instead of GAST (if we ever need it) @@ -1182,7 +1211,7 @@ def _terra(observer, st): return _terra_posvel(observer, st)[0:3] def _geo_pos(time, observer): - gast = _sidereal_time(time) + gast = SiderealTime(time) pos1 = _terra(observer, gast) pos2 = _nutation(pos1, time, _PrecessDir.Into2000) outpos = _precession(pos2, time, _PrecessDir.Into2000) @@ -2604,7 +2633,7 @@ def ObserverVector(time, observer, ofdate): An equatorial vector from the center of the Earth to the specified location on (or near) the Earth's surface. """ - gast = _sidereal_time(time) + gast = SiderealTime(time) ovec = _terra(observer, gast) if not ofdate: ovec = _nutation(ovec, time, _PrecessDir.Into2000) @@ -2646,7 +2675,7 @@ def ObserverState(time, observer, ofdate): StateVector An equatorial position vector and velocity vector relative to the center of the Earth. """ - gast = _sidereal_time(time) + gast = SiderealTime(time) ovec = _terra_posvel(observer, gast) state = StateVector( ovec[0], ovec[1], ovec[2], @@ -2684,7 +2713,7 @@ def VectorObserver(vector, ofdate): The geographic latitude, longitude, and elevation above sea level that corresponds to the given equatorial vector. """ - gast = _sidereal_time(vector.t) + gast = SiderealTime(vector.t) ovec = [vector.x, vector.y, vector.z] if not ofdate: ovec = _precession(ovec, vector.t, _PrecessDir.From2000) @@ -2863,7 +2892,7 @@ def Horizon(time, observer, ra, dec, refraction): # Multiply sidereal hours by -15 to convert to degrees and flip eastward # rotation of the Earth to westward apparent movement of objects with time. - angle = -15.0 * _sidereal_time(time) + angle = -15.0 * SiderealTime(time) uz = _spin(angle, uze) un = _spin(angle, une) uw = _spin(angle, uwe) @@ -4048,7 +4077,7 @@ def SearchHourAngle(body, observer, hourAngle, startTime): while True: iter_count += 1 # Calculate Greenwich Apparent Sidereal Time (GAST) at the given time. - gast = _sidereal_time(time) + gast = SiderealTime(time) ofdate = Equator(body, time, observer, True, True) # Calculate the adjustment needed in sidereal time to bring @@ -5183,7 +5212,7 @@ def Rotation_EQD_HOR(time, observer): uze = [coslat * coslon, coslat * sinlon, sinlat] une = [-sinlat * coslon, -sinlat * sinlon, coslat] uwe = [sinlon, -coslon, 0.0] - spin_angle = -15.0 * _sidereal_time(time) + spin_angle = -15.0 * SiderealTime(time) uz = _spin(spin_angle, uze) un = _spin(spin_angle, une) uw = _spin(spin_angle, uwe) @@ -5961,7 +5990,7 @@ def _GeoidIntersect(shadow): latitude = math.degrees(math.atan(pz / proj)) # Adjust longitude for Earth's rotation at the given UT. - gast = _sidereal_time(peak) + gast = SiderealTime(peak) longitude = math.fmod(math.degrees(math.atan2(py, px)) - (15*gast), 360.0) if longitude <= -180.0: longitude += 360.0 diff --git a/generate/template/astronomy.ts b/generate/template/astronomy.ts index dae89c86..60269c99 100644 --- a/generate/template/astronomy.ts +++ b/generate/template/astronomy.ts @@ -1186,6 +1186,34 @@ function sidereal_time(time: AstroTime): number { // calculates Greenwi return sidereal_time_cache.st; // return sidereal hours in the half-open range [0, 24). } +/** + * @brief Calculates Greenwich Apparent Sidereal Time (GAST). + * + * Given a date and time, this function calculates the rotation of the + * Earth, represented by the equatorial angle of the Greenwich prime meridian + * with respect to distant stars (not the Sun, which moves relative to background + * stars by almost one degree per day). + * This angle is called Greenwich Apparent Sidereal Time (GAST). + * GAST is measured in sidereal hours in the half-open range [0, 24). + * When GAST = 0, it means the prime meridian is aligned with the of-date equinox, + * corrected at that time for precession and nutation of the Earth's axis. + * In this context, the "equinox" is the direction in space where the Earth's + * orbital plane (the ecliptic) intersects with the plane of the Earth's equator, + * at the location on the Earth's orbit of the (seasonal) March equinox. + * As the Earth rotates, GAST increases from 0 up to 24 sidereal hours, + * then starts over at 0. + * To convert to degrees, multiply the return value by 15. + * + * @param {FlexibleDateTime} date + * The date and time for which to find GAST. + * + * @returns {number} + */ +export function SiderealTime(date: FlexibleDateTime): number { + const time = MakeTime(date); + return sidereal_time(time); +} + function inverse_terra(ovec: ArrayVector, st: number): Observer { // Convert from AU to kilometers const x = ovec[0] * KM_PER_AU; diff --git a/generate/test.js b/generate/test.js index 48f7a4bd..98e08069 100644 --- a/generate/test.js +++ b/generate/test.js @@ -2702,6 +2702,21 @@ function LagrangeTest() { } +function SiderealTimeTest() { + const date = new Date('2022-03-15T21:50:00Z'); + const gast = Astronomy.SiderealTime(date); + const correct = 140.975528 / 15; // https://eco.mtk.nao.ac.jp/cgi-bin/koyomi/cande/gst_en.cgi + const diff_ms = 3.6e+6 * abs(gast - correct); // calculate time error in milliseconds + console.log(`JS SiderealTimeTest: gast=${gast.toFixed(10)}, correct=${correct.toFixed(10)}, diff=${diff_ms.toFixed(3)} milliseconds.`); + if (diff_ms > 0.263) { + console.error('JS SiderealTimeTest: FAIL - excessive error.'); + return 1; + } + console.log('JS SiderealTimeTest: PASS'); + return 0; +} + + const UnitTests = { aberration: AberrationTest, axis: AxisTest, @@ -2728,6 +2743,7 @@ const UnitTests = { rise_set: RiseSet, rotation: Rotation, seasons: Seasons, + sidereal: SiderealTimeTest, topostate: TopoStateTest, transit: Transit, twilight: TwilightTest, diff --git a/generate/test.py b/generate/test.py index aad2d37b..b4f2ac21 100755 --- a/generate/test.py +++ b/generate/test.py @@ -2412,6 +2412,20 @@ def Lagrange(): #----------------------------------------------------------------------------------------------------------- +def SiderealTime(): + correct = 140.975528 / 15 # https://eco.mtk.nao.ac.jp/cgi-bin/koyomi/cande/gst_en.cgi + time = astronomy.Time.Make(2022, 3, 15, 21, 50, 0) + gast = astronomy.SiderealTime(time) + diff = 3.6e+6 * abs(gast - correct) # calculate error in millseconds + print('PY SiderealTime: gast={:0.10f}, correct={:0.10f}, diff={:0.3f} milliseconds.'.format(gast, correct, diff)) + if diff > 0.263: + print('PY SiderealTime: EXCESSIVE ERROR') + return 1 + print('PY SiderealTime: PASS') + return 0 + +#----------------------------------------------------------------------------------------------------------- + UnitTests = { 'aberration': Aberration, 'axis': Axis, @@ -2439,6 +2453,7 @@ UnitTests = { 'riseset': RiseSet, 'rotation': Rotation, 'seasons': Seasons, + 'sidereal': SiderealTime, 'time': AstroTime, 'topostate': TopoState, 'transit': Transit, diff --git a/source/c/README.md b/source/c/README.md index b6662219..a92fda7d 100644 --- a/source/c/README.md +++ b/source/c/README.md @@ -2556,6 +2556,30 @@ Most programs should not call this function. It is for advanced use cases only. +--- + + +### Astronomy_SiderealTime(time) ⇒ `double` + +**Calculates Greenwich Apparent Sidereal Time (GAST).** + + + +Given a date and time, this function calculates the rotation of the Earth, represented by the equatorial angle of the Greenwich prime meridian with respect to distant stars (not the Sun, which moves relative to background stars by almost one degree per day). This angle is called Greenwich Apparent Sidereal Time (GAST). GAST is measured in sidereal hours in the half-open range [0, 24). When GAST = 0, it means the prime meridian is aligned with the of-date equinox, corrected at that time for precession and nutation of the Earth's axis. In this context, the "equinox" is the direction in space where the Earth's orbital plane (the ecliptic) intersects with the plane of the Earth's equator, at the location on the Earth's orbit of the (seasonal) March equinox. As the Earth rotates, GAST increases from 0 up to 24 sidereal hours, then starts over at 0. To convert to degrees, multiply the return value by 15. + + + +**Returns:** {number} + + + +| Type | Parameter | Description | +| --- | --- | --- | +| astro_time_t * | `time` | The date and time for which to find GAST. The parameter is passed by address because it can be modified by the call: As an optimization, this function caches the sideral time value in `time`, unless it has already been cached, in which case the cached value is reused. | + + + + --- diff --git a/source/c/astronomy.c b/source/c/astronomy.c index 34fb543d..4cdd6956 100644 --- a/source/c/astronomy.c +++ b/source/c/astronomy.c @@ -1565,7 +1565,33 @@ static double era(double ut) /* Earth Rotation Angle */ return theta; } -static double sidereal_time(astro_time_t *time) +/** + * @brief Calculates Greenwich Apparent Sidereal Time (GAST). + * + * Given a date and time, this function calculates the rotation of the + * Earth, represented by the equatorial angle of the Greenwich prime meridian + * with respect to distant stars (not the Sun, which moves relative to background + * stars by almost one degree per day). + * This angle is called Greenwich Apparent Sidereal Time (GAST). + * GAST is measured in sidereal hours in the half-open range [0, 24). + * When GAST = 0, it means the prime meridian is aligned with the of-date equinox, + * corrected at that time for precession and nutation of the Earth's axis. + * In this context, the "equinox" is the direction in space where the Earth's + * orbital plane (the ecliptic) intersects with the plane of the Earth's equator, + * at the location on the Earth's orbit of the (seasonal) March equinox. + * As the Earth rotates, GAST increases from 0 up to 24 sidereal hours, + * then starts over at 0. + * To convert to degrees, multiply the return value by 15. + * + * @param time + * The date and time for which to find GAST. + * The parameter is passed by address because it can be modified by the call: + * As an optimization, this function caches the sideral time value in `time`, + * unless it has already been cached, in which case the cached value is reused. + * + * @returns {number} + */ +double Astronomy_SiderealTime(astro_time_t *time) { if (isnan(time->st)) { @@ -1696,7 +1722,7 @@ static void geo_pos(astro_time_t *time, astro_observer_t observer, double pos[3] double gast; double pos1[3], pos2[3]; - gast = sidereal_time(time); + gast = Astronomy_SiderealTime(time); terra(observer, gast, pos1, NULL); nutation(pos1, time, INTO_2000, pos2); precession(pos2, *time, INTO_2000, pos); @@ -5051,7 +5077,7 @@ astro_vector_t Astronomy_ObserverVector( astro_vector_t vec; double gast, pos[3], temp[3]; - gast = sidereal_time(time); + gast = Astronomy_SiderealTime(time); terra(observer, gast, pos, NULL); switch (equdate) @@ -5128,7 +5154,7 @@ astro_state_vector_t Astronomy_ObserverState( astro_state_vector_t state; double gast, pos[3], vel[3], postemp[3], veltemp[3]; - gast = sidereal_time(time); + gast = Astronomy_SiderealTime(time); terra(observer, gast, pos, vel); switch (equdate) @@ -5195,7 +5221,7 @@ astro_observer_t Astronomy_VectorObserver( double pos1[3]; double pos2[3]; - gast = sidereal_time(&vector->t); + gast = Astronomy_SiderealTime(&vector->t); pos1[0] = vector->x; pos1[1] = vector->y; pos1[2] = vector->z; @@ -5350,7 +5376,7 @@ astro_horizon_t Astronomy_Horizon( rotation of the Earth to westward apparent movement of objects with time. */ - spin_angle = -15.0 * sidereal_time(time); + spin_angle = -15.0 * Astronomy_SiderealTime(time); spin(spin_angle, uze, uz); spin(spin_angle, une, un); spin(spin_angle, uwe, uw); @@ -6705,7 +6731,7 @@ astro_hour_angle_t Astronomy_SearchHourAngle( ++iter; /* Calculate Greenwich Apparent Sidereal Time (GAST) at the given time. */ - gast = sidereal_time(&time); + gast = Astronomy_SiderealTime(&time); /* Obtain equatorial coordinates of date for the body. */ ofdate = Astronomy_Equator(body, &time, observer, EQUATOR_OF_DATE, ABERRATION); @@ -8715,7 +8741,7 @@ astro_rotation_t Astronomy_Rotation_EQD_HOR(astro_time_t *time, astro_observer_t uwe[1] = -coslon; uwe[2] = 0.0; - spin_angle = -15.0 * sidereal_time(time); + spin_angle = -15.0 * Astronomy_SiderealTime(time); spin(spin_angle, uze, uz); spin(spin_angle, une, un); spin(spin_angle, uwe, uw); @@ -10090,7 +10116,7 @@ static astro_global_solar_eclipse_t GeoidIntersect(shadow_t shadow) eclipse.latitude = RAD2DEG * atan(pz / proj); /* Adjust longitude for Earth's rotation at the given UT. */ - gast = sidereal_time(&eclipse.peak); + gast = Astronomy_SiderealTime(&eclipse.peak); eclipse.longitude = fmod((RAD2DEG*atan2(py, px)) - (15*gast), 360.0); if (eclipse.longitude <= -180.0) eclipse.longitude += 360.0; diff --git a/source/c/astronomy.h b/source/c/astronomy.h index 7c8b9e81..fe7a852e 100644 --- a/source/c/astronomy.h +++ b/source/c/astronomy.h @@ -1142,6 +1142,7 @@ astro_status_t Astronomy_FormatTime(astro_time_t time, astro_time_format_t forma astro_time_t Astronomy_TimeFromDays(double ut); astro_time_t Astronomy_TerrestrialTime(double tt); astro_time_t Astronomy_AddDays(astro_time_t time, double days); +double Astronomy_SiderealTime(astro_time_t *time); astro_func_result_t Astronomy_HelioDistance(astro_body_t body, astro_time_t time); astro_vector_t Astronomy_HelioVector(astro_body_t body, astro_time_t time); astro_vector_t Astronomy_GeoVector(astro_body_t body, astro_time_t time, astro_aberration_t aberration); diff --git a/source/csharp/README.md b/source/csharp/README.md index 51978dcc..25a443ed 100644 --- a/source/csharp/README.md +++ b/source/csharp/README.md @@ -1952,6 +1952,32 @@ of winter in the southern hemisphere. **Returns:** A [`SeasonsInfo`](#SeasonsInfo) structure that contains four [`AstroTime`](#AstroTime) values: the March and September equinoxes and the June and December solstices. + +### Astronomy.SiderealTime(time) ⇒ `double` + +**Calculates Greenwich Apparent Sidereal Time (GAST).** + +Given a date and time, this function calculates the rotation of the +Earth, represented by the equatorial angle of the Greenwich prime meridian +with respect to distant stars (not the Sun, which moves relative to background +stars by almost one degree per day). +This angle is called Greenwich Apparent Sidereal Time (GAST). +GAST is measured in sidereal hours in the half-open range [0, 24). +When GAST = 0, it means the prime meridian is aligned with the of-date equinox, +corrected at that time for precession and nutation of the Earth's axis. +In this context, the "equinox" is the direction in space where the Earth's +orbital plane (the ecliptic) intersects with the plane of the Earth's equator, +at the location on the Earth's orbit of the (seasonal) March equinox. +As the Earth rotates, GAST increases from 0 up to 24 sidereal hours, +then starts over at 0. +To convert to degrees, multiply the return value by 15. + +| Type | Parameter | Description | +| --- | --- | --- | +| [`AstroTime`](#AstroTime) | `time` | The date and time for which to find GAST. As an optimization, this function caches the sideral time value in `time`, unless it has already been cached, in which case the cached value is reused. | + +**Returns:** GAST in sidereal hours. + ### Astronomy.SphereFromVector(vector) ⇒ [`Spherical`](#Spherical) diff --git a/source/csharp/astronomy.cs b/source/csharp/astronomy.cs index 43a96770..8b765c21 100644 --- a/source/csharp/astronomy.cs +++ b/source/csharp/astronomy.cs @@ -4469,7 +4469,32 @@ namespace CosineKitty return theta; } - private static double sidereal_time(AstroTime time) + /// + /// Calculates Greenwich Apparent Sidereal Time (GAST). + /// + /// + /// Given a date and time, this function calculates the rotation of the + /// Earth, represented by the equatorial angle of the Greenwich prime meridian + /// with respect to distant stars (not the Sun, which moves relative to background + /// stars by almost one degree per day). + /// This angle is called Greenwich Apparent Sidereal Time (GAST). + /// GAST is measured in sidereal hours in the half-open range [0, 24). + /// When GAST = 0, it means the prime meridian is aligned with the of-date equinox, + /// corrected at that time for precession and nutation of the Earth's axis. + /// In this context, the "equinox" is the direction in space where the Earth's + /// orbital plane (the ecliptic) intersects with the plane of the Earth's equator, + /// at the location on the Earth's orbit of the (seasonal) March equinox. + /// As the Earth rotates, GAST increases from 0 up to 24 sidereal hours, + /// then starts over at 0. + /// To convert to degrees, multiply the return value by 15. + /// + /// + /// The date and time for which to find GAST. + /// As an optimization, this function caches the sideral time value in `time`, + /// unless it has already been cached, in which case the cached value is reused. + /// + /// GAST in sidereal hours. + public static double SiderealTime(AstroTime time) { if (double.IsNaN(time.st)) { @@ -4560,7 +4585,7 @@ namespace CosineKitty private static StateVector terra(Observer observer, AstroTime time) { - double st = sidereal_time(time); + double st = SiderealTime(time); double df = 1.0 - 0.003352819697896; /* flattening of the Earth */ double df2 = df * df; double phi = observer.latitude * DEG2RAD; @@ -5593,7 +5618,7 @@ namespace CosineKitty AstroVector vector, EquatorEpoch equdate) { - double gast = sidereal_time(vector.t); + double gast = SiderealTime(vector.t); if (equdate == EquatorEpoch.J2000) vector = gyration(vector, vector.t, PrecessDirection.From2000); return inverse_terra(vector, gast); @@ -5710,7 +5735,7 @@ namespace CosineKitty // the Earth's axis to yield corrected unit vectors uz, un, uw. // Multiply sidereal hours by -15 to convert to degrees and flip eastward // rotation of the Earth to westward apparent movement of objects with time. - double angle = -15.0 * sidereal_time(time); + double angle = -15.0 * SiderealTime(time); AstroVector uz = spin(angle, uze); AstroVector un = spin(angle, une); AstroVector uw = spin(angle, uwe); @@ -6630,7 +6655,7 @@ namespace CosineKitty ++iter; /* Calculate Greenwich Apparent Sidereal Time (GAST) at the given time. */ - double gast = sidereal_time(time); + double gast = SiderealTime(time); /* Obtain equatorial coordinates of date for the body. */ Equatorial ofdate = Equator(body, time, observer, EquatorEpoch.OfDate, Aberration.Corrected); @@ -7710,7 +7735,7 @@ namespace CosineKitty eclipse.latitude = RAD2DEG * Math.Atan(pz / proj); /* Adjust longitude for Earth's rotation at the given UT. */ - double gast = sidereal_time(eclipse.peak); + double gast = SiderealTime(eclipse.peak); eclipse.longitude = ((RAD2DEG*Math.Atan2(py, px)) - (15*gast)) % 360.0; if (eclipse.longitude <= -180.0) eclipse.longitude += 360.0; @@ -9609,7 +9634,7 @@ namespace CosineKitty // Multiply sidereal hours by -15 to convert to degrees and flip eastward // rotation of the Earth to westward apparent movement of objects with time. - double angle = -15.0 * sidereal_time(time); + double angle = -15.0 * SiderealTime(time); AstroVector uz = spin(angle, uze); AstroVector un = spin(angle, une); AstroVector uw = spin(angle, uwe); diff --git a/source/js/README.md b/source/js/README.md index ff9f9ac8..d1bba6aa 100644 --- a/source/js/README.md +++ b/source/js/README.md @@ -1036,6 +1036,34 @@ and the apparent angular diameter of the Moon `diam_deg`. | date | [FlexibleDateTime](#FlexibleDateTime) | A Date object, a number of UTC days since the J2000 epoch (noon on January 1, 2000), or an AstroTime object. | +* * * + + + +## SiderealTime(date) ⇒ number +**Kind**: global function +**Brief**: Calculates Greenwich Apparent Sidereal Time (GAST). + +Given a date and time, this function calculates the rotation of the +Earth, represented by the equatorial angle of the Greenwich prime meridian +with respect to distant stars (not the Sun, which moves relative to background +stars by almost one degree per day). +This angle is called Greenwich Apparent Sidereal Time (GAST). +GAST is measured in sidereal hours in the half-open range [0, 24). +When GAST = 0, it means the prime meridian is aligned with the of-date equinox, +corrected at that time for precession and nutation of the Earth's axis. +In this context, the "equinox" is the direction in space where the Earth's +orbital plane (the ecliptic) intersects with the plane of the Earth's equator, +at the location on the Earth's orbit of the (seasonal) March equinox. +As the Earth rotates, GAST increases from 0 up to 24 sidereal hours, +then starts over at 0. +To convert to degrees, multiply the return value by 15. + +| Param | Type | Description | +| --- | --- | --- | +| date | [FlexibleDateTime](#FlexibleDateTime) | The date and time for which to find GAST. | + + * * * diff --git a/source/js/astronomy.browser.js b/source/js/astronomy.browser.js index 3d0f5ef8..032b36d8 100644 --- a/source/js/astronomy.browser.js +++ b/source/js/astronomy.browser.js @@ -34,9 +34,9 @@ */ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); -exports.GeoVector = exports.HelioDistance = exports.HelioVector = exports.JupiterMoons = exports.JupiterMoonsInfo = exports.GeoEmbState = exports.GeoMoonState = exports.EclipticGeoMoon = exports.GeoMoon = exports.Ecliptic = exports.ObserverGravity = exports.VectorObserver = exports.ObserverState = exports.ObserverVector = exports.Equator = exports.SunPosition = exports.Observer = exports.Horizon = exports.EclipticCoordinates = exports.HorizontalCoordinates = exports.MakeRotation = exports.RotationMatrix = exports.EquatorialCoordinates = exports.Spherical = exports.StateVector = exports.Vector = exports.Libration = exports.LibrationInfo = exports.CalcMoonCount = exports.MakeTime = exports.AstroTime = exports.SetDeltaTFunction = exports.DeltaT_JplHorizons = exports.DeltaT_EspenakMeeus = exports.Body = exports.AngleBetween = exports.MassProduct = exports.CALLISTO_RADIUS_KM = exports.GANYMEDE_RADIUS_KM = exports.EUROPA_RADIUS_KM = exports.IO_RADIUS_KM = exports.JUPITER_MEAN_RADIUS_KM = exports.JUPITER_POLAR_RADIUS_KM = exports.JUPITER_EQUATORIAL_RADIUS_KM = exports.RAD2HOUR = exports.RAD2DEG = exports.HOUR2RAD = exports.DEG2RAD = exports.KM_PER_AU = exports.C_AUDAY = void 0; -exports.Rotation_HOR_EQJ = exports.Rotation_HOR_EQD = exports.Rotation_EQD_HOR = exports.Rotation_EQD_EQJ = exports.Rotation_EQJ_EQD = exports.Rotation_ECL_EQJ = exports.Rotation_EQJ_ECL = exports.RotateState = exports.RotateVector = exports.InverseRefraction = exports.Refraction = exports.VectorFromHorizon = exports.HorizonFromVector = exports.SphereFromVector = exports.EquatorFromVector = exports.VectorFromSphere = exports.Pivot = exports.IdentityMatrix = exports.CombineRotation = exports.InverseRotation = exports.NextPlanetApsis = exports.SearchPlanetApsis = exports.NextLunarApsis = exports.SearchLunarApsis = exports.Apsis = exports.SearchPeakMagnitude = exports.SearchMaxElongation = exports.Elongation = exports.ElongationEvent = exports.Seasons = exports.SeasonInfo = exports.SearchHourAngle = exports.HourAngleEvent = exports.SearchAltitude = exports.SearchRiseSet = exports.NextMoonQuarter = exports.SearchMoonQuarter = exports.MoonQuarter = exports.SearchMoonPhase = exports.MoonPhase = exports.SearchRelativeLongitude = exports.Illumination = exports.IlluminationInfo = exports.EclipticLongitude = exports.AngleFromSun = exports.PairLongitude = exports.SearchSunLongitude = exports.Search = exports.HelioState = exports.BaryState = void 0; -exports.LagrangePointFast = exports.LagrangePoint = exports.RotationAxis = exports.AxisInfo = exports.NextMoonNode = exports.SearchMoonNode = exports.NodeEventInfo = exports.NodeEventKind = exports.NextTransit = exports.SearchTransit = exports.TransitInfo = exports.NextLocalSolarEclipse = exports.SearchLocalSolarEclipse = exports.LocalSolarEclipseInfo = exports.EclipseEvent = exports.NextGlobalSolarEclipse = exports.SearchGlobalSolarEclipse = exports.NextLunarEclipse = exports.GlobalSolarEclipseInfo = exports.SearchLunarEclipse = exports.LunarEclipseInfo = exports.Constellation = exports.ConstellationInfo = exports.Rotation_GAL_EQJ = exports.Rotation_EQJ_GAL = exports.Rotation_HOR_ECL = exports.Rotation_ECL_HOR = exports.Rotation_ECL_EQD = exports.Rotation_EQD_ECL = exports.Rotation_EQJ_HOR = void 0; +exports.HelioDistance = exports.HelioVector = exports.JupiterMoons = exports.JupiterMoonsInfo = exports.GeoEmbState = exports.GeoMoonState = exports.EclipticGeoMoon = exports.GeoMoon = exports.Ecliptic = exports.ObserverGravity = exports.VectorObserver = exports.ObserverState = exports.ObserverVector = exports.Equator = exports.SunPosition = exports.Observer = exports.Horizon = exports.EclipticCoordinates = exports.HorizontalCoordinates = exports.MakeRotation = exports.RotationMatrix = exports.EquatorialCoordinates = exports.Spherical = exports.StateVector = exports.Vector = exports.SiderealTime = exports.Libration = exports.LibrationInfo = exports.CalcMoonCount = exports.MakeTime = exports.AstroTime = exports.SetDeltaTFunction = exports.DeltaT_JplHorizons = exports.DeltaT_EspenakMeeus = exports.Body = exports.AngleBetween = exports.MassProduct = exports.CALLISTO_RADIUS_KM = exports.GANYMEDE_RADIUS_KM = exports.EUROPA_RADIUS_KM = exports.IO_RADIUS_KM = exports.JUPITER_MEAN_RADIUS_KM = exports.JUPITER_POLAR_RADIUS_KM = exports.JUPITER_EQUATORIAL_RADIUS_KM = exports.RAD2HOUR = exports.RAD2DEG = exports.HOUR2RAD = exports.DEG2RAD = exports.KM_PER_AU = exports.C_AUDAY = void 0; +exports.Rotation_HOR_EQD = exports.Rotation_EQD_HOR = exports.Rotation_EQD_EQJ = exports.Rotation_EQJ_EQD = exports.Rotation_ECL_EQJ = exports.Rotation_EQJ_ECL = exports.RotateState = exports.RotateVector = exports.InverseRefraction = exports.Refraction = exports.VectorFromHorizon = exports.HorizonFromVector = exports.SphereFromVector = exports.EquatorFromVector = exports.VectorFromSphere = exports.Pivot = exports.IdentityMatrix = exports.CombineRotation = exports.InverseRotation = exports.NextPlanetApsis = exports.SearchPlanetApsis = exports.NextLunarApsis = exports.SearchLunarApsis = exports.Apsis = exports.SearchPeakMagnitude = exports.SearchMaxElongation = exports.Elongation = exports.ElongationEvent = exports.Seasons = exports.SeasonInfo = exports.SearchHourAngle = exports.HourAngleEvent = exports.SearchAltitude = exports.SearchRiseSet = exports.NextMoonQuarter = exports.SearchMoonQuarter = exports.MoonQuarter = exports.SearchMoonPhase = exports.MoonPhase = exports.SearchRelativeLongitude = exports.Illumination = exports.IlluminationInfo = exports.EclipticLongitude = exports.AngleFromSun = exports.PairLongitude = exports.SearchSunLongitude = exports.Search = exports.HelioState = exports.BaryState = exports.GeoVector = void 0; +exports.LagrangePointFast = exports.LagrangePoint = exports.RotationAxis = exports.AxisInfo = exports.NextMoonNode = exports.SearchMoonNode = exports.NodeEventInfo = exports.NodeEventKind = exports.NextTransit = exports.SearchTransit = exports.TransitInfo = exports.NextLocalSolarEclipse = exports.SearchLocalSolarEclipse = exports.LocalSolarEclipseInfo = exports.EclipseEvent = exports.NextGlobalSolarEclipse = exports.SearchGlobalSolarEclipse = exports.NextLunarEclipse = exports.GlobalSolarEclipseInfo = exports.SearchLunarEclipse = exports.LunarEclipseInfo = exports.Constellation = exports.ConstellationInfo = exports.Rotation_GAL_EQJ = exports.Rotation_EQJ_GAL = exports.Rotation_HOR_ECL = exports.Rotation_ECL_HOR = exports.Rotation_ECL_EQD = exports.Rotation_EQD_ECL = exports.Rotation_EQJ_HOR = exports.Rotation_HOR_EQJ = void 0; /** * @brief The speed of light in AU/day. */ @@ -1849,6 +1849,34 @@ function sidereal_time(time) { } return sidereal_time_cache.st; // return sidereal hours in the half-open range [0, 24). } +/** + * @brief Calculates Greenwich Apparent Sidereal Time (GAST). + * + * Given a date and time, this function calculates the rotation of the + * Earth, represented by the equatorial angle of the Greenwich prime meridian + * with respect to distant stars (not the Sun, which moves relative to background + * stars by almost one degree per day). + * This angle is called Greenwich Apparent Sidereal Time (GAST). + * GAST is measured in sidereal hours in the half-open range [0, 24). + * When GAST = 0, it means the prime meridian is aligned with the of-date equinox, + * corrected at that time for precession and nutation of the Earth's axis. + * In this context, the "equinox" is the direction in space where the Earth's + * orbital plane (the ecliptic) intersects with the plane of the Earth's equator, + * at the location on the Earth's orbit of the (seasonal) March equinox. + * As the Earth rotates, GAST increases from 0 up to 24 sidereal hours, + * then starts over at 0. + * To convert to degrees, multiply the return value by 15. + * + * @param {FlexibleDateTime} date + * The date and time for which to find GAST. + * + * @returns {number} + */ +function SiderealTime(date) { + const time = MakeTime(date); + return sidereal_time(time); +} +exports.SiderealTime = SiderealTime; function inverse_terra(ovec, st) { // Convert from AU to kilometers const x = ovec[0] * exports.KM_PER_AU; diff --git a/source/js/astronomy.browser.min.js b/source/js/astronomy.browser.min.js index e7000fe3..2b53cb4b 100644 --- a/source/js/astronomy.browser.min.js +++ b/source/js/astronomy.browser.min.js @@ -44,52 +44,52 @@ Math.floor(a)}function O(a,b){var c=a.x*a.x+a.y*a.y+a.z*a.z;if(1E-8>Math.abs(c)) b*c+.0090316521*c*c}if(1600>b)return a=(b-1E3)/100,b=a*a,c=a*b,1574.2-556.01*a+71.23472*b+.319781*c-.8503463*b*b-.005050998*b*c+.0083572073*c*c;if(1700>b)return a=b-1600,b=a*a,120-.9808*a-.01532*b+a*b/7129;if(1800>b)return a=b-1700,b=a*a,8.83+.1603*a-.0059285*b+1.3336E-4*a*b-b*b/1174E3;if(1860>b){a=b-1800;b=a*a;c=a*b;var d=b*b;return 13.72-.332447*a+.0068612*b+.0041116*c-3.7436E-4*d+1.21272E-5*b*c-1.699E-7*c*c+8.75E-10*c*d}if(1900>b)return a=b-1860,b=a*a,c=a*b,7.62+.5737*a-.251754*b+.01680668*c-4.473624E-4* b*b+b*c/233174;if(1920>b)return a=b-1900,b=a*a,-2.79+1.494119*a-.0598939*b+.0061966*a*b-1.97E-4*b*b;if(1941>b)return a=b-1920,b=a*a,21.2+.84493*a-.0761*b+.0020936*a*b;if(1961>b)return a=b-1950,b=a*a,29.07+.407*a-b/233+a*b/2547;if(1986>b)return a=b-1975,b=a*a,45.45+1.067*a-b/260-a*b/718;if(2005>b)return a=b-2E3,b=a*a,c=a*b,63.86+.3345*a-.060374*b+.0017275*c+6.51814E-4*b*b+2.373599E-5*b*c;if(2050>b)return a=b-2E3,62.92+.32217*a+.005589*a*a;if(2150>b)return a=(b-1820)/100,-20+32*a*a-.5628*(2150-b);a= (b-1820)/100;return-20+32*a*a}function za(a){return a+Nb(a)/86400}function w(a){return a instanceof P?a:new P(a)}function Ob(a){a=a.tt/36525;return(((((-4.34E-8*a-5.76E-7)*a+.0020034)*a-1.831E-4)*a-46.836769)*a+84381.406)/3600}function Ta(a){var b;if(!Ua||1E-6=E;++E)0!==C[E]&&h(J.x,J.y,c(x,C[E],E),c(v,C[E],E),function(ma, -na){return J.x=ma,J.y=na});return J}function g(C,E,M,Y,J,ma,na,lb){J=k(J,ma,na,lb);p+=C*J.y;t+=E*J.y;ea+=M*J.x;S+=Y*J.x}++e.CalcMoonCount;a=a.tt/36525;var m,n,p,t,x=b(-6,6,1,4),v=b(-6,6,1,4);var z=a*a;var ea=t=p=0;var S=3422.7;var fa=l(.19833+.05611*a);var Z=l(.27869+.04508*a);var T=l(.16827-.36903*a);var W=l(.34734-5.37261*a);var Aa=l(.10498-5.37899*a);var Va=l(.42681-.41855*a),Wc=l(.14943-5.37511*a);var Wa=.84*fa+.31*Z+14.27*T+7.26*W+.28*Aa+.24*Va;var nb=2.94*fa+.31*Z+14.27*T+9.34*W+1.12*Aa+.83* -Va;var Xa=-6.4*fa-1.89*Va;Z=.21*fa+.31*Z+14.27*T-88.7*W-15.3*Aa+.24*Va-1.86*Wc;T=Wa-Xa;fa=-3.332E-6*l(.59734-5.37261*a)-5.39E-7*l(.35498-5.37899*a)-6.4E-8*l(.39943-5.37511*a);Wa=R*N(.60643382+1336.85522467*a-3.13E-6*z)+Wa/aa;nb=R*N(.37489701+1325.55240982*a+2.565E-5*z)+nb/aa;Xa=R*N(.99312619+99.99735956*a-4.4E-7*z)+Xa/aa;Z=R*N(.25909118+1342.2278298*a-8.92E-6*z)+Z/aa;Aa=R*N(.82736186+1236.85308708*a-3.97E-6*z)+T/aa;for(m=1;4>=m;++m){switch(m){case 1:T=nb;z=4;W=1.000002208;break;case 2:T=Xa;z=3;W= -.997504612-.002495388*a;break;case 3:T=Z;z=4;W=1.000002708+139.978*fa;break;case 4:T=Aa;z=6;W=1;break;default:throw"Internal error: I = "+m;}d(0,m,1);d(1,m,Math.cos(T)*W);f(0,m,0);f(1,m,Math.sin(T)*W);for(n=2;n<=z;++n)h(c(x,n-1,m),c(v,n-1,m),c(x,1,m),c(v,1,m),function(C,E){return d(n,m,C),f(n,m,E)});for(n=1;n<=z;++n)d(-n,m,c(x,n,m)),f(-n,m,-c(v,n,m))}g(13.902,14.06,-.001,.2607,0,0,0,4);g(.403,-4.01,.394,.0023,0,0,0,3);g(2369.912,2373.36,.601,28.2333,0,0,0,2);g(-125.154,-112.79,-.725,-.9781,0,0,0, -1);g(1.979,6.98,-.445,.0433,1,0,0,4);g(191.953,192.72,.029,3.0861,1,0,0,2);g(-8.466,-13.51,.455,-.1093,1,0,0,1);g(22639.5,22609.07,.079,186.5398,1,0,0,0);g(18.609,3.59,-.094,.0118,1,0,0,-1);g(-4586.465,-4578.13,-.077,34.3117,1,0,0,-2);g(3.215,5.44,.192,-.0386,1,0,0,-3);g(-38.428,-38.64,.001,.6008,1,0,0,-4);g(-.393,-1.43,-.092,.0086,1,0,0,-6);g(-.289,-1.59,.123,-.0053,0,1,0,4);g(-24.42,-25.1,.04,-.3,0,1,0,2);g(18.023,17.93,.007,.1494,0,1,0,1);g(-668.146,-126.98,-1.302,-.3997,0,1,0,0);g(.56,.32,-.001, --.0037,0,1,0,-1);g(-165.145,-165.06,.054,1.9178,0,1,0,-2);g(-1.877,-6.46,-.416,.0339,0,1,0,-4);g(.213,1.02,-.074,.0054,2,0,0,4);g(14.387,14.78,-.017,.2833,2,0,0,2);g(-.586,-1.2,.054,-.01,2,0,0,1);g(769.016,767.96,.107,10.1657,2,0,0,0);g(1.75,2.01,-.018,.0155,2,0,0,-1);g(-211.656,-152.53,5.679,-.3039,2,0,0,-2);g(1.225,.91,-.03,-.0088,2,0,0,-3);g(-30.773,-34.07,-.308,.3722,2,0,0,-4);g(-.57,-1.4,-.074,.0109,2,0,0,-6);g(-2.921,-11.75,.787,-.0484,1,1,0,2);g(1.267,1.52,-.022,.0164,1,1,0,1);g(-109.673,-115.18, -.461,-.949,1,1,0,0);g(-205.962,-182.36,2.056,1.4437,1,1,0,-2);g(.233,.36,.012,-.0025,1,1,0,-3);g(-4.391,-9.66,-.471,.0673,1,1,0,-4);g(.283,1.53,-.111,.006,1,-1,0,4);g(14.577,31.7,-1.54,.2302,1,-1,0,2);g(147.687,138.76,.679,1.1528,1,-1,0,0);g(-1.089,.55,.021,0,1,-1,0,-1);g(28.475,23.59,-.443,-.2257,1,-1,0,-2);g(-.276,-.38,-.006,-.0036,1,-1,0,-3);g(.636,2.27,.146,-.0102,1,-1,0,-4);g(-.189,-1.68,.131,-.0028,0,2,0,2);g(-7.486,-.66,-.037,-.0086,0,2,0,0);g(-8.096,-16.35,-.74,.0918,0,2,0,-2);g(-5.741,-.04, -0,-9E-4,0,0,2,2);g(.255,0,0,0,0,0,2,1);g(-411.608,-.2,0,-.0124,0,0,2,0);g(.584,.84,0,.0071,0,0,2,-1);g(-55.173,-52.14,0,-.1052,0,0,2,-2);g(.254,.25,0,-.0017,0,0,2,-3);g(.025,-1.67,0,.0031,0,0,2,-4);g(1.06,2.96,-.166,.0243,3,0,0,2);g(36.124,50.64,-1.3,.6215,3,0,0,0);g(-13.193,-16.4,.258,-.1187,3,0,0,-2);g(-1.187,-.74,.042,.0074,3,0,0,-4);g(-.293,-.31,-.002,.0046,3,0,0,-6);g(-.29,-1.45,.116,-.0051,2,1,0,2);g(-7.649,-10.56,.259,-.1038,2,1,0,0);g(-8.627,-7.59,.078,-.0192,2,1,0,-2);g(-2.74,-2.54,.022, -.0324,2,1,0,-4);g(1.181,3.32,-.212,.0213,2,-1,0,2);g(9.703,11.67,-.151,.1268,2,-1,0,0);g(-.352,-.37,.001,-.0028,2,-1,0,-1);g(-2.494,-1.17,-.003,-.0017,2,-1,0,-2);g(.36,.2,-.012,-.0043,2,-1,0,-4);g(-1.167,-1.25,.008,-.0106,1,2,0,0);g(-7.412,-6.12,.117,.0484,1,2,0,-2);g(-.311,-.65,-.032,.0044,1,2,0,-4);g(.757,1.82,-.105,.0112,1,-2,0,2);g(2.58,2.32,.027,.0196,1,-2,0,0);g(2.533,2.4,-.014,-.0212,1,-2,0,-2);g(-.344,-.57,-.025,.0036,0,3,0,-2);g(-.992,-.02,0,0,1,0,2,2);g(-45.099,-.02,0,-.001,1,0,2,0);g(-.179, --9.52,0,-.0833,1,0,2,-2);g(-.301,-.33,0,.0014,1,0,2,-4);g(-6.382,-3.37,0,-.0481,1,0,-2,2);g(39.528,85.13,0,-.7136,1,0,-2,0);g(9.366,.71,0,-.0112,1,0,-2,-2);g(.202,.02,0,0,1,0,-2,-4);g(.415,.1,0,.0013,0,1,2,0);g(-2.152,-2.26,0,-.0066,0,1,2,-2);g(-1.44,-1.3,0,.0014,0,1,-2,2);g(.384,-.04,0,0,0,1,-2,-2);g(1.938,3.6,-.145,.0401,4,0,0,0);g(-.952,-1.58,.052,-.013,4,0,0,-2);g(-.551,-.94,.032,-.0097,3,1,0,0);g(-.482,-.57,.005,-.0045,3,1,0,-2);g(.681,.96,-.026,.0115,3,-1,0,0);g(-.297,-.27,.002,-9E-4,2,2,0, --2);g(.254,.21,-.003,0,2,-2,0,-2);g(-.25,-.22,.004,.0014,1,3,0,-2);g(-3.996,0,0,4E-4,2,0,2,0);g(.557,-.75,0,-.009,2,0,2,-2);g(-.459,-.38,0,-.0053,2,0,-2,2);g(-1.298,.74,0,4E-4,2,0,-2,0);g(.538,1.14,0,-.0141,2,0,-2,-2);g(.263,.02,0,0,1,1,2,0);g(.426,.07,0,-6E-4,1,1,-2,-2);g(-.304,.03,0,3E-4,1,-1,2,0);g(-.372,-.19,0,-.0027,1,-1,-2,2);g(.418,0,0,0,0,0,4,0);g(-.33,-.04,0,0,3,0,2,0);z=-526.069*k(0,0,1,-2).y;z+=-3.352*k(0,0,1,-4).y;z+=44.297*k(1,0,1,-2).y;z+=-6*k(1,0,1,-4).y;z+=20.599*k(-1,0,1,0).y;z+= --30.598*k(-1,0,1,-2).y;z+=-24.649*k(-2,0,1,0).y;z+=-2*k(-2,0,1,-2).y;z+=-22.571*k(0,1,1,-2).y;z+=10.985*k(0,-1,1,-2).y;p+=.82*l(.7736-62.5512*a)+.31*l(.0466-125.1025*a)+.35*l(.5785-25.1042*a)+.66*l(.4591+1335.8075*a)+.64*l(.313-91.568*a)+1.14*l(.148+1331.2898*a)+.21*l(.5918+1056.5859*a)+.44*l(.5784+1322.8595*a)+.24*l(.2275-5.7374*a)+.28*l(.2965+2.6929*a)+.33*l(.3132+6.3368*a);a=Z+t/aa;a=(1.000002708+139.978*fa)*(18518.511+1.189+ea)*Math.sin(a)-6.24*Math.sin(3*a)+z;return{geo_eclip_lon:R*N((Wa+p/aa)/ -R),geo_eclip_lat:Math.PI/648E3*a,distance_au:aa*Xc/(.999953253*S)}}function Sb(a,b){return[a.rot[0][0]*b[0]+a.rot[1][0]*b[1]+a.rot[2][0]*b[2],a.rot[0][1]*b[0]+a.rot[1][1]*b[1]+a.rot[2][1]*b[2],a.rot[0][2]*b[0]+a.rot[1][2]*b[1]+a.rot[2][2]*b[2]]}function Ba(a,b,c){b=Ca(b,c);return Sb(b,a)}function Ca(a,b){a=a.tt/36525;var c=84381.406,d=((((3.337E-7*a-4.67E-7)*a-.00772503)*a+.0512623)*a-.025754)*a+c;c*=4.84813681109536E-6;var f=((((-9.51E-8*a+1.32851E-4)*a-.00114045)*a-1.0790069)*a+5038.481507)*a*4.84813681109536E-6; -d*=4.84813681109536E-6;var h=((((-5.6E-8*a+1.70663E-4)*a-.00121197)*a-2.3814292)*a+10.556403)*a*4.84813681109536E-6;a=Math.sin(c);c=Math.cos(c);var l=Math.sin(-f);f=Math.cos(-f);var k=Math.sin(-d);d=Math.cos(-d);var g=Math.sin(h),m=Math.cos(h);h=m*f-l*g*d;var n=m*l*c+g*d*f*c-a*g*k,p=m*l*a+g*d*f*a+c*g*k,t=-g*f-l*m*d,x=-g*l*c+m*d*f*c-a*m*k;g=-g*l*a+m*d*f*a+c*m*k;l*=k;m=-k*f*c-a*d;a=-k*f*a+d*c;if(b===F.Into2000)return new L([[h,n,p],[t,x,g],[l,m,a]]);if(b===F.From2000)return new L([[h,t,l],[n,x,m],[p, -g,a]]);throw"Invalid precess direction";}function ba(a){if(!Ya||Ya.tt!==a.tt){var b=a.tt/36525,c=15*Ta(a).ee,d=(.779057273264+.00273781191135448*a.ut+a.ut%1)%1*360;0>d&&(d+=360);b=((c+.014506+((((-3.68E-8*b-2.9956E-5)*b-4.4E-7)*b+1.3915817)*b+4612.156534)*b)/3600+d)%360/15;0>b&&(b+=24);Ya={tt:a.tt,st:b}}return Ya.st}function ob(a,b){var c=a.latitude*e.DEG2RAD,d=Math.sin(c);c=Math.cos(c);var f=1/Math.hypot(c,.996647180302104*d),h=a.height/1E3,l=6378.1366*f+h;b=(15*b+a.longitude)*e.DEG2RAD;a=Math.sin(b); -b=Math.cos(b);return{pos:[l*c*b/e.KM_PER_AU,l*c*a/e.KM_PER_AU,(6335.438815127603*f+h)*d/e.KM_PER_AU],vel:[-7.292115E-5*l*c*a*86400/e.KM_PER_AU,7.292115E-5*l*c*b*86400/e.KM_PER_AU,0]}}function Za(a,b,c){b=Da(b,c);return Sb(b,a)}function Da(a,b){a=Ta(a);var c=a.mobl*e.DEG2RAD,d=a.tobl*e.DEG2RAD,f=4.84813681109536E-6*a.dpsi;a=Math.cos(c);c=Math.sin(c);var h=Math.cos(d),l=Math.sin(d);d=Math.cos(f);var k=Math.sin(f);f=-k*a;var g=-k*c,m=k*h,n=d*a*h+c*l,p=d*c*h-a*l;k*=l;var t=d*a*l-c*h;a=d*c*l+a*h;if(b=== -F.From2000)return new L([[d,m,k],[f,n,t],[g,p,a]]);if(b===F.Into2000)return new L([[d,f,g],[m,n,p],[k,t,a]]);throw"Invalid precess direction";}function $a(a,b,c){return c===F.Into2000?Ba(Za(a,b,c),b,c):Za(Ba(a,b,c),b,c)}function Tb(a,b){var c=ba(a);b=ob(b,c).pos;return $a(b,a,F.Into2000)}function Yc(a){if(!(a instanceof Array)||3!==a.length)return!1;for(var b=0;3>b;++b){if(!(a[b]instanceof Array)||3!==a[b].length)return!1;for(var c=0;3>c;++c)if(!Number.isFinite(a[b][c]))return!1}return!0}function Ub(a, -b){b=new D(a[0],a[1],a[2],b);var c=b.x*b.x+b.y*b.y,d=Math.sqrt(c+b.z*b.z);if(0===c){if(0===b.z)throw"Indeterminate sky coordinates";return new ab(0,0>b.z?-90:90,d,b)}var f=e.RAD2HOUR*Math.atan2(b.y,b.x);0>f&&(f+=24);return new ab(f,e.RAD2DEG*Math.atan2(a[2],Math.sqrt(c)),d,b)}function oa(a,b){var c=a*e.DEG2RAD;a=Math.cos(c);c=Math.sin(c);return[a*b[0]+c*b[1],a*b[1]-c*b[0],b[2]]}function Ea(a,b,c,d,f){a=w(a);pa(b);y(c);y(d);var h=Math.sin(b.latitude*e.DEG2RAD),l=Math.cos(b.latitude*e.DEG2RAD),k=Math.sin(b.longitude* -e.DEG2RAD),g=Math.cos(b.longitude*e.DEG2RAD);b=Math.sin(d*e.DEG2RAD);var m=Math.cos(d*e.DEG2RAD),n=Math.sin(c*e.HOUR2RAD),p=Math.cos(c*e.HOUR2RAD),t=[l*g,l*k,h];h=[-h*g,-h*k,l];k=[k,-g,0];l=-15*ba(a);a=oa(l,t);t=oa(l,h);k=oa(l,k);b=[m*p,m*n,b];n=b[0]*a[0]+b[1]*a[1]+b[2]*a[2];m=b[0]*t[0]+b[1]*t[1]+b[2]*t[2];t=b[0]*k[0]+b[1]*k[1]+b[2]*k[2];p=Math.hypot(m,t);0m&&(m+=360)):m=0;n=e.RAD2DEG*Math.atan2(p,n);p=d;if(f&&(d=n,f=Fa(f,90-n),n-=f,0k;++k)f.push((b[k]-d*a[k])/t*c+a[k]*p);p=Math.hypot(f[0],f[1]);0c&&(c+=24)):c=0;p=e.RAD2DEG*Math.atan2(f[2],p)}return new Vb(m,90-n,c,p)}function pa(a){if(!(a instanceof pb))throw"Not an instance of the Observer class: "+a;y(a.latitude);y(a.longitude);y(a.height);if(-90>a.latitude||90b&&(b+=360));h=e.RAD2DEG*Math.atan2(c,h);a=new D(d,f,c,a.t);return new Yb(a,h,b)}function Ha(a){void 0===bb&&(bb=e.DEG2RAD*Ta(w(qb)).mobl,Zb=Math.cos(bb),$b=Math.sin(bb));return Xb(a,Zb,$b)}function V(a){a=w(a);var b=X(a),c=b.distance_au*Math.cos(b.geo_eclip_lat);b=[c*Math.cos(b.geo_eclip_lon),c*Math.sin(b.geo_eclip_lon),b.distance_au*Math.sin(b.geo_eclip_lat)];var d=Ob(a)*e.DEG2RAD;c=Math.cos(d);d=Math.sin(d);b=Ba([b[0],b[1]*c-b[2]*d,b[1]*d+b[2]*c],a,F.Into2000);return new D(b[0], -b[1],b[2],a)}function cb(a){a=w(a);a=X(a);return new Ia(a.geo_eclip_lat*e.RAD2DEG,a.geo_eclip_lon*e.RAD2DEG,a.distance_au)}function Ja(a){a=w(a);var b=a.AddDays(-1E-5),c=a.AddDays(1E-5);b=V(b);c=V(c);return new K((b.x+c.x)/2,(b.y+c.y)/2,(b.z+c.z)/2,(c.x-b.x)/2E-5,(c.y-b.y)/2E-5,(c.z-b.z)/2E-5,a)}function rb(a){a=w(a);var b=Ja(a);return new K(b.x/82.30056,b.y/82.30056,b.z/82.30056,b.vx/82.30056,b.vy/82.30056,b.vz/82.30056,a)}function ha(a,b,c){var d=1,f=0;a=$jscomp.makeIterator(a);for(var h=a.next();!h.done;h= -a.next()){var l=0;h=$jscomp.makeIterator(h.value);for(var k=h.next();!k.done;k=h.next()){var g=$jscomp.makeIterator(k.value);k=g.next().value;var m=g.next().value;g=g.next().value;l+=k*Math.cos(m+b*g)}l*=d;c&&(l%=R);f+=l;d*=b}return f}function sb(a,b){var c=1,d=0,f=0,h=0;a=$jscomp.makeIterator(a);for(var l=a.next();!l.done;l=a.next()){var k=0,g=0;l=$jscomp.makeIterator(l.value);for(var m=l.next();!m.done;m=l.next()){var n=$jscomp.makeIterator(m.value);m=n.next().value;var p=n.next().value;n=n.next().value; -p+=b*n;k+=m*n*Math.sin(p);0a?0:a>=b?b-1:a}function vb(a){var b=$jscomp.makeIterator(a); -a=b.next().value;var c=$jscomp.makeIterator(b.next().value);var d=c.next().value;var f=c.next().value;c=c.next().value;var h=$jscomp.makeIterator(b.next().value);b=h.next().value;var l=h.next().value;h=h.next().value;d=new db(a,new G(d,f,c),new G(b,l,h));a=new qa(d.tt);f=d.r.add(a.Sun.r);c=d.v.add(a.Sun.v);b=a.Acceleration(f);d=new cc(d.tt,f,c,b);return new dc(a,d)}function fc(a,b,c){a=vb(a);for(var d=Math.ceil((b-a.grav.tt)/c),f=0;fia[50][0])c=null;else{c=ec((c-d)/29200,50);if(!xb[c]){d=xb[c]=[];d[0]=vb(ia[c]).grav;d[200]=vb(ia[c+1]).grav;var f,h=d[0].tt;for(f=1;200>f;++f)d[f]=ub(h+=146,d[f-1]).grav;h=d[200].tt;var l=[];l[200]=d[200];for(f=199;0k;++k){f=Ma(a,l);c?d=U(H.Earth,l):d||(d=U(H.Earth,b));f=new D(f.x-d.x,f.y-d.y,f.z-d.z,b);var g=b.AddDays(-f.Length()/e.C_AUDAY);h=Math.abs(g.tt-l.tt);if(1E-9>h)return f;l=g}throw"Light-travel time solver did not converge: dt="+h;}function ra(a,b){return new K(a.r.x,a.r.y,a.r.z,a.v.x,a.v.y,a.v.z,b)}function yb(a,b){b=w(b);switch(a){case q.Sun:return new K(0,0,0,0,0,0,b);case q.SSB:return a= -new qa(b.tt),new K(-a.Sun.r.x,-a.Sun.r.y,-a.Sun.r.z,-a.Sun.v.x,-a.Sun.v.y,-a.Sun.v.z,b);case q.Mercury:case q.Venus:case q.Earth:case q.Mars:case q.Jupiter:case q.Saturn:case q.Uranus:case q.Neptune:return a=Ka(H[a],b.tt),ra(a,b);case q.Pluto:return wb(b,!0);case q.Moon:case q.EMB:var c=Ka(H.Earth,b.tt);a=a==q.Moon?Ja(b):rb(b);return new K(a.x+c.r.x,a.y+c.r.y,a.z+c.r.z,a.vx+c.v.x,a.vy+c.v.y,a.vz+c.v.z,b);default:throw'HelioState: Unsupported body "'+a+'"';}}function Zc(a,b,c,d,f){var h=(f+c)/2-d; -c=(f-c)/2;if(0==h){if(0==c)return null;d=-d/c;if(-1>d||1=d)return null;f=Math.sqrt(d);d=(-c+f)/(2*h);f=(-c-f)/(2*h);if(-1<=d&&1>=d){if(-1<=f&&1>=f)return null}else if(-1<=f&&1>=f)d=f;else return null}return{x:d,t:a+d*b,df_dt:(2*h*d+c)/b}}function I(a,b,c,d){var f=y(d&&d.dt_tolerance_seconds||1);f=Math.abs(f/86400);var h=d&&d.init_f1||a(b),l=d&&d.init_f2||a(c),k=NaN,g=0;d=d&&d.iter_limit||20;for(var m=!0;;){if(++g>d)throw"Excessive iteration in Search()";var n= -new P(b.ut+.5*(c.ut-b.ut)),p=n.ut-b.ut;if(Math.abs(p)(p.ut-b.ut)*(p.ut-c.ut)&&0>(x.ut-b.ut)*(x.ut-c.ut))){t=a(p);var z=a(x);if(0>t&&0<=z){h=t;l=z;b=p;c=x;k=v;m=!1;continue}}}}if(0>h&&0<=k)c=n,l=k;else if(0>k&&0<=l)b=n,h=k;else return null}}function sa(a){for(;-180>=a;)a+=360;for(;180a;)a+=360;for(;360<=a;)a-=360;return a}function gc(a,b,c){y(a);y(c);b=w(b);c=b.AddDays(c);return I(function(d){d=Wb(d);return sa(d.elon-a)},b,c)}function zb(a,b,c){if(a===q.Earth||b===q.Earth)throw"The Earth does not have a longitude as seen from itself.";c=w(c);a=ca(a,c,!1);a=Ha(a);b=ca(b,c,!1);b=Ha(b);return ta(a.elon-b.elon)}function ua(a,b){if(a==q.Earth)throw"The Earth does not have an angle as seen from itself.";var c=w(b);b=ca(q.Sun,c,!0);a=ca(a,c,!0);return O(b,a)}function ka(a, -b){if(a===q.Sun)throw"Cannot calculate heliocentric longitude of the Sun.";a=Ma(a,b);return Ha(a).elon}function hb(a,b){if(a===q.Earth)throw"The illumination of the Earth is not defined.";var c=w(b),d=U(H.Earth,c);if(a===q.Sun){var f=new D(-d.x,-d.y,-d.z,c);b=new D(0,0,0,c);d=0}else a===q.Moon?(f=V(c),b=new D(d.x+f.x,d.y+f.y,d.z+f.z,c)):(b=Ma(a,b),f=new D(b.x-d.x,b.y-d.y,b.z-d.z,c)),d=O(f,b);var h=f.Length(),l=b.Length();if(a===q.Sun)var k=$c+5*Math.log10(h);else if(a===q.Moon)a=d*e.DEG2RAD,k=a*a, -a=-12.717+1.49*Math.abs(a)+.0431*k*k,k=a+=5*Math.log10(h/(385000.6/e.KM_PER_AU)*l);else if(a===q.Saturn){var g=d;a=Ha(f);k=28.06*e.DEG2RAD;var m=e.DEG2RAD*a.elat;a=Math.asin(Math.sin(m)*Math.cos(k)-Math.cos(m)*Math.sin(k)*Math.sin(e.DEG2RAD*a.elon-e.DEG2RAD*(169.51+3.82E-5*c.tt)));k=Math.sin(Math.abs(a));g=-9+.044*g+k*(-2.6+1.2*k)+5*Math.log10(l*h);a*=e.RAD2DEG;k=g;g=a}else{var n=m=k=0;switch(a){case q.Mercury:a=-.6;k=4.98;m=-4.88;n=3.02;break;case q.Venus:163.6>d?(a=-4.47,k=1.03,m=.57,n=.13):(a= -.98,k=-1.02);break;case q.Mars:a=-1.52;k=1.6;break;case q.Jupiter:a=-9.4;k=.5;break;case q.Uranus:a=-7.19;k=.25;break;case q.Neptune:a=-6.87;break;case q.Pluto:a=-1;k=4;break;default:throw"VisualMagnitude: unsupported body "+a;}var p=d/100;k=a+p*(k+p*(m+p*n))+5*Math.log10(l*h)}return new hc(c,k,d,l,h,f,b,g)}function Na(a){if(a===q.Earth)throw"The Earth does not have a synodic period as seen from itself.";if(a===q.Moon)return 29.530588;var b=da[a];if(!b)throw"Not a valid planet name: "+a;a=da.Earth.OrbitalPeriod; -return Math.abs(a/(a/b.OrbitalPeriod-1))}function va(a,b,c){function d(m){var n=ka(a,m);m=ka(q.Earth,m);return sa(h*(m-n)-b)}y(b);var f=da[a];if(!f)throw"Cannot search relative longitude because body is not a planet: "+a;if(a===q.Earth)throw"Cannot search relative longitude for the Earth (it is always 0)";var h=f.OrbitalPeriod>da.Earth.OrbitalPeriod?1:-1;f=Na(a);c=w(c);var l=d(c);0k;++k){var g=-l/360*f;c=c.AddDays(g);if(1>86400*Math.abs(g))return c;g=l;l=d(c);30>Math.abs(g)&& -g!==l&&(g/=g-l,.5g&&(f*=g))}throw"Relative longitude search failed to converge for "+a+" near "+c.toString()+" (error_angle = "+l+").";}function Ab(a){return zb(q.Moon,q.Sun,a)}function Oa(a,b,c){function d(l){l=Ab(l);return sa(l-a)}y(a);y(c);b=w(b);var f=d(b);0c)return null;c=Math.min(c,h+1.5);f=b.AddDays(f);b=b.AddDays(c);return I(d,f,b)}function ic(a){var b=Ab(a);b=(Math.floor(b/90)+1)%4;a=Oa(90*b,a,10);if(!a)throw"Cannot find moon quarter"; +var k=(1072260.70369+1.602961601209E9*d)%1296E3*4.84813681109536E-6;var g=(450160.398036-6962890.5431*d)%1296E3*4.84813681109536E-6;var m=c=0;for(b=76;0<=b;--b){var n=Pb[b][0];var p=Pb[b][1];var t=(n[0]*f+n[1]*h+n[2]*l+n[3]*k+n[4]*g)%R;n=Math.sin(t);t=Math.cos(t);c+=(p[0]+p[1]*d)*n+p[2]*t;m+=(p[3]+p[4]*d)*t+p[5]*n}b=-1.35E-4+1E-7*c;m=3.88E-4+1E-7*m;d=Ob(a);Ua={tt:a.tt,dpsi:b,deps:m,ee:b*Math.cos(d*e.DEG2RAD)/15,mobl:d,tobl:d+m/3600}}return Ua}function Y(a){function b(C,E,M,Z){for(var J=[],ma=0;ma<= +E-C;++ma){var na=J,lb=na.push,mb,Qb=M,Vc=Z,Rb=[];for(mb=0;mb<=Vc-Qb;++mb)Rb.push(0);lb.call(na,{min:Qb,array:Rb})}return{min:C,array:J}}function c(C,E,M){C=C.array[E-C.min];return C.array[M-C.min]}function d(C,E,M){C=x.array[C-x.min];C.array[E-C.min]=M}function f(C,E,M){C=v.array[C-v.min];C.array[E-C.min]=M}function h(C,E,M,Z,J){J(C*M-E*Z,E*M+C*Z)}function l(C){return Math.sin(R*C)}function k(C,E,M,Z){var J={x:1,y:0};C=[0,C,E,M,Z];for(E=1;4>=E;++E)0!==C[E]&&h(J.x,J.y,c(x,C[E],E),c(v,C[E],E),function(ma, +na){return J.x=ma,J.y=na});return J}function g(C,E,M,Z,J,ma,na,lb){J=k(J,ma,na,lb);p+=C*J.y;t+=E*J.y;ea+=M*J.x;S+=Z*J.x}++e.CalcMoonCount;a=a.tt/36525;var m,n,p,t,x=b(-6,6,1,4),v=b(-6,6,1,4);var z=a*a;var ea=t=p=0;var S=3422.7;var fa=l(.19833+.05611*a);var aa=l(.27869+.04508*a);var T=l(.16827-.36903*a);var W=l(.34734-5.37261*a);var Aa=l(.10498-5.37899*a);var Va=l(.42681-.41855*a),Wc=l(.14943-5.37511*a);var Wa=.84*fa+.31*aa+14.27*T+7.26*W+.28*Aa+.24*Va;var nb=2.94*fa+.31*aa+14.27*T+9.34*W+1.12*Aa+ +.83*Va;var Xa=-6.4*fa-1.89*Va;aa=.21*fa+.31*aa+14.27*T-88.7*W-15.3*Aa+.24*Va-1.86*Wc;T=Wa-Xa;fa=-3.332E-6*l(.59734-5.37261*a)-5.39E-7*l(.35498-5.37899*a)-6.4E-8*l(.39943-5.37511*a);Wa=R*N(.60643382+1336.85522467*a-3.13E-6*z)+Wa/ba;nb=R*N(.37489701+1325.55240982*a+2.565E-5*z)+nb/ba;Xa=R*N(.99312619+99.99735956*a-4.4E-7*z)+Xa/ba;aa=R*N(.25909118+1342.2278298*a-8.92E-6*z)+aa/ba;Aa=R*N(.82736186+1236.85308708*a-3.97E-6*z)+T/ba;for(m=1;4>=m;++m){switch(m){case 1:T=nb;z=4;W=1.000002208;break;case 2:T=Xa; +z=3;W=.997504612-.002495388*a;break;case 3:T=aa;z=4;W=1.000002708+139.978*fa;break;case 4:T=Aa;z=6;W=1;break;default:throw"Internal error: I = "+m;}d(0,m,1);d(1,m,Math.cos(T)*W);f(0,m,0);f(1,m,Math.sin(T)*W);for(n=2;n<=z;++n)h(c(x,n-1,m),c(v,n-1,m),c(x,1,m),c(v,1,m),function(C,E){return d(n,m,C),f(n,m,E)});for(n=1;n<=z;++n)d(-n,m,c(x,n,m)),f(-n,m,-c(v,n,m))}g(13.902,14.06,-.001,.2607,0,0,0,4);g(.403,-4.01,.394,.0023,0,0,0,3);g(2369.912,2373.36,.601,28.2333,0,0,0,2);g(-125.154,-112.79,-.725,-.9781, +0,0,0,1);g(1.979,6.98,-.445,.0433,1,0,0,4);g(191.953,192.72,.029,3.0861,1,0,0,2);g(-8.466,-13.51,.455,-.1093,1,0,0,1);g(22639.5,22609.07,.079,186.5398,1,0,0,0);g(18.609,3.59,-.094,.0118,1,0,0,-1);g(-4586.465,-4578.13,-.077,34.3117,1,0,0,-2);g(3.215,5.44,.192,-.0386,1,0,0,-3);g(-38.428,-38.64,.001,.6008,1,0,0,-4);g(-.393,-1.43,-.092,.0086,1,0,0,-6);g(-.289,-1.59,.123,-.0053,0,1,0,4);g(-24.42,-25.1,.04,-.3,0,1,0,2);g(18.023,17.93,.007,.1494,0,1,0,1);g(-668.146,-126.98,-1.302,-.3997,0,1,0,0);g(.56,.32, +-.001,-.0037,0,1,0,-1);g(-165.145,-165.06,.054,1.9178,0,1,0,-2);g(-1.877,-6.46,-.416,.0339,0,1,0,-4);g(.213,1.02,-.074,.0054,2,0,0,4);g(14.387,14.78,-.017,.2833,2,0,0,2);g(-.586,-1.2,.054,-.01,2,0,0,1);g(769.016,767.96,.107,10.1657,2,0,0,0);g(1.75,2.01,-.018,.0155,2,0,0,-1);g(-211.656,-152.53,5.679,-.3039,2,0,0,-2);g(1.225,.91,-.03,-.0088,2,0,0,-3);g(-30.773,-34.07,-.308,.3722,2,0,0,-4);g(-.57,-1.4,-.074,.0109,2,0,0,-6);g(-2.921,-11.75,.787,-.0484,1,1,0,2);g(1.267,1.52,-.022,.0164,1,1,0,1);g(-109.673, +-115.18,.461,-.949,1,1,0,0);g(-205.962,-182.36,2.056,1.4437,1,1,0,-2);g(.233,.36,.012,-.0025,1,1,0,-3);g(-4.391,-9.66,-.471,.0673,1,1,0,-4);g(.283,1.53,-.111,.006,1,-1,0,4);g(14.577,31.7,-1.54,.2302,1,-1,0,2);g(147.687,138.76,.679,1.1528,1,-1,0,0);g(-1.089,.55,.021,0,1,-1,0,-1);g(28.475,23.59,-.443,-.2257,1,-1,0,-2);g(-.276,-.38,-.006,-.0036,1,-1,0,-3);g(.636,2.27,.146,-.0102,1,-1,0,-4);g(-.189,-1.68,.131,-.0028,0,2,0,2);g(-7.486,-.66,-.037,-.0086,0,2,0,0);g(-8.096,-16.35,-.74,.0918,0,2,0,-2);g(-5.741, +-.04,0,-9E-4,0,0,2,2);g(.255,0,0,0,0,0,2,1);g(-411.608,-.2,0,-.0124,0,0,2,0);g(.584,.84,0,.0071,0,0,2,-1);g(-55.173,-52.14,0,-.1052,0,0,2,-2);g(.254,.25,0,-.0017,0,0,2,-3);g(.025,-1.67,0,.0031,0,0,2,-4);g(1.06,2.96,-.166,.0243,3,0,0,2);g(36.124,50.64,-1.3,.6215,3,0,0,0);g(-13.193,-16.4,.258,-.1187,3,0,0,-2);g(-1.187,-.74,.042,.0074,3,0,0,-4);g(-.293,-.31,-.002,.0046,3,0,0,-6);g(-.29,-1.45,.116,-.0051,2,1,0,2);g(-7.649,-10.56,.259,-.1038,2,1,0,0);g(-8.627,-7.59,.078,-.0192,2,1,0,-2);g(-2.74,-2.54, +.022,.0324,2,1,0,-4);g(1.181,3.32,-.212,.0213,2,-1,0,2);g(9.703,11.67,-.151,.1268,2,-1,0,0);g(-.352,-.37,.001,-.0028,2,-1,0,-1);g(-2.494,-1.17,-.003,-.0017,2,-1,0,-2);g(.36,.2,-.012,-.0043,2,-1,0,-4);g(-1.167,-1.25,.008,-.0106,1,2,0,0);g(-7.412,-6.12,.117,.0484,1,2,0,-2);g(-.311,-.65,-.032,.0044,1,2,0,-4);g(.757,1.82,-.105,.0112,1,-2,0,2);g(2.58,2.32,.027,.0196,1,-2,0,0);g(2.533,2.4,-.014,-.0212,1,-2,0,-2);g(-.344,-.57,-.025,.0036,0,3,0,-2);g(-.992,-.02,0,0,1,0,2,2);g(-45.099,-.02,0,-.001,1,0,2,0); +g(-.179,-9.52,0,-.0833,1,0,2,-2);g(-.301,-.33,0,.0014,1,0,2,-4);g(-6.382,-3.37,0,-.0481,1,0,-2,2);g(39.528,85.13,0,-.7136,1,0,-2,0);g(9.366,.71,0,-.0112,1,0,-2,-2);g(.202,.02,0,0,1,0,-2,-4);g(.415,.1,0,.0013,0,1,2,0);g(-2.152,-2.26,0,-.0066,0,1,2,-2);g(-1.44,-1.3,0,.0014,0,1,-2,2);g(.384,-.04,0,0,0,1,-2,-2);g(1.938,3.6,-.145,.0401,4,0,0,0);g(-.952,-1.58,.052,-.013,4,0,0,-2);g(-.551,-.94,.032,-.0097,3,1,0,0);g(-.482,-.57,.005,-.0045,3,1,0,-2);g(.681,.96,-.026,.0115,3,-1,0,0);g(-.297,-.27,.002,-9E-4, +2,2,0,-2);g(.254,.21,-.003,0,2,-2,0,-2);g(-.25,-.22,.004,.0014,1,3,0,-2);g(-3.996,0,0,4E-4,2,0,2,0);g(.557,-.75,0,-.009,2,0,2,-2);g(-.459,-.38,0,-.0053,2,0,-2,2);g(-1.298,.74,0,4E-4,2,0,-2,0);g(.538,1.14,0,-.0141,2,0,-2,-2);g(.263,.02,0,0,1,1,2,0);g(.426,.07,0,-6E-4,1,1,-2,-2);g(-.304,.03,0,3E-4,1,-1,2,0);g(-.372,-.19,0,-.0027,1,-1,-2,2);g(.418,0,0,0,0,0,4,0);g(-.33,-.04,0,0,3,0,2,0);z=-526.069*k(0,0,1,-2).y;z+=-3.352*k(0,0,1,-4).y;z+=44.297*k(1,0,1,-2).y;z+=-6*k(1,0,1,-4).y;z+=20.599*k(-1,0,1,0).y; +z+=-30.598*k(-1,0,1,-2).y;z+=-24.649*k(-2,0,1,0).y;z+=-2*k(-2,0,1,-2).y;z+=-22.571*k(0,1,1,-2).y;z+=10.985*k(0,-1,1,-2).y;p+=.82*l(.7736-62.5512*a)+.31*l(.0466-125.1025*a)+.35*l(.5785-25.1042*a)+.66*l(.4591+1335.8075*a)+.64*l(.313-91.568*a)+1.14*l(.148+1331.2898*a)+.21*l(.5918+1056.5859*a)+.44*l(.5784+1322.8595*a)+.24*l(.2275-5.7374*a)+.28*l(.2965+2.6929*a)+.33*l(.3132+6.3368*a);a=aa+t/ba;a=(1.000002708+139.978*fa)*(18518.511+1.189+ea)*Math.sin(a)-6.24*Math.sin(3*a)+z;return{geo_eclip_lon:R*N((Wa+ +p/ba)/R),geo_eclip_lat:Math.PI/648E3*a,distance_au:ba*Xc/(.999953253*S)}}function Sb(a,b){return[a.rot[0][0]*b[0]+a.rot[1][0]*b[1]+a.rot[2][0]*b[2],a.rot[0][1]*b[0]+a.rot[1][1]*b[1]+a.rot[2][1]*b[2],a.rot[0][2]*b[0]+a.rot[1][2]*b[1]+a.rot[2][2]*b[2]]}function Ba(a,b,c){b=Ca(b,c);return Sb(b,a)}function Ca(a,b){a=a.tt/36525;var c=84381.406,d=((((3.337E-7*a-4.67E-7)*a-.00772503)*a+.0512623)*a-.025754)*a+c;c*=4.84813681109536E-6;var f=((((-9.51E-8*a+1.32851E-4)*a-.00114045)*a-1.0790069)*a+5038.481507)* +a*4.84813681109536E-6;d*=4.84813681109536E-6;var h=((((-5.6E-8*a+1.70663E-4)*a-.00121197)*a-2.3814292)*a+10.556403)*a*4.84813681109536E-6;a=Math.sin(c);c=Math.cos(c);var l=Math.sin(-f);f=Math.cos(-f);var k=Math.sin(-d);d=Math.cos(-d);var g=Math.sin(h),m=Math.cos(h);h=m*f-l*g*d;var n=m*l*c+g*d*f*c-a*g*k,p=m*l*a+g*d*f*a+c*g*k,t=-g*f-l*m*d,x=-g*l*c+m*d*f*c-a*m*k;g=-g*l*a+m*d*f*a+c*m*k;l*=k;m=-k*f*c-a*d;a=-k*f*a+d*c;if(b===F.Into2000)return new L([[h,n,p],[t,x,g],[l,m,a]]);if(b===F.From2000)return new L([[h, +t,l],[n,x,m],[p,g,a]]);throw"Invalid precess direction";}function X(a){if(!Ya||Ya.tt!==a.tt){var b=a.tt/36525,c=15*Ta(a).ee,d=(.779057273264+.00273781191135448*a.ut+a.ut%1)%1*360;0>d&&(d+=360);b=((c+.014506+((((-3.68E-8*b-2.9956E-5)*b-4.4E-7)*b+1.3915817)*b+4612.156534)*b)/3600+d)%360/15;0>b&&(b+=24);Ya={tt:a.tt,st:b}}return Ya.st}function ob(a,b){var c=a.latitude*e.DEG2RAD,d=Math.sin(c);c=Math.cos(c);var f=1/Math.hypot(c,.996647180302104*d),h=a.height/1E3,l=6378.1366*f+h;b=(15*b+a.longitude)*e.DEG2RAD; +a=Math.sin(b);b=Math.cos(b);return{pos:[l*c*b/e.KM_PER_AU,l*c*a/e.KM_PER_AU,(6335.438815127603*f+h)*d/e.KM_PER_AU],vel:[-7.292115E-5*l*c*a*86400/e.KM_PER_AU,7.292115E-5*l*c*b*86400/e.KM_PER_AU,0]}}function Za(a,b,c){b=Da(b,c);return Sb(b,a)}function Da(a,b){a=Ta(a);var c=a.mobl*e.DEG2RAD,d=a.tobl*e.DEG2RAD,f=4.84813681109536E-6*a.dpsi;a=Math.cos(c);c=Math.sin(c);var h=Math.cos(d),l=Math.sin(d);d=Math.cos(f);var k=Math.sin(f);f=-k*a;var g=-k*c,m=k*h,n=d*a*h+c*l,p=d*c*h-a*l;k*=l;var t=d*a*l-c*h;a=d* +c*l+a*h;if(b===F.From2000)return new L([[d,m,k],[f,n,t],[g,p,a]]);if(b===F.Into2000)return new L([[d,f,g],[m,n,p],[k,t,a]]);throw"Invalid precess direction";}function $a(a,b,c){return c===F.Into2000?Ba(Za(a,b,c),b,c):Za(Ba(a,b,c),b,c)}function Tb(a,b){var c=X(a);b=ob(b,c).pos;return $a(b,a,F.Into2000)}function Yc(a){if(!(a instanceof Array)||3!==a.length)return!1;for(var b=0;3>b;++b){if(!(a[b]instanceof Array)||3!==a[b].length)return!1;for(var c=0;3>c;++c)if(!Number.isFinite(a[b][c]))return!1}return!0} +function Ub(a,b){b=new D(a[0],a[1],a[2],b);var c=b.x*b.x+b.y*b.y,d=Math.sqrt(c+b.z*b.z);if(0===c){if(0===b.z)throw"Indeterminate sky coordinates";return new ab(0,0>b.z?-90:90,d,b)}var f=e.RAD2HOUR*Math.atan2(b.y,b.x);0>f&&(f+=24);return new ab(f,e.RAD2DEG*Math.atan2(a[2],Math.sqrt(c)),d,b)}function oa(a,b){var c=a*e.DEG2RAD;a=Math.cos(c);c=Math.sin(c);return[a*b[0]+c*b[1],a*b[1]-c*b[0],b[2]]}function Ea(a,b,c,d,f){a=w(a);pa(b);y(c);y(d);var h=Math.sin(b.latitude*e.DEG2RAD),l=Math.cos(b.latitude*e.DEG2RAD), +k=Math.sin(b.longitude*e.DEG2RAD),g=Math.cos(b.longitude*e.DEG2RAD);b=Math.sin(d*e.DEG2RAD);var m=Math.cos(d*e.DEG2RAD),n=Math.sin(c*e.HOUR2RAD),p=Math.cos(c*e.HOUR2RAD),t=[l*g,l*k,h];h=[-h*g,-h*k,l];k=[k,-g,0];l=-15*X(a);a=oa(l,t);t=oa(l,h);k=oa(l,k);b=[m*p,m*n,b];n=b[0]*a[0]+b[1]*a[1]+b[2]*a[2];m=b[0]*t[0]+b[1]*t[1]+b[2]*t[2];t=b[0]*k[0]+b[1]*k[1]+b[2]*k[2];p=Math.hypot(m,t);0m&&(m+=360)):m=0;n=e.RAD2DEG*Math.atan2(p,n);p=d;if(f&&(d=n,f=Fa(f,90-n),n-=f,0k;++k)f.push((b[k]-d*a[k])/t*c+a[k]*p);p=Math.hypot(f[0],f[1]);0c&&(c+=24)):c=0;p=e.RAD2DEG*Math.atan2(f[2],p)}return new Vb(m,90-n,c,p)}function pa(a){if(!(a instanceof pb))throw"Not an instance of the Observer class: "+a;y(a.latitude);y(a.longitude);y(a.height);if(-90>a.latitude||90b&&(b+=360));h=e.RAD2DEG*Math.atan2(c,h);a=new D(d,f,c,a.t);return new Yb(a,h,b)}function Ha(a){void 0===bb&&(bb=e.DEG2RAD*Ta(w(qb)).mobl,Zb=Math.cos(bb),$b=Math.sin(bb));return Xb(a,Zb,$b)}function V(a){a=w(a);var b=Y(a),c=b.distance_au*Math.cos(b.geo_eclip_lat);b=[c*Math.cos(b.geo_eclip_lon),c*Math.sin(b.geo_eclip_lon),b.distance_au*Math.sin(b.geo_eclip_lat)];var d=Ob(a)*e.DEG2RAD;c=Math.cos(d);d=Math.sin(d);b=Ba([b[0],b[1]*c-b[2]*d,b[1]*d+b[2]*c],a,F.Into2000); +return new D(b[0],b[1],b[2],a)}function cb(a){a=w(a);a=Y(a);return new Ia(a.geo_eclip_lat*e.RAD2DEG,a.geo_eclip_lon*e.RAD2DEG,a.distance_au)}function Ja(a){a=w(a);var b=a.AddDays(-1E-5),c=a.AddDays(1E-5);b=V(b);c=V(c);return new K((b.x+c.x)/2,(b.y+c.y)/2,(b.z+c.z)/2,(c.x-b.x)/2E-5,(c.y-b.y)/2E-5,(c.z-b.z)/2E-5,a)}function rb(a){a=w(a);var b=Ja(a);return new K(b.x/82.30056,b.y/82.30056,b.z/82.30056,b.vx/82.30056,b.vy/82.30056,b.vz/82.30056,a)}function ha(a,b,c){var d=1,f=0;a=$jscomp.makeIterator(a); +for(var h=a.next();!h.done;h=a.next()){var l=0;h=$jscomp.makeIterator(h.value);for(var k=h.next();!k.done;k=h.next()){var g=$jscomp.makeIterator(k.value);k=g.next().value;var m=g.next().value;g=g.next().value;l+=k*Math.cos(m+b*g)}l*=d;c&&(l%=R);f+=l;d*=b}return f}function sb(a,b){var c=1,d=0,f=0,h=0;a=$jscomp.makeIterator(a);for(var l=a.next();!l.done;l=a.next()){var k=0,g=0;l=$jscomp.makeIterator(l.value);for(var m=l.next();!m.done;m=l.next()){var n=$jscomp.makeIterator(m.value);m=n.next().value; +var p=n.next().value;n=n.next().value;p+=b*n;k+=m*n*Math.sin(p);0a?0:a>=b?b-1:a}function vb(a){var b= +$jscomp.makeIterator(a);a=b.next().value;var c=$jscomp.makeIterator(b.next().value);var d=c.next().value;var f=c.next().value;c=c.next().value;var h=$jscomp.makeIterator(b.next().value);b=h.next().value;var l=h.next().value;h=h.next().value;d=new db(a,new G(d,f,c),new G(b,l,h));a=new qa(d.tt);f=d.r.add(a.Sun.r);c=d.v.add(a.Sun.v);b=a.Acceleration(f);d=new cc(d.tt,f,c,b);return new dc(a,d)}function fc(a,b,c){a=vb(a);for(var d=Math.ceil((b-a.grav.tt)/c),f=0;fia[50][0])c=null;else{c=ec((c-d)/29200,50);if(!xb[c]){d=xb[c]=[];d[0]=vb(ia[c]).grav;d[200]=vb(ia[c+1]).grav;var f,h=d[0].tt;for(f=1;200>f;++f)d[f]=ub(h+=146,d[f-1]).grav;h=d[200].tt;var l=[];l[200]=d[200];for(f=199;0k;++k){f=Ma(a,l);c?d=U(H.Earth,l):d||(d=U(H.Earth,b));f=new D(f.x-d.x,f.y-d.y,f.z-d.z,b);var g=b.AddDays(-f.Length()/e.C_AUDAY);h=Math.abs(g.tt-l.tt);if(1E-9>h)return f;l=g}throw"Light-travel time solver did not converge: dt="+h;}function ra(a,b){return new K(a.r.x,a.r.y,a.r.z,a.v.x,a.v.y,a.v.z,b)}function yb(a,b){b=w(b);switch(a){case q.Sun:return new K(0, +0,0,0,0,0,b);case q.SSB:return a=new qa(b.tt),new K(-a.Sun.r.x,-a.Sun.r.y,-a.Sun.r.z,-a.Sun.v.x,-a.Sun.v.y,-a.Sun.v.z,b);case q.Mercury:case q.Venus:case q.Earth:case q.Mars:case q.Jupiter:case q.Saturn:case q.Uranus:case q.Neptune:return a=Ka(H[a],b.tt),ra(a,b);case q.Pluto:return wb(b,!0);case q.Moon:case q.EMB:var c=Ka(H.Earth,b.tt);a=a==q.Moon?Ja(b):rb(b);return new K(a.x+c.r.x,a.y+c.r.y,a.z+c.r.z,a.vx+c.v.x,a.vy+c.v.y,a.vz+c.v.z,b);default:throw'HelioState: Unsupported body "'+a+'"';}}function Zc(a, +b,c,d,f){var h=(f+c)/2-d;c=(f-c)/2;if(0==h){if(0==c)return null;d=-d/c;if(-1>d||1=d)return null;f=Math.sqrt(d);d=(-c+f)/(2*h);f=(-c-f)/(2*h);if(-1<=d&&1>=d){if(-1<=f&&1>=f)return null}else if(-1<=f&&1>=f)d=f;else return null}return{x:d,t:a+d*b,df_dt:(2*h*d+c)/b}}function I(a,b,c,d){var f=y(d&&d.dt_tolerance_seconds||1);f=Math.abs(f/86400);var h=d&&d.init_f1||a(b),l=d&&d.init_f2||a(c),k=NaN,g=0;d=d&&d.iter_limit||20;for(var m=!0;;){if(++g>d)throw"Excessive iteration in Search()"; +var n=new P(b.ut+.5*(c.ut-b.ut)),p=n.ut-b.ut;if(Math.abs(p)(p.ut-b.ut)*(p.ut-c.ut)&&0>(x.ut-b.ut)*(x.ut-c.ut))){t=a(p);var z=a(x);if(0>t&&0<=z){h=t;l=z;b=p;c=x;k=v;m=!1;continue}}}}if(0>h&&0<=k)c=n,l=k;else if(0>k&&0<=l)b=n,h=k;else return null}}function sa(a){for(;-180>=a;)a+=360;for(;180a;)a+=360;for(;360<=a;)a-=360;return a}function gc(a,b,c){y(a);y(c);b=w(b);c=b.AddDays(c);return I(function(d){d=Wb(d);return sa(d.elon-a)},b,c)}function zb(a,b,c){if(a===q.Earth||b===q.Earth)throw"The Earth does not have a longitude as seen from itself.";c=w(c);a=ca(a,c,!1);a=Ha(a);b=ca(b,c,!1);b=Ha(b);return ta(a.elon-b.elon)}function ua(a,b){if(a==q.Earth)throw"The Earth does not have an angle as seen from itself.";var c=w(b);b=ca(q.Sun,c,!0);a=ca(a,c,!0);return O(b, +a)}function ka(a,b){if(a===q.Sun)throw"Cannot calculate heliocentric longitude of the Sun.";a=Ma(a,b);return Ha(a).elon}function hb(a,b){if(a===q.Earth)throw"The illumination of the Earth is not defined.";var c=w(b),d=U(H.Earth,c);if(a===q.Sun){var f=new D(-d.x,-d.y,-d.z,c);b=new D(0,0,0,c);d=0}else a===q.Moon?(f=V(c),b=new D(d.x+f.x,d.y+f.y,d.z+f.z,c)):(b=Ma(a,b),f=new D(b.x-d.x,b.y-d.y,b.z-d.z,c)),d=O(f,b);var h=f.Length(),l=b.Length();if(a===q.Sun)var k=$c+5*Math.log10(h);else if(a===q.Moon)a= +d*e.DEG2RAD,k=a*a,a=-12.717+1.49*Math.abs(a)+.0431*k*k,k=a+=5*Math.log10(h/(385000.6/e.KM_PER_AU)*l);else if(a===q.Saturn){var g=d;a=Ha(f);k=28.06*e.DEG2RAD;var m=e.DEG2RAD*a.elat;a=Math.asin(Math.sin(m)*Math.cos(k)-Math.cos(m)*Math.sin(k)*Math.sin(e.DEG2RAD*a.elon-e.DEG2RAD*(169.51+3.82E-5*c.tt)));k=Math.sin(Math.abs(a));g=-9+.044*g+k*(-2.6+1.2*k)+5*Math.log10(l*h);a*=e.RAD2DEG;k=g;g=a}else{var n=m=k=0;switch(a){case q.Mercury:a=-.6;k=4.98;m=-4.88;n=3.02;break;case q.Venus:163.6>d?(a=-4.47,k=1.03, +m=.57,n=.13):(a=.98,k=-1.02);break;case q.Mars:a=-1.52;k=1.6;break;case q.Jupiter:a=-9.4;k=.5;break;case q.Uranus:a=-7.19;k=.25;break;case q.Neptune:a=-6.87;break;case q.Pluto:a=-1;k=4;break;default:throw"VisualMagnitude: unsupported body "+a;}var p=d/100;k=a+p*(k+p*(m+p*n))+5*Math.log10(l*h)}return new hc(c,k,d,l,h,f,b,g)}function Na(a){if(a===q.Earth)throw"The Earth does not have a synodic period as seen from itself.";if(a===q.Moon)return 29.530588;var b=da[a];if(!b)throw"Not a valid planet name: "+ +a;a=da.Earth.OrbitalPeriod;return Math.abs(a/(a/b.OrbitalPeriod-1))}function va(a,b,c){function d(m){var n=ka(a,m);m=ka(q.Earth,m);return sa(h*(m-n)-b)}y(b);var f=da[a];if(!f)throw"Cannot search relative longitude because body is not a planet: "+a;if(a===q.Earth)throw"Cannot search relative longitude for the Earth (it is always 0)";var h=f.OrbitalPeriod>da.Earth.OrbitalPeriod?1:-1;f=Na(a);c=w(c);var l=d(c);0k;++k){var g=-l/360*f;c=c.AddDays(g);if(1>86400*Math.abs(g))return c; +g=l;l=d(c);30>Math.abs(g)&&g!==l&&(g/=g-l,.5g&&(f*=g))}throw"Relative longitude search failed to converge for "+a+" near "+c.toString()+" (error_angle = "+l+").";}function Ab(a){return zb(q.Moon,q.Sun,a)}function Oa(a,b,c){function d(l){l=Ab(l);return sa(l-a)}y(a);y(c);b=w(b);var f=d(b);0c)return null;c=Math.min(c,h+1.5);f=b.AddDays(f);b=b.AddDays(c);return I(d,f,b)}function ic(a){var b=Ab(a);b=(Math.floor(b/90)+1)%4;a=Oa(90*b,a,10);if(!a)throw"Cannot find moon quarter"; return new jc(b,a)}function kc(a,b,c,d,f,h){pa(b);y(f);if(0>=f)throw"Invalid value for limitDays: "+f;if(a===q.Earth)throw"Cannot find altitude event for the Earth.";if(1===c){c=12;var l=0}else if(-1===c)c=0,l=12;else throw"Invalid direction parameter "+c+" -- must be +1 or -1";d=w(d);var k=h(d);var g;if(0=k&&0= -d.ut+f)return null;m=k.time;k=h(k.time);g=h(n.time)}}function Pa(a,b,c,d){pa(b);d=w(d);var f=0;if(a===q.Earth)throw"Cannot search for hour angle of the Earth.";y(c);if(0>c||24<=c)throw"Invalid hour angle "+c;for(;;){++f;var h=ba(d),l=Ga(a,d,b,!0,!0);h=(c+l.ra-b.longitude/15-h)%24;1===f?0>h&&(h+=24):-12>h?h+=24:123600*Math.abs(h))return a=Ea(d,b,l.ra,l.dec,"normal"),new lc(d,a);d=d.AddDays(h/24*.9972695717592592)}}function mc(a,b){b=w(b);var c=zb(a,q.Sun,b);if(1805*f;++f){var h=a.AddDays(5),l=b(h);if(0>=d*l){if(0>d||0l){a=I(c,a,h,{init_f1:-d,init_f2:-l});if(!a)throw"SearchLunarApsis INTERNAL ERROR: apogee search failed!"; -d=X(a).distance_au;return new Qa(a,1,d)}throw"SearchLunarApsis INTERNAL ERROR: cannot classify apsis event!";}a=h;d=l}throw"SearchLunarApsis INTERNAL ERROR: could not find apsis within 2 synodic months of start date.";}function pc(a,b,c,d){for(var f=1===b?1:-1;;){d/=9;if(d<1/1440)return c=c.AddDays(d/2),a=ja(a,c),new Qa(c,b,a);for(var h=-1,l=0,k=0;10>k;++k){var g=c.AddDays(k*d);g=f*ja(a,g);if(0==k||g>l)h=k,l=g}c=c.AddDays((h-1)*d);d*=2}}function ad(a,b){var c=b.AddDays(-30/360*da[a].OrbitalPeriod), +d.ut+f)return null;m=k.time;k=h(k.time);g=h(n.time)}}function Pa(a,b,c,d){pa(b);d=w(d);var f=0;if(a===q.Earth)throw"Cannot search for hour angle of the Earth.";y(c);if(0>c||24<=c)throw"Invalid hour angle "+c;for(;;){++f;var h=X(d),l=Ga(a,d,b,!0,!0);h=(c+l.ra-b.longitude/15-h)%24;1===f?0>h&&(h+=24):-12>h?h+=24:123600*Math.abs(h))return a=Ea(d,b,l.ra,l.dec,"normal"),new lc(d,a);d=d.AddDays(h/24*.9972695717592592)}}function mc(a,b){b=w(b);var c=zb(a,q.Sun,b);if(1805*f;++f){var h=a.AddDays(5),l=b(h);if(0>=d*l){if(0>d||0l){a=I(c,a,h,{init_f1:-d,init_f2:-l});if(!a)throw"SearchLunarApsis INTERNAL ERROR: apogee search failed!"; +d=Y(a).distance_au;return new Qa(a,1,d)}throw"SearchLunarApsis INTERNAL ERROR: cannot classify apsis event!";}a=h;d=l}throw"SearchLunarApsis INTERNAL ERROR: could not find apsis within 2 synodic months of start date.";}function pc(a,b,c,d){for(var f=1===b?1:-1;;){d/=9;if(d<1/1440)return c=c.AddDays(d/2),a=ja(a,c),new Qa(c,b,a);for(var h=-1,l=0,k=0;10>k;++k){var g=c.AddDays(k*d);g=f*ja(a,g);if(0==k||g>l)h=k,l=g}c=c.AddDays((h-1)*d);d*=2}}function ad(a,b){var c=b.AddDays(-30/360*da[a].OrbitalPeriod), d=b.AddDays(.75*da[a].OrbitalPeriod),f=c,h=c,l=-1,k=-1;d=(d.ut-c.ut)/99;for(var g=0;100>g;++g){var m=c.AddDays(g*d),n=ja(a,m);0===g?k=l=n:(n>k&&(k=n,h=m),n=b.tt)return a.time.tt>=b.tt&&a.time.tt=b.tt)return a;throw"Internal error: failed to find Neptune apsis.";}function qc(a,b){function c(n){var p=n.AddDays(-5E-4);n=n.AddDays(5E-4);p=ja(a,p);return(ja(a,n)-p)/.001}function d(n){return-c(n)} b=w(b);if(a===q.Neptune||a===q.Pluto)return ad(a,b);for(var f=da[a].OrbitalPeriod,h=f/6,l=c(b),k=0;k*h<2*f;++k){var g=b.AddDays(h),m=c(g);if(0>=l*m){f=h=void 0;if(0>l||0m)h=d,f=1;else throw"Internal error with slopes in SearchPlanetApsis";b=I(h,b,g);if(!b)throw"Failed to find slope transition in planetary apsis search.";l=ja(a,b);return new Qa(b,f,l)}b=g;l=m}throw"Internal error: should have found planetary apsis within 2 orbital periods.";}function wa(a){return new L([[a.rot[0][0], a.rot[1][0],a.rot[2][0]],[a.rot[0][1],a.rot[1][1],a.rot[2][1]],[a.rot[0][2],a.rot[1][2],a.rot[2][2]]])}function xa(a,b){return new L([[b.rot[0][0]*a.rot[0][0]+b.rot[1][0]*a.rot[0][1]+b.rot[2][0]*a.rot[0][2],b.rot[0][1]*a.rot[0][0]+b.rot[1][1]*a.rot[0][1]+b.rot[2][1]*a.rot[0][2],b.rot[0][2]*a.rot[0][0]+b.rot[1][2]*a.rot[0][1]+b.rot[2][2]*a.rot[0][2]],[b.rot[0][0]*a.rot[1][0]+b.rot[1][0]*a.rot[1][1]+b.rot[2][0]*a.rot[1][2],b.rot[0][1]*a.rot[1][0]+b.rot[1][1]*a.rot[1][1]+b.rot[2][1]*a.rot[1][2],b.rot[0][2]* @@ -97,25 +97,25 @@ a.rot[1][0]+b.rot[1][2]*a.rot[1][1]+b.rot[2][2]*a.rot[1][2]],[b.rot[0][0]*a.rot[ a.x*a.x+a.y*a.y,c=Math.sqrt(b+a.z*a.z);if(0===b){if(0===a.z)throw"Zero-length vector not allowed.";var d=0;a=0>a.z?-90:90}else d=e.RAD2DEG*Math.atan2(a.y,a.x),0>d&&(d+=360),a=e.RAD2DEG*Math.atan2(a.z,Math.sqrt(b));return new Ia(a,d,c)}function rc(a){a=360-a;360<=a?a-=360:0>a&&(a+=360);return a}function Fa(a,b){y(b);if(-90>b||90c&&(c=-1);c=1.02/Math.tan((c+10.3/(c+5.11))*e.DEG2RAD)/60;"normal"===a&&-1>b&&(c*=(b+90)/89)}else{if(a)throw"Invalid refraction option: "+ a;c=0}return c}function sc(a,b){if(-90>b||90Math.abs(d))return c-b;c-=d}}function Ra(a,b){return new D(a.rot[0][0]*b.x+a.rot[1][0]*b.y+a.rot[2][0]*b.z,a.rot[0][1]*b.x+a.rot[1][1]*b.y+a.rot[2][1]*b.z,a.rot[0][2]*b.x+a.rot[1][2]*b.y+a.rot[2][2]*b.z,b.t)}function ya(a,b){return new K(a.rot[0][0]*b.x+a.rot[1][0]*b.y+a.rot[2][0]*b.z,a.rot[0][1]*b.x+a.rot[1][1]*b.y+a.rot[2][1]*b.z,a.rot[0][2]*b.x+a.rot[1][2]*b.y+a.rot[2][2]*b.z,a.rot[0][0]*b.vx+ a.rot[1][0]*b.vy+a.rot[2][0]*b.vz,a.rot[0][1]*b.vx+a.rot[1][1]*b.vy+a.rot[2][1]*b.vz,a.rot[0][2]*b.vx+a.rot[1][2]*b.vy+a.rot[2][2]*b.vz,b.t)}function tc(){return new L([[1,0,0],[0,.9174821430670688,-.3977769691083922],[0,.3977769691083922,.9174821430670688]])}function Eb(a){a=w(a);var b=Ca(a,F.From2000);a=Da(a,F.From2000);return xa(b,a)}function Fb(a){a=w(a);var b=Da(a,F.Into2000);a=Ca(a,F.Into2000);return xa(b,a)}function Gb(a,b){a=w(a);var c=Math.sin(b.latitude*e.DEG2RAD),d=Math.cos(b.latitude* -e.DEG2RAD),f=Math.sin(b.longitude*e.DEG2RAD),h=Math.cos(b.longitude*e.DEG2RAD);b=[d*h,d*f,c];c=[-c*h,-c*f,d];f=[f,-h,0];a=-15*ba(a);b=oa(a,b);c=oa(a,c);a=oa(a,f);return new L([[c[0],a[0],b[0]],[c[1],a[1],b[1]],[c[2],a[2],b[2]]])}function uc(a,b){a=Gb(a,b);return wa(a)}function vc(a,b){a=w(a);b=uc(a,b);a=Fb(a);return xa(b,a)}function wc(a){a=Fb(a);var b=tc();return xa(a,b)}function xc(a){a=wc(a);return wa(a)}function yc(a,b){a=w(a);var c=xc(a);a=Gb(a,b);return xa(c,a)}function Sa(a,b,c,d){var f=(d.x* +e.DEG2RAD),f=Math.sin(b.longitude*e.DEG2RAD),h=Math.cos(b.longitude*e.DEG2RAD);b=[d*h,d*f,c];c=[-c*h,-c*f,d];f=[f,-h,0];a=-15*X(a);b=oa(a,b);c=oa(a,c);a=oa(a,f);return new L([[c[0],a[0],b[0]],[c[1],a[1],b[1]],[c[2],a[2],b[2]]])}function uc(a,b){a=Gb(a,b);return wa(a)}function vc(a,b){a=w(a);b=uc(a,b);a=Fb(a);return xa(b,a)}function wc(a){a=Fb(a);var b=tc();return xa(a,b)}function xc(a){a=wc(a);return wa(a)}function yc(a,b){a=w(a);var c=xc(a);a=Gb(a,b);return xa(c,a)}function Sa(a,b,c,d){var f=(d.x* c.x+d.y*c.y+d.z*c.z)/(d.x*d.x+d.y*d.y+d.z*d.z);return new bd(b,f,e.KM_PER_AU*Math.hypot(f*d.x-c.x,f*d.y-c.y,f*d.z-c.z),695700-(1+f)*(695700-a),-695700+(1+f)*(695700+a),c,d)}function ib(a){var b=U(H.Earth,a),c=V(a);return Sa(6459,a,c,b)}function zc(a){var b=U(H.Earth,a),c=V(a),d=new D(-c.x,-c.y,-c.z,c.t);c.x+=b.x;c.y+=b.y;c.z+=b.z;return Sa(1737.4,a,d,c)}function Hb(a,b){var c=Tb(a,b);b=U(H.Earth,a);var d=V(a);c=new D(c[0]-d.x,c[1]-d.y,c[2]-d.z,a);d.x+=b.x;d.y+=b.y;d.z+=b.z;return Sa(1737.4,a,c,d)} function jb(a,b,c){a=ca(a,c,!1);var d=ca(q.Sun,c,!1),f=new D(a.x-d.x,a.y-d.y,a.z-d.z,c);d.x=-a.x;d.y=-a.y;d.z=-a.z;return Sa(b,c,d,f)}function Ib(a,b){var c=1/86400,d=b.AddDays(-c);b=b.AddDays(+c);d=a(d);return(a(b).r-d.r)/c}function cd(a){var b=a.AddDays(-.03);a=a.AddDays(.03);b=I(function(c){return Ib(ib,c)},b,a);if(!b)throw"Failed to find peak Earth shadow time.";return ib(b)}function dd(a){var b=a.AddDays(-.03);a=a.AddDays(.03);b=I(function(c){return Ib(zc,c)},b,a);if(!b)throw"Failed to find peak Moon shadow time."; return zc(b)}function ed(a,b,c){var d=c.AddDays(-1);c=c.AddDays(1);d=I(function(f){var h=1/86400,l=jb(a,b,f.AddDays(-h));return(jb(a,b,f.AddDays(+h)).r-l.r)/h},d,c);if(!d)throw"Failed to find peak planet shadow time.";return jb(a,b,d)}function fd(a,b){function c(h){return Hb(h,b)}var d=a.AddDays(-.2),f=a.AddDays(.2);d=I(function(h){return Ib(c,h)},d,f);if(!d)throw"PeakLocalMoonShadow: search failure for search_center_time = "+a;return Hb(d,b)}function Jb(a,b,c){var d=c/1440;c=a.AddDays(-d);d=a.AddDays(+d); -c=I(function(f){return-(ib(f).r-b)},c,a);a=I(function(f){return+(ib(f).r-b)},a,d);if(!c||!a)throw"Failed to find shadow semiduration";return 720*(a.ut-c.ut)}function Kb(a){a=X(a);return e.RAD2DEG*a.geo_eclip_lat}function Ac(a){a=w(a);for(var b=0;12>b;++b){var c=Oa(180,a,40);if(!c)throw"Cannot find full moon.";a=Kb(c);if(1.8>Math.abs(a)&&(a=cd(c),a.rb;++b){var c=Oa(180,a,40);if(!c)throw"Cannot find full moon.";a=Kb(c);if(1.8>Math.abs(a)&&(a=cd(c),a.rb;++b){var c=Oa(0,a,40);if(!c)throw"Cannot find new moon";a=Kb(c);if(1.8>Math.abs(a)&&(a=dd(c),a.r=d?d+=360:180=d?d+=360:180a.r)throw"Unexpected shadow distance from geoid intersection = "+a.r;h=.014Math.abs(c)){var d=fd(a,b);if(d.rua(a,d)&&(b=ed(a,c,d),b.r=c.lat*f.lat){a.$jscomp$loop$prop$kind$26=f.lat>c.lat?la.Ascending:la.Descending;b=I(function(h){return function(l){return h.$jscomp$loop$prop$kind$26*cb(l).lat}}(a),b,d);if(!b)throw"Could not find moon node.";return new Oc(a.$jscomp$loop$prop$kind$26,b)}b=d;c=f}}function Pc(a,b,c,d,f){if(1>a||5=c)throw"Major mass must be a positive number."; if(!Number.isFinite(f)||0>=f)throw"Minor mass must be a negative number.";var h=d.x-b.x,l=d.y-b.y,k=d.z-b.z,g=h*h+l*l+k*k,m=Math.sqrt(g),n=d.vx-b.vx,p=d.vy-b.vy;d=d.vz-b.vz;if(4===a||5===a){g=l*d-k*p;c=k*n-h*d;var t=h*p-l*n,x=c*k-t*l;t=t*h-g*k;g=g*l-c*h;c=Math.sqrt(x*x+t*t+g*g);x/=c;t/=c;g/=c;h/=m;l/=m;k/=m;a=4==a?.8660254037844386:-.8660254037844386;c=.5*h+a*x;f=.5*l+a*t;var v=.5*k+a*g,z=n*h+p*l+d*k;n=n*x+p*t+d*g;b=new K(m*c,m*f,m*v,z*c+n*(.5*x-a*h),z*f+n*(.5*t-a*l),z*v+n*(.5*g-a*k),b.t)}else{x= -f/(c+f)*-m;t=c/(c+f)*+m;g=(c+f)/(g*m);if(1===a||2===a)v=c/(c+f)*Math.cbrt(f/(3*c)),c=-c,1==a?(v=1-v,a=+f):(v=1+v,a=-f);else if(3===a)v=(7/12*f-c)/(f+c),c=+c,a=+f;else throw"Invalid Langrage point "+a+". Must be an integer 1..5.";f=m*v-x;do v=f-x,z=f-t,v=(g*f+c/(v*v)+a/(z*z))/(g-2*c/(v*v*v)-2*a/(z*z*z)),f-=v;while(1E-14a){c=0;var h=0=c;)c+=360;for(;180Math.abs(n))break;h-=n/(-42.69778487239616*((k-g)/l-g*k*-.006694397995865464/(-42.69778487239616*m))+d*f+a*b)}h*=e.RAD2DEG;l=6378.1366/l;d=Math.abs(f)>Math.abs(b)?d/f-.9933056020041345*l:a/b-l}return new pb(h,c,1E3*d)};e.ObserverGravity=function(a,b){a=Math.sin(a*e.DEG2RAD);a*=a;return 9.7803253359* +g)+.00115*Math.sin(p-2*d)+-9.6E-4*Math.sin(p-d)+4.6E-4*Math.sin(2*g-2*d)+-3.9E-4*Math.sin(p-g)+-3.2E-4*Math.sin(p-n-d)+2.7E-4*Math.sin(2*p-n-2*d)+2.3E-4*Math.sin(e.DEG2RAD*(72.56+20.186*a))+-1.4E-4*Math.sin(2*d)+1.4E-4*Math.cos(2*p-2*g)+-1.2E-4*Math.sin(p-2*g)+-1.2E-4*Math.sin(2*p)+1.1E-4*Math.sin(2*p-2*n-2*d))+(t*Math.cos(f)+v*Math.sin(f))*Math.tan(k)),l,b,h,2*e.RAD2DEG*Math.atan(1737.4/Math.sqrt(h*h-1737.4*1737.4)))};var Ya;e.SiderealTime=function(a){a=w(a);return X(a)};var D=function(a,b,c,d){this.x= +a;this.y=b;this.z=c;this.t=d};D.prototype.Length=function(){return Math.hypot(this.x,this.y,this.z)};e.Vector=D;var K=function(a,b,c,d,f,h,l){this.x=a;this.y=b;this.z=c;this.vx=d;this.vy=f;this.vz=h;this.t=l};e.StateVector=K;var Ia=function(a,b,c){this.lat=y(a);this.lon=y(b);this.dist=y(c)};e.Spherical=Ia;var ab=function(a,b,c,d){this.ra=y(a);this.dec=y(b);this.dist=y(c);this.vec=d};e.EquatorialCoordinates=ab;var L=function(a){this.rot=a};e.RotationMatrix=L;e.MakeRotation=function(a){if(!Yc(a))throw"Argument must be a [3][3] array of numbers"; +return new L(a)};var Vb=function(a,b,c,d){this.azimuth=y(a);this.altitude=y(b);this.ra=y(c);this.dec=y(d)};e.HorizontalCoordinates=Vb;var Yb=function(a,b,c){this.vec=a;this.elat=y(b);this.elon=y(c)};e.EclipticCoordinates=Yb;e.Horizon=Ea;var pb=function(a,b,c){this.latitude=a;this.longitude=b;this.height=c;pa(this)};e.Observer=pb;e.SunPosition=Wb;e.Equator=Ga;e.ObserverVector=function(a,b,c){a=w(a);var d=X(a);b=ob(b,d).pos;c||(b=$a(b,a,F.Into2000));return new D(b[0],b[1],b[2],a)};e.ObserverState=function(a, +b,c){a=w(a);var d=X(a);b=ob(b,d);b=new K(b.pos[0],b.pos[1],b.pos[2],b.vel[0],b.vel[1],b.vel[2],a);return c?b:(c=F.Into2000,c===F.Into2000?(d=Da(a,c),b=ya(d,b),a=Ca(a,c),a=ya(a,b)):(d=Ca(a,c),b=ya(d,b),a=Da(a,c),a=ya(a,b)),a)};e.VectorObserver=function(a,b){var c=X(a.t),d=[a.x,a.y,a.z];b||(d=Ba(d,a.t,F.From2000),d=Za(d,a.t,F.From2000));b=d[0]*e.KM_PER_AU;var f=d[1]*e.KM_PER_AU;d=d[2]*e.KM_PER_AU;a=Math.hypot(b,f);if(1E-6>a){c=0;var h=0=c;)c+=360;for(;180Math.abs(n))break;h-=n/(-42.69778487239616*((k-g)/l-g*k*-.006694397995865464/(-42.69778487239616*m))+d*f+a*b)}h*=e.RAD2DEG;l=6378.1366/l;d=Math.abs(f)>Math.abs(b)?d/f-.9933056020041345*l:a/b-l}return new pb(h,c,1E3*d)};e.ObserverGravity=function(a,b){a=Math.sin(a*e.DEG2RAD);a*=a;return 9.7803253359* (1+.00193185265241*a)/Math.sqrt(1-.00669437999013*a)*(1-(3.15704E-7-2.10269E-9*a)*b+7.37452E-14*b*b)};e.Ecliptic=Ha;e.GeoMoon=V;e.EclipticGeoMoon=cb;e.GeoMoonState=Ja;e.GeoEmbState=rb;var ia=[[-73E4,[-26.118207232108,-14.376168177825,3.384402515299],[.0016339372163656,-.0027861699588508,-.0013585880229445]],[-700800,[41.974905202127,-.448502952929,-12.770351505989],[7.3458569351457E-4,.0022785014891658,4.8619778602049E-4]],[-671600,[14.706930780744,44.269110540027,9.353698474772],[-.00210001479998, 2.2295915939915E-4,7.0143443551414E-4]],[-642400,[-29.441003929957,-6.43016153057,6.858481011305],[8.4495803960544E-4,-.0030783914758711,-.0012106305981192]],[-613200,[39.444396946234,-6.557989760571,-13.913760296463],[.0011480029005873,.0022400006880665,3.5168075922288E-4]],[-584E3,[20.2303809507,43.266966657189,7.382966091923],[-.0019754081700585,5.3457141292226E-4,7.5929169129793E-4]],[-554800,[-30.65832536462,2.093818874552,9.880531138071],[6.1010603013347E-5,-.0031326500935382,-9.9346125151067E-4]], [-525600,[35.737703251673,-12.587706024764,-14.677847247563],[.0015802939375649,.0021347678412429,1.9074436384343E-4]],[-496400,[25.466295188546,41.367478338417,5.216476873382],[-.0018054401046468,8.328308359951E-4,8.0260156912107E-4]],[-467200,[-29.847174904071,10.636426313081,12.297904180106],[-6.3257063052907E-4,-.0029969577578221,-7.4476074151596E-4]],[-438E3,[30.774692107687,-18.236637015304,-14.945535879896],[.0020113162005465,.0019353827024189,-2.0937793168297E-6]],[-408800,[30.243153324028, diff --git a/source/js/astronomy.d.ts b/source/js/astronomy.d.ts index da023d0f..fc3d3f5f 100644 --- a/source/js/astronomy.d.ts +++ b/source/js/astronomy.d.ts @@ -311,6 +311,30 @@ export declare class LibrationInfo { * @returns {LibrationInfo} */ export declare function Libration(date: FlexibleDateTime): LibrationInfo; +/** + * @brief Calculates Greenwich Apparent Sidereal Time (GAST). + * + * Given a date and time, this function calculates the rotation of the + * Earth, represented by the equatorial angle of the Greenwich prime meridian + * with respect to distant stars (not the Sun, which moves relative to background + * stars by almost one degree per day). + * This angle is called Greenwich Apparent Sidereal Time (GAST). + * GAST is measured in sidereal hours in the half-open range [0, 24). + * When GAST = 0, it means the prime meridian is aligned with the of-date equinox, + * corrected at that time for precession and nutation of the Earth's axis. + * In this context, the "equinox" is the direction in space where the Earth's + * orbital plane (the ecliptic) intersects with the plane of the Earth's equator, + * at the location on the Earth's orbit of the (seasonal) March equinox. + * As the Earth rotates, GAST increases from 0 up to 24 sidereal hours, + * then starts over at 0. + * To convert to degrees, multiply the return value by 15. + * + * @param {FlexibleDateTime} date + * The date and time for which to find GAST. + * + * @returns {number} + */ +export declare function SiderealTime(date: FlexibleDateTime): number; /** * @brief A 3D Cartesian vector with a time attached to it. * diff --git a/source/js/astronomy.js b/source/js/astronomy.js index f27ede14..498dd34a 100644 --- a/source/js/astronomy.js +++ b/source/js/astronomy.js @@ -33,9 +33,9 @@ */ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); -exports.GeoVector = exports.HelioDistance = exports.HelioVector = exports.JupiterMoons = exports.JupiterMoonsInfo = exports.GeoEmbState = exports.GeoMoonState = exports.EclipticGeoMoon = exports.GeoMoon = exports.Ecliptic = exports.ObserverGravity = exports.VectorObserver = exports.ObserverState = exports.ObserverVector = exports.Equator = exports.SunPosition = exports.Observer = exports.Horizon = exports.EclipticCoordinates = exports.HorizontalCoordinates = exports.MakeRotation = exports.RotationMatrix = exports.EquatorialCoordinates = exports.Spherical = exports.StateVector = exports.Vector = exports.Libration = exports.LibrationInfo = exports.CalcMoonCount = exports.MakeTime = exports.AstroTime = exports.SetDeltaTFunction = exports.DeltaT_JplHorizons = exports.DeltaT_EspenakMeeus = exports.Body = exports.AngleBetween = exports.MassProduct = exports.CALLISTO_RADIUS_KM = exports.GANYMEDE_RADIUS_KM = exports.EUROPA_RADIUS_KM = exports.IO_RADIUS_KM = exports.JUPITER_MEAN_RADIUS_KM = exports.JUPITER_POLAR_RADIUS_KM = exports.JUPITER_EQUATORIAL_RADIUS_KM = exports.RAD2HOUR = exports.RAD2DEG = exports.HOUR2RAD = exports.DEG2RAD = exports.KM_PER_AU = exports.C_AUDAY = void 0; -exports.Rotation_HOR_EQJ = exports.Rotation_HOR_EQD = exports.Rotation_EQD_HOR = exports.Rotation_EQD_EQJ = exports.Rotation_EQJ_EQD = exports.Rotation_ECL_EQJ = exports.Rotation_EQJ_ECL = exports.RotateState = exports.RotateVector = exports.InverseRefraction = exports.Refraction = exports.VectorFromHorizon = exports.HorizonFromVector = exports.SphereFromVector = exports.EquatorFromVector = exports.VectorFromSphere = exports.Pivot = exports.IdentityMatrix = exports.CombineRotation = exports.InverseRotation = exports.NextPlanetApsis = exports.SearchPlanetApsis = exports.NextLunarApsis = exports.SearchLunarApsis = exports.Apsis = exports.SearchPeakMagnitude = exports.SearchMaxElongation = exports.Elongation = exports.ElongationEvent = exports.Seasons = exports.SeasonInfo = exports.SearchHourAngle = exports.HourAngleEvent = exports.SearchAltitude = exports.SearchRiseSet = exports.NextMoonQuarter = exports.SearchMoonQuarter = exports.MoonQuarter = exports.SearchMoonPhase = exports.MoonPhase = exports.SearchRelativeLongitude = exports.Illumination = exports.IlluminationInfo = exports.EclipticLongitude = exports.AngleFromSun = exports.PairLongitude = exports.SearchSunLongitude = exports.Search = exports.HelioState = exports.BaryState = void 0; -exports.LagrangePointFast = exports.LagrangePoint = exports.RotationAxis = exports.AxisInfo = exports.NextMoonNode = exports.SearchMoonNode = exports.NodeEventInfo = exports.NodeEventKind = exports.NextTransit = exports.SearchTransit = exports.TransitInfo = exports.NextLocalSolarEclipse = exports.SearchLocalSolarEclipse = exports.LocalSolarEclipseInfo = exports.EclipseEvent = exports.NextGlobalSolarEclipse = exports.SearchGlobalSolarEclipse = exports.NextLunarEclipse = exports.GlobalSolarEclipseInfo = exports.SearchLunarEclipse = exports.LunarEclipseInfo = exports.Constellation = exports.ConstellationInfo = exports.Rotation_GAL_EQJ = exports.Rotation_EQJ_GAL = exports.Rotation_HOR_ECL = exports.Rotation_ECL_HOR = exports.Rotation_ECL_EQD = exports.Rotation_EQD_ECL = exports.Rotation_EQJ_HOR = void 0; +exports.HelioDistance = exports.HelioVector = exports.JupiterMoons = exports.JupiterMoonsInfo = exports.GeoEmbState = exports.GeoMoonState = exports.EclipticGeoMoon = exports.GeoMoon = exports.Ecliptic = exports.ObserverGravity = exports.VectorObserver = exports.ObserverState = exports.ObserverVector = exports.Equator = exports.SunPosition = exports.Observer = exports.Horizon = exports.EclipticCoordinates = exports.HorizontalCoordinates = exports.MakeRotation = exports.RotationMatrix = exports.EquatorialCoordinates = exports.Spherical = exports.StateVector = exports.Vector = exports.SiderealTime = exports.Libration = exports.LibrationInfo = exports.CalcMoonCount = exports.MakeTime = exports.AstroTime = exports.SetDeltaTFunction = exports.DeltaT_JplHorizons = exports.DeltaT_EspenakMeeus = exports.Body = exports.AngleBetween = exports.MassProduct = exports.CALLISTO_RADIUS_KM = exports.GANYMEDE_RADIUS_KM = exports.EUROPA_RADIUS_KM = exports.IO_RADIUS_KM = exports.JUPITER_MEAN_RADIUS_KM = exports.JUPITER_POLAR_RADIUS_KM = exports.JUPITER_EQUATORIAL_RADIUS_KM = exports.RAD2HOUR = exports.RAD2DEG = exports.HOUR2RAD = exports.DEG2RAD = exports.KM_PER_AU = exports.C_AUDAY = void 0; +exports.Rotation_HOR_EQD = exports.Rotation_EQD_HOR = exports.Rotation_EQD_EQJ = exports.Rotation_EQJ_EQD = exports.Rotation_ECL_EQJ = exports.Rotation_EQJ_ECL = exports.RotateState = exports.RotateVector = exports.InverseRefraction = exports.Refraction = exports.VectorFromHorizon = exports.HorizonFromVector = exports.SphereFromVector = exports.EquatorFromVector = exports.VectorFromSphere = exports.Pivot = exports.IdentityMatrix = exports.CombineRotation = exports.InverseRotation = exports.NextPlanetApsis = exports.SearchPlanetApsis = exports.NextLunarApsis = exports.SearchLunarApsis = exports.Apsis = exports.SearchPeakMagnitude = exports.SearchMaxElongation = exports.Elongation = exports.ElongationEvent = exports.Seasons = exports.SeasonInfo = exports.SearchHourAngle = exports.HourAngleEvent = exports.SearchAltitude = exports.SearchRiseSet = exports.NextMoonQuarter = exports.SearchMoonQuarter = exports.MoonQuarter = exports.SearchMoonPhase = exports.MoonPhase = exports.SearchRelativeLongitude = exports.Illumination = exports.IlluminationInfo = exports.EclipticLongitude = exports.AngleFromSun = exports.PairLongitude = exports.SearchSunLongitude = exports.Search = exports.HelioState = exports.BaryState = exports.GeoVector = void 0; +exports.LagrangePointFast = exports.LagrangePoint = exports.RotationAxis = exports.AxisInfo = exports.NextMoonNode = exports.SearchMoonNode = exports.NodeEventInfo = exports.NodeEventKind = exports.NextTransit = exports.SearchTransit = exports.TransitInfo = exports.NextLocalSolarEclipse = exports.SearchLocalSolarEclipse = exports.LocalSolarEclipseInfo = exports.EclipseEvent = exports.NextGlobalSolarEclipse = exports.SearchGlobalSolarEclipse = exports.NextLunarEclipse = exports.GlobalSolarEclipseInfo = exports.SearchLunarEclipse = exports.LunarEclipseInfo = exports.Constellation = exports.ConstellationInfo = exports.Rotation_GAL_EQJ = exports.Rotation_EQJ_GAL = exports.Rotation_HOR_ECL = exports.Rotation_ECL_HOR = exports.Rotation_ECL_EQD = exports.Rotation_EQD_ECL = exports.Rotation_EQJ_HOR = exports.Rotation_HOR_EQJ = void 0; /** * @brief The speed of light in AU/day. */ @@ -1848,6 +1848,34 @@ function sidereal_time(time) { } return sidereal_time_cache.st; // return sidereal hours in the half-open range [0, 24). } +/** + * @brief Calculates Greenwich Apparent Sidereal Time (GAST). + * + * Given a date and time, this function calculates the rotation of the + * Earth, represented by the equatorial angle of the Greenwich prime meridian + * with respect to distant stars (not the Sun, which moves relative to background + * stars by almost one degree per day). + * This angle is called Greenwich Apparent Sidereal Time (GAST). + * GAST is measured in sidereal hours in the half-open range [0, 24). + * When GAST = 0, it means the prime meridian is aligned with the of-date equinox, + * corrected at that time for precession and nutation of the Earth's axis. + * In this context, the "equinox" is the direction in space where the Earth's + * orbital plane (the ecliptic) intersects with the plane of the Earth's equator, + * at the location on the Earth's orbit of the (seasonal) March equinox. + * As the Earth rotates, GAST increases from 0 up to 24 sidereal hours, + * then starts over at 0. + * To convert to degrees, multiply the return value by 15. + * + * @param {FlexibleDateTime} date + * The date and time for which to find GAST. + * + * @returns {number} + */ +function SiderealTime(date) { + const time = MakeTime(date); + return sidereal_time(time); +} +exports.SiderealTime = SiderealTime; function inverse_terra(ovec, st) { // Convert from AU to kilometers const x = ovec[0] * exports.KM_PER_AU; diff --git a/source/js/astronomy.min.js b/source/js/astronomy.min.js index db7b7762..0c0a2cd1 100644 --- a/source/js/astronomy.min.js +++ b/source/js/astronomy.min.js @@ -37,15 +37,15 @@ $jscomp.POLYFILL_PREFIX+e),$jscomp.defineProperty(d,$jscomp.propertyToPolyfillSy $jscomp.polyfill("Math.hypot",function(a){return a?a:function(b){if(2>arguments.length)return arguments.length?Math.abs(arguments[0]):0;var c,d,e;for(c=e=0;ce){if(!e)return e;for(c=d=0;cb?-c:c}},"es6","es3");Object.defineProperty(exports,"__esModule",{value:!0}); -exports.GeoVector=exports.HelioDistance=exports.HelioVector=exports.JupiterMoons=exports.JupiterMoonsInfo=exports.GeoEmbState=exports.GeoMoonState=exports.EclipticGeoMoon=exports.GeoMoon=exports.Ecliptic=exports.ObserverGravity=exports.VectorObserver=exports.ObserverState=exports.ObserverVector=exports.Equator=exports.SunPosition=exports.Observer=exports.Horizon=exports.EclipticCoordinates=exports.HorizontalCoordinates=exports.MakeRotation=exports.RotationMatrix=exports.EquatorialCoordinates=exports.Spherical= -exports.StateVector=exports.Vector=exports.Libration=exports.LibrationInfo=exports.CalcMoonCount=exports.MakeTime=exports.AstroTime=exports.SetDeltaTFunction=exports.DeltaT_JplHorizons=exports.DeltaT_EspenakMeeus=exports.Body=exports.AngleBetween=exports.MassProduct=exports.CALLISTO_RADIUS_KM=exports.GANYMEDE_RADIUS_KM=exports.EUROPA_RADIUS_KM=exports.IO_RADIUS_KM=exports.JUPITER_MEAN_RADIUS_KM=exports.JUPITER_POLAR_RADIUS_KM=exports.JUPITER_EQUATORIAL_RADIUS_KM=exports.RAD2HOUR=exports.RAD2DEG=exports.HOUR2RAD= -exports.DEG2RAD=exports.KM_PER_AU=exports.C_AUDAY=void 0; -exports.Rotation_HOR_EQJ=exports.Rotation_HOR_EQD=exports.Rotation_EQD_HOR=exports.Rotation_EQD_EQJ=exports.Rotation_EQJ_EQD=exports.Rotation_ECL_EQJ=exports.Rotation_EQJ_ECL=exports.RotateState=exports.RotateVector=exports.InverseRefraction=exports.Refraction=exports.VectorFromHorizon=exports.HorizonFromVector=exports.SphereFromVector=exports.EquatorFromVector=exports.VectorFromSphere=exports.Pivot=exports.IdentityMatrix=exports.CombineRotation=exports.InverseRotation=exports.NextPlanetApsis=exports.SearchPlanetApsis= -exports.NextLunarApsis=exports.SearchLunarApsis=exports.Apsis=exports.SearchPeakMagnitude=exports.SearchMaxElongation=exports.Elongation=exports.ElongationEvent=exports.Seasons=exports.SeasonInfo=exports.SearchHourAngle=exports.HourAngleEvent=exports.SearchAltitude=exports.SearchRiseSet=exports.NextMoonQuarter=exports.SearchMoonQuarter=exports.MoonQuarter=exports.SearchMoonPhase=exports.MoonPhase=exports.SearchRelativeLongitude=exports.Illumination=exports.IlluminationInfo=exports.EclipticLongitude= -exports.AngleFromSun=exports.PairLongitude=exports.SearchSunLongitude=exports.Search=exports.HelioState=exports.BaryState=void 0; +exports.HelioDistance=exports.HelioVector=exports.JupiterMoons=exports.JupiterMoonsInfo=exports.GeoEmbState=exports.GeoMoonState=exports.EclipticGeoMoon=exports.GeoMoon=exports.Ecliptic=exports.ObserverGravity=exports.VectorObserver=exports.ObserverState=exports.ObserverVector=exports.Equator=exports.SunPosition=exports.Observer=exports.Horizon=exports.EclipticCoordinates=exports.HorizontalCoordinates=exports.MakeRotation=exports.RotationMatrix=exports.EquatorialCoordinates=exports.Spherical=exports.StateVector= +exports.Vector=exports.SiderealTime=exports.Libration=exports.LibrationInfo=exports.CalcMoonCount=exports.MakeTime=exports.AstroTime=exports.SetDeltaTFunction=exports.DeltaT_JplHorizons=exports.DeltaT_EspenakMeeus=exports.Body=exports.AngleBetween=exports.MassProduct=exports.CALLISTO_RADIUS_KM=exports.GANYMEDE_RADIUS_KM=exports.EUROPA_RADIUS_KM=exports.IO_RADIUS_KM=exports.JUPITER_MEAN_RADIUS_KM=exports.JUPITER_POLAR_RADIUS_KM=exports.JUPITER_EQUATORIAL_RADIUS_KM=exports.RAD2HOUR=exports.RAD2DEG= +exports.HOUR2RAD=exports.DEG2RAD=exports.KM_PER_AU=exports.C_AUDAY=void 0; +exports.Rotation_HOR_EQD=exports.Rotation_EQD_HOR=exports.Rotation_EQD_EQJ=exports.Rotation_EQJ_EQD=exports.Rotation_ECL_EQJ=exports.Rotation_EQJ_ECL=exports.RotateState=exports.RotateVector=exports.InverseRefraction=exports.Refraction=exports.VectorFromHorizon=exports.HorizonFromVector=exports.SphereFromVector=exports.EquatorFromVector=exports.VectorFromSphere=exports.Pivot=exports.IdentityMatrix=exports.CombineRotation=exports.InverseRotation=exports.NextPlanetApsis=exports.SearchPlanetApsis=exports.NextLunarApsis= +exports.SearchLunarApsis=exports.Apsis=exports.SearchPeakMagnitude=exports.SearchMaxElongation=exports.Elongation=exports.ElongationEvent=exports.Seasons=exports.SeasonInfo=exports.SearchHourAngle=exports.HourAngleEvent=exports.SearchAltitude=exports.SearchRiseSet=exports.NextMoonQuarter=exports.SearchMoonQuarter=exports.MoonQuarter=exports.SearchMoonPhase=exports.MoonPhase=exports.SearchRelativeLongitude=exports.Illumination=exports.IlluminationInfo=exports.EclipticLongitude=exports.AngleFromSun= +exports.PairLongitude=exports.SearchSunLongitude=exports.Search=exports.HelioState=exports.BaryState=exports.GeoVector=void 0; exports.LagrangePointFast=exports.LagrangePoint=exports.RotationAxis=exports.AxisInfo=exports.NextMoonNode=exports.SearchMoonNode=exports.NodeEventInfo=exports.NodeEventKind=exports.NextTransit=exports.SearchTransit=exports.TransitInfo=exports.NextLocalSolarEclipse=exports.SearchLocalSolarEclipse=exports.LocalSolarEclipseInfo=exports.EclipseEvent=exports.NextGlobalSolarEclipse=exports.SearchGlobalSolarEclipse=exports.NextLunarEclipse=exports.GlobalSolarEclipseInfo=exports.SearchLunarEclipse=exports.LunarEclipseInfo= -exports.Constellation=exports.ConstellationInfo=exports.Rotation_GAL_EQJ=exports.Rotation_EQJ_GAL=exports.Rotation_HOR_ECL=exports.Rotation_ECL_HOR=exports.Rotation_ECL_EQD=exports.Rotation_EQD_ECL=exports.Rotation_EQJ_HOR=void 0;exports.C_AUDAY=173.1446326846693;exports.KM_PER_AU=1.4959787069098932E8;exports.DEG2RAD=.017453292519943295;exports.HOUR2RAD=.26179938779914946;exports.RAD2DEG=57.29577951308232;exports.RAD2HOUR=3.819718634205488;exports.JUPITER_EQUATORIAL_RADIUS_KM=71492; -exports.JUPITER_POLAR_RADIUS_KM=66854;exports.JUPITER_MEAN_RADIUS_KM=69911;exports.IO_RADIUS_KM=1821.6;exports.EUROPA_RADIUS_KM=1560.8;exports.GANYMEDE_RADIUS_KM=2631.2;exports.CALLISTO_RADIUS_KM=2410.3; +exports.Constellation=exports.ConstellationInfo=exports.Rotation_GAL_EQJ=exports.Rotation_EQJ_GAL=exports.Rotation_HOR_ECL=exports.Rotation_ECL_HOR=exports.Rotation_ECL_EQD=exports.Rotation_EQD_ECL=exports.Rotation_EQJ_HOR=exports.Rotation_HOR_EQJ=void 0;exports.C_AUDAY=173.1446326846693;exports.KM_PER_AU=1.4959787069098932E8;exports.DEG2RAD=.017453292519943295;exports.HOUR2RAD=.26179938779914946;exports.RAD2DEG=57.29577951308232;exports.RAD2HOUR=3.819718634205488; +exports.JUPITER_EQUATORIAL_RADIUS_KM=71492;exports.JUPITER_POLAR_RADIUS_KM=66854;exports.JUPITER_MEAN_RADIUS_KM=69911;exports.IO_RADIUS_KM=1821.6;exports.EUROPA_RADIUS_KM=1560.8;exports.GANYMEDE_RADIUS_KM=2631.2;exports.CALLISTO_RADIUS_KM=2410.3; var DAYS_PER_TROPICAL_YEAR=365.24217,J2000=new Date("2000-01-01T12:00:00Z"),PI2=2*Math.PI,ARC=180/Math.PI*3600,ASEC2RAD=4.84813681109536E-6,ASEC180=648E3,ASEC360=2*ASEC180,ANGVEL=7.292115E-5,AU_PER_PARSEC=ASEC180/Math.PI,SUN_MAG_1AU=-.17-5*Math.log10(AU_PER_PARSEC),MEAN_SYNODIC_MONTH=29.530588,SECONDS_PER_DAY=86400,MILLIS_PER_DAY=1E3*SECONDS_PER_DAY,SOLAR_DAYS_PER_SIDEREAL_DAY=.9972695717592592,SUN_RADIUS_KM=695700,SUN_RADIUS_AU=SUN_RADIUS_KM/exports.KM_PER_AU,EARTH_FLATTENING=.996647180302104,EARTH_FLATTENING_SQUARED= EARTH_FLATTENING*EARTH_FLATTENING,EARTH_EQUATORIAL_RADIUS_KM=6378.1366,EARTH_EQUATORIAL_RADIUS_AU=EARTH_EQUATORIAL_RADIUS_KM/exports.KM_PER_AU,EARTH_POLAR_RADIUS_KM=EARTH_EQUATORIAL_RADIUS_KM*EARTH_FLATTENING,EARTH_MEAN_RADIUS_KM=6371,EARTH_ATMOSPHERE_KM=88,EARTH_ECLIPSE_RADIUS_KM=EARTH_MEAN_RADIUS_KM+EARTH_ATMOSPHERE_KM,MOON_EQUATORIAL_RADIUS_KM=1738.1,MOON_MEAN_RADIUS_KM=1737.4,MOON_POLAR_RADIUS_KM=1736,MOON_EQUATORIAL_RADIUS_AU=MOON_EQUATORIAL_RADIUS_KM/exports.KM_PER_AU,REFRACTION_NEAR_HORIZON= 34/60,EARTH_MOON_MASS_RATIO=81.30056,SUN_GM=2.959122082855911E-4,MERCURY_GM=4.912547451450812E-11,VENUS_GM=7.243452486162703E-10,EARTH_GM=8.887692390113509E-10,MARS_GM=9.549535105779258E-11,JUPITER_GM=2.825345909524226E-7,SATURN_GM=8.459715185680659E-8,URANUS_GM=1.292024916781969E-8,NEPTUNE_GM=1.524358900784276E-8,PLUTO_GM=2.18869976542597E-12,MOON_GM=EARTH_GM/EARTH_MOON_MASS_RATIO; @@ -128,7 +128,7 @@ h-2*d)+-5.4E-4*Math.cos(n-2*d)+-2E-4*Math.sin(n+h)+-2E-4*Math.cos(n+2*h)+-2E-4*M Math.tan(k)),g,b,f,2*exports.RAD2DEG*Math.atan(MOON_MEAN_RADIUS_KM/Math.sqrt(f*f-MOON_MEAN_RADIUS_KM*MOON_MEAN_RADIUS_KM)))}exports.Libration=Libration;function rotate(a,b){return[a.rot[0][0]*b[0]+a.rot[1][0]*b[1]+a.rot[2][0]*b[2],a.rot[0][1]*b[0]+a.rot[1][1]*b[1]+a.rot[2][1]*b[2],a.rot[0][2]*b[0]+a.rot[1][2]*b[1]+a.rot[2][2]*b[2]]}function precession(a,b,c){b=precession_rot(b,c);return rotate(b,a)}function precession_posvel(a,b,c){b=precession_rot(b,c);return RotateState(b,a)} function precession_rot(a,b){a=a.tt/36525;var c=84381.406,d=((((3.337E-7*a-4.67E-7)*a-.00772503)*a+.0512623)*a-.025754)*a+c;c*=ASEC2RAD;var e=((((-9.51E-8*a+1.32851E-4)*a-.00114045)*a-1.0790069)*a+5038.481507)*a*ASEC2RAD;d*=ASEC2RAD;var f=((((-5.6E-8*a+1.70663E-4)*a-.00121197)*a-2.3814292)*a+10.556403)*a*ASEC2RAD;a=Math.sin(c);c=Math.cos(c);var g=Math.sin(-e);e=Math.cos(-e);var k=Math.sin(-d);d=Math.cos(-d);var h=Math.sin(f),l=Math.cos(f);f=l*e-g*h*d;var m=l*g*c+h*d*e*c-a*h*k,n=l*g*a+h*d*e*a+c*h* k,p=-h*e-g*l*d,r=-h*g*c+l*d*e*c-a*l*k;h=-h*g*a+l*d*e*a+c*l*k;g*=k;l=-k*e*c-a*d;a=-k*e*a+d*c;if(b===PrecessDirection.Into2000)return new RotationMatrix([[f,m,n],[p,r,h],[g,l,a]]);if(b===PrecessDirection.From2000)return new RotationMatrix([[f,p,g],[m,r,l],[n,h,a]]);throw"Invalid precess direction";}function era(a){a=(.779057273264+.00273781191135448*a.ut+a.ut%1)%1*360;0>a&&(a+=360);return a}var sidereal_time_cache; -function sidereal_time(a){if(!sidereal_time_cache||sidereal_time_cache.tt!==a.tt){var b=a.tt/36525,c=15*e_tilt(a).ee,d=era(a);b=((c+.014506+((((-3.68E-8*b-2.9956E-5)*b-4.4E-7)*b+1.3915817)*b+4612.156534)*b)/3600+d)%360/15;0>b&&(b+=24);sidereal_time_cache={tt:a.tt,st:b}}return sidereal_time_cache.st} +function sidereal_time(a){if(!sidereal_time_cache||sidereal_time_cache.tt!==a.tt){var b=a.tt/36525,c=15*e_tilt(a).ee,d=era(a);b=((c+.014506+((((-3.68E-8*b-2.9956E-5)*b-4.4E-7)*b+1.3915817)*b+4612.156534)*b)/3600+d)%360/15;0>b&&(b+=24);sidereal_time_cache={tt:a.tt,st:b}}return sidereal_time_cache.st}function SiderealTime(a){a=MakeTime(a);return sidereal_time(a)}exports.SiderealTime=SiderealTime; function inverse_terra(a,b){var c=a[0]*exports.KM_PER_AU,d=a[1]*exports.KM_PER_AU;a=a[2]*exports.KM_PER_AU;var e=Math.hypot(c,d);if(1E-6>e){b=0;var f=0=b;)b+=360;for(;180Math.abs(n))break; f-=n/(k*((h-l)/g-l*h*(EARTH_FLATTENING_SQUARED-1)/(k*m))+a*d+e*c)}f*=exports.RAD2DEG;g=EARTH_EQUATORIAL_RADIUS_KM/g;a=Math.abs(d)>Math.abs(c)?a/d-EARTH_FLATTENING_SQUARED*g:e/c-g}return new Observer(f,b,1E3*a)} function terra(a,b){var c=a.latitude*exports.DEG2RAD,d=Math.sin(c);c=Math.cos(c);var e=1/Math.hypot(c,EARTH_FLATTENING*d),f=a.height/1E3,g=EARTH_EQUATORIAL_RADIUS_KM*e+f;b=(15*b+a.longitude)*exports.DEG2RAD;a=Math.sin(b);b=Math.cos(b);return{pos:[g*c*b/exports.KM_PER_AU,g*c*a/exports.KM_PER_AU,(EARTH_EQUATORIAL_RADIUS_KM*EARTH_FLATTENING_SQUARED*e+f)*d/exports.KM_PER_AU],vel:[-ANGVEL*g*c*a*86400/exports.KM_PER_AU,ANGVEL*g*c*b*86400/exports.KM_PER_AU,0]}} diff --git a/source/js/astronomy.ts b/source/js/astronomy.ts index 298c818c..43990850 100644 --- a/source/js/astronomy.ts +++ b/source/js/astronomy.ts @@ -1997,6 +1997,34 @@ function sidereal_time(time: AstroTime): number { // calculates Greenwi return sidereal_time_cache.st; // return sidereal hours in the half-open range [0, 24). } +/** + * @brief Calculates Greenwich Apparent Sidereal Time (GAST). + * + * Given a date and time, this function calculates the rotation of the + * Earth, represented by the equatorial angle of the Greenwich prime meridian + * with respect to distant stars (not the Sun, which moves relative to background + * stars by almost one degree per day). + * This angle is called Greenwich Apparent Sidereal Time (GAST). + * GAST is measured in sidereal hours in the half-open range [0, 24). + * When GAST = 0, it means the prime meridian is aligned with the of-date equinox, + * corrected at that time for precession and nutation of the Earth's axis. + * In this context, the "equinox" is the direction in space where the Earth's + * orbital plane (the ecliptic) intersects with the plane of the Earth's equator, + * at the location on the Earth's orbit of the (seasonal) March equinox. + * As the Earth rotates, GAST increases from 0 up to 24 sidereal hours, + * then starts over at 0. + * To convert to degrees, multiply the return value by 15. + * + * @param {FlexibleDateTime} date + * The date and time for which to find GAST. + * + * @returns {number} + */ +export function SiderealTime(date: FlexibleDateTime): number { + const time = MakeTime(date); + return sidereal_time(time); +} + function inverse_terra(ovec: ArrayVector, st: number): Observer { // Convert from AU to kilometers const x = ovec[0] * KM_PER_AU; diff --git a/source/js/esm/astronomy.js b/source/js/esm/astronomy.js index bc92c296..b3ddaba4 100644 --- a/source/js/esm/astronomy.js +++ b/source/js/esm/astronomy.js @@ -1835,6 +1835,33 @@ function sidereal_time(time) { } return sidereal_time_cache.st; // return sidereal hours in the half-open range [0, 24). } +/** + * @brief Calculates Greenwich Apparent Sidereal Time (GAST). + * + * Given a date and time, this function calculates the rotation of the + * Earth, represented by the equatorial angle of the Greenwich prime meridian + * with respect to distant stars (not the Sun, which moves relative to background + * stars by almost one degree per day). + * This angle is called Greenwich Apparent Sidereal Time (GAST). + * GAST is measured in sidereal hours in the half-open range [0, 24). + * When GAST = 0, it means the prime meridian is aligned with the of-date equinox, + * corrected at that time for precession and nutation of the Earth's axis. + * In this context, the "equinox" is the direction in space where the Earth's + * orbital plane (the ecliptic) intersects with the plane of the Earth's equator, + * at the location on the Earth's orbit of the (seasonal) March equinox. + * As the Earth rotates, GAST increases from 0 up to 24 sidereal hours, + * then starts over at 0. + * To convert to degrees, multiply the return value by 15. + * + * @param {FlexibleDateTime} date + * The date and time for which to find GAST. + * + * @returns {number} + */ +export function SiderealTime(date) { + const time = MakeTime(date); + return sidereal_time(time); +} function inverse_terra(ovec, st) { // Convert from AU to kilometers const x = ovec[0] * KM_PER_AU; diff --git a/source/python/README.md b/source/python/README.md index ef5f6332..0c9bd683 100644 --- a/source/python/README.md +++ b/source/python/README.md @@ -2950,6 +2950,35 @@ of winter in the southern hemisphere. --- + +### SiderealTime(time) + +**Calculates Greenwich Apparent Sidereal Time (GAST).** + +Given a date and time, this function calculates the rotation of the +Earth, represented by the equatorial angle of the Greenwich prime meridian +with respect to distant stars (not the Sun, which moves relative to background +stars by almost one degree per day). +This angle is called Greenwich Apparent Sidereal Time (GAST). +GAST is measured in sidereal hours in the half-open range [0, 24). +When GAST = 0, it means the prime meridian is aligned with the of-date equinox, +corrected at that time for precession and nutation of the Earth's axis. +In this context, the "equinox" is the direction in space where the Earth's +orbital plane (the ecliptic) intersects with the plane of the Earth's equator, +at the location on the Earth's orbit of the (seasonal) March equinox. +As the Earth rotates, GAST increases from 0 up to 24 sidereal hours, +then starts over at 0. +To convert to degrees, multiply the return value by 15. + +| Type | Parameter | Description | +| --- | --- | --- | +| [`Time`](#Time) | `time` | The date and time for which to find GAST. As an optimization, this function caches the sideral time value in `time`, unless it has already been cached, in which case the cached value is reused. | + +### Returns: `float` +GAST expressed in sidereal hours. + +--- + ### SphereFromVector(vector) diff --git a/source/python/astronomy.py b/source/python/astronomy.py index 7d68627b..4aeef96b 100644 --- a/source/python/astronomy.py +++ b/source/python/astronomy.py @@ -1618,7 +1618,36 @@ def _era(time): # Earth Rotation Angle theta += 360.0 return theta -def _sidereal_time(time): +def SiderealTime(time): + """Calculates Greenwich Apparent Sidereal Time (GAST). + + Given a date and time, this function calculates the rotation of the + Earth, represented by the equatorial angle of the Greenwich prime meridian + with respect to distant stars (not the Sun, which moves relative to background + stars by almost one degree per day). + This angle is called Greenwich Apparent Sidereal Time (GAST). + GAST is measured in sidereal hours in the half-open range [0, 24). + When GAST = 0, it means the prime meridian is aligned with the of-date equinox, + corrected at that time for precession and nutation of the Earth's axis. + In this context, the "equinox" is the direction in space where the Earth's + orbital plane (the ecliptic) intersects with the plane of the Earth's equator, + at the location on the Earth's orbit of the (seasonal) March equinox. + As the Earth rotates, GAST increases from 0 up to 24 sidereal hours, + then starts over at 0. + To convert to degrees, multiply the return value by 15. + + Parameters + ---------- + time : Time + The date and time for which to find GAST. + As an optimization, this function caches the sideral time value in `time`, + unless it has already been cached, in which case the cached value is reused. + + Returns + ------- + float + GAST expressed in sidereal hours. + """ if time._st is None: t = time.tt / 36525.0 eqeq = 15.0 * time._etilt().ee # Replace with eqeq=0 to get GMST instead of GAST (if we ever need it) @@ -1718,7 +1747,7 @@ def _terra(observer, st): return _terra_posvel(observer, st)[0:3] def _geo_pos(time, observer): - gast = _sidereal_time(time) + gast = SiderealTime(time) pos1 = _terra(observer, gast) pos2 = _nutation(pos1, time, _PrecessDir.Into2000) outpos = _precession(pos2, time, _PrecessDir.Into2000) @@ -4646,7 +4675,7 @@ def ObserverVector(time, observer, ofdate): An equatorial vector from the center of the Earth to the specified location on (or near) the Earth's surface. """ - gast = _sidereal_time(time) + gast = SiderealTime(time) ovec = _terra(observer, gast) if not ofdate: ovec = _nutation(ovec, time, _PrecessDir.Into2000) @@ -4688,7 +4717,7 @@ def ObserverState(time, observer, ofdate): StateVector An equatorial position vector and velocity vector relative to the center of the Earth. """ - gast = _sidereal_time(time) + gast = SiderealTime(time) ovec = _terra_posvel(observer, gast) state = StateVector( ovec[0], ovec[1], ovec[2], @@ -4726,7 +4755,7 @@ def VectorObserver(vector, ofdate): The geographic latitude, longitude, and elevation above sea level that corresponds to the given equatorial vector. """ - gast = _sidereal_time(vector.t) + gast = SiderealTime(vector.t) ovec = [vector.x, vector.y, vector.z] if not ofdate: ovec = _precession(ovec, vector.t, _PrecessDir.From2000) @@ -4905,7 +4934,7 @@ def Horizon(time, observer, ra, dec, refraction): # Multiply sidereal hours by -15 to convert to degrees and flip eastward # rotation of the Earth to westward apparent movement of objects with time. - angle = -15.0 * _sidereal_time(time) + angle = -15.0 * SiderealTime(time) uz = _spin(angle, uze) un = _spin(angle, une) uw = _spin(angle, uwe) @@ -6090,7 +6119,7 @@ def SearchHourAngle(body, observer, hourAngle, startTime): while True: iter_count += 1 # Calculate Greenwich Apparent Sidereal Time (GAST) at the given time. - gast = _sidereal_time(time) + gast = SiderealTime(time) ofdate = Equator(body, time, observer, True, True) # Calculate the adjustment needed in sidereal time to bring @@ -7225,7 +7254,7 @@ def Rotation_EQD_HOR(time, observer): uze = [coslat * coslon, coslat * sinlon, sinlat] une = [-sinlat * coslon, -sinlat * sinlon, coslat] uwe = [sinlon, -coslon, 0.0] - spin_angle = -15.0 * _sidereal_time(time) + spin_angle = -15.0 * SiderealTime(time) uz = _spin(spin_angle, uze) un = _spin(spin_angle, une) uw = _spin(spin_angle, uwe) @@ -8454,7 +8483,7 @@ def _GeoidIntersect(shadow): latitude = math.degrees(math.atan(pz / proj)) # Adjust longitude for Earth's rotation at the given UT. - gast = _sidereal_time(peak) + gast = SiderealTime(peak) longitude = math.fmod(math.degrees(math.atan2(py, px)) - (15*gast), 360.0) if longitude <= -180.0: longitude += 360.0