import { describeFrequencyAliasing, observeFrequencyBand, } from "/packages/acoustics-core/sampling.js"; /** * Select evaluation truth sources for one observed band. * * Arrival ids come from synthesis path timing and take precedence over the * legacy emission-time fallback. This helper is evaluation-only; localization * never receives truth positions or ids. */ export function sourcesForObservedBand( scene, band, fftSize, options = {}, ) { const observation = observeFrequencyBand(band, scene.sampleRate, fftSize); const observedBins = new Set(observation.bins); const arrivingSourceIdSet = Array.isArray(options.arrivingSourceIds) ? new Set(options.arrivingSourceIds) : null; const time = options.time ?? null; return scene.sources.filter((source) => { if (arrivingSourceIdSet) { if (!arrivingSourceIdSet.has(source.id)) return false; } else if (time !== null) { const startTime = source.startTime ?? 0; const endTime = startTime + (source.duration ?? scene.duration); if (time < startTime || time >= endTime) return false; } if (source.signal.type !== "tone") return true; const analogInside = source.signal.frequency >= band.minHz && source.signal.frequency <= band.maxHz; if (analogInside) return true; const alias = describeFrequencyAliasing(source.signal.frequency, scene.sampleRate); const nearestAdcBin = Math.round(alias.adcFrequencyHz / observation.binWidthHz); return observedBins.has(nearestAdcBin); }); }