prop/test/microwaveprop/weather/grib2/simple_packing_test.exs
Graham McIntire 5a9359191f
Replace wgrib2 with pure Elixir GRIB2 decoder for HRRR pipeline
Eliminates the external wgrib2 C tool dependency that blocked HRRR
processing. Implements Lambert Conformal projection, simple packing
(Template 5.0), complex packing with spatial differencing (Template 5.3),
and GRIB2 section parsing — enough to extract point values from HRRR
grid data using only Elixir.
2026-03-29 18:43:43 -05:00

168 lines
4.6 KiB
Elixir

defmodule Microwaveprop.Weather.Grib2.SimplePackingTest do
use ExUnit.Case, async: true
alias Microwaveprop.Weather.Grib2.SimplePacking
describe "extract_value/3" do
test "basic 16-bit unpacking" do
# value = (R + X * 2^E) * 10^(-D)
# R=0.0, E=0, D=0, N=16 → value = X
params = %{
reference_value: 0.0,
binary_scale: 0,
decimal_scale: 0,
bits_per_value: 16,
num_data_points: 4
}
# 4 values: 100, 200, 300, 400 as 16-bit big-endian
data = <<100::16-big, 200::16-big, 300::16-big, 400::16-big>>
assert {:ok, 100.0} = SimplePacking.extract_value(params, data, 0)
assert {:ok, 200.0} = SimplePacking.extract_value(params, data, 1)
assert {:ok, 300.0} = SimplePacking.extract_value(params, data, 2)
assert {:ok, 400.0} = SimplePacking.extract_value(params, data, 3)
end
test "applies reference value" do
# R=273.15, E=0, D=0 → value = 273.15 + X
params = %{
reference_value: 273.15,
binary_scale: 0,
decimal_scale: 0,
bits_per_value: 16,
num_data_points: 2
}
data = <<10::16-big, 20::16-big>>
assert {:ok, val} = SimplePacking.extract_value(params, data, 0)
assert_in_delta val, 283.15, 0.001
end
test "applies binary scale factor" do
# R=0, E=2, D=0 → value = X * 2^2 = X * 4
params = %{
reference_value: 0.0,
binary_scale: 2,
decimal_scale: 0,
bits_per_value: 16,
num_data_points: 1
}
data = <<25::16-big>>
assert {:ok, val} = SimplePacking.extract_value(params, data, 0)
assert_in_delta val, 100.0, 0.001
end
test "applies decimal scale factor" do
# R=0, E=0, D=2 → value = X * 10^(-2) = X / 100
params = %{
reference_value: 0.0,
binary_scale: 0,
decimal_scale: 2,
bits_per_value: 16,
num_data_points: 1
}
data = <<15_000::16-big>>
assert {:ok, val} = SimplePacking.extract_value(params, data, 0)
assert_in_delta val, 150.0, 0.001
end
test "combined R, E, D" do
# value = (R + X * 2^E) * 10^(-D)
# R=100.0, E=1, D=1 → (100 + X * 2) / 10
params = %{
reference_value: 100.0,
binary_scale: 1,
decimal_scale: 1,
bits_per_value: 16,
num_data_points: 1
}
data = <<50::16-big>>
assert {:ok, val} = SimplePacking.extract_value(params, data, 0)
# (100 + 50 * 2) / 10 = 200 / 10 = 20.0
assert_in_delta val, 20.0, 0.001
end
test "12-bit non-byte-aligned extraction" do
# 3 values of 12 bits each = 36 bits = 4.5 bytes
# Values: 0xABC=2748, 0x123=291, 0xDEF=3567
params = %{
reference_value: 0.0,
binary_scale: 0,
decimal_scale: 0,
bits_per_value: 12,
num_data_points: 3
}
# Pack 3 12-bit values: 0xABC, 0x123, 0xDEF
# Binary: 1010_1011_1100 | 0001_0010_0011 | 1101_1110_1111
# As bytes: AB C1 23 DE F0 (with 4 bits padding)
data = <<0xAB, 0xC1, 0x23, 0xDE, 0xF0>>
assert {:ok, val0} = SimplePacking.extract_value(params, data, 0)
assert_in_delta val0, 2748.0, 0.001
assert {:ok, val1} = SimplePacking.extract_value(params, data, 1)
assert_in_delta val1, 291.0, 0.001
assert {:ok, val2} = SimplePacking.extract_value(params, data, 2)
assert_in_delta val2, 3567.0, 0.001
end
test "zero bits_per_value returns reference value" do
params = %{
reference_value: 293.5,
binary_scale: 0,
decimal_scale: 0,
bits_per_value: 0,
num_data_points: 10
}
# With 0 bits, data is empty
data = <<>>
assert {:ok, val} = SimplePacking.extract_value(params, data, 0)
assert_in_delta val, 293.5, 0.001
assert {:ok, val} = SimplePacking.extract_value(params, data, 5)
assert_in_delta val, 293.5, 0.001
end
test "negative binary scale factor" do
# E=-1 → 2^(-1) = 0.5
params = %{
reference_value: 0.0,
binary_scale: -1,
decimal_scale: 0,
bits_per_value: 16,
num_data_points: 1
}
data = <<200::16-big>>
assert {:ok, val} = SimplePacking.extract_value(params, data, 0)
assert_in_delta val, 100.0, 0.001
end
test "index out of range returns error" do
params = %{
reference_value: 0.0,
binary_scale: 0,
decimal_scale: 0,
bits_per_value: 16,
num_data_points: 2
}
data = <<100::16-big, 200::16-big>>
assert {:error, :index_out_of_range} = SimplePacking.extract_value(params, data, 5)
end
end
end