TdoaAlgorithmBenchmarkRunner.java
package org.hammer.audio.experimental.acoustic.benchmark.tdoa;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import org.hammer.audio.experimental.acoustic.DiagnosticTdoaEstimate;
import org.hammer.audio.experimental.acoustic.DiagnosticTdoaEstimator;
import org.hammer.audio.experimental.acoustic.TdoaEstimate;
/** Runs named TDOA estimators over identical deterministic known-delay cases. */
public final class TdoaAlgorithmBenchmarkRunner {
private final List<NamedTdoaEstimator> estimators;
/** Creates a runner with unique non-empty estimator registrations. */
public TdoaAlgorithmBenchmarkRunner(List<NamedTdoaEstimator> estimators) {
this.estimators = List.copyOf(Objects.requireNonNull(estimators, "estimators"));
if (this.estimators.isEmpty()) {
throw new IllegalArgumentException("estimators must not be empty");
}
Set<String> names = new HashSet<>();
for (NamedTdoaEstimator estimator : this.estimators) {
if (!names.add(estimator.name())) {
throw new IllegalArgumentException("duplicate estimator name: " + estimator.name());
}
}
}
/** Evaluates every estimator over the same ordered benchmark cases. */
public TdoaAlgorithmBenchmarkReport run(List<TdoaBenchmarkCase> cases) {
List<TdoaBenchmarkCase> requiredCases = List.copyOf(Objects.requireNonNull(cases, "cases"));
if (requiredCases.isEmpty()) {
throw new IllegalArgumentException("cases must not be empty");
}
List<TdoaAlgorithmBenchmarkResult> results = new ArrayList<>(estimators.size());
for (NamedTdoaEstimator named : estimators) {
results.add(runEstimator(named, requiredCases));
}
return new TdoaAlgorithmBenchmarkReport(results);
}
private static TdoaAlgorithmBenchmarkResult runEstimator(
NamedTdoaEstimator named, List<TdoaBenchmarkCase> cases) {
double totalAbsoluteError = 0.0;
double maximumAbsoluteError = 0.0;
double totalConfidence = 0.0;
int ambiguousCount = 0;
for (TdoaBenchmarkCase benchmarkCase : cases) {
TdoaEstimate estimate;
if (named.estimator() instanceof DiagnosticTdoaEstimator diagnostic) {
DiagnosticTdoaEstimate detailed =
diagnostic.estimateDetailed(
benchmarkCase.block(),
benchmarkCase.array(),
benchmarkCase.firstChannel(),
benchmarkCase.secondChannel());
estimate = detailed.estimate();
if (detailed.diagnostics().ambiguous()) {
ambiguousCount++;
}
} else {
estimate =
named
.estimator()
.estimate(
benchmarkCase.block(),
benchmarkCase.array(),
benchmarkCase.firstChannel(),
benchmarkCase.secondChannel());
}
double estimatedDelaySamples =
estimate.delaySeconds() * benchmarkCase.block().format().sampleRate();
double absoluteError = Math.abs(estimatedDelaySamples - benchmarkCase.expectedDelaySamples());
totalAbsoluteError += absoluteError;
maximumAbsoluteError = Math.max(maximumAbsoluteError, absoluteError);
totalConfidence += estimate.confidence();
}
return new TdoaAlgorithmBenchmarkResult(
named.name(),
cases.size(),
totalAbsoluteError / cases.size(),
maximumAbsoluteError,
totalConfidence / cases.size(),
ambiguousCount);
}
}