@@ -16,6 +16,7 @@ jobs: | |||
- name: Prepare tests | |||
run: | | |||
sudo apt update | |||
sudo apt install ruby | |||
sudo gem install ronn | |||
pip install -U setuptools pip requests wheel Babel | |||
@@ -44,7 +44,9 @@ EVENTS = { | |||
'OPPOSITION': {'message': _('%s is in opposition')}, | |||
'CONJUNCTION': {'message': _('%s and %s are in conjunction')}, | |||
'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.radius = radius | |||
def __repr__(self): | |||
return '<Object type=%s name=%s />' % (self.get_type(), self.name) | |||
def get_skyfield_object(self) -> SkfPlanet: | |||
return get_skf_objects()[self.skyfield_name] | |||
@@ -176,6 +181,13 @@ class Event(Serializable): | |||
self.end_time = end_time | |||
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: | |||
description = EVENTS[self.event_type]['message'] % self._get_objects_name() | |||
if show_details and self.details is not None: | |||
@@ -20,7 +20,7 @@ from datetime import date as date_type | |||
from skyfield.errors import EphemerisRangeError | |||
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 .data import Event, Star, Planet, ASTERS | |||
@@ -135,6 +135,45 @@ def _search_maximal_elongations(start_time: Time, end_time: Time, timezone: int) | |||
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]: | |||
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) | |||
@@ -143,7 +182,9 @@ def search_events(date: date_type, timezone: int = 0) -> [Event]: | |||
return sorted(flatten_list([ | |||
_search_oppositions(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) | |||
except EphemerisRangeError as error: | |||
start_date = translate_to_timezone(error.start_time.utc_datetime(), timezone) | |||
@@ -8,7 +8,7 @@ msgid "" | |||
msgstr "" | |||
"Project-Id-Version: kosmorro 0.8.1\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" | |||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | |||
"Language-Team: LANGUAGE <LL@li.org>\n" | |||
@@ -83,43 +83,53 @@ msgstr "" | |||
msgid "%s's largest elongation" | |||
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" | |||
msgstr "" | |||
#: kosmorrolib/data.py:263 | |||
#: kosmorrolib/data.py:275 | |||
msgid "Moon" | |||
msgstr "" | |||
#: kosmorrolib/data.py:264 | |||
#: kosmorrolib/data.py:276 | |||
msgid "Mercury" | |||
msgstr "" | |||
#: kosmorrolib/data.py:265 | |||
#: kosmorrolib/data.py:277 | |||
msgid "Venus" | |||
msgstr "" | |||
#: kosmorrolib/data.py:266 | |||
#: kosmorrolib/data.py:278 | |||
msgid "Mars" | |||
msgstr "" | |||
#: kosmorrolib/data.py:267 | |||
#: kosmorrolib/data.py:279 | |||
msgid "Jupiter" | |||
msgstr "" | |||
#: kosmorrolib/data.py:268 | |||
#: kosmorrolib/data.py:280 | |||
msgid "Saturn" | |||
msgstr "" | |||
#: kosmorrolib/data.py:269 | |||
#: kosmorrolib/data.py:281 | |||
msgid "Uranus" | |||
msgstr "" | |||
#: kosmorrolib/data.py:270 | |||
#: kosmorrolib/data.py:282 | |||
msgid "Neptune" | |||
msgstr "" | |||
#: kosmorrolib/data.py:271 | |||
#: kosmorrolib/data.py:283 | |||
msgid "Pluto" | |||
msgstr "" | |||
@@ -7,6 +7,10 @@ The terms are given in an alphabetically order. | |||
## TERMS | |||
### Apogee | |||
The Moon is told being at its apogee when it is at its furthest point from the Earth. | |||
### Conjunction | |||
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 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 | |||
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. | |||
@@ -1,6 +1,7 @@ | |||
import unittest | |||
from datetime import date, datetime | |||
from json import dumps | |||
from kosmorrolib import events | |||
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(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)), | |||
Event('CONJUNCTION', [ASTERS[2], ASTERS[9]], datetime(2020, 1, 12, 10, 13)), | |||
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°'), | |||
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°')]), | |||
(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) | |||
def test_search_events(self, d: date, expected_events: [Event]): | |||
actual_events = events.search_events(d) | |||
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): | |||
actual_event = actual_events[i] | |||