Merge pull request #73 from outis1one/claude/fix-sunrise-video-timing-hKgfU
sunrise.py: replace suntime with skyfield for accurate sunrise calcul…
This commit is contained in:
+1
-1
@@ -39,7 +39,7 @@ echo "Processing: $yesterday"
|
||||
|
||||
# ── Season / movement info from astronomical calculation ──────────────────────
|
||||
_season_info="$(python3 "$SCRIPT_DIR/season_info.py" "$yesterday")" || {
|
||||
echo "Error: season_info.py failed — check Python dependencies (suntime pytz)"
|
||||
echo "Error: season_info.py failed — check Python dependencies (pytz)"
|
||||
exit 1
|
||||
}
|
||||
eval "$_season_info"
|
||||
|
||||
+2
-2
@@ -22,8 +22,8 @@ echo ""
|
||||
echo "Done. Next steps:"
|
||||
echo " 1. Install system packages (if not already present):"
|
||||
echo " sudo apt install ffmpeg bc fonts-dejavu"
|
||||
echo " pip3 install suntime pytz requests"
|
||||
echo " pip3 install skyfield Pillow numpy scipy # moon jobs (moon-track, moon-phase-monthly)"
|
||||
echo " pip3 install pytz requests skyfield"
|
||||
echo " pip3 install Pillow numpy scipy # moon jobs (moon-track, moon-phase-monthly)"
|
||||
echo ""
|
||||
echo " 2. Edit $TARGET/sky-cam.conf"
|
||||
echo " — SCRIPT_DIR full path to the directory you'll run scripts from"
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ ATTR_FADE="$MONTAGE_ATTR_FADE"
|
||||
|
||||
# ── Season / movement info ────────────────────────────────────────────────────
|
||||
_season_info="$(python3 "$SCRIPT_DIR/season_info.py" ${DATE_ARG:+"$DATE_ARG"})" || {
|
||||
echo "Error: season_info.py failed — check Python dependencies (suntime pytz)"
|
||||
echo "Error: season_info.py failed — check Python dependencies (pytz)"
|
||||
exit 1
|
||||
}
|
||||
eval "$_season_info"
|
||||
|
||||
+51
-7
@@ -1,13 +1,24 @@
|
||||
#!/usr/bin/env python3
|
||||
from datetime import datetime
|
||||
"""sunrise.py — compute today's local sunrise time using skyfield.
|
||||
|
||||
Outputs: YYYY-MM-DD HH:MM:SS (local time, no timezone suffix)
|
||||
Exits non-zero with a message on stderr if the sun does not rise today
|
||||
(polar night or midnight sun).
|
||||
|
||||
Reads LATITUDE, LONGITUDE, TIMEZONE from sky-cam.conf / .env.
|
||||
Requires skyfield and the de421.bsp ephemeris alongside this script
|
||||
(skyfield downloads de421.bsp automatically on first use if absent).
|
||||
"""
|
||||
|
||||
from datetime import datetime, timezone, timedelta
|
||||
import pathlib
|
||||
import re
|
||||
|
||||
import pytz
|
||||
from suntime import Sun
|
||||
|
||||
_here = pathlib.Path(__file__).resolve().parent
|
||||
|
||||
|
||||
def _read_conf(path):
|
||||
conf = {}
|
||||
try:
|
||||
@@ -30,6 +41,7 @@ def _read_conf(path):
|
||||
pass
|
||||
return conf
|
||||
|
||||
|
||||
conf = _read_conf(_here / 'sky-cam.conf')
|
||||
# .env overrides sky-cam.conf — mirrors the sourcing order at the bottom of sky-cam.conf
|
||||
conf.update(_read_conf(_here / '.env'))
|
||||
@@ -42,9 +54,41 @@ latitude = float(conf['LATITUDE'])
|
||||
longitude = float(conf['LONGITUDE'])
|
||||
tz = pytz.timezone(conf['TIMEZONE'])
|
||||
|
||||
sun = Sun(latitude, longitude)
|
||||
now = datetime.now(tz)
|
||||
sunrise_utc = sun.get_sunrise_time(now)
|
||||
sunrise_time = sunrise_utc.astimezone(tz)
|
||||
# Search the full local calendar day (midnight→midnight) expressed in UTC.
|
||||
# This bracket is timezone-correct and handles any UTC offset, including large
|
||||
# positive offsets (UTC+12/+14) where local midnight falls on the previous UTC day.
|
||||
now_local = datetime.now(tz)
|
||||
midnight_local = now_local.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
t0_utc = midnight_local.astimezone(timezone.utc)
|
||||
t1_utc = t0_utc + timedelta(hours=25) # 25 h covers any timezone's full day
|
||||
|
||||
print(sunrise_time.strftime('%Y-%m-%d %H:%M:%S'))
|
||||
from skyfield.api import Loader, wgs84
|
||||
from skyfield.almanac import find_discrete, sunrise_sunset
|
||||
|
||||
loader = Loader(str(_here), verbose=False)
|
||||
ts = loader.timescale()
|
||||
eph = loader('de421.bsp') # downloaded automatically on first run
|
||||
topos = wgs84.latlon(latitude, longitude)
|
||||
|
||||
t0 = ts.from_datetime(t0_utc)
|
||||
t1 = ts.from_datetime(t1_utc)
|
||||
|
||||
times, events = find_discrete(t0, t1, sunrise_sunset(eph, topos))
|
||||
|
||||
# events: True = sunrise, False = sunset — pick the first sunrise on today's date
|
||||
sunrise_local = None
|
||||
for t, is_rise in zip(times, events):
|
||||
if is_rise:
|
||||
candidate = t.utc_datetime().astimezone(tz)
|
||||
if candidate.date() == now_local.date():
|
||||
sunrise_local = candidate
|
||||
break
|
||||
|
||||
if sunrise_local is None:
|
||||
raise SystemExit(
|
||||
"sunrise.py: no sunrise found for today "
|
||||
f"({now_local.date()} at {latitude:.4f}°, {longitude:.4f}°) — "
|
||||
"polar night or midnight sun?"
|
||||
)
|
||||
|
||||
print(sunrise_local.strftime('%Y-%m-%d %H:%M:%S'))
|
||||
|
||||
Reference in New Issue
Block a user