prop/lib/microwaveprop/weather/grib2/simple_packing.ex
Graham McIntire 828814e767
fix: resolve all compiler warnings except framework-generated phoenix_component_verify
- Replace deprecated xref:exclude with elixirc_options in vendor/oban_pro
- Remove unused require Logger from 8 files
- Pin bitstring size variables with ^ in simple_packing, wgrib2,
  complex_packing, section, nexrad_client, mqtt, and radar worker
- Remove dead defp clauses (format/1 nil, depression/2 nil)
- Rename Buildings.Parser type record/0 -> building_record/0 to avoid
  overriding built-in type
- Remove redundant catch-all in candidate_detail.ex
- Simplify contact_live/show.ex conditional based on type narrowing
- Fix DateTime.from_iso8601 return pattern in vendor/oban_web
- Upgrade phoenix_live_view to 1.1.31
- Drop --warnings-as-errors from precommit alias; known false positives
  from @after_verify-generated code in Elixir 1.20.0-rc.6 + PLV 1.1.x
- Add credo --strict to precommit as replacement static-analysis gate
2026-05-29 10:18:52 -05:00

86 lines
2.3 KiB
Elixir

defmodule Microwaveprop.Weather.Grib2.SimplePacking do
@moduledoc false
@doc """
Extract a single value from GRIB2 simple-packed data at the given grid index.
Formula: value = (R + X * 2^E) * 10^(-D)
Where R = reference_value, E = binary_scale, D = decimal_scale,
X = raw packed integer at the given index.
"""
@spec extract_value(map(), binary(), non_neg_integer()) :: {:ok, float()} | {:error, :index_out_of_range}
def extract_value(%{bits_per_value: 0} = params, _data, _index) do
{:ok, params.reference_value * :math.pow(10, -params.decimal_scale)}
end
def extract_value(params, data, index) do
%{
reference_value: r,
binary_scale: e,
decimal_scale: d,
bits_per_value: n,
num_data_points: num
} = params
if index < 0 or index >= num do
{:error, :index_out_of_range}
else
x = extract_bits(data, index, n)
value = (r + x * :math.pow(2, e)) * :math.pow(10, -d)
{:ok, value}
end
end
@doc """
Extract multiple values from GRIB2 simple-packed data at the given grid indices.
Returns `{:ok, %{index => value}}`. Out-of-range indices are silently skipped.
"""
@spec extract_values(map(), binary(), [non_neg_integer()]) :: {:ok, %{non_neg_integer() => float()}}
def extract_values(%{bits_per_value: 0} = params, _data, indices) do
value = params.reference_value * :math.pow(10, -params.decimal_scale)
{:ok, Map.new(indices, fn i -> {i, value} end)}
end
def extract_values(params, data, indices) do
%{
reference_value: r,
binary_scale: e,
decimal_scale: d,
bits_per_value: n,
num_data_points: num
} = params
factor_2e = :math.pow(2, e)
factor_10d = :math.pow(10, -d)
results =
Enum.reduce(indices, %{}, fn index, acc ->
if index >= 0 and index < num do
x = extract_bits(data, index, n)
value = (r + x * factor_2e) * factor_10d
Map.put(acc, index, value)
else
acc
end
end)
{:ok, results}
end
defp extract_bits(data, index, bits_per_value) do
bit_offset = index * bits_per_value
byte_offset = div(bit_offset, 8)
remainder = rem(bit_offset, 8)
<<
_skip::binary-size(^byte_offset),
_pad::size(^remainder),
x::size(^bits_per_value)-unsigned-big,
_rest::bitstring
>> = data
x
end
end