# 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, }