@@ -0,0 +1 @@ | |||
/cache |
@@ -6,7 +6,7 @@ verify_ssl = true | |||
[dev-packages] | |||
[packages] | |||
ephem = "*" | |||
skyfield = "*" | |||
[requires] | |||
python_version = "3.7" |
@@ -1,7 +1,7 @@ | |||
{ | |||
"_meta": { | |||
"hash": { | |||
"sha256": "522d4a9be67c6920ba1cc2d46f4ac93658a70bf4f0c51611fe4425b6785f27c0" | |||
"sha256": "a4606ffb65d1c5bf46146e7c9cd8add1593934c7536c007bfd84a9f10427258e" | |||
}, | |||
"pipfile-spec": 6, | |||
"requires": { | |||
@@ -16,20 +16,52 @@ | |||
] | |||
}, | |||
"default": { | |||
"ephem": { | |||
"jplephem": { | |||
"hashes": [ | |||
"sha256:3884de133045d2f12784ef456c0f5557139a247b88d2c26097f7bd420803ed7f", | |||
"sha256:4bcd9899863ef04f4e75d894a6973dce4b4d16baeb8c2e96fb66bd3c677491a2", | |||
"sha256:6a2e445ba3a1e6bd9d6dedcafa4dda83957f4f9b0efac3d642974c55faebcfa4", | |||
"sha256:7a4c82b1def2893e02aec0394f108d24adb17bd7b0ca6f4bc78eb7120c0212ac", | |||
"sha256:7af6d726c3d903087c284e3dd72c5cda2b5438e84f2d564314469f0fb7494fab", | |||
"sha256:9ea5c8d9b407fe151cece238d13e3ca12114ac5c73269ef6541bf65b208048a3", | |||
"sha256:bb3e04e981352ab8c6049325533944b882d9be1bf13c19be5d85b918ba75723f", | |||
"sha256:f19a380f83f36e56e6e499bf673a43c42ed28c766c9cafb4326b5defcca0a116", | |||
"sha256:fd15421938cac27cd87c3b73c81e4695ab7a22cd37d43e05090140f5d48392d8" | |||
"sha256:9dffb9f3d3f6d996ade875102431fe385e8ea422da25c8ba17b0508d9ca1282b" | |||
], | |||
"version": "==2.9" | |||
}, | |||
"numpy": { | |||
"hashes": [ | |||
"sha256:0778076e764e146d3078b17c24c4d89e0ecd4ac5401beff8e1c87879043a0633", | |||
"sha256:141c7102f20abe6cf0d54c4ced8d565b86df4d3077ba2343b61a6db996cefec7", | |||
"sha256:14270a1ee8917d11e7753fb54fc7ffd1934f4d529235beec0b275e2ccf00333b", | |||
"sha256:27e11c7a8ec9d5838bc59f809bfa86efc8a4fd02e58960fa9c49d998e14332d5", | |||
"sha256:2a04dda79606f3d2f760384c38ccd3d5b9bb79d4c8126b67aff5eb09a253763e", | |||
"sha256:3c26010c1b51e1224a3ca6b8df807de6e95128b0908c7e34f190e7775455b0ca", | |||
"sha256:52c40f1a4262c896420c6ea1c6fda62cf67070e3947e3307f5562bd783a90336", | |||
"sha256:6e4f8d9e8aa79321657079b9ac03f3cf3fd067bf31c1cca4f56d49543f4356a5", | |||
"sha256:7242be12a58fec245ee9734e625964b97cf7e3f2f7d016603f9e56660ce479c7", | |||
"sha256:7dc253b542bfd4b4eb88d9dbae4ca079e7bf2e2afd819ee18891a43db66c60c7", | |||
"sha256:94f5bd885f67bbb25c82d80184abbf7ce4f6c3c3a41fbaa4182f034bba803e69", | |||
"sha256:a89e188daa119ffa0d03ce5123dee3f8ffd5115c896c2a9d4f0dbb3d8b95bfa3", | |||
"sha256:ad3399da9b0ca36e2f24de72f67ab2854a62e623274607e37e0ce5f5d5fa9166", | |||
"sha256:b0348be89275fd1d4c44ffa39530c41a21062f52299b1e3ee7d1c61f060044b8", | |||
"sha256:b5554368e4ede1856121b0dfa35ce71768102e4aa55e526cb8de7f374ff78722", | |||
"sha256:cbddc56b2502d3f87fda4f98d948eb5b11f36ff3902e17cb6cc44727f2200525", | |||
"sha256:d79f18f41751725c56eceab2a886f021d70fd70a6188fd386e29a045945ffc10", | |||
"sha256:dc2ca26a19ab32dc475dbad9dfe723d3a64c835f4c23f625c2b6566ca32b9f29", | |||
"sha256:dd9bcd4f294eb0633bb33d1a74febdd2b9018b8b8ed325f861fffcd2c7660bb8", | |||
"sha256:e8baab1bc7c9152715844f1faca6744f2416929de10d7639ed49555a85549f52", | |||
"sha256:ec31fe12668af687b99acf1567399632a7c47b0e17cfb9ae47c098644ef36797", | |||
"sha256:f12b4f7e2d8f9da3141564e6737d79016fe5336cc92de6814eba579744f65b0a", | |||
"sha256:f58ac38d5ca045a377b3b377c84df8175ab992c970a53332fa8ac2373df44ff7" | |||
], | |||
"version": "==1.16.4" | |||
}, | |||
"sgp4": { | |||
"hashes": [ | |||
"sha256:1fb3cdbc11981a9ff34a032169f83c1f4a2877d1b6c295aed044e1d890b73892" | |||
], | |||
"version": "==1.4" | |||
}, | |||
"skyfield": { | |||
"hashes": [ | |||
"sha256:7711838214a23ba09bec0bc0c8040ba18dab58f4d496f5be66cf00b56e63ec34" | |||
], | |||
"index": "pypi", | |||
"version": "==3.7.6.0" | |||
"version": "==1.10" | |||
} | |||
}, | |||
"develop": {} | |||
@@ -0,0 +1,100 @@ | |||
from skyfield.api import Loader, Topos | |||
from skyfield import almanac | |||
class Ephemeris: | |||
position = None | |||
timescale = None | |||
planets = None | |||
def __init__(self, position): | |||
self.MONTH = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'] | |||
self.PLANETS = ['mercury', 'venus', 'mars', 'jupiter', 'saturn', 'uranus', 'neptune', 'pluto'] | |||
load = Loader('./cache') | |||
self.timescale = load.timescale() | |||
self.planets = load('de421.bsp') | |||
self.position = Topos(latitude_degrees=position['lat'], longitude_degrees=position['lon']) | |||
def compute_ephemeris_for_day(self, year: int, month: int, day: int) -> dict: | |||
ephemeris = {} | |||
time1 = self.timescale.utc(year, month, day, 2) | |||
time2 = self.timescale.utc(year, month, day + 1, 2) | |||
# Compute sunrise and sunset | |||
ephemeris['sun'] = self.get_sun(time1, time2) | |||
ephemeris['moon'] = self.get_moon(year, month, day) | |||
return ephemeris | |||
def get_sun(self, time1, time2) -> dict: | |||
t, y = almanac.find_discrete(time1, time2, almanac.sunrise_sunset(self.planets, self.position)) | |||
sunrise = t[0] if y[0] else t[1] | |||
sunset = t[1] if not y[1] else t[0] | |||
return {'rise': sunrise.utc_iso(), 'set': sunset.utc_iso()} | |||
def get_moon(self, year, month, day) -> dict: | |||
time1 = self.timescale.utc(year, month, day - 10) | |||
time2 = self.timescale.utc(year, month, day) | |||
_, y = almanac.find_discrete(time1, time2, almanac.moon_phases(self.planets)) | |||
return {'phase': y[-1]} | |||
def compute_ephemeris_for_month(self, year: int, month: int) -> list: | |||
if month == 2: | |||
is_leap_year = (year % 4 == 0 and year % 100 > 0) or (year % 400 == 0) | |||
max_day = 29 if is_leap_year else 28 | |||
elif month < 8: | |||
max_day = 30 if month % 2 == 0 else 31 | |||
else: | |||
max_day = 31 if month % 2 == 0 else 30 | |||
e = [] | |||
for day in range(1, max_day + 1): | |||
e.append(self.compute_ephemeris_for_day(year, month, day)) | |||
return e | |||
def compute_ephemeris_for_year(self, year: int) -> dict: | |||
e = {} | |||
for month in range(0, 12): | |||
e[self.MONTH[month]] = self.compute_ephemeris_for_month(year, month + 1) | |||
e['seasons'] = self.get_seasons(year) | |||
return e | |||
def get_seasons(self, year: int) -> dict: | |||
t1 = self.timescale.utc(year, 1, 1) | |||
t2 = self.timescale.utc(year, 12, 31) | |||
t, y = almanac.find_discrete(t1, t2, almanac.seasons(self.planets)) | |||
seasons = {} | |||
for ti, yi in zip(t, y): | |||
if yi == 0: | |||
season = 'MARCH' | |||
elif yi == 1: | |||
season = 'JUNE' | |||
elif yi == 2: | |||
season = 'SEPTEMBER' | |||
elif yi == 3: | |||
season = 'DECEMBER' | |||
else: | |||
raise AssertionError | |||
seasons[season] = ti.utc_iso() | |||
return seasons | |||
def compute_ephemeris(self, year: int, month: int, day: int): | |||
if day is not None: | |||
return self.compute_ephemeris_for_day(year, month, day) | |||
elif month is not None: | |||
return self.compute_ephemeris_for_month(year, month) | |||
else: | |||
return self.compute_ephemeris_for_year(year) |
@@ -1,8 +1,54 @@ | |||
import sys | |||
import ephem | |||
import argparse | |||
import numpy | |||
from datetime import date | |||
from ephemeris import Ephemeris | |||
import json | |||
# Fixes the "TypeError: Object of type int64 is not JSON serializable" | |||
# See https://stackoverflow.com/a/50577730 | |||
def json_default(o): | |||
if isinstance(o, numpy.int64): | |||
return int(o) | |||
raise TypeError('Object of type ' + str(type(o)) + ' could not be integrated in the JSON') | |||
def main(): | |||
args = get_args() | |||
year = args.year | |||
month = args.month | |||
day = args.date | |||
position = {'lat': args.latitude, 'lon': args.longitude, 'altitude': args.altitude} | |||
if day is not None and month is None: | |||
month = date.today().month | |||
ephemeris = Ephemeris(position) | |||
e = ephemeris.compute_ephemeris(year, month, day) | |||
print(json.dumps(e, default=json_default, indent=4, separators=(',', ': '))) | |||
def get_args(): | |||
parser = argparse.ArgumentParser(description='Compute the ephemeris for a given day/month/year, by default for' | |||
' Paris, France.', epilog='By default, the observer will be set at' | |||
' position (0,0) with an altitude of 0.' | |||
' You will more likely want to change that.') | |||
parser.add_argument('--latitude', '-lat', type=float, default=0., help="The observer's position on Earth" | |||
" (latitude)") | |||
parser.add_argument('--longitude', '-lon', type=float, default=0., help="The observer's position on Earth" | |||
" (longitude)") | |||
parser.add_argument('--altitude', '-alt', type=float, default=0., help="The observer's position on Earth" | |||
" (altitude)") | |||
parser.add_argument('--date', '-d', type=int, help='A number between 1 and 28, 29, 30 or 31 (depending on the' | |||
' month). The date you want to compute the ephemeris for') | |||
parser.add_argument('--month', '-m', type=int, help='A number between 1 and 12. The month you want to compute the' | |||
' ephemeris for (defaults to the current month if the day is' | |||
' defined)') | |||
parser.add_argument('year', type=int, help='The year you want to compute the ephemeris for') | |||
return parser.parse_args() | |||
def main(argv): | |||
print(argv) | |||
if '__main__' == __name__: | |||
main(sys.argv) | |||
main() |