fix: expose physical aliasing and delayed arrivals
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
import { getFrameCount, binsForBand } from "/packages/acoustics-core/stft.js";
|
||||
import { observeFrequencyBand } from "/packages/acoustics-core/sampling.js";
|
||||
import {
|
||||
DEFAULT_LOCALIZATION_ALGORITHM,
|
||||
localizationAlgorithmDefinition,
|
||||
} from "/packages/acoustics-core/localization-algorithms.js";
|
||||
import { RESULT_SCHEMA_VERSION } from "/packages/contracts/validation.js";
|
||||
|
||||
const RESULT_METRIC_KEYS = [
|
||||
"peakToSidelobeDb",
|
||||
@@ -254,6 +256,70 @@ function environmentForResult({ arrayDefinition, experiment, run }) {
|
||||
return environment;
|
||||
}
|
||||
|
||||
function samplingForResult(sampling) {
|
||||
if (!sampling || typeof sampling !== "object") return null;
|
||||
return {
|
||||
model: sampling.model,
|
||||
sampleRate: sampling.sampleRate,
|
||||
nyquistHz: sampling.nyquistHz,
|
||||
toneObservations: (sampling.toneObservations ?? []).map((observation) => ({
|
||||
scope: observation.scope,
|
||||
id: observation.id,
|
||||
analogFrequencyHz: observation.analogFrequencyHz,
|
||||
adcFrequencyHz: observation.adcFrequencyHz,
|
||||
signedAdcFrequencyHz: observation.signedAdcFrequencyHz,
|
||||
aliased: observation.aliased,
|
||||
spectralInversion: observation.spectralInversion,
|
||||
dcEdge: observation.dcEdge,
|
||||
nyquistEdge: observation.nyquistEdge,
|
||||
phaseDegenerate: observation.phaseDegenerate,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
function frequencyObservationForResult(scene, experiment, band, algorithm) {
|
||||
if (
|
||||
!Number.isFinite(scene?.sampleRate) ||
|
||||
!Number.isInteger(experiment?.stft?.fftSize) ||
|
||||
!Number.isFinite(band?.minHz) ||
|
||||
!Number.isFinite(band?.maxHz)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
const observation = observeFrequencyBand(
|
||||
band,
|
||||
scene.sampleRate,
|
||||
experiment.stft.fftSize,
|
||||
);
|
||||
return {
|
||||
model: observation.model,
|
||||
sampleRate: observation.sampleRate,
|
||||
nyquistHz: observation.nyquistHz,
|
||||
fftSize: observation.fftSize,
|
||||
binWidthHz: observation.binWidthHz,
|
||||
requestedMinHz: observation.requestedMinHz,
|
||||
requestedMaxHz: observation.requestedMaxHz,
|
||||
aliased: observation.aliased,
|
||||
crossesFirstNyquist: observation.crossesFirstNyquist,
|
||||
localizationInputModel: observation.localizationInputModel,
|
||||
selectionModel: observation.selectionModel,
|
||||
localizationModel:
|
||||
algorithm === "gcc-phat"
|
||||
? "band-limited-gcc-phat-geometric-lag"
|
||||
: "spherical-steering-at-positive-adc-bin-frequency",
|
||||
originalCarrierUsed: observation.originalCarrierUsed,
|
||||
aliasOrderResolution: observation.aliased ? "not-attempted" : "not-applicable",
|
||||
foldBoundaryCount: observation.foldBoundaryCount,
|
||||
continuousManyToOne: observation.continuousManyToOne,
|
||||
gridManyToOne: observation.gridManyToOne,
|
||||
foldSegments: observation.foldSegments.map((segment) => ({ ...segment })),
|
||||
adcBins: [...observation.bins],
|
||||
adcBinCenterRanges: observation.adcBinCenterRanges.map((range) => ({ ...range })),
|
||||
adcBinSetKey: observation.adcBinSetKey,
|
||||
equivalentBandIds: [],
|
||||
};
|
||||
}
|
||||
|
||||
export function createResultDocument(options) {
|
||||
const {
|
||||
arrayDefinition,
|
||||
@@ -266,8 +332,31 @@ export function createResultDocument(options) {
|
||||
} = options;
|
||||
const algorithm = run.algorithm ?? experiment.beamforming?.algorithm ?? DEFAULT_LOCALIZATION_ALGORITHM;
|
||||
const rawScale = localizationAlgorithmDefinition(algorithm)?.rawScale ?? "power";
|
||||
const sampling = samplingForResult(run.signalStats.sampling);
|
||||
const frequencyObservations = new Map();
|
||||
for (const definition of experiment.frequencyBands) {
|
||||
const observation = frequencyObservationForResult(
|
||||
scene,
|
||||
experiment,
|
||||
definition,
|
||||
algorithm,
|
||||
);
|
||||
if (observation) frequencyObservations.set(definition.id, observation);
|
||||
}
|
||||
const equivalentGroups = new Map();
|
||||
for (const [bandId, observation] of frequencyObservations) {
|
||||
const group = equivalentGroups.get(observation.adcBinSetKey) ?? [];
|
||||
group.push(bandId);
|
||||
equivalentGroups.set(observation.adcBinSetKey, group);
|
||||
}
|
||||
for (const [bandId, observation] of frequencyObservations) {
|
||||
observation.equivalentBandIds = equivalentGroups
|
||||
.get(observation.adcBinSetKey)
|
||||
.filter((candidateId) => candidateId !== bandId)
|
||||
.sort();
|
||||
}
|
||||
return {
|
||||
schemaVersion: "1.0.0",
|
||||
schemaVersion: RESULT_SCHEMA_VERSION,
|
||||
kind: "micarrsim.result",
|
||||
experimentId: experiment.id,
|
||||
arrayId: arrayDefinition.id,
|
||||
@@ -289,6 +378,7 @@ export function createResultDocument(options) {
|
||||
...(run.signalStats.acousticPathCounts
|
||||
? { acousticPathCounts: { ...run.signalStats.acousticPathCounts } }
|
||||
: {}),
|
||||
...(sampling ? { sampling } : {}),
|
||||
},
|
||||
timingMs: {
|
||||
total: run.timingMs.total,
|
||||
@@ -317,7 +407,15 @@ export function createResultDocument(options) {
|
||||
peaks: evaluation.peaks.map(peakForResult),
|
||||
metrics: finiteMetricSubset(evaluation.metrics),
|
||||
};
|
||||
if (evaluation.evaluation) bandResult.evaluation = { ...evaluation.evaluation };
|
||||
const frequencyObservation = frequencyObservations.get(band.id);
|
||||
if (frequencyObservation) bandResult.frequencyObservation = frequencyObservation;
|
||||
if (evaluation.evaluation) {
|
||||
bandResult.evaluation = {
|
||||
...evaluation.evaluation,
|
||||
truthSelection:
|
||||
"tone-analog-interval-or-nearest-adc-bin-and-all-nontone-sources",
|
||||
};
|
||||
}
|
||||
return bandResult;
|
||||
}),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user