mgth/LittleBigMouse · error · CalibrationMeasurementException

The probe returned an invalid Delta E value.

Error message

The probe returned an invalid Delta E value.

What it means

WhitePointOptimizer.MeasureAtGainsAsync applies a gain setting, waits the settle delay, and reads the probe measurement. If measurement.DeltaE is not finite (NaN or Infinity), the value is useless for optimization, so the library throws CalibrationMeasurementException instead of caching or optimizing against garbage data.

Solutions

  1. Retry the measurement at the same gains after re-seating/stabilizing the probe on the panel
  2. Verify the probe is connected, calibrated, and its driver/firmware reports valid readings outside this library
  3. Increase settleDelay so the display and probe reach steady state before MeasureAsync
  4. Catch CalibrationMeasurementException around optimization and abort/restart the calibration run

Example fix

// before
var result = await optimizer.OptimizeAsync(...); // throws on NaN DeltaE
// after
try { var result = await optimizer.OptimizeAsync(...); }
catch (CalibrationMeasurementException ex)
{
    logger.LogWarning(ex, "Probe returned invalid Delta E; recheck probe placement and retry.");
}
Defensive patterns

Strategy: try-catch

Validate before calling

var m = await hardware.MeasureAsync(ct);
if (!double.IsFinite(m.DeltaE))
    throw new CalibrationMeasurementException("Probe returned invalid Delta E.");

Type guard

static bool IsValidMeasurement(ProbeMeasurement m) => double.IsFinite(m.DeltaE);

Try / catch

try
{
    await optimizer.OptimizeAsync(...);
}
catch (CalibrationMeasurementException ex)
{
    // abort calibration, prompt probe recheck, offer retry
}

Prevention

When it happens

Trigger: Calling MeasureAtGainsAsync (directly, or via TuneChannelAsync / the baseline and measured passes) when hardware.MeasureAsync returns a DeltaE of NaN or Infinity — e.g. the colorimeter lost its calibration, returned a failed reading, or measured in an invalid state.

Common situations: Probe unclipped or moved during settling; ambient light or a black screen producing a division-by-zero in the meter's firmware; a colorimeter driver returning sentinel values; USB hiccup producing an empty reading.

Related errors


AI-assisted analysis of mgth/LittleBigMouse@7a42f01d47 (2026-09-16). Data as JSON: /api/errors/731455b304702642. Report an issue: GitHub.

Appendix: source

Thrown at LittleBigMouse.Plugins/LittleBigMouse.Plugin.Vcp/Calibration/WhitePointOptimizer.cs:140

        return new(gains, deltaE, gains != original, reads);
    }

    static async Task<CachedMeasurement> MeasureAtGainsAsync(
        ICalibrationHardware hardware,
        CalibrationRgb gains,
        TimeSpan settleDelay,
        Dictionary<CalibrationRgb, double> cache,
        CancellationToken cancellationToken)
    {
        if (cache.TryGetValue(gains, out var cached)) return new(cached, false);

        await hardware.SetGainsAsync(gains, cancellationToken).ConfigureAwait(false);
        if (settleDelay > TimeSpan.Zero)
            await Task.Delay(settleDelay, cancellationToken).ConfigureAwait(false);
        var measurement = await hardware.MeasureAsync(cancellationToken).ConfigureAwait(false);
        if (!double.IsFinite(measurement.DeltaE))
            throw new CalibrationMeasurementException("The probe returned an invalid Delta E value.");
        cache[gains] = measurement.DeltaE;
        return new(measurement.DeltaE, true);
    }

    static bool CanMove(
        CalibrationRgb gains,
        IReadOnlyList<int> channels,
        int count,
        IReadOnlyList<uint> boundary,
        bool down)
    {
        for (var i = 0; i < count; i++)
        {
            var value = gains.Channel(channels[i]);
            if (down ? value <= boundary[i] : value >= boundary[i]) return false;
        }
        return true;
    }

View on GitHub (pinned to 7a42f01d47)