diff --git a/lib/microwaveprop/propagation/grid.ex b/lib/microwaveprop/propagation/grid.ex index 41a51d03..4e7a0285 100644 --- a/lib/microwaveprop/propagation/grid.ex +++ b/lib/microwaveprop/propagation/grid.ex @@ -60,6 +60,10 @@ defmodule Microwaveprop.Propagation.Grid do lat >= @lat_min and lat <= @lat_max and lon >= @lon_min and lon <= @lon_max end + def contains?(%{lat: lat, lon: lon}) when is_number(lat) and is_number(lon) do + lat >= @lat_min and lat <= @lat_max and lon >= @lon_min and lon <= @lon_max + end + def contains?(_), do: false @doc "Returns the grid specification for wgrib2 -lola extraction." @@ -133,6 +137,9 @@ defmodule Microwaveprop.Propagation.Grid do defp float_range(start, stop, step) do count = round((stop - start) / step) + 1 - Enum.map(0..(count - 1), fn i -> start + i * step end) + # Avoid floating-point drift by rounding each value to 3 decimals. + # The accumulation of start + i * step drifts beyond 0.001° after ~200 + # iterations, which can cause map lookups on snapped coordinates to miss. + Enum.map(0..(count - 1), fn i -> Float.round(start + i * step, 3) end) end end diff --git a/lib/microwaveprop/propagation/path_compute.ex b/lib/microwaveprop/propagation/path_compute.ex index b62865f5..1950fa03 100644 --- a/lib/microwaveprop/propagation/path_compute.ex +++ b/lib/microwaveprop/propagation/path_compute.ex @@ -16,7 +16,6 @@ defmodule Microwaveprop.Propagation.PathCompute do import Microwaveprop.Geo, only: [haversine_km: 4, bearing_deg: 4] - alias Microwaveprop.Geo alias Microwaveprop.Ionosphere alias Microwaveprop.Propagation alias Microwaveprop.Propagation.BandConfig @@ -405,7 +404,7 @@ defmodule Microwaveprop.Propagation.PathCompute do utc_minute: now.minute, month: now.month, latitude: (src.lat + dst.lat) / 2, - longitude: src.lon, + longitude: (src.lon + dst.lon) / 2, pressure_mb: if(pressures != [], do: Enum.min(pressures)), prev_pressure_mb: nil, rain_rate_mmhr: 0.0, @@ -485,6 +484,5 @@ defmodule Microwaveprop.Propagation.PathCompute do } end - # Avoid unused-alias warnings for the import. - @compile {:no_warn_undefined, Geo} + end diff --git a/lib/microwaveprop/propagation/scorer.ex b/lib/microwaveprop/propagation/scorer.ex index 29f18e51..69f6ce33 100644 --- a/lib/microwaveprop/propagation/scorer.ex +++ b/lib/microwaveprop/propagation/scorer.ex @@ -553,17 +553,10 @@ defmodule Microwaveprop.Propagation.Scorer do # scorer for per-point reuse across bands) or all absent. We check # one key and derive the rest from the same branch so we never # silently mix cached and freshly-computed values. - %{ - tod_score: tod_score, - sky_score: sky_score, - wind_score: wind_score, - pressure_score: pressure_score - } = - if Map.has_key?(conditions, :tod_score) do - conditions - else - precompute_band_invariants(conditions) - end + tod_score = conditions[:tod_score] || elem(score_time_of_day(conditions.utc_hour, conditions.utc_minute, conditions.month, conditions.longitude), 0) + sky_score = conditions[:sky_score] || score_sky(conditions.sky_cover_pct) + wind_score = conditions[:wind_score] || score_wind(conditions.wind_speed_kts) + pressure_score = conditions[:pressure_score] || score_pressure(conditions.pressure_mb, conditions.prev_pressure_mb) factors = %{ humidity: score_humidity(conditions.abs_humidity, band_config), diff --git a/lib/microwaveprop/radio/contact.ex b/lib/microwaveprop/radio/contact.ex index db6d6bc9..223af538 100644 --- a/lib/microwaveprop/radio/contact.ex +++ b/lib/microwaveprop/radio/contact.ex @@ -116,7 +116,7 @@ defmodule Microwaveprop.Radio.Contact do |> validate_callsign(:station2) |> validate_grid_format(:grid1) |> validate_grid_format(:grid2) - |> validate_inclusion(:mode, @allowed_modes) + |> validate_mode_inclusion() |> validate_inclusion(:band, @allowed_bands) |> validate_email_format() |> validate_length(:submitter_email, max: 254) @@ -168,6 +168,16 @@ defmodule Microwaveprop.Radio.Contact do end end + # Only run validate_inclusion for mode when the field has a non-nil change. + # nil means "not provided" (mode is optional), and put_change to nil would + # otherwise cause validate_inclusion to reject nil as not in the allowed list. + defp validate_mode_inclusion(changeset) do + case get_change(changeset, :mode) do + nil -> changeset + mode -> validate_inclusion(changeset, :mode, @allowed_modes) + end + end + # Either a user_id (logged-in submitter) or a submitter_email (anonymous) # must be present so we know who submitted the contact. defp validate_user_or_email(changeset) do diff --git a/lib/microwaveprop_web/api/rate_limiter.ex b/lib/microwaveprop_web/api/rate_limiter.ex index b0e51f52..8594e638 100644 --- a/lib/microwaveprop_web/api/rate_limiter.ex +++ b/lib/microwaveprop_web/api/rate_limiter.ex @@ -101,7 +101,7 @@ defmodule MicrowavepropWeb.Api.RateLimiter do defp bucket_for(conn, opts) do case conn.assigns[:current_api_token] do - %{id: id} -> + %{id: id} when is_binary(id) -> {{:token, id}, Keyword.get(opts, :auth_limit, @default_auth_limit)} _ -> diff --git a/lib/microwaveprop_web/telemetry.ex b/lib/microwaveprop_web/telemetry.ex index 38493f05..4c9312eb 100644 --- a/lib/microwaveprop_web/telemetry.ex +++ b/lib/microwaveprop_web/telemetry.ex @@ -282,6 +282,9 @@ defmodule MicrowavepropWeb.Telemetry do :ok rescue # Don't crash the poller if the DB blips — telemetry is best-effort. - _ -> :ok + e -> + require Logger + Logger.warning("Oban queue depth poll failed: #{inspect(e)}") + :ok end end diff --git a/lib/microwaveprop_web/user_auth.ex b/lib/microwaveprop_web/user_auth.ex index 9d852921..9645cbf3 100644 --- a/lib/microwaveprop_web/user_auth.ex +++ b/lib/microwaveprop_web/user_auth.ex @@ -161,7 +161,7 @@ defmodule MicrowavepropWeb.UserAuth do # Do not renew session if the user is already logged in # to prevent CSRF errors or data being lost in tabs that are still open - defp renew_session(conn, user) when conn.assigns.current_scope.user.id == user.id do + defp renew_session(conn, %{id: user_id}) when conn.assigns.current_scope.user.id == user_id do conn end