Files
weather-base-station/environment/pysky.py
T

153 lines
4.6 KiB
Python

# environment/pysky.py
import logging
from datetime import datetime, timedelta
from math import acos, asin, ceil, cos, degrees, fmod
from math import pi as PI
from math import radians, sin, sqrt
from constants import Coordinate
from tools.julian import from_julian, julian_date
logger = logging.getLogger(__name__)
class Sun:
@staticmethod
def _calc(location: Coordinate, date: datetime, elevation: float = 0.0) -> dict:
J_date = julian_date(date)
n = ceil(J_date - (2451545.0 + 0.0009) + 69.184 / 86400.0) - 1
J_ = n + 0.0009 - location.long / 360.0
M_degrees = fmod(357.5291 + 0.98560028 * J_, 360)
M_radians = radians(M_degrees)
C_degrees = (
1.9148 * sin(M_radians)
+ 0.02 * sin(2 * M_radians)
+ 0.0003 * sin(3 * M_radians)
)
L_degrees = fmod(M_degrees + C_degrees + 180.0 + 102.9372, 360)
Lambda_radians = radians(L_degrees)
J_transit = (
2451545.0 + J_ + 0.0053 * sin(M_radians) - 0.0069 * sin(2 * Lambda_radians)
)
sin_d = sin(Lambda_radians) * sin(radians(23.4397))
cos_d = cos(asin(sin_d))
some_cos = (
sin(radians(-0.833 - 2.076 * sqrt(elevation) / 60.0))
- sin(radians(location.lat)) * sin_d
) / (cos(radians(location.lat)) * cos_d)
try:
w0_radians = acos(some_cos)
except ValueError:
return {"sunrise": "--:--", "sunset": "--:--"}
w0_degrees = degrees(w0_radians)
j_rise = J_transit - w0_degrees / 360
j_set = J_transit + w0_degrees / 360
sunset_time = from_julian(j_set)
sunrise_time = from_julian(j_rise)
return {
"sunrise": f"{sunrise_time.hour:02d}:{sunrise_time.minute:02d}",
"sunset": f"{sunset_time.hour:02d}:{sunset_time.minute:02d}",
}
@staticmethod
def get_sunset_sunrise(location: Coordinate, date: datetime | None = None) -> dict:
if date is None:
date = datetime.now()
logger.debug("Calculating Sun trajectory...")
return Sun._calc(location, date)
class Moon:
@staticmethod
def _illumination_name(illumination: float, waxing: bool = True) -> str:
if illumination > 0.996:
return "Fullmåne"
elif illumination > 0.57:
return "Tilltagande halvmåne" if waxing else "Avtagande halvmåne"
elif illumination > 0.43:
return "Halvmåne (tilltagande)" if waxing else "Halvmåne (avtagande)"
elif illumination > 0.02:
return "Tilltagande skära" if waxing else "Avtagande skära"
else:
return "Nymåne"
@staticmethod
def _constrain(d: float) -> float:
t = d % 360
if t < 0:
t += 360
return t
@staticmethod
def _get_illuminated_fraction(jd: float) -> float:
toRad = PI / 180.0
T = (jd - 2451545) / 36525.0
D = (
Moon._constrain(
297.8501921
+ 445267.1114034 * T
- 0.0018819 * T * T
+ 1.0 / 545868.0 * T * T * T
- 1.0 / 113065000.0 * T * T * T * T
)
* toRad
)
M = (
Moon._constrain(
357.5291092
+ 35999.0502909 * T
- 0.0001536 * T * T
+ 1.0 / 24490000.0 * T * T * T
)
* toRad
)
Mp = (
Moon._constrain(
134.9633964
+ 477198.8675055 * T
+ 0.0087414 * T * T
+ 1.0 / 69699.0 * T * T * T
- 1.0 / 14712000.0 * T * T * T * T
)
* toRad
)
i = (
Moon._constrain(
180
- D * 180 / PI
- 6.289 * sin(Mp)
+ 2.1 * sin(M)
- 1.274 * sin(2 * D - Mp)
- 0.658 * sin(2 * D)
- 0.214 * sin(2 * Mp)
- 0.11 * sin(D)
)
* toRad
)
return (1 + cos(i)) / 2
@staticmethod
def get_illumination(date: datetime | None = None) -> dict:
if date is None:
date = datetime.now()
logger.debug("Calculating Moon illumination...")
i = Moon._get_illuminated_fraction(julian_date(date))
i_future = Moon._get_illuminated_fraction(
julian_date(date + timedelta(seconds=1))
)
waxing = i_future > i
return {
"moon_illumination": round(i, 4),
"moon_phase": Moon._illumination_name(i, waxing),
"waxing": waxing,
}