@@ -20,6 +20,8 @@ from abc import ABC, abstractmethod | |||
from typing import Union | |||
from datetime import datetime | |||
from numpy import pi, arcsin | |||
from skyfield.api import Topos, Time | |||
from skyfield.vectorlib import VectorSum as SkfPlanet | |||
@@ -40,6 +42,7 @@ MOON_PHASES = { | |||
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")} | |||
} | |||
@@ -106,16 +109,19 @@ class Object(ABC): | |||
def __init__(self, | |||
name: str, | |||
skyfield_name: str, | |||
ephemerides: AsterEphemerides or None = None): | |||
ephemerides: AsterEphemerides or None = None, | |||
radius: float = None): | |||
""" | |||
Initialize an astronomical object | |||
:param str name: the official name of the object (may be internationalized) | |||
:param str skyfield_name: the internal name of the object in Skyfield library | |||
:param float radius: the radius (in km) of the object | |||
:param AsterEphemerides ephemerides: the ephemerides associated to the object | |||
""" | |||
self.name = name | |||
self.skyfield_name = skyfield_name | |||
self.radius = radius | |||
self.ephemerides = ephemerides | |||
def get_skyfield_object(self) -> SkfPlanet: | |||
@@ -125,6 +131,18 @@ class Object(ABC): | |||
def get_type(self) -> str: | |||
pass | |||
def get_apparent_radius(self, time: Time, from_place) -> float: | |||
""" | |||
Calculate the apparent radius, in degrees, of the object from the given place at a given time. | |||
:param time: | |||
:param from_place: | |||
:return: | |||
""" | |||
if self.radius is None: | |||
raise ValueError('Missing radius for %s object' % self.name) | |||
return 360 / pi * arcsin(self.radius / from_place.at(time).observe(self.get_skyfield_object()).distance().km) | |||
class Star(Object): | |||
def get_type(self) -> str: | |||
@@ -212,13 +230,13 @@ def skyfield_to_moon_phase(times: [Time], vals: [int], now: Time) -> Union[MoonP | |||
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')] | |||
ASTERS = [Star(_('Sun'), 'SUN', radius=696342), | |||
Satellite(_('Moon'), 'MOON', radius=1737.4), | |||
Planet(_('Mercury'), 'MERCURY', radius=2439.7), | |||
Planet(_('Venus'), 'VENUS', radius=6051.8), | |||
Planet(_('Mars'), 'MARS', radius=3396.2), | |||
Planet(_('Jupiter'), 'JUPITER BARYCENTER', radius=71492), | |||
Planet(_('Saturn'), 'SATURN BARYCENTER', radius=60268), | |||
Planet(_('Uranus'), 'URANUS BARYCENTER', radius=25559), | |||
Planet(_('Neptune'), 'NEPTUNE BARYCENTER', radius=24764), | |||
Planet(_('Pluto'), 'PLUTO BARYCENTER', radius=1185)] |
@@ -116,6 +116,7 @@ class JsonDumper(Dumper): | |||
if isinstance(obj, Object): | |||
obj = obj.__dict__ | |||
obj.pop('skyfield_name') | |||
obj.pop('radius') | |||
obj['object'] = obj.pop('name') | |||
obj['details'] = obj.pop('ephemerides') | |||
return obj | |||
@@ -56,7 +56,18 @@ def _search_conjunction(start_time: Time, end_time: Time) -> [Event]: | |||
for i, time in enumerate(times): | |||
if is_conjs[i]: | |||
conjunctions.append(Event('CONJUNCTION', [aster1, aster2], time.utc_datetime())) | |||
aster1_pos = (aster1.get_skyfield_object() - earth).at(time) | |||
aster2_pos = (aster2.get_skyfield_object() - earth).at(time) | |||
distance = aster1_pos.separation_from(aster2_pos).degrees | |||
if distance - aster2.get_apparent_radius(time, earth) < aster1.get_apparent_radius(time, earth): | |||
occulting_aster = [aster1, | |||
aster2] if aster1_pos.distance().km < aster2_pos.distance().km else [aster2, | |||
aster1] | |||
conjunctions.append(Event('OCCULTATION', occulting_aster, time.utc_datetime())) | |||
else: | |||
conjunctions.append(Event('CONJUNCTION', [aster1, aster2], time.utc_datetime())) | |||
computed.append(aster1) | |||
@@ -8,7 +8,7 @@ msgid "" | |||
msgstr "" | |||
"Project-Id-Version: kosmorro 0.6.2\n" | |||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" | |||
"POT-Creation-Date: 2020-03-24 13:44+0100\n" | |||
"POT-Creation-Date: 2020-03-29 14:06+0200\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" | |||
@@ -17,90 +17,95 @@ msgstr "" | |||
"Content-Transfer-Encoding: 8bit\n" | |||
"Generated-By: Babel 2.8.0\n" | |||
#: kosmorrolib/data.py:30 | |||
#: kosmorrolib/data.py:32 | |||
msgid "New Moon" | |||
msgstr "" | |||
#: kosmorrolib/data.py:31 | |||
#: kosmorrolib/data.py:33 | |||
msgid "Waxing crescent" | |||
msgstr "" | |||
#: kosmorrolib/data.py:32 | |||
#: kosmorrolib/data.py:34 | |||
msgid "First Quarter" | |||
msgstr "" | |||
#: kosmorrolib/data.py:33 | |||
#: kosmorrolib/data.py:35 | |||
msgid "Waxing gibbous" | |||
msgstr "" | |||
#: kosmorrolib/data.py:34 | |||
#: kosmorrolib/data.py:36 | |||
msgid "Full Moon" | |||
msgstr "" | |||
#: kosmorrolib/data.py:35 | |||
#: kosmorrolib/data.py:37 | |||
msgid "Waning gibbous" | |||
msgstr "" | |||
#: kosmorrolib/data.py:36 | |||
#: kosmorrolib/data.py:38 | |||
msgid "Last Quarter" | |||
msgstr "" | |||
#: kosmorrolib/data.py:37 | |||
#: kosmorrolib/data.py:39 | |||
msgid "Waning crescent" | |||
msgstr "" | |||
#: kosmorrolib/data.py:41 | |||
#: kosmorrolib/data.py:43 | |||
#, python-format | |||
msgid "%s is in opposition" | |||
msgstr "" | |||
#: kosmorrolib/data.py:42 | |||
#: kosmorrolib/data.py:44 | |||
#, python-format | |||
msgid "%s and %s are in conjunction" | |||
msgstr "" | |||
#: kosmorrolib/data.py:43 | |||
#: kosmorrolib/data.py:45 | |||
#, python-format | |||
msgid "%s occults %s" | |||
msgstr "" | |||
#: kosmorrolib/data.py:46 | |||
#, python-format | |||
msgid "%s's largest elongation" | |||
msgstr "" | |||
#: kosmorrolib/data.py:215 | |||
#: kosmorrolib/data.py:233 | |||
msgid "Sun" | |||
msgstr "" | |||
#: kosmorrolib/data.py:216 | |||
#: kosmorrolib/data.py:234 | |||
msgid "Moon" | |||
msgstr "" | |||
#: kosmorrolib/data.py:217 | |||
#: kosmorrolib/data.py:235 | |||
msgid "Mercury" | |||
msgstr "" | |||
#: kosmorrolib/data.py:218 | |||
#: kosmorrolib/data.py:236 | |||
msgid "Venus" | |||
msgstr "" | |||
#: kosmorrolib/data.py:219 | |||
#: kosmorrolib/data.py:237 | |||
msgid "Mars" | |||
msgstr "" | |||
#: kosmorrolib/data.py:220 | |||
#: kosmorrolib/data.py:238 | |||
msgid "Jupiter" | |||
msgstr "" | |||
#: kosmorrolib/data.py:221 | |||
#: kosmorrolib/data.py:239 | |||
msgid "Saturn" | |||
msgstr "" | |||
#: kosmorrolib/data.py:222 | |||
#: kosmorrolib/data.py:240 | |||
msgid "Uranus" | |||
msgstr "" | |||
#: kosmorrolib/data.py:223 | |||
#: kosmorrolib/data.py:241 | |||
msgid "Neptune" | |||
msgstr "" | |||
#: kosmorrolib/data.py:224 | |||
#: kosmorrolib/data.py:242 | |||
msgid "Pluto" | |||
msgstr "" | |||
@@ -116,68 +121,68 @@ msgstr "" | |||
msgid "{hours}:{minutes}" | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:147 | |||
#: kosmorrolib/dumper.py:148 | |||
msgid "Expected events:" | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:151 | |||
#: kosmorrolib/dumper.py:152 | |||
msgid "Note: All the hours are given in UTC." | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:156 | |||
#: kosmorrolib/dumper.py:157 | |||
msgid "Note: All the hours are given in the UTC{offset} timezone." | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:202 kosmorrolib/dumper.py:271 | |||
#: kosmorrolib/dumper.py:203 kosmorrolib/dumper.py:272 | |||
msgid "Object" | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:203 kosmorrolib/dumper.py:272 | |||
#: kosmorrolib/dumper.py:204 kosmorrolib/dumper.py:273 | |||
msgid "Rise time" | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:204 kosmorrolib/dumper.py:273 | |||
#: kosmorrolib/dumper.py:205 kosmorrolib/dumper.py:274 | |||
msgid "Culmination time" | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:205 kosmorrolib/dumper.py:274 | |||
#: kosmorrolib/dumper.py:206 kosmorrolib/dumper.py:275 | |||
msgid "Set time" | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:219 kosmorrolib/dumper.py:277 | |||
#: kosmorrolib/dumper.py:220 kosmorrolib/dumper.py:278 | |||
msgid "Moon phase:" | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:220 | |||
#: kosmorrolib/dumper.py:221 | |||
msgid "{next_moon_phase} on {next_moon_phase_date} at {next_moon_phase_time}" | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:258 | |||
#: kosmorrolib/dumper.py:259 | |||
msgid "A Summary of your Sky" | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:262 | |||
#: kosmorrolib/dumper.py:263 | |||
msgid "" | |||
"This document summarizes the ephemerides and the events of {date}. It " | |||
"aims to help you to prepare your observation session. All the hours are " | |||
"given in {timezone}." | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:268 | |||
#: kosmorrolib/dumper.py:269 | |||
msgid "" | |||
"Don't forget to check the weather forecast before you go out with your " | |||
"material." | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:270 | |||
#: kosmorrolib/dumper.py:271 | |||
msgid "Ephemerides of the day" | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:279 | |||
#: kosmorrolib/dumper.py:280 | |||
msgid "Expected events" | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:354 | |||
#: kosmorrolib/dumper.py:355 | |||
msgid "" | |||
"Building PDFs was not possible, because some dependencies are not " | |||
"installed.\n" | |||
@@ -1,5 +1,5 @@ | |||
from .core import * | |||
from .data import * | |||
from .dumper import * | |||
from .ephemerides import * | |||
from .events import * | |||
from .core import * | |||
@@ -0,0 +1,17 @@ | |||
import unittest | |||
from kosmorrolib import data, core | |||
class DataTestCase(unittest.TestCase): | |||
def test_object_radius_must_be_set_to_get_apparent_radius(self): | |||
o = data.Planet('Saturn', 'SATURN') | |||
with self.assertRaises(ValueError) as context: | |||
o.get_apparent_radius(core.get_timescale().now(), core.get_skf_objects()['earth']) | |||
self.assertEqual(('Missing radius for Saturn object',), context.exception.args) | |||
if __name__ == '__main__': | |||
unittest.main() |
@@ -38,6 +38,8 @@ class EventTestCase(unittest.TestCase): | |||
(date(2020, 3, 24), [Event('MAXIMAL_ELONGATION', [ASTERS[2]], datetime(2020, 3, 24, 1, 56), details='27.8°'), | |||
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))]) | |||
) | |||
@data_provider(expected_events_provider) | |||