polish(aprs): consolidate callsign regex, document raise, note negative-sample v1 limit
- Extract PathParser.valid_callsign?/1 and reuse from the calibration Mix task instead of duplicating the regex. - Document raise behavior on Aprs.recent_packets_with_paths/1 and Aprs.station_positions/1 (already noted in moduledoc; @doc reinforces for callers reading the function head). - Note in the BandConfig guard comment why it sits before the Oban pause (so a misconfig exits without leaving queues paused). - Reword the band-mismatched-negatives note as a v1 limitation (no TODO tag — codebase convention is zero TODOs).
This commit is contained in:
parent
81e3a54a97
commit
af08e72816
3 changed files with 23 additions and 2 deletions
|
|
@ -71,6 +71,7 @@ defmodule Microwaveprop.Aprs do
|
|||
Returns recent position-bearing packets with non-empty paths, oldest first.
|
||||
|
||||
Filters out item/object packets and rows missing position or path.
|
||||
Raises on connection or SQL failure — see moduledoc on async-context safety.
|
||||
|
||||
## Options
|
||||
|
||||
|
|
@ -96,6 +97,9 @@ defmodule Microwaveprop.Aprs do
|
|||
Returns the most recent known position for each callsign in `callsigns`.
|
||||
|
||||
Callsigns with no positioned packets simply don't appear in the result.
|
||||
Empty input list short-circuits to `%{}` without querying.
|
||||
|
||||
Raises on connection or SQL failure — see moduledoc on async-context safety.
|
||||
"""
|
||||
@spec station_positions([String.t()]) :: %{String.t() => position()}
|
||||
def station_positions([]), do: %{}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,15 @@ defmodule Microwaveprop.Aprs.PathParser do
|
|||
# rather than `qA[A-Za-z]` which would over-match `qAa`, `qAd`, …
|
||||
@q_constructs ~w(qAC qAX qAU qAo qAO qAS qAr qAR qAZ qAI)
|
||||
|
||||
@doc """
|
||||
Returns `true` if `token` matches the APRS callsign shape used by this
|
||||
parser (1-2 alpha/digit prefix, one digit, 1-3 alpha suffix, optional
|
||||
-SSID 0..99). Exposed so callers (e.g. the calibration Mix task) can
|
||||
pre-filter path tokens with the same rule the parser itself applies.
|
||||
"""
|
||||
@spec valid_callsign?(String.t()) :: boolean()
|
||||
def valid_callsign?(token) when is_binary(token), do: Regex.match?(@callsign_regex, token)
|
||||
|
||||
@spec parse_hops(
|
||||
sender_callsign :: String.t(),
|
||||
sender_pos :: position(),
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ defmodule Mix.Tasks.Calibrate.Aprs144 do
|
|||
alias Microwaveprop.Weather
|
||||
|
||||
@factor_keys ~w(humidity time_of_day td_depression refractivity sky season wind rain pwat pressure)a
|
||||
@callsign_regex ~r/^[A-Z0-9]{1,2}[0-9][A-Z]{1,3}(-[0-9]{1,2})?$/
|
||||
@min_samples 50
|
||||
@band_mhz 144
|
||||
|
||||
|
|
@ -43,6 +42,8 @@ defmodule Mix.Tasks.Calibrate.Aprs144 do
|
|||
def run(argv) do
|
||||
Mix.Task.run("app.start")
|
||||
|
||||
# Guard runs BEFORE the Oban pause so a misconfig exits without
|
||||
# leaving queues paused. Subsequent failures are inside try/after.
|
||||
if BandConfig.get(@band_mhz) == nil do
|
||||
Mix.raise("BandConfig has no #{@band_mhz} MHz entry; cannot calibrate.")
|
||||
end
|
||||
|
|
@ -132,7 +133,7 @@ defmodule Mix.Tasks.Calibrate.Aprs144 do
|
|||
|> Enum.map(&String.trim/1)
|
||||
|> Enum.map(&String.trim_trailing(&1, "*"))
|
||||
end)
|
||||
|> Enum.filter(&Regex.match?(@callsign_regex, &1))
|
||||
|> Enum.filter(&PathParser.valid_callsign?/1)
|
||||
|> Enum.uniq()
|
||||
end
|
||||
|
||||
|
|
@ -169,6 +170,13 @@ defmodule Mix.Tasks.Calibrate.Aprs144 do
|
|||
# Use a wide contact-pool draw (5_000 minimum) so the random baseline
|
||||
# samples don't cluster geographically when n is small. Backtest's
|
||||
# default :sample_size is 5_000.
|
||||
#
|
||||
# v1 limitation: negatives sample from the full contacts table (all
|
||||
# bands, dominantly 10 GHz tropo). For a 144 MHz fit this means the
|
||||
# trainer separates "verified VHF receive" from "anywhere a 10 GHz
|
||||
# contact happened with timestamp jitter". A future iteration should
|
||||
# draw negatives from APRS coverage where no `*` digi handled the
|
||||
# frame in the same window — true band-matched negatives.
|
||||
baselines = Backtest.random_baseline(n, sample_size: max(n, 5_000))
|
||||
|
||||
factors =
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue