Backfill missing QSO distances during weather enqueue scan
Adds haversine_km/4 to Radio context and backfill_distances/1 which calculates distance_km from pos1/pos2 for QSOs missing it. Called by QsoWeatherEnqueueWorker before enqueueing weather fetches.
This commit is contained in:
parent
c38596cc36
commit
d50c78102d
4 changed files with 91 additions and 0 deletions
|
|
@ -57,6 +57,42 @@ defmodule Microwaveprop.Radio do
|
||||||
|> Repo.update_all(set: [weather_queued: true])
|
|> Repo.update_all(set: [weather_queued: true])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@earth_radius_km 6371.0
|
||||||
|
|
||||||
|
def haversine_km(lat1, lon1, lat2, lon2) do
|
||||||
|
dlat = deg_to_rad(lat2 - lat1)
|
||||||
|
dlon = deg_to_rad(lon2 - lon1)
|
||||||
|
rlat1 = deg_to_rad(lat1)
|
||||||
|
rlat2 = deg_to_rad(lat2)
|
||||||
|
|
||||||
|
a =
|
||||||
|
:math.sin(dlat / 2) ** 2 +
|
||||||
|
:math.cos(rlat1) * :math.cos(rlat2) * :math.sin(dlon / 2) ** 2
|
||||||
|
|
||||||
|
2 * @earth_radius_km * :math.asin(:math.sqrt(a))
|
||||||
|
end
|
||||||
|
|
||||||
|
def backfill_distances(qsos) do
|
||||||
|
Enum.each(qsos, fn qso ->
|
||||||
|
if is_nil(qso.distance_km) && qso.pos1 && qso.pos2 do
|
||||||
|
lat1 = qso.pos1["lat"]
|
||||||
|
lon1 = qso.pos1["lon"] || qso.pos1["lng"]
|
||||||
|
lat2 = qso.pos2["lat"]
|
||||||
|
lon2 = qso.pos2["lon"] || qso.pos2["lng"]
|
||||||
|
|
||||||
|
if lat1 && lon1 && lat2 && lon2 do
|
||||||
|
dist = lat1 |> haversine_km(lon1, lat2, lon2) |> round() |> Decimal.new()
|
||||||
|
|
||||||
|
Qso
|
||||||
|
|> where([q], q.id == ^qso.id)
|
||||||
|
|> Repo.update_all(set: [distance_km: dist])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp deg_to_rad(deg), do: deg * :math.pi() / 180
|
||||||
|
|
||||||
def get_qso!(id) do
|
def get_qso!(id) do
|
||||||
Repo.get!(Qso, id)
|
Repo.get!(Qso, id)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ defmodule Microwaveprop.Workers.QsoWeatherEnqueueWorker do
|
||||||
qsos = Radio.unprocessed_qsos()
|
qsos = Radio.unprocessed_qsos()
|
||||||
|
|
||||||
if qsos != [] do
|
if qsos != [] do
|
||||||
|
Radio.backfill_distances(qsos)
|
||||||
|
|
||||||
jobs = build_weather_jobs(qsos)
|
jobs = build_weather_jobs(qsos)
|
||||||
|
|
||||||
if jobs != [] do
|
if jobs != [] do
|
||||||
|
|
|
||||||
|
|
@ -162,6 +162,48 @@ defmodule Microwaveprop.RadioTest do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "haversine_km/4" do
|
||||||
|
test "calculates distance between two known points" do
|
||||||
|
# Dallas to Austin ~roughly 295 km
|
||||||
|
dist = Radio.haversine_km(32.9, -97.0, 30.3, -97.7)
|
||||||
|
assert_in_delta dist, 295, 5
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns 0 for same point" do
|
||||||
|
assert Radio.haversine_km(32.9, -97.0, 32.9, -97.0) == 0.0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "backfill_distances/1" do
|
||||||
|
test "calculates and stores distance for QSOs missing distance_km" do
|
||||||
|
qso = create_qso(%{distance_km: nil})
|
||||||
|
assert is_nil(qso.distance_km)
|
||||||
|
|
||||||
|
Radio.backfill_distances([qso])
|
||||||
|
|
||||||
|
updated = Repo.get!(Qso, qso.id)
|
||||||
|
assert_in_delta Decimal.to_float(updated.distance_km), 295, 5
|
||||||
|
end
|
||||||
|
|
||||||
|
test "skips QSOs that already have a distance" do
|
||||||
|
qso = create_qso(%{distance_km: Decimal.new("100")})
|
||||||
|
|
||||||
|
Radio.backfill_distances([qso])
|
||||||
|
|
||||||
|
updated = Repo.get!(Qso, qso.id)
|
||||||
|
assert updated.distance_km == Decimal.new("100")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "skips QSOs missing pos2" do
|
||||||
|
qso = create_qso(%{distance_km: nil, pos2: nil})
|
||||||
|
|
||||||
|
Radio.backfill_distances([qso])
|
||||||
|
|
||||||
|
updated = Repo.get!(Qso, qso.id)
|
||||||
|
assert is_nil(updated.distance_km)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "get_qso!/1" do
|
describe "get_qso!/1" do
|
||||||
test "returns a QSO by ID" do
|
test "returns a QSO by ID" do
|
||||||
qso = create_qso()
|
qso = create_qso()
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,17 @@ defmodule Microwaveprop.Workers.QsoWeatherEnqueueWorkerTest do
|
||||||
assert :ok = QsoWeatherEnqueueWorker.perform(%Oban.Job{args: %{}})
|
assert :ok = QsoWeatherEnqueueWorker.perform(%Oban.Job{args: %{}})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "backfills distance_km for QSOs missing it" do
|
||||||
|
_station = create_asos_station("KDFW", 32.90, -97.04)
|
||||||
|
qso = create_qso(%{distance_km: nil})
|
||||||
|
|
||||||
|
assert :ok = QsoWeatherEnqueueWorker.perform(%Oban.Job{args: %{}})
|
||||||
|
|
||||||
|
updated = Repo.get!(Qso, qso.id)
|
||||||
|
assert updated.distance_km
|
||||||
|
assert_in_delta Decimal.to_float(updated.distance_km), 295, 5
|
||||||
|
end
|
||||||
|
|
||||||
test "skips QSOs without positions" do
|
test "skips QSOs without positions" do
|
||||||
_station = create_asos_station("KDFW", 32.90, -97.04)
|
_station = create_asos_station("KDFW", 32.90, -97.04)
|
||||||
_qso = create_qso(%{pos1: nil})
|
_qso = create_qso(%{pos1: nil})
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue