CalibrationEventObservation.java

package org.hammer.audio.experimental.acoustic.calibration;

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

/**
 * Estimated relative channel delays for one simultaneous calibration event.
 *
 * @param frameIndex absolute nominal frame of the observation block
 * @param referenceChannel zero-delay reference channel
 * @param offsetsSamples observed per-channel delay relative to the reference channel
 * @param confidences normalized per-channel correlation confidence
 */
public record CalibrationEventObservation(
    long frameIndex, int referenceChannel, List<Double> offsetsSamples, List<Double> confidences) {

  // Validate and defensively copy one complete event observation.
  public CalibrationEventObservation {
    if (frameIndex < 0) {
      throw new IllegalArgumentException("frameIndex must be >= 0");
    }
    offsetsSamples = List.copyOf(Objects.requireNonNull(offsetsSamples, "offsetsSamples"));
    confidences = List.copyOf(Objects.requireNonNull(confidences, "confidences"));
    if (offsetsSamples.isEmpty() || offsetsSamples.size() != confidences.size()) {
      throw new IllegalArgumentException("offset and confidence lists must be non-empty and equal");
    }
    if (referenceChannel < 0 || referenceChannel >= offsetsSamples.size()) {
      throw new IllegalArgumentException("referenceChannel must exist in observations");
    }
    for (int channel = 0; channel < offsetsSamples.size(); channel++) {
      double offset = offsetsSamples.get(channel);
      double confidence = confidences.get(channel);
      if (!Double.isFinite(offset)) {
        throw new IllegalArgumentException("offsets must be finite");
      }
      if (!Double.isFinite(confidence) || confidence < 0.0 || confidence > 1.0) {
        throw new IllegalArgumentException("confidences must be finite and in [0,1]");
      }
    }
    if (Math.abs(offsetsSamples.get(referenceChannel)) > 1.0e-9) {
      throw new IllegalArgumentException("reference channel offset must be zero");
    }
  }

  /** Number of observed channels. */
  public int channels() {
    return offsetsSamples.size();
  }
}