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