feat: add realistic portable localization core
This commit is contained in:
@@ -3,11 +3,56 @@
|
||||
두 앱이 공유하는 무의존성 수치 기준 구현입니다.
|
||||
|
||||
- 결정적 tone/white/pink noise 및 오디오 asset resampling
|
||||
- 자유음장 점 음원의 `delay = r / c`, `amplitude = 1 / r` 구면 전파
|
||||
- zero-padded FFT phase shift 기반 fractional sample delay
|
||||
- 자유음장 직접음과 선택적 저비용 shoebox multipath
|
||||
- zero-padded FFT 전달함수 기반 fractional sample delay와 마이크 주파수 응답
|
||||
- radix-2 FFT, Hann STFT와 선택 bin 추출
|
||||
- scan grid 거리 cache 및 conventional frequency-domain DAS
|
||||
|
||||
## 실측 다채널 스트림 위치 추정
|
||||
|
||||
`realtime-localizer.js`는 Scene·음원 합성 없이 이미 sampling된 동기식 마이크 채널을 직접
|
||||
받습니다. 외부 package 의존성이 없는 ES module이며, 다른 프로젝트에는 `acoustics-core`
|
||||
디렉터리를 그대로 복사해 사용할 수 있습니다. 채널 순서는 `array.microphones` 순서와 같아야 하고
|
||||
각 chunk는 동일 길이의 `Float32Array` 목록이어야 합니다.
|
||||
|
||||
```js
|
||||
import { createStreamingLocalizer } from "./packages/acoustics-core/realtime-localizer.js";
|
||||
|
||||
const localizer = createStreamingLocalizer({
|
||||
sampleRate: 48_000,
|
||||
array,
|
||||
scan: { xMin: -1, xMax: 1, yMin: -1, yMax: 1, z: 2, columns: 50, rows: 50 },
|
||||
bands: [{ id: "machine", minHz: 900, maxHz: 1100, color: "#65f2bf" }],
|
||||
stft: { fftSize: 512, hopSize: 256 },
|
||||
analysisWindowSamples: 4_800,
|
||||
analysisHopSamples: 4_800,
|
||||
maximumBufferedSamples: 19_200,
|
||||
maximumResultsPerCall: 1,
|
||||
});
|
||||
|
||||
// Capture callback: 복사 후 즉시 반환하며, 오래 밀린 sample은 oldest-first로 버립니다.
|
||||
localizer.append(channels);
|
||||
|
||||
// Analysis loop: 호출마다 설정된 상한 이하의 map만 반환합니다.
|
||||
const results = await localizer.analyzeAvailable({ signal: abortController.signal });
|
||||
for (const result of results) {
|
||||
const { position, rawPower } = result.locations[0];
|
||||
drawMarker(position, rawPower, result.centerTime);
|
||||
}
|
||||
```
|
||||
|
||||
한 블록만 처리할 때는 `createLocalizationPlan()`과 `localizeMultichannel()`을 사용합니다.
|
||||
`getState()`은 absolute sample origin, buffered/dropped sample, 대기 window 수와 고정 input
|
||||
buffer byte 수를 돌려주므로 capture adapter가 지연을 감시할 수 있습니다. 실제 장치의 clock,
|
||||
channel framing과 transport는 하드웨어별 adapter 책임이며 이 모듈은 그 다음 단계부터 시작합니다.
|
||||
|
||||
반복 CPU 분석 계획은 거리 행렬, bin calibration, 제한 내의 Float64 steering matrix와 임시 bin map을
|
||||
재사용합니다. 예를 들어 192채널·50×50·2 bins를 10회 분석하면 거리 계산은 4,800,000회에서
|
||||
480,000회로, steering point/microphone pair는 9,600,000회에서 960,000회로 줄어듭니다
|
||||
(sin/cos 호출 17,280,000회 회피). 이 경우 약 16.5 MiB를 유지하며, 기본 64 MiB steering 한도를
|
||||
넘는 설정은 steering cache만 끕니다. 자동 테스트는 cache 전후 raw/display Float32 map의 전 요소
|
||||
bit-exact 일치를 고정합니다.
|
||||
|
||||
## 이동 음원과 분석 시간창
|
||||
|
||||
`sourcePositionAtTime`은 scene 시작 기준의 절대 시각에서 trajectory keyframe 사이를 선형 보간하며,
|
||||
@@ -33,8 +78,40 @@ calibration 값의 역 gain/phase로 보정합니다. raw power map과 표시용
|
||||
buffer로 보존합니다. bin spectrum은 제한된 batch로 처리하며 point batch 사이에서 event loop를
|
||||
양보해 진행 callback과 `AbortSignal` 취소가 동작합니다.
|
||||
|
||||
현재 전파 모델은 자유음장·무지향성 점 음원이며 잔향은 포함하지 않습니다. 전체 signal을
|
||||
메모리에 보관하는 CPU 기준 구현이므로 source/channel과 delay FFT working set을 512 MiB로 제한합니다.
|
||||
장시간 입력은 이후 chunk/STFT streaming backend에서 지원합니다.
|
||||
마이크별 `frequencyResponse`는 2~64개의 `{ frequencyHz, gainDb, phaseDeg? }` 점으로 정의합니다.
|
||||
`interpolateFrequencyResponse`는 dB와 wrapped phase를 구간 선형 보간하고 측정 범위 밖에서는 가장
|
||||
가까운 끝점을 사용합니다. 합성 시 기존 sensitivity/phase 편차에 이 응답을 추가하며, CPU DAS는
|
||||
선택 FFT bin의 역응답을 calibration/steering cache에 포함합니다. WebGPU 경로는 GPU 전송 직전의
|
||||
packed STFT에 같은 역응답을 적용합니다. `beamforming.compensateFrequencyResponse: false`는
|
||||
sensitivity/phase 보정은 유지한 채 곡선 보정만 꺼 robustness 비교를 재현합니다.
|
||||
|
||||
응답 곡선이 없는 배열은 기존 계산 경로와 결과를 유지합니다. 곡선이 있으면 합성 FFT마다 bin당
|
||||
보간·복소 곱이 추가되고, WebGPU 준비 단계에는 `선택 bin × 마이크` calibration과 STFT 복소 곱이
|
||||
추가되지만 GPU buffer layout이나 dispatch 크기는 늘어나지 않습니다.
|
||||
|
||||
## 저비용 환경 근사
|
||||
|
||||
Scene의 `acoustics`를 생략하거나 `{ "model": "free-field" }`로 두면 기존 직접음
|
||||
`delay = r/c`, `gain = 1/r`만 사용합니다. `model: "shoebox"`는 다음 경로를 선택적으로
|
||||
추가합니다.
|
||||
|
||||
- 직육면체 `room`의 여섯 면에 대한 1차 image-source 반사
|
||||
- 반사 에너지 일부를 deterministic 지연 경로로 나누는 scattering 근사
|
||||
- 축 정렬 box `occluders`를 통과하는 직접음 감쇠와 가장 짧은 corner detour 회절 경로
|
||||
- RT60·gain·tap 수로 제한한 deterministic diffuse-reverb tail
|
||||
|
||||
모든 경로의 위상과 gain은 마이크마다 하나의 frequency-domain 전달함수로 합친 뒤 IFFT를 한 번만
|
||||
수행합니다. 경로 위상은 recurrence로 갱신하고 1,024 bin마다 재고정하므로 각 bin·path마다
|
||||
삼각함수를 다시 호출하지 않습니다. UI preset도 수신점당 sparse path 상한을 표시합니다.
|
||||
|
||||
이 모델은 배열 배치와 위치추정 robustness를 빠르게 비교하기 위한 근사입니다. 파동 방정식,
|
||||
고차 반사, 재질별 주파수 의존 흡음, 복잡한 mesh 회절, Doppler, 음원·마이크 지향성, 압력/SPL,
|
||||
센서 self-noise, ADC quantization·clock jitter는 계산하지 않습니다. 그런 효과가 중요한
|
||||
검증에서는 실측 다채널 데이터를 아래 streaming API에 넣어 결과를 비교해야 합니다.
|
||||
|
||||
snapshot 합성은 전체 signal과 가장 늦은 sparse path를 포함하는 delay FFT working set을
|
||||
512 MiB로 제한합니다. 실측·장시간 입력은 위 streaming localizer의 bounded ring buffer로
|
||||
전체 녹음을 메모리에 보관하지 않고 처리할 수 있습니다. CPU analysis plan도 기본 512 MiB
|
||||
hard cap을 할당 전에 검사하며 steering cache는 별도의 64 MiB 한도를 넘으면 자동으로 끕니다.
|
||||
|
||||
192채널·50×50 fixture는 브라우저 통합 테스트에서 실제 합성부터 DAS peak 검출까지 실행합니다.
|
||||
|
||||
Reference in New Issue
Block a user