mirror of
https://github.com/cosinekitty/astronomy.git
synced 2025-12-30 02:58:42 -05:00
3928 lines
136 KiB
Python
3928 lines
136 KiB
Python
#!/usr/bin/env python3
|
|
#
|
|
# MIT License
|
|
#
|
|
# Copyright (c) 2019 Don Cross <cosinekitty@gmail.com>
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
# of this software and associated documentation files (the "Software"), to deal
|
|
# in the Software without restriction, including without limitation the rights
|
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
# copies of the Software, and to permit persons to whom the Software is
|
|
# furnished to do so, subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be included in all
|
|
# copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
# SOFTWARE.
|
|
#
|
|
"""Astronomy Engine by Don Cross
|
|
|
|
See the GitHub project page for full documentation, examples,
|
|
and other information:
|
|
|
|
https://github.com/cosinekitty/astronomy
|
|
|
|
"""
|
|
|
|
import math
|
|
import datetime
|
|
import enum
|
|
|
|
_PI2 = 2.0 * math.pi
|
|
_EPOCH = datetime.datetime(2000, 1, 1, 12)
|
|
_T0 = 2451545.0
|
|
_MJD_BASIS = 2400000.5
|
|
_Y2000_IN_MJD = _T0 - _MJD_BASIS
|
|
_ASEC360 = 1296000.0
|
|
_ASEC2RAD = 4.848136811095359935899141e-6
|
|
_ARC = 3600.0 * 180.0 / math.pi # arcseconds per radian
|
|
_C_AUDAY = 173.1446326846693 # speed of light in AU/day
|
|
_ERAD = 6378136.6 # mean earth radius in meters
|
|
_AU = 1.4959787069098932e+11 # astronomical unit in meters
|
|
_KM_PER_AU = 1.4959787069098932e+8
|
|
_ANGVEL = 7.2921150e-5
|
|
_SECONDS_PER_DAY = 24.0 * 3600.0
|
|
_SOLAR_DAYS_PER_SIDEREAL_DAY = 0.9972695717592592
|
|
_MEAN_SYNODIC_MONTH = 29.530588
|
|
_EARTH_ORBITAL_PERIOD = 365.256
|
|
_REFRACTION_NEAR_HORIZON = 34.0 / 60.0
|
|
_SUN_RADIUS_AU = 4.6505e-3
|
|
_MOON_RADIUS_AU = 1.15717e-5
|
|
_ASEC180 = 180.0 * 60.0 * 60.0
|
|
_AU_PER_PARSEC = _ASEC180 / math.pi
|
|
|
|
def _LongitudeOffset(diff):
|
|
offset = diff
|
|
while offset <= -180.0:
|
|
offset += 360.0
|
|
while offset > 180.0:
|
|
offset -= 360.0
|
|
return offset
|
|
|
|
def _NormalizeLongitude(lon):
|
|
while lon < 0.0:
|
|
lon += 360.0
|
|
while lon >= 360.0:
|
|
lon -= 360.0
|
|
return lon
|
|
|
|
class Vector:
|
|
def __init__(self, x, y, z, t):
|
|
self.x = x
|
|
self.y = y
|
|
self.z = z
|
|
self.t = t
|
|
|
|
def Length(self):
|
|
return math.sqrt(self.x**2 + self.y**2 + self.z**2)
|
|
|
|
@enum.unique
|
|
class Body(enum.IntEnum):
|
|
"""The celestial bodies supported by Astronomy Engine calculations.
|
|
|
|
Values
|
|
------
|
|
Invalid: An unknown, invalid, or undefined celestial body.
|
|
Mercury: The planet Mercury.
|
|
Venus: The planet Venus.
|
|
Earth: The planet Earth.
|
|
Mars: The planet Mars.
|
|
Jupiter: The planet Jupiter.
|
|
Saturn: The planet Saturn.
|
|
Uranus: The planet Uranus.
|
|
Neptune: The planet Neptune.
|
|
Pluto: The planet Pluto.
|
|
Sun: The Sun.
|
|
Moon: The Earth's moon.
|
|
"""
|
|
Invalid = -1
|
|
Mercury = 0
|
|
Venus = 1
|
|
Earth = 2
|
|
Mars = 3
|
|
Jupiter = 4
|
|
Saturn = 5
|
|
Uranus = 6
|
|
Neptune = 7
|
|
Pluto = 8
|
|
Sun = 9
|
|
Moon = 10
|
|
|
|
def BodyCode(name):
|
|
"""Finds the Body enumeration value, given the name of a body.
|
|
|
|
Parameters
|
|
----------
|
|
name: str
|
|
The common English name of a supported celestial body.
|
|
|
|
Returns
|
|
-------
|
|
Body
|
|
If `name` is a valid body name, returns the enumeration
|
|
value associated with that body.
|
|
Otherwise, returns `Body.Invalid`.
|
|
|
|
Example
|
|
-------
|
|
|
|
>>> astronomy.BodyCode('Mars')
|
|
<Body.Mars: 3>
|
|
|
|
"""
|
|
if name not in Body.__members__:
|
|
return Body.Invalid
|
|
return Body[name]
|
|
|
|
def _IsSuperiorPlanet(body):
|
|
return body in [Body.Mars, Body.Jupiter, Body.Saturn, Body.Uranus, Body.Neptune, Body.Pluto]
|
|
|
|
_PlanetOrbitalPeriod = [
|
|
87.969,
|
|
224.701,
|
|
_EARTH_ORBITAL_PERIOD,
|
|
686.980,
|
|
4332.589,
|
|
10759.22,
|
|
30685.4,
|
|
60189.0,
|
|
90560.0
|
|
]
|
|
|
|
class Error(Exception):
|
|
def __init__(self, message):
|
|
Exception.__init__(self, message)
|
|
|
|
class EarthNotAllowedError(Error):
|
|
def __init__(self):
|
|
Error.__init__(self, 'The Earth is not allowed as the body.')
|
|
|
|
class InvalidBodyError(Error):
|
|
def __init__(self):
|
|
Error.__init__(self, 'Invalid astronomical body.')
|
|
|
|
class BadVectorError(Error):
|
|
def __init__(self):
|
|
Error.__init__(self, 'Vector is too small to have a direction.')
|
|
|
|
class InternalError(Error):
|
|
def __init__(self):
|
|
Error.__init__(self, 'Internal error - please report issue at https://github.com/cosinekitty/astronomy/issues')
|
|
|
|
class NoConvergeError(Error):
|
|
def __init__(self):
|
|
Error.__init__(self, 'Numeric solver did not converge - please report issue at https://github.com/cosinekitty/astronomy/issues')
|
|
|
|
def _SynodicPeriod(body):
|
|
if body == Body.Earth:
|
|
raise EarthNotAllowedError()
|
|
if body < 0 or body >= len(_PlanetOrbitalPeriod):
|
|
raise InvalidBodyError()
|
|
if body == Body.Moon:
|
|
return _MEAN_SYNODIC_MONTH
|
|
return abs(_EARTH_ORBITAL_PERIOD / (_EARTH_ORBITAL_PERIOD/_PlanetOrbitalPeriod[body] - 1.0))
|
|
|
|
def _AngleBetween(a, b):
|
|
r = a.Length() * b.Length()
|
|
if r < 1.0e-8:
|
|
return BadVectorError()
|
|
dot = (a.x*b.x + a.y*b.y + a.z*b.z) / r
|
|
if dot <= -1.0:
|
|
return 180.0
|
|
if dot >= +1.0:
|
|
return 0.0
|
|
return math.degrees(math.acos(dot))
|
|
|
|
class _delta_t_entry_t:
|
|
def __init__(self, mjd, dt):
|
|
self.mjd = mjd
|
|
self.dt = dt
|
|
|
|
_DT = [
|
|
_delta_t_entry_t(-72638.0, 38),
|
|
_delta_t_entry_t(-65333.0, 26),
|
|
_delta_t_entry_t(-58028.0, 21),
|
|
_delta_t_entry_t(-50724.0, 21.1),
|
|
_delta_t_entry_t(-43419.0, 13.5),
|
|
_delta_t_entry_t(-39766.0, 13.7),
|
|
_delta_t_entry_t(-36114.0, 14.8),
|
|
_delta_t_entry_t(-32461.0, 15.7),
|
|
_delta_t_entry_t(-28809.0, 15.6),
|
|
_delta_t_entry_t(-25156.0, 13.3),
|
|
_delta_t_entry_t(-21504.0, 12.6),
|
|
_delta_t_entry_t(-17852.0, 11.2),
|
|
_delta_t_entry_t(-14200.0, 11.13),
|
|
_delta_t_entry_t(-10547.0, 7.95),
|
|
_delta_t_entry_t(-6895.0, 6.22),
|
|
_delta_t_entry_t(-3242.0, 6.55),
|
|
_delta_t_entry_t(-1416.0, 7.26),
|
|
_delta_t_entry_t(410.0, 7.35),
|
|
_delta_t_entry_t(2237.0, 5.92),
|
|
_delta_t_entry_t(4063.0, 1.04),
|
|
_delta_t_entry_t(5889.0, -3.19),
|
|
_delta_t_entry_t(7715.0, -5.36),
|
|
_delta_t_entry_t(9542.0, -5.74),
|
|
_delta_t_entry_t(11368.0, -5.86),
|
|
_delta_t_entry_t(13194.0, -6.41),
|
|
_delta_t_entry_t(15020.0, -2.70),
|
|
_delta_t_entry_t(16846.0, 3.92),
|
|
_delta_t_entry_t(18672.0, 10.38),
|
|
_delta_t_entry_t(20498.0, 17.19),
|
|
_delta_t_entry_t(22324.0, 21.41),
|
|
_delta_t_entry_t(24151.0, 23.63),
|
|
_delta_t_entry_t(25977.0, 24.02),
|
|
_delta_t_entry_t(27803.0, 23.91),
|
|
_delta_t_entry_t(29629.0, 24.35),
|
|
_delta_t_entry_t(31456.0, 26.76),
|
|
_delta_t_entry_t(33282.0, 29.15),
|
|
_delta_t_entry_t(35108.0, 31.07),
|
|
_delta_t_entry_t(36934.0, 33.150),
|
|
_delta_t_entry_t(38761.0, 35.738),
|
|
_delta_t_entry_t(40587.0, 40.182),
|
|
_delta_t_entry_t(42413.0, 45.477),
|
|
_delta_t_entry_t(44239.0, 50.540),
|
|
_delta_t_entry_t(44605.0, 51.3808),
|
|
_delta_t_entry_t(44970.0, 52.1668),
|
|
_delta_t_entry_t(45335.0, 52.9565),
|
|
_delta_t_entry_t(45700.0, 53.7882),
|
|
_delta_t_entry_t(46066.0, 54.3427),
|
|
_delta_t_entry_t(46431.0, 54.8712),
|
|
_delta_t_entry_t(46796.0, 55.3222),
|
|
_delta_t_entry_t(47161.0, 55.8197),
|
|
_delta_t_entry_t(47527.0, 56.3000),
|
|
_delta_t_entry_t(47892.0, 56.8553),
|
|
_delta_t_entry_t(48257.0, 57.5653),
|
|
_delta_t_entry_t(48622.0, 58.3092),
|
|
_delta_t_entry_t(48988.0, 59.1218),
|
|
_delta_t_entry_t(49353.0, 59.9845),
|
|
_delta_t_entry_t(49718.0, 60.7853),
|
|
_delta_t_entry_t(50083.0, 61.6287),
|
|
_delta_t_entry_t(50449.0, 62.2950),
|
|
_delta_t_entry_t(50814.0, 62.9659),
|
|
_delta_t_entry_t(51179.0, 63.4673),
|
|
_delta_t_entry_t(51544.0, 63.8285),
|
|
_delta_t_entry_t(51910.0, 64.0908),
|
|
_delta_t_entry_t(52275.0, 64.2998),
|
|
_delta_t_entry_t(52640.0, 64.4734),
|
|
_delta_t_entry_t(53005.0, 64.5736),
|
|
_delta_t_entry_t(53371.0, 64.6876),
|
|
_delta_t_entry_t(53736.0, 64.8452),
|
|
_delta_t_entry_t(54101.0, 65.1464),
|
|
_delta_t_entry_t(54466.0, 65.4573),
|
|
_delta_t_entry_t(54832.0, 65.7768),
|
|
_delta_t_entry_t(55197.0, 66.0699),
|
|
_delta_t_entry_t(55562.0, 66.3246),
|
|
_delta_t_entry_t(55927.0, 66.6030),
|
|
_delta_t_entry_t(56293.0, 66.9069),
|
|
_delta_t_entry_t(56658.0, 67.2810),
|
|
_delta_t_entry_t(57023.0, 67.6439),
|
|
_delta_t_entry_t(57388.0, 68.1024),
|
|
_delta_t_entry_t(57754.0, 68.5927),
|
|
_delta_t_entry_t(58119.0, 68.9676),
|
|
_delta_t_entry_t(58484.0, 69.2201),
|
|
_delta_t_entry_t(58849.0, 69.87),
|
|
_delta_t_entry_t(59214.0, 70.39),
|
|
_delta_t_entry_t(59580.0, 70.91),
|
|
_delta_t_entry_t(59945.0, 71.40),
|
|
_delta_t_entry_t(60310.0, 71.88),
|
|
_delta_t_entry_t(60675.0, 72.36),
|
|
_delta_t_entry_t(61041.0, 72.83),
|
|
_delta_t_entry_t(61406.0, 73.32),
|
|
_delta_t_entry_t(61680.0, 73.66)
|
|
]
|
|
|
|
def _DeltaT(mjd):
|
|
if mjd <= _DT[0].mjd:
|
|
return _DT[0].dt
|
|
if mjd >= _DT[-1].mjd:
|
|
return _DT[-1].dt
|
|
# Do a binary search to find the pair of indexes this mjd lies between.
|
|
lo = 0
|
|
hi = len(_DT) - 2 # Make sure there is always an array element after the one we are looking at.
|
|
while True:
|
|
if lo > hi:
|
|
# This should never happen unless there is a bug in the binary search.
|
|
raise Error('Could not find delta-t value.')
|
|
c = (lo + hi) // 2
|
|
if mjd < _DT[c].mjd:
|
|
hi = c-1
|
|
elif mjd > _DT[c+1].mjd:
|
|
lo = c+1
|
|
else:
|
|
frac = (mjd - _DT[c].mjd) / (_DT[c+1].mjd - _DT[c].mjd)
|
|
return _DT[c].dt + frac*(_DT[c+1].dt - _DT[c].dt)
|
|
|
|
def _TerrestrialTime(ut):
|
|
return ut + _DeltaT(ut + _Y2000_IN_MJD) / 86400.0
|
|
|
|
class Time:
|
|
"""Represents a date and time used for performing astronomy calculations.
|
|
|
|
All calculations performed by Astronomy Engine are based on
|
|
dates and times represented by `Time` objects.
|
|
|
|
Parameters
|
|
----------
|
|
ut : float
|
|
UT1/UTC number of days since noon on January 1, 2000.
|
|
See the `ut` attribute of this class for more details.
|
|
|
|
Attributes
|
|
----------
|
|
ut : float
|
|
The floating point number of days of Universal Time since noon UTC January 1, 2000.
|
|
Astronomy Engine approximates UTC and UT1 as being the same thing, although they are
|
|
not exactly equivalent; UTC and UT1 can disagree by up to 0.9 seconds.
|
|
This approximation is sufficient for the accuracy requirements of Astronomy Engine.
|
|
Universal Time Coordinate (UTC) is the international standard for legal and civil
|
|
timekeeping and replaces the older Greenwich Mean Time (GMT) standard.
|
|
UTC is kept in sync with unpredictable observed changes in the Earth's rotation
|
|
by occasionally adding leap seconds as needed.
|
|
UT1 is an idealized time scale based on observed rotation of the Earth, which
|
|
gradually slows down in an unpredictable way over time, due to tidal drag by the Moon and Sun,
|
|
large scale weather events like hurricanes, and internal seismic and convection effects.
|
|
Conceptually, UT1 drifts from atomic time continuously and erratically, whereas UTC
|
|
is adjusted by a scheduled whole number of leap seconds as needed.
|
|
The value in `ut` is appropriate for any calculation involving the Earth's rotation,
|
|
such as calculating rise/set times, culumination, and anything involving apparent
|
|
sidereal time.
|
|
Before the era of atomic timekeeping, days based on the Earth's rotation
|
|
were often known as *mean solar days*.
|
|
tt : float
|
|
Terrestrial Time days since noon on January 1, 2000.
|
|
Terrestrial Time is an atomic time scale defined as a number of days since noon on January 1, 2000.
|
|
In this system, days are not based on Earth rotations, but instead by
|
|
the number of elapsed [SI seconds](https://physics.nist.gov/cuu/Units/second.html)
|
|
divided by 86400. Unlike `ut`, `tt` increases uniformly without adjustments
|
|
for changes in the Earth's rotation.
|
|
The value in `tt` is used for calculations of movements not involving the Earth's rotation,
|
|
such as the orbits of planets around the Sun, or the Moon around the Earth.
|
|
Historically, Terrestrial Time has also been known by the term *Ephemeris Time* (ET).
|
|
"""
|
|
def __init__(self, ut):
|
|
self.ut = ut
|
|
self.tt = _TerrestrialTime(ut)
|
|
self.etilt = None
|
|
|
|
@staticmethod
|
|
def Make(year, month, day, hour, minute, second):
|
|
"""Creates a #Time object from a UTC calendar date and time.
|
|
|
|
Parameters
|
|
----------
|
|
year : int
|
|
The UTC 4-digit year value, e.g. 2019.
|
|
month : int
|
|
The UTC month in the range 1..12.
|
|
day : int
|
|
The UTC day of the month, in the range 1..31.
|
|
hour : int
|
|
The UTC hour, in the range 0..23.
|
|
minute : int
|
|
The UTC minute, in the range 0..59.
|
|
second : float
|
|
The real-valued UTC second, in the range [0, 60).
|
|
|
|
Returns
|
|
-------
|
|
Time
|
|
"""
|
|
micro = round(math.fmod(second, 1.0) * 1000000)
|
|
second = math.floor(second - micro/1000000)
|
|
d = datetime.datetime(year, month, day, hour, minute, second, micro)
|
|
ut = (d - _EPOCH).total_seconds() / 86400
|
|
return Time(ut)
|
|
|
|
@staticmethod
|
|
def Now():
|
|
ut = (datetime.datetime.utcnow() - _EPOCH).total_seconds() / 86400.0
|
|
return Time(ut)
|
|
|
|
def AddDays(self, days):
|
|
return Time(self.ut + days)
|
|
|
|
def __str__(self):
|
|
millis = round(self.ut * 86400000.0)
|
|
n = _EPOCH + datetime.timedelta(milliseconds=millis)
|
|
return '{:04d}-{:02d}-{:02d}T{:02d}:{:02d}:{:02d}.{:03d}Z'.format(n.year, n.month, n.day, n.hour, n.minute, n.second, math.floor(n.microsecond / 1000))
|
|
|
|
def Utc(self):
|
|
return _EPOCH + datetime.timedelta(days=self.ut)
|
|
|
|
def _etilt(self):
|
|
# Calculates precession and nutation of the Earth's axis.
|
|
# The calculations are very expensive, so lazy-evaluate and cache
|
|
# the result inside this Time object.
|
|
if self.etilt is None:
|
|
self.etilt = _e_tilt(self)
|
|
return self.etilt
|
|
|
|
|
|
class Observer:
|
|
"""Represents the geographic location of an observer on the surface of the Earth.
|
|
|
|
Parameters
|
|
----------
|
|
latitude : float
|
|
Geographic latitude in degrees north of the equator.
|
|
longitude : float
|
|
Geographic longitude in degrees east of the prime meridian at Greenwich, England.
|
|
height : float
|
|
Elevation above sea level in meters.
|
|
"""
|
|
def __init__(self, latitude, longitude, height=0):
|
|
self.latitude = latitude
|
|
self.longitude = longitude
|
|
self.height = height
|
|
|
|
class _iau2000b:
|
|
def __init__(self, time):
|
|
t = time.tt / 36525.0
|
|
el = math.fmod((485868.249036 + t*1717915923.2178), _ASEC360) * _ASEC2RAD
|
|
elp = math.fmod((1287104.79305 + t*129596581.0481), _ASEC360) * _ASEC2RAD
|
|
f = math.fmod((335779.526232 + t*1739527262.8478), _ASEC360) * _ASEC2RAD
|
|
d = math.fmod((1072260.70369 + t*1602961601.2090), _ASEC360) * _ASEC2RAD
|
|
om = math.fmod((450160.398036 - t*6962890.5431), _ASEC360) * _ASEC2RAD
|
|
dp = 0
|
|
de = 0
|
|
|
|
|
|
sarg = math.sin(om)
|
|
carg = math.cos(om)
|
|
dp += (-172064161.0 - 174666.0*t)*sarg + 33386.0*carg
|
|
de += (92052331.0 + 9086.0*t)*carg + 15377.0*sarg
|
|
|
|
|
|
arg = 2.0*f - 2.0*d + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-13170906.0 - 1675.0*t)*sarg - 13696.0*carg
|
|
de += (5730336.0 - 3015.0*t)*carg - 4587.0*sarg
|
|
|
|
|
|
arg = 2.0*f + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-2276413.0 - 234.0*t)*sarg + 2796.0*carg
|
|
de += (978459.0 - 485.0*t)*carg + 1374.0*sarg
|
|
|
|
|
|
arg = 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (2074554.0 + 207.0*t)*sarg - 698.0*carg
|
|
de += (-897492.0 + 470.0*t)*carg - 291.0*sarg
|
|
|
|
|
|
sarg = math.sin(elp)
|
|
carg = math.cos(elp)
|
|
dp += (1475877.0 - 3633.0*t)*sarg + 11817.0*carg
|
|
de += (73871.0 - 184.0*t)*carg - 1924.0*sarg
|
|
|
|
|
|
arg = elp + 2.0*f - 2.0*d + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-516821.0 + 1226.0*t)*sarg - 524.0*carg
|
|
de += (224386.0 - 677.0*t)*carg - 174.0*sarg
|
|
|
|
|
|
sarg = math.sin(el)
|
|
carg = math.cos(el)
|
|
dp += (711159.0 + 73.0*t)*sarg - 872.0*carg
|
|
de += (-6750.0)*carg + 358.0*sarg
|
|
|
|
|
|
arg = 2.0*f + om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-387298.0 - 367.0*t)*sarg + 380.0*carg
|
|
de += (200728.0 + 18.0*t)*carg + 318.0*sarg
|
|
|
|
|
|
arg = el + 2.0*f + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-301461.0 - 36.0*t)*sarg + 816.0*carg
|
|
de += (129025.0 - 63.0*t)*carg + 367.0*sarg
|
|
|
|
|
|
arg = -elp + 2.0*f - 2.0*d + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (215829.0 - 494.0*t)*sarg + 111.0*carg
|
|
de += (-95929.0 + 299.0*t)*carg + 132.0*sarg
|
|
|
|
|
|
arg = 2.0*f - 2.0*d + om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (128227.0 + 137.0*t)*sarg + 181.0*carg
|
|
de += (-68982.0 - 9.0*t)*carg + 39.0*sarg
|
|
|
|
|
|
arg = -el + 2.0*f + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (123457.0 + 11.0*t)*sarg + 19.0*carg
|
|
de += (-53311.0 + 32.0*t)*carg - 4.0*sarg
|
|
|
|
|
|
arg = -el + 2.0*d
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (156994.0 + 10.0*t)*sarg - 168.0*carg
|
|
de += (-1235.0)*carg + 82.0*sarg
|
|
|
|
|
|
arg = el + om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (63110.0 + 63.0*t)*sarg + 27.0*carg
|
|
de += (-33228.0)*carg - 9.0*sarg
|
|
|
|
|
|
arg = -el + om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-57976.0 - 63.0*t)*sarg - 189.0*carg
|
|
de += (31429.0)*carg - 75.0*sarg
|
|
|
|
|
|
arg = -el + 2.0*f + 2.0*d + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-59641.0 - 11.0*t)*sarg + 149.0*carg
|
|
de += (25543.0 - 11.0*t)*carg + 66.0*sarg
|
|
|
|
|
|
arg = el + 2.0*f + om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-51613.0 - 42.0*t)*sarg + 129.0*carg
|
|
de += (26366.0)*carg + 78.0*sarg
|
|
|
|
|
|
arg = -2.0*el + 2.0*f + om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (45893.0 + 50.0*t)*sarg + 31.0*carg
|
|
de += (-24236.0 - 10.0*t)*carg + 20.0*sarg
|
|
|
|
|
|
arg = 2.0*d
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (63384.0 + 11.0*t)*sarg - 150.0*carg
|
|
de += (-1220.0)*carg + 29.0*sarg
|
|
|
|
|
|
arg = 2.0*f + 2.0*d + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-38571.0 - 1.0*t)*sarg + 158.0*carg
|
|
de += (16452.0 - 11.0*t)*carg + 68.0*sarg
|
|
|
|
|
|
arg = -2.0*elp + 2.0*f - 2.0*d + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (32481.0)*sarg
|
|
de += (-13870.0)*carg
|
|
|
|
|
|
arg = -2.0*el + 2.0*d
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-47722.0)*sarg - 18.0*carg
|
|
de += (477.0)*carg - 25.0*sarg
|
|
|
|
|
|
arg = 2.0*el + 2.0*f + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-31046.0 - 1.0*t)*sarg + 131.0*carg
|
|
de += (13238.0 - 11.0*t)*carg + 59.0*sarg
|
|
|
|
|
|
arg = el + 2.0*f - 2.0*d + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (28593.0)*sarg - carg
|
|
de += (-12338.0 + 10.0*t)*carg - 3.0*sarg
|
|
|
|
|
|
arg = -el + 2.0*f + om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (20441.0 + 21.0*t)*sarg + 10.0*carg
|
|
de += (-10758.0)*carg - 3.0*sarg
|
|
|
|
|
|
arg = 2.0*el
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (29243.0)*sarg - 74.0*carg
|
|
de += (-609.0)*carg + 13.0*sarg
|
|
|
|
|
|
arg = 2.0*f
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (25887.0)*sarg - 66.0*carg
|
|
de += (-550.0)*carg + 11.0*sarg
|
|
|
|
|
|
arg = elp + om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-14053.0 - 25.0*t)*sarg + 79.0*carg
|
|
de += (8551.0 - 2.0*t)*carg - 45.0*sarg
|
|
|
|
|
|
arg = -el + 2.0*d + om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (15164.0 + 10.0*t)*sarg + 11.0*carg
|
|
de += (-8001.0)*carg - sarg
|
|
|
|
|
|
arg = 2.0*elp + 2.0*f - 2.0*d + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-15794.0 + 72.0*t)*sarg - 16.0*carg
|
|
de += (6850.0 - 42.0*t)*carg - 5.0*sarg
|
|
|
|
|
|
arg = -2.0*f + 2.0*d
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (21783.0)*sarg + 13.0*carg
|
|
de += (-167.0)*carg + 13.0*sarg
|
|
|
|
|
|
arg = el - 2.0*d + om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-12873.0 - 10.0*t)*sarg - 37.0*carg
|
|
de += (6953.0)*carg - 14.0*sarg
|
|
|
|
|
|
arg = -elp + om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-12654.0 + 11.0*t)*sarg + 63.0*carg
|
|
de += (6415.0)*carg + 26.0*sarg
|
|
|
|
|
|
arg = -el + 2.0*f + 2.0*d + om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-10204.0)*sarg + 25.0*carg
|
|
de += (5222.0)*carg + 15.0*sarg
|
|
|
|
|
|
arg = 2.0*elp
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (16707.0 - 85.0*t)*sarg - 10.0*carg
|
|
de += (168.0 - 1.0*t)*carg + 10.0*sarg
|
|
|
|
|
|
arg = el + 2.0*f + 2.0*d + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-7691.0)*sarg + 44.0*carg
|
|
de += (3268.0)*carg + 19.0*sarg
|
|
|
|
|
|
arg = -2.0*el + 2.0*f
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-11024.0)*sarg - 14.0*carg
|
|
de += (104.0)*carg + 2.0*sarg
|
|
|
|
|
|
arg = elp + 2.0*f + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (7566.0 - 21.0*t)*sarg - 11.0*carg
|
|
de += (-3250.0)*carg - 5.0*sarg
|
|
|
|
|
|
arg = 2.0*f + 2.0*d + om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-6637.0 - 11.0*t)*sarg + 25.0*carg
|
|
de += (3353.0)*carg + 14.0*sarg
|
|
|
|
|
|
arg = -elp + 2.0*f + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-7141.0 + 21.0*t)*sarg + 8.0*carg
|
|
de += (3070.0)*carg + 4.0*sarg
|
|
|
|
|
|
arg = 2.0*d + om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-6302.0 - 11.0*t)*sarg + 2.0*carg
|
|
de += (3272.0)*carg + 4.0*sarg
|
|
|
|
|
|
arg = el + 2.0*f - 2.0*d + om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (5800.0 + 10.0*t)*sarg + 2.0*carg
|
|
de += (-3045.0)*carg - sarg
|
|
|
|
|
|
arg = 2.0*el + 2.0*f - 2.0*d + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (6443.0)*sarg - 7.0*carg
|
|
de += (-2768.0)*carg - 4.0*sarg
|
|
|
|
|
|
arg = -2.0*el + 2.0*d + om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-5774.0 - 11.0*t)*sarg - 15.0*carg
|
|
de += (3041.0)*carg - 5.0*sarg
|
|
|
|
|
|
arg = 2.0*el + 2.0*f + om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-5350.0)*sarg + 21.0*carg
|
|
de += (2695.0)*carg + 12.0*sarg
|
|
|
|
|
|
arg = -elp + 2.0*f - 2.0*d + om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-4752.0 - 11.0*t)*sarg - 3.0*carg
|
|
de += (2719.0)*carg - 3.0*sarg
|
|
|
|
|
|
arg = -2.0*d + om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-4940.0 - 11.0*t)*sarg - 21.0*carg
|
|
de += (2720.0)*carg - 9.0*sarg
|
|
|
|
|
|
arg = -el - elp + 2.0*d
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (7350.0)*sarg - 8.0*carg
|
|
de += (-51.0)*carg + 4.0*sarg
|
|
|
|
|
|
arg = 2.0*el - 2.0*d + om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (4065.0)*sarg + 6.0*carg
|
|
de += (-2206.0)*carg + sarg
|
|
|
|
|
|
arg = el + 2.0*d
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (6579.0)*sarg - 24.0*carg
|
|
de += (-199.0)*carg + 2.0*sarg
|
|
|
|
|
|
arg = elp + 2.0*f - 2.0*d + om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (3579.0)*sarg + 5.0*carg
|
|
de += (-1900.0)*carg + sarg
|
|
|
|
|
|
arg = el - elp
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (4725.0)*sarg - 6.0*carg
|
|
de += (-41.0)*carg + 3.0*sarg
|
|
|
|
|
|
arg = -2.0*el + 2.0*f + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-3075.0)*sarg - 2.0*carg
|
|
de += (1313.0)*carg - sarg
|
|
|
|
|
|
arg = 3.0*el + 2.0*f + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-2904.0)*sarg + 15.0*carg
|
|
de += (1233.0)*carg + 7.0*sarg
|
|
|
|
|
|
arg = -elp + 2.0*d
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (4348.0)*sarg - 10.0*carg
|
|
de += (-81.0)*carg + 2.0*sarg
|
|
|
|
|
|
arg = el - elp + 2.0*f + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-2878.0)*sarg + 8.0*carg
|
|
de += (1232.0)*carg + 4.0*sarg
|
|
|
|
|
|
sarg = math.sin(d)
|
|
carg = math.cos(d)
|
|
dp += (-4230.0)*sarg + 5.0*carg
|
|
de += (-20.0)*carg - 2.0*sarg
|
|
|
|
|
|
arg = -el - elp + 2.0*f + 2.0*d + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-2819.0)*sarg + 7.0*carg
|
|
de += (1207.0)*carg + 3.0*sarg
|
|
|
|
|
|
arg = -el + 2.0*f
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-4056.0)*sarg + 5.0*carg
|
|
de += (40.0)*carg - 2.0*sarg
|
|
|
|
|
|
arg = -elp + 2.0*f + 2.0*d + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-2647.0)*sarg + 11.0*carg
|
|
de += (1129.0)*carg + 5.0*sarg
|
|
|
|
|
|
arg = -2.0*el + om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-2294.0)*sarg - 10.0*carg
|
|
de += (1266.0)*carg - 4.0*sarg
|
|
|
|
|
|
arg = el + elp + 2.0*f + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (2481.0)*sarg - 7.0*carg
|
|
de += (-1062.0)*carg - 3.0*sarg
|
|
|
|
|
|
arg = 2.0*el + om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (2179.0)*sarg - 2.0*carg
|
|
de += (-1129.0)*carg - 2.0*sarg
|
|
|
|
|
|
arg = -el + elp + d
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (3276.0)*sarg + carg
|
|
de += (-9.0)*carg
|
|
|
|
|
|
arg = el + elp
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-3389.0)*sarg + 5.0*carg
|
|
de += (35.0)*carg - 2.0*sarg
|
|
|
|
|
|
arg = el + 2.0*f
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (3339.0)*sarg - 13.0*carg
|
|
de += (-107.0)*carg + sarg
|
|
|
|
|
|
arg = -el + 2.0*f - 2.0*d + om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-1987.0)*sarg - 6.0*carg
|
|
de += (1073.0)*carg - 2.0*sarg
|
|
|
|
|
|
arg = el + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-1981.0)*sarg
|
|
de += (854.0)*carg
|
|
|
|
|
|
arg = -el + d
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (4026.0)*sarg - 353.0*carg
|
|
de += (-553.0)*carg - 139.0*sarg
|
|
|
|
|
|
arg = 2.0*f + d + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (1660.0)*sarg - 5.0*carg
|
|
de += (-710.0)*carg - 2.0*sarg
|
|
|
|
|
|
arg = -el + 2.0*f + 4.0*d + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-1521.0)*sarg + 9.0*carg
|
|
de += (647.0)*carg + 4.0*sarg
|
|
|
|
|
|
arg = -el + elp + d + om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (1314.0)*sarg
|
|
de += (-700.0)*carg
|
|
|
|
|
|
arg = -2.0*elp + 2.0*f - 2.0*d + om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-1283.0)*sarg
|
|
de += (672.0)*carg
|
|
|
|
|
|
arg = el + 2.0*f + 2.0*d + om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (-1331.0)*sarg + 8.0*carg
|
|
de += (663.0)*carg + 4.0*sarg
|
|
|
|
|
|
arg = -2.0*el + 2.0*f + 2.0*d + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (1383.0)*sarg - 2.0*carg
|
|
de += (-594.0)*carg - 2.0*sarg
|
|
|
|
|
|
arg = -el + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (1405.0)*sarg + 4.0*carg
|
|
de += (-610.0)*carg + 2.0*sarg
|
|
|
|
|
|
arg = el + elp + 2.0*f - 2.0*d + 2.0*om
|
|
sarg = math.sin(arg)
|
|
carg = math.cos(arg)
|
|
dp += (1290.0)*sarg
|
|
de += (-556.0)*carg
|
|
|
|
|
|
self.dpsi = -0.000135 + (dp * 1.0e-7)
|
|
self.deps = +0.000388 + (de * 1.0e-7)
|
|
|
|
def _mean_obliq(tt):
|
|
t = tt / 36525
|
|
asec = (
|
|
(((( - 0.0000000434 * t
|
|
- 0.000000576 ) * t
|
|
+ 0.00200340 ) * t
|
|
- 0.0001831 ) * t
|
|
- 46.836769 ) * t + 84381.406
|
|
)
|
|
return asec / 3600.0
|
|
|
|
class _e_tilt:
|
|
def __init__(self, time):
|
|
e = _iau2000b(time)
|
|
self.dpsi = e.dpsi
|
|
self.deps = e.deps
|
|
self.mobl = _mean_obliq(time.tt)
|
|
self.tobl = self.mobl + (e.deps / 3600.0)
|
|
self.tt = time.tt
|
|
self.ee = e.dpsi * math.cos(math.radians(self.mobl)) / 15.0
|
|
|
|
def _ecl2equ_vec(time, ecl):
|
|
obl = math.radians(_mean_obliq(time.tt))
|
|
cos_obl = math.cos(obl)
|
|
sin_obl = math.sin(obl)
|
|
return [
|
|
ecl[0],
|
|
ecl[1]*cos_obl - ecl[2]*sin_obl,
|
|
ecl[1]*sin_obl + ecl[2]*cos_obl
|
|
]
|
|
|
|
def _precession(tt1, pos1, tt2):
|
|
eps0 = 84381.406
|
|
if tt1 != 0 and tt2 != 0:
|
|
raise Error('One of (tt1, tt2) must be zero.')
|
|
t = (tt2 - tt1) / 36525
|
|
if tt2 == 0:
|
|
t = -t
|
|
|
|
psia = (((((- 0.0000000951 * t
|
|
+ 0.000132851 ) * t
|
|
- 0.00114045 ) * t
|
|
- 1.0790069 ) * t
|
|
+ 5038.481507 ) * t)
|
|
|
|
omegaa = (((((+ 0.0000003337 * t
|
|
- 0.000000467 ) * t
|
|
- 0.00772503 ) * t
|
|
+ 0.0512623 ) * t
|
|
- 0.025754 ) * t + eps0)
|
|
|
|
chia = (((((- 0.0000000560 * t
|
|
+ 0.000170663 ) * t
|
|
- 0.00121197 ) * t
|
|
- 2.3814292 ) * t
|
|
+ 10.556403 ) * t)
|
|
|
|
eps0 *= _ASEC2RAD
|
|
psia *= _ASEC2RAD
|
|
omegaa *= _ASEC2RAD
|
|
chia *= _ASEC2RAD
|
|
|
|
sa = math.sin(eps0)
|
|
ca = math.cos(eps0)
|
|
sb = math.sin(-psia)
|
|
cb = math.cos(-psia)
|
|
sc = math.sin(-omegaa)
|
|
cc = math.cos(-omegaa)
|
|
sd = math.sin(chia)
|
|
cd = math.cos(chia)
|
|
|
|
xx = cd * cb - sb * sd * cc
|
|
yx = cd * sb * ca + sd * cc * cb * ca - sa * sd * sc
|
|
zx = cd * sb * sa + sd * cc * cb * sa + ca * sd * sc
|
|
xy = -sd * cb - sb * cd * cc
|
|
yy = -sd * sb * ca + cd * cc * cb * ca - sa * cd * sc
|
|
zy = -sd * sb * sa + cd * cc * cb * sa + ca * cd * sc
|
|
xz = sb * sc
|
|
yz = -sc * cb * ca - sa * cc
|
|
zz = -sc * cb * sa + cc * ca
|
|
if tt2 == 0.0:
|
|
# Perform rotation from other epoch to J2000.0.
|
|
return [
|
|
xx * pos1[0] + xy * pos1[1] + xz * pos1[2],
|
|
yx * pos1[0] + yy * pos1[1] + yz * pos1[2],
|
|
zx * pos1[0] + zy * pos1[1] + zz * pos1[2]
|
|
]
|
|
|
|
# Perform rotation from J2000.0 to other epoch.
|
|
return [
|
|
xx * pos1[0] + yx * pos1[1] + zx * pos1[2],
|
|
xy * pos1[0] + yy * pos1[1] + zy * pos1[2],
|
|
xz * pos1[0] + yz * pos1[1] + zz * pos1[2]
|
|
]
|
|
|
|
class Equatorial:
|
|
def __init__(self, ra, dec, dist):
|
|
self.ra = ra
|
|
self.dec = dec
|
|
self.dist = dist
|
|
|
|
def _vector2radec(pos):
|
|
xyproj = pos[0]*pos[0] + pos[1]*pos[1]
|
|
dist = math.sqrt(xyproj + pos[2]*pos[2])
|
|
if xyproj == 0.0:
|
|
if pos[2] == 0.0:
|
|
# Indeterminate coordinates: pos vector has zero length.
|
|
raise Error('Cannot convert vector to polar coordinates')
|
|
ra = 0.0
|
|
if pos[2] < 0.0:
|
|
dec = -90.0
|
|
else:
|
|
dec = +90.0
|
|
else:
|
|
ra = math.degrees(math.atan2(pos[1], pos[0])) / 15.0
|
|
if ra < 0:
|
|
ra += 24
|
|
dec = math.degrees(math.atan2(pos[2], math.sqrt(xyproj)))
|
|
return Equatorial(ra, dec, dist)
|
|
|
|
|
|
def _nutation(time, direction, inpos):
|
|
tilt = time._etilt()
|
|
oblm = math.radians(tilt.mobl)
|
|
oblt = math.radians(tilt.tobl)
|
|
psi = tilt.dpsi * _ASEC2RAD
|
|
cobm = math.cos(oblm)
|
|
sobm = math.sin(oblm)
|
|
cobt = math.cos(oblt)
|
|
sobt = math.sin(oblt)
|
|
cpsi = math.cos(psi)
|
|
spsi = math.sin(psi)
|
|
|
|
xx = cpsi
|
|
yx = -spsi * cobm
|
|
zx = -spsi * sobm
|
|
xy = spsi * cobt
|
|
yy = cpsi * cobm * cobt + sobm * sobt
|
|
zy = cpsi * sobm * cobt - cobm * sobt
|
|
xz = spsi * sobt
|
|
yz = cpsi * cobm * sobt - sobm * cobt
|
|
zz = cpsi * sobm * sobt + cobm * cobt
|
|
|
|
if direction == 0:
|
|
# forward rotation
|
|
return [
|
|
xx * inpos[0] + yx * inpos[1] + zx * inpos[2],
|
|
xy * inpos[0] + yy * inpos[1] + zy * inpos[2],
|
|
xz * inpos[0] + yz * inpos[1] + zz * inpos[2]
|
|
]
|
|
|
|
# inverse rotation
|
|
return [
|
|
xx * inpos[0] + xy * inpos[1] + xz * inpos[2],
|
|
yx * inpos[0] + yy * inpos[1] + yz * inpos[2],
|
|
zx * inpos[0] + zy * inpos[1] + zz * inpos[2]
|
|
]
|
|
|
|
def _era(time): # Earth Rotation Angle
|
|
thet1 = 0.7790572732640 + 0.00273781191135448 * time.ut
|
|
thet3 = math.fmod(time.ut, 1.0)
|
|
theta = 360.0 * math.fmod((thet1 + thet3), 1.0)
|
|
if theta < 0.0:
|
|
theta += 360.0
|
|
return theta
|
|
|
|
|
|
def _sidereal_time(time):
|
|
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)
|
|
theta = _era(time)
|
|
st = (eqeq + 0.014506 +
|
|
(((( - 0.0000000368 * t
|
|
- 0.000029956 ) * t
|
|
- 0.00000044 ) * t
|
|
+ 1.3915817 ) * t
|
|
+ 4612.156534 ) * t)
|
|
gst = math.fmod((st/3600.0 + theta), 360.0) / 15.0
|
|
if gst < 0.0:
|
|
gst += 24.0
|
|
return gst
|
|
|
|
|
|
def _terra(observer, st):
|
|
erad_km = _ERAD / 1000.0
|
|
df = 1.0 - 0.003352819697896 # flattening of the Earth
|
|
df2 = df * df
|
|
phi = math.radians(observer.latitude)
|
|
sinphi = math.sin(phi)
|
|
cosphi = math.cos(phi)
|
|
c = 1.0 / math.sqrt(cosphi*cosphi + df2*sinphi*sinphi)
|
|
s = df2 * c
|
|
ht_km = observer.height / 1000.0
|
|
ach = erad_km*c + ht_km
|
|
ash = erad_km*s + ht_km
|
|
stlocl = math.radians(15.0*st + observer.longitude)
|
|
sinst = math.sin(stlocl)
|
|
cosst = math.cos(stlocl)
|
|
return [
|
|
ach * cosphi * cosst / _KM_PER_AU,
|
|
ach * cosphi * sinst / _KM_PER_AU,
|
|
ash * sinphi / _KM_PER_AU
|
|
]
|
|
|
|
def _geo_pos(time, observer):
|
|
gast = _sidereal_time(time)
|
|
pos1 = _terra(observer, gast)
|
|
pos2 = _nutation(time, -1, pos1)
|
|
outpos = _precession(time.tt, pos2, 0.0)
|
|
return outpos
|
|
|
|
def _spin(angle, pos1):
|
|
angr = math.radians(angle)
|
|
cosang = math.cos(angr)
|
|
sinang = math.sin(angr)
|
|
return [
|
|
+cosang*pos1[0] + sinang*pos1[1],
|
|
-sinang*pos1[0] + cosang*pos1[1],
|
|
pos1[2]
|
|
]
|
|
|
|
#----------------------------------------------------------------------------
|
|
# BEGIN CalcMoon
|
|
|
|
def _Array1(xmin, xmax):
|
|
return dict((key, 0j) for key in range(xmin, 1+xmax))
|
|
|
|
def _Array2(xmin, xmax, ymin, ymax):
|
|
return dict((key, _Array1(ymin, ymax)) for key in range(xmin, 1+xmax))
|
|
|
|
class _moonpos:
|
|
def __init__(self, lon, lat, dist):
|
|
self.geo_eclip_lon = lon
|
|
self.geo_eclip_lat = lat
|
|
self.distance_au = dist
|
|
|
|
def _CalcMoon(time):
|
|
T = time.tt / 36525
|
|
ex = _Array2(-6, 6, 1, 4)
|
|
|
|
def Sine(phi):
|
|
return math.sin(_PI2 * phi)
|
|
|
|
def Frac(x):
|
|
return x - math.floor(x)
|
|
|
|
T2 = T*T
|
|
DLAM = 0
|
|
DS = 0
|
|
GAM1C = 0
|
|
SINPI = 3422.7000
|
|
S1 = Sine(0.19833+0.05611*T)
|
|
S2 = Sine(0.27869+0.04508*T)
|
|
S3 = Sine(0.16827-0.36903*T)
|
|
S4 = Sine(0.34734-5.37261*T)
|
|
S5 = Sine(0.10498-5.37899*T)
|
|
S6 = Sine(0.42681-0.41855*T)
|
|
S7 = Sine(0.14943-5.37511*T)
|
|
DL0 = 0.84*S1+0.31*S2+14.27*S3+ 7.26*S4+ 0.28*S5+0.24*S6
|
|
DL = 2.94*S1+0.31*S2+14.27*S3+ 9.34*S4+ 1.12*S5+0.83*S6
|
|
DLS =-6.40*S1 -1.89*S6
|
|
DF = 0.21*S1+0.31*S2+14.27*S3-88.70*S4-15.30*S5+0.24*S6-1.86*S7
|
|
DD = DL0-DLS
|
|
DGAM = ((-3332E-9 * Sine(0.59734-5.37261*T)
|
|
-539E-9 * Sine(0.35498-5.37899*T)
|
|
-64E-9 * Sine(0.39943-5.37511*T)))
|
|
|
|
L0 = _PI2*Frac(0.60643382+1336.85522467*T-0.00000313*T2) + DL0/_ARC
|
|
L = _PI2*Frac(0.37489701+1325.55240982*T+0.00002565*T2) + DL /_ARC
|
|
LS = _PI2*Frac(0.99312619+ 99.99735956*T-0.00000044*T2) + DLS/_ARC
|
|
F = _PI2*Frac(0.25909118+1342.22782980*T-0.00000892*T2) + DF /_ARC
|
|
D = _PI2*Frac(0.82736186+1236.85308708*T-0.00000397*T2) + DD /_ARC
|
|
|
|
I = 1
|
|
while I <= 4:
|
|
if I == 1:
|
|
ARG=L; MAX=4; FAC=1.000002208
|
|
elif I == 2:
|
|
ARG=LS; MAX=3; FAC=0.997504612-0.002495388*T
|
|
elif I == 3:
|
|
ARG=F; MAX=4; FAC=1.000002708+139.978*DGAM
|
|
else:
|
|
ARG=D; MAX=6; FAC=1.0
|
|
|
|
ex[0][I] = complex(1, 0)
|
|
ex[1][I] = complex(FAC * math.cos(ARG), FAC * math.sin(ARG))
|
|
|
|
J = 2
|
|
while J <= MAX:
|
|
ex[J][I] = ex[J-1][I] * ex[1][I]
|
|
J += 1
|
|
|
|
J = 1
|
|
while J <= MAX:
|
|
ex[-J][I] = ex[J][I].conjugate()
|
|
J += 1
|
|
|
|
I += 1
|
|
|
|
|
|
|
|
# AddSol(13.902000, 14.060000, -0.001000, 0.260700, 0.000000, 0.000000, 0.000000, 4.000000)
|
|
z = ex[4][4]
|
|
DLAM += 13.902 * z.imag
|
|
DS += 14.06 * z.imag
|
|
GAM1C += -0.001 * z.real
|
|
SINPI += 0.2607 * z.real
|
|
|
|
# AddSol(0.403000, -4.010000, 0.394000, 0.002300, 0.000000, 0.000000, 0.000000, 3.000000)
|
|
z = ex[3][4]
|
|
DLAM += 0.403 * z.imag
|
|
DS += -4.01 * z.imag
|
|
GAM1C += 0.394 * z.real
|
|
SINPI += 0.0023 * z.real
|
|
|
|
# AddSol(2369.912000, 2373.360000, 0.601000, 28.233300, 0.000000, 0.000000, 0.000000, 2.000000)
|
|
z = ex[2][4]
|
|
DLAM += 2369.912 * z.imag
|
|
DS += 2373.36 * z.imag
|
|
GAM1C += 0.601 * z.real
|
|
SINPI += 28.2333 * z.real
|
|
|
|
# AddSol(-125.154000, -112.790000, -0.725000, -0.978100, 0.000000, 0.000000, 0.000000, 1.000000)
|
|
z = ex[1][4]
|
|
DLAM += -125.154 * z.imag
|
|
DS += -112.79 * z.imag
|
|
GAM1C += -0.725 * z.real
|
|
SINPI += -0.9781 * z.real
|
|
|
|
# AddSol(1.979000, 6.980000, -0.445000, 0.043300, 1.000000, 0.000000, 0.000000, 4.000000)
|
|
z = ex[1][1] * ex[4][4]
|
|
DLAM += 1.979 * z.imag
|
|
DS += 6.98 * z.imag
|
|
GAM1C += -0.445 * z.real
|
|
SINPI += 0.0433 * z.real
|
|
|
|
# AddSol(191.953000, 192.720000, 0.029000, 3.086100, 1.000000, 0.000000, 0.000000, 2.000000)
|
|
z = ex[1][1] * ex[2][4]
|
|
DLAM += 191.953 * z.imag
|
|
DS += 192.72 * z.imag
|
|
GAM1C += 0.029 * z.real
|
|
SINPI += 3.0861 * z.real
|
|
|
|
# AddSol(-8.466000, -13.510000, 0.455000, -0.109300, 1.000000, 0.000000, 0.000000, 1.000000)
|
|
z = ex[1][1] * ex[1][4]
|
|
DLAM += -8.466 * z.imag
|
|
DS += -13.51 * z.imag
|
|
GAM1C += 0.455 * z.real
|
|
SINPI += -0.1093 * z.real
|
|
|
|
# AddSol(22639.500000, 22609.070000, 0.079000, 186.539800, 1.000000, 0.000000, 0.000000, 0.000000)
|
|
z = ex[1][1]
|
|
DLAM += 22639.500 * z.imag
|
|
DS += 22609.07 * z.imag
|
|
GAM1C += 0.079 * z.real
|
|
SINPI += 186.5398 * z.real
|
|
|
|
# AddSol(18.609000, 3.590000, -0.094000, 0.011800, 1.000000, 0.000000, 0.000000, -1.000000)
|
|
z = ex[1][1] * ex[-1][4]
|
|
DLAM += 18.609 * z.imag
|
|
DS += 3.59 * z.imag
|
|
GAM1C += -0.094 * z.real
|
|
SINPI += 0.0118 * z.real
|
|
|
|
# AddSol(-4586.465000, -4578.130000, -0.077000, 34.311700, 1.000000, 0.000000, 0.000000, -2.000000)
|
|
z = ex[1][1] * ex[-2][4]
|
|
DLAM += -4586.465 * z.imag
|
|
DS += -4578.13 * z.imag
|
|
GAM1C += -0.077 * z.real
|
|
SINPI += 34.3117 * z.real
|
|
|
|
# AddSol(3.215000, 5.440000, 0.192000, -0.038600, 1.000000, 0.000000, 0.000000, -3.000000)
|
|
z = ex[1][1] * ex[-3][4]
|
|
DLAM += 3.215 * z.imag
|
|
DS += 5.44 * z.imag
|
|
GAM1C += 0.192 * z.real
|
|
SINPI += -0.0386 * z.real
|
|
|
|
# AddSol(-38.428000, -38.640000, 0.001000, 0.600800, 1.000000, 0.000000, 0.000000, -4.000000)
|
|
z = ex[1][1] * ex[-4][4]
|
|
DLAM += -38.428 * z.imag
|
|
DS += -38.64 * z.imag
|
|
GAM1C += 0.001 * z.real
|
|
SINPI += 0.6008 * z.real
|
|
|
|
# AddSol(-0.393000, -1.430000, -0.092000, 0.008600, 1.000000, 0.000000, 0.000000, -6.000000)
|
|
z = ex[1][1] * ex[-6][4]
|
|
DLAM += -0.393 * z.imag
|
|
DS += -1.43 * z.imag
|
|
GAM1C += -0.092 * z.real
|
|
SINPI += 0.0086 * z.real
|
|
|
|
# AddSol(-0.289000, -1.590000, 0.123000, -0.005300, 0.000000, 1.000000, 0.000000, 4.000000)
|
|
z = ex[1][2] * ex[4][4]
|
|
DLAM += -0.289 * z.imag
|
|
DS += -1.59 * z.imag
|
|
GAM1C += 0.123 * z.real
|
|
SINPI += -0.0053 * z.real
|
|
|
|
# AddSol(-24.420000, -25.100000, 0.040000, -0.300000, 0.000000, 1.000000, 0.000000, 2.000000)
|
|
z = ex[1][2] * ex[2][4]
|
|
DLAM += -24.420 * z.imag
|
|
DS += -25.10 * z.imag
|
|
GAM1C += 0.040 * z.real
|
|
SINPI += -0.3000 * z.real
|
|
|
|
# AddSol(18.023000, 17.930000, 0.007000, 0.149400, 0.000000, 1.000000, 0.000000, 1.000000)
|
|
z = ex[1][2] * ex[1][4]
|
|
DLAM += 18.023 * z.imag
|
|
DS += 17.93 * z.imag
|
|
GAM1C += 0.007 * z.real
|
|
SINPI += 0.1494 * z.real
|
|
|
|
# AddSol(-668.146000, -126.980000, -1.302000, -0.399700, 0.000000, 1.000000, 0.000000, 0.000000)
|
|
z = ex[1][2]
|
|
DLAM += -668.146 * z.imag
|
|
DS += -126.98 * z.imag
|
|
GAM1C += -1.302 * z.real
|
|
SINPI += -0.3997 * z.real
|
|
|
|
# AddSol(0.560000, 0.320000, -0.001000, -0.003700, 0.000000, 1.000000, 0.000000, -1.000000)
|
|
z = ex[1][2] * ex[-1][4]
|
|
DLAM += 0.560 * z.imag
|
|
DS += 0.32 * z.imag
|
|
GAM1C += -0.001 * z.real
|
|
SINPI += -0.0037 * z.real
|
|
|
|
# AddSol(-165.145000, -165.060000, 0.054000, 1.917800, 0.000000, 1.000000, 0.000000, -2.000000)
|
|
z = ex[1][2] * ex[-2][4]
|
|
DLAM += -165.145 * z.imag
|
|
DS += -165.06 * z.imag
|
|
GAM1C += 0.054 * z.real
|
|
SINPI += 1.9178 * z.real
|
|
|
|
# AddSol(-1.877000, -6.460000, -0.416000, 0.033900, 0.000000, 1.000000, 0.000000, -4.000000)
|
|
z = ex[1][2] * ex[-4][4]
|
|
DLAM += -1.877 * z.imag
|
|
DS += -6.46 * z.imag
|
|
GAM1C += -0.416 * z.real
|
|
SINPI += 0.0339 * z.real
|
|
|
|
# AddSol(0.213000, 1.020000, -0.074000, 0.005400, 2.000000, 0.000000, 0.000000, 4.000000)
|
|
z = ex[2][1] * ex[4][4]
|
|
DLAM += 0.213 * z.imag
|
|
DS += 1.02 * z.imag
|
|
GAM1C += -0.074 * z.real
|
|
SINPI += 0.0054 * z.real
|
|
|
|
# AddSol(14.387000, 14.780000, -0.017000, 0.283300, 2.000000, 0.000000, 0.000000, 2.000000)
|
|
z = ex[2][1] * ex[2][4]
|
|
DLAM += 14.387 * z.imag
|
|
DS += 14.78 * z.imag
|
|
GAM1C += -0.017 * z.real
|
|
SINPI += 0.2833 * z.real
|
|
|
|
# AddSol(-0.586000, -1.200000, 0.054000, -0.010000, 2.000000, 0.000000, 0.000000, 1.000000)
|
|
z = ex[2][1] * ex[1][4]
|
|
DLAM += -0.586 * z.imag
|
|
DS += -1.20 * z.imag
|
|
GAM1C += 0.054 * z.real
|
|
SINPI += -0.0100 * z.real
|
|
|
|
# AddSol(769.016000, 767.960000, 0.107000, 10.165700, 2.000000, 0.000000, 0.000000, 0.000000)
|
|
z = ex[2][1]
|
|
DLAM += 769.016 * z.imag
|
|
DS += 767.96 * z.imag
|
|
GAM1C += 0.107 * z.real
|
|
SINPI += 10.1657 * z.real
|
|
|
|
# AddSol(1.750000, 2.010000, -0.018000, 0.015500, 2.000000, 0.000000, 0.000000, -1.000000)
|
|
z = ex[2][1] * ex[-1][4]
|
|
DLAM += 1.750 * z.imag
|
|
DS += 2.01 * z.imag
|
|
GAM1C += -0.018 * z.real
|
|
SINPI += 0.0155 * z.real
|
|
|
|
# AddSol(-211.656000, -152.530000, 5.679000, -0.303900, 2.000000, 0.000000, 0.000000, -2.000000)
|
|
z = ex[2][1] * ex[-2][4]
|
|
DLAM += -211.656 * z.imag
|
|
DS += -152.53 * z.imag
|
|
GAM1C += 5.679 * z.real
|
|
SINPI += -0.3039 * z.real
|
|
|
|
# AddSol(1.225000, 0.910000, -0.030000, -0.008800, 2.000000, 0.000000, 0.000000, -3.000000)
|
|
z = ex[2][1] * ex[-3][4]
|
|
DLAM += 1.225 * z.imag
|
|
DS += 0.91 * z.imag
|
|
GAM1C += -0.030 * z.real
|
|
SINPI += -0.0088 * z.real
|
|
|
|
# AddSol(-30.773000, -34.070000, -0.308000, 0.372200, 2.000000, 0.000000, 0.000000, -4.000000)
|
|
z = ex[2][1] * ex[-4][4]
|
|
DLAM += -30.773 * z.imag
|
|
DS += -34.07 * z.imag
|
|
GAM1C += -0.308 * z.real
|
|
SINPI += 0.3722 * z.real
|
|
|
|
# AddSol(-0.570000, -1.400000, -0.074000, 0.010900, 2.000000, 0.000000, 0.000000, -6.000000)
|
|
z = ex[2][1] * ex[-6][4]
|
|
DLAM += -0.570 * z.imag
|
|
DS += -1.40 * z.imag
|
|
GAM1C += -0.074 * z.real
|
|
SINPI += 0.0109 * z.real
|
|
|
|
# AddSol(-2.921000, -11.750000, 0.787000, -0.048400, 1.000000, 1.000000, 0.000000, 2.000000)
|
|
z = ex[1][1] * ex[1][2] * ex[2][4]
|
|
DLAM += -2.921 * z.imag
|
|
DS += -11.75 * z.imag
|
|
GAM1C += 0.787 * z.real
|
|
SINPI += -0.0484 * z.real
|
|
|
|
# AddSol(1.267000, 1.520000, -0.022000, 0.016400, 1.000000, 1.000000, 0.000000, 1.000000)
|
|
z = ex[1][1] * ex[1][2] * ex[1][4]
|
|
DLAM += 1.267 * z.imag
|
|
DS += 1.52 * z.imag
|
|
GAM1C += -0.022 * z.real
|
|
SINPI += 0.0164 * z.real
|
|
|
|
# AddSol(-109.673000, -115.180000, 0.461000, -0.949000, 1.000000, 1.000000, 0.000000, 0.000000)
|
|
z = ex[1][1] * ex[1][2]
|
|
DLAM += -109.673 * z.imag
|
|
DS += -115.18 * z.imag
|
|
GAM1C += 0.461 * z.real
|
|
SINPI += -0.9490 * z.real
|
|
|
|
# AddSol(-205.962000, -182.360000, 2.056000, 1.443700, 1.000000, 1.000000, 0.000000, -2.000000)
|
|
z = ex[1][1] * ex[1][2] * ex[-2][4]
|
|
DLAM += -205.962 * z.imag
|
|
DS += -182.36 * z.imag
|
|
GAM1C += 2.056 * z.real
|
|
SINPI += 1.4437 * z.real
|
|
|
|
# AddSol(0.233000, 0.360000, 0.012000, -0.002500, 1.000000, 1.000000, 0.000000, -3.000000)
|
|
z = ex[1][1] * ex[1][2] * ex[-3][4]
|
|
DLAM += 0.233 * z.imag
|
|
DS += 0.36 * z.imag
|
|
GAM1C += 0.012 * z.real
|
|
SINPI += -0.0025 * z.real
|
|
|
|
# AddSol(-4.391000, -9.660000, -0.471000, 0.067300, 1.000000, 1.000000, 0.000000, -4.000000)
|
|
z = ex[1][1] * ex[1][2] * ex[-4][4]
|
|
DLAM += -4.391 * z.imag
|
|
DS += -9.66 * z.imag
|
|
GAM1C += -0.471 * z.real
|
|
SINPI += 0.0673 * z.real
|
|
|
|
# AddSol(0.283000, 1.530000, -0.111000, 0.006000, 1.000000, -1.000000, 0.000000, 4.000000)
|
|
z = ex[1][1] * ex[-1][2] * ex[4][4]
|
|
DLAM += 0.283 * z.imag
|
|
DS += 1.53 * z.imag
|
|
GAM1C += -0.111 * z.real
|
|
SINPI += 0.0060 * z.real
|
|
|
|
# AddSol(14.577000, 31.700000, -1.540000, 0.230200, 1.000000, -1.000000, 0.000000, 2.000000)
|
|
z = ex[1][1] * ex[-1][2] * ex[2][4]
|
|
DLAM += 14.577 * z.imag
|
|
DS += 31.70 * z.imag
|
|
GAM1C += -1.540 * z.real
|
|
SINPI += 0.2302 * z.real
|
|
|
|
# AddSol(147.687000, 138.760000, 0.679000, 1.152800, 1.000000, -1.000000, 0.000000, 0.000000)
|
|
z = ex[1][1] * ex[-1][2]
|
|
DLAM += 147.687 * z.imag
|
|
DS += 138.76 * z.imag
|
|
GAM1C += 0.679 * z.real
|
|
SINPI += 1.1528 * z.real
|
|
|
|
# AddSol(-1.089000, 0.550000, 0.021000, 0.000000, 1.000000, -1.000000, 0.000000, -1.000000)
|
|
z = ex[1][1] * ex[-1][2] * ex[-1][4]
|
|
DLAM += -1.089 * z.imag
|
|
DS += 0.55 * z.imag
|
|
GAM1C += 0.021 * z.real
|
|
|
|
# AddSol(28.475000, 23.590000, -0.443000, -0.225700, 1.000000, -1.000000, 0.000000, -2.000000)
|
|
z = ex[1][1] * ex[-1][2] * ex[-2][4]
|
|
DLAM += 28.475 * z.imag
|
|
DS += 23.59 * z.imag
|
|
GAM1C += -0.443 * z.real
|
|
SINPI += -0.2257 * z.real
|
|
|
|
# AddSol(-0.276000, -0.380000, -0.006000, -0.003600, 1.000000, -1.000000, 0.000000, -3.000000)
|
|
z = ex[1][1] * ex[-1][2] * ex[-3][4]
|
|
DLAM += -0.276 * z.imag
|
|
DS += -0.38 * z.imag
|
|
GAM1C += -0.006 * z.real
|
|
SINPI += -0.0036 * z.real
|
|
|
|
# AddSol(0.636000, 2.270000, 0.146000, -0.010200, 1.000000, -1.000000, 0.000000, -4.000000)
|
|
z = ex[1][1] * ex[-1][2] * ex[-4][4]
|
|
DLAM += 0.636 * z.imag
|
|
DS += 2.27 * z.imag
|
|
GAM1C += 0.146 * z.real
|
|
SINPI += -0.0102 * z.real
|
|
|
|
# AddSol(-0.189000, -1.680000, 0.131000, -0.002800, 0.000000, 2.000000, 0.000000, 2.000000)
|
|
z = ex[2][2] * ex[2][4]
|
|
DLAM += -0.189 * z.imag
|
|
DS += -1.68 * z.imag
|
|
GAM1C += 0.131 * z.real
|
|
SINPI += -0.0028 * z.real
|
|
|
|
# AddSol(-7.486000, -0.660000, -0.037000, -0.008600, 0.000000, 2.000000, 0.000000, 0.000000)
|
|
z = ex[2][2]
|
|
DLAM += -7.486 * z.imag
|
|
DS += -0.66 * z.imag
|
|
GAM1C += -0.037 * z.real
|
|
SINPI += -0.0086 * z.real
|
|
|
|
# AddSol(-8.096000, -16.350000, -0.740000, 0.091800, 0.000000, 2.000000, 0.000000, -2.000000)
|
|
z = ex[2][2] * ex[-2][4]
|
|
DLAM += -8.096 * z.imag
|
|
DS += -16.35 * z.imag
|
|
GAM1C += -0.740 * z.real
|
|
SINPI += 0.0918 * z.real
|
|
|
|
# AddSol(-5.741000, -0.040000, 0.000000, -0.000900, 0.000000, 0.000000, 2.000000, 2.000000)
|
|
z = ex[2][3] * ex[2][4]
|
|
DLAM += -5.741 * z.imag
|
|
DS += -0.04 * z.imag
|
|
SINPI += -0.0009 * z.real
|
|
|
|
# AddSol(0.255000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 2.000000, 1.000000)
|
|
z = ex[2][3] * ex[1][4]
|
|
DLAM += 0.255 * z.imag
|
|
|
|
# AddSol(-411.608000, -0.200000, 0.000000, -0.012400, 0.000000, 0.000000, 2.000000, 0.000000)
|
|
z = ex[2][3]
|
|
DLAM += -411.608 * z.imag
|
|
DS += -0.20 * z.imag
|
|
SINPI += -0.0124 * z.real
|
|
|
|
# AddSol(0.584000, 0.840000, 0.000000, 0.007100, 0.000000, 0.000000, 2.000000, -1.000000)
|
|
z = ex[2][3] * ex[-1][4]
|
|
DLAM += 0.584 * z.imag
|
|
DS += 0.84 * z.imag
|
|
SINPI += 0.0071 * z.real
|
|
|
|
# AddSol(-55.173000, -52.140000, 0.000000, -0.105200, 0.000000, 0.000000, 2.000000, -2.000000)
|
|
z = ex[2][3] * ex[-2][4]
|
|
DLAM += -55.173 * z.imag
|
|
DS += -52.14 * z.imag
|
|
SINPI += -0.1052 * z.real
|
|
|
|
# AddSol(0.254000, 0.250000, 0.000000, -0.001700, 0.000000, 0.000000, 2.000000, -3.000000)
|
|
z = ex[2][3] * ex[-3][4]
|
|
DLAM += 0.254 * z.imag
|
|
DS += 0.25 * z.imag
|
|
SINPI += -0.0017 * z.real
|
|
|
|
# AddSol(0.025000, -1.670000, 0.000000, 0.003100, 0.000000, 0.000000, 2.000000, -4.000000)
|
|
z = ex[2][3] * ex[-4][4]
|
|
DLAM += 0.025 * z.imag
|
|
DS += -1.67 * z.imag
|
|
SINPI += 0.0031 * z.real
|
|
|
|
# AddSol(1.060000, 2.960000, -0.166000, 0.024300, 3.000000, 0.000000, 0.000000, 2.000000)
|
|
z = ex[3][1] * ex[2][4]
|
|
DLAM += 1.060 * z.imag
|
|
DS += 2.96 * z.imag
|
|
GAM1C += -0.166 * z.real
|
|
SINPI += 0.0243 * z.real
|
|
|
|
# AddSol(36.124000, 50.640000, -1.300000, 0.621500, 3.000000, 0.000000, 0.000000, 0.000000)
|
|
z = ex[3][1]
|
|
DLAM += 36.124 * z.imag
|
|
DS += 50.64 * z.imag
|
|
GAM1C += -1.300 * z.real
|
|
SINPI += 0.6215 * z.real
|
|
|
|
# AddSol(-13.193000, -16.400000, 0.258000, -0.118700, 3.000000, 0.000000, 0.000000, -2.000000)
|
|
z = ex[3][1] * ex[-2][4]
|
|
DLAM += -13.193 * z.imag
|
|
DS += -16.40 * z.imag
|
|
GAM1C += 0.258 * z.real
|
|
SINPI += -0.1187 * z.real
|
|
|
|
# AddSol(-1.187000, -0.740000, 0.042000, 0.007400, 3.000000, 0.000000, 0.000000, -4.000000)
|
|
z = ex[3][1] * ex[-4][4]
|
|
DLAM += -1.187 * z.imag
|
|
DS += -0.74 * z.imag
|
|
GAM1C += 0.042 * z.real
|
|
SINPI += 0.0074 * z.real
|
|
|
|
# AddSol(-0.293000, -0.310000, -0.002000, 0.004600, 3.000000, 0.000000, 0.000000, -6.000000)
|
|
z = ex[3][1] * ex[-6][4]
|
|
DLAM += -0.293 * z.imag
|
|
DS += -0.31 * z.imag
|
|
GAM1C += -0.002 * z.real
|
|
SINPI += 0.0046 * z.real
|
|
|
|
# AddSol(-0.290000, -1.450000, 0.116000, -0.005100, 2.000000, 1.000000, 0.000000, 2.000000)
|
|
z = ex[2][1] * ex[1][2] * ex[2][4]
|
|
DLAM += -0.290 * z.imag
|
|
DS += -1.45 * z.imag
|
|
GAM1C += 0.116 * z.real
|
|
SINPI += -0.0051 * z.real
|
|
|
|
# AddSol(-7.649000, -10.560000, 0.259000, -0.103800, 2.000000, 1.000000, 0.000000, 0.000000)
|
|
z = ex[2][1] * ex[1][2]
|
|
DLAM += -7.649 * z.imag
|
|
DS += -10.56 * z.imag
|
|
GAM1C += 0.259 * z.real
|
|
SINPI += -0.1038 * z.real
|
|
|
|
# AddSol(-8.627000, -7.590000, 0.078000, -0.019200, 2.000000, 1.000000, 0.000000, -2.000000)
|
|
z = ex[2][1] * ex[1][2] * ex[-2][4]
|
|
DLAM += -8.627 * z.imag
|
|
DS += -7.59 * z.imag
|
|
GAM1C += 0.078 * z.real
|
|
SINPI += -0.0192 * z.real
|
|
|
|
# AddSol(-2.740000, -2.540000, 0.022000, 0.032400, 2.000000, 1.000000, 0.000000, -4.000000)
|
|
z = ex[2][1] * ex[1][2] * ex[-4][4]
|
|
DLAM += -2.740 * z.imag
|
|
DS += -2.54 * z.imag
|
|
GAM1C += 0.022 * z.real
|
|
SINPI += 0.0324 * z.real
|
|
|
|
# AddSol(1.181000, 3.320000, -0.212000, 0.021300, 2.000000, -1.000000, 0.000000, 2.000000)
|
|
z = ex[2][1] * ex[-1][2] * ex[2][4]
|
|
DLAM += 1.181 * z.imag
|
|
DS += 3.32 * z.imag
|
|
GAM1C += -0.212 * z.real
|
|
SINPI += 0.0213 * z.real
|
|
|
|
# AddSol(9.703000, 11.670000, -0.151000, 0.126800, 2.000000, -1.000000, 0.000000, 0.000000)
|
|
z = ex[2][1] * ex[-1][2]
|
|
DLAM += 9.703 * z.imag
|
|
DS += 11.67 * z.imag
|
|
GAM1C += -0.151 * z.real
|
|
SINPI += 0.1268 * z.real
|
|
|
|
# AddSol(-0.352000, -0.370000, 0.001000, -0.002800, 2.000000, -1.000000, 0.000000, -1.000000)
|
|
z = ex[2][1] * ex[-1][2] * ex[-1][4]
|
|
DLAM += -0.352 * z.imag
|
|
DS += -0.37 * z.imag
|
|
GAM1C += 0.001 * z.real
|
|
SINPI += -0.0028 * z.real
|
|
|
|
# AddSol(-2.494000, -1.170000, -0.003000, -0.001700, 2.000000, -1.000000, 0.000000, -2.000000)
|
|
z = ex[2][1] * ex[-1][2] * ex[-2][4]
|
|
DLAM += -2.494 * z.imag
|
|
DS += -1.17 * z.imag
|
|
GAM1C += -0.003 * z.real
|
|
SINPI += -0.0017 * z.real
|
|
|
|
# AddSol(0.360000, 0.200000, -0.012000, -0.004300, 2.000000, -1.000000, 0.000000, -4.000000)
|
|
z = ex[2][1] * ex[-1][2] * ex[-4][4]
|
|
DLAM += 0.360 * z.imag
|
|
DS += 0.20 * z.imag
|
|
GAM1C += -0.012 * z.real
|
|
SINPI += -0.0043 * z.real
|
|
|
|
# AddSol(-1.167000, -1.250000, 0.008000, -0.010600, 1.000000, 2.000000, 0.000000, 0.000000)
|
|
z = ex[1][1] * ex[2][2]
|
|
DLAM += -1.167 * z.imag
|
|
DS += -1.25 * z.imag
|
|
GAM1C += 0.008 * z.real
|
|
SINPI += -0.0106 * z.real
|
|
|
|
# AddSol(-7.412000, -6.120000, 0.117000, 0.048400, 1.000000, 2.000000, 0.000000, -2.000000)
|
|
z = ex[1][1] * ex[2][2] * ex[-2][4]
|
|
DLAM += -7.412 * z.imag
|
|
DS += -6.12 * z.imag
|
|
GAM1C += 0.117 * z.real
|
|
SINPI += 0.0484 * z.real
|
|
|
|
# AddSol(-0.311000, -0.650000, -0.032000, 0.004400, 1.000000, 2.000000, 0.000000, -4.000000)
|
|
z = ex[1][1] * ex[2][2] * ex[-4][4]
|
|
DLAM += -0.311 * z.imag
|
|
DS += -0.65 * z.imag
|
|
GAM1C += -0.032 * z.real
|
|
SINPI += 0.0044 * z.real
|
|
|
|
# AddSol(0.757000, 1.820000, -0.105000, 0.011200, 1.000000, -2.000000, 0.000000, 2.000000)
|
|
z = ex[1][1] * ex[-2][2] * ex[2][4]
|
|
DLAM += 0.757 * z.imag
|
|
DS += 1.82 * z.imag
|
|
GAM1C += -0.105 * z.real
|
|
SINPI += 0.0112 * z.real
|
|
|
|
# AddSol(2.580000, 2.320000, 0.027000, 0.019600, 1.000000, -2.000000, 0.000000, 0.000000)
|
|
z = ex[1][1] * ex[-2][2]
|
|
DLAM += 2.580 * z.imag
|
|
DS += 2.32 * z.imag
|
|
GAM1C += 0.027 * z.real
|
|
SINPI += 0.0196 * z.real
|
|
|
|
# AddSol(2.533000, 2.400000, -0.014000, -0.021200, 1.000000, -2.000000, 0.000000, -2.000000)
|
|
z = ex[1][1] * ex[-2][2] * ex[-2][4]
|
|
DLAM += 2.533 * z.imag
|
|
DS += 2.40 * z.imag
|
|
GAM1C += -0.014 * z.real
|
|
SINPI += -0.0212 * z.real
|
|
|
|
# AddSol(-0.344000, -0.570000, -0.025000, 0.003600, 0.000000, 3.000000, 0.000000, -2.000000)
|
|
z = ex[3][2] * ex[-2][4]
|
|
DLAM += -0.344 * z.imag
|
|
DS += -0.57 * z.imag
|
|
GAM1C += -0.025 * z.real
|
|
SINPI += 0.0036 * z.real
|
|
|
|
# AddSol(-0.992000, -0.020000, 0.000000, 0.000000, 1.000000, 0.000000, 2.000000, 2.000000)
|
|
z = ex[1][1] * ex[2][3] * ex[2][4]
|
|
DLAM += -0.992 * z.imag
|
|
DS += -0.02 * z.imag
|
|
|
|
# AddSol(-45.099000, -0.020000, 0.000000, -0.001000, 1.000000, 0.000000, 2.000000, 0.000000)
|
|
z = ex[1][1] * ex[2][3]
|
|
DLAM += -45.099 * z.imag
|
|
DS += -0.02 * z.imag
|
|
SINPI += -0.0010 * z.real
|
|
|
|
# AddSol(-0.179000, -9.520000, 0.000000, -0.083300, 1.000000, 0.000000, 2.000000, -2.000000)
|
|
z = ex[1][1] * ex[2][3] * ex[-2][4]
|
|
DLAM += -0.179 * z.imag
|
|
DS += -9.52 * z.imag
|
|
SINPI += -0.0833 * z.real
|
|
|
|
# AddSol(-0.301000, -0.330000, 0.000000, 0.001400, 1.000000, 0.000000, 2.000000, -4.000000)
|
|
z = ex[1][1] * ex[2][3] * ex[-4][4]
|
|
DLAM += -0.301 * z.imag
|
|
DS += -0.33 * z.imag
|
|
SINPI += 0.0014 * z.real
|
|
|
|
# AddSol(-6.382000, -3.370000, 0.000000, -0.048100, 1.000000, 0.000000, -2.000000, 2.000000)
|
|
z = ex[1][1] * ex[-2][3] * ex[2][4]
|
|
DLAM += -6.382 * z.imag
|
|
DS += -3.37 * z.imag
|
|
SINPI += -0.0481 * z.real
|
|
|
|
# AddSol(39.528000, 85.130000, 0.000000, -0.713600, 1.000000, 0.000000, -2.000000, 0.000000)
|
|
z = ex[1][1] * ex[-2][3]
|
|
DLAM += 39.528 * z.imag
|
|
DS += 85.13 * z.imag
|
|
SINPI += -0.7136 * z.real
|
|
|
|
# AddSol(9.366000, 0.710000, 0.000000, -0.011200, 1.000000, 0.000000, -2.000000, -2.000000)
|
|
z = ex[1][1] * ex[-2][3] * ex[-2][4]
|
|
DLAM += 9.366 * z.imag
|
|
DS += 0.71 * z.imag
|
|
SINPI += -0.0112 * z.real
|
|
|
|
# AddSol(0.202000, 0.020000, 0.000000, 0.000000, 1.000000, 0.000000, -2.000000, -4.000000)
|
|
z = ex[1][1] * ex[-2][3] * ex[-4][4]
|
|
DLAM += 0.202 * z.imag
|
|
DS += 0.02 * z.imag
|
|
|
|
# AddSol(0.415000, 0.100000, 0.000000, 0.001300, 0.000000, 1.000000, 2.000000, 0.000000)
|
|
z = ex[1][2] * ex[2][3]
|
|
DLAM += 0.415 * z.imag
|
|
DS += 0.10 * z.imag
|
|
SINPI += 0.0013 * z.real
|
|
|
|
# AddSol(-2.152000, -2.260000, 0.000000, -0.006600, 0.000000, 1.000000, 2.000000, -2.000000)
|
|
z = ex[1][2] * ex[2][3] * ex[-2][4]
|
|
DLAM += -2.152 * z.imag
|
|
DS += -2.26 * z.imag
|
|
SINPI += -0.0066 * z.real
|
|
|
|
# AddSol(-1.440000, -1.300000, 0.000000, 0.001400, 0.000000, 1.000000, -2.000000, 2.000000)
|
|
z = ex[1][2] * ex[-2][3] * ex[2][4]
|
|
DLAM += -1.440 * z.imag
|
|
DS += -1.30 * z.imag
|
|
SINPI += 0.0014 * z.real
|
|
|
|
# AddSol(0.384000, -0.040000, 0.000000, 0.000000, 0.000000, 1.000000, -2.000000, -2.000000)
|
|
z = ex[1][2] * ex[-2][3] * ex[-2][4]
|
|
DLAM += 0.384 * z.imag
|
|
DS += -0.04 * z.imag
|
|
|
|
# AddSol(1.938000, 3.600000, -0.145000, 0.040100, 4.000000, 0.000000, 0.000000, 0.000000)
|
|
z = ex[4][1]
|
|
DLAM += 1.938 * z.imag
|
|
DS += 3.60 * z.imag
|
|
GAM1C += -0.145 * z.real
|
|
SINPI += 0.0401 * z.real
|
|
|
|
# AddSol(-0.952000, -1.580000, 0.052000, -0.013000, 4.000000, 0.000000, 0.000000, -2.000000)
|
|
z = ex[4][1] * ex[-2][4]
|
|
DLAM += -0.952 * z.imag
|
|
DS += -1.58 * z.imag
|
|
GAM1C += 0.052 * z.real
|
|
SINPI += -0.0130 * z.real
|
|
|
|
# AddSol(-0.551000, -0.940000, 0.032000, -0.009700, 3.000000, 1.000000, 0.000000, 0.000000)
|
|
z = ex[3][1] * ex[1][2]
|
|
DLAM += -0.551 * z.imag
|
|
DS += -0.94 * z.imag
|
|
GAM1C += 0.032 * z.real
|
|
SINPI += -0.0097 * z.real
|
|
|
|
# AddSol(-0.482000, -0.570000, 0.005000, -0.004500, 3.000000, 1.000000, 0.000000, -2.000000)
|
|
z = ex[3][1] * ex[1][2] * ex[-2][4]
|
|
DLAM += -0.482 * z.imag
|
|
DS += -0.57 * z.imag
|
|
GAM1C += 0.005 * z.real
|
|
SINPI += -0.0045 * z.real
|
|
|
|
# AddSol(0.681000, 0.960000, -0.026000, 0.011500, 3.000000, -1.000000, 0.000000, 0.000000)
|
|
z = ex[3][1] * ex[-1][2]
|
|
DLAM += 0.681 * z.imag
|
|
DS += 0.96 * z.imag
|
|
GAM1C += -0.026 * z.real
|
|
SINPI += 0.0115 * z.real
|
|
|
|
# AddSol(-0.297000, -0.270000, 0.002000, -0.000900, 2.000000, 2.000000, 0.000000, -2.000000)
|
|
z = ex[2][1] * ex[2][2] * ex[-2][4]
|
|
DLAM += -0.297 * z.imag
|
|
DS += -0.27 * z.imag
|
|
GAM1C += 0.002 * z.real
|
|
SINPI += -0.0009 * z.real
|
|
|
|
# AddSol(0.254000, 0.210000, -0.003000, 0.000000, 2.000000, -2.000000, 0.000000, -2.000000)
|
|
z = ex[2][1] * ex[-2][2] * ex[-2][4]
|
|
DLAM += 0.254 * z.imag
|
|
DS += 0.21 * z.imag
|
|
GAM1C += -0.003 * z.real
|
|
|
|
# AddSol(-0.250000, -0.220000, 0.004000, 0.001400, 1.000000, 3.000000, 0.000000, -2.000000)
|
|
z = ex[1][1] * ex[3][2] * ex[-2][4]
|
|
DLAM += -0.250 * z.imag
|
|
DS += -0.22 * z.imag
|
|
GAM1C += 0.004 * z.real
|
|
SINPI += 0.0014 * z.real
|
|
|
|
# AddSol(-3.996000, 0.000000, 0.000000, 0.000400, 2.000000, 0.000000, 2.000000, 0.000000)
|
|
z = ex[2][1] * ex[2][3]
|
|
DLAM += -3.996 * z.imag
|
|
SINPI += 0.0004 * z.real
|
|
|
|
# AddSol(0.557000, -0.750000, 0.000000, -0.009000, 2.000000, 0.000000, 2.000000, -2.000000)
|
|
z = ex[2][1] * ex[2][3] * ex[-2][4]
|
|
DLAM += 0.557 * z.imag
|
|
DS += -0.75 * z.imag
|
|
SINPI += -0.0090 * z.real
|
|
|
|
# AddSol(-0.459000, -0.380000, 0.000000, -0.005300, 2.000000, 0.000000, -2.000000, 2.000000)
|
|
z = ex[2][1] * ex[-2][3] * ex[2][4]
|
|
DLAM += -0.459 * z.imag
|
|
DS += -0.38 * z.imag
|
|
SINPI += -0.0053 * z.real
|
|
|
|
# AddSol(-1.298000, 0.740000, 0.000000, 0.000400, 2.000000, 0.000000, -2.000000, 0.000000)
|
|
z = ex[2][1] * ex[-2][3]
|
|
DLAM += -1.298 * z.imag
|
|
DS += 0.74 * z.imag
|
|
SINPI += 0.0004 * z.real
|
|
|
|
# AddSol(0.538000, 1.140000, 0.000000, -0.014100, 2.000000, 0.000000, -2.000000, -2.000000)
|
|
z = ex[2][1] * ex[-2][3] * ex[-2][4]
|
|
DLAM += 0.538 * z.imag
|
|
DS += 1.14 * z.imag
|
|
SINPI += -0.0141 * z.real
|
|
|
|
# AddSol(0.263000, 0.020000, 0.000000, 0.000000, 1.000000, 1.000000, 2.000000, 0.000000)
|
|
z = ex[1][1] * ex[1][2] * ex[2][3]
|
|
DLAM += 0.263 * z.imag
|
|
DS += 0.02 * z.imag
|
|
|
|
# AddSol(0.426000, 0.070000, 0.000000, -0.000600, 1.000000, 1.000000, -2.000000, -2.000000)
|
|
z = ex[1][1] * ex[1][2] * ex[-2][3] * ex[-2][4]
|
|
DLAM += 0.426 * z.imag
|
|
DS += 0.07 * z.imag
|
|
SINPI += -0.0006 * z.real
|
|
|
|
# AddSol(-0.304000, 0.030000, 0.000000, 0.000300, 1.000000, -1.000000, 2.000000, 0.000000)
|
|
z = ex[1][1] * ex[-1][2] * ex[2][3]
|
|
DLAM += -0.304 * z.imag
|
|
DS += 0.03 * z.imag
|
|
SINPI += 0.0003 * z.real
|
|
|
|
# AddSol(-0.372000, -0.190000, 0.000000, -0.002700, 1.000000, -1.000000, -2.000000, 2.000000)
|
|
z = ex[1][1] * ex[-1][2] * ex[-2][3] * ex[2][4]
|
|
DLAM += -0.372 * z.imag
|
|
DS += -0.19 * z.imag
|
|
SINPI += -0.0027 * z.real
|
|
|
|
# AddSol(0.418000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 4.000000, 0.000000)
|
|
z = ex[4][3]
|
|
DLAM += 0.418 * z.imag
|
|
|
|
# AddSol(-0.330000, -0.040000, 0.000000, 0.000000, 3.000000, 0.000000, 2.000000, 0.000000)
|
|
z = ex[3][1] * ex[2][3]
|
|
DLAM += -0.330 * z.imag
|
|
DS += -0.04 * z.imag
|
|
|
|
|
|
def ADDN(coeffn, p, q, r, s):
|
|
return coeffn * (ex[p][1] * ex[q][2] * ex[r][3] * ex[s][4]).imag
|
|
|
|
N = 0
|
|
N += ADDN(-526.069, 0, 0,1,-2)
|
|
N += ADDN( -3.352, 0, 0,1,-4)
|
|
N += ADDN( +44.297,+1, 0,1,-2)
|
|
N += ADDN( -6.000,+1, 0,1,-4)
|
|
N += ADDN( +20.599,-1, 0,1, 0)
|
|
N += ADDN( -30.598,-1, 0,1,-2)
|
|
N += ADDN( -24.649,-2, 0,1, 0)
|
|
N += ADDN( -2.000,-2, 0,1,-2)
|
|
N += ADDN( -22.571, 0,+1,1,-2)
|
|
N += ADDN( +10.985, 0,-1,1,-2)
|
|
|
|
DLAM += (
|
|
+0.82*Sine(0.7736 -62.5512*T)+0.31*Sine(0.0466 -125.1025*T)
|
|
+0.35*Sine(0.5785 -25.1042*T)+0.66*Sine(0.4591+1335.8075*T)
|
|
+0.64*Sine(0.3130 -91.5680*T)+1.14*Sine(0.1480+1331.2898*T)
|
|
+0.21*Sine(0.5918+1056.5859*T)+0.44*Sine(0.5784+1322.8595*T)
|
|
+0.24*Sine(0.2275 -5.7374*T)+0.28*Sine(0.2965 +2.6929*T)
|
|
+0.33*Sine(0.3132 +6.3368*T)
|
|
)
|
|
S = F + DS/_ARC
|
|
lat_seconds = (1.000002708 + 139.978*DGAM)*(18518.511+1.189+GAM1C)*math.sin(S) - 6.24*math.sin(3*S) + N
|
|
return _moonpos(
|
|
_PI2 * Frac((L0+DLAM/_ARC) / _PI2),
|
|
(math.pi / (180 * 3600)) * lat_seconds,
|
|
(_ARC * (_ERAD / _AU)) / (0.999953253 * SINPI)
|
|
)
|
|
|
|
def GeoMoon(time):
|
|
"""Calculates the geocentric position of the Moon at a given time.
|
|
|
|
Given a time of observation, calculates the Moon's position as a vector.
|
|
The vector gives the location of the Moon's center relative to the Earth's center
|
|
with x-, y-, and z-components measured in astronomical units.
|
|
|
|
This algorithm is based on Nautical Almanac Office's *Improved Lunar Ephemeris* of 1954,
|
|
which in turn derives from E. W. Brown's lunar theories from the early twentieth century.
|
|
It is adapted from Turbo Pascal code from the book
|
|
[Astronomy on the Personal Computer](https://www.springer.com/us/book/9783540672210)
|
|
by Montenbruck and Pfleger.
|
|
|
|
Parameters
|
|
----------
|
|
time : Time
|
|
The date and time for which to calculate the Moon's position.
|
|
|
|
Returns
|
|
-------
|
|
Vector
|
|
The Moon's position as a vector in J2000 Cartesian equatorial coordinates.
|
|
|
|
"""
|
|
m = _CalcMoon(time)
|
|
|
|
# Convert geocentric ecliptic spherical coordinates to Cartesian coordinates.
|
|
dist_cos_lat = m.distance_au * math.cos(m.geo_eclip_lat)
|
|
gepos = [
|
|
dist_cos_lat * math.cos(m.geo_eclip_lon),
|
|
dist_cos_lat * math.sin(m.geo_eclip_lon),
|
|
m.distance_au * math.sin(m.geo_eclip_lat)
|
|
]
|
|
|
|
# Convert ecliptic coordinates to equatorial coordinates, both in mean equinox of date.
|
|
mpos1 = _ecl2equ_vec(time, gepos)
|
|
|
|
# Convert from mean equinox of date to J2000.
|
|
mpos2 = _precession(time.tt, mpos1, 0)
|
|
return Vector(mpos2[0], mpos2[1], mpos2[2], time)
|
|
|
|
# END CalcMoon
|
|
#----------------------------------------------------------------------------
|
|
# BEGIN VSOP
|
|
|
|
_vsop = [
|
|
# Mercury
|
|
[
|
|
[
|
|
[
|
|
[4.40250710144, 0.00000000000, 0.00000000000],
|
|
[0.40989414977, 1.48302034195, 26087.90314157420],
|
|
[0.05046294200, 4.47785489551, 52175.80628314840],
|
|
[0.00855346844, 1.16520322459, 78263.70942472259],
|
|
[0.00165590362, 4.11969163423, 104351.61256629678],
|
|
[0.00034561897, 0.77930768443, 130439.51570787099],
|
|
[0.00007583476, 3.71348404924, 156527.41884944518]
|
|
],
|
|
[
|
|
[26087.90313685529, 0.00000000000, 0.00000000000],
|
|
[0.01131199811, 6.21874197797, 26087.90314157420],
|
|
[0.00292242298, 3.04449355541, 52175.80628314840],
|
|
[0.00075775081, 6.08568821653, 78263.70942472259],
|
|
[0.00019676525, 2.80965111777, 104351.61256629678]
|
|
]
|
|
],
|
|
[
|
|
[
|
|
[0.11737528961, 1.98357498767, 26087.90314157420],
|
|
[0.02388076996, 5.03738959686, 52175.80628314840],
|
|
[0.01222839532, 3.14159265359, 0.00000000000],
|
|
[0.00543251810, 1.79644363964, 78263.70942472259],
|
|
[0.00129778770, 4.83232503958, 104351.61256629678],
|
|
[0.00031866927, 1.58088495658, 130439.51570787099],
|
|
[0.00007963301, 4.60972126127, 156527.41884944518]
|
|
],
|
|
[
|
|
[0.00274646065, 3.95008450011, 26087.90314157420],
|
|
[0.00099737713, 3.14159265359, 0.00000000000]
|
|
]
|
|
],
|
|
[
|
|
[
|
|
[0.39528271651, 0.00000000000, 0.00000000000],
|
|
[0.07834131818, 6.19233722598, 26087.90314157420],
|
|
[0.00795525558, 2.95989690104, 52175.80628314840],
|
|
[0.00121281764, 6.01064153797, 78263.70942472259],
|
|
[0.00021921969, 2.77820093972, 104351.61256629678],
|
|
[0.00004354065, 5.82894543774, 130439.51570787099]
|
|
],
|
|
[
|
|
[0.00217347740, 4.65617158665, 26087.90314157420],
|
|
[0.00044141826, 1.42385544001, 52175.80628314840]
|
|
]
|
|
]
|
|
],
|
|
|
|
# Venus
|
|
[
|
|
[
|
|
[
|
|
[3.17614666774, 0.00000000000, 0.00000000000],
|
|
[0.01353968419, 5.59313319619, 10213.28554621100],
|
|
[0.00089891645, 5.30650047764, 20426.57109242200],
|
|
[0.00005477194, 4.41630661466, 7860.41939243920],
|
|
[0.00003455741, 2.69964447820, 11790.62908865880],
|
|
[0.00002372061, 2.99377542079, 3930.20969621960],
|
|
[0.00001317168, 5.18668228402, 26.29831979980],
|
|
[0.00001664146, 4.25018630147, 1577.34354244780],
|
|
[0.00001438387, 4.15745084182, 9683.59458111640],
|
|
[0.00001200521, 6.15357116043, 30639.85663863300]
|
|
],
|
|
[
|
|
[10213.28554621638, 0.00000000000, 0.00000000000],
|
|
[0.00095617813, 2.46406511110, 10213.28554621100],
|
|
[0.00007787201, 0.62478482220, 20426.57109242200]
|
|
]
|
|
],
|
|
[
|
|
[
|
|
[0.05923638472, 0.26702775812, 10213.28554621100],
|
|
[0.00040107978, 1.14737178112, 20426.57109242200],
|
|
[0.00032814918, 3.14159265359, 0.00000000000]
|
|
],
|
|
[
|
|
[0.00287821243, 1.88964962838, 10213.28554621100]
|
|
]
|
|
],
|
|
[
|
|
[
|
|
[0.72334820891, 0.00000000000, 0.00000000000],
|
|
[0.00489824182, 4.02151831717, 10213.28554621100],
|
|
[0.00001658058, 4.90206728031, 20426.57109242200]
|
|
],
|
|
[
|
|
[0.00034551041, 0.89198706276, 10213.28554621100]
|
|
]
|
|
]
|
|
],
|
|
|
|
# Earth
|
|
[
|
|
[
|
|
[
|
|
[1.75347045673, 0.00000000000, 0.00000000000],
|
|
[0.03341656453, 4.66925680415, 6283.07584999140],
|
|
[0.00034894275, 4.62610242189, 12566.15169998280],
|
|
[0.00003417572, 2.82886579754, 3.52311834900],
|
|
[0.00003497056, 2.74411783405, 5753.38488489680],
|
|
[0.00003135899, 3.62767041756, 77713.77146812050],
|
|
[0.00002676218, 4.41808345438, 7860.41939243920],
|
|
[0.00002342691, 6.13516214446, 3930.20969621960],
|
|
[0.00001273165, 2.03709657878, 529.69096509460],
|
|
[0.00001324294, 0.74246341673, 11506.76976979360],
|
|
[0.00000901854, 2.04505446477, 26.29831979980],
|
|
[0.00001199167, 1.10962946234, 1577.34354244780],
|
|
[0.00000857223, 3.50849152283, 398.14900340820],
|
|
[0.00000779786, 1.17882681962, 5223.69391980220],
|
|
[0.00000990250, 5.23268072088, 5884.92684658320],
|
|
[0.00000753141, 2.53339052847, 5507.55323866740],
|
|
[0.00000505267, 4.58292599973, 18849.22754997420],
|
|
[0.00000492392, 4.20505711826, 775.52261132400],
|
|
[0.00000356672, 2.91954114478, 0.06731030280],
|
|
[0.00000284125, 1.89869240932, 796.29800681640],
|
|
[0.00000242879, 0.34481445893, 5486.77784317500],
|
|
[0.00000317087, 5.84901948512, 11790.62908865880],
|
|
[0.00000271112, 0.31486255375, 10977.07880469900],
|
|
[0.00000206217, 4.80646631478, 2544.31441988340],
|
|
[0.00000205478, 1.86953770281, 5573.14280143310],
|
|
[0.00000202318, 2.45767790232, 6069.77675455340],
|
|
[0.00000126225, 1.08295459501, 20.77539549240],
|
|
[0.00000155516, 0.83306084617, 213.29909543800]
|
|
],
|
|
[
|
|
[6283.07584999140, 0.00000000000, 0.00000000000],
|
|
[0.00206058863, 2.67823455808, 6283.07584999140],
|
|
[0.00004303419, 2.63512233481, 12566.15169998280]
|
|
],
|
|
[
|
|
[0.00008721859, 1.07253635559, 6283.07584999140]
|
|
]
|
|
],
|
|
[
|
|
[
|
|
],
|
|
[
|
|
[0.00227777722, 3.41376620530, 6283.07584999140],
|
|
[0.00003805678, 3.37063423795, 12566.15169998280]
|
|
]
|
|
],
|
|
[
|
|
[
|
|
[1.00013988784, 0.00000000000, 0.00000000000],
|
|
[0.01670699632, 3.09846350258, 6283.07584999140],
|
|
[0.00013956024, 3.05524609456, 12566.15169998280],
|
|
[0.00003083720, 5.19846674381, 77713.77146812050],
|
|
[0.00001628463, 1.17387558054, 5753.38488489680],
|
|
[0.00001575572, 2.84685214877, 7860.41939243920],
|
|
[0.00000924799, 5.45292236722, 11506.76976979360],
|
|
[0.00000542439, 4.56409151453, 3930.20969621960],
|
|
[0.00000472110, 3.66100022149, 5884.92684658320]
|
|
],
|
|
[
|
|
[0.00103018607, 1.10748968172, 6283.07584999140],
|
|
[0.00001721238, 1.06442300386, 12566.15169998280]
|
|
],
|
|
[
|
|
[0.00004359385, 5.78455133808, 6283.07584999140]
|
|
]
|
|
]
|
|
],
|
|
|
|
# Mars
|
|
[
|
|
[
|
|
[
|
|
[6.20347711581, 0.00000000000, 0.00000000000],
|
|
[0.18656368093, 5.05037100270, 3340.61242669980],
|
|
[0.01108216816, 5.40099836344, 6681.22485339960],
|
|
[0.00091798406, 5.75478744667, 10021.83728009940],
|
|
[0.00027744987, 5.97049513147, 3.52311834900],
|
|
[0.00010610235, 2.93958560338, 2281.23049651060],
|
|
[0.00012315897, 0.84956094002, 2810.92146160520],
|
|
[0.00008926784, 4.15697846427, 0.01725365220],
|
|
[0.00008715691, 6.11005153139, 13362.44970679920],
|
|
[0.00006797556, 0.36462229657, 398.14900340820],
|
|
[0.00007774872, 3.33968761376, 5621.84292321040],
|
|
[0.00003575078, 1.66186505710, 2544.31441988340],
|
|
[0.00004161108, 0.22814971327, 2942.46342329160],
|
|
[0.00003075252, 0.85696614132, 191.44826611160],
|
|
[0.00002628117, 0.64806124465, 3337.08930835080],
|
|
[0.00002937546, 6.07893711402, 0.06731030280],
|
|
[0.00002389414, 5.03896442664, 796.29800681640],
|
|
[0.00002579844, 0.02996736156, 3344.13554504880],
|
|
[0.00001528141, 1.14979301996, 6151.53388830500],
|
|
[0.00001798806, 0.65634057445, 529.69096509460],
|
|
[0.00001264357, 3.62275122593, 5092.15195811580],
|
|
[0.00001286228, 3.06796065034, 2146.16541647520],
|
|
[0.00001546404, 2.91579701718, 1751.53953141600],
|
|
[0.00001024902, 3.69334099279, 8962.45534991020],
|
|
[0.00000891566, 0.18293837498, 16703.06213349900],
|
|
[0.00000858759, 2.40093811940, 2914.01423582380],
|
|
[0.00000832715, 2.46418619474, 3340.59517304760],
|
|
[0.00000832720, 4.49495782139, 3340.62968035200],
|
|
[0.00000712902, 3.66335473479, 1059.38193018920],
|
|
[0.00000748723, 3.82248614017, 155.42039943420],
|
|
[0.00000723861, 0.67497311481, 3738.76143010800],
|
|
[0.00000635548, 2.92182225127, 8432.76438481560],
|
|
[0.00000655162, 0.48864064125, 3127.31333126180],
|
|
[0.00000550474, 3.81001042328, 0.98032106820],
|
|
[0.00000552750, 4.47479317037, 1748.01641306700],
|
|
[0.00000425966, 0.55364317304, 6283.07584999140],
|
|
[0.00000415131, 0.49662285038, 213.29909543800],
|
|
[0.00000472167, 3.62547124025, 1194.44701022460],
|
|
[0.00000306551, 0.38052848348, 6684.74797174860],
|
|
[0.00000312141, 0.99853944405, 6677.70173505060],
|
|
[0.00000293198, 4.22131299634, 20.77539549240],
|
|
[0.00000302375, 4.48618007156, 3532.06069281140],
|
|
[0.00000274027, 0.54222167059, 3340.54511639700],
|
|
[0.00000281079, 5.88163521788, 1349.86740965880],
|
|
[0.00000231183, 1.28242156993, 3870.30339179440],
|
|
[0.00000283602, 5.76885434940, 3149.16416058820],
|
|
[0.00000236117, 5.75503217933, 3333.49887969900],
|
|
[0.00000274033, 0.13372524985, 3340.67973700260],
|
|
[0.00000299395, 2.78323740866, 6254.62666252360]
|
|
],
|
|
[
|
|
[3340.61242700512, 0.00000000000, 0.00000000000],
|
|
[0.01457554523, 3.60433733236, 3340.61242669980],
|
|
[0.00168414711, 3.92318567804, 6681.22485339960],
|
|
[0.00020622975, 4.26108844583, 10021.83728009940],
|
|
[0.00003452392, 4.73210393190, 3.52311834900],
|
|
[0.00002586332, 4.60670058555, 13362.44970679920],
|
|
[0.00000841535, 4.45864030426, 2281.23049651060]
|
|
],
|
|
[
|
|
[0.00058152577, 2.04961712429, 3340.61242669980],
|
|
[0.00013459579, 2.45738706163, 6681.22485339960]
|
|
]
|
|
],
|
|
[
|
|
[
|
|
[0.03197134986, 3.76832042431, 3340.61242669980],
|
|
[0.00298033234, 4.10616996305, 6681.22485339960],
|
|
[0.00289104742, 0.00000000000, 0.00000000000],
|
|
[0.00031365539, 4.44651053090, 10021.83728009940],
|
|
[0.00003484100, 4.78812549260, 13362.44970679920]
|
|
],
|
|
[
|
|
[0.00217310991, 6.04472194776, 3340.61242669980],
|
|
[0.00020976948, 3.14159265359, 0.00000000000],
|
|
[0.00012834709, 1.60810667915, 6681.22485339960]
|
|
]
|
|
],
|
|
[
|
|
[
|
|
[1.53033488271, 0.00000000000, 0.00000000000],
|
|
[0.14184953160, 3.47971283528, 3340.61242669980],
|
|
[0.00660776362, 3.81783443019, 6681.22485339960],
|
|
[0.00046179117, 4.15595316782, 10021.83728009940],
|
|
[0.00008109733, 5.55958416318, 2810.92146160520],
|
|
[0.00007485318, 1.77239078402, 5621.84292321040],
|
|
[0.00005523191, 1.36436303770, 2281.23049651060],
|
|
[0.00003825160, 4.49407183687, 13362.44970679920],
|
|
[0.00002306537, 0.09081579001, 2544.31441988340],
|
|
[0.00001999396, 5.36059617709, 3337.08930835080],
|
|
[0.00002484394, 4.92545639920, 2942.46342329160],
|
|
[0.00001960195, 4.74249437639, 3344.13554504880],
|
|
[0.00001167119, 2.11260868341, 5092.15195811580],
|
|
[0.00001102816, 5.00908403998, 398.14900340820],
|
|
[0.00000899066, 4.40791133207, 529.69096509460],
|
|
[0.00000992252, 5.83861961952, 6151.53388830500],
|
|
[0.00000807354, 2.10217065501, 1059.38193018920],
|
|
[0.00000797915, 3.44839203899, 796.29800681640],
|
|
[0.00000740975, 1.49906336885, 2146.16541647520]
|
|
],
|
|
[
|
|
[0.01107433345, 2.03250524857, 3340.61242669980],
|
|
[0.00103175887, 2.37071847807, 6681.22485339960],
|
|
[0.00012877200, 0.00000000000, 0.00000000000],
|
|
[0.00010815880, 2.70888095665, 10021.83728009940]
|
|
],
|
|
[
|
|
[0.00044242249, 0.47930604954, 3340.61242669980],
|
|
[0.00008138042, 0.86998389204, 6681.22485339960]
|
|
]
|
|
]
|
|
],
|
|
|
|
# Jupiter
|
|
[
|
|
[
|
|
[
|
|
[0.59954691494, 0.00000000000, 0.00000000000],
|
|
[0.09695898719, 5.06191793158, 529.69096509460],
|
|
[0.00573610142, 1.44406205629, 7.11354700080],
|
|
[0.00306389205, 5.41734730184, 1059.38193018920],
|
|
[0.00097178296, 4.14264726552, 632.78373931320],
|
|
[0.00072903078, 3.64042916389, 522.57741809380],
|
|
[0.00064263975, 3.41145165351, 103.09277421860],
|
|
[0.00039806064, 2.29376740788, 419.48464387520],
|
|
[0.00038857767, 1.27231755835, 316.39186965660],
|
|
[0.00027964629, 1.78454591820, 536.80451209540],
|
|
[0.00013589730, 5.77481040790, 1589.07289528380],
|
|
[0.00008246349, 3.58227925840, 206.18554843720],
|
|
[0.00008768704, 3.63000308199, 949.17560896980],
|
|
[0.00007368042, 5.08101194270, 735.87651353180],
|
|
[0.00006263150, 0.02497628807, 213.29909543800],
|
|
[0.00006114062, 4.51319998626, 1162.47470440780],
|
|
[0.00004905396, 1.32084470588, 110.20632121940],
|
|
[0.00005305285, 1.30671216791, 14.22709400160],
|
|
[0.00005305441, 4.18625634012, 1052.26838318840],
|
|
[0.00004647248, 4.69958103684, 3.93215326310],
|
|
[0.00003045023, 4.31676431084, 426.59819087600],
|
|
[0.00002609999, 1.56667394063, 846.08283475120],
|
|
[0.00002028191, 1.06376530715, 3.18139373770],
|
|
[0.00001764763, 2.14148655117, 1066.49547719000],
|
|
[0.00001722972, 3.88036268267, 1265.56747862640],
|
|
[0.00001920945, 0.97168196472, 639.89728631400],
|
|
[0.00001633223, 3.58201833555, 515.46387109300],
|
|
[0.00001431999, 4.29685556046, 625.67019231240],
|
|
[0.00000973272, 4.09764549134, 95.97922721780]
|
|
],
|
|
[
|
|
[529.69096508814, 0.00000000000, 0.00000000000],
|
|
[0.00489503243, 4.22082939470, 529.69096509460],
|
|
[0.00228917222, 6.02646855621, 7.11354700080],
|
|
[0.00030099479, 4.54540782858, 1059.38193018920],
|
|
[0.00020720920, 5.45943156902, 522.57741809380],
|
|
[0.00012103653, 0.16994816098, 536.80451209540],
|
|
[0.00006067987, 4.42422292017, 103.09277421860],
|
|
[0.00005433968, 3.98480737746, 419.48464387520],
|
|
[0.00004237744, 5.89008707199, 14.22709400160]
|
|
],
|
|
[
|
|
[0.00047233601, 4.32148536482, 7.11354700080],
|
|
[0.00030649436, 2.92977788700, 529.69096509460],
|
|
[0.00014837605, 3.14159265359, 0.00000000000]
|
|
]
|
|
],
|
|
[
|
|
[
|
|
[0.02268615702, 3.55852606721, 529.69096509460],
|
|
[0.00109971634, 3.90809347197, 1059.38193018920],
|
|
[0.00110090358, 0.00000000000, 0.00000000000],
|
|
[0.00008101428, 3.60509572885, 522.57741809380],
|
|
[0.00006043996, 4.25883108339, 1589.07289528380],
|
|
[0.00006437782, 0.30627119215, 536.80451209540]
|
|
],
|
|
[
|
|
[0.00078203446, 1.52377859742, 529.69096509460]
|
|
]
|
|
],
|
|
[
|
|
[
|
|
[5.20887429326, 0.00000000000, 0.00000000000],
|
|
[0.25209327119, 3.49108639871, 529.69096509460],
|
|
[0.00610599976, 3.84115365948, 1059.38193018920],
|
|
[0.00282029458, 2.57419881293, 632.78373931320],
|
|
[0.00187647346, 2.07590383214, 522.57741809380],
|
|
[0.00086792905, 0.71001145545, 419.48464387520],
|
|
[0.00072062974, 0.21465724607, 536.80451209540],
|
|
[0.00065517248, 5.97995884790, 316.39186965660],
|
|
[0.00029134542, 1.67759379655, 103.09277421860],
|
|
[0.00030135335, 2.16132003734, 949.17560896980],
|
|
[0.00023453271, 3.54023522184, 735.87651353180],
|
|
[0.00022283743, 4.19362594399, 1589.07289528380],
|
|
[0.00023947298, 0.27458037480, 7.11354700080],
|
|
[0.00013032614, 2.96042965363, 1162.47470440780],
|
|
[0.00009703360, 1.90669633585, 206.18554843720],
|
|
[0.00012749023, 2.71550286592, 1052.26838318840]
|
|
],
|
|
[
|
|
[0.01271801520, 2.64937512894, 529.69096509460],
|
|
[0.00061661816, 3.00076460387, 1059.38193018920],
|
|
[0.00053443713, 3.89717383175, 522.57741809380],
|
|
[0.00031185171, 4.88276958012, 536.80451209540],
|
|
[0.00041390269, 0.00000000000, 0.00000000000]
|
|
]
|
|
]
|
|
],
|
|
|
|
# Saturn
|
|
[
|
|
[
|
|
[
|
|
[0.87401354025, 0.00000000000, 0.00000000000],
|
|
[0.11107659762, 3.96205090159, 213.29909543800],
|
|
[0.01414150957, 4.58581516874, 7.11354700080],
|
|
[0.00398379389, 0.52112032699, 206.18554843720],
|
|
[0.00350769243, 3.30329907896, 426.59819087600],
|
|
[0.00206816305, 0.24658372002, 103.09277421860],
|
|
[0.00079271300, 3.84007056878, 220.41264243880],
|
|
[0.00023990355, 4.66976924553, 110.20632121940],
|
|
[0.00016573588, 0.43719228296, 419.48464387520],
|
|
[0.00014906995, 5.76903183869, 316.39186965660],
|
|
[0.00015820290, 0.93809155235, 632.78373931320],
|
|
[0.00014609559, 1.56518472000, 3.93215326310],
|
|
[0.00013160301, 4.44891291899, 14.22709400160],
|
|
[0.00015053543, 2.71669915667, 639.89728631400],
|
|
[0.00013005299, 5.98119023644, 11.04570026390],
|
|
[0.00010725067, 3.12939523827, 202.25339517410],
|
|
[0.00005863206, 0.23656938524, 529.69096509460],
|
|
[0.00005227757, 4.20783365759, 3.18139373770],
|
|
[0.00006126317, 1.76328667907, 277.03499374140],
|
|
[0.00005019687, 3.17787728405, 433.71173787680],
|
|
[0.00004592550, 0.61977744975, 199.07200143640],
|
|
[0.00004005867, 2.24479718502, 63.73589830340],
|
|
[0.00002953796, 0.98280366998, 95.97922721780],
|
|
[0.00003873670, 3.22283226966, 138.51749687070],
|
|
[0.00002461186, 2.03163875071, 735.87651353180],
|
|
[0.00003269484, 0.77492638211, 949.17560896980],
|
|
[0.00001758145, 3.26580109940, 522.57741809380],
|
|
[0.00001640172, 5.50504453050, 846.08283475120],
|
|
[0.00001391327, 4.02333150505, 323.50541665740],
|
|
[0.00001580648, 4.37265307169, 309.27832265580],
|
|
[0.00001123498, 2.83726798446, 415.55249061210],
|
|
[0.00001017275, 3.71700135395, 227.52618943960],
|
|
[0.00000848642, 3.19150170830, 209.36694217490]
|
|
],
|
|
[
|
|
[213.29909521690, 0.00000000000, 0.00000000000],
|
|
[0.01297370862, 1.82834923978, 213.29909543800],
|
|
[0.00564345393, 2.88499717272, 7.11354700080],
|
|
[0.00093734369, 1.06311793502, 426.59819087600],
|
|
[0.00107674962, 2.27769131009, 206.18554843720],
|
|
[0.00040244455, 2.04108104671, 220.41264243880],
|
|
[0.00019941774, 1.27954390470, 103.09277421860],
|
|
[0.00010511678, 2.74880342130, 14.22709400160],
|
|
[0.00006416106, 0.38238295041, 639.89728631400],
|
|
[0.00004848994, 2.43037610229, 419.48464387520],
|
|
[0.00004056892, 2.92133209468, 110.20632121940],
|
|
[0.00003768635, 3.64965330780, 3.93215326310]
|
|
],
|
|
[
|
|
[0.00116441330, 1.17988132879, 7.11354700080],
|
|
[0.00091841837, 0.07325195840, 213.29909543800],
|
|
[0.00036661728, 0.00000000000, 0.00000000000],
|
|
[0.00015274496, 4.06493179167, 206.18554843720]
|
|
]
|
|
],
|
|
[
|
|
[
|
|
[0.04330678039, 3.60284428399, 213.29909543800],
|
|
[0.00240348302, 2.85238489373, 426.59819087600],
|
|
[0.00084745939, 0.00000000000, 0.00000000000],
|
|
[0.00030863357, 3.48441504555, 220.41264243880],
|
|
[0.00034116062, 0.57297307557, 206.18554843720],
|
|
[0.00014734070, 2.11846596715, 639.89728631400],
|
|
[0.00009916667, 5.79003188904, 419.48464387520],
|
|
[0.00006993564, 4.73604689720, 7.11354700080],
|
|
[0.00004807588, 5.43305312061, 316.39186965660]
|
|
],
|
|
[
|
|
[0.00198927992, 4.93901017903, 213.29909543800],
|
|
[0.00036947916, 3.14159265359, 0.00000000000],
|
|
[0.00017966989, 0.51979431110, 426.59819087600]
|
|
]
|
|
],
|
|
[
|
|
[
|
|
[9.55758135486, 0.00000000000, 0.00000000000],
|
|
[0.52921382865, 2.39226219573, 213.29909543800],
|
|
[0.01873679867, 5.23549604660, 206.18554843720],
|
|
[0.01464663929, 1.64763042902, 426.59819087600],
|
|
[0.00821891141, 5.93520042303, 316.39186965660],
|
|
[0.00547506923, 5.01532618980, 103.09277421860],
|
|
[0.00371684650, 2.27114821115, 220.41264243880],
|
|
[0.00361778765, 3.13904301847, 7.11354700080],
|
|
[0.00140617506, 5.70406606781, 632.78373931320],
|
|
[0.00108974848, 3.29313390175, 110.20632121940],
|
|
[0.00069006962, 5.94099540992, 419.48464387520],
|
|
[0.00061053367, 0.94037691801, 639.89728631400],
|
|
[0.00048913294, 1.55733638681, 202.25339517410],
|
|
[0.00034143772, 0.19519102597, 277.03499374140],
|
|
[0.00032401773, 5.47084567016, 949.17560896980],
|
|
[0.00020936596, 0.46349251129, 735.87651353180]
|
|
],
|
|
[
|
|
[0.06182981340, 0.25843511480, 213.29909543800],
|
|
[0.00506577242, 0.71114625261, 206.18554843720],
|
|
[0.00341394029, 5.79635741658, 426.59819087600],
|
|
[0.00188491195, 0.47215589652, 220.41264243880],
|
|
[0.00186261486, 3.14159265359, 0.00000000000],
|
|
[0.00143891146, 1.40744822888, 7.11354700080]
|
|
],
|
|
[
|
|
[0.00436902572, 4.78671677509, 213.29909543800]
|
|
]
|
|
]
|
|
],
|
|
|
|
# Uranus
|
|
[
|
|
[
|
|
[
|
|
[5.48129294297, 0.00000000000, 0.00000000000],
|
|
[0.09260408234, 0.89106421507, 74.78159856730],
|
|
[0.01504247898, 3.62719260920, 1.48447270830],
|
|
[0.00365981674, 1.89962179044, 73.29712585900],
|
|
[0.00272328168, 3.35823706307, 149.56319713460],
|
|
[0.00070328461, 5.39254450063, 63.73589830340],
|
|
[0.00068892678, 6.09292483287, 76.26607127560],
|
|
[0.00061998615, 2.26952066061, 2.96894541660],
|
|
[0.00061950719, 2.85098872691, 11.04570026390],
|
|
[0.00026468770, 3.14152083966, 71.81265315070],
|
|
[0.00025710476, 6.11379840493, 454.90936652730],
|
|
[0.00021078850, 4.36059339067, 148.07872442630],
|
|
[0.00017818647, 1.74436930289, 36.64856292950],
|
|
[0.00014613507, 4.73732166022, 3.93215326310],
|
|
[0.00011162509, 5.82681796350, 224.34479570190],
|
|
[0.00010997910, 0.48865004018, 138.51749687070],
|
|
[0.00009527478, 2.95516862826, 35.16409022120],
|
|
[0.00007545601, 5.23626582400, 109.94568878850],
|
|
[0.00004220241, 3.23328220918, 70.84944530420],
|
|
[0.00004051900, 2.27755017300, 151.04766984290],
|
|
[0.00003354596, 1.06549007380, 4.45341812490],
|
|
[0.00002926718, 4.62903718891, 9.56122755560],
|
|
[0.00003490340, 5.48306144511, 146.59425171800],
|
|
[0.00003144069, 4.75199570434, 77.75054398390],
|
|
[0.00002922333, 5.35235361027, 85.82729883120],
|
|
[0.00002272788, 4.36600400036, 70.32818044240],
|
|
[0.00002051219, 1.51773566586, 0.11187458460],
|
|
[0.00002148602, 0.60745949945, 38.13303563780],
|
|
[0.00001991643, 4.92437588682, 277.03499374140],
|
|
[0.00001376226, 2.04283539351, 65.22037101170],
|
|
[0.00001666902, 3.62744066769, 380.12776796000],
|
|
[0.00001284107, 3.11347961505, 202.25339517410],
|
|
[0.00001150429, 0.93343589092, 3.18139373770],
|
|
[0.00001533221, 2.58594681212, 52.69019803950],
|
|
[0.00001281604, 0.54271272721, 222.86032299360],
|
|
[0.00001372139, 4.19641530878, 111.43016149680],
|
|
[0.00001221029, 0.19900650030, 108.46121608020],
|
|
[0.00000946181, 1.19253165736, 127.47179660680],
|
|
[0.00001150989, 4.17898916639, 33.67961751290]
|
|
],
|
|
[
|
|
[74.78159860910, 0.00000000000, 0.00000000000],
|
|
[0.00154332863, 5.24158770553, 74.78159856730],
|
|
[0.00024456474, 1.71260334156, 1.48447270830],
|
|
[0.00009258442, 0.42829732350, 11.04570026390],
|
|
[0.00008265977, 1.50218091379, 63.73589830340],
|
|
[0.00009150160, 1.41213765216, 149.56319713460]
|
|
]
|
|
],
|
|
[
|
|
[
|
|
[0.01346277648, 2.61877810547, 74.78159856730],
|
|
[0.00062341400, 5.08111189648, 149.56319713460],
|
|
[0.00061601196, 3.14159265359, 0.00000000000],
|
|
[0.00009963722, 1.61603805646, 76.26607127560],
|
|
[0.00009926160, 0.57630380333, 73.29712585900]
|
|
],
|
|
[
|
|
[0.00034101978, 0.01321929936, 74.78159856730]
|
|
]
|
|
],
|
|
[
|
|
[
|
|
[19.21264847206, 0.00000000000, 0.00000000000],
|
|
[0.88784984413, 5.60377527014, 74.78159856730],
|
|
[0.03440836062, 0.32836099706, 73.29712585900],
|
|
[0.02055653860, 1.78295159330, 149.56319713460],
|
|
[0.00649322410, 4.52247285911, 76.26607127560],
|
|
[0.00602247865, 3.86003823674, 63.73589830340],
|
|
[0.00496404167, 1.40139935333, 454.90936652730],
|
|
[0.00338525369, 1.58002770318, 138.51749687070],
|
|
[0.00243509114, 1.57086606044, 71.81265315070],
|
|
[0.00190522303, 1.99809394714, 1.48447270830],
|
|
[0.00161858838, 2.79137786799, 148.07872442630],
|
|
[0.00143706183, 1.38368544947, 11.04570026390],
|
|
[0.00093192405, 0.17437220467, 36.64856292950],
|
|
[0.00071424548, 4.24509236074, 224.34479570190],
|
|
[0.00089806014, 3.66105364565, 109.94568878850],
|
|
[0.00039009723, 1.66971401684, 70.84944530420],
|
|
[0.00046677296, 1.39976401694, 35.16409022120],
|
|
[0.00039025624, 3.36234773834, 277.03499374140],
|
|
[0.00036755274, 3.88649278513, 146.59425171800],
|
|
[0.00030348723, 0.70100838798, 151.04766984290],
|
|
[0.00029156413, 3.18056336700, 77.75054398390]
|
|
],
|
|
[
|
|
[0.01479896629, 3.67205697578, 74.78159856730]
|
|
]
|
|
]
|
|
],
|
|
|
|
# Neptune
|
|
[
|
|
[
|
|
[
|
|
[5.31188633046, 0.00000000000, 0.00000000000],
|
|
[0.01798475530, 2.90101273890, 38.13303563780],
|
|
[0.01019727652, 0.48580922867, 1.48447270830],
|
|
[0.00124531845, 4.83008090676, 36.64856292950],
|
|
[0.00042064466, 5.41054993053, 2.96894541660],
|
|
[0.00037714584, 6.09221808686, 35.16409022120],
|
|
[0.00033784738, 1.24488874087, 76.26607127560],
|
|
[0.00016482741, 0.00007727998, 491.55792945680],
|
|
[0.00009198584, 4.93747051954, 39.61750834610],
|
|
[0.00008994250, 0.27462171806, 175.16605980020]
|
|
],
|
|
[
|
|
[38.13303563957, 0.00000000000, 0.00000000000],
|
|
[0.00016604172, 4.86323329249, 1.48447270830],
|
|
[0.00015744045, 2.27887427527, 38.13303563780]
|
|
]
|
|
],
|
|
[
|
|
[
|
|
[0.03088622933, 1.44104372644, 38.13303563780],
|
|
[0.00027780087, 5.91271884599, 76.26607127560],
|
|
[0.00027623609, 0.00000000000, 0.00000000000],
|
|
[0.00015355489, 2.52123799551, 36.64856292950],
|
|
[0.00015448133, 3.50877079215, 39.61750834610]
|
|
]
|
|
],
|
|
[
|
|
[
|
|
[30.07013205828, 0.00000000000, 0.00000000000],
|
|
[0.27062259632, 1.32999459377, 38.13303563780],
|
|
[0.01691764014, 3.25186135653, 36.64856292950],
|
|
[0.00807830553, 5.18592878704, 1.48447270830],
|
|
[0.00537760510, 4.52113935896, 35.16409022120],
|
|
[0.00495725141, 1.57105641650, 491.55792945680],
|
|
[0.00274571975, 1.84552258866, 175.16605980020]
|
|
]
|
|
]
|
|
],
|
|
]
|
|
|
|
def _CalcVsop(model, time):
|
|
spher = []
|
|
t = time.tt / 365250.0
|
|
for formula in model:
|
|
tpower = 1.0
|
|
coord = 0.0
|
|
for series in formula:
|
|
coord += tpower * sum(A * math.cos(B + C*t) for (A, B, C) in series)
|
|
tpower *= t
|
|
spher.append(coord)
|
|
|
|
# Convert spherical coordinates to ecliptic cartesian coordinates.
|
|
r_coslat = spher[2] * math.cos(spher[1])
|
|
ex = r_coslat * math.cos(spher[0])
|
|
ey = r_coslat * math.sin(spher[0])
|
|
ez = spher[2] * math.sin(spher[1])
|
|
|
|
# Convert ecliptic cartesian coordinates to equatorial cartesian coordinates.
|
|
vx = ex + 0.000000440360*ey - 0.000000190919*ez
|
|
vy = -0.000000479966*ex + 0.917482137087*ey - 0.397776982902*ez
|
|
vz = 0.397776982902*ey + 0.917482137087*ez
|
|
return Vector(vx, vy, vz, time)
|
|
|
|
def _CalcEarth(time):
|
|
return _CalcVsop(_vsop[Body.Earth], time)
|
|
|
|
# END VSOP
|
|
#----------------------------------------------------------------------------
|
|
# BEGIN CHEBYSHEV
|
|
|
|
_pluto = [
|
|
{ 'tt':-109573.500000, 'ndays':26141.000000, 'coeff':[
|
|
[-30.303124711144, -18.980368465705, 3.206649343866],
|
|
[20.092745278347, -27.533908687219, -14.641121965990],
|
|
[9.137264744925, 6.513103657467, -0.720732357468],
|
|
[-1.201554708717, 2.149917852301, 1.032022293526],
|
|
[-0.566068170022, -0.285737361191, 0.081379987808],
|
|
[0.041678527795, -0.143363105040, -0.057534475984],
|
|
[0.041087908142, 0.007911321580, -0.010270655537],
|
|
[0.001611769878, 0.011409821837, 0.003679980733],
|
|
[-0.002536458296, -0.000145632543, 0.000949924030],
|
|
[0.001167651969, -0.000049912680, 0.000115867710],
|
|
[-0.000196953286, 0.000420406270, 0.000110147171],
|
|
[0.001073825784, 0.000442658285, 0.000146985332],
|
|
[-0.000906160087, 0.001702360394, 0.000758987924],
|
|
[-0.001467464335, -0.000622191266, -0.000231866243],
|
|
[-0.000008986691, 0.000004086384, 0.000001442956],
|
|
[-0.001099078039, -0.000544633529, -0.000205534708],
|
|
[0.001259974751, -0.002178533187, -0.000965315934],
|
|
[0.001695288316, 0.000768480768, 0.000287916141],
|
|
[-0.001428026702, 0.002707551594, 0.001195955756]]
|
|
},
|
|
{ 'tt':-83432.500000, 'ndays':26141.000000, 'coeff':[
|
|
[67.049456204563, -9.279626603192, -23.091941092128],
|
|
[14.860676672314, 26.594121136143, 3.819668867047],
|
|
[-6.254409044120, 1.408757903538, 2.323726101433],
|
|
[0.114416381092, -0.942273228585, -0.328566335886],
|
|
[0.074973631246, 0.106749156044, 0.010806547171],
|
|
[-0.018627741964, -0.009983491157, 0.002589955906],
|
|
[0.006167206174, -0.001042430439, -0.001521881831],
|
|
[-0.000471293617, 0.002337935239, 0.001060879763],
|
|
[-0.000240627462, -0.001380351742, -0.000546042590],
|
|
[0.001872140444, 0.000679876620, 0.000240384842],
|
|
[-0.000334705177, 0.000693528330, 0.000301138309],
|
|
[0.000796124758, 0.000653183163, 0.000259527079],
|
|
[-0.001276116664, 0.001393959948, 0.000629574865],
|
|
[-0.001235158458, -0.000889985319, -0.000351392687],
|
|
[-0.000019881944, 0.000048339979, 0.000021342186],
|
|
[-0.000987113745, -0.000748420747, -0.000296503569],
|
|
[0.001721891782, -0.001893675502, -0.000854270937],
|
|
[0.001505145187, 0.001081653337, 0.000426723640],
|
|
[-0.002019479384, 0.002375617497, 0.001068258925]]
|
|
},
|
|
{ 'tt':-57291.500000, 'ndays':26141.000000, 'coeff':[
|
|
[46.038290912405, 73.773759757856, 9.148670950706],
|
|
[-22.354364534703, 10.217143138926, 9.921247676076],
|
|
[-2.696282001399, -4.440843715929, -0.572373037840],
|
|
[0.385475818800, -0.287872688575, -0.205914693555],
|
|
[0.020994433095, 0.004256602589, -0.004817361041],
|
|
[0.003212255378, 0.000574875698, -0.000764464370],
|
|
[-0.000158619286, -0.001035559544, -0.000535612316],
|
|
[0.000967952107, -0.000653111849, -0.000292019750],
|
|
[0.001763494906, -0.000370815938, -0.000224698363],
|
|
[0.001157990330, 0.001849810828, 0.000759641577],
|
|
[-0.000883535516, 0.000384038162, 0.000191242192],
|
|
[0.000709486562, 0.000655810827, 0.000265431131],
|
|
[-0.001525810419, 0.001126870468, 0.000520202001],
|
|
[-0.000983210860, -0.001116073455, -0.000456026382],
|
|
[-0.000015655450, 0.000069184008, 0.000029796623],
|
|
[-0.000815102021, -0.000900597010, -0.000365274209],
|
|
[0.002090300438, -0.001536778673, -0.000709827438],
|
|
[0.001234661297, 0.001342978436, 0.000545313112],
|
|
[-0.002517963678, 0.001941826791, 0.000893859860]]
|
|
},
|
|
{ 'tt':-31150.500000, 'ndays':26141.000000, 'coeff':[
|
|
[-39.074661990988, 30.963513412373, 21.431709298065],
|
|
[-12.033639281924, -31.693679132310, -6.263961539568],
|
|
[7.233936758611, -3.979157072767, -3.421027935569],
|
|
[1.383182539917, 1.090729793400, -0.076771771448],
|
|
[-0.009894394996, 0.313614402007, 0.101180677344],
|
|
[-0.055459383449, 0.031782406403, 0.026374448864],
|
|
[-0.011074105991, -0.007176759494, 0.001896208351],
|
|
[-0.000263363398, -0.001145329444, 0.000215471838],
|
|
[0.000405700185, -0.000839229891, -0.000418571366],
|
|
[0.001004921401, 0.001135118493, 0.000406734549],
|
|
[-0.000473938695, 0.000282751002, 0.000114911593],
|
|
[0.000528685886, 0.000966635293, 0.000401955197],
|
|
[-0.001838869845, 0.000806432189, 0.000394594478],
|
|
[-0.000713122169, -0.001334810971, -0.000554511235],
|
|
[0.000006449359, 0.000060730000, 0.000024513230],
|
|
[-0.000596025142, -0.000999492770, -0.000413930406],
|
|
[0.002364904429, -0.001099236865, -0.000528480902],
|
|
[0.000907458104, 0.001537243912, 0.000637001965],
|
|
[-0.002909908764, 0.001413648354, 0.000677030924]]
|
|
},
|
|
{ 'tt':-5009.500000, 'ndays':26141.000000, 'coeff':[
|
|
[23.380075041204, -38.969338804442, -19.204762094135],
|
|
[33.437140696536, 8.735194448531, -7.348352917314],
|
|
[-3.127251304544, 8.324311848708, 3.540122328502],
|
|
[-1.491354030154, -1.350371407475, 0.028214278544],
|
|
[0.361398480996, -0.118420687058, -0.145375605480],
|
|
[-0.011771350229, 0.085880588309, 0.030665997197],
|
|
[-0.015839541688, -0.014165128211, 0.000523465951],
|
|
[0.004213218926, -0.001426373728, -0.001906412496],
|
|
[0.001465150002, 0.000451513538, 0.000081936194],
|
|
[0.000640069511, 0.001886692235, 0.000884675556],
|
|
[-0.000883554940, 0.000301907356, 0.000127310183],
|
|
[0.000245524038, 0.000910362686, 0.000385555148],
|
|
[-0.001942010476, 0.000438682280, 0.000237124027],
|
|
[-0.000425455660, -0.001442138768, -0.000607751390],
|
|
[0.000004168433, 0.000033856562, 0.000013881811],
|
|
[-0.000337920193, -0.001074290356, -0.000452503056],
|
|
[0.002544755354, -0.000620356219, -0.000327246228],
|
|
[0.000534534110, 0.001670320887, 0.000702775941],
|
|
[-0.003169380270, 0.000816186705, 0.000427213817]]
|
|
},
|
|
{ 'tt':21131.500000, 'ndays':26141.000000, 'coeff':[
|
|
[74.130449310804, 43.372111541004, -8.799489207171],
|
|
[-8.705941488523, 23.344631690845, 9.908006472122],
|
|
[-4.614752911564, -2.587334376729, 0.583321715294],
|
|
[0.316219286624, -0.395448970181, -0.219217574801],
|
|
[0.004593734664, 0.027528474371, 0.007736197280],
|
|
[-0.001192268851, -0.004987723997, -0.001599399192],
|
|
[0.003051998429, -0.001287028653, -0.000780744058],
|
|
[0.001482572043, 0.001613554244, 0.000635747068],
|
|
[0.000581965277, 0.000788286674, 0.000315285159],
|
|
[-0.000311830730, 0.001622369930, 0.000714817617],
|
|
[-0.000711275723, -0.000160014561, -0.000050445901],
|
|
[0.000177159088, 0.001032713853, 0.000435835541],
|
|
[-0.002032280820, 0.000144281331, 0.000111910344],
|
|
[-0.000148463759, -0.001495212309, -0.000635892081],
|
|
[-0.000009629403, -0.000013678407, -0.000006187457],
|
|
[-0.000061196084, -0.001119783520, -0.000479221572],
|
|
[0.002630993795, -0.000113042927, -0.000112115452],
|
|
[0.000132867113, 0.001741417484, 0.000743224630],
|
|
[-0.003293498893, 0.000182437998, 0.000158073228]]
|
|
},
|
|
{ 'tt':47272.500000, 'ndays':26141.000000, 'coeff':[
|
|
[-5.727994625506, 71.194823351703, 23.946198176031],
|
|
[-26.767323214686, -12.264949302780, 4.238297122007],
|
|
[0.890596204250, -5.970227904551, -2.131444078785],
|
|
[0.808383708156, -0.143104108476, -0.288102517987],
|
|
[0.089303327519, 0.049290470655, -0.010970501667],
|
|
[0.010197195705, 0.012879721400, 0.001317586740],
|
|
[0.001795282629, 0.004482403780, 0.001563326157],
|
|
[-0.001974716105, 0.001278073933, 0.000652735133],
|
|
[0.000906544715, -0.000805502229, -0.000336200833],
|
|
[0.000283816745, 0.001799099064, 0.000756827653],
|
|
[-0.000784971304, 0.000123081220, 0.000068812133],
|
|
[-0.000237033406, 0.000980100466, 0.000427758498],
|
|
[-0.001976846386, -0.000280421081, -0.000072417045],
|
|
[0.000195628511, -0.001446079585, -0.000624011074],
|
|
[-0.000044622337, -0.000035865046, -0.000013581236],
|
|
[0.000204397832, -0.001127474894, -0.000488668673],
|
|
[0.002625373003, 0.000389300123, 0.000102756139],
|
|
[-0.000277321614, 0.001732818354, 0.000749576471],
|
|
[-0.003280537764, -0.000457571669, -0.000116383655]]
|
|
}]
|
|
|
|
def _ChebScale(t_min, t_max, t):
|
|
return (2*t - (t_max + t_min)) / (t_max - t_min)
|
|
|
|
def _CalcChebyshev(model, time):
|
|
# Search for a record that overlaps the given time value.
|
|
for record in model:
|
|
x = _ChebScale(record['tt'], record['tt'] + record['ndays'], time.tt)
|
|
if -1 <= x <= +1:
|
|
coeff = record['coeff']
|
|
pos = []
|
|
for d in range(3):
|
|
p0 = 1
|
|
sum = coeff[0][d]
|
|
p1 = x
|
|
sum += coeff[1][d] * p1
|
|
for k in range(2, len(coeff)):
|
|
p2 = (2 * x * p1) - p0
|
|
sum += coeff[k][d] * p2
|
|
p0 = p1
|
|
p1 = p2
|
|
pos.append(sum - coeff[0][d]/2)
|
|
return Vector(pos[0], pos[1], pos[2], time)
|
|
raise Error('Cannot extrapolate Chebyshev model for given Terrestrial Time: {}'.format(time.tt))
|
|
|
|
# END CHEBYSHEV
|
|
#----------------------------------------------------------------------------
|
|
# BEGIN Search
|
|
|
|
def _QuadInterp(tm, dt, fa, fm, fb):
|
|
Q = (fb + fa)/2 - fm
|
|
R = (fb - fa)/2
|
|
S = fm
|
|
|
|
if Q == 0:
|
|
# This is a line, not a parabola.
|
|
if R == 0:
|
|
# This is a HORIZONTAL line... can't make progress!
|
|
return None
|
|
x = -S / R
|
|
if not (-1 <= x <= +1):
|
|
return None # out of bounds
|
|
else:
|
|
# It really is a parabola. Find roots x1, x2.
|
|
u = R*R - 4*Q*S
|
|
if u <= 0:
|
|
return None
|
|
ru = math.sqrt(u)
|
|
x1 = (-R + ru) / (2 * Q)
|
|
x2 = (-R - ru) / (2 * Q)
|
|
|
|
if -1 <= x1 <= +1:
|
|
if -1 <= x2 <= +1:
|
|
# Two solutions... so parabola intersects twice.
|
|
return None
|
|
x = x1
|
|
elif -1 <= x2 <= +1:
|
|
x = x2
|
|
else:
|
|
return None
|
|
|
|
t = tm + x*dt
|
|
df_dt = (2*Q*x + R) / dt
|
|
return (x, t, df_dt)
|
|
|
|
def Search(func, context, t1, t2, dt_tolerance_seconds):
|
|
dt_days = abs(dt_tolerance_seconds / _SECONDS_PER_DAY)
|
|
f1 = func(context, t1)
|
|
f2 = func(context, t2)
|
|
iter = 0
|
|
iter_limit = 20
|
|
calc_fmid = True
|
|
while True:
|
|
iter += 1
|
|
if iter > iter_limit:
|
|
raise Error('Excessive iteration in Search')
|
|
|
|
dt = (t2.tt - t1.tt) / 2.0
|
|
tmid = t1.AddDays(dt)
|
|
if abs(dt) < dt_days:
|
|
# We are close enough to the event to stop the search.
|
|
return tmid
|
|
|
|
if calc_fmid:
|
|
fmid = func(context, tmid)
|
|
else:
|
|
# We already have the correct value of fmid from the previous loop.
|
|
calc_fmid = True
|
|
|
|
# Quadratic interpolation:
|
|
# Try to find a parabola that passes through the 3 points we have sampled:
|
|
# (t1,f1), (tmid,fmid), (t2,f2).
|
|
q = _QuadInterp(tmid.ut, t2.ut - tmid.ut, f1, fmid, f2)
|
|
if q:
|
|
(q_x, q_ut, q_df_dt) = q
|
|
tq = Time(q_ut)
|
|
fq = func(context, tq)
|
|
if q_df_dt != 0.0:
|
|
dt_guess = abs(fq / q_df_dt)
|
|
if dt_guess < dt_days:
|
|
# The estimated time error is small enough that we can quit now.
|
|
return tq
|
|
|
|
# Try guessing a tighter boundary with the interpolated root at the center.
|
|
dt_guess *= 1.2
|
|
if dt_guess < dt / 10.0:
|
|
tleft = tq.AddDays(-dt_guess)
|
|
tright = tq.AddDays(+dt_guess)
|
|
if (tleft.ut - t1.ut)*(tleft.ut - t2.ut) < 0.0:
|
|
if (tright.ut - t1.ut)*(tright.ut - t2.ut) < 0.0:
|
|
fleft = func(context, tleft)
|
|
fright = func(context, tright)
|
|
if fleft < 0.0 and fright >= 0.0:
|
|
f1 = fleft
|
|
f2 = fright
|
|
t1 = tleft
|
|
t2 = tright
|
|
fmid = fq
|
|
calc_fmid = False
|
|
continue
|
|
|
|
# Quadratic interpolation attempt did not work out.
|
|
# Just divide the region in two parts and pick whichever one appears to contain a root.
|
|
if f1 < 0.0 and fmid >= 0.0:
|
|
t2 = tmid
|
|
f2 = fmid
|
|
continue
|
|
|
|
if fmid < 0.0 and f2 >= 0.0:
|
|
t1 = tmid
|
|
f1 = fmid
|
|
continue
|
|
|
|
# Either there is no ascending zero-crossing in this range
|
|
# or the search window is too wide (more than one zero-crossing).
|
|
return None
|
|
|
|
# END Search
|
|
#----------------------------------------------------------------------------
|
|
|
|
def HelioVector(body, time):
|
|
if body == Body.Pluto:
|
|
return _CalcChebyshev(_pluto, time)
|
|
|
|
if 0 <= body <= len(_vsop):
|
|
return _CalcVsop(_vsop[body], time)
|
|
|
|
if body == Body.Sun:
|
|
return Vector(0.0, 0.0, 0.0, time)
|
|
|
|
if body == Body.Moon:
|
|
e = _CalcEarth(time)
|
|
m = GeoMoon(time)
|
|
return Vector(e.x+m.x, e.y+m.y, e.z+m.z, time)
|
|
|
|
raise InvalidBodyError()
|
|
|
|
|
|
def GeoVector(body, time, aberration):
|
|
if body == Body.Moon:
|
|
return GeoMoon(time)
|
|
|
|
if body == Body.Earth:
|
|
return Vector(0.0, 0.0, 0.0, time)
|
|
|
|
if not aberration:
|
|
# No aberration, so calculate Earth's position once, at the time of observation.
|
|
earth = _CalcEarth(time)
|
|
|
|
# Correct for light-travel time, to get position of body as seen from Earth's center.
|
|
ltime = time
|
|
for iter in range(10):
|
|
h = HelioVector(body, ltime)
|
|
if aberration:
|
|
# Include aberration, so make a good first-order approximation
|
|
# by backdating the Earth's position also.
|
|
# This is confusing, but it works for objects within the Solar System
|
|
# because the distance the Earth moves in that small amount of light
|
|
# travel time (a few minutes to a few hours) is well approximated
|
|
# by a line segment that substends the angle seen from the remote
|
|
# body viewing Earth. That angle is pretty close to the aberration
|
|
# angle of the moving Earth viewing the remote body.
|
|
# In other words, both of the following approximate the aberration angle:
|
|
# (transverse distance Earth moves) / (distance to body)
|
|
# (transverse speed of Earth) / (speed of light).
|
|
earth = _CalcEarth(ltime)
|
|
|
|
geo = Vector(h.x-earth.x, h.y-earth.y, h.z-earth.z, time)
|
|
if body == Body.Sun:
|
|
# The Sun's heliocentric coordinates are always (0,0,0). No need to correct.
|
|
return geo
|
|
|
|
ltime2 = time.AddDays(-geo.Length() / _C_AUDAY)
|
|
dt = abs(ltime2.tt - ltime.tt)
|
|
if dt < 1.0e-9:
|
|
return geo
|
|
|
|
ltime = ltime2
|
|
|
|
raise Error('Light-travel time solver did not converge: dt={}'.format(dt))
|
|
|
|
|
|
def Equator(body, time, observer, ofdate, aberration):
|
|
gc_observer = _geo_pos(time, observer)
|
|
gc = GeoVector(body, time, aberration)
|
|
j2000 = [
|
|
gc.x - gc_observer[0],
|
|
gc.y - gc_observer[1],
|
|
gc.z - gc_observer[2]
|
|
]
|
|
if not ofdate:
|
|
return _vector2radec(j2000)
|
|
temp = _precession(0, j2000, time.tt)
|
|
datevect = _nutation(time, 0, temp)
|
|
return _vector2radec(datevect)
|
|
|
|
@enum.unique
|
|
class Refraction(enum.IntEnum):
|
|
"""Selects if/how to correct for atmospheric refraction.
|
|
|
|
Some functions allow enabling or disabling atmospheric refraction
|
|
for the calculated apparent position of a celestial body
|
|
as seen by an observer on the surface of the Earth.
|
|
|
|
Values
|
|
------
|
|
Airless: No atmospheric refraction correction.
|
|
Normal: Recommended correction for standard atmospheric refraction.
|
|
JplHorizons: Used only for compatibility testing with JPL Horizons online tool.
|
|
"""
|
|
Airless = 0
|
|
Normal = 1
|
|
JplHorizons = 2
|
|
|
|
class HorizontalCoordinates:
|
|
def __init__(self, azimuth, altitude, ra, dec):
|
|
self.azimuth = azimuth
|
|
self.altitude = altitude
|
|
self.ra = ra
|
|
self.dec = dec
|
|
|
|
def Horizon(time, observer, ra, dec, refraction):
|
|
if not (Refraction.Airless <= refraction <= Refraction.JplHorizons):
|
|
raise Error('Invalid refraction type: ' + str(refraction))
|
|
|
|
latrad = math.radians(observer.latitude)
|
|
lonrad = math.radians(observer.longitude)
|
|
decrad = math.radians(dec)
|
|
rarad = math.radians(ra * 15.0)
|
|
|
|
sinlat = math.sin(latrad)
|
|
coslat = math.cos(latrad)
|
|
sinlon = math.sin(lonrad)
|
|
coslon = math.cos(lonrad)
|
|
sindc = math.sin(decrad)
|
|
cosdc = math.cos(decrad)
|
|
sinra = math.sin(rarad)
|
|
cosra = math.cos(rarad)
|
|
|
|
uze = [coslat*coslon, coslat*sinlon, sinlat]
|
|
une = [-sinlat*coslon, -sinlat*sinlon, coslat]
|
|
uwe = [sinlon, -coslon, 0.0]
|
|
|
|
angle = -15.0 * _sidereal_time(time)
|
|
uz = _spin(angle, uze)
|
|
un = _spin(angle, une)
|
|
uw = _spin(angle, uwe)
|
|
|
|
p = [cosdc*cosra, cosdc*sinra, sindc]
|
|
|
|
pz = p[0]*uz[0] + p[1]*uz[1] + p[2]*uz[2]
|
|
pn = p[0]*un[0] + p[1]*un[1] + p[2]*un[2]
|
|
pw = p[0]*uw[0] + p[1]*uw[1] + p[2]*uw[2]
|
|
|
|
proj = math.sqrt(pn*pn + pw*pw)
|
|
az = 0.0
|
|
if proj > 0.0:
|
|
az = math.degrees(-math.atan2(pw, pn))
|
|
if az < 0:
|
|
az += 360
|
|
if az >= 360:
|
|
az -= 360
|
|
zd = math.degrees(math.atan2(proj, pz))
|
|
hor_ra = ra
|
|
hor_dec = dec
|
|
|
|
if refraction != Refraction.Airless:
|
|
zd0 = zd
|
|
|
|
# http://extras.springer.com/1999/978-1-4471-0555-8/chap4/horizons/horizons.pdf
|
|
# JPL Horizons says it uses refraction algorithm from
|
|
# Meeus "Astronomical Algorithms", 1991, p. 101-102.
|
|
# I found the following Go implementation:
|
|
# https://github.com/soniakeys/meeus/blob/master/v3/refraction/refract.go
|
|
# This is a translation from the function "Saemundsson" there.
|
|
# I found experimentally that JPL Horizons clamps the angle to 1 degree below the horizon.
|
|
# This is important because the 'refr' formula below goes crazy near hd = -5.11.
|
|
hd = 90.0 - zd
|
|
if hd < -1.0:
|
|
hd = -1.0
|
|
|
|
refr = (1.02 / math.tan(math.radians(hd+10.3/(hd+5.11)))) / 60.0
|
|
|
|
if refraction == Refraction.Normal and zd > 91.0:
|
|
# In "normal" mode we gradually reduce refraction toward the nadir
|
|
# so that we never get an altitude angle less than -90 degrees.
|
|
# When horizon angle is -1 degrees, zd = 91, and the factor is exactly 1.
|
|
# As zd approaches 180 (the nadir), the fraction approaches 0 linearly.
|
|
refr *= (180.0 - zd) / 89.0
|
|
|
|
zd -= refr
|
|
|
|
if refr > 0.0 and zd > 3.0e-4:
|
|
zdrad = math.radians(zd)
|
|
sinzd = math.sin(zdrad)
|
|
coszd = math.cos(zdrad)
|
|
zd0rad = math.radians(zd0)
|
|
sinzd0 = math.sin(zd0rad)
|
|
coszd0 = math.cos(zd0rad)
|
|
|
|
pr = [(((p[j] - coszd0 * uz[j]) / sinzd0)*sinzd + uz[j]*coszd) for j in range(3)]
|
|
proj = math.sqrt(pr[0]*pr[0] + pr[1]*pr[1])
|
|
if proj > 0:
|
|
hor_ra = math.degrees(math.atan2(pr[1], pr[0])) / 15
|
|
if hor_ra < 0:
|
|
hor_ra += 24
|
|
if hor_ra >= 24:
|
|
hor_ra -= 24
|
|
else:
|
|
hor_ra = 0
|
|
hor_dec = math.degrees(math.atan2(pr[2], proj))
|
|
|
|
return HorizontalCoordinates(az, 90.0 - zd, hor_ra, hor_dec)
|
|
|
|
class EclipticCoordinates:
|
|
def __init__(self, ex, ey, ez, elat, elon):
|
|
self.ex = ex
|
|
self.ey = ey
|
|
self.ez = ez
|
|
self.elat = elat
|
|
self.elon = elon
|
|
|
|
def _RotateEquatorialToEcliptic(pos, obliq_radians):
|
|
cos_ob = math.cos(obliq_radians)
|
|
sin_ob = math.sin(obliq_radians)
|
|
ex = +pos[0]
|
|
ey = +pos[1]*cos_ob + pos[2]*sin_ob
|
|
ez = -pos[1]*sin_ob + pos[2]*cos_ob
|
|
xyproj = math.sqrt(ex*ex + ey*ey)
|
|
if xyproj > 0.0:
|
|
elon = math.degrees(math.atan2(ey, ex))
|
|
if elon < 0.0:
|
|
elon += 360.0
|
|
else:
|
|
elon = 0.0
|
|
elat = math.degrees(math.atan2(ez, xyproj))
|
|
return EclipticCoordinates(ex, ey, ez, elat, elon)
|
|
|
|
def SunPosition(time):
|
|
# Correct for light travel time from the Sun.
|
|
# Otherwise season calculations (equinox, solstice) will all be early by about 8 minutes!
|
|
adjusted_time = time.AddDays(-1.0 / _C_AUDAY)
|
|
earth2000 = _CalcEarth(adjusted_time)
|
|
sun2000 = [-earth2000.x, -earth2000.y, -earth2000.z]
|
|
|
|
# Convert to equatorial Cartesian coordinates of date.
|
|
stemp = _precession(0.0, sun2000, adjusted_time.tt)
|
|
sun_ofdate = _nutation(adjusted_time, 0, stemp)
|
|
|
|
# Convert equatorial coordinates to ecliptic coordinates.
|
|
true_obliq = math.radians(adjusted_time._etilt().tobl)
|
|
return _RotateEquatorialToEcliptic(sun_ofdate, true_obliq)
|
|
|
|
def Ecliptic(equ):
|
|
# Based on NOVAS functions equ2ecl() and equ2ecl_vec().
|
|
ob2000 = 0.40909260059599012 # mean obliquity of the J2000 ecliptic in radians
|
|
return _RotateEquatorialToEcliptic([equ.x, equ.y, equ.z], ob2000)
|
|
|
|
def EclipticLongitude(body, time):
|
|
if body == Body.Sun:
|
|
raise InvalidBodyError()
|
|
hv = HelioVector(body, time)
|
|
eclip = Ecliptic(hv)
|
|
return eclip.elon
|
|
|
|
def AngleFromSun(body, time):
|
|
if body == Body.Earth:
|
|
raise EarthNotAllowedError()
|
|
sv = GeoVector(Body.Sun, time, True)
|
|
bv = GeoVector(body, time, True)
|
|
return _AngleBetween(sv, bv)
|
|
|
|
def LongitudeFromSun(body, time):
|
|
if body == Body.Earth:
|
|
raise EarthNotAllowedError()
|
|
sv = GeoVector(Body.Sun, time, True)
|
|
se = Ecliptic(sv)
|
|
bv = GeoVector(body, time, True)
|
|
be = Ecliptic(bv)
|
|
return _NormalizeLongitude(be.elon - se.elon)
|
|
|
|
class ElongationEvent:
|
|
def __init__(self, time, visibility, elongation, ecliptic_separation):
|
|
self.time = time
|
|
self.visibility = visibility
|
|
self.elongation = elongation
|
|
self.ecliptic_separation = ecliptic_separation
|
|
|
|
def Elongation(body, time):
|
|
angle = LongitudeFromSun(body, time)
|
|
if angle > 180.0:
|
|
visibility = 'morning'
|
|
esep = 360.0 - angle
|
|
else:
|
|
visibility = 'evening'
|
|
esep = angle
|
|
angle = AngleFromSun(body, time)
|
|
return ElongationEvent(time, visibility, angle, esep)
|
|
|
|
def _rlon_offset(body, time, direction, targetRelLon):
|
|
plon = EclipticLongitude(body, time)
|
|
elon = EclipticLongitude(Body.Earth, time)
|
|
diff = direction * (elon - plon)
|
|
return _LongitudeOffset(diff - targetRelLon)
|
|
|
|
def SearchRelativeLongitude(body, targetRelLon, startTime):
|
|
if body == Body.Earth:
|
|
raise EarthNotAllowedError()
|
|
if body == Body.Moon or body == Body.Sun:
|
|
raise InvalidBodyError()
|
|
syn = _SynodicPeriod(body)
|
|
direction = +1 if _IsSuperiorPlanet(body) else -1
|
|
# Iterate until we converge on the desired event.
|
|
# Calculate the error angle, which will be a negative number of degrees,
|
|
# meaning we are "behind" the target relative longitude.
|
|
error_angle = _rlon_offset(body, startTime, direction, targetRelLon)
|
|
if error_angle > 0.0:
|
|
error_angle -= 360.0 # force searching forward in time
|
|
time = startTime
|
|
iter = 0
|
|
while iter < 100:
|
|
# Estimate how many days in the future (positive) or past (negative)
|
|
# we have to go to get closer to the target relative longitude.
|
|
day_adjust = (-error_angle/360.0) * syn
|
|
time = time.AddDays(day_adjust)
|
|
if abs(day_adjust) * _SECONDS_PER_DAY < 1.0:
|
|
return time
|
|
prev_angle = error_angle
|
|
error_angle = _rlon_offset(body, time, direction, targetRelLon)
|
|
if abs(prev_angle) < 30.0 and prev_angle != error_angle:
|
|
# Improve convergence for Mercury/Mars (eccentric orbits)
|
|
# by adjusting the synodic period to more closely match the
|
|
# variable speed of both planets in this part of their respective orbits.
|
|
ratio = prev_angle / (prev_angle - error_angle)
|
|
if 0.5 < ratio < 2.0:
|
|
syn *= ratio
|
|
iter += 1
|
|
raise NoConvergeError()
|
|
|
|
def _neg_elong_slope(body, time):
|
|
dt = 0.1
|
|
t1 = time.AddDays(-dt/2.0)
|
|
t2 = time.AddDays(+dt/2.0)
|
|
e1 = AngleFromSun(body, t1)
|
|
e2 = AngleFromSun(body, t2)
|
|
return (e1 - e2)/dt
|
|
|
|
def SearchMaxElongation(body, startTime):
|
|
if body == Body.Mercury:
|
|
s1 = 50.0
|
|
s2 = 85.0
|
|
elif body == Body.Venus:
|
|
s1 = 40.0
|
|
s2 = 50.0
|
|
else:
|
|
raise InvalidBodyError()
|
|
syn = _SynodicPeriod(body)
|
|
iter = 1
|
|
while iter <= 2:
|
|
plon = EclipticLongitude(body, startTime)
|
|
elon = EclipticLongitude(Body.Earth, startTime)
|
|
rlon = _LongitudeOffset(plon - elon) # clamp to (-180, +180]
|
|
|
|
# The slope function is not well-behaved when rlon is near 0 degrees or 180 degrees
|
|
# because there is a cusp there that causes a discontinuity in the derivative.
|
|
# So we need to guard against searching near such times.
|
|
if rlon >= -s1 and rlon < +s1:
|
|
# Seek to the window [+s1, +s2].
|
|
adjust_days = 0.0
|
|
# Search forward for the time t1 when rel lon = +s1.
|
|
rlon_lo = +s1
|
|
# Search forward for the time t2 when rel lon = +s2.
|
|
rlon_hi = +s2
|
|
elif rlon > +s2 or rlon < -s2:
|
|
# Seek to the next search window at [-s2, -s1].
|
|
adjust_days = 0.0
|
|
# Search forward for the time t1 when rel lon = -s2.
|
|
rlon_lo = -s2
|
|
# Search forward for the time t2 when rel lon = -s1.
|
|
rlon_hi = -s1
|
|
elif rlon >= 0.0:
|
|
# rlon must be in the middle of the window [+s1, +s2].
|
|
# Search BACKWARD for the time t1 when rel lon = +s1.
|
|
adjust_days = -syn / 4.0
|
|
rlon_lo = +s1
|
|
rlon_hi = +s2
|
|
# Search forward from t1 to find t2 such that rel lon = +s2.
|
|
else:
|
|
# rlon must be in the middle of the window [-s2, -s1].
|
|
# Search BACKWARD for the time t1 when rel lon = -s2.
|
|
adjust_days = -syn / 4.0
|
|
rlon_lo = -s2
|
|
# Search forward from t1 to find t2 such that rel lon = -s1.
|
|
rlon_hi = -s1
|
|
|
|
t_start = startTime.AddDays(adjust_days)
|
|
t1 = SearchRelativeLongitude(body, rlon_lo, t_start)
|
|
if t1 is None:
|
|
return None
|
|
|
|
t2 = SearchRelativeLongitude(body, rlon_hi, t1)
|
|
if t2 is None:
|
|
return None
|
|
|
|
# Now we have a time range [t1,t2] that brackets a maximum elongation event.
|
|
# Confirm the bracketing.
|
|
m1 = _neg_elong_slope(body, t1)
|
|
if m1 >= 0.0:
|
|
raise InternalError() # there is a bug in the bracketing algorithm!
|
|
|
|
m2 = _neg_elong_slope(body, t2)
|
|
if m2 <= 0.0:
|
|
raise InternalError() # there is a bug in the bracketing algorithm!
|
|
|
|
# Use the generic search algorithm to home in on where the slope crosses from negative to positive.
|
|
tx = Search(_neg_elong_slope, body, t1, t2, 10.0)
|
|
if tx is None:
|
|
return None
|
|
|
|
if tx.tt >= startTime.tt:
|
|
return Elongation(body, tx)
|
|
|
|
# This event is in the past (earlier than startTime).
|
|
# We need to search forward from t2 to find the next possible window.
|
|
# We never need to search more than twice.
|
|
startTime = t2.AddDays(1.0)
|
|
iter += 1
|
|
|
|
|
|
def _sun_offset(targetLon, time):
|
|
ecl = SunPosition(time)
|
|
return _LongitudeOffset(ecl.elon - targetLon)
|
|
|
|
def SearchSunLongitude(targetLon, startTime, limitDays):
|
|
t2 = startTime.AddDays(limitDays)
|
|
return Search(_sun_offset, targetLon, startTime, t2, 1.0)
|
|
|
|
def MoonPhase(time):
|
|
return LongitudeFromSun(Body.Moon, time)
|
|
|
|
def _moon_offset(targetLon, time):
|
|
angle = MoonPhase(time)
|
|
return _LongitudeOffset(angle - targetLon)
|
|
|
|
def SearchMoonPhase(targetLon, startTime, limitDays):
|
|
# To avoid discontinuities in the _moon_offset function causing problems,
|
|
# we need to approximate when that function will next return 0.
|
|
# We probe it with the start time and take advantage of the fact
|
|
# that every lunar phase repeats roughly every 29.5 days.
|
|
# There is a surprising uncertainty in the quarter timing,
|
|
# due to the eccentricity of the moon's orbit.
|
|
# I have seen up to 0.826 days away from the simple prediction.
|
|
# To be safe, we take the predicted time of the event and search
|
|
# +/-0.9 days around it (a 1.8-day wide window).
|
|
# But we must return None if the final result goes beyond limitDays after startTime.
|
|
uncertainty = 0.9
|
|
ya = _moon_offset(targetLon, startTime)
|
|
if ya > 0.0:
|
|
ya -= 360.0 # force searching forward in time, not backward
|
|
est_dt = -(_MEAN_SYNODIC_MONTH * ya) / 360.0
|
|
dt1 = est_dt - uncertainty
|
|
if dt1 > limitDays:
|
|
return None # not possible for moon phase to occur within the specified window
|
|
dt2 = min(limitDays, est_dt + uncertainty)
|
|
t1 = startTime.AddDays(dt1)
|
|
t2 = startTime.AddDays(dt2)
|
|
return Search(_moon_offset, targetLon, t1, t2, 1.0)
|
|
|
|
class MoonQuarter:
|
|
def __init__(self, quarter, time):
|
|
self.quarter = quarter
|
|
self.time = time
|
|
|
|
def SearchMoonQuarter(startTime):
|
|
angle = MoonPhase(startTime)
|
|
quarter = int(1 + math.floor(angle / 90.0)) % 4
|
|
time = SearchMoonPhase(90.0 * quarter, startTime, 10.0)
|
|
if time is None:
|
|
# The search should never fail. We should always find another lunar quarter.
|
|
raise InternalError()
|
|
return MoonQuarter(quarter, time)
|
|
|
|
def NextMoonQuarter(mq):
|
|
# Skip 6 days past the previous found moon quarter to find the next one.
|
|
# This is less than the minimum possible increment.
|
|
# So far I have seen the interval well contained by the range (6.5, 8.3) days.
|
|
time = mq.time.AddDays(6.0)
|
|
next_mq = SearchMoonQuarter(time)
|
|
# Verify that we found the expected moon quarter.
|
|
if next_mq.quarter != (1 + mq.quarter) % 4:
|
|
raise InternalError()
|
|
return next_mq
|
|
|
|
|
|
class IlluminationInfo:
|
|
def __init__(self, time, mag, phase, helio_dist, geo_dist, gc, hc, ring_tilt):
|
|
self.time = time
|
|
self.mag = mag
|
|
self.phase_angle = phase
|
|
self.phase_fraction = (1.0 + math.cos(math.radians(phase))) / 2.0
|
|
self.helio_dist = helio_dist
|
|
self.geo_dist = geo_dist
|
|
self.gc = gc
|
|
self.hc = hc
|
|
self.ring_tilt = ring_tilt
|
|
|
|
def _MoonMagnitude(phase, helio_dist, geo_dist):
|
|
# https://astronomy.stackexchange.com/questions/10246/is-there-a-simple-analytical-formula-for-the-lunar-phase-brightness-curve
|
|
rad = math.radians(phase)
|
|
mag = -12.717 + 1.49*abs(rad) + 0.0431*(rad**4)
|
|
moon_mean_distance_au = 385000.6 / _KM_PER_AU
|
|
geo_au = geo_dist / moon_mean_distance_au
|
|
mag += 5.0 * math.log10(helio_dist * geo_au)
|
|
return mag
|
|
|
|
def _SaturnMagnitude(phase, helio_dist, geo_dist, gc, time):
|
|
# Based on formulas by Paul Schlyter found here:
|
|
# http://www.stjarnhimlen.se/comp/ppcomp.html#15
|
|
|
|
# We must handle Saturn's rings as a major component of its visual magnitude.
|
|
# Find geocentric ecliptic coordinates of Saturn.
|
|
eclip = Ecliptic(gc)
|
|
|
|
ir = math.radians(28.06) # tilt of Saturn's rings to the ecliptic, in radians
|
|
Nr = math.radians(169.51 + (3.82e-5 * time.tt)) # ascending node of Saturn's rings, in radians
|
|
|
|
# Find tilt of Saturn's rings, as seen from Earth.
|
|
lat = math.radians(eclip.elat)
|
|
lon = math.radians(eclip.elon)
|
|
tilt = math.asin(math.sin(lat)*math.cos(ir) - math.cos(lat)*math.sin(ir)*math.sin(lon-Nr))
|
|
sin_tilt = math.sin(abs(tilt))
|
|
|
|
mag = -9.0 + 0.044*phase
|
|
mag += sin_tilt*(-2.6 + 1.2*sin_tilt)
|
|
mag += 5.0 * math.log10(helio_dist * geo_dist)
|
|
ring_tilt = math.degrees(tilt)
|
|
return (mag, ring_tilt)
|
|
|
|
def _VisualMagnitude(body, phase, helio_dist, geo_dist):
|
|
# For Mercury and Venus, see: https://iopscience.iop.org/article/10.1086/430212
|
|
c0 = c1 = c2 = c3 = 0
|
|
if body == Body.Mercury:
|
|
c0 = -0.60; c1 = +4.98; c2 = -4.88; c3 = +3.02
|
|
elif body == Body.Venus:
|
|
if phase < 163.6:
|
|
c0 = -4.47; c1 = +1.03; c2 = +0.57; c3 = +0.13
|
|
else:
|
|
c0 = +0.98; c1 = -1.02
|
|
elif body == Body.Mars:
|
|
c0 = -1.52; c1 = +1.60
|
|
elif body == Body.Jupiter:
|
|
c0 = -9.40; c1 = +0.50
|
|
elif body == Body.Uranus:
|
|
c0 = -7.19; c1 = +0.25
|
|
elif body == Body.Neptune:
|
|
c0 = -6.87
|
|
elif body == Body.Pluto:
|
|
c0 = -1.00; c1 = +4.00
|
|
else:
|
|
raise InvalidBodyError()
|
|
|
|
x = phase / 100.0
|
|
mag = c0 + x*(c1 + x*(c2 + x*c3))
|
|
mag += 5.0 * math.log10(helio_dist * geo_dist)
|
|
return mag
|
|
|
|
def Illumination(body, time):
|
|
if body == Body.Earth:
|
|
raise EarthNotAllowedError()
|
|
earth = _CalcEarth(time)
|
|
if body == Body.Sun:
|
|
gc = Vector(-earth.x, -earth.y, -earth.z, time)
|
|
hc = Vector(0.0, 0.0, 0.0, time)
|
|
phase = 0.0 # placeholder value; the Sun does not have a phase angle.
|
|
else:
|
|
if body == Body.Moon:
|
|
# For extra numeric precision, use geocentric moon formula directly.
|
|
gc = GeoMoon(time)
|
|
hc = Vector(earth.x + gc.x, earth.y + gc.y, earth.z + gc.z, time)
|
|
else:
|
|
# For planets, heliocentric vector is most direct to calculate.
|
|
hc = HelioVector(body, time)
|
|
gc = Vector(hc.x - earth.x, hc.y - earth.y, hc.z - earth.z, time)
|
|
phase = _AngleBetween(gc, hc)
|
|
|
|
geo_dist = gc.Length() # distance from body to center of Earth
|
|
helio_dist = hc.Length() # distance from body to center of Sun
|
|
ring_tilt = None # only reported for Saturn
|
|
if body == Body.Sun:
|
|
mag = -0.17 + 5.0*math.log10(geo_dist / _AU_PER_PARSEC)
|
|
elif body == Body.Moon:
|
|
mag = _MoonMagnitude(phase, helio_dist, geo_dist)
|
|
elif body == Body.Saturn:
|
|
mag, ring_tilt = _SaturnMagnitude(phase, helio_dist, geo_dist, gc, time)
|
|
else:
|
|
mag = _VisualMagnitude(body, phase, helio_dist, geo_dist)
|
|
return IlluminationInfo(time, mag, phase, helio_dist, geo_dist, gc, hc, ring_tilt)
|
|
|
|
def _mag_slope(body, time):
|
|
# The Search() function finds a transition from negative to positive values.
|
|
# The derivative of magnitude y with respect to time t (dy/dt)
|
|
# is negative as an object gets brighter, because the magnitude numbers
|
|
# get smaller. At peak magnitude dy/dt = 0, then as the object gets dimmer,
|
|
# dy/dt > 0.
|
|
dt = 0.01
|
|
t1 = time.AddDays(-dt/2)
|
|
t2 = time.AddDays(+dt/2)
|
|
y1 = Illumination(body, t1)
|
|
y2 = Illumination(body, t2)
|
|
return (y2.mag - y1.mag) / dt
|
|
|
|
def SearchPeakMagnitude(body, startTime):
|
|
# s1 and s2 are relative longitudes within which peak magnitude of Venus can occur.
|
|
s1 = 10.0
|
|
s2 = 30.0
|
|
if body != Body.Venus:
|
|
raise InvalidBodyError()
|
|
|
|
iter = 1
|
|
while iter <= 2:
|
|
# Find current heliocentric relative longitude between the
|
|
# inferior planet and the Earth.
|
|
plon = EclipticLongitude(body, startTime)
|
|
elon = EclipticLongitude(Body.Earth, startTime)
|
|
rlon = _LongitudeOffset(plon - elon)
|
|
# The slope function is not well-behaved when rlon is near 0 degrees or 180 degrees
|
|
# because there is a cusp there that causes a discontinuity in the derivative.
|
|
# So we need to guard against searching near such times.
|
|
if -s1 <= rlon < +s1:
|
|
# Seek to the window [+s1, +s2].
|
|
adjust_days = 0.0
|
|
# Search forward for the time t1 when rel lon = +s1.
|
|
rlon_lo = +s1
|
|
# Search forward for the time t2 when rel lon = +s2.
|
|
rlon_hi = +s2
|
|
elif rlon >= +s2 or rlon < -s2:
|
|
# Seek to the next search window at [-s2, -s1].
|
|
adjust_days = 0.0
|
|
# Search forward for the time t1 when rel lon = -s2.
|
|
rlon_lo = -s2
|
|
# Search forward for the time t2 when rel lon = -s1.
|
|
rlon_hi = -s1
|
|
elif rlon >= 0:
|
|
# rlon must be in the middle of the window [+s1, +s2].
|
|
# Search BACKWARD for the time t1 when rel lon = +s1.
|
|
syn = _SynodicPeriod(body)
|
|
adjust_days = -syn / 4
|
|
rlon_lo = +s1
|
|
# Search forward from t1 to find t2 such that rel lon = +s2.
|
|
rlon_hi = +s2
|
|
else:
|
|
# rlon must be in the middle of the window [-s2, -s1].
|
|
# Search BACKWARD for the time t1 when rel lon = -s2.
|
|
syn = _SynodicPeriod(body)
|
|
adjust_days = -syn / 4
|
|
rlon_lo = -s2
|
|
# Search forward from t1 to find t2 such that rel lon = -s1.
|
|
rlon_hi = -s1
|
|
|
|
t_start = startTime.AddDays(adjust_days)
|
|
t1 = SearchRelativeLongitude(body, rlon_lo, t_start)
|
|
t2 = SearchRelativeLongitude(body, rlon_hi, t1)
|
|
|
|
# Now we have a time range [t1,t2] that brackets a maximum magnitude event.
|
|
# Confirm the bracketing.
|
|
m1 = _mag_slope(body, t1)
|
|
if m1 >= 0.0:
|
|
raise InternalError()
|
|
|
|
m2 = _mag_slope(body, t2)
|
|
if m2 <= 0.0:
|
|
raise InternalError()
|
|
|
|
# Use the generic search algorithm to home in on where the slope crosses from negative to positive.
|
|
tx = Search(_mag_slope, body, t1, t2, 10.0)
|
|
if tx is None:
|
|
# The search should have found the ascending root in the interval [t1, t2].
|
|
raise InternalError()
|
|
|
|
if tx.tt >= startTime.tt:
|
|
return Illumination(body, tx)
|
|
|
|
# This event is in the past (earlier than startTime).
|
|
# We need to search forward from t2 to find the next possible window.
|
|
# We never need to search more than twice.
|
|
startTime = t2.AddDays(1.0)
|
|
iter += 1
|
|
|
|
# We should have found the peak magnitude in at most 2 iterations.
|
|
raise InternalError()
|
|
|
|
class HourAngleEvent:
|
|
def __init__(self, time, hor):
|
|
self.time = time
|
|
self.hor = hor
|
|
|
|
def SearchHourAngle(body, observer, hourAngle, startTime):
|
|
if body == Body.Earth:
|
|
raise EarthNotAllowedError()
|
|
|
|
if hourAngle < 0.0 or hourAngle >= 24.0:
|
|
raise Error('Invalid hour angle.')
|
|
|
|
iter = 0
|
|
time = startTime
|
|
while True:
|
|
iter += 1
|
|
# Calculate Greenwich Apparent Sidereal Time (GAST) at the given time.
|
|
gast = _sidereal_time(time)
|
|
ofdate = Equator(body, time, observer, True, True)
|
|
|
|
# Calculate the adjustment needed in sidereal time to bring
|
|
# the hour angle to the desired value.
|
|
delta_sidereal_hours = math.fmod(((hourAngle + ofdate.ra - observer.longitude/15) - gast), 24.0)
|
|
if iter == 1:
|
|
# On the first iteration, always search forward in time.
|
|
if delta_sidereal_hours < 0.0:
|
|
delta_sidereal_hours += 24.0
|
|
else:
|
|
# On subsequent iterations, we make the smallest possible adjustment,
|
|
# either forward or backward in time.
|
|
if delta_sidereal_hours < -12.0:
|
|
delta_sidereal_hours += 24.0
|
|
elif delta_sidereal_hours > +12.0:
|
|
delta_sidereal_hours -= 24.0
|
|
|
|
# If the error is tolerable (less than 0.1 seconds), stop searching.
|
|
if abs(delta_sidereal_hours) * 3600.0 < 0.1:
|
|
hor = Horizon(time, observer, ofdate.ra, ofdate.dec, Refraction.Normal)
|
|
return HourAngleEvent(time, hor)
|
|
|
|
# We need to loop another time to get more accuracy.
|
|
# Update the terrestrial time (in solar days) adjusting by sidereal time.
|
|
delta_days = (delta_sidereal_hours / 24.0) * _SOLAR_DAYS_PER_SIDEREAL_DAY
|
|
time = time.AddDays(delta_days)
|
|
|
|
@enum.unique
|
|
class Direction(enum.IntEnum):
|
|
"""Indicates whether a body is rising above or setting below the horizon.
|
|
|
|
Specifies the direction of a rising or setting event for a body.
|
|
For example, `Direction.Rise` is used to find sunrise times,
|
|
and `Direction.Set` is used to find sunset times.
|
|
|
|
Values
|
|
------
|
|
Rise: First appearance of a body as it rises above the horizon.
|
|
Set: Last appearance of a body as it sinks below the horizon.
|
|
"""
|
|
Rise = +1
|
|
Set = -1
|
|
|
|
class _peak_altitude_context:
|
|
def __init__(self, body, direction, observer, body_radius_au):
|
|
self.body = body
|
|
self.direction = direction
|
|
self.observer = observer
|
|
self.body_radius_au = body_radius_au
|
|
|
|
def _peak_altitude(context, time):
|
|
# Return the angular altitude above or below the horizon
|
|
# of the highest part (the peak) of the given object.
|
|
# This is defined as the apparent altitude of the center of the body plus
|
|
# the body's angular radius.
|
|
# The 'direction' parameter controls whether the angle is measured
|
|
# positive above the horizon or positive below the horizon,
|
|
# depending on whether the caller wants rise times or set times, respectively.
|
|
|
|
ofdate = Equator(context.body, time, context.observer, True, True)
|
|
|
|
# We calculate altitude without refraction, then add fixed refraction near the horizon.
|
|
# This gives us the time of rise/set without the extra work.
|
|
hor = Horizon(time, context.observer, ofdate.ra, ofdate.dec, Refraction.Airless)
|
|
alt = hor.altitude + math.degrees(context.body_radius_au / ofdate.dist)
|
|
return context.direction * (alt + _REFRACTION_NEAR_HORIZON)
|
|
|
|
def SearchRiseSet(body, observer, direction, startTime, limitDays):
|
|
if body == Body.Earth:
|
|
raise EarthNotAllowedError()
|
|
elif body == Body.Sun:
|
|
body_radius = _SUN_RADIUS_AU
|
|
elif body == Body.Moon:
|
|
body_radius = _MOON_RADIUS_AU
|
|
else:
|
|
body_radius = 0.0
|
|
|
|
if direction == Direction.Rise:
|
|
ha_before = 12.0 # minimum altitude (bottom) happens BEFORE the body rises.
|
|
ha_after = 0.0 # maximum altitude (culmination) happens AFTER the body rises.
|
|
elif direction == Direction.Set:
|
|
ha_before = 0.0 # culmination happens BEFORE the body sets.
|
|
ha_after = 12.0 # bottom happens AFTER the body sets.
|
|
else:
|
|
raise Error('Invalid value for direction parameter')
|
|
|
|
context = _peak_altitude_context(body, direction, observer, body_radius)
|
|
|
|
# See if the body is currently above/below the horizon.
|
|
# If we are looking for next rise time and the body is below the horizon,
|
|
# we use the current time as the lower time bound and the next culmination
|
|
# as the upper bound.
|
|
# If the body is above the horizon, we search for the next bottom and use it
|
|
# as the lower bound and the next culmination after that bottom as the upper bound.
|
|
# The same logic applies for finding set times, only we swap the hour angles.
|
|
time_start = startTime
|
|
alt_before = _peak_altitude(context, time_start)
|
|
if alt_before > 0.0:
|
|
# We are past the sought event, so we have to wait for the next "before" event (culm/bottom).
|
|
evt_before = SearchHourAngle(body, observer, ha_before, time_start)
|
|
time_before = evt_before.time
|
|
alt_before = _peak_altitude(context, time_before)
|
|
else:
|
|
# We are before or at the sought ebvent, so we find the next "after" event (bottom/culm),
|
|
# and use the current time as the "before" event.
|
|
time_before = time_start
|
|
|
|
evt_after = SearchHourAngle(body, observer, ha_after, time_before)
|
|
alt_after = _peak_altitude(context, evt_after.time)
|
|
|
|
while True:
|
|
if alt_before <= 0.0 and alt_after > 0.0:
|
|
# Search between the "before time" and the "after time" for the desired event.
|
|
event_time = Search(_peak_altitude, context, time_before, evt_after.time, 1.0)
|
|
if event_time is not None:
|
|
return event_time
|
|
# We didn't find the desired event, so use the "after" time to find the next "before" event.
|
|
evt_before = SearchHourAngle(body, observer, ha_before, evt_after.time)
|
|
evt_after = SearchHourAngle(body, observer, ha_after, evt_before.time)
|
|
if evt_before.time.ut >= time_start.ut + limitDays:
|
|
return None
|
|
time_before = evt_before.time
|
|
alt_before = _peak_altitude(context, evt_before.time)
|
|
alt_after = _peak_altitude(context, evt_after.time)
|
|
|
|
class SeasonInfo:
|
|
def __init__(self, mar_equinox, jun_solstice, sep_equinox, dec_solstice):
|
|
self.mar_equinox = mar_equinox
|
|
self.jun_solstice = jun_solstice
|
|
self.sep_equinox = sep_equinox
|
|
self.dec_solstice = dec_solstice
|
|
|
|
def _FindSeasonChange(targetLon, year, month, day):
|
|
startTime = Time.Make(year, month, day, 0, 0, 0)
|
|
time = SearchSunLongitude(targetLon, startTime, 4.0)
|
|
if time is None:
|
|
# We should always be able to find a season change.
|
|
raise InternalError()
|
|
return time
|
|
|
|
def Seasons(year):
|
|
mar_equinox = _FindSeasonChange(0, year, 3, 19)
|
|
jun_solstice = _FindSeasonChange(90, year, 6, 19)
|
|
sep_equinox = _FindSeasonChange(180, year, 9, 21)
|
|
dec_solstice = _FindSeasonChange(270, year, 12, 20)
|
|
return SeasonInfo(mar_equinox, jun_solstice, sep_equinox, dec_solstice)
|
|
|
|
def _MoonDistance(time):
|
|
return _CalcMoon(time).distance_au
|
|
|
|
def _distance_slope(direction, time):
|
|
dt = 0.001
|
|
t1 = time.AddDays(-dt/2.0)
|
|
t2 = time.AddDays(+dt/2.0)
|
|
dist1 = _MoonDistance(t1)
|
|
dist2 = _MoonDistance(t2)
|
|
return direction * (dist2 - dist1) / dt
|
|
|
|
@enum.unique
|
|
class ApsisKind(enum.IntEnum):
|
|
"""Represents whether a satellite is at a closest or farthest point in its orbit.
|
|
|
|
An apsis is a point in a satellite's orbit that is closest to,
|
|
or farthest from, the body it orbits (its primary).
|
|
`ApsisKind` is an enumerated type that indicates which of these
|
|
two cases applies to a particular apsis event.
|
|
|
|
Values
|
|
------
|
|
Pericenter: The satellite is at its closest point to its primary.
|
|
Apocenter: The satellite is at its farthest point from its primary.
|
|
Invalid: A placeholder for an undefined, unknown, or invalid apsis.
|
|
"""
|
|
Pericenter = 0
|
|
Apocenter = 1
|
|
Invalid = 2
|
|
|
|
class Apsis:
|
|
def __init__(self, time, kind, dist_au):
|
|
self.time = time
|
|
self.kind = kind
|
|
self.dist_au = dist_au
|
|
self.dist_km = dist_au * _KM_PER_AU
|
|
|
|
def SearchLunarApsis(startTime):
|
|
increment = 5.0 # number of days to skip on each iteration
|
|
t1 = startTime
|
|
m1 = _distance_slope(+1, t1)
|
|
iter = 0
|
|
while iter * increment < 2.0 * _MEAN_SYNODIC_MONTH:
|
|
t2 = t1.AddDays(increment)
|
|
m2 = _distance_slope(+1, t2)
|
|
if m1 * m2 <= 0.0:
|
|
# There is a change of slope polarity within the time range [t1, t2].
|
|
# Therefore this time range contains an apsis.
|
|
# Figure out whether it is perigee or apogee.
|
|
if m1 < 0.0 or m2 > 0.0:
|
|
# We found a minimum-distance event: perigee.
|
|
# Search the time range for the time when the slope goes from negative to positive.
|
|
apsis_time = Search(_distance_slope, +1, t1, t2, 1.0)
|
|
kind = ApsisKind.Pericenter
|
|
elif m1 > 0.0 or m2 < 0.0:
|
|
# We found a maximum-distance event: apogee.
|
|
# Search the time range for the time when the slope goes from positive to negative.
|
|
apsis_time = Search(_distance_slope, -1, t1, t2, 1.0)
|
|
kind = ApsisKind.Apocenter
|
|
else:
|
|
# This should never happen. It should not be possible for both slopes to be zero.
|
|
raise InternalError()
|
|
|
|
if apsis_time is None:
|
|
# We should always be able to find a lunar apsis!
|
|
raise InternalError()
|
|
|
|
dist = _MoonDistance(apsis_time)
|
|
return Apsis(apsis_time, kind, dist)
|
|
|
|
# We have not yet found a slope polarity change. Keep searching.
|
|
t1 = t2
|
|
m1 = m2
|
|
iter += 1
|
|
|
|
# It should not be possible to fail to find an apsis within 2 synodic months.
|
|
raise InternalError()
|
|
|
|
|
|
def NextLunarApsis(apsis):
|
|
skip = 11.0 # number of days to skip to start looking for next apsis event
|
|
time = apsis.time.AddDays(skip)
|
|
next = SearchLunarApsis(time)
|
|
# Verify that we found the opposite apsis from the previous one.
|
|
if apsis.kind not in [ApsisKind.Apocenter, ApsisKind.Pericenter]:
|
|
raise Error('Parameter "apsis" contains an invalid "kind" value.')
|
|
if next.kind + apsis.kind != 1:
|
|
raise InternalError()
|
|
return next
|