neiam
/apollos_crib
It's Apollo's Crib
public · Issues · Pulls 1 · Labels · Forks · Compare · Actions failure · Badges · Packages
⭐
Log in to mark this repository.
moonshit
d48398d · gmorell · 2026-04-09 03:08
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