NCEI ASOS 5-minute data client (Weather.NceiMetarClient): - fetch/3 pulls per-station monthly .dat files from NCEI C00418 - parse/1 decodes the fixed-width METAR format including precise T-group temperatures (T02110094 → 21.1/9.4°C) - metar_5min_observations table: schema-identical to surface_observations, separate table to avoid mixing cadences Weather.recent_surface_obs/3 prefers 5-min data when available, falls back to the hourly surface_observations table. Data URL: https://www.ncei.noaa.gov/data/automated-surface-observing-system-five-minute/access/YYYY/MM/asos-5min-KXXX-YYYYMM.dat Available back to 1996.
49 lines
1.9 KiB
Elixir
49 lines
1.9 KiB
Elixir
defmodule Microwaveprop.Weather.NceiMetarClientTest do
|
|
use ExUnit.Case, async: true
|
|
|
|
alias Microwaveprop.Weather.NceiMetarClient
|
|
|
|
@sample_line "03927KDFW DFW20260301000010303/01/26 00:00:31 5-MIN KDFW 010600Z 17012KT 10SM CLR 21/09 A2997 560 47 1400 160/12 RMK AO2 T02110094"
|
|
|
|
describe "parse/1" do
|
|
test "parses a single KDFW observation" do
|
|
[obs] = NceiMetarClient.parse(@sample_line)
|
|
|
|
assert obs.icao == "KDFW"
|
|
assert obs.observed_at == ~U[2026-03-01 00:00:00Z]
|
|
# T02110094 → 21.1°C = 70.0°F, 9.4°C = 48.9°F
|
|
assert_in_delta obs.temp_f, 70.0, 0.2
|
|
assert_in_delta obs.dewpoint_f, 48.9, 0.2
|
|
assert obs.wind_speed_kts == 12.0
|
|
assert obs.wind_direction_deg == 170
|
|
assert_in_delta obs.altimeter_setting, 29.97, 0.01
|
|
assert obs.sky_condition == "CLR"
|
|
end
|
|
|
|
test "parses multiple lines" do
|
|
text = """
|
|
03927KDFW DFW20260301000010303/01/26 00:00:31 5-MIN KDFW 010600Z 17012KT 10SM CLR 21/09 A2997 560 47 1400 160/12 RMK AO2 T02110094
|
|
03927KDFW DFW20260301000510303/01/26 00:05:31 5-MIN KDFW 010605Z 17012KT 10SM CLR 21/09 A2997 560 47 1400 160/12 RMK AO2 T02110094
|
|
"""
|
|
|
|
obs = NceiMetarClient.parse(text)
|
|
assert length(obs) == 2
|
|
assert Enum.at(obs, 0).observed_at == ~U[2026-03-01 00:00:00Z]
|
|
assert Enum.at(obs, 1).observed_at == ~U[2026-03-01 00:05:00Z]
|
|
end
|
|
|
|
test "skips blank or too-short lines" do
|
|
assert NceiMetarClient.parse("") == []
|
|
assert NceiMetarClient.parse("short\n\n") == []
|
|
end
|
|
|
|
test "handles negative temperatures via M prefix" do
|
|
line = "03927KDFW DFW20260115120010103/15/26 12:00:31 5-MIN KDFW 151200Z 36005KT 10SM CLR M02/M08 A3032 560 47 1400 360/05 RMK AO2"
|
|
[obs] = NceiMetarClient.parse(line)
|
|
|
|
# M02 = -2°C = 28.4°F, M08 = -8°C = 17.6°F
|
|
assert_in_delta obs.temp_f, 28.4, 0.2
|
|
assert_in_delta obs.dewpoint_f, 17.6, 0.2
|
|
end
|
|
end
|
|
end
|