import { createAnalysisFramePlan, playbackFrameAtTime, sceneHasTrajectories, scenePlaybackState, } from "/apps/acoustic-camera/timeline.js"; import { applyAcousticProfile, detectAcousticProfile, inferShoeboxRoom, maximumSparsePathsPerReceiver, } from "/apps/acoustic-camera/acoustic-profiles.js"; import { alignExperimentToScene, canonicalStringify, createComparisonSignature, createHistorySummary, createResultDocument, estimateExperimentWorkload, hashSimulationInputs, shiftSceneDistance, } from "/apps/acoustic-camera/model.js"; import { validateResult } from "/packages/contracts/validation.js"; import { computeLocalizationMap } from "/packages/acoustics-core/localization.js"; import { sourcesForObservedBand } from "/apps/acoustic-camera/source-selection.js"; function assertNear(actual, expected, tolerance, label) { assert( Math.abs(actual - expected) <= tolerance, `${label}: expected ${expected} ± ${tolerance}, received ${actual}`, ); } function assertThrows(callback, expectedText, label) { let error; try { callback(); } catch (caught) { error = caught; } assert(error instanceof Error, `${label}: expected an error`); assert(error.message.includes(expectedText), `${label}: unexpected message ${error.message}`); } test("Camera detects moving source trajectories", () => { assertEqual(sceneHasTrajectories(null), false, "missing scene"); assertEqual(sceneHasTrajectories({ sources: [{ id: "fixed", position: [0, 0, 2] }] }), false, "fixed scene"); assertEqual(sceneHasTrajectories({ sources: [{ id: "moving", position: [0, 0, 2], trajectory: [ { time: 0, position: [0, 0, 2] }, { time: 1, position: [1, 0, 2] }, ], }], }), true, "moving scene"); }); test("Camera live truth uses synthesized path arrivals instead of emission midpoint", () => { const scene = { sampleRate: 8_000, duration: 1, sources: [ { id: "ended-tone-tail", startTime: 0, duration: 0.1, signal: { type: "tone", frequency: 1_000 }, }, { id: "ended-noise-tail", startTime: 0, duration: 0.1, signal: { type: "noise", color: "white" }, }, { id: "wrong-band-tone", startTime: 0, duration: 0.1, signal: { type: "tone", frequency: 2_000 }, }, ], }; const band = { minHz: 900, maxHz: 1_100 }; const fallback = sourcesForObservedBand(scene, band, 256, { time: 0.5 }); assertEqual(fallback.length, 0, "legacy emission-time fallback sees no active source"); assertEqual( sourcesForObservedBand(scene, band, 256, { time: 0.1 }).length, 0, "emission-time fallback treats the exact end boundary as half-open", ); const arrived = sourcesForObservedBand(scene, band, 256, { time: 0.5, arrivingSourceIds: ["ended-tone-tail", "ended-noise-tail", "wrong-band-tone"], }); assertEqual( arrived.map(({ id }) => id).join(","), "ended-tone-tail,ended-noise-tail", "path-arrival ids override time while band selection remains independent", ); assertEqual( sourcesForObservedBand(scene, band, 256, { arrivingSourceIds: [] }).length, 0, "an explicit empty arrival set does not fall back to emission activity", ); }); test("Camera analysis plan covers a scene and respects its frame budget", () => { const scene = { duration: 2, sampleRate: 1000, sources: [] }; const options = { fftSize: 100, targetMapHz: 4, maxFrames: 3 }; const first = createAnalysisFramePlan(scene, options); const second = createAnalysisFramePlan(scene, options); assertEqual(first.frameCount, 3, "frame count is capped"); assertNear(first.minimumWindowDuration, 0.1, 1e-12, "minimum FFT window"); assertNear(first.windowDuration, 2 / 3, 1e-12, "windows grow to cover capped timeline"); assertEqual(first.frames[0].startTime, 0, "first frame begins at zero"); assertEqual(first.frames.at(-1).endTime, 2, "last frame reaches scene end"); first.frames.forEach((frame, index) => { assertEqual(frame.index, index, `frame ${index} index`); assertNear(frame.sampleTime, (frame.startTime + frame.endTime) / 2, 1e-12, `frame ${index} midpoint`); if (index > 0) { assert(frame.startTime <= first.frames[index - 1].endTime, `frame ${index} leaves no timeline gap`); } }); assertEqual(JSON.stringify(second), JSON.stringify(first), "frame planning is deterministic"); }); test("Camera analysis windows obey FFT and short-scene constraints", () => { const fftLimited = createAnalysisFramePlan( { duration: 1, sampleRate: 1000, sources: [] }, { fftSize: 400, targetMapHz: 10, maxFrames: 100 }, ); assertEqual(fftLimited.frameCount, 10, "requested timeline frames"); assertNear(fftLimited.windowDuration, 0.4, 1e-12, "FFT-sized window"); assertEqual(fftLimited.frames[0].startTime, 0, "overlapping first window"); assertEqual(fftLimited.frames.at(-1).endTime, 1, "overlapping last window"); assert(fftLimited.frames.every((frame) => frame.duration >= 0.4 - 1e-12), "no window is shorter than one FFT"); const shortScene = createAnalysisFramePlan( { duration: 0.05, sampleRate: 1000, sources: [] }, { fftSize: 100, targetMapHz: 100, maxFrames: 50 }, ); assertEqual(shortScene.frameCount, 1, "short scene avoids duplicate frames"); assertEqual(shortScene.windowDuration, 0.05, "window is bounded by scene duration"); assertEqual(shortScene.zeroPaddingRequired, true, "short scene reports FFT padding requirement"); assertNear(shortScene.frames[0].sampleTime, 0.025, 1e-12, "short scene midpoint"); }); test("Camera playback selects and interpolates bounded analysis frames", () => { const plan = createAnalysisFramePlan( { duration: 1, sampleRate: 1000, sources: [] }, { fftSize: 100, targetMapHz: 4, maxFrames: 10 }, ); const middle = playbackFrameAtTime(plan, 0.5); assertEqual(middle.leftFrameIndex, 1, "middle left frame"); assertEqual(middle.rightFrameIndex, 2, "middle right frame"); assertEqual(middle.frameIndex, 2, "nearest frame resolves midpoint ties forward"); assertNear(middle.progress, 0.5, 1e-12, "middle interpolation progress"); assertNear(middle.framePosition, 1.5, 1e-12, "fractional frame position"); const before = playbackFrameAtTime(plan, -10); assertEqual(before.time, 0, "negative playback time is clamped"); assertEqual(before.frameIndex, 0, "negative playback selects first frame"); const after = playbackFrameAtTime(plan, 10); assertEqual(after.time, 1, "late playback time is clamped"); assertEqual(after.frameIndex, 3, "late playback selects last frame"); assertEqual(after.sceneProgress, 1, "late playback progress"); }); test("Camera playback resolves moving positions without mutating the scene", () => { const movingSource = { id: "moving", position: [99, 99, 99], trajectory: [ { time: 0, position: [0, 0, 2] }, { time: 1, position: [4, 2, 2] }, ], }; const fixedSource = { id: "fixed", position: [-1, 1, 3] }; const scene = { duration: 1, sampleRate: 1000, sources: [movingSource, fixedSource] }; const plan = createAnalysisFramePlan(scene, { fftSize: 100, targetMapHz: 4 }); const state = scenePlaybackState(scene, plan, 0.25); assertNear(state.scene.sources[0].position[0], 1, 1e-12, "moving x"); assertNear(state.scene.sources[0].position[1], 0.5, 1e-12, "moving y"); assertEqual(state.scene.sources[1].position.join(","), "-1,1,3", "fixed source"); assertEqual(state.sourcePositions[0].sourceId, "moving", "position metadata source id"); assertEqual(state.sourcePositions[0].position.join(","), "1,0.5,2", "position metadata value"); assertEqual(movingSource.position.join(","), "99,99,99", "input source remains unchanged"); assert(state.scene !== scene && state.scene.sources[0] !== movingSource, "sampled scene is immutable"); }); test("Camera timeline rejects non-finite and invalid planning inputs", () => { const scene = { duration: 1, sampleRate: 1000, sources: [] }; assertThrows( () => createAnalysisFramePlan({ ...scene, duration: Number.NaN }, { fftSize: 100 }), "scene.duration", "NaN duration", ); assertThrows( () => createAnalysisFramePlan(scene, { fftSize: Number.POSITIVE_INFINITY }), "fftSize", "infinite FFT size", ); assertThrows( () => createAnalysisFramePlan(scene, { fftSize: 100, targetMapHz: 0 }), "targetMapHz", "zero map rate", ); assertThrows( () => createAnalysisFramePlan(scene, { fftSize: 100, maxFrames: 1.5 }), "maxFrames", "fractional frame limit", ); assertThrows( () => createAnalysisFramePlan(scene, { fftSize: 100, windowDuration: Number.NaN }), "windowDuration", "NaN analysis window", ); const plan = createAnalysisFramePlan(scene, { fftSize: 100 }); assertThrows(() => playbackFrameAtTime(plan, Number.NaN), "time", "NaN playback time"); assertThrows( () => scenePlaybackState({ ...scene, duration: 2 }, plan, 0.5), "scene.duration", "scene and plan mismatch", ); }); test("Camera input hash is canonical across object key order", async () => { const first = { arrayDefinition: { id: "array", microphones: [ { id: "mic-1", position: [0, 0, 0] }, { id: "mic-2", position: [0.1, 0, 0] }, ], }, scene: { id: "scene", sampleRate: 8000, sources: [{ id: "source", position: [0, 0, 2], signal: { type: "tone", frequency: 1000 } }], }, experiment: { id: "experiment", scan: { z: 2, columns: 8, rows: 8 }, frequencyBands: [{ id: "band", minHz: 900, maxHz: 1100 }], }, }; const reordered = { experiment: { frequencyBands: [{ maxHz: 1100, minHz: 900, id: "band" }], scan: { rows: 8, columns: 8, z: 2 }, id: "experiment", }, scene: { sources: [{ signal: { frequency: 1000, type: "tone" }, position: [0, 0, 2], id: "source" }], sampleRate: 8000, id: "scene", }, arrayDefinition: { microphones: [ { position: [0, 0, 0], id: "mic-1" }, { position: [0.1, 0, 0], id: "mic-2" }, ], id: "array", }, }; assertEqual(canonicalStringify({ z: undefined, b: 2, a: { d: 4, c: 3 } }), '{"a":{"c":3,"d":4},"b":2}', "canonical JSON"); const firstHash = await hashSimulationInputs(first); const reorderedHash = await hashSimulationInputs(reordered); assertEqual(firstHash, reorderedHash, "key ordering does not alter hash"); assert(/^[a-f0-9]{64}$/.test(firstHash), "hash is lowercase SHA-256"); reordered.scene.sources[0].position[0] = 0.01; const changedHash = await hashSimulationInputs(reordered); assert(changedHash !== firstHash, "semantic input change alters hash"); }); test("Camera workload counts unique overlapping FFT bins", () => { const arrayDefinition = { microphones: Array.from({ length: 3 }, (_, index) => ({ id: `mic-${index}`, position: [index, 0, 0] })), }; const scene = { sampleRate: 8000, duration: 1 }; const experiment = { scan: { columns: 10, rows: 8 }, stft: { fftSize: 256, hopSize: 128 }, frequencyBands: [ { id: "lower", minHz: 100, maxHz: 200 }, { id: "upper", minHz: 187.5, maxHz: 250 }, ], }; const workload = estimateExperimentWorkload(arrayDefinition, scene, experiment); assertEqual(workload.pointCount, 80, "scan point count"); assertEqual(workload.microphoneCount, 3, "microphone count"); assertEqual(workload.frameCount, 62, "STFT frame count"); assertEqual(JSON.stringify(workload.bandBins), JSON.stringify([[4, 5, 6], [6, 7, 8]]), "bins per band"); assertEqual(workload.selectedBinCount, 5, "overlapping bin is counted once"); assertEqual(workload.stftTransformCount, 186, "STFT transform count"); assertEqual(workload.scanOperationCount, 74_400, "DAS scan operation count"); assertEqual(workload.scanOperationKind, "complex MAC", "DAS operation kind"); assertEqual(workload.complexMacCount, 74_400, "estimated complex MAC count"); assertNear(workload.scalingVs50, 80 / 2500, 1e-15, "50x50 scaling"); assertNear(workload.distanceMiB, (80 * 3 * 4) / 1024 ** 2, 1e-15, "distance table size"); const gccExperiment = structuredClone(experiment); gccExperiment.beamforming = { algorithm: "gcc-phat" }; const gccWorkload = estimateExperimentWorkload(arrayDefinition, scene, gccExperiment); assertEqual(gccWorkload.algorithmLabel, "GCC-PHAT", "GCC algorithm label"); assertEqual(gccWorkload.referencePairCount, 2, "GCC reference pairs"); assertEqual(gccWorkload.bandBinSelectionCount, 6, "GCC counts overlapping band bins per band"); assertEqual(gccWorkload.gccCrossSpectrumComplexOps, 1_488, "GCC two-pass cross-spectrum ops"); assertEqual(gccWorkload.inverseFftTransformCount, 4, "GCC inverse FFT count"); assertEqual(gccWorkload.minimumBandBinCount, 3, "GCC narrowest band bin count"); assertEqual(gccWorkload.scanOperationCount, 320, "GCC pair-band scan lookups"); assertEqual(gccWorkload.scanOperationKind, "reference-pair lookup", "GCC operation kind"); assertEqual(gccWorkload.complexMacCount, 0, "GCC is not mislabeled as DAS complex MAC"); const musicExperiment = structuredClone(experiment); musicExperiment.frequencyBands = [ { id: "single-bin", minHz: 120, maxHz: 130 }, ]; musicExperiment.beamforming = { algorithm: "music", musicSourceCountMethod: "fixed", musicSourceCount: 1, }; const musicWorkload = estimateExperimentWorkload( arrayDefinition, scene, musicExperiment, ); assertEqual(musicWorkload.algorithmLabel, "MUSIC", "MUSIC algorithm label"); assertEqual( musicWorkload.musicCovarianceComplexAccumulations, 372, "MUSIC covariance accumulation count", ); assertEqual( musicWorkload.musicJacobiWorstCasePairUpdates, 384, "MUSIC 32-sweep Jacobi upper bound", ); assertEqual( musicWorkload.musicProjectionComplexMacs, 480, "MUSIC fixed-K projection count", ); assertEqual(musicWorkload.scanOperationCount, 1236, "MUSIC disclosed total work"); assertEqual( musicWorkload.scanOperationKind, "covariance + Jacobi upper bound + projection", "MUSIC work is not mislabeled as DAS", ); assertNear( musicWorkload.distanceMiB, (80 * 3 * 8) / 1024 ** 2, 1e-15, "MUSIC Float64 distance table size", ); }); test("Camera comparison signature groups array, algorithm, and backend variants only", () => { const scene = { id: "scene", seed: 7, sources: [] }; const baseline = { id: "experiment", arrayId: "array-a", scan: { rows: 8, columns: 8 }, beamforming: { algorithm: "das", backend: "cpu", normalization: "peak" }, }; const variant = structuredClone(baseline); variant.arrayId = "array-b"; variant.beamforming.algorithm = "das-phat"; variant.beamforming.backend = "webgpu"; assertEqual( createComparisonSignature(scene, variant), createComparisonSignature(scene, baseline), "comparison axes share one history group", ); variant.scan.columns = 9; assert( createComparisonSignature(scene, variant) !== createComparisonSignature(scene, baseline), "scan changes remain separate", ); }); test("Camera acoustic presets bound current geometry and expose predictable sparse cost", () => { const scene = { sources: [{ id: "moving", position: [0, 0, 2], trajectory: [ { time: 0, position: [-0.5, 0, 2] }, { time: 1, position: [0.75, 0.5, 3] }, ], }], }; const arrayDefinition = { microphones: [ { id: "left", position: [-0.2, 0, 0] }, { id: "right", position: [0.2, 0, 0] }, ], }; const experiment = { scan: { xMin: -1, xMax: 1, yMin: -0.75, yMax: 0.75, z: 2.5 }, }; const room = inferShoeboxRoom(scene, arrayDefinition, experiment); assert(room.min[0] < -1 && room.max[0] > 1, "scan x is padded"); assert(room.min[2] < 0 && room.max[2] > 3, "array and trajectory depth are padded"); applyAcousticProfile(scene, arrayDefinition, experiment, "typical-room"); assertEqual(detectAcousticProfile(scene), "typical-room", "profile round trip"); assertEqual(maximumSparsePathsPerReceiver(scene), 19, "bounded typical-room path count"); assertEqual(scene.acoustics.room.min.join(","), room.min.join(","), "inferred room minimum"); assertEqual(scene.acoustics.room.max.join(","), room.max.join(","), "inferred room maximum"); applyAcousticProfile(scene, arrayDefinition, experiment, "free-field"); assertEqual(detectAcousticProfile(scene), "free-field", "free-field selection"); assertEqual(maximumSparsePathsPerReceiver(scene), 1, "free-field path count"); }); test("Camera distance shift keeps the scene and scan on one z plane", () => { const scene = { sources: [ { id: "one", position: [0.1, -0.2, 2], trajectory: [ { time: 0, position: [0.1, -0.2, 2] }, { time: 1, position: [0.6, 0.3, 2.5] }, ], }, { id: "two", position: [-0.3, 0.4, 2.5] }, ], image: { plane: { z: 2, xMin: -1, xMax: 1, yMin: -1, yMax: 1 } }, acoustics: { model: "shoebox", room: { min: [-2, -2, -0.5], max: [2, 2, 4] }, occluders: [{ id: "screen", min: [-0.2, -0.2, 1], max: [0.2, 0.2, 1.1], transmissionDb: -20, diffractionGainDb: -12, }], }, }; const experiment = { scan: { z: 2 } }; shiftSceneDistance(scene, experiment, 3.25); assertEqual(experiment.scan.z, 3.25, "scan z"); assertEqual(scene.sources[0].position.join(","), "0.1,-0.2,3.25", "first source z shift"); assertEqual( scene.sources[0].trajectory[0].position.join(","), "0.1,-0.2,3.25", "first trajectory keyframe z shift", ); assertEqual( scene.sources[0].trajectory[1].position.join(","), "0.6,0.3,3.75", "later trajectory keyframe retains relative depth", ); assertEqual(scene.sources[1].position.join(","), "-0.3,0.4,3.75", "relative source depth retained"); assertEqual(scene.image.plane.z, 3.25, "background image plane z shift"); assertEqual(scene.acoustics.room.min[2], 0.75, "room minimum z shift"); assertEqual(scene.acoustics.room.max[2], 5.25, "room maximum z shift"); assertEqual(scene.acoustics.occluders[0].min[2], 2.25, "occluder minimum z shift"); assertEqual(scene.acoustics.occluders[0].max[2], 2.35, "occluder maximum z shift"); }); test("Camera aligns an experiment to an imported scene without changing grid resolution", () => { const experiment = { scan: { z: 2, xMin: -1, xMax: 1, yMin: -1, yMax: 1, columns: 32, rows: 24 }, frequencyBands: [ { id: "low", minHz: 600, maxHz: 900, color: "#65f2bf" }, { id: "high", minHz: 1100, maxHz: 1500, color: "#58c7ff" }, ], }; const originalBands = structuredClone(experiment.frequencyBands); const imageScene = { sources: [{ id: "one", position: [0.2, -0.1, 4.5] }], image: { plane: { z: 4.5, xMin: -2, xMax: 3, yMin: -1.5, yMax: 2.5 } }, }; assertEqual(alignExperimentToScene(experiment, imageScene), "image-plane", "alignment source"); assertEqual( JSON.stringify(experiment.scan), JSON.stringify({ z: 4.5, xMin: -2, xMax: 3, yMin: -1.5, yMax: 2.5, columns: 32, rows: 24, }), "image plane becomes scan plane while rows and columns stay unchanged", ); const sourceScene = { sources: [ { id: "one", position: [-0.5, 0.1, 3] }, { id: "two", position: [0.5, 0.2, 5] }, ], }; assertEqual(alignExperimentToScene(experiment, sourceScene), "source-bounds", "source fallback"); assertEqual(experiment.scan.z, 4, "median source depth"); assertNear(experiment.scan.xMin, -0.75, 1e-12, "source x minimum with padding"); assertNear(experiment.scan.xMax, 0.75, 1e-12, "source x maximum with padding"); assertNear(experiment.scan.yMin, -0.15, 1e-12, "source y minimum with padding"); assertNear(experiment.scan.yMax, 0.45, 1e-12, "source y maximum with padding"); assertEqual(experiment.scan.columns, 32, "columns retained after source fallback"); assertEqual(experiment.scan.rows, 24, "rows retained after source fallback"); const movingScene = { sources: [ { id: "moving", position: [-2, -3, 1], trajectory: [ { time: 0, position: [-2, -3, 1] }, { time: 1, position: [4, 5, 9] }, ], }, { id: "fixed", position: [0, 1, 5] }, ], }; assertEqual(alignExperimentToScene(experiment, movingScene), "source-bounds", "trajectory fallback"); assertEqual(experiment.scan.z, 5, "trajectory depths contribute to median scan depth"); assertEqual( JSON.stringify(experiment.frequencyBands), JSON.stringify(originalBands), "scene alignment preserves every analysis band", ); assertNear(experiment.scan.xMin, -2.9, 1e-12, "trajectory x minimum with padding"); assertNear(experiment.scan.xMax, 4.9, 1e-12, "trajectory x maximum with padding"); assertNear(experiment.scan.yMin, -4.2, 1e-12, "trajectory y minimum with padding"); assertNear(experiment.scan.yMax, 6.2, 1e-12, "trajectory y maximum with padding"); }); test("Camera result document is schema-safe and linked to its snapshot", async () => { const arrayDefinition = { id: "test-array", name: "Test array", microphones: [ { id: "mic-1", position: [0, 0, 0] }, { id: "mic-2", position: [0.1, 0, 0] }, ], }; const scene = { id: "test-scene", sampleRate: 8_000, sources: [{ id: "source-1", position: [0, 0, 2], signal: { type: "tone", frequency: 5_000, phaseDeg: 0 }, }], }; const experiment = { id: "test-experiment", arrayId: "declared-array-provenance", sceneId: "declared-scene-provenance", scan: { rows: 2, columns: 2, xMin: -1, xMax: 1, yMin: -1, yMax: 1, z: 2 }, beamforming: { algorithm: "das-phat", backend: "auto" }, stft: { fftSize: 256, hopSize: 128, window: "hann" }, frequencyBands: [{ id: "band-1", minHz: 4_900, maxHz: 5_100, color: "#65f2bf", }, { id: "band-2", minHz: 2_900, maxHz: 3_100, color: "#58c7ff", }], }; const run = { algorithm: "das-phat", backend: "cpu", referenceMic: "mic-1", signalStats: { preNormalizationPeak: 0.75, appliedScale: 1, clippedSampleCount: 0, acousticModel: "shoebox", acousticPathCount: 38, acousticPathCounts: { direct: 2, reflection: 12, scattering: 12, diffuse: 12 }, sampling: { model: "ideal-real-sampler-no-antialias-filter", sampleRate: 8_000, nyquistHz: 4_000, toneObservations: [{ scope: "source", id: "source-1", analogFrequencyHz: 5_000, adcFrequencyHz: 3_000, signedAdcFrequencyHz: -3_000, aliased: true, spectralInversion: true, dcEdge: false, nyquistEdge: false, phaseDegenerate: false, }], }, }, timingMs: { total: 12.5, signalGeneration: 2.5, beamforming: 10 }, fallback: { code: "webgpu-unavailable", message: "CPU fallback" }, bands: [{ id: "band-1", color: "#65f2bf", rawScale: "score", rawMap: Float32Array.of(0.1, 0.2, 0.3, 1), displayMap: Float32Array.of(0.1, 0.2, 0.3, 1), displayScale: "normalized", }, { id: "band-2", color: "#58c7ff", rawScale: "score", rawMap: Float32Array.of(0.1, 0.2, 0.3, 1), displayMap: Float32Array.of(0.1, 0.2, 0.3, 1), displayScale: "normalized", }], }; const evaluations = new Map([["band-1", { peaks: [{ index: 3, sourceId: "source-1", position: [1, -1, 2], value: 1, localizationErrorM: 0.05, localizationErrorPixels: 0.1, beamSpread: { calibrated: false, method: "connected-half-maximum-score-weighted-covariance", halfPowerRatio: 0.5, thresholdValue: 0.5, pixelCount: 3, centroid: [0.9, -0.9, 2], covarianceM2: { xx: 0.04, xy: 0.01, yy: 0.02 }, ellipse: { axisScale: "rms", angleReference: "counterclockwise-from-positive-x", majorRmsRadiusM: 0.21, minorRmsRadiusM: 0.12, angleDeg: 22.5, }, }, }], metrics: { peakToSidelobeDb: 10, energyInsideTruthRatio: 0.75, precision: 1, recall: 1, mainLobeWidthM: 0.2, ignoredNonFinite: Number.NaN, }, evaluation: { version: "1.0.0", truthRadiusM: 0.1, matchToleranceM: 0.2, detectionThresholdRatio: 0.5, detectionThresholdValue: 0.25, minimumPeakDistanceM: 0.1, sidelobeExclusionRadiusM: 0.1, halfPowerRatio: 0.5, dynamicRangeDb: 30, maximumDetections: 128, }, }]]); evaluations.set("band-2", evaluations.get("band-1")); const result = createResultDocument({ arrayDefinition, scene, experiment, run, evaluations, inputHash: "a".repeat(64), createdAt: "2026-08-28T00:00:00.000Z", }); assertEqual(result.schemaVersion, "1.1.0", "sampling-aware Result contract version"); assertEqual(result.arrayId, arrayDefinition.id, "Result records the Array actually used"); assertEqual(result.sceneId, scene.id, "Result records the Scene actually used"); assertEqual( experiment.arrayId, "declared-array-provenance", "Result creation preserves the Experiment Array provenance label", ); assertEqual( experiment.sceneId, "declared-scene-provenance", "Result creation preserves the Experiment Scene provenance label", ); assert(Array.isArray(result.bands[0].rawMap.values), "typed raw map becomes JSON array"); assertEqual(result.algorithm, "das-phat", "actual algorithm is exported"); assertEqual(result.timingMs.localization, 10, "generic localization timing is exported"); assertEqual(result.bands[0].rawMap.scale, "score", "algorithm raw score scale is exported"); assertEqual(result.environment.referenceMic, "mic-1", "reference microphone metadata is exported"); assert(!("index" in result.bands[0].peaks[0]), "internal peak index is omitted"); assertEqual( Object.keys(result.bands[0].metrics).join(","), "peakToSidelobeDb,energyInsideTruthRatio,precision,recall,mainLobeWidthM", "only finite schema metrics are exported", ); assertEqual(result.bands[0].peaks[0].localizationErrorPixels, 0.1, "pixel error is exported"); assertEqual(result.bands[0].peaks[0].beamSpread.pixelCount, 3, "beam spread is exported"); assertEqual(result.bands[0].peaks[0].beamSpread.calibrated, false, "beam spread is not probability"); assertEqual(result.bands[0].evaluation.matchToleranceM, 0.2, "evaluation settings are exported"); assertEqual(result.environment.scanXMin, -1, "scan bounds are recorded"); assertEqual(result.environment.fallbackCode, "webgpu-unavailable", "fallback is recorded"); assertEqual(result.signalStats.acousticModel, "shoebox", "acoustic model is exported"); assertEqual(result.signalStats.acousticPathCount, 38, "acoustic path count is exported"); assertEqual(result.signalStats.acousticPathCounts.diffuse, 12, "path kinds are exported"); assertEqual( result.signalStats.sampling.toneObservations[0].adcFrequencyHz, 3_000, "ADC alias is exported", ); assertEqual( result.bands[0].frequencyObservation.adcBins.join(","), "93,94,95,96,97,98,99", "requested analog band exports every folded ADC bin", ); assertEqual( result.bands[0].frequencyObservation.equivalentBandIds.join(","), "band-2", "alias-equivalent band is disclosed", ); assertEqual( result.bands[1].frequencyObservation.equivalentBandIds.join(","), "band-1", "equivalence is symmetric", ); assertEqual( result.bands[0].frequencyObservation.localizationModel, "spherical-steering-at-positive-adc-bin-frequency", "actual DAS-family localization model is explicit", ); assertEqual( result.bands[0].frequencyObservation.originalCarrierUsed, false, "result does not imply hidden original-carrier steering", ); assertEqual( result.bands[0].evaluation.truthSelection, "tone-analog-interval-or-nearest-adc-bin-and-all-nontone-sources", "truth-selection policy is explicit", ); const history = createHistorySummary({ arrayDefinition, scene, experiment, run, evaluations, inputHash: "a".repeat(64), }); assertEqual(history.algorithm, "das-phat", "history records the actual algorithm"); assertEqual(history.localizationMs, 10, "history records localization time"); const issues = await validateResult(result, { experiment, scene, arrayDefinition, }); assertEqual(issues.length, 0, `generated result validation: ${JSON.stringify(issues)}`); assertEqual( (await validateResult(result)).length, 0, "generated Result is self-consistent without external context", ); const standaloneCorruption = structuredClone(result); standaloneCorruption.signalStats.sampling.nyquistHz += 1; standaloneCorruption.signalStats.sampling.toneObservations.push( structuredClone(standaloneCorruption.signalStats.sampling.toneObservations[0]), ); standaloneCorruption.bands[0].frequencyObservation.adcBins.push( standaloneCorruption.bands[0].frequencyObservation.adcBins[0], ); standaloneCorruption.bands[0].frequencyObservation.equivalentBandIds.push("band-2"); standaloneCorruption.bands[0].frequencyObservation.localizationModel = "band-limited-gcc-phat-geometric-lag"; const standaloneIssues = await validateResult(standaloneCorruption); assert( standaloneIssues.some(({ path }) => path === "$.signalStats.sampling.nyquistHz"), "standalone validator checks Nyquist self-consistency", ); assert( standaloneIssues.some(({ message }) => message.includes("duplicate tone observation")), "standalone validator rejects duplicate tone provenance", ); assert( standaloneIssues.some(({ path }) => path.endsWith("frequencyObservation.adcBins")), "standalone validator recomputes unique ADC bins", ); assert( standaloneIssues.some(({ path }) => path.endsWith("frequencyObservation.equivalentBandIds")), "standalone validator recomputes equivalent band IDs", ); assert( standaloneIssues.some(({ path }) => path.endsWith("frequencyObservation.localizationModel")), "standalone validator links localization model to actual algorithm", ); result.signalStats.sampling.toneObservations[0].signedAdcFrequencyHz += 1; let semanticIssues = await validateResult(result, { experiment, scene }); assert( semanticIssues.some(({ path }) => path.endsWith("signedAdcFrequencyHz")), "validator recomputes the temporal alias", ); result.signalStats.sampling.toneObservations[0].signedAdcFrequencyHz -= 1; result.bands[0].frequencyObservation.adcBins[0] += 1; semanticIssues = await validateResult(result, { experiment, scene }); assert( semanticIssues.some(({ path }) => path.endsWith("frequencyObservation.adcBins")), "validator recomputes folded ADC bins", ); result.bands[0].frequencyObservation.adcBins[0] -= 1; const missingBandResult = structuredClone(result); missingBandResult.bands.pop(); semanticIssues = await validateResult(missingBandResult, { experiment, scene }); assert( semanticIssues.some(({ message }) => message.includes("missing Experiment band result")), "validator rejects incomplete Result band sets", ); const wrongArrayResult = structuredClone(result); wrongArrayResult.arrayId = "another-array"; let linkageIssues = await validateResult(wrongArrayResult, { experiment, scene, arrayDefinition, }); assert( linkageIssues.some(({ path }) => path === "$.arrayId"), "validator catches an actual Array snapshot linkage mismatch", ); const wrongSceneResult = structuredClone(result); wrongSceneResult.sceneId = "another-scene"; linkageIssues = await validateResult(wrongSceneResult, { experiment, scene, arrayDefinition, }); assert( linkageIssues.some(({ path }) => path === "$.sceneId"), "validator catches an actual Scene snapshot linkage mismatch", ); }); test("MUSIC Result preserves its physical model and numerical audit", async () => { const sampleRate = 8192; const fftSize = 128; const sampleCount = 256; const arrayDefinition = { id: "music-result-array", microphones: [ { id: "m0", position: [-0.1, 0.1, 0] }, { id: "m1", position: [0.1, 0.1, 0] }, { id: "m2", position: [-0.1, -0.1, 0] }, { id: "m3", position: [0.1, -0.1, 0] }, ], }; const channels = arrayDefinition.microphones.map((_, microphoneIndex) => Float64Array.from( { length: sampleCount }, (_, sampleIndex) => Math.cos( 2 * Math.PI * 16 * sampleIndex / fftSize + microphoneIndex * 0.21, ), )); const scene = { id: "music-result-scene", sampleRate, duration: sampleCount / sampleRate, speedOfSound: 343, sources: [], }; const experiment = { id: "music-result-experiment", arrayId: arrayDefinition.id, sceneId: scene.id, scan: { rows: 2, columns: 2, xMin: -0.2, xMax: 0.2, yMin: -0.2, yMax: 0.2, z: 1, }, frequencyBands: [ { id: "single-bin", minHz: 1000, maxHz: 1050, color: "#65f2bf" }, ], stft: { fftSize, hopSize: 64, window: "hann" }, beamforming: { algorithm: "music", backend: "cpu", minimumDistance: 0.01, normalization: "none", compensateFrequencyResponse: true, musicSourceCountMethod: "fixed", musicSourceCount: 1, }, }; const localization = await computeLocalizationMap({ channels, sampleRate, arrayDefinition, scene, experiment, }); const run = { ...localization, signalStats: { preNormalizationPeak: 1, appliedScale: 1, clippedSampleCount: 0, sampling: { model: "ideal-real-sampler-no-antialias-filter", sampleRate, nyquistHz: sampleRate / 2, toneObservations: [], }, }, timingMs: { total: 1, signalGeneration: 0, localization: 1, beamforming: 1, }, }; const result = createResultDocument({ arrayDefinition, scene, experiment, run, evaluations: new Map([[ "single-bin", { peaks: [], metrics: { precision: 1, recall: 1 } }, ]]), inputHash: "b".repeat(64), createdAt: "2026-08-29T00:00:00.000Z", }); assertEqual(result.bands[0].rawMap.scale, "noise-projection", "raw q scale"); assertEqual(result.bands[0].rawMap.objectiveDirection, "minimum", "q is minimized"); assertEqual(result.musicModel.originalCarrierUsed, true, "configured carrier is used"); assertEqual( result.musicModel.hypothesisSource, "experiment-band-not-scene-truth", "carrier comes from Experiment rather than Scene truth", ); assertEqual(result.musicModel.diagonalLoading, "none", "no hidden diagonal loading"); assertEqual(result.musicModel.multibandFusion, "none-each-band-is-separate-single-analog-hypothesis", "no band fusion"); assertEqual(result.bands[0].musicDiagnostics.eigenvalues.length, 4, "all eigenvalues exported"); assert(result.bands[0].musicDiagnostics.eigensolver.numericallyVerified, "EVD audit exported"); assertEqual(result.bands[0].musicDiagnostics.sourceCount.value, 1, "fixed K exported"); const resultIssues = await validateResult(result, { experiment, scene, arrayDefinition, }); assertEqual( resultIssues.length, 0, `MUSIC Result passes schema and semantic validation: ${JSON.stringify(resultIssues)}`, ); const corrupted = structuredClone(result); corrupted.bands[0].musicDiagnostics.sourceCount.value = 4; const issues = await validateResult(corrupted, { experiment, scene, arrayDefinition, }); assert( issues.some(({ path }) => path.endsWith("musicDiagnostics.sourceCount.value")), "source count cannot equal microphone count", ); const corruptedHypothesis = structuredClone(result); corruptedHypothesis.bands[0].musicDiagnostics.frequencyHypothesis .analogFrequencyHz += sampleRate; const hypothesisIssues = await validateResult(corruptedHypothesis, { experiment, scene, arrayDefinition, }); assert( hypothesisIssues.some(({ path }) => path.endsWith( "musicDiagnostics.frequencyHypothesis.analogFrequencyHz", )), "numerical diagnostics cannot claim a different analog carrier", ); });