Transforms & scaling
A raw register rarely is the engineering value. A transform turns the decoded integer into the number you want, exactly.
Static rational scale and offset
The common case: value = raw × scale + offset, where scale and offset are
exact rationals (numerator / denominator), never lossy floats.
# 2305 → 230.5 V
transform: { scale: { numerator: 1, denominator: 10 } }
# raw × 0.1 − 1 (a signed-range trick some inverters use)
transform:
scale: { numerator: 1, denominator: 10 }
offset: { numerator: -1, denominator: 1 }
Runtimes compute this with exact rational arithmetic (i128, bigint,
fractions.Fraction, …) and surface a double. Encoding a write runs the
inverse exactly.
Register-referenced scale factors (SunSpec)
SunSpec devices store a shared scale factor in a companion register: the
value's real magnitude is raw × 10^SF, where SF is read live from another
point. Use scale_ref:
# ac_power = raw × 10^(value of w_sf)
transform:
scale_ref: { point_id: w_sf, mode: POW10 }
mode: MULTIPLY covers the raw × ref / denominator variant. The runtime reads
the referenced point automatically before decoding.
Selector cases
Some registers change meaning based on another register. For example, an energy
counter's unit (Wh or kWh) can depend on a format flag. selector_ref picks a
per-case scale and offset, falling back to the point's own transform when no
case matches:
selector_ref:
point_id: energy_format
cases:
0: { scale: { numerator: 1, denominator: 1000 }, unit: kWh }
1: { scale: { numerator: 1, denominator: 1 }, unit: kWh }
Composed values
COMPOSED storage reconstructs mantissa × base^exponent from two
sub-mappings of the same register span. A few meters use this form for
wide-dynamic-range readings. It is read-only; sub-mapping offsets are word
offsets relative to the point's mapped window.
Since v0.5 a sub-mapping can also carry a bit window
(bit_offset/bit_length), for meters that embed the decade exponent in the
high bits of the same word as the mantissa — Iskra's T5/T6 measurement types
(signed 8-bit exponent in bits 31–24, 24-bit mantissa in bits 23–0) and the
Eaton PXM "GENERAL FORMAT" (8-bit exponent + 56-bit mantissa). The vendor
example FD01 E240 decodes as 123456 × 10⁻³ = 123.456:
storage_type: COMPOSED
mapping:
offset: 140
length_words: 2
composed:
kind: MANTISSA_EXPONENT
base: 10
mantissa: { offset: 0, length_words: 2, storage_type: S32, bit_offset: 0, bit_length: 24 }
exponent: { offset: 0, length_words: 2, storage_type: S16, bit_offset: 24, bit_length: 8 }
The sub-mapping's storage_type supplies signedness; a bit-windowed value
sign-extends from bit_length. Composed points may also overlap each other
— their parent mappings are read windows, which is how interleaved
mantissa/exponent register pools (the Iskra non-reset energy totals) are
modeled.
All of these are defined once in the document and applied identically by every runtime, and the conformance suite pins them to the same numeric results.