Skip to content

Commit c47a665

Browse files
committed
python-ecosys/suntime: add new module
Signed-off-by: Lorenzo Cappelletti <lorenzo.cappelletti@gmail.com>
1 parent 3c383f6 commit c47a665

File tree

5 files changed

+215
-0
lines changed

5 files changed

+215
-0
lines changed

python-ecosys/suntime/example.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import suntime
2+
3+
Rome = suntime.Sun( 41.902782 , 12.496366 )
4+
Warsaw = suntime.Sun( 51.21 , 21.01 )
5+
CapeTown = suntime.Sun(-33.9252192, 18.4240762)
6+
7+
dt1 = (2000, 1, 1)
8+
sr1 = Rome.get_sunrise_time(*dt1) # (6, 38)
9+
ss1 = Rome.get_sunset_time (*dt1) # (15, 49)
10+
print('Rome:', sr1, ss1)
11+
12+
dt2 = (2014, 10, 3)
13+
sr2 = Warsaw.get_sunrise_time(*dt2) # (4, 39)
14+
ss2 = Warsaw.get_sunset_time (*dt2) # (16, 10)
15+
print('Warsaw:', sr2, ss2)
16+
17+
dt3 = (2016, 12, 21)
18+
sr3 = CapeTown.get_sunrise_time(*dt3) # (3, 32)
19+
ss3 = CapeTown.get_sunset_time (*dt3) # (17, 57)
20+
print('Cape Town:', sr3, ss3)
21+
22+
#######################################################################
23+
24+
# if `datetime` module is available
25+
import datetime as datetimelib
26+
27+
timedelta = datetimelib.timedelta
28+
timezone = datetimelib.timezone
29+
datetime = datetimelib.datetime
30+
31+
utc = timezone.utc
32+
tz1 = timezone(timedelta(hours=1))
33+
tz2 = timezone(timedelta(hours=3))
34+
tz3 = timezone(timedelta(hours=2))
35+
36+
# https://www.timeanddate.com/sun/italy/rome?month=1&year=2000
37+
print('Rome:')
38+
rt1 = datetime(*dt1, *sr1, tzinfo=utc).astimezone(tz1) # 2000-01-01 07:38:00+01:00
39+
st1 = datetime(*dt1, *ss1, tzinfo=utc).astimezone(tz1) # 2000-01-01 16:49:00+01:00
40+
print('>', rt1)
41+
print('>', st1)
42+
43+
# https://www.timeanddate.com/sun/poland/warsaw?month=10&year=2014
44+
print('Warsaw:')
45+
rt2 = datetime(*dt2, *sr2, tzinfo=utc).astimezone(tz2) # 2014-10-03 07:39:00+03:00
46+
st2 = datetime(*dt2, *ss2, tzinfo=utc).astimezone(tz2) # 2014-10-03 19:10:00+03:00
47+
print('>', rt2)
48+
print('>', st2)
49+
50+
# https://www.timeanddate.com/sun/south-africa/cape-town?month=12&year=2016
51+
print('Cape Town:')
52+
rt3 = datetime(*dt3, *sr3, tzinfo=utc).astimezone(tz3) # 2016-12-21 05:32:00+02:00
53+
st3 = datetime(*dt3, *ss3, tzinfo=utc).astimezone(tz3) # 2016-12-21 19:57:00+02:00
54+
print('>', rt3)
55+
print('>', st3)

python-ecosys/suntime/metadata.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
srctype = cpython
2+
type = module
3+
version = 1.0.0
4+
author = Lorenzo Cappelletti

python-ecosys/suntime/setup.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import sys
2+
3+
# Remove current dir from sys.path, otherwise setuptools will peek up our
4+
# module instead of system's.
5+
sys.path.pop(0)
6+
from setuptools import setup
7+
8+
sys.path.append("..")
9+
import sdist_upip
10+
11+
setup(
12+
name="micropython-suntime",
13+
version="1.0.0",
14+
description="suntime module for MicroPython",
15+
long_description="This is a module reimplemented specifically for MicroPython standard library,\nwith efficient and lean design in mind. Note that this module is likely work\nin progress and likely supports just a subset of CPython's corresponding\nmodule. Please help with the development if you are interested in this\nmodule.",
16+
url="https://github.com/micropython/micropython-lib",
17+
author="micropython-lib Developers",
18+
author_email="micro-python@googlegroups.com",
19+
maintainer="micropython-lib Developers",
20+
maintainer_email="micro-python@googlegroups.com",
21+
license="GPL",
22+
cmdclass={"sdist": sdist_upip.sdist},
23+
py_modules=["suntime"],
24+
)

python-ecosys/suntime/suntime.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# suntime.py
2+
3+
import math
4+
5+
class Sun:
6+
def __init__(self, lat, lon):
7+
self._lat = lat
8+
self._lon = lon
9+
10+
def get_sunrise_time(self, year, month, day):
11+
return self._calc_sun_time(year, month, day, True)
12+
13+
def get_sunset_time(self, year, month, day):
14+
return self._calc_sun_time(year, month, day, False)
15+
16+
def _calc_sun_time(self, year, month, day, isRiseTime, zenith=90.8):
17+
TO_RAD = math.pi/180.0
18+
19+
# 1. first calculate the day of the year
20+
N1 = math.floor(275 * month / 9)
21+
N2 = math.floor((month + 9) / 12)
22+
N3 = (1 + math.floor((year - 4 * math.floor(year / 4) + 2) / 3))
23+
N = N1 - (N2 * N3) + day - 30
24+
25+
# 2. convert the longitude to hour value and calculate an approximate time
26+
lngHour = self._lon / 15
27+
28+
if isRiseTime:
29+
t = N + ((6 - lngHour) / 24)
30+
else: #sunset
31+
t = N + ((18 - lngHour) / 24)
32+
33+
# 3. calculate the Sun's mean anomaly
34+
M = (0.9856 * t) - 3.289
35+
36+
# 4. calculate the Sun's true longitude
37+
L = M + (1.916 * math.sin(TO_RAD*M)) + (0.020 * math.sin(TO_RAD * 2 * M)) + 282.634
38+
L = self._force_range(L, 360 ) #NOTE: L adjusted into the range [0,360)
39+
40+
# 5a. calculate the Sun's right ascension
41+
42+
RA = (1/TO_RAD) * math.atan(0.91764 * math.tan(TO_RAD*L))
43+
RA = self._force_range(RA, 360 ) #NOTE: RA adjusted into the range [0,360)
44+
45+
# 5b. right ascension value needs to be in the same quadrant as L
46+
Lquadrant = (math.floor( L/90)) * 90
47+
RAquadrant = (math.floor(RA/90)) * 90
48+
RA = RA + (Lquadrant - RAquadrant)
49+
50+
# 5c. right ascension value needs to be converted into hours
51+
RA = RA / 15
52+
53+
# 6. calculate the Sun's declination
54+
sinDec = 0.39782 * math.sin(TO_RAD*L)
55+
cosDec = math.cos(math.asin(sinDec))
56+
57+
# 7a. calculate the Sun's local hour angle
58+
cosH = (math.cos(TO_RAD*zenith) - (sinDec * math.sin(TO_RAD*self._lat))) / (cosDec * math.cos(TO_RAD*self._lat))
59+
60+
if cosH > 1:
61+
return None # The sun never rises on this location (on the specified date)
62+
if cosH < -1:
63+
return None # The sun never sets on this location (on the specified date)
64+
65+
# 7b. finish calculating H and convert into hours
66+
67+
if isRiseTime:
68+
H = 360 - (1/TO_RAD) * math.acos(cosH)
69+
else: #setting
70+
H = (1/TO_RAD) * math.acos(cosH)
71+
72+
H = H / 15
73+
74+
#8. calculate local mean time of rising/setting
75+
T = H + RA - (0.06571 * t) - 6.622
76+
77+
#9. adjust back to UTC
78+
UT = T - lngHour
79+
UT = self._force_range(UT, 24) # UTC time in decimal format (e.g. 23.23)
80+
81+
#10. Return
82+
hr = self._force_range(int(UT), 24)
83+
min = round((UT - int(UT))*60)
84+
if min == 60:
85+
min = 0
86+
hr = self._force_range(hr + 1, 24)
87+
88+
return hr, min
89+
90+
def _force_range(self, v, max):
91+
# force v to be >= 0 and < max
92+
if v < 0:
93+
return v + max
94+
elif v >= max:
95+
return v - max
96+
97+
return v

python-ecosys/suntime/test_suntime.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# test_suntime.py
2+
3+
import unittest
4+
import suntime
5+
6+
Rome = suntime.Sun( 41.902782 , 12.496366 )
7+
Warsaw = suntime.Sun( 51.21 , 21.01 )
8+
CapeTown = suntime.Sun(-33.9252192, 18.4240762)
9+
10+
dt1 = (2000, 1, 1)
11+
dt2 = (2014, 10, 3)
12+
dt3 = (2016, 12, 21)
13+
14+
class TestSunTime(unittest.TestCase):
15+
16+
def test_sunrise1(self):
17+
self.assertEqual(Rome.get_sunrise_time(*dt1), (6, 38))
18+
19+
def test_sunset1(self):
20+
self.assertEqual(Rome.get_sunset_time(*dt1), (15, 49))
21+
22+
def test_sunrise2(self):
23+
self.assertEqual(Warsaw.get_sunrise_time(*dt2), (4, 39))
24+
25+
def test_sunset2(self):
26+
self.assertEqual(Warsaw.get_sunset_time (*dt2), (16, 10))
27+
28+
def test_sunrise3(self):
29+
self.assertEqual(CapeTown.get_sunrise_time(*dt3), (3, 32))
30+
31+
def test_sunset3(self):
32+
self.assertEqual(CapeTown.get_sunset_time (*dt3), (17, 57))
33+
34+
if __name__ == '__main__':
35+
unittest.main()

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy