55 lines
1.6 KiB
Elixir
55 lines
1.6 KiB
Elixir
defmodule Microwaveprop.Rover.Hilltop do
|
|
@moduledoc """
|
|
Snaps a propagation-grid cell center to the highest SRTM point inside
|
|
its footprint. The propagation grid is on a 0.125° lattice (~14 km),
|
|
but a rover wants to park on the actual hilltop inside that cell, not
|
|
at the nominal cell center. Sample SRTM at sub-cell resolution and
|
|
return the highest point.
|
|
"""
|
|
|
|
alias Microwaveprop.Rover.Elevation
|
|
|
|
@default_half_deg 0.0625
|
|
@default_step_deg 0.01
|
|
|
|
@type latlon :: {float(), float()}
|
|
|
|
@doc """
|
|
Returns `{best_lat, best_lon, best_elev}` for the highest SRTM point
|
|
inside `±half_deg` of `cell_center`, or `nil` if SRTM coverage is
|
|
missing across the whole footprint.
|
|
"""
|
|
@spec snap(latlon(), keyword()) :: {float(), float(), integer()} | nil
|
|
def snap({lat, lon}, opts \\ []) do
|
|
half = Keyword.get(opts, :half_deg, @default_half_deg)
|
|
step = Keyword.get(opts, :step_deg, @default_step_deg)
|
|
elev_lookup = Keyword.get(opts, :elev_lookup, &Elevation.lookup_many/1)
|
|
|
|
points = sample_grid(lat, lon, half, step)
|
|
elev_map = elev_lookup.(points)
|
|
|
|
points
|
|
|> Enum.flat_map(fn pt ->
|
|
case Map.get(elev_map, pt) do
|
|
nil -> []
|
|
elev -> [{pt, elev}]
|
|
end
|
|
end)
|
|
|> case do
|
|
[] ->
|
|
nil
|
|
|
|
list ->
|
|
{{best_lat, best_lon}, best_elev} = Enum.max_by(list, fn {_, e} -> e end)
|
|
{best_lat, best_lon, best_elev}
|
|
end
|
|
end
|
|
|
|
defp sample_grid(lat, lon, half, step) do
|
|
n = max(trunc(half / step), 1)
|
|
|
|
for i <- -n..n, j <- -n..n do
|
|
{Float.round(lat + i * step, 4), Float.round(lon + j * step, 4)}
|
|
end
|
|
end
|
|
end
|