Fetch elevation data along the path between two stations via the Open-Meteo Elevation API (with Open-Topo-Data fallback), compute Fresnel zone clearance, earth bulge, and knife-edge diffraction loss, and store the results per QSO. - terrain_profiles table with migration and TerrainProfile schema - ElevationClient with batched API calls and fallback - TerrainAnalysis with Fresnel/diffraction physics (ITU-R P.526-15) - TerrainProfileWorker on Oban :terrain queue - QsoWeatherEnqueueWorker enqueues terrain jobs automatically - QSO show page displays verdict badge and collapsible elevation table - Reorder show page: terrain, soundings, solar, HRRR, surface obs - Fix Dockerfile wgrib2 build (add cmake dependency)
57 lines
1.7 KiB
Elixir
57 lines
1.7 KiB
Elixir
defmodule Microwaveprop.Workers.TerrainProfileWorker do
|
|
@moduledoc false
|
|
use Oban.Worker, queue: :terrain, max_attempts: 3
|
|
|
|
alias Microwaveprop.Radio
|
|
alias Microwaveprop.Terrain
|
|
alias Microwaveprop.Terrain.ElevationClient
|
|
alias Microwaveprop.Terrain.TerrainAnalysis
|
|
|
|
@impl Oban.Worker
|
|
def perform(%Oban.Job{args: %{"qso_id" => qso_id}}) do
|
|
if Terrain.has_terrain_profile?(qso_id) do
|
|
:ok
|
|
else
|
|
qso = Radio.get_qso!(qso_id)
|
|
lat1 = qso.pos1["lat"]
|
|
lon1 = qso.pos1["lon"] || qso.pos1["lng"]
|
|
lat2 = qso.pos2["lat"]
|
|
lon2 = qso.pos2["lon"] || qso.pos2["lng"]
|
|
dist_km = Decimal.to_float(qso.distance_km)
|
|
freq_ghz = Decimal.to_float(qso.band) / 1000
|
|
|
|
case ElevationClient.fetch_elevation_profile(lat1, lon1, lat2, lon2) do
|
|
{:ok, profile} ->
|
|
analysis = TerrainAnalysis.analyse(profile, dist_km, freq_ghz)
|
|
|
|
path_points =
|
|
Enum.map(profile, fn p ->
|
|
%{
|
|
"lat" => p.lat,
|
|
"lon" => p.lon,
|
|
"d" => p.d,
|
|
"elev" => p.elev,
|
|
"dist_km" => p.dist_km
|
|
}
|
|
end)
|
|
|
|
Terrain.upsert_terrain_profile(%{
|
|
qso_id: qso_id,
|
|
sample_count: length(profile),
|
|
path_points: path_points,
|
|
max_elevation_m: analysis.max_elevation_m,
|
|
min_clearance_m: analysis.min_clearance_m,
|
|
diffraction_db: analysis.diffraction_db,
|
|
fresnel_hit_count: analysis.fresnel_hit_count,
|
|
obstructed_count: analysis.obstructed_count,
|
|
verdict: analysis.verdict
|
|
})
|
|
|
|
:ok
|
|
|
|
{:error, reason} ->
|
|
{:error, reason}
|
|
end
|
|
end
|
|
end
|
|
end
|