45 lines
1.5 KiB
Elixir
45 lines
1.5 KiB
Elixir
defmodule Microwaveprop.Rover.LinkMargin do
|
||
@moduledoc """
|
||
Per-link SNR-margin math for the rover planner.
|
||
|
||
Maps a propagation grid score (0-100) into a predicted SNR using the
|
||
linear calibration `(score − 50) × 0.5`, then subtracts the mode's
|
||
required SNR threshold to yield "dB above decode".
|
||
"""
|
||
|
||
alias Microwaveprop.Propagation.ModeThresholds
|
||
alias Microwaveprop.Propagation.ScoresFile
|
||
|
||
@spec score_to_db(integer() | nil) :: float() | nil
|
||
def score_to_db(nil), do: nil
|
||
def score_to_db(score) when is_integer(score), do: (score - 50) * 0.5
|
||
|
||
@doc """
|
||
Pure helper: compute the link margin from an already-resolved
|
||
grid score and a mode atom. Used by tests and `Rover.Compute`,
|
||
which fetches the score itself.
|
||
"""
|
||
@spec link_margin_from_score(integer() | nil, ModeThresholds.mode()) :: float() | nil
|
||
def link_margin_from_score(nil, _mode), do: nil
|
||
|
||
def link_margin_from_score(score, mode) when is_integer(score) do
|
||
score_to_db(score) - ModeThresholds.required_snr_db(mode)
|
||
end
|
||
|
||
@doc """
|
||
Look up the score for the candidate cell and return the link margin.
|
||
"""
|
||
@spec link_margin_db(
|
||
non_neg_integer(),
|
||
DateTime.t(),
|
||
{float(), float()},
|
||
{float(), float()},
|
||
ModeThresholds.mode()
|
||
) :: float() | nil
|
||
def link_margin_db(band_mhz, %DateTime{} = valid_time, {c_lat, c_lon}, {_s_lat, _s_lon}, mode) do
|
||
case ScoresFile.read_point(band_mhz, valid_time, c_lat, c_lon) do
|
||
nil -> nil
|
||
score -> link_margin_from_score(score, mode)
|
||
end
|
||
end
|
||
end
|