From ef7c36af371a83743072320d10a1e630d3fec180 Mon Sep 17 00:00:00 2001 From: Adriano Caloiaro Date: Sat, 11 Apr 2026 12:28:30 -0600 Subject: [PATCH] feat(weather): add dawn and dusk conditions based on solar sensor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Detect twilight by checking when the solar radiation sensor reads low but positive values (< 30 W/m²) while the sun is geometrically near the horizon (sinusoidal fraction < 0.2 or outside model daylight hours). Morning/afternoon local time distinguishes dawn from dusk without hard-coding sunrise/sunset times. --- .ignore | 1 + weather/condition.go | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 .ignore diff --git a/.ignore b/.ignore new file mode 100644 index 0000000..48b8bf9 --- /dev/null +++ b/.ignore @@ -0,0 +1 @@ +vendor/ diff --git a/weather/condition.go b/weather/condition.go index f943997..66d56ed 100644 --- a/weather/condition.go +++ b/weather/condition.go @@ -11,6 +11,10 @@ type Condition struct { // peakSolarRadiation is the approximate clear-sky maximum at sea level (W/m²). const peakSolarRadiation = 950.0 +// twilightSolar is the solar radiation threshold (W/m²) below which the sun is considered +// low in the sky, producing dawn or dusk conditions. +const twilightSolar = 30.0 + // sunFraction returns the fraction of peak solar radiation expected at the given // local time of day, using a simple sinusoidal model (sunrise ~6, sunset ~18). // Returns 0 outside of daylight hours. @@ -52,8 +56,20 @@ func Infer(d Data, lat, lon float64) *Condition { // Estimate local time from longitude (rough UTC offset). utcMinutes := d.LastUpdated.Hour()*60 + d.LastUpdated.Minute() localMinutes := utcMinutes + int(math.Round(lon/15))*60 + localMinutes = ((localMinutes % 1440) + 1440) % 1440 fraction := sunFraction(localMinutes) isDaytime := fraction > 0 + isMorning := localMinutes < 720 + + // Dawn/dusk: sensor detects low but positive solar radiation while the sun is + // geometrically near the horizon (fraction < 0.2 or before/after model daylight). + // isMorning distinguishes the two without hard-coding sunrise/sunset hours. + if solar > 0 && solar < twilightSolar && (!isDaytime || fraction < 0.2) { + if isMorning { + return &Condition{"Dawn", "🌅"} + } + return &Condition{"Dusk", "🌇"} + } if !isDaytime || solar < 10 { if !isDaytime { @@ -81,3 +97,4 @@ func Infer(d Data, lat, lon float64) *Condition { } return &Condition{"Overcast", "☁️"} } +