- 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
86 lines
2.3 KiB
Elixir
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
|