# -*- coding: utf-8 -*-
# PyPlanets: Object-oriented refactoring of PyMeeus, a Python library implementing astronomical algorithms.
# Copyright (C) 2020 Martin Fünffinger
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from math import sin, cos, sqrt, log10
from pyplanets.core.angle import Angle
from pyplanets.core.epoch import Epoch
from pyplanets.parameters.venus_params import VSOP87_L, VSOP87_B, VSOP87_R, ORBITAL_ELEM, ORBITAL_ELEM_J2000
from pyplanets.planets.planet import Planet
"""
.. module:: Venus
:synopsis: Class to model Venus planet
:license: GNU Lesser General Public License v3 (LGPLv3)
.. moduleauthor:: Martin Fünffinger
"""
[docs]class Venus(Planet):
"""
Class Venus models that planet.
"""
[docs] def __init__(self, epoch):
super().__init__(epoch, VSOP87_L, VSOP87_B, VSOP87_R, ORBITAL_ELEM, ORBITAL_ELEM_J2000)
[docs] def inferior_conjunction(self) -> Epoch:
"""This method computes the time of the inferior conjunction closest to
the given epoch.
:returns: The time when the inferior conjunction happens, as an Epoch
:rtype: :py:class:`Epoch`
:raises: ValueError if input epoch outside the -2000/4000 range.
>>> epoch = Epoch(1882, 12, 1.0)
>>> conjunction = Venus(epoch).inferior_conjunction()
>>> y, m, d = conjunction.get_date()
>>> print(y)
1882
>>> print(m)
12
>>> print(round(d, 1))
6.7
"""
# Check that the input epoch is within valid range
y = self.epoch.year()
if y < -2000.0 or y > 4000.0:
raise ValueError("Epoch outside the -2000/4000 range")
# Set some specific constants for Venus' inferior conjunction
a = 2451996.706
b = 583.921361
m0 = 82.7311
m1 = 215.513058
k = round((365.2425 * y + 1721060.0 - a) / b)
jde0 = a + k * b
m = m0 + k * m1
m = Angle(m).to_positive()
m = m.rad()
t = (jde0 - 2451545.0) / 36525.0
corr = (-0.0096 + t * (0.0002 - t * 0.00001) +
sin(m) * (2.0009 + t * (-0.0033 - t * 0.00001)) +
cos(m) * (0.598 + t * (-0.0104 + t * 0.00001)) +
sin(2.0 * m) * (0.0967 + t * (-0.0018 - t * 0.00003)) +
cos(2.0 * m) * (0.0913 + t * (0.0009 - t * 0.00002)) +
sin(3.0 * m) * (0.0046 - t * 0.0002) +
cos(3.0 * m) * (0.0079 + t * 0.0001))
to_return = jde0 + corr
return Epoch(to_return)
[docs] def superior_conjunction(self) -> Epoch:
"""This method computes the time of the superior conjunction closest to
the given epoch.
:returns: The time when the superior conjunction happens, as an Epoch
:rtype: :py:class:`Epoch`
:raises: ValueError if input epoch outside the -2000/4000 range.
>>> epoch = Epoch(1993, 10, 1.0)
>>> conjunction = Venus(epoch).superior_conjunction()
>>> y, m, d = conjunction.get_date()
>>> print(y)
1994
>>> print(m)
1
>>> print(round(d, 2))
17.05
"""
# Check that the input epoch is within valid range
y = self.epoch.year()
if y < -2000.0 or y > 4000.0:
raise ValueError("Epoch outside the -2000/4000 range")
# Set some specific constants for Venus' superior conjunction
a = 2451704.746
b = 583.921361
m0 = 154.9745
m1 = 215.513058
k = round((365.2425 * y + 1721060.0 - a) / b)
jde0 = a + k * b
m = m0 + k * m1
m = Angle(m).to_positive()
m = m.rad()
t = (jde0 - 2451545.0) / 36525.0
corr = (0.0099 + t * (-0.0002 - t * 0.00001) +
sin(m) * (4.1991 + t * (-0.0121 - t * 0.00003)) +
cos(m) * (-0.6095 + t * (0.0102 - t * 0.00002)) +
sin(2.0 * m) * (0.25 + t * (-0.0028 - t * 0.00003)) +
cos(2.0 * m) * (0.0063 + t * (0.0025 - t * 0.00002)) +
sin(3.0 * m) * (0.0232 + t * (-0.0005 - t * 0.00001)) +
cos(3.0 * m) * (0.0031 + t * 0.0004))
to_return = jde0 + corr
return Epoch(to_return)
[docs] def western_elongation(self) -> (Epoch, Angle):
"""This method computes the time of the western elongation closest to
the given epoch, as well as the corresponding maximum elongation angle.
:returns: A tuple with the time when the western elongation happens, as
an Epoch, and the maximum elongation angle, as an Angle
:rtype: tuple
:raises: ValueError if input epoch outside the -2000/4000 range.
>>> epoch = Epoch(2019, 1, 1.0)
>>> time, elongation = Venus(epoch).western_elongation()
>>> y, m, d = time.get_date()
>>> print(y)
2019
>>> print(m)
1
>>> print(round(d, 4))
6.1895
>>> print(round(elongation, 4))
46.9571
"""
# Check that the input epoch is within valid range
y = self.epoch.year()
if y < -2000.0 or y > 4000.0:
raise ValueError("Epoch outside the -2000/4000 range")
# Set some specific constants for Venus' inferior conjunction
a = 2451996.706
b = 583.921361
m0 = 82.7311
m1 = 215.513058
k = round((365.2425 * y + 1721060.0 - a) / b)
jde0 = a + k * b
m = m0 + k * m1
m = Angle(m).to_positive()
m = m.rad()
t = (jde0 - 2451545.0) / 36525.0
corr = (70.7462 - t * t * 0.00001 +
sin(m) * (1.1218 + t * (-0.0025 - t * 0.00001)) +
cos(m) * (0.4538 - t * 0.0066) +
sin(2.0 * m) * (0.132 + t * (0.002 - t * 0.00003)) +
cos(2.0 * m) * (-0.0702 + t * (0.0022 + t * 0.00004)) +
sin(3.0 * m) * (0.0062 - t * 0.0001) +
cos(3.0 * m) * (0.0015 - t * t * 0.00001))
elon = (46.3245 +
sin(m) * (-0.5366 + t * (-0.0003 + t * 0.00001)) +
cos(m) * (0.3097 + t * (0.0016 - t * 0.00001)) +
sin(2.0 * m) * (-0.0163) +
cos(2.0 * m) * (-0.0075 + t * 0.0001))
elon = Angle(elon).to_positive()
to_return = jde0 + corr
return Epoch(to_return), elon
[docs] def eastern_elongation(self) -> (Epoch, Angle):
"""This method computes the time of the eastern elongation closest to
the given epoch, as well as the corresponding maximum elongation angle.
:returns: A tuple with the time when the eastern elongation happens, as
an Epoch, and the maximum elongation angle, as an Angle
:rtype: tuple
:raises: ValueError if input epoch outside the -2000/4000 range.
>>> epoch = Epoch(2019, 10, 1.0)
>>> time, elongation = Venus(epoch).eastern_elongation()
>>> y, m, d = time.get_date()
>>> print(y)
2020
>>> print(m)
3
>>> print(round(d, 4))
24.9179
>>> print(round(elongation, 4))
46.078
"""
# Check that the input epoch is within valid range
y = self.epoch.year()
if y < -2000.0 or y > 4000.0:
raise ValueError("Epoch outside the -2000/4000 range")
# Set some specific constants for Venus' inferior conjunction
a = 2451996.706
b = 583.921361
m0 = 82.7311
m1 = 215.513058
k = round((365.2425 * y + 1721060.0 - a) / b)
jde0 = a + k * b
m = m0 + k * m1
m = Angle(m).to_positive()
m = m.rad()
t = (jde0 - 2451545.0) / 36525.0
corr = (-70.76 + t * (0.0002 - t * 0.00001) +
sin(m) * (1.0282 + t * (-0.001 - t * 0.00001)) +
cos(m) * (0.2761 - t * 0.006) +
sin(2.0 * m) * (-0.0438 + t * (-0.0023 + t * 0.00002)) +
cos(2.0 * m) * (0.166 + t * (-0.0037 - t * 0.00004)) +
sin(3.0 * m) * (0.0036 + t * 0.0001) +
cos(3.0 * m) * (-0.0011 + t * t * 0.00001))
elon = (46.3173 + t * 0.0001 +
sin(m) * (0.6916 - t * 0.0024) +
cos(m) * (0.6676 - t * 0.0045) +
sin(2.0 * m) * (0.0309 - t * 0.0002) +
cos(2.0 * m) * (0.0036 - t * 0.0001))
elon = Angle(elon).to_positive()
to_return = jde0 + corr
return Epoch(to_return), elon
[docs] def station_longitude_1(self) -> Epoch:
"""This method computes the time of the 1st station in longitude
(i.e. when the planet is stationary and begins to move westward -
retrograde - among the starts) closest to the given epoch.
:returns: Time when the 1st station in longitude happens, as an Epoch
:rtype: :py:class:`Epoch`
:raises: ValueError if input epoch outside the -2000/4000 range.
>>> epoch = Epoch(2018, 12, 1.0)
>>> sta1 = Venus(epoch).station_longitude_1()
>>> y, m, d = sta1.get_date()
>>> print(y)
2018
>>> print(m)
10
>>> print(round(d, 4))
5.7908
"""
# Check that the input epoch is within valid range
y = self.epoch.year()
if y < -2000.0 or y > 4000.0:
raise ValueError("Epoch outside the -2000/4000 range")
# Set some specific constants for Venus' inferior conjunction
a = 2451996.706
b = 583.921361
m0 = 82.7311
m1 = 215.513058
k = round((365.2425 * y + 1721060.0 - a) / b)
jde0 = a + k * b
m = m0 + k * m1
m = Angle(m).to_positive()
m = m.rad()
t = (jde0 - 2451545.0) / 36525.0
corr = (-21.0672 + t * (0.0002 - t * 0.00001) +
sin(m) * (1.9396 + t * (-0.0029 - t * 0.00001)) +
cos(m) * (1.0727 - t * 0.0102) +
sin(2.0 * m) * (0.0404 + t * (-0.0023 - t * 0.00001)) +
cos(2.0 * m) * (0.1305 + t * (-0.0004 - t * 0.00003)) +
sin(3.0 * m) * (-0.0007 - t * 0.0002) +
cos(3.0 * m) * (0.0098))
to_return = jde0 + corr
return Epoch(to_return)
[docs] def station_longitude_2(self) -> Epoch:
"""This method computes the time of the 1st station in longitude
(i.e. when the planet is stationary and begins to move eastward -
prograde - among the starts) closest to the given epoch.
:returns: Time when the 2nd station in longitude happens, as an Epoch
:rtype: :py:class:`Epoch`
:raises: ValueError if input epoch outside the -2000/4000 range.
>>> epoch = Epoch(2018, 12, 1.0)
>>> sta2 = Venus(epoch).station_longitude_2()
>>> y, m, d = sta2.get_date()
>>> print(y)
2018
>>> print(m)
11
>>> print(round(d, 4))
16.439
"""
# Check that the input epoch is within valid range
y = self.epoch.year()
if y < -2000.0 or y > 4000.0:
raise ValueError("Epoch outside the -2000/4000 range")
# Set some specific constants for Venus' inferior conjunction
a = 2451996.706
b = 583.921361
m0 = 82.7311
m1 = 215.513058
k = round((365.2425 * y + 1721060.0 - a) / b)
jde0 = a + k * b
m = m0 + k * m1
m = Angle(m).to_positive()
m = m.rad()
t = (jde0 - 2451545.0) / 36525.0
corr = (21.0623 - t * t * 0.00001 +
sin(m) * (1.9913 + t * (-0.004 - t * 0.00001)) +
cos(m) * (-0.0407 - t * 0.0077) +
sin(2.0 * m) * (0.1351 + t * (-0.0009 - t * 0.00004)) +
cos(2.0 * m) * (0.0303 + t * 0.0019) +
sin(3.0 * m) * (0.0089 - t * 0.0002) +
cos(3.0 * m) * (0.0043 + t * 0.0001))
to_return = jde0 + corr
return Epoch(to_return)
[docs] def aphelion(self) -> Epoch:
"""This method computes the time of Aphelion closer to
a given epoch.
:returns: The epoch of the desired Aphelion
:rtype: :py:class:`Epoch`
>>> epoch = Epoch(1979, 2, 1.0)
>>> e = Venus(epoch).aphelion()
>>> y, m, d, h, mi, s = e.get_full_date()
>>> print(y)
1979
>>> print(m)
4
>>> print(d)
22
>>> print(h)
12
"""
# First approximation
k = 1.62549 * (self.epoch.year() - 2000.53)
k = round(k + 0.5) - 0.5
jde = 2451738.233 + k * (224.7008188 - k * 0.0000000327)
# Compute the neighboring epochs half a day before and after
sol = self._interpolate_jde(jde, delta=0.5)
return Epoch(sol)
[docs] def perihelion(self) -> Epoch:
"""This method computes the time of Perihelion closer to
a given epoch.
:returns: The epoch of the desired Perihelion
:rtype: :py:class:`Epoch`
>>> epoch = Epoch(1978, 10, 15.0)
>>> e = Venus(epoch).perihelion()
>>> y, m, d, h, mi, s = e.get_full_date()
>>> print(y)
1978
>>> print(m)
12
>>> print(d)
31
>>> print(h)
4
"""
# First approximation
k = 1.62549 * (self.epoch.year() - 2000.53)
k = round(k)
jde = 2451738.233 + k * (224.7008188 - k * 0.0000000327)
# Compute the neighboring epochs half a day before and after
sol = self._interpolate_jde(jde, delta=0.5)
return Epoch(sol)
[docs] @staticmethod
def illuminated_fraction(epoch: Epoch):
"""This function computes an approximation of the illuminated fraction
of Venus disk, as seen from Earth.
:param epoch: Epoch to compute the illuminated fraction
:type epoch: :py:class:`Epoch`
:returns: Illuminated fraction of Venus disk
:rtype: float
:raises: TypeError if input values are of wrong type.
>>> epoch = Epoch(1992, 12, 20)
>>> k = Venus.illuminated_fraction(epoch)
>>> print(round(k, 2))
0.64
"""
if not isinstance(epoch, Epoch):
raise TypeError("Invalid input types")
t = (epoch.jde() - 2451545.0) / 36525.0
v = Angle(261.51 + 22518.443 * t)
m = Angle(177.53 + 35999.050 * t)
n = Angle(50.42 + 58517.811 * t)
w = Angle(v + 1.91 * sin(m.rad()) + 0.78 * sin(n.rad()))
delta2 = abs(1.52321 + 1.44666 * cos(w.rad()))
delta = sqrt(delta2)
k = ((0.72333 + delta) * (0.72333 + delta) - 1.0) / (2.89332 * delta)
return k
[docs] @staticmethod
def magnitude(sun_dist, earth_dist, phase_angle):
"""This function computes the approximate magnitude of Venus.
:param sun_dist: Distance from Venus to the Sun, in Astronomical Units
:type sun_dist: float
:param earth_dist: Distance from Venus to Earth, in Astronomical Units
:type earth_dist: float
:param phase_angle: Venus phase angle
:type phase_angle: float, :py:class:`Angle`
:returns: Venus' magnitude
:rtype: float
:raises: TypeError if input values are of wrong type.
>>> sun_dist = 0.724604
>>> earth_dist = 0.910947
>>> phase_angle = Angle(72.96)
>>> m = Venus.magnitude(sun_dist, earth_dist, phase_angle)
>>> print(m)
-3.8
"""
if not (isinstance(sun_dist, float) and isinstance(earth_dist, float)
and isinstance(phase_angle, (float, Angle))):
raise TypeError("Invalid input types")
i = float(phase_angle)
m = (-4.0 + 5.0 * log10(sun_dist * earth_dist) + 0.01322 * i
+ 0.0000004247 * i * i * i)
return round(m, 1)