-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathsunrise_sunset.py
More file actions
executable file
·151 lines (124 loc) · 5.73 KB
/
sunrise_sunset.py
File metadata and controls
executable file
·151 lines (124 loc) · 5.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# -*- coding: utf-8 -*-
"""
This module is a wrapper to compute sunset and sunrise for a given day,
location, and zenith value. Sunset and sunrise are returned for the local time
zone.
"""
from __future__ import print_function
from builtins import object
import math
import datetime
CIVIL_ZENITH = 90.83333 # civil
class SunriseSunset(object):
"""
This class wraps the computation for sunset and sunrise. It relies on the
datetime class as input and output.
"""
def __init__(self, dt, latitude, longitude, localOffset=0, zenith=None):
self.dt = dt.replace(hour=0, minute=0, second=0, microsecond=0)
if latitude < -90 or latitude > 90:
raise ValueError('Invalid latitude value')
if longitude < -180 or longitude > 180:
raise ValueError('Invalid longitude value')
if localOffset < -12 or localOffset > 14:
raise ValueError('Invalid localOffset value')
self.latitude = latitude
self.longitude = longitude
self.localOffset = localOffset
self.zenith = zenith if zenith is not None else CIVIL_ZENITH
# ALGORITHM
def calculate(self, date=None):
"""Computes the sunset and sunrise for the current day, in local time"""
if date is None:
date = self.dt
# Calculate the day of the year
N = self.dt.timetuple().tm_yday
# Convert the longitude to hour value and calculate an approximate time
lngHour = self.longitude / 15
t_rise = N + ((6 - lngHour) / 24)
t_set = N + ((18 - lngHour) / 24)
# Calculate the Sun's mean anomaly
M_rise = (0.9856 * t_rise) - 3.289
M_set = (0.9856 * t_set) - 3.289
# Calculate the Sun's true longitude, and adjust angle to be between 0
# and 360
L_rise = (M_rise + (1.916 * math.sin(math.radians(M_rise))) + (0.020 * math.sin(math.radians(2 * M_rise))) + 282.634) % 360
L_set = (M_set + (1.916 * math.sin(math.radians(M_set))) + (0.020 * math.sin(math.radians(2 * M_set))) + 282.634) % 360
# Calculate the Sun's right ascension, and adjust angle to be between 0 and
# 360
RA_rise = (math.degrees(math.atan(0.91764 * math.tan(math.radians(L_rise))))) % 360
RA_set = (math.degrees(math.atan(0.91764 * math.tan(math.radians(L_set))))) % 360
# Right ascension value needs to be in the same quadrant as L
Lquadrant_rise = (math.floor(L_rise/90)) * 90
RAquadrant_rise = (math.floor(RA_rise/90)) * 90
RA_rise = RA_rise + (Lquadrant_rise - RAquadrant_rise)
Lquadrant_set = (math.floor(L_set/90)) * 90
RAquadrant_set = (math.floor(RA_set/90)) * 90
RA_set = RA_set + (Lquadrant_set - RAquadrant_set)
# Right ascension value needs to be converted into hours
RA_rise = RA_rise / 15
RA_set = RA_set / 15
# Calculate the Sun's declination
sinDec_rise = 0.39782 * math.sin(math.radians(L_rise))
cosDec_rise = math.cos(math.asin(sinDec_rise))
sinDec_set = 0.39782 * math.sin(math.radians(L_set))
cosDec_set = math.cos(math.asin(sinDec_set))
# Calculate the Sun's local hour angle
cos_zenith = math.cos(math.radians(self.zenith))
radian_lat = math.radians(self.latitude)
sin_latitude = math.sin(radian_lat)
cos_latitude = math.cos(radian_lat)
cosH_rise = (cos_zenith - (sinDec_rise * sin_latitude)) / (cosDec_rise * cos_latitude)
cosH_set = (cos_zenith - (sinDec_set * sin_latitude)) / (cosDec_set * cos_latitude)
#for extreme latitudes, this patch keeps the arccos domain within bounds.
#only acceptable because these light calculations don't seem to matter
#beyond site viz. Will fix one day regardless.
if cosH_rise < -1:
cosH_rise = -1
if cosH_rise > 1:
cosH_rise = 1
if cosH_set < -1:
cosH_set = -1
if cosH_set > 1:
cosH_set = 1
# Finish calculating H and convert into hours
H_rise = (360 - math.degrees(math.acos(cosH_rise))) / 15
H_set = math.degrees(math.acos(cosH_set)) / 15
# Calculate local mean time of rising/setting
T_rise = H_rise + RA_rise - (0.06571 * t_rise) - 6.622
T_set = H_set + RA_set - (0.06571 * t_set) - 6.622
# Adjust back to UTC, and keep the time between 0 and 24
UT_rise = (T_rise - lngHour) % 24
UT_set = (T_set - lngHour) % 24
# Convert UT value to local time zone of latitude/longitude
localT_rise = (UT_rise + self.localOffset) % 24
localT_set = (UT_set + self.localOffset) % 24
# Conversion
h_rise = int(localT_rise)
m_rise = int(localT_rise % 1 * 60)
h_set = int(localT_set)
m_set = int(localT_set % 1 * 60)
# Create datetime objects with same date, but with hour and minute
# specified
rise_dt = date.replace(hour=h_rise, minute=m_rise)
set_dt = date.replace(hour=h_set, minute=m_set)
return rise_dt, set_dt
if __name__ == "__main__":
# INPUTS
# Date -- uses current date
# Longitude and latitude
latitude = 46.805
longitude = -71.2316
# Sun's zenith for sunrise/sunset
zenith = CIVIL_ZENITH
# Offset from UTC (GMT)
localOffset = -5 # Eastern standard time
right_now = datetime.datetime.now()
# zenith is optional and defaults to the CIVIL_ZENITH value
rise_obj = SunriseSunset(dt=right_now, latitude=latitude,
longitude=longitude, localOffset=localOffset,
zenith=zenith)
rise_time, set_time = rise_obj.calculate()
print("Using information for Quebec City")
print("Sunrise", rise_time)
print("Sunset", set_time)