Estimate sunup/sundown times for earlier wallpaper changes in winter, update for Qtile 0.34.x
This commit is contained in:
parent
27b41f975d
commit
fa5bbee56e
3 changed files with 161 additions and 9 deletions
141
kuro/utils/suntime.py
Normal file
141
kuro/utils/suntime.py
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
import math
|
||||
import warnings
|
||||
from datetime import datetime, timedelta, time, timezone
|
||||
|
||||
# Copied from: https://github.com/SatAgro/suntime/blob/master/suntime/suntime.py
|
||||
|
||||
# CONSTANT
|
||||
TO_RAD = math.pi/180.0
|
||||
|
||||
|
||||
class SunTimeException(Exception):
|
||||
|
||||
def __init__(self, message):
|
||||
super(SunTimeException, self).__init__(message)
|
||||
|
||||
|
||||
class Sun:
|
||||
"""
|
||||
Approximated calculation of sunrise and sunset datetimes. Adapted from:
|
||||
https://stackoverflow.com/questions/19615350/calculate-sunrise-and-sunset-times-for-a-given-gps-coordinate-within-postgresql
|
||||
"""
|
||||
def __init__(self, lat, lon):
|
||||
self._lat = lat
|
||||
self._lon = lon
|
||||
|
||||
self.lngHour = self._lon / 15
|
||||
|
||||
def get_sunrise_time(self, at_date=datetime.now(), time_zone=timezone.utc):
|
||||
"""
|
||||
:param at_date: Reference date. datetime.now() if not provided.
|
||||
:param time_zone: pytz object with .tzinfo() or None
|
||||
:return: sunrise datetime.
|
||||
:raises: SunTimeException when there is no sunrise and sunset on given location and date.
|
||||
"""
|
||||
time_delta = self.get_sun_timedelta(at_date, time_zone=time_zone, is_rise_time=True)
|
||||
if time_delta is None:
|
||||
raise SunTimeException('The sun never rises on this location (on the specified date)')
|
||||
else:
|
||||
return datetime.combine(at_date, time(tzinfo=time_zone)) + time_delta
|
||||
|
||||
def get_sunset_time(self, at_date=datetime.now(), time_zone=timezone.utc):
|
||||
"""
|
||||
Calculate the sunset time for given date.
|
||||
:param at_date: Reference date. datetime.now() if not provided.
|
||||
:param time_zone: pytz object with .tzinfo() or None
|
||||
:return: sunset datetime.
|
||||
:raises: SunTimeException when there is no sunrise and sunset on given location and date.
|
||||
"""
|
||||
time_delta = self.get_sun_timedelta(at_date, time_zone=time_zone, is_rise_time=False)
|
||||
if time_delta is None:
|
||||
raise SunTimeException('The sun never rises on this location (on the specified date)')
|
||||
else:
|
||||
return datetime.combine(at_date, time(tzinfo=time_zone)) + time_delta
|
||||
|
||||
def get_sun_timedelta(self, at_date, time_zone, is_rise_time=True, zenith=90.8):
|
||||
"""
|
||||
Calculate sunrise or sunset date.
|
||||
:param at_date: Reference date
|
||||
:param time_zone: pytz object with .tzinfo() or None
|
||||
:param is_rise_time: True if you want to calculate sunrise time.
|
||||
:param zenith: Sun reference zenith
|
||||
:return: timedelta showing hour, minute, and second of sunrise or sunset
|
||||
"""
|
||||
|
||||
# If not set get local timezone from datetime
|
||||
if time_zone is None:
|
||||
time_zone = datetime.now().tzinfo
|
||||
|
||||
# 1. first get the day of the year
|
||||
N = at_date.timetuple().tm_yday
|
||||
|
||||
# 2. convert the longitude to hour value and calculate an approximate time
|
||||
if is_rise_time:
|
||||
t = N + ((6 - self.lngHour) / 24)
|
||||
else: # sunset
|
||||
t = N + ((18 - self.lngHour) / 24)
|
||||
|
||||
# 3a. calculate the Sun's mean anomaly
|
||||
M = (0.9856 * t) - 3.289
|
||||
|
||||
# 3b. calculate the Sun's true longitude
|
||||
L = M + (1.916 * math.sin(TO_RAD*M)) + (0.020 * math.sin(TO_RAD * 2 * M)) + 282.634
|
||||
L = self._force_range(L, 360) # NOTE: L adjusted into the range [0,360)
|
||||
|
||||
# 4a. calculate the Sun's declination
|
||||
sinDec = 0.39782 * math.sin(TO_RAD*L)
|
||||
cosDec = math.cos(math.asin(sinDec))
|
||||
|
||||
# 4b. calculate the Sun's local hour angle
|
||||
cosH = (math.cos(TO_RAD*zenith) - (sinDec * math.sin(TO_RAD*self._lat))) / (cosDec * math.cos(TO_RAD*self._lat))
|
||||
|
||||
if cosH > 1:
|
||||
return None # The sun never rises on this location (on the specified date)
|
||||
if cosH < -1:
|
||||
return None # The sun never sets on this location (on the specified date)
|
||||
|
||||
# 4c. finish calculating H and convert into hours
|
||||
if is_rise_time:
|
||||
H = 360 - (1/TO_RAD) * math.acos(cosH)
|
||||
else: # setting
|
||||
H = (1/TO_RAD) * math.acos(cosH)
|
||||
H = H / 15
|
||||
|
||||
# 5a. calculate the Sun's right ascension
|
||||
RA = (1/TO_RAD) * math.atan(0.91764 * math.tan(TO_RAD*L))
|
||||
RA = self._force_range(RA, 360) # NOTE: RA adjusted into the range [0,360)
|
||||
|
||||
# 5b. right ascension value needs to be in the same quadrant as L
|
||||
Lquadrant = (math.floor(L/90)) * 90
|
||||
RAquadrant = (math.floor(RA/90)) * 90
|
||||
RA = RA + (Lquadrant - RAquadrant)
|
||||
|
||||
# 5c. right ascension value needs to be converted into hours
|
||||
RA = RA / 15
|
||||
|
||||
# 6. calculate local mean time of rising/setting
|
||||
T = H + RA - (0.06571 * t) - 6.622
|
||||
|
||||
# 7a. adjust back to UTC
|
||||
UT = T - self.lngHour
|
||||
|
||||
if time_zone:
|
||||
# 7b. adjust back to local time
|
||||
UT += time_zone.utcoffset(at_date).total_seconds() / 3600
|
||||
|
||||
# 7c. rounding and impose range bounds
|
||||
UT = round(UT, 2)
|
||||
# if is_rise_time:
|
||||
UT = self._force_range(UT, 24)
|
||||
|
||||
# 8. return timedelta
|
||||
return timedelta(hours=UT)
|
||||
|
||||
@staticmethod
|
||||
def _force_range(v, max):
|
||||
# force v to be >= 0 and < max
|
||||
if v < 0:
|
||||
return v + max
|
||||
elif v >= max:
|
||||
return v - max
|
||||
return v
|
||||
Loading…
Add table
Add a link
Reference in a new issue