SynchronizationAssessment.java

package org.hammer.audio.acquisition;

import java.util.List;
import java.util.Objects;

/**
 * Immutable synchronization quality attached to one localization observation.
 *
 * @param mode synchronization model used by the estimator
 * @param status trust decision against the configured timing-error budget
 * @param estimatedErrorSamples conservative timing error in samples
 * @param estimatedErrorSeconds conservative timing error in seconds
 * @param calibrationCurrent whether the calibration validity window contains the observation time
 * @param diagnostics human-readable deterministic diagnostics
 */
public record SynchronizationAssessment(
    SynchronizationMode mode,
    SynchronizationStatus status,
    double estimatedErrorSamples,
    double estimatedErrorSeconds,
    boolean calibrationCurrent,
    List<String> diagnostics) {

  // Validate and defensively copy one synchronization assessment.
  public SynchronizationAssessment {
    Objects.requireNonNull(mode, "mode");
    Objects.requireNonNull(status, "status");
    requireNonNegativeFinite(estimatedErrorSamples, "estimatedErrorSamples");
    requireNonNegativeFinite(estimatedErrorSeconds, "estimatedErrorSeconds");
    diagnostics = List.copyOf(Objects.requireNonNull(diagnostics, "diagnostics"));
  }

  /** Declares a shared-clock observation with no measured inter-channel calibration error. */
  public static SynchronizationAssessment nominalSharedClock() {
    return new SynchronizationAssessment(
        SynchronizationMode.NOMINAL_SHARED_CLOCK,
        SynchronizationStatus.TRUSTED,
        0.0,
        0.0,
        true,
        List.of("Channels are assumed to share one hardware sample clock."));
  }

  /** Returns whether localization should proceed with this synchronization state. */
  public boolean usable() {
    return status != SynchronizationStatus.REJECTED;
  }

  private static void requireNonNegativeFinite(double value, String name) {
    if (Double.isFinite(value) && value >= 0.0) {
      return;
    }
    throw new IllegalArgumentException(name + " must be finite and >= 0");
  }
}