feat: tune multi-source peak sensitivity
This commit is contained in:
@@ -43,6 +43,7 @@ const BAND_COLORS = ["#65f2bf", "#58c7ff", "#ff7f8f", "#ffc56e", "#b294ff", "#8f
|
||||
const MAX_HISTORY_ITEMS = 24;
|
||||
const MAX_LIVE_ANALYSIS_FRAMES = 48;
|
||||
const MAX_LIVE_SCAN_POINTS = 2500;
|
||||
const DEFAULT_DETECTION_THRESHOLD_RATIO = 0.25;
|
||||
|
||||
const byId = (id) => document.getElementById(id);
|
||||
const elements = {
|
||||
@@ -102,6 +103,7 @@ const elements = {
|
||||
hopSize: byId("hop-size"),
|
||||
normalization: byId("normalization"),
|
||||
dynamicRange: byId("dynamic-range"),
|
||||
detectionThreshold: byId("detection-threshold"),
|
||||
postprocess: byId("postprocess"),
|
||||
addBandButton: byId("add-band-button"),
|
||||
bandList: byId("band-list"),
|
||||
@@ -168,6 +170,14 @@ function comparisonSignature(scene = state.scene, sourceExperiment = state.exper
|
||||
return canonicalStringify({ scene, experiment });
|
||||
}
|
||||
|
||||
function applyExperimentDefaults(experiment) {
|
||||
if (!experiment.evaluation) experiment.evaluation = {};
|
||||
if (experiment.evaluation.detectionThresholdRatio === undefined) {
|
||||
experiment.evaluation.detectionThresholdRatio = DEFAULT_DETECTION_THRESHOLD_RATIO;
|
||||
}
|
||||
return experiment;
|
||||
}
|
||||
|
||||
function setRunStatus(message) {
|
||||
elements.runStatus.textContent = message;
|
||||
}
|
||||
@@ -359,6 +369,9 @@ function syncControls() {
|
||||
elements.hopSize.value = experiment.stft.hopSize;
|
||||
elements.normalization.value = experiment.beamforming.normalization;
|
||||
elements.dynamicRange.value = experiment.beamforming.dynamicRangeDb;
|
||||
const detectionThresholdRatio =
|
||||
experiment.evaluation?.detectionThresholdRatio ?? DEFAULT_DETECTION_THRESHOLD_RATIO;
|
||||
elements.detectionThreshold.value = String(Number((detectionThresholdRatio * 100).toFixed(4)));
|
||||
elements.arrayPreset.value = ARRAY_PRESET_URLS.has(state.arrayDefinition.id)
|
||||
? state.arrayDefinition.id
|
||||
: "custom";
|
||||
@@ -638,6 +651,14 @@ function renderResult() {
|
||||
`${formatNumber(displayed.run.timingMs.beamforming, 0)} ms`,
|
||||
`${displayed.snapshot.experiment.scan.columns}×${displayed.snapshot.experiment.scan.rows} grid`,
|
||||
);
|
||||
const detectionThreshold =
|
||||
displayed.snapshot.experiment.evaluation?.detectionThresholdRatio ??
|
||||
DEFAULT_DETECTION_THRESHOLD_RATIO;
|
||||
appendMetric(
|
||||
"Peak 검출 임계값",
|
||||
`${formatNumber(detectionThreshold * 100, 0)}%`,
|
||||
"최강 peak power 기준 · 낮을수록 민감",
|
||||
);
|
||||
for (const band of displayed.snapshot.experiment.frequencyBands) {
|
||||
const evaluation = displayed.evaluations.get(band.id);
|
||||
if (!evaluation) continue;
|
||||
@@ -1001,6 +1022,9 @@ async function runSimulation() {
|
||||
scan: snapshot.experiment.scan,
|
||||
sources: sourcesForBand(snapshot.scene, definition),
|
||||
dynamicRangeDb: snapshot.experiment.beamforming.dynamicRangeDb,
|
||||
detectionThresholdRatio:
|
||||
snapshot.experiment.evaluation?.detectionThresholdRatio ??
|
||||
DEFAULT_DETECTION_THRESHOLD_RATIO,
|
||||
});
|
||||
evaluations.set(band.id, evaluation);
|
||||
exportedEvaluations.set(band.id, exportBandEvaluation(evaluation));
|
||||
@@ -1124,6 +1148,9 @@ function evaluateLiveFrames(snapshot, plan, frames) {
|
||||
scan: snapshot.experiment.scan,
|
||||
sources: sourcesForBand(frameScene, definition, frame.sampleTime),
|
||||
dynamicRangeDb: snapshot.experiment.beamforming.dynamicRangeDb,
|
||||
detectionThresholdRatio:
|
||||
snapshot.experiment.evaluation?.detectionThresholdRatio ??
|
||||
DEFAULT_DETECTION_THRESHOLD_RATIO,
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -1312,7 +1339,7 @@ async function loadMovingExample() {
|
||||
if (issues.length > 0) throw new Error(issues.slice(0, 4).map(formatIssue).join(" · "));
|
||||
state.arrayDefinition = arrayDefinition;
|
||||
state.scene = scene;
|
||||
state.experiment = experiment;
|
||||
state.experiment = applyExperimentDefaults(experiment);
|
||||
state.sceneDirty = false;
|
||||
state.experimentDirty = false;
|
||||
state.visibleBandIds = new Set(experiment.frequencyBands.map((band) => band.id));
|
||||
@@ -1403,7 +1430,7 @@ async function importDocument(kind, input) {
|
||||
state.sceneDirty = false;
|
||||
assetRuntime.reset();
|
||||
} else {
|
||||
state.experiment = value;
|
||||
state.experiment = applyExperimentDefaults(value);
|
||||
state.experimentDirty = false;
|
||||
}
|
||||
state.visibleBandIds = new Set(state.experiment.frequencyBands.map((band) => band.id));
|
||||
@@ -1498,6 +1525,13 @@ function installDocumentInputListeners() {
|
||||
state.experiment.beamforming.dynamicRangeDb = value;
|
||||
markChanged();
|
||||
});
|
||||
elements.detectionThreshold.addEventListener("change", () => {
|
||||
const value = readFiniteInput(elements.detectionThreshold);
|
||||
if (value === null) return syncControls();
|
||||
state.experiment.evaluation ??= {};
|
||||
state.experiment.evaluation.detectionThresholdRatio = value / 100;
|
||||
markChanged();
|
||||
});
|
||||
}
|
||||
|
||||
function installBandListeners() {
|
||||
@@ -1697,7 +1731,7 @@ async function initialize() {
|
||||
]);
|
||||
state.arrayDefinition = arrayDefinition;
|
||||
state.scene = scene;
|
||||
state.experiment = experiment;
|
||||
state.experiment = applyExperimentDefaults(experiment);
|
||||
state.visibleBandIds = new Set(experiment.frequencyBands.map((band) => band.id));
|
||||
syncControls();
|
||||
renderBandEditors();
|
||||
|
||||
Reference in New Issue
Block a user