Skip to content

Commit

Permalink
feat: add support for maximal elongations of Mercury and Venus
Browse files Browse the repository at this point in the history
  • Loading branch information
Jérôme Deuchnord committed Feb 21, 2020
1 parent 410c553 commit a382669
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 120 deletions.
3 changes: 2 additions & 1 deletion kosmorrolib/assets/pdf/template.tex
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
\usepackage{graphicx}
\usepackage{hyperref}

% Fix non-break spaces issues
% Fix Unicode issues
\DeclareUnicodeCharacter{202F}{~}
\DeclareUnicodeCharacter{00B0}{$^\circ$}

\hypersetup{pdfinfo={
Title={+++DOCUMENT-TITLE+++},
Expand Down
54 changes: 0 additions & 54 deletions kosmorrolib/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,13 @@

from shutil import rmtree
from pathlib import Path
from typing import Union

from skyfield.api import Loader
from skyfield.timelib import Time
from skyfield.nutationlib import iau2000b

from .data import Star, Planet, Satellite, MOON_PHASES, MoonPhase
from .i18n import _

CACHE_FOLDER = str(Path.home()) + '/.kosmorro-cache'

MONTHS = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC']

ASTERS = [Star(_('Sun'), 'SUN'),
Satellite(_('Moon'), 'MOON'),
Planet(_('Mercury'), 'MERCURY'),
Planet(_('Venus'), 'VENUS'),
Planet(_('Mars'), 'MARS'),
Planet(_('Jupiter'), 'JUPITER BARYCENTER'),
Planet(_('Saturn'), 'SATURN BARYCENTER'),
Planet(_('Uranus'), 'URANUS BARYCENTER'),
Planet(_('Neptune'), 'NEPTUNE BARYCENTER'),
Planet(_('Pluto'), 'PLUTO BARYCENTER')]


def get_loader():
return Loader(CACHE_FOLDER)
Expand All @@ -63,43 +46,6 @@ def clear_cache():
rmtree(CACHE_FOLDER)


def skyfield_to_moon_phase(times: [Time], vals: [int], now: Time) -> Union[MoonPhase, None]:
tomorrow = get_timescale().utc(now.utc_datetime().year, now.utc_datetime().month, now.utc_datetime().day + 1)

phases = list(MOON_PHASES.keys())
current_phase = None
current_phase_time = None
next_phase_time = None
i = 0

if len(times) == 0:
return None

for i, time in enumerate(times):
if now.utc_iso() <= time.utc_iso():
if vals[i] in [0, 2, 4, 6]:
if time.utc_datetime() < tomorrow.utc_datetime():
current_phase_time = time
current_phase = phases[vals[i]]
else:
i -= 1
current_phase_time = None
current_phase = phases[vals[i]]
else:
current_phase = phases[vals[i]]

break

for j in range(i + 1, len(times)):
if vals[j] in [0, 2, 4, 6]:
next_phase_time = times[j]
break

return MoonPhase(current_phase,
current_phase_time.utc_datetime() if current_phase_time is not None else None,
next_phase_time.utc_datetime() if next_phase_time is not None else None)


def flatten_list(the_list: list):
new_list = []
for item in the_list:
Expand Down
78 changes: 69 additions & 9 deletions kosmorrolib/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
from typing import Union
from datetime import datetime

from skyfield.api import Topos
from skyfield.api import Topos, Time
from skyfield.vectorlib import VectorSum as SkfPlanet

from .core import get_skf_objects, get_timescale
from .i18n import _

MOON_PHASES = {
Expand All @@ -37,7 +39,8 @@

EVENTS = {
'OPPOSITION': {'message': _('%s is in opposition')},
'CONJUNCTION': {'message': _('%s and %s are in conjunction')}
'CONJUNCTION': {'message': _('%s and %s are in conjunction')},
'MAXIMAL_ELONGATION': {'message': _("%s's largest elongation")}
}


Expand Down Expand Up @@ -115,6 +118,9 @@ def __init__(self,
self.skyfield_name = skyfield_name
self.ephemerides = ephemerides

def get_skyfield_object(self) -> SkfPlanet:
return get_skf_objects()[self.skyfield_name]

@abstractmethod
def get_type(self) -> str:
pass
Expand Down Expand Up @@ -142,23 +148,77 @@ def get_type(self) -> str:

class Event:
def __init__(self, event_type: str, objects: [Object], start_time: datetime,
end_time: Union[datetime, None] = None):
end_time: Union[datetime, None] = None, details: str = None):
if event_type not in EVENTS.keys():
raise ValueError('event_type parameter must be one of the following: %s (got %s)' % (
', '.join(EVENTS.keys()),
event_type)
)
accepted_types = ', '.join(EVENTS.keys())
raise ValueError('event_type parameter must be one of the following: %s (got %s)' % (accepted_types,
event_type))

self.event_type = event_type
self.objects = objects
self.start_time = start_time
self.end_time = end_time
self.details = details

def get_description(self) -> str:
return EVENTS[self.event_type]['message'] % self._get_objects_name()
def get_description(self, show_details: bool = True) -> str:
description = EVENTS[self.event_type]['message'] % self._get_objects_name()
if show_details and self.details is not None:
description += ' ({:s})'.format(self.details)
return description

def _get_objects_name(self):
if len(self.objects) == 1:
return self.objects[0].name

return tuple(object.name for object in self.objects)


def skyfield_to_moon_phase(times: [Time], vals: [int], now: Time) -> Union[MoonPhase, None]:
tomorrow = get_timescale().utc(now.utc_datetime().year, now.utc_datetime().month, now.utc_datetime().day + 1)

phases = list(MOON_PHASES.keys())
current_phase = None
current_phase_time = None
next_phase_time = None
i = 0

if len(times) == 0:
return None

for i, time in enumerate(times):
if now.utc_iso() <= time.utc_iso():
if vals[i] in [0, 2, 4, 6]:
if time.utc_datetime() < tomorrow.utc_datetime():
current_phase_time = time
current_phase = phases[vals[i]]
else:
i -= 1
current_phase_time = None
current_phase = phases[vals[i]]
else:
current_phase = phases[vals[i]]

break

for j in range(i + 1, len(times)):
if vals[j] in [0, 2, 4, 6]:
next_phase_time = times[j]
break

return MoonPhase(current_phase,
current_phase_time.utc_datetime() if current_phase_time is not None else None,
next_phase_time.utc_datetime() if next_phase_time is not None else None)


MONTHS = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC']

ASTERS = [Star(_('Sun'), 'SUN'),
Satellite(_('Moon'), 'MOON'),
Planet(_('Mercury'), 'MERCURY'),
Planet(_('Venus'), 'VENUS'),
Planet(_('Mars'), 'MARS'),
Planet(_('Jupiter'), 'JUPITER BARYCENTER'),
Planet(_('Saturn'), 'SATURN BARYCENTER'),
Planet(_('Uranus'), 'URANUS BARYCENTER'),
Planet(_('Neptune'), 'NEPTUNE BARYCENTER'),
Planet(_('Pluto'), 'PLUTO BARYCENTER')]
18 changes: 11 additions & 7 deletions kosmorrolib/ephemerides.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
from skyfield.timelib import Time
from skyfield.constants import tau

from .data import Object, Position, AsterEphemerides, MoonPhase
from .core import get_skf_objects, get_timescale, get_iau2000b, ASTERS, MONTHS, skyfield_to_moon_phase
from .data import Object, Position, AsterEphemerides, MoonPhase, ASTERS, MONTHS, skyfield_to_moon_phase
from .core import get_skf_objects, get_timescale, get_iau2000b

RISEN_ANGLE = -0.8333

Expand Down Expand Up @@ -88,6 +88,7 @@ def is_risen(time: Time) -> bool:
rise_times, arr = find_discrete(start_time, end_time, is_risen)
try:
culmination_time, _ = find_maxima(start_time, end_time, f=get_angle, epsilon=1./3600/24, num=12)
culmination_time = culmination_time[0] if len(culmination_time) > 0 else None
except ValueError:
culmination_time = None

Expand All @@ -98,12 +99,15 @@ def is_risen(time: Time) -> bool:
rise_time = rise_times[0] if arr[0] else None
set_time = rise_times[0] if not arr[0] else None

culmination_time = culmination_time[0] if culmination_time is not None else None

# Convert the Time instances to Python datetime objects
rise_time = rise_time.utc_datetime().replace(microsecond=0)
culmination_time = culmination_time.utc_datetime().replace(microsecond=0)
set_time = set_time.utc_datetime().replace(microsecond=0)
if rise_time is not None:
rise_time = rise_time.utc_datetime().replace(microsecond=0)

if culmination_time is not None:
culmination_time = culmination_time.utc_datetime().replace(microsecond=0)

if set_time is not None:
set_time = set_time.utc_datetime().replace(microsecond=0) if set_time is not None else None

aster.ephemerides = AsterEphemerides(rise_time, culmination_time, set_time)
return aster
Expand Down
39 changes: 35 additions & 4 deletions kosmorrolib/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@
from datetime import date as date_type

from skyfield.timelib import Time
from skyfield.almanac import find_discrete
from skyfield.searchlib import find_discrete, find_maxima

from .data import Event, Planet
from .core import get_timescale, get_skf_objects, ASTERS, flatten_list
from .data import Event, Planet, ASTERS
from .core import get_timescale, get_skf_objects, flatten_list


def _search_conjunction(start_time: Time, end_time: Time) -> [Event]:
Expand Down Expand Up @@ -91,11 +91,42 @@ def is_oppositing(time: Time) -> [bool]:
return events


def _search_maximal_elongations(start_time: Time, end_time: Time) -> [Event]:
earth = get_skf_objects()['earth']
sun = get_skf_objects()['sun']
aster = None

def get_elongation(time: Time):
sun_pos = (sun - earth).at(time)
aster_pos = (aster.get_skyfield_object() - earth).at(time)
separation = sun_pos.separation_from(aster_pos)
return separation.degrees

get_elongation.rough_period = 1.0

events = []

for aster in ASTERS:
if aster.skyfield_name not in ['MERCURY', 'VENUS']:
continue

times, elongations = find_maxima(start_time, end_time, f=get_elongation, epsilon=1./24/3600, num=12)

for i, time in enumerate(times):
elongation = elongations[i]
print(elongation)
events.append(Event('MAXIMAL_ELONGATION', [aster], time.utc_datetime(),
details='{:.3n}°'.format(elongation)))

return events


def search_events(date: date_type) -> [Event]:
start_time = get_timescale().utc(date.year, date.month, date.day)
end_time = get_timescale().utc(date.year, date.month, date.day + 1)

return sorted(flatten_list([
_search_oppositions(start_time, end_time),
_search_conjunction(start_time, end_time)
_search_conjunction(start_time, end_time),
_search_maximal_elongations(start_time, end_time)
]), key=lambda event: event.start_time)
Loading

0 comments on commit a382669

Please sign in to comment.