TdoaLocalizationBenchmark.java
package org.hammer.audio.experimental.acoustic.benchmark.localization;
import java.util.Objects;
import org.hammer.audio.experimental.acoustic.benchmark.BenchmarkReport;
import org.hammer.audio.experimental.acoustic.benchmark.TrackingScenarioBenchmarkRunner;
import org.hammer.audio.experimental.acoustic.simulation.SimulationScenarios.SimulationScenario;
/**
* {@link LocalizationBenchmark} implementation using TDOA-based localization via the existing
* {@link TrackingScenarioBenchmarkRunner} pipeline (GCC-PHAT TDOA + delay-and-sum beamforming +
* Kalman tracking).
*
* <p>This benchmark delegates all signal processing to the default tracking pipeline and extracts
* the relevant localization metrics from the resulting {@link BenchmarkReport}.
*/
public final class TdoaLocalizationBenchmark implements LocalizationBenchmark {
private static final int DEFAULT_BLOCK_FRAMES = 1024;
private final TrackingScenarioBenchmarkRunner runner;
/** Create a benchmark with the default block size ({@value DEFAULT_BLOCK_FRAMES} frames). */
public TdoaLocalizationBenchmark() {
this(DEFAULT_BLOCK_FRAMES);
}
/**
* Create a benchmark with the given block size.
*
* @param blockFrames FFT block size in samples; must be {@code >= 128}
*/
public TdoaLocalizationBenchmark(int blockFrames) {
if (blockFrames < 128) {
throw new IllegalArgumentException("blockFrames must be >= 128");
}
this.runner = new TrackingScenarioBenchmarkRunner(blockFrames);
}
@Override
public LocalizationBenchmarkResult run(SimulationScenario scenario) {
Objects.requireNonNull(scenario, "scenario");
BenchmarkReport report = runner.run(scenario);
return extractResult(scenario.name(), report);
}
@Override
public String name() {
return "TdoaLocalizationBenchmark";
}
/** Extract a {@link LocalizationBenchmarkResult} from a {@link BenchmarkReport}. */
private static LocalizationBenchmarkResult extractResult(
String scenarioId, BenchmarkReport report) {
double meanLocError =
report.localization().meanDistanceErrorMeters() != null
? report.localization().meanDistanceErrorMeters()
: 0.0;
// trackingError: 1 - continuity, or 0 if unavailable
double trackingError = 0.0;
if (report.trackContinuity() != null) {
trackingError = 1.0 - report.trackContinuity();
}
// False positives and negatives: derived from rates × total evaluated frames
int evaluatedFrames = report.snapshotCount();
int falsePositives = 0;
int falseNegatives = 0;
if (report.falsePositiveRate() != null && evaluatedFrames > 0) {
falsePositives = (int) Math.round(report.falsePositiveRate() * evaluatedFrames);
}
if (report.falseNegativeRate() != null && evaluatedFrames > 0) {
falseNegatives = (int) Math.round(report.falseNegativeRate() * evaluatedFrames);
}
return new LocalizationBenchmarkResult(
scenarioId,
meanLocError,
Math.min(1.0, Math.max(0.0, trackingError)),
falsePositives,
falseNegatives,
evaluatedFrames);
}
}