Browse Source

feat(event): add support for moon apogee and perigee

tags/v0.9.0
Jérôme Deuchnord 3 years ago
parent
commit
331ab99aa1
No known key found for this signature in database GPG Key ID: 72F9D1A7272D53DD
6 changed files with 103 additions and 21 deletions
  1. +1
    -0
      .github/workflows/e2e.yml
  2. +13
    -1
      kosmorrolib/data.py
  3. +43
    -2
      kosmorrolib/events.py
  4. +21
    -11
      kosmorrolib/locales/messages.pot
  5. +8
    -0
      manpage/kosmorro.7.md
  6. +17
    -7
      test/events.py

+ 1
- 0
.github/workflows/e2e.yml View File

@@ -16,6 +16,7 @@ jobs:


- name: Prepare tests - name: Prepare tests
run: | run: |
sudo apt update
sudo apt install ruby sudo apt install ruby
sudo gem install ronn sudo gem install ronn
pip install -U setuptools pip requests wheel Babel pip install -U setuptools pip requests wheel Babel


+ 13
- 1
kosmorrolib/data.py View File

@@ -44,7 +44,9 @@ EVENTS = {
'OPPOSITION': {'message': _('%s is in opposition')}, 'OPPOSITION': {'message': _('%s is in opposition')},
'CONJUNCTION': {'message': _('%s and %s are in conjunction')}, 'CONJUNCTION': {'message': _('%s and %s are in conjunction')},
'OCCULTATION': {'message': _('%s occults %s')}, 'OCCULTATION': {'message': _('%s occults %s')},
'MAXIMAL_ELONGATION': {'message': _("%s's largest elongation")}
'MAXIMAL_ELONGATION': {'message': _("%s's largest elongation")},
'MOON_PERIGEE': {'message': _("%s is at its perigee")},
'MOON_APOGEE': {'message': _("%s is at its apogee")},
} }




@@ -115,6 +117,9 @@ class Object(Serializable):
self.skyfield_name = skyfield_name self.skyfield_name = skyfield_name
self.radius = radius self.radius = radius


def __repr__(self):
return '<Object type=%s name=%s />' % (self.get_type(), self.name)

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


@@ -176,6 +181,13 @@ class Event(Serializable):
self.end_time = end_time self.end_time = end_time
self.details = details self.details = details


def __repr__(self):
return '<Event type=%s objects=[%s] start=%s end=%s details=%s>' % (self.event_type,
self.objects,
self.start_time,
self.end_time,
self.details)

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


+ 43
- 2
kosmorrolib/events.py View File

@@ -20,7 +20,7 @@ from datetime import date as date_type


from skyfield.errors import EphemerisRangeError from skyfield.errors import EphemerisRangeError
from skyfield.timelib import Time from skyfield.timelib import Time
from skyfield.searchlib import find_discrete, find_maxima
from skyfield.searchlib import find_discrete, find_maxima, find_minima
from numpy import pi from numpy import pi


from .data import Event, Star, Planet, ASTERS from .data import Event, Star, Planet, ASTERS
@@ -135,6 +135,45 @@ def _search_maximal_elongations(start_time: Time, end_time: Time, timezone: int)
return events return events




def _get_moon_distance():
earth = get_skf_objects()['earth']
moon = get_skf_objects()['moon']

def get_distance(time: Time):
earth_pos = earth.at(time)
moon_pos = earth_pos.observe(moon).apparent()

return moon_pos.distance().au

get_distance.rough_period = 1.0

return get_distance


def _search_moon_apogee(start_time: Time, end_time: Time, timezone: int) -> [Event]:
moon = ASTERS[1]
events = []

times, _ = find_maxima(start_time, end_time, f=_get_moon_distance(), epsilon=1./24/60)

for time in times:
events.append(Event('MOON_APOGEE', [moon], translate_to_timezone(time.utc_datetime(), timezone)))

return events


def _search_moon_perigee(start_time: Time, end_time: Time, timezone: int) -> [Event]:
moon = ASTERS[1]
events = []

times, _ = find_minima(start_time, end_time, f=_get_moon_distance(), epsilon=1./24/60)

for time in times:
events.append(Event('MOON_PERIGEE', [moon], translate_to_timezone(time.utc_datetime(), timezone)))

return events


def search_events(date: date_type, timezone: int = 0) -> [Event]: def search_events(date: date_type, timezone: int = 0) -> [Event]:
start_time = get_timescale().utc(date.year, date.month, date.day, -timezone) start_time = get_timescale().utc(date.year, date.month, date.day, -timezone)
end_time = get_timescale().utc(date.year, date.month, date.day + 1, -timezone) end_time = get_timescale().utc(date.year, date.month, date.day + 1, -timezone)
@@ -143,7 +182,9 @@ def search_events(date: date_type, timezone: int = 0) -> [Event]:
return sorted(flatten_list([ return sorted(flatten_list([
_search_oppositions(start_time, end_time, timezone), _search_oppositions(start_time, end_time, timezone),
_search_conjunction(start_time, end_time, timezone), _search_conjunction(start_time, end_time, timezone),
_search_maximal_elongations(start_time, end_time, timezone)
_search_maximal_elongations(start_time, end_time, timezone),
_search_moon_apogee(start_time, end_time, timezone),
_search_moon_perigee(start_time, end_time, timezone),
]), key=lambda event: event.start_time) ]), key=lambda event: event.start_time)
except EphemerisRangeError as error: except EphemerisRangeError as error:
start_date = translate_to_timezone(error.start_time.utc_datetime(), timezone) start_date = translate_to_timezone(error.start_time.utc_datetime(), timezone)


+ 21
- 11
kosmorrolib/locales/messages.pot View File

@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: kosmorro 0.8.1\n" "Project-Id-Version: kosmorro 0.8.1\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2020-06-07 11:13+0200\n"
"POT-Creation-Date: 2020-12-01 12:01+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -83,43 +83,53 @@ msgstr ""
msgid "%s's largest elongation" msgid "%s's largest elongation"
msgstr "" msgstr ""


#: kosmorrolib/data.py:262
#: kosmorrolib/data.py:48
#, python-format
msgid "%s is at its perigee"
msgstr ""

#: kosmorrolib/data.py:49
#, python-format
msgid "%s is at its apogee"
msgstr ""

#: kosmorrolib/data.py:274
msgid "Sun" msgid "Sun"
msgstr "" msgstr ""


#: kosmorrolib/data.py:263
#: kosmorrolib/data.py:275
msgid "Moon" msgid "Moon"
msgstr "" msgstr ""


#: kosmorrolib/data.py:264
#: kosmorrolib/data.py:276
msgid "Mercury" msgid "Mercury"
msgstr "" msgstr ""


#: kosmorrolib/data.py:265
#: kosmorrolib/data.py:277
msgid "Venus" msgid "Venus"
msgstr "" msgstr ""


#: kosmorrolib/data.py:266
#: kosmorrolib/data.py:278
msgid "Mars" msgid "Mars"
msgstr "" msgstr ""


#: kosmorrolib/data.py:267
#: kosmorrolib/data.py:279
msgid "Jupiter" msgid "Jupiter"
msgstr "" msgstr ""


#: kosmorrolib/data.py:268
#: kosmorrolib/data.py:280
msgid "Saturn" msgid "Saturn"
msgstr "" msgstr ""


#: kosmorrolib/data.py:269
#: kosmorrolib/data.py:281
msgid "Uranus" msgid "Uranus"
msgstr "" msgstr ""


#: kosmorrolib/data.py:270
#: kosmorrolib/data.py:282
msgid "Neptune" msgid "Neptune"
msgstr "" msgstr ""


#: kosmorrolib/data.py:271
#: kosmorrolib/data.py:283
msgid "Pluto" msgid "Pluto"
msgstr "" msgstr ""




+ 8
- 0
manpage/kosmorro.7.md View File

@@ -7,6 +7,10 @@ The terms are given in an alphabetically order.


## TERMS ## TERMS


### Apogee

The Moon is told being at its apogee when it is at its furthest point from the Earth.

### Conjunction ### Conjunction


From the point of view of the Earth, two asters are said in conjunction when they are close together. From the point of view of the Earth, two asters are said in conjunction when they are close together.
@@ -27,6 +31,10 @@ An aster is said in opposition when it is positionned at the exact opposite of t
For all the superior planets, it is the best moment to observe them, because they will be at the smallest distance to the Earth. Plus, they will appear full. For all the superior planets, it is the best moment to observe them, because they will be at the smallest distance to the Earth. Plus, they will appear full.
For instance, Mars is in opposition when the angle Mars-Earth-Sun is equal to 180 degrees. For instance, Mars is in opposition when the angle Mars-Earth-Sun is equal to 180 degrees.


### Perigee

The Moon is told being at its apogee when it is at its nearest point from the Earth.

### Planet ### Planet


A planet is an aster that orbits around a star, is not a star itself, is massive enough to maintain it nearly round, and has cleaned its orbit from other massive objects. A planet is an aster that orbits around a star, is not a star itself, is massive enough to maintain it nearly round, and has cleaned its orbit from other massive objects.


+ 17
- 7
test/events.py View File

@@ -1,6 +1,7 @@
import unittest import unittest


from datetime import date, datetime from datetime import date, datetime
from json import dumps


from kosmorrolib import events from kosmorrolib import events
from kosmorrolib.data import Event, ASTERS from kosmorrolib.data import Event, ASTERS
@@ -27,29 +28,38 @@ class EventTestCase(unittest.TestCase):


(date(2025, 1, 16), [Event('OPPOSITION', [ASTERS[4]], datetime(2025, 1, 16, 2, 38))]), (date(2025, 1, 16), [Event('OPPOSITION', [ASTERS[4]], datetime(2025, 1, 16, 2, 38))]),


(date(2027, 2, 19), [Event('OPPOSITION', [ASTERS[4]], datetime(2027, 2, 19, 15, 50))]),
(date(2027, 2, 19), [Event('MOON_PERIGEE', [ASTERS[1]], datetime(2027, 2, 19, 7, 38)),
Event('OPPOSITION', [ASTERS[4]], datetime(2027, 2, 19, 15, 50))]),


(date(2020, 1, 2), [Event('CONJUNCTION', [ASTERS[2], ASTERS[5]], datetime(2020, 1, 2, 16, 41))]),
(date(2020, 1, 2), [Event('MOON_APOGEE', [ASTERS[1]], datetime(2020, 1, 2, 1, 32)),
Event('CONJUNCTION', [ASTERS[2], ASTERS[5]], datetime(2020, 1, 2, 16, 41))]),


(date(2020, 1, 12), [Event('CONJUNCTION', [ASTERS[2], ASTERS[6]], datetime(2020, 1, 12, 9, 51)), (date(2020, 1, 12), [Event('CONJUNCTION', [ASTERS[2], ASTERS[6]], datetime(2020, 1, 12, 9, 51)),
Event('CONJUNCTION', [ASTERS[2], ASTERS[9]], datetime(2020, 1, 12, 10, 13)), Event('CONJUNCTION', [ASTERS[2], ASTERS[9]], datetime(2020, 1, 12, 10, 13)),
Event('CONJUNCTION', [ASTERS[6], ASTERS[9]], datetime(2020, 1, 12, 16, 57))]), Event('CONJUNCTION', [ASTERS[6], ASTERS[9]], datetime(2020, 1, 12, 16, 57))]),


(date(2020, 2, 10), [Event('MAXIMAL_ELONGATION', [ASTERS[2]], datetime(2020, 2, 10, 13, 46), details='18.2°')]),
(date(2020, 2, 10), [Event('MAXIMAL_ELONGATION', [ASTERS[2]], datetime(2020, 2, 10, 13, 46), details='18.2°'),
Event('MOON_PERIGEE', [ASTERS[1]], datetime(2020, 2, 10, 20, 34))]),


(date(2020, 3, 24), [Event('MAXIMAL_ELONGATION', [ASTERS[2]], datetime(2020, 3, 24, 1, 56), details='27.8°'), (date(2020, 3, 24), [Event('MAXIMAL_ELONGATION', [ASTERS[2]], datetime(2020, 3, 24, 1, 56), details='27.8°'),
Event('MOON_APOGEE', [ASTERS[1]], datetime(2020, 3, 24, 15, 39)),
Event('MAXIMAL_ELONGATION', [ASTERS[3]], datetime(2020, 3, 24, 21, 58), details='46.1°')]), Event('MAXIMAL_ELONGATION', [ASTERS[3]], datetime(2020, 3, 24, 21, 58), details='46.1°')]),


(date(2005, 6, 16), [Event('OCCULTATION', [ASTERS[1], ASTERS[5]], datetime(2005, 6, 16, 6, 31))])
(date(2005, 6, 16), [Event('OCCULTATION', [ASTERS[1], ASTERS[5]], datetime(2005, 6, 16, 6, 31))]),

(date(2020, 4, 7), [Event('MOON_PERIGEE', [ASTERS[1]], datetime(2020, 4, 7, 18, 14))]),

(date(2020, 1, 29), [Event('MOON_APOGEE', [ASTERS[1]], datetime(2020, 1, 29, 21, 32))])
) )


@data_provider(expected_events_provider) @data_provider(expected_events_provider)
def test_search_events(self, d: date, expected_events: [Event]): def test_search_events(self, d: date, expected_events: [Event]):
actual_events = events.search_events(d) actual_events = events.search_events(d)
self.assertEqual(len(expected_events), len(actual_events), self.assertEqual(len(expected_events), len(actual_events),
'Expected %d elements, got %d for date %s.' % (len(expected_events),
len(actual_events),
d.isoformat()))
'Expected %d elements, got %d for date %s.\n%s' % (len(expected_events),
len(actual_events),
d.isoformat(),
actual_events))


for i, expected_event in enumerate(expected_events): for i, expected_event in enumerate(expected_events):
actual_event = actual_events[i] actual_event = actual_events[i]


Loading…
Cancel
Save