docs: explain localization choices and cost

This commit is contained in:
2026-08-28 20:58:38 +09:00
parent fe0f70ab11
commit a48791075e
11 changed files with 675 additions and 185 deletions
+110 -14
View File
@@ -11,8 +11,9 @@ Snapshot 한 번의 처리 순서는 다음과 같다.
1. tone, noise 또는 audio asset으로 음원 신호를 만든다.
2. 음원과 각 마이크 사이의 직접음 또는 희소 multipath 전달함수를 만든다.
3. 마이크별 `sensitivityDb`, `phaseOffsetDeg`와 `frequencyResponse`를 수신 신호에 적용한다.
4. 합성된 동기식 다채널 신호를 STFT하고 conventional DAS map을 계산한다.
5. 설정에 따라 알려진 마이크 응답을 DAS에서 역보정하고 peak와 평가 지표를 구한다.
4. 합성된 동기식 다채널 신호를 STFT하고 `beamforming.algorithm`으로 선택한 위치 추정
알고리즘의 scan map을 계산한다.
5. 알고리즘에 맞게 알려진 마이크 응답을 보정하고 map의 peak와 평가 지표를 구한다.
Scene의 `acoustics`를 생략하거나 `model: "free-field"`로 지정하면 기존 직접음 결과를
유지한다. `model: "shoebox"`는 직육면체 방 안에서 경로 수가 제한된 근사를 추가한다.
@@ -97,9 +98,91 @@ preset을 적용한다.
각 source–microphone 직접 경로를 실제로 막을 때 하나씩 추가된다. 대략적인 합성 경로 예산은
`음원 수 × 마이크 수 × 수신점당 최대 경로`다. Camera는 실행 전에 이 예산을 표시한다.
반사·tail을 늘리면 beamforming의 `O(scan points × microphones × selected bins × STFT frames)`
뿐 아니라 신호 합성의 전달함수 누산도 늘어난다. 처음에는 50×50 grid와 자유음장으로 기준선을
만들고, 같은 입력에서 preset을 하나씩 바꾸어 비교하는 것이 좋다.
반사·tail을 늘리면 신호 합성의 전달함수 누산도 늘어난다. DAS 계열의 scan 누산은 대략
`O(scan points × microphones × selected bins × STFT frames)`이고, GCC-PHAT는 pair별 상호
상관을 만든 뒤 `O(scan points × (microphones - 1) × bands)` lookup을 수행한다. 어느 방식이든
STFT와 거리 cache 비용은 별도로 필요하다. 처음에는 50×50 grid와 자유음장으로 기준선을 만들고,
같은 입력에서 환경과 알고리즘을 하나씩 바꾸어 비교하는 것이 좋다.
## 위치 추정 알고리즘
마이크 `m`, STFT frame `t`, 주파수 `f`의 복소 spectrum을 `X_m(t, f)`, scan point `p`와
마이크 사이 거리를 `r_m(p)`, 파수를 `k = 2πf/c`라고 하자. `φ_m(f)`는 알려진 마이크의 정적
phase offset과, 주파수 응답 보정을 켠 경우 response phase를 합한 값이다. 모든 map은 scan point
마다 하나의 비음수 상대값을 만들지만, 그 값은 위치의 확률이나 posterior가 아니다.
### DAS (`das`)
Conventional frequency-domain delay-and-sum은 각 후보점에서 구면파 전파 위상을 되돌려 채널을
coherent sum한다.
```text
B_DAS(p,t,f) = (1/M) Σ_m A_m⁻¹(f) X_m(t,f) exp(j[k r_m(p) - φ_m(f)])
P_DAS(p) = mean_(t,f) |B_DAS(p,t,f)|²
```
`A_m⁻¹(f)`에는 sensitivity의 역 gain과, 설정한 경우 frequency-response gain의 제한된 역보정이
들어간다. 구현은 frame별 제곱 크기를 먼저 평균하고 선택 band의 bin들을 다시 평균한다. 따라서
Result의 `rawMap.scale`은 `power`지만, 절대 SPL로 calibration한 power가 아니라 같은 입력·설정
안에서 비교하는 선형 상대 power다. CPU와 WebGPU를 지원한다.
### DAS-PHAT (`das-phat`)
DAS-PHAT는 steering 전에 각 frame·bin·마이크 spectrum을 위상만 남기도록 정규화한다.
```text
U_m(t,f) = X_m(t,f) / |X_m(t,f)|
S_DAS-PHAT(p) = mean_(t,f) |(1/M) Σ_m U_m(t,f) exp(j[k r_m(p) - φ_m(f)])|²
```
한 frame·bin에서 최강 마이크 크기의 기본 `10⁻⁶`배인 상대 floor 이하의 값은 `0`으로 두어
무음이나 극소값의 불안정한 위상을 제외한다. 입력과 calibration의 amplitude gain은 coherent sum에 사용하지 않고,
알려진 phase만 보정한다. 그 결과 source spectrum이나 채널 gain 차이에 덜 끌릴 수 있지만 유용한
amplitude 정보도 버리며, 약한 bin의 위상과 multipath에 민감할 수 있다. `rawMap.scale`은 무차원
`score`이고 CPU와 WebGPU를 지원한다.
### GCC-PHAT (`gcc-phat`)
현재 GCC-PHAT는 배열 중심에서 가장 먼 마이크 하나를 결정적인 reference로 고르고, reference와
나머지 `M - 1`개 마이크의 pair만 사용한다. 각 band와 pair에 대해 다음 PHAT cross spectrum을
STFT frame에 걸쳐 평균한다.
```text
C_mr(t,f) = X_m(t,f) conj(X_r(t,f))
G_mr(f) = mean_t [C_mr(t,f) / max(|C_mr(t,f)|, floor)]
× exp(j[φ_r(f) - φ_m(f)])
```
기본 `floor`는 해당 band·pair의 모든 선택 bin과 frame에서 얻은 최대 cross-spectrum 크기의
`10⁻⁶`배다. 이 floor는 amplitude를 score 가중치로 쓰기 위한 것이 아니라 극소 cross spectrum의
불안정한 위상 정규화를 제한한다.
선택 band 밖의 bin은 `0`으로 두고 켤레 대칭 spectrum을 IFFT하여 원형 상호상관
`R_mr,b(ℓ)`을 만든다. scan point마다 far-field 각도 근사가 아니라 정확한 거리 차이로 near-field
예측 lag를 계산한다.
```text
ℓ_mr(p) = [r_m(p) - r_r(p)] sampleRate / c
Q_b(p) = Σ_(m≠r) R_mr,b(ℓ_mr(p))
K_b = I(0 ∈ b) + I(N/2 ∈ b) + 2 × #(나머지 positive bins)
S_b(p) = clamp[N Q_b(p) / ((M-1) K_b), 0, 1]
```
분수 sample lag는 IFFT 원형 상호상관의 이웃 두 sample을 선형 보간한다. Hermitian coefficient
수 `K_b`, IFFT의 `1/N`, reference pair 수로 나눈 뒤 음의 상관은 0으로 두어 score를 0–1로
제한한다. 따라서 같은 위치 가설의 raw GCC score는 scan 범위나 마이크 수 자체에 의해 재척도화되지
않는다. 이 값도 확률이나 calibration된 confidence는 아니다. `rawMap.scale`은 `score`다.
PHAT가 amplitude를 제거하므로 sensitivity와 frequency-response gain은 역보정하지 않고 알려진
phase만 보정한다. 현재 CPU만 지원하고 두 개 이상의 마이크가 필요하다.
GCC-PHAT는 broadband 신호에서 상호상관 peak가 뚜렷할수록 유리하다. 단일 tone이나 지나치게 좁은
band는 주기적으로 같은 phase를 만드는 lag가 많아 여러 위치가 비슷한 score를 얻을 수 있고,
반사음이나 여러 동시 음원은 pair 상관 peak를 섞는다. IFFT 길이가 `N = fftSize`이면 표현 가능한
비중첩 원형 lag는 절대값 `N/2` sample까지다. 현재 구현은 scan 전체의 최대 기하학적 lag가 이를
넘으면 실행을 거절하므로 FFT를 키우거나 array aperture 또는 scan 범위를 줄여야 한다. 이 검사는
원형 lag alias를 막지만 좁은 대역의 주기적 모호성, 제한된 bandwidth, scan grid 양자화까지
해결하지는 않는다. 단일 reference 방식은 모든 마이크 쌍을 쓰는 GCC보다 계산량이 작지만 pair
중복성과 고장 마이크에 대한 강건성도 작다.
## 마이크 주파수 응답
@@ -126,11 +209,17 @@ preset을 적용한다.
보간하고 측정 범위 밖에서는 가장 가까운 끝점 값을 유지한다. 합성기는 이 curve를 실제 마이크
채널에 적용한다.
Experiment에서 `beamforming.compensateFrequencyResponse`를 생략하거나 `true`로 두면
CPU와 WebGPU DAS가 선택 FFT bin에서 알려진 curve의 역 gain/phase를 적용한다. 응답 notch의
무제한 역보정은 noise까지 크게 증폭할 수 있으므로 양의 보상 boost는
`beamforming.maxCompensationDb`로 제한하며 기본값은 24 dB다. 허용 범위는 0–120 dB이고
응답 peak를 줄이는 attenuation과 phase 보상은 이 boost 상한에 의해 잘리지 않는다.
합성기는 알고리즘과 무관하게 sensitivity, 정적 phase offset과 frequency-response curve를 수신
channel에 적용한다. 위치 추정에서의 역보정은 알고리즘별로 다르다.
- DAS는 sensitivity 역 gain과 정적 phase를 항상 보정한다. `compensateFrequencyResponse`를
생략하거나 `true`로 두면 response의 역 gain과 phase도 보정한다. 응답 notch의 무제한 gain
boost는 noise까지 키울 수 있으므로 `maxCompensationDb`로 제한하며 기본값은 24 dB, 허용 범위는
0–120 dB다. 응답 peak를 줄이는 attenuation과 phase 보상에는 이 boost 상한을 적용하지 않는다.
- DAS-PHAT와 GCC-PHAT는 amplitude를 정규화하므로 sensitivity와 response gain의 역보정을
사용하지 않는다. 정적 phase는 항상 보정하고, `compensateFrequencyResponse`가 `true`이면 알려진
response phase도 보정한다. 따라서 `maxCompensationDb`는 두 PHAT 알고리즘의 score에는 영향을
주지 않는다.
```json
{
@@ -147,8 +236,10 @@ CPU와 WebGPU DAS가 선택 FFT bin에서 알려진 curve의 역 gain/phase를
```
Camera의 **마이크 주파수 응답 처리**에서 **측정 curve 적용 + 보정하지 않음**을 고르면 합성에는
curve가 남고 DAS 역보정만 꺼진다. 같은 배열에서 보정 유무의 robustness를 비교할 때 사용한다.
curve가 없는 마이크는 0 dB, 0°로 취급한다.
curve가 남고 위치 추정의 response gain/phase 역보정만 꺼진다. DAS-PHAT와 GCC-PHAT는 여전히
amplitude를 버리지만, curve가 신호 대 잡음과 relative floor 통과 여부에 준 영향까지 되돌리지는
못한다. 같은 배열에서 알려진 response 보정 유무의 robustness를 비교할 때 사용한다. curve가 없는
마이크는 0 dB, 0°로 취급한다.
## 결과에서 확인할 값
@@ -162,6 +253,10 @@ Result `signalStats`에는 다음 선택 필드가 들어간다.
같은 입력과 seed에서 환경 preset만 바꿨다면 이 값과 위치 오차·precision/recall·PSR을 함께
비교할 수 있다.
Result 최상위 `algorithm`은 실제 실행한 `das`, `das-phat` 또는 `gcc-phat`를 기록한다. band의
`rawMap.scale`은 DAS에서 `power`, 두 PHAT 방식에서 `score`다. peak 정규화된 `displayMap`은
시각 비교용일 뿐 어느 scale도 확률분포나 신뢰구간으로 바꾸지 않는다.
## 정확도 범위와 제외 사항
이 구현은 FDTD/FEM/BEM이나 room impulse response 측정치를 대신하지 않는다. 다음 항목은
@@ -175,8 +270,9 @@ Result `signalStats`에는 다음 선택 필드가 들어간다.
- preamp, anti-alias filter, ADC quantization, channel clock jitter·drift
- 실제 장착 구조와 마이크 위치 공차
따라서 preset의 RT60이나 wall gain은 특정 방을 인증하는 값이 아니라 배열과 DAS가 multipath에
얼마나 민감한지 비교하는 조건이다. 실제 장치 성능을 평가할 때는 같은 Array/scan/band 설정으로
따라서 preset의 RT60이나 wall gain은 특정 방을 인증하는 값이 아니라 배열과 위치 추정 알고리즘이
multipath에 얼마나 민감한지 비교하는 조건이다. 실제 장치 성능을 평가할 때는 같은
Array/scan/band 설정으로
동기식 실측 채널을 [실시간 위치추정 코어](realtime-porting.md)에 입력하고 합성 결과와 나란히
비교해야 한다. 주파수 응답 curve만으로 noise floor, distortion, ADC와 clock 오차까지 모사되지는
않는다.
+14 -1
View File
@@ -1,4 +1,4 @@
# WebGPU 기준 실측
# WebGPU DAS 기준 실측
2026-08-28에 Chrome 151 headless, Intel Gen9 adapter에서 synthetic 1 kHz fixture를 측정했다. 입력은
48 kHz · 0.25 s, 192 microphones, FFT 512 / hop 256, 900–1100 Hz band이며 16×16 warm-up 뒤 각
@@ -10,6 +10,19 @@
| 100×100 | 176,640,000 | 251.4 ms | 2,102.0 ms | 36.91 MiB | 3.17× |
| 200×200 | 706,560,000 | 891.9 ms | 4,206.7 ms | 80.47 MiB | 11.26× |
이 표는 conventional DAS의 저장된 기준 측정이며 DAS-PHAT이나 GCC-PHAT의 실측값이 아니다.
50×50에서 CPU STFT median은 1,669.8 ms로 `end-to-end` 1,818.1 ms의 약 91.8%였고,
실제 GPU queue 구간은 79.2 ms였다. 별도로 한 번 수행한 192채널 signal 합성은 1,200.5 ms이며
위 `end-to-end` 열에 포함되지 않는다. 따라서 이 fixture에서는 scan MAC만 줄인 비율을 전체
Camera 실행 시간 향상으로 해석하면 안 된다.
같은 192채널·50×50·46 frames·2 bins·한 band를 식으로만 비교하면 DAS scan은
44,160,000 complex MAC이다. 현재 single-reference GCC-PHAT은 공통 forward STFT 뒤
35,144회의 2-pass cross-spectrum 연산, 191회의 inverse FFT와 477,500회의 point/pair
lookup을 수행한다. 이는 구현 연산량 비교이지 GCC 실측 benchmark가 아니며, 2 bins는 GCC-PHAT에
너무 좁어 주기적 지연 모호성이 크다. 정확도·시간 비교에는 band마다 8 bins 이상의 광대역 fixture와
같은 입력·장치·warm-up 조건을 사용해야 한다.
`GPU wall`은 host packing/`writeBuffer()` 호출 직후부터 queue에 들어간 transfer 완료, steering pass,
DAS pass와 readback까지 기다린 시간이다. `end-to-end`에는 이 host 작업과 장치/pipeline 초기화,
distance 생성, CPU STFT가 포함된다. 200×200은 64 MiB steering buffer 목표를 지키면서 두 FFT bin을
+73 -19
View File
@@ -8,7 +8,7 @@ micarrsim의 저장 형식은 JSON Schema Draft 2020-12와 추가 관계 검증
- `micarrsim.array`: aperture와 마이크별 `[x, y, z]` 좌표
- `micarrsim.scene`: 음원·ambient recipe, 위치 ground truth와 로컬 asset manifest
- `micarrsim.experiment`: scan plane/grid, 주파수 대역, STFT와 DAS 설정
- `micarrsim.experiment`: scan plane/grid, 주파수 대역, STFT와 위치 추정 설정
- `micarrsim.result`: raw/display acoustic map, 복수 peak와 source 매칭, 평가·성능 정보
- `micarrsim.comparison-history`: 세션의 배열별 Result 요약을 모은 편의 export. core schema
검증 대상은 아니며 각 원본 Result JSON을 대신하지 않는다.
@@ -18,17 +18,36 @@ scene JSON에는 192채널 원시 sample이나 원본 asset byte를 넣지 않
`sha256` manifest로 참조한다. 사용자 컴퓨터의 절대 경로, URL, `..` 경로는 저장하지 않는다.
array의 `sensitivityDb`와 `phaseOffsetDeg`는 각 마이크 수신 channel에 적용되는 편차다. 합성기는
이를 신호에 주입하고, beamformer는 알려진 calibration 값의 역 gain/phase를 적용한다. 선택적
이를 신호에 주입하고, 위치 추정기는 선택한 알고리즘의 규칙에 따라 알려진 calibration을 보정한다.
선택적
`frequencyResponse`는 2–64개의 `{ frequencyHz, gainDb, phaseDeg? }` 측정점이다.
주파수는 0–1,000,000 Hz에서 중복 없이 증가해야 하며, dB와 wrapped phase를 구간 선형 보간하고
측정 범위 밖에서는 가까운 끝점을 사용한다.
Experiment의 선택적 `beamforming.compensateFrequencyResponse`는 기본 `true`다. 합성
channel에는 curve가 항상 적용되며, `true`이면 CPU/WebGPU DAS가 선택 bin에서 curve의
역 gain/phase도 적용한다. `false`이면 sensitivity/phase calibration은 유지하고 curve
역보정만 끈다. 선택적 `beamforming.maxCompensationDb`는 응답 notch의 역보정에서 양의
gain boost만 제한하는 0–120 dB 값이며 생략 시 24 dB다. 응답 peak를 줄이는 attenuation과
phase 보상은 이 boost 상한을 그대로 적용하지 않는다.
Experiment의 필수 `beamforming.algorithm` enum은 `das`, `das-phat`, `gcc-phat`다. backend와
raw map 계약은 다음과 같다.
| algorithm | `rawMap.scale` | 허용 backend |
| --- | --- | --- |
| `das` | `power` | `cpu`, `webgpu`; `auto` 가능 |
| `das-phat` | `score` | `cpu`, `webgpu`; `auto` 가능 |
| `gcc-phat` | `score` | `cpu`; `auto`는 CPU 선택 |
`gcc-phat`에 명시적 `webgpu`를 조합하면 검증 오류이며, 마이크가 두 개 이상이어야 한다. 필드
그룹 이름 `beamforming`은 기존 계약과의 호환을 위해 유지하지만 세 알고리즘을 모두 선택하는 위치
추정 설정이다.
선택적 `beamforming.compensateFrequencyResponse`는 기본 `true`다. 합성 channel에는 curve가
항상 적용된다. DAS는 sensitivity와 정적 phase를 항상 역보정하고, 값이 `true`이면 선택 bin에서
curve의 gain과 phase도 역보정한다. DAS-PHAT와 GCC-PHAT는 입력과 calibration의 amplitude gain을
사용하지 않지만 정적 phase를 보정하며, 값이 `true`이면 알려진 curve phase도 보정한다. `false`는
모든 알고리즘에서 curve 보정만 끄고 정적 phase 보정은 유지한다.
선택적 `beamforming.maxCompensationDb`는 DAS가 응답 notch를 역보정할 때 양의 gain boost만
제한하는 0–120 dB 값이며 생략 시 24 dB다. 응답 peak를 줄이는 attenuation과 phase 보상은 이
boost 상한의 대상이 아니다. amplitude를 사용하지 않는 DAS-PHAT와 GCC-PHAT에는 이 값이 영향을
주지 않는다. 세 알고리즘의 계산식과 한계는
[음향 환경과 마이크 응답 모델](acoustic-model.md)에 설명한다.
## 로컬 asset 재연결
@@ -80,24 +99,55 @@ room 안의 min/max, 고유 id, -120–0 dB의 `transmissionDb`와
gain을 보존하고, `peak`는 합성 전 peak를 `targetPeak`에 맞춘다. result의 `signalStats`에는
정규화 전 peak, 실제 scale, scale 적용 후에도 범위를 넘은 sample 수를 기록한다.
이는 acoustic map 표시 정규화와 별개다. result의 `rawMap`은 반드시 선형 power이고,
`displayMap`은 peak-normalized 값 또는 dB 값일 수 있다. 후처리 결과는 `processedMaps`에 넣어
raw DAS map을 덮어쓰지 않는다.
이는 acoustic map 표시 정규화와 별개다. result의 `rawMap.scale`은 알고리즘의 원래 비음수 map을
뜻하는 `power` 또는 `score`다. DAS의 `power`는 절대 SPL로 calibration한 값이 아니며,
DAS-PHAT와 GCC-PHAT의 `score`도 확률, likelihood 또는 confidence가 아니다. 합이 1이 되도록
정규화되지 않으며 서로 다른 scale이나 알고리즘의 절대값을 직접 비교해서는 안 된다.
GCC-PHAT raw score는 pair 수·band의 Hermitian coefficient 수·IFFT 크기로 나눈 0–1 양의
상관 응답이므로 scan 범위를 바꿔도 같은 위치 가설의 척도가 유지된다. 그래도 확률 calibration은
아니며 DAS-PHAT score와 절대값을 비교하는 용도가 아니다.
`displayMap`은 원래 scale, peak-normalized `normalized` 또는 `db`일 수 있다. peak 정규화는
최댓값을 1로 만드는 표시 변환일 뿐 확률 정규화가 아니다. `processedMaps`는 `damas`, `clean-sc`
또는 `custom` 후처리 결과를 별도로 교환하기 위한 예약 슬롯이다. 현재 위치 추정 코어는 DAMAS나
CLEAN-SC를 실행하거나 이 필드를 자동 생성하지 않으며, raw map을 후처리 map으로 덮어쓰지 않는다.
map은 row-major 배열이다. 첫 행은 `yMax`, 마지막 행은 `yMin`, 각 행은 `xMin`에서 `xMax`
방향이다. `values.length`는 반드시 `rows × columns`와 같아야 한다.
Experiment의 선택적 `evaluation.detectionThresholdRatio`는 최강 local peak power에 대한 검출
임계 비율이며 `0 < ratio <= 1`이다. 생략된 이전 문서를 Camera에서 열면 기본값 `0.25`를 적용하며,
Experiment JSON을 다시 저장하면 이 값이 명시된다. 값을 낮추면 약한 peak 후보를 포함하지만
Experiment의 선택적 `evaluation.detectionThresholdRatio`는 최강 local peak의 같은-scale raw
값에 대한 검출 임계 비율이며 `0 < ratio <= 1`이다. 생략된 이전 문서를 Camera에서 열면 기본값
`0.25`를 적용하며, Experiment JSON을 다시 저장하면 이 값이 명시된다. 값을 낮추면 약한 peak 후보를 포함하지만
sidelobe false positive도 늘 수 있다.
## Result 평가 정보
각 band의 `peaks`는 위치와 power 값, 선택적 `sourceId`, `localizationErrorM`,
`localizationErrorPixels`, `beamSpread`를 저장한다. `beamSpread`는 선택 peak를 포함하는 연결된
half-power 성분의 power 가중 centroid·공분산과 major/minor RMS 반경·각도다. `calibrated`는 현재
항상 `false`이며 확률분포, Gaussian 신뢰구간 또는 통계적 위치 불확실성으로 해석하지 않는다.
새 Result는 최상위 `algorithm`에 실제 실행한 `das`, `das-phat` 또는 `gcc-phat`를 기록하며
Experiment의 선택값과 같아야 한다. 이 필드는 이전 `1.0.0` Result를 읽기 위해 schema상 선택적으로
남아 있지만 현재 export에는 항상 들어간다. `backend`는 요청값 `auto`가 아니라 실제 사용한
`cpu` 또는 `webgpu`다.
관계 검증은 `das → power`, `das-phat/gcc-phat → score`를 강제한다. Peak에 `beamSpread`가
있으면 power map은 power-weighted method, score map은 score-weighted method여야 한다. 따라서
구조상 유효하지만 의미가 모순된 외부 Result도 import 시 거절된다.
`timingMs.localization`은 선택한 위치 추정 알고리즘의 wall time이다. 현재 export는 기존 DAS 전용
소비자를 위해 같은 값을 `timingMs.beamforming`에도 호환 alias로 기록한다. `1.0.0` schema는 이전
문서를 위해 `beamforming`을 여전히 필수로 두므로, 새 writer는 `localization`과 `beamforming`을
함께 저장해야 한다. `timingMs.total`은 전체 시간이고 선택적 `signalGeneration`은 합성 시간이다.
각 band의 `peaks`는 위치와 해당 `rawMap.scale`의 값, 선택적 `sourceId`, `localizationErrorM`,
`localizationErrorPixels`, `beamSpread`를 저장한다. `beamSpread`는 선택 peak를 포함하는 8-connected
성분의 가중 centroid·공분산과 major/minor RMS 반경·각도다.
- `power` map은 `connected-half-power-power-weighted-covariance`를 사용해 peak의
`halfPowerRatio` 이상인 연결 성분을 power로 가중한다.
- `score` map은 `connected-half-maximum-score-weighted-covariance`를 사용해 peak의 같은 비율
이상인 연결 성분을 score로 가중한다. 호환 필드명 `halfPowerRatio`는 이 경우 실제로
half-maximum score 비율을 뜻한다.
두 방식 모두 `calibrated`는 현재 항상 `false`다. covariance ellipse는 map의 공간적 퍼짐을
결정적으로 요약할 뿐 확률분포, Gaussian 신뢰구간 또는 통계적 위치 불확실성이 아니다.
`metrics`에는 다음 유한값만 기록한다.
- `peakToSidelobeDb`
@@ -105,6 +155,9 @@ half-power 성분의 power 가중 centroid·공분산과 major/minor RMS 반경
- `precision`, `recall`
- `mainLobeWidthM`
호환 필드명 `energyInsideTruthRatio`는 `power` map에서는 truth 주변 power 비율, `score` map에서는
truth 주변 score 비율이다. 후자의 값은 물리적 energy 비율이나 위치 확률이 아니다.
`evaluation`은 지표를 다시 해석할 수 있도록 truth 반경, source 매칭 tolerance, peak 검출
threshold와 최소 거리, sidelobe 제외 반경, half-power 비율, dynamic range, 최대 검출 수를
버전과 함께 저장한다. `environment`에는 실제 backend, microphone/grid 수, scan bounds와 z,
@@ -146,7 +199,8 @@ CPU 분석 계획은 기본 512 MiB, 반복 steering cache는 64 MiB, streaming
256 MiB hard cap을 할당 전에 적용한다. 채널 순서와 streaming 상태 계약은
[실측 다채널 위치추정 코어 포팅](realtime-porting.md)을 참고한다.
portable block 결과의 `processingTimeMs`는 DAS와 local-peak 검출 wall time이다. Streaming
portable block 결과의 `processingTimeMs`는 선택한 위치 추정 알고리즘과 local-peak 검출 wall
time이다. Streaming
결과는 `deadlineMs`와 `deadlineMet`을 추가하고 상태에는 최근·최대 처리 시간,
`deadlineMisses`와 `droppedSamples`를 누적한다. 이 runtime telemetry는
`micarrsim.result` 저장 스키마의 필수 필드가 아니다.
+24 -18
View File
@@ -2,8 +2,8 @@
> 구현 상태 · 2026-08-28
>
> 단계 0–4와 단계 5의 저비용 음향 환경·마이크 응답·portable streaming core까지 완료했다.
> 설치 없는 브라우저 앱, CPU/WebGPU DAS, Builder→Camera asset 재연결, 임의 오디오·이동
> 단계 0–4와 단계 5의 저비용 음향 환경·마이크 응답·다중 알고리즘 portable streaming core까지 완료했다.
> 설치 없는 브라우저 앱, CPU/WebGPU DAS·DAS-PHAT와 CPU GCC-PHAT, Builder→Camera asset 재연결, 임의 오디오·이동
> trajectory, 빔 분포·복수 peak 감도, 라이브 시간축 재생, 배열 비교와 Result 내보내기를
> 전체 자동 테스트로 검증했다. 실제 장치 adapter와 현장 검증은 장치 계약이 필요한 후속 범위다.
@@ -15,7 +15,7 @@
1. 기계음 데이터와 ambient audio를 조합해 장면 및 ground truth를 생성한다.
2. 배열 JSON과 장면 정보를 사용해 각 마이크에 도달하는 신호를 합성한다.
3. WGSL로 conventional delay-and-sum(DAS) 빔포밍을 수행한다.
3. DAS, DAS-PHAT 또는 GCC-PHAT로 위치 map을 계산하고 같은 입력에서 비교한다.
4. 주파수 구간별 acoustic image를 ground truth 위에 표시하고 정량 평가한다.
5. 배열, 거리, 음원, 주파수, grid 해상도를 바꾼 실험을 저장하고 비교한다.
@@ -79,17 +79,21 @@ tap 수가 제한된 RT60 tail만 추가한다. 경로를 source–microphone
DAS는 STFT 후 각 scan point와 마이크 사이 거리로 steering phase를 만들고, 선택된 주파수 bin의
정렬 합 power를 계산한다. 여러 주파수 구간은 각각 독립 map과 색을 갖는다. CPU reference를 먼저
작성한 뒤 같은 buffer layout으로 WGSL 결과를 대조한다. DAMAS, CLEAN-SC는 인터페이스만 마련하고
초기 완료 범위에서는 제외한다.
작성한 뒤 같은 buffer layout으로 WGSL 결과를 대조한다. DAS-PHAT은 각 frame/bin에서 마이크
spectrum의 진폭을 제거하되 알려진 응답 phase를 보정한다. GCC-PHAT은 한 reference와 나머지
마이크의 band-limited PHAT 상관을 IFFT하고 scan point의 near-field 예상 지연에서 보간한다.
DAMAS, CLEAN-SC는 초기 완료 범위에서 제외한다.
연산량의 주항은 대략 `O(P × M × F × T)`다. `M=192`, `P=50²=2,500`일 때 STFT
frame·주파수 bin 하나당
약 48만 개의 복소 steering 누산이 필요하다. 100×100은 50×50보다 4배, 200×200은 16배이므로
장치별 실제 GPU timing을 UI에 표시하고 목표 frame budget에 맞춰 해상도를 제한한다. 정확한 성능과
메모리는 대표 GPU에서 benchmark하여 기록하며 이론치만으로 약속하지 않는다.
`P` points, `M` microphones, `T__ frames, `F` selected bins에서 DAS 계열 scan은
`O(P × M × T × F)`다. GCC-PHAT은 공통 forward STFT 외에 2-pass cross spectrum
`2(M-1)TΣF_b`, `(M-1)B` inverse FFT와 `P(M-1)B` lookup을 수행한다.
192채널·50×50·46 frames·2 bins의 DAS는 44,160,000 complex MAC이고 같은 한 band
GCC scan은 477,500 lookup이다. 다만 작은 grid에서는 공통 STFT가 지배할 수 있으므로 이 비율을
end-to-end 속도 향상으로 해석하지 않는다. 100×100은 50×50보다 4배, 200×200은 16배 points이며
장치별 실제 timing과 메모리를 기록하고 이론치만으로 실시간성을 약속하지 않는다.
실측·반복 분석용 CPU 계획은 거리 행렬, bin calibration, 임시 map과 허용되는 steering matrix를
재사용한다. 192채널·50×50·2 bins를 10회 분석하는 기준에서 거리 계산은 4,800,000회에서
실측·반복 분석용 CPU 계획은 거리 행렬과 workspace를 재사용하고, DAS 계열은 bin calibration과
허용되는 steering matrix도 재사용한다. 192채널·50×50·2 bins를 10회 DAS 분석하는 기준에서 거리 계산은 4,800,000회에서
480,000회, steering pair는 9,600,000회에서 960,000회로 줄어든다. snapshot working set과 계획은
512 MiB, steering cache는 64 MiB, streaming input은 256 MiB 기본 hard cap을 할당 전에 적용한다.
Streaming ring은 backlog에서 가장 오래된 sample을 버리고 호출당 결과 수를 제한한다.
@@ -176,20 +180,21 @@ flattening이 모델 타당성을 훼손하는지 청취 및 spectrum 통계로
- peak별 half-power 공분산/RMS 빔 분포와 재현 가능한 검출 임계값
- sample-aligned 준정적 시간창 분석, bounded map 계획과 이동 장면 재생
완료 조건: 사용자가 코드 수정 없이 배열 JSON 두 개를 같은 장면에서 비교할 수 있다.
완료 조건: 사용자가 코드 수정 없이 배열 JSON과 위치 추정 알고리즘을 같은 장면에서 비교할 수 있다.
### 단계 5 — 현실성·포팅과 실제 데이터 · 부분 완료
- [x] 마이크별 gain·phase·frequency-response 합성 및 선택적 DAS 역보정
- [x] 마이크별 gain·phase·frequency-response 합성과 알고리즘별 amplitude/phase 보정
- [x] 자유음장을 보존하는 선택형 shoebox 1차 반사·scattering·회절·RT60 tail
- [x] Scene 합성에서 분리한 block/streaming multichannel 위치추정 API
- [x] 반복 거리·calibration·steering cache, bounded ring과 메모리 hard cap
- [x] DAS·DAS-PHAT·reference-pair GCC-PHAT와 알고리즘별 power/score 계약
- [ ] 라이선스 검토를 통과한 DCASE/MIMII adapter와 batch workflow
- [ ] 실제 장치의 channel/clock/sample 형식에 맞춘 capture·transport adapter와 현장 검증
- [ ] 필요 시 센서 self-noise·위치 공차와 DAMAS/CLEAN-SC 후처리
현재 완료 조건은 기존 자유음장 결과를 유지한 채 propagation model과 응답 보상을 선택해
재실행하고, 같은 CPU DAS를 실측 `Float32Array` channel에 직접 적용하는 것이다. 실제
재실행하고, 선택한 CPU 위치 추정기를 실측 `Float32Array` channel에 직접 적용하는 것이다. 실제
hardware deadline과 정확도는 장치 adapter가 정해진 뒤 별도 측정한다.
## 9. 주요 위험과 대응
@@ -203,7 +208,7 @@ hardware deadline과 정확도는 장치 adapter가 정해진 뒤 별도 측정
쓴다. 실제 데이터 adapter는 단계 5의 라이선스 검토 뒤 추가한다.
- 실제 AC와 simulation gap: gain·phase·주파수 응답과 희소 실내 환경을 비교 조건으로 제공하되,
위치 공차·self-noise·ADC·clock은 실측 adapter와 현장 검증으로 다룬다.
- post-processing이 기본 결과를 가림: raw DAS map과 후처리 map을 항상 별도로 보존한다.
- post-processing이 기본 결과를 가림: 알고리즘별 raw power/score map과 후처리 map을 별도로 보존한다.
## 10. 구현 결과와 다음 백로그
@@ -211,6 +216,7 @@ hardware deadline과 정확도는 장치 adapter가 정해진 뒤 별도 측정
- [x] array/scene/experiment/result schema와 synthetic fixture
- [x] 좌표·거리·구면 감쇠·분수 지연·STFT 단위 테스트
- [x] CPU DAS golden implementation
- [x] CPU/WebGPU DAS-PHAT와 CPU reference-pair GCC-PHAT 구현·비교 UI
- [x] WGSL kernel, CPU/GPU 비교와 50²/100²/200² benchmark
- [x] scene canvas, array view, parameter controls와 Result export
- [x] Builder→Camera SHA-256 asset 재연결 왕복 테스트
@@ -220,7 +226,7 @@ hardware deadline과 정확도는 장치 adapter가 정해진 뒤 별도 측정
- [x] 복수 음원용 Experiment peak 검출 감도
- [x] 마이크 주파수 응답 합성·CPU/WebGPU 역보정 비교
- [x] 자유음장·shoebox 희소 반사/산란/회절/RT60 환경 선택
- [x] portable block/streaming localizer와 반복 DAS cache·메모리 상한
- [x] portable block/streaming localizer와 알고리즘별 반복 plan·메모리 상한
- [x] Camera 입력→설정→실행→확인의 Snapshot/Moving UI 재구성
- [x] `test.cmd` one-command headless browser 검증
- [ ] DCASE/MIMII 라이선스 검토와 adapter
@@ -229,5 +235,5 @@ hardware deadline과 정확도는 장치 adapter가 정해진 뒤 별도 측정
- [ ] DAMAS/CLEAN-SC 후처리
정확성 test를 먼저 통과시킨 뒤 UI 또는 최적화를 진행한다는 원칙은 유지한다. 추가 현실성 모델은
자유음장 DAS 결과를 바꾸지 않는 선택형 확장으로, 장치 연결은 portable localizer 앞의 adapter로
자유음장 DAS 기준 결과를 바꾸지 않는 선택형 확장으로, 장치 연결은 portable localizer 앞의 adapter로
추가한다.
+59 -24
View File
@@ -112,26 +112,48 @@
<section class="guide-section">
<div class="section-number">04</div>
<div>
<p class="eyebrow">Compute backend</p>
<h2>속도와 격자 선택</h2>
<p class="eyebrow">Localization &amp; compute</p>
<h2>알고리즘, 속도와 격자 선택</h2>
<p class="guide-copy">
Camera의 <strong>위치 추정 알고리즘</strong>에서 실행할 방법을 고릅니다. 처음에는
DAS로 전체 흐름을 확인한 뒤 같은 scene·scan·STFT에서 PHAT 계열을 실행해 비교하세요.
</p>
<div class="guide-table-wrap">
<table>
<thead><tr><th>알고리즘</th><th>raw map</th><th>Backend</th><th>특징</th></tr></thead>
<tbody>
<tr><td>DAS</td><td>선형 power</td><td>CPU · WebGPU</td><td>진폭과 위상을 유지하는 기준선</td></tr>
<tr><td>DAS-PHAT</td><td>무차원 score</td><td>CPU · WebGPU</td><td>STFT 크기를 정규화한 위상 기반 합산</td></tr>
<tr><td>GCC-PHAT</td><td>무차원 score</td><td>CPU</td><td>기준 마이크 pair의 TDOA 상관</td></tr>
</tbody>
</table>
</div>
<div class="guide-table-wrap">
<table>
<thead><tr><th>선택</th><th>동작</th><th>권장 상황</th></tr></thead>
<tbody>
<tr><td>Auto (GPU→CPU)</td><td>WebGPU를 먼저 시도하고 불가능하면 CPU 사용</td><td>기본 권장</td></tr>
<tr><td>WebGPU 강제</td><td>GPU 장치가 없거나 실패하면 fallback 없이 오류</td><td>GPU 비교·측정</td></tr>
<tr><td>CPU 강제</td><td>동일한 conventional DAS를 CPU에서 계산</td><td>호환성 확인·기준 비교</td></tr>
<tr><td>WebGPU 강제</td><td>DAS/DAS-PHAT를 GPU에서만 실행; 실패하면 fallback 없이 오류</td><td>GPU 비교·측정</td></tr>
<tr><td>CPU 강제</td><td>선택한 알고리즘을 CPU에서 계산</td><td>호환성 확인·기준 비교</td></tr>
</tbody>
</table>
</div>
<p class="guide-copy">
먼저 50×50을 권장합니다. 100×100은 4배, 200×200은 16배의 scan point를 계산합니다.
실행 중에는 취소할 수 있으며, 취소하거나 실패해도 이전 완료 결과는 유지됩니다.
GCC-PHAT는 CPU 전용이므로 Auto가 자동으로 CPU를 사용하고 WebGPU 강제는 비활성화됩니다.
각 band가 8개 미만의 FFT bin을 포함하면 협대역 상관 경고가 나오므로 band 폭이나 FFT
설정을 확인하세요.
</p>
<p class="guide-copy">
복수 음원에서 약한 peak가 빠지면 <strong>Peak 검출 임계값</strong>을 기본 25%에서
10–20%로 낮춰 보세요. 약한 후보와 함께 sidelobe 오검출도 늘 수 있으며, main lobe보다
가까운 같은 대역 음원은 conventional DAS에서 하나로 합쳐질 수 있습니다.
먼저 50×50을 권장합니다. 100×100은 4배, 200×200은 16배의 scan point를 계산합니다.
연산량 줄은 공통 STFT transform과 함께 DAS/DAS-PHAT의 complex MAC, 또는 GCC-PHAT의
two-pass cross-spectrum·pair×band inverse FFT·scan correlation lookup을 분리해
표시합니다. 이는 실행 시간 예측값이 아니며 장치와 입력에 따라 달라집니다. 실행 중에는
취소할 수 있고, 취소하거나 실패해도 이전 완료 결과는 유지됩니다.
</p>
<p class="guide-copy">
복수 음원에서 약한 peak가 빠지면 <strong>Peak 검출 임계값</strong>을 최강 map 값의
기본 25%에서 10–20%로 낮춰 보세요. 약한 후보와 함께 sidelobe 오검출도 늘 수 있으며,
main lobe보다 가까운 같은 대역 음원은 어느 알고리즘에서도 하나로 합쳐질 수 있습니다.
</p>
</div>
</section>
@@ -144,11 +166,12 @@
<p class="guide-copy">
고급 설정에서 자유음장 또는 흡음·일반·반사 강한 실내 preset을 고릅니다. 실내 모델은
1차 반사, scattering, occluder 회절과 제한된 RT60 tail을 희소 경로로 근사합니다.
Array의 마이크 응답 curve는 수신 신호에 적용하며 DAS 역보정 유무를 비교할 수 있습니다.
Array의 마이크 응답 curve는 수신 신호에 적용하며 위치추정 역보정 유무를 비교할 수 있습니다.
값과 한계는 <a href="/docs/acoustic-model.md">음향 모델 설명</a>에서 확인하세요.
</p>
<p class="guide-copy">
실제 동기식 마이크 채널은 Scene 합성 없이 portable CPU DAS에 넣을 수 있습니다.
실제 동기식 마이크 채널은 Scene 합성 없이 portable CPU 위치추정 코어에 넣고
DAS·DAS-PHAT·GCC-PHAT를 선택할 수 있습니다.
장치별 channel·clock·transport adapter는 별도로 필요하며 실시간 deadline은 해당
장치에서 <code>processingTimeMs ≤ deadlineMs</code>와 miss·drop 누계로 확인해야
합니다. <a href="/docs/realtime-porting.md">포팅 설명서</a>에 입력 형식, ring buffer,
@@ -163,19 +186,26 @@
<p class="eyebrow">Result JSON</p>
<h2>무엇이 저장되나요?</h2>
<dl class="result-list">
<div><dt>rawMap / displayMap</dt><dd>선형 power 원본과 화면 표시용 acoustic map</dd></div>
<div><dt>algorithm / backend</dt><dd>실제로 실행한 위치추정과 CPU·WebGPU 경로</dd></div>
<div><dt>rawMap / displayMap</dt><dd>DAS 선형 power 또는 PHAT 무차원 score와 화면 표시용 map</dd></div>
<div><dt>peaks</dt><dd>검출 위치, source 매칭, m·pixel 위치 오차</dd></div>
<div><dt>beamSpread</dt><dd>half-power lobe의 power 가중 공분산과 1× RMS ellipse</dd></div>
<div><dt>metrics</dt><dd>energy 비율, precision/recall, sidelobe, main-lobe 폭</dd></div>
<div><dt>beamSpread</dt><dd>half-power 또는 half-maximum 응답의 map 값 가중 공분산과 1× RMS ellipse</dd></div>
<div><dt>metrics</dt><dd>truth concentration, precision/recall, sidelobe, main-lobe 폭</dd></div>
<div><dt>evaluation</dt><dd>검출 threshold, 반경, tolerance 등 지표 계산 조건</dd></div>
<div><dt>signalStats</dt><dd>normalization·clipping과 실제 음향 모델·종류별 sparse path 수</dd></div>
<div><dt>timing / environment</dt><dd>backend, grid·scan 범위, 장치 정보와 구간별 시간</dd></div>
<div><dt>timing / environment</dt><dd>signal generation·localization 시간, backend, grid·scan과 장치 정보</dd></div>
<div><dt>inputHash</dt><dd>Array·Scene·Experiment 입력을 묶은 SHA-256 재현성 식별자</dd></div>
<div><dt>comparison-history</dt><dd>세션의 배열별 시간·위치 오차·PSR 요약을 한 번에 저장</dd></div>
<div><dt>comparison-history</dt><dd>배열·알고리즘·실제 backend별 시간·위치 오차·PSR 요약</dd></div>
</dl>
<aside class="callout">
화면의 <strong>빔 분포</strong> 1×/2× RMS contour는 acoustic power lobe의 모양입니다.
위치 확률, Gaussian 신뢰구간 또는 보정된 측정 불확실성이 아닙니다.
DAS map은 acoustic power 응답이고 PHAT map은 무차원 위치추정 score입니다. score와
화면의 <strong>빔 분포</strong> 1×/2× RMS contour는 위치 확률, Gaussian 신뢰구간 또는
보정된 측정 불확실성이 아닙니다. GCC-PHAT의 0–1 raw score도 pair·band·IFFT scale을
제거한 양의 상관 응답이지 “확률 80%” 같은 의미가 아닙니다.
</aside>
<aside class="callout">
<code>timingMs.localization</code>은 위치추정 시간을 기록합니다. 기존 Result 소비자와의
호환을 위해 <code>timingMs.beamforming</code>에도 같은 값이 alias로 남습니다.
</aside>
<aside class="callout">
평가는 tone 음원을 해당 주파수 대역이 tone 주파수를 포함할 때만 정답으로 사용합니다.
@@ -183,7 +213,7 @@
에너지를 자동 판정하는 기능은 아직 없습니다.
</aside>
<p class="guide-copy">
Camera에서 바꾼 Scene과 Experiment는 각각 저장할 수 있습니다. 완료 Result와 배열 비교
Camera에서 바꾼 Scene과 Experiment는 각각 저장할 수 있습니다. 완료 Result와 비교
기록도 별도 JSON으로 저장하세요. 저장하지 않은 항목이나 실행 중인 계산이 있으면
페이지를 닫을 때 브라우저가 확인합니다.
</p>
@@ -213,7 +243,11 @@
</details>
<details>
<summary>WebGPU가 없거나 실행이 너무 느립니다.</summary>
<p>Backend를 Auto 또는 CPU로 두고 50×50 grid부터 실행하세요. 최신 Chrome/Edge와 그래픽 드라이버도 확인하세요.</p>
<p>Backend를 Auto 또는 CPU로 두고 50×50 grid부터 실행하세요. GCC-PHAT는 원래 CPU 전용입니다. DAS/DAS-PHAT WebGPU를 쓴다면 최신 Chrome/Edge와 그래픽 드라이버도 확인하세요.</p>
</details>
<details>
<summary>GCC-PHAT에 8 bins 경고가 나옵니다.</summary>
<p>각 band가 포함하는 FFT bin이 너무 적으면 PHAT 상관이 협대역 주기성에 민감해집니다. 필요한 신호 대역 안에서 band를 넓히거나 FFT size를 조정하세요.</p>
</details>
<details>
<summary>두 번째 음원을 찾지 못합니다.</summary>
@@ -233,10 +267,11 @@
<h2>현재 모델의 범위</h2>
<p class="guide-copy">
micarrsim 1.0은 점 음원의 자유음장과 계산량을 제한한 shoebox 희소 경로, 마이크
주파수 응답과 conventional DAS를 제공합니다. 파동 방정식·고차 반사·재질별 흡음,
복잡한 mesh, sensor self-noise·비선형, ADC·clock, DAMAS/CLEAN-SC와 자동 장치
capture adapter는 포함하지 않습니다. 결과는 장비의 절대 성능 보증이 아니라 같은
조건에서 배열과 알고리즘을 비교하는 재현 가능한 기준입니다.
주파수 응답과 DAS·DAS-PHAT·GCC-PHAT를 제공합니다. 파동 방정식·고차 반사·재질별
흡음, 복잡한 mesh, sensor self-noise·비선형, ADC·clock, DAMAS/CLEAN-SC 후처리와
자동 장치 capture adapter는 포함하지 않습니다. 실행할 수 없는 DAMAS/CLEAN-SC
placeholder는 Camera UI에서 제거했습니다. 결과는 장비의 절대 성능 보증이 아니라
같은 조건에서 배열과 알고리즘을 비교하는 재현 가능한 기준입니다.
</p>
<p class="guide-copy">
원본 asset은 브라우저 메모리에서만 읽고 서버나 외부 네트워크로 보내지 않습니다.
+175 -35
View File
@@ -1,7 +1,7 @@
# 실측 다채널 위치추정 코어 포팅
`packages/acoustics-core/realtime-localizer.js`는 Scene 생성이나 음향 합성 없이 이미 sampling된
동기식 마이크 채널을 conventional CPU DAS에 직접 넣는다. 브라우저 DOM과 외부 package에
동기식 마이크 채널을 CPU DAS·DAS-PHAT·GCC-PHAT 위치 추정에 직접 넣는다. 브라우저 DOM과 외부 package에
의존하지 않는 ES module이므로, 실제 장치 adapter 뒤에 붙이거나 다른 프로젝트로 분리할 수 있다.
이 API는 ADC와 transport를 제어하지 않는다. 입력 channel을 획득·정렬·동기화한 다음 위치 map과
@@ -9,13 +9,16 @@
## 최소 복사 단위
직접 경로로 `realtime-localizer.js`를 import한다면 다음 여덟 파일과 상대 경로를 함께
직접 경로로 `realtime-localizer.js`를 import한다면 다음 열한 파일과 상대 경로를 함께
복사하면 된다.
```text
acoustics-core/
realtime-localizer.js
localization.js
localization-algorithms.js
das.js
gcc-phat.js
stft.js
fft.js
math.js
@@ -24,8 +27,10 @@ acoustics-core/
evaluation.js
```
`index.js`는 전체 core의 편의 export이므로 최소 포팅에는 필요하지 않다. 현재 portable API는
CPU 기준 구현이며 Camera의 WebGPU worker나 Scene·propagation 모듈을 요구하지 않는다.
`index.js`는 전체 core의 편의 export이므로 최소 포팅에는 필요하지 않다. Portable API와
`localization.js`의 analysis plan은 CPU 기준 구현이며 Camera의 WebGPU worker나 Scene·propagation
모듈을 요구하지 않는다. Camera backend 통합은 DAS와 DAS-PHAT에 WebGPU를 제공하지만 GCC-PHAT는
CPU only다. GCC-PHAT에 명시적 WebGPU를 요청하면 오류가 나며 `auto`는 CPU를 선택한다.
## 입력 계약
@@ -68,6 +73,58 @@ const array = {
`beamforming.maxCompensationDb`로 제한하며 기본 24 dB, 허용 범위 0–120 dB다.
응답 peak를 줄이는 attenuation과 phase 보상은 이 boost 상한에 의해 잘리지 않는다.
## 알고리즘 선택과 결과 scale
Portable plan은 top-level `algorithm`으로 위치 추정기를 선택한다. 이 값을 생략하면 기존 동작과
결과를 유지하는 `das`다. 기존 구성 형태인 `beamforming.algorithm`도 허용한다.
| `algorithm` | 핵심 계산 | `rawScale` | portable backend |
| --- | --- | --- | --- |
| `das` | frequency-domain delay-and-sum | `power` | CPU |
| `das-phat` | phase-only spectrum을 steering하는 DAS | `score` | CPU |
| `gcc-phat` | 한 reference mic에 대한 `M-1`개 GCC-PHAT 지연 scan | `score` | CPU only |
같은 array·scan·band·STFT로 알고리즘을 비교할 때도 알고리즘마다 별도 plan을 만든다.
```js
const dasPlan = createLocalizationPlan({
...commonConfig,
algorithm: "das",
});
const dasPhatPlan = createLocalizationPlan({
...commonConfig,
algorithm: "das-phat",
phatRelativeFloor: 1e-6,
});
const gccPhatPlan = createLocalizationPlan({
...commonConfig,
algorithm: "gcc-phat",
phatFloorRatio: 1e-6,
});
```
`phatRelativeFloor`는 DAS-PHAT의 channel spectrum 정규화 floor이고, `phatFloorRatio`는
GCC-PHAT pair cross spectrum 정규화 floor다. 둘 다 해당 묶음의 최대 magnitude에 대한 0~1
비율이며 기본값은 `1e-6`이다. GCC-PHAT는 `phatRelativeFloor`도 호환 alias로 받지만 새 코드에서는
`phatFloorRatio`를 쓰는 편이 의미가 명확하다. 값을 키우면 낮은 magnitude 성분의 기여가 줄어들지만
너무 크게 두면 유효한 broadband 정보도 약해질 수 있으므로 현장 데이터로 조정한다.
각 band는 `rawScale`을 반환한다. `rawValue`는 scale에 관계없이 읽을 수 있는 canonical peak
값이다. `rawPower`는 `rawScale === "power"`, `rawScore`는 `rawScale === "score"`인 peak에만
사용하는 편의 alias이며 두 필드가 항상 함께 있다고 가정하면 안 된다. `score`는 음향 power,
확률 또는 신뢰도가 아니라 동일 알고리즘·설정 안에서 상대 peak를 비교하는 비보정 값이다.
`power`도 capture amplitude와 calibration이 절대 단위로 교정되지 않았다면 W 또는 Pa²가 아니다.
GCC-PHAT의 raw score는 pair 수·band coefficient 수·IFFT 크기로 정규화한 0–1 양의 상관 응답이라
scan 범위와 microphone 수 자체에 따른 척도 변화는 피하지만, 확률로 보정된 confidence는 아니다.
GCC-PHAT에는 여러 FFT bin에 에너지가 있는 broadband 입력을 권장한다. Band당 8 bin 미만도 hard
error는 아니지만 단일 tone이나 좁은 band에서는 반복 correlation peak 때문에 위치가 모호할 수 있다.
Plan 생성 시 reference pair와 scan point에서 가능한 최대 절대 기하 지연을 계산하며, 이 값이
`fftSize / 2` sample을 넘으면 circular alias를 막기 위해 거절한다. 더 큰 `fftSize`를 쓰거나
배열·scan 기하를 줄여야 하며, 결과의 `maximumLagSamples`로 실제 상한을 기록할 수 있다.
## 공통 계획 만들기
Array, scan grid, band와 STFT 설정이 바뀌지 않는 동안 계획을 한 번 만들고 재사용한다.
@@ -94,6 +151,7 @@ const plan = createLocalizationPlan({
{ id: "machine", minHz: 900, maxHz: 1100, color: "#65f2bf" },
],
stft: { fftSize: 512, hopSize: 256 },
algorithm: "das", // "das" | "das-phat" | "gcc-phat"
speedOfSound: 343,
normalization: "peak",
compensateFrequencyResponse: true,
@@ -113,19 +171,63 @@ const plan = createLocalizationPlan({
- scan의 `columns`와 `rows`는 양의 정수
- `speedOfSound`는 양수
- `normalization`은 `peak` 또는 `none`
- `algorithm`은 `das`, `das-phat`, `gcc-phat` 중 하나
- `phatRelativeFloor`와 `phatFloorRatio`는 지정한 경우 0–1
- `maxCompensationDb`는 0–120 dB
- `peakDetection.thresholdRatio`는 0–1,
`minimumPeakDistanceM`는 0 이상,
`maximumDetections`는 1–128의 정수
계획은 거리 행렬, bin calibration, 임시 map과 허용되는 경우 steering matrix를 유지하는
stateful 객체다. 같은 계획으로 두 분석을 동시에 실행하지 말고, worker마다 별도 계획을 만든다.
Array·scan·band·STFT를 바꾸면 새 계획을 만들어야 한다.
계획은 알고리즘별 거리 행렬과 workspace를 유지하는 stateful 객체다. DAS 계열은 bin calibration,
임시 map과 허용되는 경우 steering matrix를 유지하고, GCC-PHAT는 reference mic 선택,
STFT·correlation workspace를 유지한다. 같은 계획으로 두 분석을 동시에 실행하지 말고 worker마다
별도 계획을 만든다. Array·scan·band·STFT·알고리즘을 바꾸면 새 계획을 만들어야 한다.
Portable plan의 generic cache는 `plan.analysisPlan`이며 기존 호출자를 위해 DAS 계열에는
`plan.dasPlan`, GCC-PHAT에는 `plan.gccPhatPlan` alias도 제공한다.
portable 계획의 기본 compute batch는 scan point 256개와 STFT transform 128개 단위로 event
loop에 양보한다. `computeOptions.pointBatchSize`와
`computeOptions.stftTransformBatchSize`로 조정할 수 있지만, 큰 batch는 throughput과
취소·UI 응답성의 trade-off가 있으므로 장치에서 함께 측정한다.
portable 계획의 기본 scan point batch는 DAS 계열 256개, GCC-PHAT 4,096개이고 STFT transform
batch는 128개다. GCC-PHAT cross-spectrum은 16,384 operation 단위로도 양보한다. 각 batch 경계에서
event loop에 기회를 주며 `computeOptions.pointBatchSize`,
`computeOptions.stftTransformBatchSize`, `computeOptions.crossSpectrumBatchSize`로 조정할
수 있다. 큰 batch는 throughput과 취소·UI 응답성의 trade-off가 있으므로 장치에서 함께 측정한다.
낮은 수준에서 직접 사용할 때는 `localization.js`의 generic plan과 compute 함수를 짝지어 쓴다.
Plan compatibility는 값이 같은 새 객체가 아니라 생성할 때 넘긴 `arrayDefinition`, `scene`,
`experiment`의 정확히 같은 객체를 요구한다.
```js
import {
computeLocalizationMap,
createLocalizationAnalysisPlan,
} from "./acoustics-core/localization.js";
const experiment = {
stft: { fftSize: 512, hopSize: 256, window: "hann" },
scan,
frequencyBands: bands,
beamforming: { algorithm: "das-phat", normalization: "peak" },
};
const scene = { speedOfSound: 343 };
const analysisPlan = createLocalizationAnalysisPlan({
sampleRate: 48_000,
arrayDefinition: array,
scene,
experiment,
});
for (const channels of capturedBlocks) {
const result = await computeLocalizationMap({
channels,
sampleRate: 48_000,
arrayDefinition: array,
scene,
experiment,
analysisPlan,
phatRelativeFloor: 1e-6,
});
consume(result);
}
```
## 한 블록 분석
@@ -144,22 +246,28 @@ const result = await localizeMultichannel({
});
for (const location of result.locations) {
console.log(location.bandId, location.position, location.rawPower);
console.log(
location.bandId,
location.position,
location.rawScale,
location.rawValue,
);
}
```
`result.locations`는 입력 band별 raw map의 local peak를 flat array로 담는다. 각 band
안에서는 power가 강한 순서이며 항목에는 `bandId`, band 내 `rank`, scan
`index`, `position`과 `rawPower`가 있다. 기본 threshold는 해당 band 최강
peak의 50%, 최소 peak 간격은 scan cell 대각선, 최대 검출 수는 band당 8개다. 여러 대역을
사용한다면 `bandId`로 묶어 해석한다.
안에서는 `rawValue`가 큰 순서이며 항목에는 `bandId`, band 내 `rank`, scan `index`,
`position`, `rawValue`, `rawScale`이 있다. DAS의 기존 `rawPower` 필드는 호환 alias로 유지된다.
Score 결과에서는 `rawValue`를 공통 필드로 읽고, `rawScore`가 포함된 peak 객체에서는 같은 값의
score alias로 사용할 수 있다. 기본 threshold는 해당 band 최강 peak의 50%, 최소 peak 간격은
scan cell 대각선, 최대 검출 수는 band당 8개다. 여러 대역을 사용한다면 `bandId`로 묶어 해석한다.
`peakDetection`은 후처리 위치 목록만 바꾸며 `result.bands[n].rawMap`과
`displayMap`은 그대로 보존한다. 같은 대역의 두 음원이 main lobe보다 가까우면 local
peak가 하나로 합쳐질 수 있고, threshold를 낮추면 sidelobe 후보도 늘 수 있다.
`sampleCount`와 `duration`도 결과에 함께 반환된다.
Block 결과의 `processingTimeMs`는 DAS 계산과 local-peak 검출에 걸린 monotonic wall
Block 결과의 `processingTimeMs`는 선택한 위치 추정 계산과 local-peak 검출에 걸린 monotonic wall
time이다. capture, adapter 변환과 결과 rendering 시간은 포함하지 않으므로 end-to-end latency는
호출자가 별도로 측정한다.
@@ -168,13 +276,14 @@ time이다. capture, adapter 변환과 결과 rendering 시간은 포함하지
## 연속 chunk 처리
`StreamingDasLocalizer` 또는 같은 factory인 `createStreamingLocalizer`는 고정 용량
ring buffer와 분석 workspace를 갖는다.
`StreamingLocalizer`는 알고리즘 중립 이름이며 기존 `StreamingDasLocalizer` class의 export alias다.
오래된 이름은 호환을 위해 유지된다. 두 이름과 `createStreamingLocalizer` factory는 모두 선택한
`algorithm`에 맞는 고정 용량 ring buffer와 분석 workspace를 만든다.
```js
import { createStreamingLocalizer } from "./acoustics-core/realtime-localizer.js";
import { StreamingLocalizer } from "./acoustics-core/realtime-localizer.js";
const localizer = createStreamingLocalizer({
const localizer = new StreamingLocalizer({
sampleRate: 48_000,
array,
scan: {
@@ -190,6 +299,8 @@ const localizer = createStreamingLocalizer({
{ id: "machine", minHz: 900, maxHz: 1100, color: "#65f2bf" },
],
stft: { fftSize: 512, hopSize: 256 },
algorithm: "das-phat",
phatRelativeFloor: 1e-6,
analysisWindowSamples: 4_800,
analysisHopSamples: 2_400,
maximumBufferedSamples: 19_200,
@@ -224,6 +335,11 @@ async function analysisLoop(signal) {
`deadlineMs = analysisHopSamples / sampleRate × 1000`과
`deadlineMet = processingTimeMs <= deadlineMs`를 반환한다.
`deadlineMet`는 완료된 한 창의 위치 추정 시간이 hop 예산 안이었는지를 나타내는 측정값이지,
해당 구성이 실시간이라는 사전 보장이 아니다. JIT warm-up, GC, 다른 thread의 부하를 포함해 목표
장치에서 연속 실행을 측정해야 한다. 이 값은 capture·transport·rendering을 포함하지 않으므로 전체
시스템 deadline은 adapter 경계에서 따로 계측한다.
## 지연과 backpressure 조정
서로 다른 hop이 두 층에 있다.
@@ -268,18 +384,42 @@ async function analysisLoop(signal) {
입력 retained byte는
`채널 수 × (maximumBufferedSamples + analysisWindowSamples) × 4`이며 할당 전에 검사한다.
계획은 거리 행렬, bin calibration, Hann window, Float64 FFT workspace와 임시 map을 재사용한다.
기본 retained data가 `maximumPlanBytes`를 넘으면 계획 생성을 거절한다. 전체 steering
matrix가 64 MiB 또는 계획 전체 한도를 넘으면 선택 bin 앞쪽부터 예산에 들어가는 만큼만 cache하고
나머지는 고정 microphone workspace에서 필요할 때 계산한다.
모든 계획은 거리 행렬과 STFT workspace를 재사용한다. DAS 계열은 bin calibration, Hann window,
임시 map과 steering cache를, GCC-PHAT는 선택 bin metadata, reference mic와 correlation workspace를
재사용한다. 기본 retained data가 `maximumPlanBytes`를 넘으면 할당 전에 계획 생성을 거절한다.
Plan 없는 GCC 단발 분석도 거리 행렬 할당 전에 전체 working set을 `maximumWorkingBytes`와 비교한다.
DAS의 전체 steering matrix가 64 MiB 또는 계획 전체 한도를 넘으면 선택 bin 앞쪽부터 예산에 들어가는
만큼만 cache하고 나머지는 고정 microphone workspace에서 필요할 때 계산한다.
192채널, 50×50 scan, 선택 bin 2개를 같은 계획으로 10회 분석하는 기준에서 구현상 거리 계산은
기존 DAS 호환 경로에서 192채널, 50×50 scan, 선택 bin 2개를 같은 계획으로 10회 분석하면 거리 계산은
4,800,000회에서 480,000회, steering point–microphone pair는 9,600,000회에서 960,000회로
줄어든다. 17,280,000회의 `sin/cos` 호출을 피하고 약 16.5 MiB를 유지한다. 자동 테스트는
부분 cache와 cache 없음의 raw/display `Float32` map 모든 원소가 bit-exact임을 확인한다.
각 결과의 `analysisCache`에는 cache된 bin capacity와 실제 cached/uncached 사용,
거리·steering pair, STFT transform·FFT butterfly·DAS complex MAC, workspace allocation·reuse
수가 들어가므로 설정별 최적화 효과를 계측할 수 있다.
각 결과의 `analysisCache`에는 공통 거리·STFT 통계와 알고리즘별 작업량이 들어간다. DAS 계열은
cached/uncached steering pair, FFT butterfly와 complex MAC을, GCC-PHAT는 cross-spectrum
operation, pair별 IFFT, scan lookup과 correlation workspace reuse를 기록한다.
연산량을 비교할 때 다음 기호를 사용한다.
- `P`: scan point 수
- `M`: microphone 수
- `T`: 한 분석창의 STFT frame 수
- `F`: 선택한 고유 FFT bin 수
- `F_b`: band `b`의 bin 수, `B`: band 수
- `N`: `fftSize`
DAS와 DAS-PHAT의 지배적인 steering 누산은 대략 `P × M × T × F` complex MAC이다. PHAT
정규화가 추가되지만 scan 차수는 같다. GCC-PHAT는 모든 mic의 forward STFT를 한 번 수행한 뒤
reference pair `M-1`개에 대해 다음 작업을 수행한다.
- magnitude 최대값 pass와 PHAT 누산 pass:
`2 × (M-1) × T × ΣF_b` cross-spectrum operation
- 각 pair·band의 길이 `N` correlation IFFT: `(M-1) × B`회
- 각 scan point의 fractional lag lookup: `P × (M-1) × B`회
따라서 GCC-PHAT의 scan lookup은 DAS의 `P × M × T × F`보다 작을 수 있지만 공짜는 아니다.
Forward STFT는 그대로 필요하고, 작은 grid·큰 `N`·많은 band에서는 pair별 IFFT나 2-pass cross
spectrum이 지배할 수 있다. `workload`와 실제 `processingTimeMs`를 함께 비교한다.
실시간 목표를 맞추는 조정 순서는 보통 다음과 같다.
@@ -293,15 +433,15 @@ matrix가 64 MiB 또는 계획 전체 한도를 넘으면 선택 bin 앞쪽부
7. capture와 분석을 별도 Worker/thread에 두고 결과만 UI 또는 제어 loop로 전달한다.
캐시 최적화와 위 수치는 반복 계산 감소의 근거이지 192채널·50×50 또는 다른 구성이 실시간이라는
보장이 아니다. 마이크 수, scan point, 선택 bin, STFT frame에 따른 주연산은 여전히 대략
`O(P × M × F × T)`다. `deadlineMisses` 또는 `droppedSamples`가 누적되면
grid·band bin·FFT/분석창 또는 update rate를 줄여야 한다. deadline은 위치추정 처리량 판정이며
capture와 출력까지 포함하는 시스템 deadline은 adapter에서 별도로 검증한다.
보장이 아니다. `deadlineMisses` 또는 `droppedSamples`가 누적되면 grid·band bin·FFT/분석창 또는
update rate를 줄여야 한다. 평균 한 번만 재지 말고 목표 장치의 실제 입력으로 warm-up 뒤 충분히 긴
구간의 최대값과 상위 percentile을 측정한다. Capture와 출력까지 포함하는 시스템 deadline은
adapter에서 별도로 검증한다.
## 취소와 오류 복구
`localizeMultichannel`과 `analyzeAvailable`에 `AbortSignal`을 전달하면 STFT와
DAS batch 경계에서 `AbortError`를 발생시키고 계획 lock을 해제한다.
알고리즘별 scan/correlation batch 경계에서 `AbortError`를 발생시키고 계획 lock을 해제한다.
Streaming 분석이 취소되거나 실패하면 `failedAnalyses`가 증가하지만 해당 hop을
소비하지 않는다. 실패한 창은 별도 analysis workspace에 보존되며, 다음
@@ -329,4 +469,4 @@ hop을 commit하고 sequence를 증가시킨다.
코어 뒤에서는 `locations`를 제어에 바로 쓰기 전에 현장 데이터로 검출 임계값, false peak,
latency와 위치 오차를 검증해야 한다. 현재 `locations`는 통계적 신뢰구간이 아니라 각 band
DAS map에서 threshold와 거리 억제를 통과한 grid local peak 후보들이다.
위치 map에서 threshold와 거리 억제를 통과한 grid local peak 후보들이다.