TdoaPeakDiagnostics.java
package org.hammer.audio.experimental.acoustic;
/**
* Confidence evidence for one GCC-PHAT lag estimate.
*
* @param interpolatedLagSamples estimated lag at sub-sample resolution
* @param primaryPeak absolute score of the selected correlation peak
* @param secondaryPeak strongest score outside the selected peak's one-sample guard region
* @param peakRatio primary-to-secondary peak ratio
* @param normalizedCurvature interpolation-resolution-independent local peak curvature
* @param ambiguous whether the configured separation or curvature threshold was missed
*/
public record TdoaPeakDiagnostics(
double interpolatedLagSamples,
double primaryPeak,
double secondaryPeak,
double peakRatio,
double normalizedCurvature,
boolean ambiguous) {
// Validate one immutable peak diagnostic.
public TdoaPeakDiagnostics {
requireFinite(interpolatedLagSamples, "interpolatedLagSamples");
requireNonNegativeFinite(primaryPeak, "primaryPeak");
requireNonNegativeFinite(secondaryPeak, "secondaryPeak");
requireNonNegativeFinite(peakRatio, "peakRatio");
requireNonNegativeFinite(normalizedCurvature, "normalizedCurvature");
if (secondaryPeak > primaryPeak) {
throw new IllegalArgumentException("secondaryPeak must not exceed primaryPeak");
}
}
/** Normalized separation of the primary peak from competing peaks. */
public double separation() {
return primaryPeak > 0.0 ? Math.max(0.0, 1.0 - secondaryPeak / primaryPeak) : 0.0;
}
private static void requireFinite(double value, String name) {
if (Double.isFinite(value)) {
return;
}
throw new IllegalArgumentException(name + " must be finite");
}
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");
}
}