prop/test/microwaveprop/workers/ionosphere_fetch_worker_test.exs
Graham McIntire 361b9dec5a
Ionosphere: GIRO ionosonde ingestion (foF2/foEs/hmF2/MUFD)
First step toward physics-based HF / sporadic-E scoring. Polls GIRO's
DIDBase tabular endpoint every 10 minutes for two US ionosondes
(Millstone Hill MHJ45, Alpena AL945), parses the plain-text response
into observation rows, and upserts into a new ionosonde_observations
table keyed on (station_code, valid_time).

This gets foEs (E-layer sporadic critical frequency) into the database
as a direct measurement — the key input for predicting 144 MHz Es
openings via ITU-R P.534-6. foF2 + MUFD are the F2-layer inputs for
HF MUF predictions.

Not yet wired into scoring. Boulder/Wallops/Austin/Idaho/Point Arguello
are in the GIRO catalog but were silent when probed — add them back
if/when they come online.

Next steps: SWPC JSON (Kp, F10.7, sunspot), GOES X-ray flux, D-RAP text,
and the P.534-6 Es scoring factor that uses foEs at midpoint for the
144/440 band configs.
2026-04-15 14:37:43 -05:00

49 lines
1.7 KiB
Elixir
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

defmodule Microwaveprop.Workers.IonosphereFetchWorkerTest do
use Microwaveprop.DataCase, async: false
use Oban.Testing, repo: Microwaveprop.Repo
alias Microwaveprop.Ionosphere
alias Microwaveprop.Ionosphere.GiroClient
alias Microwaveprop.Ionosphere.Observation
alias Microwaveprop.Workers.IonosphereFetchWorker
@sample File.read!("test/fixtures/giro/mhj45_sample.txt")
describe "perform/1" do
test "pulls each configured station and upserts the parsed rows" do
Req.Test.stub(GiroClient, fn conn ->
assert conn.method == "GET"
params = Plug.Conn.fetch_query_params(conn).query_params
assert params["ursiCode"] in ~w(MHJ45 AL945)
Plug.Conn.resp(conn, 200, @sample)
end)
assert :ok = perform_job(IonosphereFetchWorker, %{})
# 25 observations per station × 2 stations = 50 rows (fixture has 25
# data lines; upserts go to each station code separately).
assert Repo.aggregate(Observation, :count, :id) == 50
assert Ionosphere.latest_observation("MHJ45")
assert Ionosphere.latest_observation("AL945")
end
test "survives a station's fetch error and still ingests the others" do
Req.Test.stub(GiroClient, fn conn ->
params = Plug.Conn.fetch_query_params(conn).query_params
if params["ursiCode"] == "MHJ45" do
Plug.Conn.resp(conn, 500, "boom")
else
Plug.Conn.resp(conn, 200, @sample)
end
end)
assert :ok = perform_job(IonosphereFetchWorker, %{})
# MHJ45 failed, AL945 ingested.
assert Ionosphere.latest_observation("MHJ45") == nil
assert Ionosphere.latest_observation("AL945")
end
end
end