Browse Source

Compute sunrise, sunset and phase moon

master
Jérôme Deuchnord 4 years ago
parent
commit
47b9297a78
No known key found for this signature in database GPG Key ID: BC6F3C345B7D33B0
5 changed files with 197 additions and 18 deletions
  1. +1
    -0
      .gitignore
  2. +1
    -1
      Pipfile
  3. +44
    -12
      Pipfile.lock
  4. +100
    -0
      ephemeris.py
  5. +51
    -5
      main.py

+ 1
- 0
.gitignore View File

@@ -0,0 +1 @@
/cache

+ 1
- 1
Pipfile View File

@@ -6,7 +6,7 @@ verify_ssl = true
[dev-packages]

[packages]
ephem = "*"
skyfield = "*"

[requires]
python_version = "3.7"

+ 44
- 12
Pipfile.lock View File

@@ -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": {}


+ 100
- 0
ephemeris.py View File

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

+ 51
- 5
main.py View File

@@ -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()