feat: add comparable localization algorithms
This commit is contained in:
@@ -157,6 +157,51 @@ test("단일 tone 장면과 실험이 관계 검증을 통과한다", async () =
|
||||
assertEqual((await validateExperiment(experiment, { arrayDefinition, scene })).length, 0, "experiment issues");
|
||||
});
|
||||
|
||||
test("세 위치 추정 알고리즘과 backend 제약을 계약이 검증한다", async () => {
|
||||
const [arrayDefinition, scene, baseline] = await Promise.all([
|
||||
loadJson("/assets/array-geometries/rectangular-16x12.array.json"),
|
||||
loadJson("/assets/sample-scenes/single-tone.scene.json"),
|
||||
loadJson("/assets/sample-scenes/single-tone.experiment.json"),
|
||||
]);
|
||||
|
||||
for (const algorithm of ["das", "das-phat", "gcc-phat"]) {
|
||||
const experiment = structuredClone(baseline);
|
||||
experiment.beamforming.algorithm = algorithm;
|
||||
experiment.beamforming.backend = algorithm === "gcc-phat" ? "cpu" : "auto";
|
||||
assertEqual(
|
||||
(await validateExperiment(experiment, { arrayDefinition, scene })).length,
|
||||
0,
|
||||
`${algorithm} experiment issues`,
|
||||
);
|
||||
}
|
||||
|
||||
const incompatible = structuredClone(baseline);
|
||||
incompatible.beamforming.algorithm = "gcc-phat";
|
||||
incompatible.beamforming.backend = "webgpu";
|
||||
let issues = await validateExperiment(incompatible, { arrayDefinition, scene });
|
||||
assert(
|
||||
issues.some(({ path }) => path === "$.beamforming.backend"),
|
||||
"GCC-PHAT rejects the WebGPU backend",
|
||||
);
|
||||
|
||||
incompatible.beamforming.backend = "cpu";
|
||||
issues = await validateExperiment(incompatible, {
|
||||
arrayDefinition: { ...arrayDefinition, microphones: arrayDefinition.microphones.slice(0, 1) },
|
||||
scene,
|
||||
});
|
||||
assert(
|
||||
issues.some(({ path }) => path === "$.beamforming.algorithm"),
|
||||
"GCC-PHAT requires two microphones",
|
||||
);
|
||||
|
||||
incompatible.beamforming.algorithm = "music";
|
||||
issues = await validateExperiment(incompatible, { arrayDefinition, scene });
|
||||
assert(
|
||||
issues.some(({ path }) => path === "$.beamforming.algorithm"),
|
||||
"unsupported algorithms are rejected",
|
||||
);
|
||||
});
|
||||
|
||||
test("shoebox 음향 환경의 room과 occluder 경계를 검증한다", async () => {
|
||||
const scene = await loadJson("/assets/sample-scenes/single-tone.scene.json");
|
||||
scene.acoustics = {
|
||||
@@ -357,6 +402,7 @@ test("복수 음원 대응 result map 계약과 shape 관계를 검증한다", a
|
||||
sceneId: experiment.sceneId,
|
||||
createdAt: "2026-08-28T00:00:00.000Z",
|
||||
backend: "cpu",
|
||||
algorithm: "das",
|
||||
inputHash: "a".repeat(64),
|
||||
signalStats: { preNormalizationPeak: 0.5, appliedScale: 1, clippedSampleCount: 0 },
|
||||
timingMs: { total: 10, signalGeneration: 3, beamforming: 7 },
|
||||
@@ -369,10 +415,25 @@ test("복수 음원 대응 result map 계약과 shape 관계를 검증한다", a
|
||||
}],
|
||||
};
|
||||
assertEqual((await validateResult(result, { experiment, scene })).length, 0, "valid result issues");
|
||||
|
||||
experiment.beamforming.algorithm = "das-phat";
|
||||
result.algorithm = "das-phat";
|
||||
result.bands[0].rawMap.scale = "score";
|
||||
assertEqual(
|
||||
(await validateResult(result, { experiment, scene })).length,
|
||||
0,
|
||||
"phase-weighted score result issues",
|
||||
);
|
||||
|
||||
result.algorithm = "das";
|
||||
let issues = await validateResult(result, { experiment, scene });
|
||||
assert(issues.some(({ path }) => path === "$.algorithm"), "result algorithm must match its experiment");
|
||||
|
||||
experiment.beamforming.algorithm = "das";
|
||||
result.bands[0].rawMap.values.pop();
|
||||
result.bands[0].rawMap.scale = "normalized";
|
||||
result.bands[0].peaks[0].sourceId = "missing-source";
|
||||
const issues = await validateResult(result, { experiment, scene });
|
||||
issues = await validateResult(result, { experiment, scene });
|
||||
assert(issues.some(({ message }) => message.includes("rows × columns")));
|
||||
assert(issues.some(({ path }) => path.endsWith("rawMap.scale")));
|
||||
assert(issues.some(({ path }) => path.endsWith("sourceId")));
|
||||
|
||||
Reference in New Issue
Block a user