feat: add physically explicit narrowband MUSIC
This commit is contained in:
@@ -165,6 +165,98 @@ test("portable raw-map peak detection이 같은 band의 두 진원 후보를 강
|
||||
);
|
||||
});
|
||||
|
||||
test("portable peak detection treats MUSIC noise projection as a minimum", () => {
|
||||
const scan = {
|
||||
z: 1,
|
||||
xMin: 0,
|
||||
xMax: 4,
|
||||
yMin: 0,
|
||||
yMax: 4,
|
||||
columns: 5,
|
||||
rows: 5,
|
||||
};
|
||||
const q = new Float64Array(25);
|
||||
q.fill(1);
|
||||
q[6] = 0.1;
|
||||
q[18] = 0.2;
|
||||
q[3] = 0.6;
|
||||
|
||||
const peaks = detectBandPeaks(q, scan, {
|
||||
rawScale: "noise-projection",
|
||||
objectiveDirection: "minimum",
|
||||
});
|
||||
assertEqual(peaks.length, 2, "two q minima exceed the merit threshold");
|
||||
assertEqual(peaks[0].index, 6, "smallest q ranks first");
|
||||
assert(
|
||||
Math.abs(peaks[0].noiseProjection - 0.1) <= 1e-12,
|
||||
"raw q is retained",
|
||||
);
|
||||
assertEqual(peaks[0].objectiveDirection, "minimum", "minimum objective is explicit");
|
||||
assertEqual(
|
||||
detectBandPeaks(
|
||||
new Float64Array(25).fill(0.5),
|
||||
scan,
|
||||
{
|
||||
rawScale: "noise-projection",
|
||||
objectiveDirection: "minimum",
|
||||
},
|
||||
).length,
|
||||
0,
|
||||
"flat q does not choose the first grid cell",
|
||||
);
|
||||
});
|
||||
|
||||
test("portable and streaming MUSIC consume unavailable auto-K windows without stalling", async () => {
|
||||
const fixture = createFixture(256);
|
||||
const musicConfig = {
|
||||
...fixture.config,
|
||||
algorithm: "music",
|
||||
normalization: "none",
|
||||
stft: { fftSize: 64, hopSize: 64 },
|
||||
musicSourceCountMethod: "mdl",
|
||||
musicSourceCount: 1,
|
||||
};
|
||||
const plan = createLocalizationPlan(musicConfig);
|
||||
const block = await localizeMultichannel({
|
||||
plan,
|
||||
channels: sliceChannels(fixture.channels, 0, 128),
|
||||
});
|
||||
assertEqual(block.status, "unavailable", "short auto-K block is typed unavailable");
|
||||
assertEqual(
|
||||
block.unavailable.reason,
|
||||
"insufficient-snapshots-for-source-count-selection",
|
||||
"unavailable reason is explicit",
|
||||
);
|
||||
assertEqual(block.frameCount, 2, "unavailable result preserves analyzed frame count");
|
||||
assertEqual(block.bands.length, 0, "no arbitrary MUSIC map");
|
||||
assertEqual(block.locations.length, 0, "no arbitrary location");
|
||||
|
||||
const stream = createStreamingLocalizer({
|
||||
...musicConfig,
|
||||
analysisWindowSamples: 128,
|
||||
analysisHopSamples: 128,
|
||||
maximumBufferedSamples: 256,
|
||||
maximumResultsPerCall: 2,
|
||||
});
|
||||
stream.append(fixture.channels);
|
||||
const results = await stream.analyzeAvailable({
|
||||
maximumResults: 2,
|
||||
});
|
||||
assertEqual(results.length, 2, "both unavailable windows are emitted");
|
||||
assert(
|
||||
results.every(
|
||||
(result) =>
|
||||
result.status === "unavailable" &&
|
||||
result.locations.length === 0,
|
||||
),
|
||||
"each window remains explicitly location-free",
|
||||
);
|
||||
const streamState = stream.getState();
|
||||
assertEqual(streamState.completedAnalyses, 2, "unavailable windows commit");
|
||||
assertEqual(streamState.failedAnalyses, 0, "unavailable is not a runtime failure");
|
||||
assertEqual(streamState.bufferedSamples, 0, "stream advances past both windows");
|
||||
});
|
||||
|
||||
test("CPU 분석 계획이 반복 geometry·steering 연산과 bin map 할당을 실제로 제거한다", async () => {
|
||||
const fixture = createFixture(128);
|
||||
const plan = createLocalizationPlan(fixture.config);
|
||||
|
||||
Reference in New Issue
Block a user