| @@ -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] | |||