Skip to content

Tag: add DisplayScale / DisplayOffset — engineering-unit / calibration scaling on the read path #348

Description

@HanSur94

Problem / motivation

Tag already carries a Units property (libs/SensorThreshold/Tag.m:54), but it is a pure label — there is no way to actually convert stored values into those units. Sensor engineers routinely log raw ADC counts, mV, or SI base units and then want to view and analyze them in engineering units via a simple linear calibration (y = raw * scale + offset) or a unit swap (Pa↔bar, °C↔°F, counts→%).

Today the only path is to wrap the sensor in a DerivedTag expression. That is heavyweight for a one-line scaling and forfeits the SensorTag identity: disk persistence, thresholds bound to the sensor, and live-ingestion behavior all live on the original tag, not the derived wrapper. So Units announces a unit the data isn't actually in — a real asymmetry.

Proposed feature

Two additive public scalar properties on the base Tag, applied on the read path only (stored data untouched):

  • DisplayScale (default 1)
  • DisplayOffset (default 0)

Applied at the output of getXY / getXYRange:

Y = Y * DisplayScale + DisplayOffset;

Because the defaults are the identity transform, every existing tag and every serialized tag behaves exactly as before.

Rough sketch

  • Lib/class: libs/SensorThreshold/Tag.m (property declarations + constructor name-value keys + struct serialization keys), applied in the getXY / getXYRange read path of the concrete kinds (SensorTag, DerivedTag, CompositeTag). StateTag is categorical and out of scope.
  • Public API shape:
st = SensorTag('press_a', 'Units', 'bar', 'DisplayScale', 1e-5);  % Pa -> bar
st.DisplayOffset = 0;
[t, y] = st.getXY();   % y already in bar
  • Open question for triage: whether thresholds/analysis primitives (getStats, violation checks) should read pre- or post-conversion values. Recommend documenting that the conversion is a display/analysis read transform and applying it consistently at the single getXY/getXYRange seam so downstream consumers inherit it for free.

Value

Opens a fresh dimension (unit / calibration handling) that none of the ~90 open issues touch — the current backlog clusters on analysis primitives, widget parity, and EventViewer. One-line calibration on any tag kind, no DerivedTag scaffolding, and Units finally means what it says.

Constraints check

  • Toolbox-free: yes — scalar arithmetic only.
  • Backward-compatible: yes — defaults (1, 0) reproduce current output exactly; existing scripts and serialized tags are unaffected; two scalars round-trip trivially through the existing struct (de)serialization.
  • Pure MATLAB / Octave: yes.
  • Widget/Tag contract: works through the existing Tag getXY/getXYRange contract; every subclass and every downstream widget inherits it without changes.

Effort estimate

S–M — property + serialization plumbing on Tag, plus the one-line transform at the getXY/getXYRange seam in each concrete kind, and round-trip tests.


AI-proposed via /feature-scout — needs a human product decision before implementation.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions