moonshit

d48398d · gmorell · 2026-04-09 03:08

10 files +96 -6

Files changed

modified apps/room_ephem/lib/room_ephem/ephem_worker.ex
+95 −6
@@ -16,6 +16,84 @@ defmodule RoomEphem.Worker do
16 16 end
17 17 end
18 18
19 + # ── Moon rise/set calculation ────────────────────────────────────────────────
20 + # Low-precision algorithm (~5–15 min accuracy) based on Jean Meeus,
21 + # "Astronomical Algorithms", low-precision moon position formulas.
22 +
23 + defp jd_from_date(%Date{year: y, month: m, day: d}) do
24 + a = div(14 - m, 12)
25 + yy = y + 4800 - a
26 + mm = m + 12 * a - 3
27 + (d + div(153 * mm + 2, 5) + 365 * yy + div(yy, 4) - div(yy, 100) + div(yy, 400) - 32045) - 0.5
28 + end
29 +
30 + defp sin_d(deg), do: :math.sin(deg * :math.pi() / 180.0)
31 + defp cos_d(deg), do: :math.cos(deg * :math.pi() / 180.0)
32 + defp tan_d(deg), do: :math.tan(deg * :math.pi() / 180.0)
33 + defp asin_d(x), do: :math.asin(x) * 180.0 / :math.pi()
34 + defp atan2_d(y, x), do: :math.atan2(y, x) * 180.0 / :math.pi()
35 +
36 + defp norm_deg(deg) do
37 + r = :math.fmod(deg, 360.0)
38 + if r < 0, do: r + 360.0, else: r
39 + end
40 +
41 + defp norm_hours(h) do
42 + r = :math.fmod(h, 24.0)
43 + if r < 0, do: r + 24.0, else: r
44 + end
45 +
46 + defp moon_ra_dec(jd) do
47 + d = jd - 2451545.0
48 + ll = norm_deg(218.316 + 13.176396 * d)
49 + mm = norm_deg(134.963 + 13.064993 * d)
50 + ff = norm_deg(93.272 + 13.229350 * d)
51 + lon = ll + 6.289 * sin_d(mm)
52 + lat = 5.128 * sin_d(ff)
53 + eps = 23.439 - 0.0000004 * d
54 + ra = atan2_d(sin_d(lon) * cos_d(eps) - tan_d(lat) * sin_d(eps), cos_d(lon))
55 + dec = asin_d(sin_d(lat) * cos_d(eps) + cos_d(lat) * sin_d(eps) * sin_d(lon))
56 + {norm_deg(ra), dec}
57 + end
58 +
59 + defp gmst_midnight(jd) do
60 + d = jd - 2451545.0
61 + t = d / 36525.0
62 + norm_deg(100.4606184 + 36000.77004 * t + 0.000387933 * t * t)
63 + end
64 +
65 + # Returns {:ok, rise_ut_hours, set_ut_hours} or {:error, reason}
66 + defp moon_rise_set_ut(date, lat, lon) do
67 + jd = jd_from_date(date)
68 + {ra, dec} = moon_ra_dec(jd + 0.5)
69 + gmst = gmst_midnight(jd)
70 + lst = norm_deg(gmst + lon)
71 + cos_ha = (sin_d(0.125) - sin_d(lat) * sin_d(dec)) / (cos_d(lat) * cos_d(dec))
72 +
73 + cond do
74 + cos_ha > 1.0 -> {:error, :circumpolar_below}
75 + cos_ha < -1.0 -> {:error, :circumpolar_above}
76 + true ->
77 + ha = :math.acos(cos_ha) * 180.0 / :math.pi()
78 + transit = norm_hours(norm_deg(ra - lst) / 15.0)
79 + rise = norm_hours(transit - ha / 15.0)
80 + set = norm_hours(transit + ha / 15.0)
81 + {:ok, rise, set}
82 + end
83 + end
84 +
85 + defp ut_to_local_time(date, ut_hours, tz) do
86 + clamped = max(0.0, min(ut_hours, 23.9999))
87 + h = trunc(clamped)
88 + frac_m = (clamped - h) * 60.0
89 + m = trunc(frac_m)
90 + s = trunc((frac_m - m) * 60.0)
91 + {:ok, ndt} = NaiveDateTime.new(date.year, date.month, date.day, h, m, s)
92 + ndt |> DateTime.from_naive!("Etc/UTC") |> Timex.Timezone.convert(tz) |> DateTime.to_time()
93 + end
94 +
95 + # ────────────────────────────────────────────────────────────────────────────
96 +
19 97 @decorate cacheable(cache: RoomZeus.Cache, opts: [ttl: @ttl])
20 98 def query_ephem(_name, query) do
21 99 IO.inspect("QEPH")
@@ -26,8 +104,19 @@ defmodule RoomEphem.Worker do
26 104
27 105 tz = WhereTZ.lookup(lat, lon)
28 106 {:ok, sunrise} = Solarex.Sun.rise(Date.utc_today(), lat, lon)
29 − {:ok, sunset} = Solarex.Sun.set(Date.utc_today(), lat, lon)
30 − phase = Solarex.Moon.phase(Date.utc_today())
107 + {:ok, sunset} = Solarex.Sun.set(Date.utc_today(), lat, lon)
108 + phase = Solarex.Moon.phase(Date.utc_today())
109 +
110 + moon_entries =
111 + case moon_rise_set_ut(Date.utc_today(), lat, lon) do
112 + {:ok, rise_h, set_h} ->
113 + [
114 + %{period: :moonrise, result: ut_to_local_time(Date.utc_today(), rise_h, tz)},
115 + %{period: :moonset, result: ut_to_local_time(Date.utc_today(), set_h, tz)}
116 + ]
117 + {:error, _} ->
118 + []
119 + end
31 120
32 121 [
33 122 %{
@@ -35,7 +124,7 @@ defmodule RoomEphem.Worker do
35 124 result:
36 125 sunrise
37 126 |> DateTime.from_naive!("Etc/UTC")
38 − |> DateTime.shift_zone!(tz)
127 + |> Timex.Timezone.convert(tz)
39 128 |> DateTime.to_time()
40 129 },
41 130 %{
@@ -43,11 +132,11 @@ defmodule RoomEphem.Worker do
43 132 result:
44 133 sunset
45 134 |> DateTime.from_naive!("Etc/UTC")
46 − |> DateTime.shift_zone!(tz)
135 + |> Timex.Timezone.convert(tz)
47 136 |> DateTime.to_time()
48 137 },
49 138 %{period: :phase, result: phase},
50 139 %{name: foci.name}
51 − ]
140 + ] ++ moon_entries
52 141 end
53 −end
142 +end
modified apps/room_sanctum/lib/room_sanctum/condensers/basic_mqtt.ex
+2 −1
@@ -200,7 +200,8 @@ defmodule RoomSanctum.Condenser.BasicMQTT do
200 200 def condense({id, :gtfs}, data, query) do
201 201 condensed = condense_data({id, :gtfs}, data)
202 202 route_ids = condensed |> Enum.map(& &1.route) |> Enum.uniq()
203 − alerts = RoomGtfs.Worker.query_alerts(id, query.stop, route_ids)
203 + stop = query.query.stop
204 + alerts = RoomGtfs.Worker.query_alerts(id, stop, route_ids)
204 205
205 206 condensed_with_alerts = condensed |> Enum.map(fn route ->
206 207 route_alerts = Enum.filter(alerts, fn a ->

Parents: 3ec6f92