Measurements

Learn how to capture and validate data within OpenHTF phases, including boolean, string, numeric, multidimensional, and marginal measurements.

Last updated · Verified with OpenHTF 1.6.1

Create measurements to capture and validate data within phases.

A measurement declares a name and a validator. When the phase sets the value, OpenHTF checks it against the validator: temperature 25 °C in range 0 to 100 passes, voltage 10.6 V above the 10 V maximum fails, and firmware version 1.2.4 equals the expected string and passes.

  • temperature.in_range(0, 100)25.0 °CPASS
    0100
  • voltage.in_range(maximum=10)10.6 VFAIL
    010
  • firmware_version.equals("1.2.4")"1.2.4"PASS
    expected "1.2.4"=="1.2.4"

Hardware tests are more complex than simple pass/fail checks like in software testing. They often require measuring physical values and comparing them to limits. OpenHTF simplifies logging and validating numeric, string, and boolean values, either individually or in arrays, using built-in decorators.

Numeric

You can define and validate numeric measurements.

main.py
import openhtf as htf
from openhtf.util import units

@htf.measures(
    htf.Measurement("temperature")     # Declares the measurement name
    .in_range(0, 100)                  # Defines the validator
    .with_units(units.DEGREE_CELSIUS)  # Specifies the unit
    .with_precision(1)                 # Rounds to 1 decimal place
)
def phase_temperature(test):
    test.measurements.temperature = 25 # Set the temperature value to 25°C

def main():
    test = htf.Test(phase_temperature)
    test.execute(lambda: "SN1234")

if __name__ == "__main__":
    main()

Validators

.in_range(minimum, maximum)number, number

Ensure the measurement is within the given range.

.within_percent(value, percent)number, number

Ensure the measurement is within the given percentage range.

.equals(value)number

Ensure the measurement exactly matches the specified value.

.with_validator(lambda)function → bool

Apply a custom validator function to the measurement.

Options

.with_units(units)UnitDescriptor

Define the unit of the measurement (e.g. units.AMPERE, units.VOLT). All 2,134 constants are listed in the units reference.

.with_precision(precision)int

Round the value to the specified precision before validation.

main.py
import openhtf as htf
from openhtf.util import units

@htf.measures(
    htf.Measurement("voltage")
    .in_range(maximum=10)
    .with_units(units.VOLT)
)
def phase_voltage(test):
    test.measurements.voltage = 5.3

@htf.measures(
    htf.Measurement("memory")
    .equals(8)
    .with_units(units.GIGABYTE)
)
def phase_memory(test):
    test.measurements.memory = 8

def main():
    test = htf.Test(phase_voltage, phase_memory)
    test.execute(lambda: "SN1234")

if __name__ == "__main__":
    main()

String

You can define and validate string measurements.

main.py
import openhtf as htf
from openhtf.util import units

@htf.measures(
    htf.Measurement("firmware_version")
    .equals("1.2.4")
)
def phase_firmware(test):
    test.measurements.firmware_version = "1.2.4"

def main():
    test = htf.Test(phase_firmware)
    test.execute(lambda: "SN1234")

if __name__ == "__main__":
    main()

Validators

.equals(value)str

Ensure the measurement exactly matches the specified value.

.matches_regex(pattern)str

Ensure the string matches the specified regex pattern.

.with_validator(lambda)function → bool

Apply a custom validator function to the measurement.

Boolean

You can define and validate boolean measurements.

main.py
import openhtf as htf

@htf.measures(
    htf.Measurement("is_led_switch_on")
    .equals(True)
)
def phase_led(test):
    test.measurements.is_led_switch_on = True

def main():
    test = htf.Test(phase_led)
    test.execute(lambda: "SN1234")

if __name__ == "__main__":
    main()

Validators

.equals(value)bool

Ensure the measurement exactly matches the specified value.

.with_validator(lambda)function → bool

Apply a custom validator function to the measurement.

Multi-dimensional

You can capture data in arrays, like time-series or sweeps, with multidimensional measurements. Each dimension is an input axis (the coordinates you index with); with_units describes the stored value.

main.py
import openhtf as htf
from openhtf.util import units
import random

@htf.measures(
    htf.Measurement("temperature_over_time")
    .with_dimensions(units.SECOND)          # Input axis: elapsed time
    .with_units(units.DEGREE_CELSIUS)       # Stored value: temperature
)
def thermal_soak(test):
    for t in range(10):
        temperature = round(24 + random.uniform(-0.5, 0.5), 2)
        test.measurements.temperature_over_time[t] = temperature

@htf.measures(
    htf.Measurement("gain_sweep")
    .with_dimensions(units.HERTZ, units.VOLT)  # Axes: frequency, input level
    .with_units(units.DECIBEL)                 # Stored value: gain
)
def frequency_sweep(test):
    for freq in (100, 1_000, 10_000):
        for level in (0.1, 1.0):
            test.measurements.gain_sweep[freq, level] = round(20 - freq / 10_000, 2)

def main():
    test = htf.Test(thermal_soak, frequency_sweep)
    test.execute(lambda: "SN1234")

if __name__ == "__main__":
    main()

Indexing a dimensioned measurement returns a DimensionedMeasuredValue; call .to_dataframe() on it (requires pandas) to analyse the samples inside a later phase. Monitors create a one-dimensional time series like temperature_over_time automatically from a background thread.

Options

.with_dimensions(*dims)UnitDescriptor or Dimension

Declare one input axis per argument. Use htf.Dimension(description=..., unit=...) to label an axis.

.dimension_pivot_validate(validator)validator

Apply a scalar validator to every stored value, e.g. .dimension_pivot_validate(validators.in_range(20, 30)). See the validators reference.

Multiple measurements

You can also use multiple measurements in a single phase.

main.py
import openhtf as htf
from openhtf.util import units

@htf.measures(
    htf.Measurement("is_connected").equals(True),
    htf.Measurement("firmware_version").equals("1.2.7"),
    htf.Measurement("temperature").in_range(0, 100).with_units(units.DEGREE_CELSIUS),
)
def phase_multi_measurements(test):
    test.measurements.is_connected = True
    test.measurements.firmware_version = "1.2.7" if test.measurements.is_connected else "N/A"
    test.measurements.temperature = 22.5

def main():
    test = htf.Test(phase_multi_measurements)
    test.execute(lambda: "SN1234")

if __name__ == "__main__":
    main()

Advanced use cases

Marginal

You can mark a measure as marginal to show it's close to failing, even if it passes.

main.py
import openhtf as htf
from openhtf.util import units

@htf.measures(
    htf.Measurement('resistance')
    .with_units('ohm')
    .in_range(minimum=5, maximum=17, marginal_minimum=9, marginal_maximum=11)
)
def phase_marginal(test):
  test.measurements.resistance = 13

def main():
    test = htf.Test(phase_marginal)
    test.execute(lambda: "SN1234")

if __name__ == "__main__":
    main()
Terminal
================= test: openhtf_test  outcome: PASS (MARGINAL) =================

Documentation

You can add a description to your measurements.

main.py
import openhtf as htf

@htf.measures(
    htf.Measurement("temperature")
    .in_range(0, 100)
    .doc("This measurement tracks the ambient temperature during the test.")
)
def phase_temperature(test):
    test.measurements.temperature = 25

def main():
    test = htf.Test(phase_temperature)
    test.execute(lambda: "SN1234")

if __name__ == "__main__":
    main()

Dynamic naming

You can customize measurement names dynamically at execution.

main.py
import openhtf as htf

@htf.measures(
    htf.Measurement("test_result_{level}")
    .with_args(level="high")
    .equals(True)
)
def phase_test(test):
    test.measurements.test_result_high = True

def main():
    test = htf.Test(phase_test)
    test.execute(lambda: "SN1234")

if __name__ == "__main__":
    main()

Transformation function

You can apply measurement transformation functions before validation.

main.py
import openhtf as htf

@htf.measures(
    htf.Measurement("voltage")
    .in_range(0, 10)
    .with_transform(lambda x: x * 1.1)
    .with_units("V")
)
def phase_voltage(test):
    test.measurements.voltage = 5  # Value will be transformed to 5.5 before validation

def main():
    test = htf.Test(phase_voltage)
    test.execute(lambda: "SN1234")

if __name__ == "__main__":
    main()

Callable decorators

Python decorators are callables, so you can define measurement parameters dynamically at runtime and apply them to phase functions. The Decorators page explains the mechanism; this is the measurement-specific case.

main.py
import openhtf as htf
from openhtf.util import units

def MyPhaseFunction(test):
    """Define the function without any htf decorators that need to change at runtime"""
    test.measurements.resistance = 10.5

def main():
    # Get measurement limits dynamically at runtime
    min_ohms = 5
    max_ohms = 17  # These could come from configuration, user input, etc.

    # Create the measurement with dynamic parameters
    my_measurement = (htf.Measurement('resistance')
                      .with_units('ohm')
                      .in_range(minimum=min_ohms, maximum=max_ohms))

    # Apply the decorator as a callable to create the phase
    my_phase = htf.measures(my_measurement)(MyPhaseFunction)

    # Use the dynamically created phase in your test
    test = htf.Test([my_phase])
    test.execute(lambda: "SN1234")

if __name__ == "__main__":
    main()

Typical use: limits loaded from a configuration file or a product database at station start.

On this page

First-pass yield
0%4.1
Track with TofuPilot