docs: guide acoustic modeling and realtime porting
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# micarrsim
|
||||
|
||||
192채널 MEMS 마이크 배열의 배치와 자유음장 DAS 음향 영상을 비교하는 로컬 브라우저
|
||||
192채널 MEMS 마이크 배열의 배치와 자유음장·저비용 실내 근사에서의 DAS 음향 영상을 비교하는 로컬 브라우저
|
||||
시뮬레이터입니다. Dataset Builder에서 녹음 파일·합성 신호와 이동 경로를 포함한 재현 가능한
|
||||
음향 장면을 만들고 Acoustic Camera에서 CPU 또는 WebGPU로 배열 응답, acoustic map과 위치 검출
|
||||
지표를 계산하거나 이동 장면을 시간축으로 재생합니다.
|
||||
@@ -46,7 +46,7 @@ powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File .\scripts\serve.
|
||||
1. 시작 화면에서 **Acoustic Camera**를 엽니다.
|
||||
2. 기본 192채널 rectangular 배열, single-tone scene과 50×50 experiment가 준비될 때까지
|
||||
기다립니다.
|
||||
3. **기본 예제 실행**을 누릅니다.
|
||||
3. Snapshot 카드의 **▶ Snapshot 실행**을 누릅니다.
|
||||
4. 계산이 끝나면 overlay와 평가 지표를 확인하고 **Result JSON**을 누릅니다.
|
||||
|
||||
실제 완료 시간은 브라우저와 장치에 따라 다릅니다. CPU fallback에서는 WebGPU보다 오래 걸릴 수
|
||||
@@ -54,8 +54,8 @@ powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File .\scripts\serve.
|
||||
|
||||
### 이동 음원 바로 보기
|
||||
|
||||
1. Acoustic Camera에서 **이동 예제**를 누릅니다.
|
||||
2. **라이브 준비**를 눌러 2 Hz acoustic map을 계산합니다.
|
||||
1. Acoustic Camera에서 **이동 예제 열기**를 누릅니다.
|
||||
2. Moving 카드의 **Map 준비**를 눌러 2 Hz acoustic map을 계산합니다.
|
||||
3. **재생**을 누르거나 시간 slider를 움직여 음원과 heatmap의 이동을 확인합니다.
|
||||
|
||||
이 기능은 trajectory가 있는 합성 Scene의 준정적 시간창 재생입니다. 실제 192채널 마이크 장치에서
|
||||
@@ -118,6 +118,23 @@ WebGPU 경로도 신호 합성과 STFT 일부는 CPU에서 수행합니다. grid
|
||||
100×100은 4배, 200×200은 16배입니다. 처음에는 50×50을 사용하고 해상도가 필요할 때만 높이세요.
|
||||
실행 시간은 microphone 수, scene 길이, FFT, 대역 폭과 장치에 함께 좌우됩니다.
|
||||
|
||||
## 음향 환경과 마이크 응답
|
||||
|
||||
Camera의 **고급 설정**에서 자유음장, 흡음된 실내, 일반 실내와 반사 강한 실내를 선택할 수
|
||||
있습니다. 실내 preset은 현재 Array·Scene·scan을 감싸는 shoebox room을 만들고 직접음에 다음의
|
||||
희소 경로를 추가합니다.
|
||||
|
||||
- 여섯 면의 1차 image-source 반사와 결정적 scattering
|
||||
- 축 정렬 occluder의 직접음 감쇠와 최단 corner 우회 회절
|
||||
- RT60·gain·tap 수로 제한한 deterministic diffuse tail
|
||||
|
||||
각 마이크의 Array JSON에는 선택적 `frequencyResponse` 측정 curve를 넣을 수 있습니다.
|
||||
합성기는 curve를 수신 채널에 적용하고, 기본 DAS는 알려진 curve를 역보정합니다. **마이크 주파수
|
||||
응답 처리**를 보정하지 않음으로 바꾸면 같은 curve를 합성에만 적용해 robustness를 비교합니다.
|
||||
응답 notch의 양의 역보정 boost는 기본 24 dB로 제한합니다.
|
||||
필드, preset별 경로 예산과 물리적 한계는
|
||||
[음향 환경과 마이크 응답 모델](docs/acoustic-model.md)에 정리되어 있습니다.
|
||||
|
||||
## 위치 추정, 빔 분포와 복수 음원
|
||||
|
||||
DAS 출력은 각 scan point의 상대 acoustic power map이며 위치 확률분포가 아닙니다. Camera는 map의
|
||||
@@ -143,13 +160,22 @@ index와 audio asset 재생 위치가 이어지도록 신호를 합성합니다.
|
||||
- 모델: 준정적 위치 변화이며 Doppler와 창 내부의 연속 전파 경로 변화는 포함하지 않음
|
||||
- 실제 장치: 물리 192채널 capture/stream gateway는 하드웨어 형식이 정해진 뒤 별도 adapter가 필요
|
||||
|
||||
Scene 합성 없이 동기식 `Float32Array` 마이크 채널을 바로 처리하는
|
||||
`createLocalizationPlan`, `localizeMultichannel`과 `StreamingDasLocalizer`가
|
||||
`packages/acoustics-core`에 분리되어 있습니다. 고정 ring buffer, oldest-first drop,
|
||||
반복 geometry·steering cache와 `AbortSignal` 취소를 제공하며 실제 capture 장치에는
|
||||
channel/clock/transport adapter만 별도로 연결합니다. 실시간 여부는 각 결과의
|
||||
`processingTimeMs <= deadlineMs`와 누적 `deadlineMisses`·`droppedSamples`로
|
||||
해당 장치에서 확인합니다. 복사할 파일과 운용 계약은
|
||||
[실측 다채널 위치추정 코어 포팅](docs/realtime-porting.md)을 참고하세요.
|
||||
|
||||
## Result JSON
|
||||
|
||||
Result JSON은 계약 버전 `1.0.0`의 `micarrsim.result` 문서입니다. 주요 내용은 다음과 같습니다.
|
||||
|
||||
- 실행한 `experimentId`, `arrayId`, `sceneId`, 실제 `backend`와 생성 시각
|
||||
- Array·Scene·Experiment의 canonical SHA-256인 `inputHash`
|
||||
- 정규화 전 peak, 적용 scale과 clipping 수를 담은 `signalStats`
|
||||
- 정규화 전 peak, 적용 scale, clipping 수와 음향 모델·희소 경로 수를 담은 `signalStats`
|
||||
- 전체·신호 생성·beamforming 시간인 `timingMs`
|
||||
- 주파수 대역별 선형 power `rawMap`, 표시용 `displayMap`, 검출 `peaks`
|
||||
- 위치 오차, precision/recall, truth 반경 내 energy, peak-to-sidelobe, main-lobe 폭을 계산하기
|
||||
@@ -166,10 +192,12 @@ noise source는 스펙트럼을 미리 알 수 없으므로 모든 band의 groun
|
||||
않습니다. 선택한 파일은 브라우저 메모리에서 읽으며 생성한 JSON/WAV는 브라우저 다운로드로만
|
||||
저장됩니다.
|
||||
|
||||
현재 음향 모델은 직접 경로와 거리 감쇠를 사용하는 자유음장 모델입니다. 반사, 잔향, 회절,
|
||||
차폐, 바람, 실제 MEMS 비선형과 장착 구조는 모델링하지 않습니다. Beamformer는 conventional
|
||||
DAS이며 UI의 DAMAS와 CLEAN-SC는 아직 사용할 수 없습니다. 따라서 결과는 배열·grid·대역의 상대
|
||||
비교와 소프트웨어 검증용이며, 현장 성능 인증이나 안전 판단을 대신하지 않습니다.
|
||||
음향 모델은 직접 경로의 자유음장과 1차 반사·scattering·occluder 회절·RT60 tail을 제한된
|
||||
경로로 계산하는 shoebox 근사를 제공합니다. 파동 방정식, 고차 반사, 재질별 주파수 의존 흡음,
|
||||
복잡한 mesh 회절, 바람, Doppler, 마이크 self-noise·비선형, ADC와 channel clock은 모델링하지
|
||||
않습니다. Beamformer는 conventional DAS이며 UI의 DAMAS와 CLEAN-SC는 아직 사용할 수 없습니다.
|
||||
따라서 결과는 배열·grid·대역의 상대 비교와 소프트웨어 검증용이며, 현장 성능 인증이나 안전
|
||||
판단을 대신하지 않습니다.
|
||||
|
||||
## 배열 예제
|
||||
|
||||
@@ -200,6 +228,8 @@ test.cmd
|
||||
## 저장 형식과 개발 문서
|
||||
|
||||
- [`docs/data-contracts.md`](docs/data-contracts.md): Array·Scene·Experiment·Result 계약
|
||||
- [`docs/acoustic-model.md`](docs/acoustic-model.md): 마이크 응답과 자유음장·shoebox 희소 근사
|
||||
- [`docs/realtime-porting.md`](docs/realtime-porting.md): 실측 다채널 위치추정 API 포팅
|
||||
- [`schemas/README.md`](schemas/README.md): JSON Schema 구성
|
||||
- [`docs/execution-plan.md`](docs/execution-plan.md): 구현 단계와 기술 결정
|
||||
- [`goal.md`](goal.md): 프로젝트 목표
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# Acoustic Camera
|
||||
|
||||
Scene과 마이크 배열로 자유음장 microphone signal을 합성하고, STFT와 conventional DAS를 통해
|
||||
주파수 대역별 acoustic map과 ground-truth 평가 지표를 만드는 브라우저 앱입니다.
|
||||
Scene과 마이크 배열로 자유음장 또는 저비용 shoebox microphone signal을 합성하고, STFT와
|
||||
conventional DAS를 통해 주파수 대역별 acoustic map과 ground-truth 평가 지표를 만드는 브라우저
|
||||
앱입니다. 정지 장면은 Snapshot, trajectory 장면은 Moving 흐름으로 분리되어 있습니다.
|
||||
|
||||
## 열기와 기본 예제
|
||||
## 열기와 Snapshot
|
||||
|
||||
저장소의 `start.cmd`를 실행한 뒤 시작 화면에서 **Acoustic Camera**를 선택하거나
|
||||
<http://127.0.0.1:4173/apps/acoustic-camera/>을 엽니다. `index.html`을 직접 열지 마세요.
|
||||
@@ -12,11 +13,11 @@ Scene과 마이크 배열로 자유음장 microphone signal을 합성하고, STF
|
||||
|
||||
1. 기본 fixture가 준비될 때까지 기다립니다.
|
||||
2. 192채널 rectangular 배열, single-tone scene과 50×50 grid를 그대로 둡니다.
|
||||
3. **기본 예제 실행**을 누릅니다.
|
||||
3. 상단 작업 순서에서 입력·설정 상태를 확인한 뒤 Snapshot 카드의 **▶ Snapshot 실행**을 누릅니다.
|
||||
4. 완료 후 overlay, 위치 오차와 timing을 확인하고 **Result JSON**을 저장합니다.
|
||||
|
||||
이동 장면을 바로 보려면 **이동 예제 → 라이브 준비 → 재생** 순서로 누릅니다. 기본 예제는
|
||||
24×18 grid와 2 Hz map rate를 사용해 CPU fallback에서도 준비량을 줄였습니다.
|
||||
이동 장면을 바로 보려면 Moving 카드에서 **이동 예제 열기 → Map 준비 → 재생** 순서로 누릅니다.
|
||||
이동 예제는 24×18 grid와 2 Hz map rate를 사용해 CPU fallback에서도 준비량을 줄였습니다.
|
||||
|
||||
## 사용자 입력 불러오기
|
||||
|
||||
@@ -51,6 +52,45 @@ Camera가 SHA-256을 계산해 manifest에 연결합니다. 같은 이름이라
|
||||
WebGPU가 감지돼도 실제 장치는 실행 시 확인합니다. 계산 중 **계산 취소**를 누르면 Worker를
|
||||
종료하며, 이미 완료된 이전 결과가 있으면 그대로 남습니다.
|
||||
|
||||
## 음향 환경과 마이크 주파수 응답
|
||||
|
||||
**고급 설정 → 음향 환경**에서 다음 preset을 선택합니다.
|
||||
|
||||
- **자유음장 · 직접음만**: 거리 지연과 `1/r` 감쇠의 기준선
|
||||
- **흡음된 실내 · 짧은 잔향**: 수신점당 최대 17개 sparse path
|
||||
- **일반 실내 · 균형**: 수신점당 최대 19개 sparse path
|
||||
- **반사 강한 실내**: 수신점당 최대 23개 sparse path
|
||||
|
||||
실내 preset은 현재 마이크, 정적·trajectory source와 scan corner를 포함하도록 직육면체 room을
|
||||
자동 추론합니다. 직접음에 여섯 면의 1차 image-source 반사, 결정적 scattering과 tap 수가 제한된
|
||||
RT60 diffuse tail을 추가합니다. Scene JSON에 사용자 `shoebox`와 축 정렬
|
||||
`occluders`를 넣으면 직접음 감쇠와 최단 corner 우회 회절도 계산합니다. 사용자 설정은
|
||||
dropdown에 **Scene 사용자 설정**으로 표시되고 preset 값으로 덮어쓰기 전까지 유지됩니다.
|
||||
|
||||
Array의 각 microphone에는 선택적 `frequencyResponse`를 넣을 수 있습니다.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "mic-001",
|
||||
"position": [-0.225, 0.225, 0],
|
||||
"frequencyResponse": [
|
||||
{ "frequencyHz": 100, "gainDb": -8, "phaseDeg": -12 },
|
||||
{ "frequencyHz": 1000, "gainDb": -0.7, "phaseDeg": -2 },
|
||||
{ "frequencyHz": 10000, "gainDb": 1.2, "phaseDeg": 8 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
curve는 합성 microphone channel에 항상 적용됩니다. **마이크 주파수 응답 처리**의 기본
|
||||
**측정 curve 적용 + 위치추정에서 역보정**은 CPU와 WebGPU DAS에서 알려진 gain/phase를
|
||||
역보정합니다. **측정 curve 적용 + 보정하지 않음**은 합성 응답은 유지하고 역보정만 꺼서 영향을
|
||||
비교합니다. curve가 없는 마이크는 기존 결과를 유지합니다. 응답 notch를 과도하게 증폭하지
|
||||
않도록 역보정의 양의 gain boost는 기본 24 dB로 제한하며, 사용자 Experiment JSON의
|
||||
`beamforming.maxCompensationDb`에서 0–120 dB로 지정할 수 있습니다.
|
||||
|
||||
모델 수식, preset 값, Scene/Array/Experiment 예와 제외 범위는
|
||||
[음향 환경과 마이크 응답 모델](../../docs/acoustic-model.md)을 참고하세요.
|
||||
|
||||
## Grid, STFT와 대역
|
||||
|
||||
50×50은 2,500 points입니다. 이를 1로 보면 100×100은 4배, 200×200은 16배 points를 계산합니다.
|
||||
@@ -87,11 +127,11 @@ WebGPU가 감지돼도 실제 장치는 실행 시 확인합니다. 계산 중 *
|
||||
|
||||
## 이동 음원 라이브
|
||||
|
||||
Scene source에 두 개 이상의 `trajectory` keyframe이 있으면 **라이브 준비**가 활성화됩니다.
|
||||
Scene source에 두 개 이상의 `trajectory` keyframe이 있으면 **Map 준비**가 활성화됩니다.
|
||||
Builder에서는 **시간에 따라 음원 이동**과 경로 미리보기 slider로 이 데이터를 만들 수 있습니다.
|
||||
|
||||
1. Map rate와 backend를 선택합니다. 처음에는 2 Hz와 Auto를 권장합니다.
|
||||
2. **라이브 준비**를 누릅니다. 현재 입력 snapshot을 최대 48개, map당 최대 2,500 points의 짧은
|
||||
2. **Map 준비**를 누릅니다. 현재 입력 snapshot을 최대 48개, map당 최대 2,500 points의 짧은
|
||||
분석창으로 계산합니다.
|
||||
3. **재생** 또는 시간 slider로 이동합니다. Heatmap은 이웃한 acoustic map 사이를 보간하고
|
||||
source 위치는 keyframe 사이를 연속 보간합니다.
|
||||
@@ -107,7 +147,8 @@ Scene 시간에 이어 붙입니다. 한 창 안에서는 중앙 시각의 위
|
||||
|
||||
- `experimentId`, `arrayId`, `sceneId`, `createdAt`, 실제 `backend`
|
||||
- 실행 입력 전체의 canonical SHA-256인 `inputHash`
|
||||
- `signalStats`: 정규화 전 peak, 적용 scale, clipped sample 수
|
||||
- `signalStats`: 정규화 전 peak, 적용 scale, clipped sample 수, 실제 음향 모델과 sparse
|
||||
path 전체·종류별 수
|
||||
- `timingMs`: 전체, signal generation과 beamforming 시간
|
||||
- band별 `rawMap` 선형 power, `displayMap`, 검출 `peaks`
|
||||
- 각 peak의 비보정 half-power 공분산과 RMS ellipse인 `beamSpread`
|
||||
@@ -120,9 +161,19 @@ z가 scan z와 다르면 평면에 투영한 x/y를 사용하므로 UI 경고와
|
||||
|
||||
## 해석 한계
|
||||
|
||||
신호 모델은 직접 경로와 거리 감쇠를 사용하는 자유음장입니다. 반사, 잔향, 회절, 차폐, 바람,
|
||||
센서 비선형과 장착 구조는 포함하지 않습니다. 배열 JSON의 sensitivity/phase calibration은 적용하지만
|
||||
결과는 상대 배열 비교와 알고리즘 검증용이며 실측 인증을 대신하지 않습니다.
|
||||
신호 모델은 직접 경로의 자유음장과 선택적인 shoebox 희소 근사입니다. shoebox는 1차 반사,
|
||||
deterministic scattering, 축 정렬 occluder의 직접음 감쇠·최단 corner 우회 회절과 제한된 RT60
|
||||
tail을 포함합니다. 파동 방정식, 고차 반사, 재질별 주파수 의존 흡음, 복잡한 mesh, 바람, Doppler,
|
||||
센서 self-noise·비선형, ADC·clock과 장착 구조는 포함하지 않습니다. 배열 JSON의
|
||||
sensitivity/phase/frequency-response calibration은 적용하지만 결과는 상대 배열 비교와 알고리즘
|
||||
검증용이며 실측 인증을 대신하지 않습니다.
|
||||
|
||||
실제 동기식 microphone `Float32Array`를 Scene 합성 없이 위치추정하려면
|
||||
[실시간 위치추정 코어 포팅](../../docs/realtime-porting.md)의
|
||||
`createLocalizationPlan`, `localizeMultichannel` 또는
|
||||
`StreamingDasLocalizer`를 사용하세요. 실시간성은 장치에서
|
||||
`processingTimeMs <= deadlineMs`와 `deadlineMisses`·`droppedSamples`를
|
||||
측정해 판정해야 합니다.
|
||||
|
||||
WebGPU 성능을 별도로 측정하려면 서버 실행 후
|
||||
<http://127.0.0.1:4173/benchmarks/webgpu-scaling.html>을 여세요.
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
# 음향 환경과 마이크 응답 모델
|
||||
|
||||
micarrsim의 음향 합성은 마이크 어레이 배치와 위치추정 알고리즘의 상대 성능을 빠르게 비교하기
|
||||
위한 모델이다. ADC나 마이크 전자회로까지 재현하는 하드웨어 시뮬레이터가 아니며, 자유음장과
|
||||
계산량이 제한된 실내 희소 경로 근사 중 하나를 선택한다.
|
||||
|
||||
## 신호 처리 범위
|
||||
|
||||
Snapshot 한 번의 처리 순서는 다음과 같다.
|
||||
|
||||
1. tone, noise 또는 audio asset으로 음원 신호를 만든다.
|
||||
2. 음원과 각 마이크 사이의 직접음 또는 희소 multipath 전달함수를 만든다.
|
||||
3. 마이크별 `sensitivityDb`, `phaseOffsetDeg`와 `frequencyResponse`를 수신 신호에 적용한다.
|
||||
4. 합성된 동기식 다채널 신호를 STFT하고 conventional DAS map을 계산한다.
|
||||
5. 설정에 따라 알려진 마이크 응답을 DAS에서 역보정하고 peak와 평가 지표를 구한다.
|
||||
|
||||
Scene의 `acoustics`를 생략하거나 `model: "free-field"`로 지정하면 기존 직접음 결과를
|
||||
유지한다. `model: "shoebox"`는 직육면체 방 안에서 경로 수가 제한된 근사를 추가한다.
|
||||
|
||||
## 자유음장
|
||||
|
||||
음원과 마이크 사이 거리 `r`, 음속 `c`에 대해 직접음의 지연은 `r / c`, 진폭은
|
||||
최소 거리 clamp를 적용한 `1 / r`이다. 분수 sample 지연은 zero-padded FFT의 주파수 영역
|
||||
위상 이동으로 계산한다.
|
||||
|
||||
이 모델은 반사가 거의 없는 조건의 기준선과 배열 자체의 aperture·형상·대역 효과를 비교할 때
|
||||
적합하다.
|
||||
|
||||
## Shoebox 희소 경로 근사
|
||||
|
||||
`shoebox` 모델은 source–microphone 쌍마다 다음 경로만 만든다.
|
||||
|
||||
- 직접음 1개. 축 정렬 occluder와 교차하면 `transmissionDb`만큼 감쇠한다.
|
||||
- 방의 여섯 면마다 최대 1개의 1차 image-source 반사.
|
||||
- 반사 에너지의 일부를 0.2–2 ms의 결정적 지연으로 나누는 scattering 경로.
|
||||
- 직접음을 막는 occluder마다 여덟 corner 중 최단 우회 거리로 만든 회절 경로 1개.
|
||||
- `tapCount`로 수를 제한하고 RT60에 걸쳐 -60 dB로 감쇠하는 결정적 diffuse tail.
|
||||
|
||||
같은 Scene seed와 source/microphone id에서는 scattering과 tail이 재현된다. 모든 희소 경로는
|
||||
source–microphone 쌍의 주파수 전달함수 하나로 먼저 합쳐지고 IFFT는 한 번만 수행된다. 경로 위상은
|
||||
recurrence로 갱신하고 1,024 bin마다 다시 고정해 bin마다 삼각함수를 반복하지 않는다.
|
||||
|
||||
Scene JSON의 사용자 설정 예는 다음과 같다.
|
||||
|
||||
```json
|
||||
{
|
||||
"acoustics": {
|
||||
"model": "shoebox",
|
||||
"room": {
|
||||
"min": [-3.0, -2.5, -0.5],
|
||||
"max": [3.0, 2.5, 4.0]
|
||||
},
|
||||
"reflections": {
|
||||
"order": 1,
|
||||
"wallGainDb": -8,
|
||||
"perWallDb": {
|
||||
"zMin": -14
|
||||
},
|
||||
"scattering": 0.25
|
||||
},
|
||||
"diffuseReverb": {
|
||||
"enabled": true,
|
||||
"rt60Seconds": 0.4,
|
||||
"gainDb": -20,
|
||||
"tapCount": 6
|
||||
},
|
||||
"occluders": [
|
||||
{
|
||||
"id": "screen",
|
||||
"min": [-0.4, -0.1, 0.6],
|
||||
"max": [0.4, 0.1, 1.8],
|
||||
"transmissionDb": -18,
|
||||
"diffractionGainDb": -12
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`room`은 shoebox에서 필수다. 정적 source, trajectory keyframe과 occluder는 room 안에 있어야
|
||||
하며 occluder id는 중복될 수 없다. 사용자 설정에서도 마이크 좌표가 room 안에 놓이도록 구성해야
|
||||
1차 반사가 생성된다.
|
||||
|
||||
## Camera 환경 preset과 비용
|
||||
|
||||
Camera의 **고급 설정 → 음향 환경**은 현재 geometry 전체가 들어가도록 room을 자동 추론하고 다음
|
||||
preset을 적용한다.
|
||||
|
||||
| 선택 | 벽 gain | scattering | RT60 / tail gain / taps | 수신점당 최대 경로 |
|
||||
| --- | ---: | ---: | ---: | ---: |
|
||||
| 자유음장 | — | — | — | 1 |
|
||||
| 흡음된 실내 | -14 dB | 0.15 | 0.2 s / -26 dB / 4 | 17 |
|
||||
| 일반 실내 | -8 dB | 0.25 | 0.4 s / -20 dB / 6 | 19 |
|
||||
| 반사 강한 실내 | -4 dB | 0.40 | 0.8 s / -14 dB / 10 | 23 |
|
||||
|
||||
표의 상한은 직접음 1개, 반사 6개, scattering 6개와 tail tap 수를 합한 값이다. 사용자 occluder는
|
||||
각 source–microphone 직접 경로를 실제로 막을 때 하나씩 추가된다. 대략적인 합성 경로 예산은
|
||||
`음원 수 × 마이크 수 × 수신점당 최대 경로`다. Camera는 실행 전에 이 예산을 표시한다.
|
||||
|
||||
반사·tail을 늘리면 beamforming의 `O(scan points × microphones × selected bins × STFT frames)`
|
||||
뿐 아니라 신호 합성의 전달함수 누산도 늘어난다. 처음에는 50×50 grid와 자유음장으로 기준선을
|
||||
만들고, 같은 입력에서 preset을 하나씩 바꾸어 비교하는 것이 좋다.
|
||||
|
||||
## 마이크 주파수 응답
|
||||
|
||||
각 마이크에는 2–64개의 측정점을 지정할 수 있다. `frequencyHz`는 0–1,000,000 Hz에서
|
||||
중복 없이 오름차순이어야 하며 `gainDb` 범위는 -120–120 dB, 선택적 `phaseDeg` 범위는
|
||||
-180–180°다.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "mic-001",
|
||||
"position": [-0.225, 0.225, 0],
|
||||
"sensitivityDb": 0.6,
|
||||
"phaseOffsetDeg": -1.5,
|
||||
"frequencyResponse": [
|
||||
{ "frequencyHz": 100, "gainDb": -8.0, "phaseDeg": -12 },
|
||||
{ "frequencyHz": 1000, "gainDb": -0.7, "phaseDeg": -2 },
|
||||
{ "frequencyHz": 8000, "gainDb": 2.1, "phaseDeg": 7 },
|
||||
{ "frequencyHz": 20000, "gainDb": -5.0, "phaseDeg": 20 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
측정점 사이 gain dB와 phase는 선형 보간한다. phase는 ±180° 경계에서 가장 짧은 방향으로
|
||||
보간하고 측정 범위 밖에서는 가장 가까운 끝점 값을 유지한다. 합성기는 이 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 상한에 의해 잘리지 않는다.
|
||||
|
||||
```json
|
||||
{
|
||||
"beamforming": {
|
||||
"algorithm": "das",
|
||||
"backend": "auto",
|
||||
"minimumDistance": 0.01,
|
||||
"normalization": "peak",
|
||||
"dynamicRangeDb": 45,
|
||||
"compensateFrequencyResponse": true,
|
||||
"maxCompensationDb": 24
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Camera의 **마이크 주파수 응답 처리**에서 **측정 curve 적용 + 보정하지 않음**을 고르면 합성에는
|
||||
curve가 남고 DAS 역보정만 꺼진다. 같은 배열에서 보정 유무의 robustness를 비교할 때 사용한다.
|
||||
curve가 없는 마이크는 0 dB, 0°로 취급한다.
|
||||
|
||||
## 결과에서 확인할 값
|
||||
|
||||
Result `signalStats`에는 다음 선택 필드가 들어간다.
|
||||
|
||||
- `acousticModel`: `free-field` 또는 `shoebox`
|
||||
- `acousticPathCount`: 실제 합성에 사용한 전체 경로 수
|
||||
- `acousticPathCounts`: `direct`, `reflection`, `scattering`,
|
||||
`diffraction`, `diffuse` 종류별 수
|
||||
|
||||
같은 입력과 seed에서 환경 preset만 바꿨다면 이 값과 위치 오차·precision/recall·PSR을 함께
|
||||
비교할 수 있다.
|
||||
|
||||
## 정확도 범위와 제외 사항
|
||||
|
||||
이 구현은 FDTD/FEM/BEM이나 room impulse response 측정치를 대신하지 않는다. 다음 항목은
|
||||
계산하지 않는다.
|
||||
|
||||
- 파동 방정식과 고차 반사
|
||||
- 재질별 주파수 의존 흡음과 복잡한 mesh 산란
|
||||
- edge 형상·파장에 따른 물리 회절
|
||||
- 음원과 마이크 지향성, 바람, Doppler
|
||||
- 절대 음압/SPL, 마이크 self-noise와 비선형 왜곡
|
||||
- preamp, anti-alias filter, ADC quantization, channel clock jitter·drift
|
||||
- 실제 장착 구조와 마이크 위치 공차
|
||||
|
||||
따라서 preset의 RT60이나 wall gain은 특정 방을 인증하는 값이 아니라 배열과 DAS가 multipath에
|
||||
얼마나 민감한지 비교하는 조건이다. 실제 장치 성능을 평가할 때는 같은 Array/scan/band 설정으로
|
||||
동기식 실측 채널을 [실시간 위치추정 코어](realtime-porting.md)에 입력하고 합성 결과와 나란히
|
||||
비교해야 한다. 주파수 응답 curve만으로 noise floor, distortion, ADC와 clock 오차까지 모사되지는
|
||||
않는다.
|
||||
+48
-3
@@ -18,8 +18,17 @@ scene JSON에는 192채널 원시 sample이나 원본 asset byte를 넣지 않
|
||||
`sha256` manifest로 참조한다. 사용자 컴퓨터의 절대 경로, URL, `..` 경로는 저장하지 않는다.
|
||||
|
||||
array의 `sensitivityDb`와 `phaseOffsetDeg`는 각 마이크 수신 channel에 적용되는 편차다. 합성기는
|
||||
이를 신호에 주입하고, beamformer는 알려진 calibration 값의 역 gain/phase를 적용한다. 따라서
|
||||
두 값이 0이 아닌 배열도 오류 없이 조용히 무시되지 않는다.
|
||||
이를 신호에 주입하고, beamformer는 알려진 calibration 값의 역 gain/phase를 적용한다. 선택적
|
||||
`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 상한을 그대로 적용하지 않는다.
|
||||
|
||||
## 로컬 asset 재연결
|
||||
|
||||
@@ -45,6 +54,26 @@ Camera 라이브 미리보기는 sample 경계에 맞춘 시간창 중앙에서
|
||||
창으로 Scene 시간을 다시 기준화해도 tone 위상, noise sample index와 audio asset 재생 위치는 원래
|
||||
절대 시간에 이어진다. 이는 준정적 근사이며 Doppler를 계약하지 않는다.
|
||||
|
||||
## 음향 환경
|
||||
|
||||
Scene의 선택적 `acoustics.model`은 `free-field` 또는 `shoebox`다. 필드를
|
||||
생략한 문서는 free-field로 해석해 이전 직접음 결과를 유지한다. free-field에는 사용되지 않는
|
||||
`room`, `reflections`, `diffuseReverb` 또는 `occluders`를 함께 둘 수 없다.
|
||||
|
||||
shoebox에는 `room.min`과 `room.max`가 필요하다. 각 축에서 min은 max보다 작아야 하며
|
||||
정적 source와 모든 trajectory keyframe은 room 안에 있어야 한다. `reflections.order`는
|
||||
0 또는 1이고, `wallGainDb`와 선택적 면별 `perWallDb`는 -60–0 dB,
|
||||
`scattering`은 0–1이다.
|
||||
|
||||
`diffuseReverb`는 `enabled`, 0.05–5 s의 `rt60Seconds`, -60–0 dB의
|
||||
`gainDb`와 1–32의 `tapCount`를 갖는다. 축 정렬 `occluders`는 최대 8개이며
|
||||
room 안의 min/max, 고유 id, -120–0 dB의 `transmissionDb`와
|
||||
`diffractionGainDb`를 갖는다.
|
||||
|
||||
이 계약은 직접음, 여섯 면의 1차 image-source, 결정적 scattering, 막힌 occluder별 최단 corner
|
||||
우회와 제한된 RT60 tail을 위한 것이다. 재질 mesh나 고차 room impulse response 계약은 아니다.
|
||||
세부 계산과 preset은 [음향 환경과 마이크 응답 모델](acoustic-model.md)에 설명한다.
|
||||
|
||||
## 오디오와 map 정규화
|
||||
|
||||
`scene.mix.normalization`은 마이크 신호 합성 전에 적용할 오디오 mix 정책이다. `none`은 원래
|
||||
@@ -81,6 +110,11 @@ threshold와 최소 거리, sidelobe 제외 반경, half-power 비율, dynamic r
|
||||
버전과 함께 저장한다. `environment`에는 실제 backend, microphone/grid 수, scan bounds와 z,
|
||||
가능한 경우 GPU adapter 및 fallback 정보를 기록한다.
|
||||
|
||||
`signalStats`는 정규화 전 peak, scale과 clipping 수 외에 선택적으로
|
||||
`acousticModel`, 실제 전체 `acousticPathCount`와 `acousticPathCounts`를
|
||||
기록한다. 종류별 key는 `direct`, `reflection`, `scattering`,
|
||||
`diffraction`과 `diffuse`다.
|
||||
|
||||
대역별 ground truth는 tone의 주파수가 band 안에 있을 때 그 tone source를 포함한다. noise와
|
||||
asset source는 현재 모든 band의 truth로 간주한다. 즉 녹음 asset의 실제 spectrum energy를
|
||||
대역별로 판정하는 계약은 아직 없으며, 이 가정은 실제 데이터 결과를 해석할 때 고려해야 한다.
|
||||
@@ -100,8 +134,19 @@ SHA-256을 계산해 scene의 해당 asset `sha256` 필드를 채운 상태로 c
|
||||
- 중복 id와 array/scene/experiment/result 참조
|
||||
- asset 종류와 안전한 상대 경로
|
||||
- Nyquist 및 시간·scan 범위
|
||||
- 마이크 주파수 응답점의 범위·정렬과 shoebox room/occluder 포함 관계
|
||||
- map shape와 source peak 매칭
|
||||
- band별 evaluation 파라미터와 허용 metric
|
||||
- 현재 비스트리밍 CPU backend의 512 MiB channel-buffer 예산
|
||||
- Snapshot channel·최대 sparse delay FFT working set의 512 MiB 예산
|
||||
|
||||
지원하지 않는 schemaVersion은 묵시적으로 변환하지 않고 오류로 알린다.
|
||||
|
||||
실측 channel을 받는 portable API는 저장 JSON 계약과 별도로 입력 Array를 같은 규칙으로 검사한다.
|
||||
CPU 분석 계획은 기본 512 MiB, 반복 steering cache는 64 MiB, streaming ring과 분석창은 기본
|
||||
256 MiB hard cap을 할당 전에 적용한다. 채널 순서와 streaming 상태 계약은
|
||||
[실측 다채널 위치추정 코어 포팅](realtime-porting.md)을 참고한다.
|
||||
|
||||
portable block 결과의 `processingTimeMs`는 DAS와 local-peak 검출 wall time이다. Streaming
|
||||
결과는 `deadlineMs`와 `deadlineMet`을 추가하고 상태에는 최근·최대 처리 시간,
|
||||
`deadlineMisses`와 `droppedSamples`를 누적한다. 이 runtime telemetry는
|
||||
`micarrsim.result` 저장 스키마의 필수 필드가 아니다.
|
||||
|
||||
+67
-37
@@ -2,15 +2,16 @@
|
||||
|
||||
> 구현 상태 · 2026-08-28
|
||||
>
|
||||
> 단계 0–4는 완료되어 설치 없는 브라우저 앱, CPU/WebGPU DAS, Builder→Camera asset 재연결,
|
||||
> 임의 오디오·이동 trajectory, 빔 분포·복수 peak 감도, 라이브 시간축 재생, 배열 비교와
|
||||
> Result 내보내기를 71개 자동 테스트로 검증했다. 단계 5는 실제 장치·공간 모델 고도화 범위다.
|
||||
> 단계 0–4와 단계 5의 저비용 음향 환경·마이크 응답·portable streaming core까지 완료했다.
|
||||
> 설치 없는 브라우저 앱, CPU/WebGPU DAS, Builder→Camera asset 재연결, 임의 오디오·이동
|
||||
> trajectory, 빔 분포·복수 peak 감도, 라이브 시간축 재생, 배열 비교와 Result 내보내기를
|
||||
> 전체 자동 테스트로 검증했다. 실제 장치 adapter와 현장 검증은 장치 계약이 필요한 후속 범위다.
|
||||
|
||||
## 1. 목표와 성공 기준
|
||||
|
||||
이 프로젝트는 정방형 평면에 배치할 192개 MEMS 마이크의 배열을 비교하기 위한 재현 가능한
|
||||
시뮬레이터를 만든다. 첫 버전은 자유음장(free field), 점 음원, 구면파 전파를 가정하며 다음 흐름을
|
||||
끝까지 수행해야 한다.
|
||||
시뮬레이터를 만든다. 기준선은 자유음장(free field), 점 음원과 구면파 전파이며, 같은 입력에서
|
||||
마이크 응답과 계산량이 제한된 shoebox multipath를 선택해 다음 흐름을 끝까지 수행한다.
|
||||
|
||||
1. 기계음 데이터와 ambient audio를 조합해 장면 및 ground truth를 생성한다.
|
||||
2. 배열 JSON과 장면 정보를 사용해 각 마이크에 도달하는 신호를 합성한다.
|
||||
@@ -42,8 +43,8 @@ CPU 기준 구현과 GPU 결과의 수치 오차가 합의된 허용 범위 안
|
||||
seed 고정, 합성 오디오와 ground truth manifest 내보내기
|
||||
- `acoustic-camera`: manifest와 배열 JSON 읽기, 마이크 신호 전파 합성, steering vector 생성,
|
||||
DAS 실행, overlay·수치 평가·실험 결과 내보내기
|
||||
- `acoustics-core`: 두 앱이 공유하는 좌표계, 단위, 구면 감쇠·지연, STFT, 정답 rasterization,
|
||||
지표 및 CPU reference
|
||||
- `acoustics-core`: 두 앱이 공유하는 좌표계, 단위, 구면 감쇠·희소 multipath·마이크 응답,
|
||||
STFT, 정답 rasterization, 지표, CPU reference와 실측 channel용 streaming localizer
|
||||
- `webgpu-kernels`: steering/DAS 커널, buffer layout, 장치 한도 검사와 benchmark
|
||||
|
||||
## 3. 데이터 계약
|
||||
@@ -52,43 +53,62 @@ JSON Schema를 먼저 고정하고 모든 길이는 m, 시간은 s, 주파수는
|
||||
명시한다.
|
||||
|
||||
- `array.schema.json`: schema version, 배열 폭·높이, 192개 마이크의 id와 `[x,y,z]`, 선택적
|
||||
감도·채널 보정값
|
||||
감도·위상·주파수 응답 보정값
|
||||
- `scene.schema.json`: 배경 이미지 크기와 실제 평면 변환, 음원별 위치·오디오 참조·시작/종료·
|
||||
gain, ambient 참조, sample rate, seed
|
||||
gain, ambient 참조, sample rate, seed, 선택적 자유음장·shoebox 환경
|
||||
- `experiment.schema.json`: 배열/장면 참조, AC-음원 거리, 음속, frequency band 목록과 색,
|
||||
STFT window/hop, scan bounds와 해상도, 알고리즘 및 normalization
|
||||
- `result.schema.json`: 실행 환경, timing, acoustic map, peak, 평가 지표, 입력 hash
|
||||
|
||||
오디오 파일은 manifest 옆의 상대 경로로 참조한다. 브라우저 내 객체 URL은 저장 형식에 포함하지
|
||||
않는다. 스키마에는 버전을 두고 향후 잔향 모델과 마이크 지향성을 선택 필드로 추가한다.
|
||||
않는다. 스키마에는 버전을 두며, 현재 잔향은 1차 반사·scattering·RT60 tail의 희소 근사로
|
||||
계약한다. 마이크 지향성과 더 복잡한 재질·mesh 모델은 후속 선택 필드로 남긴다.
|
||||
|
||||
## 4. 음향 및 연산 모델
|
||||
|
||||
첫 모델에서 음원 `s`와 마이크 `m` 사이 거리 `r`에 대해 지연은 `r/c`, 진폭은 기준 거리와
|
||||
최소 거리 clamp를 둔 `1/r`로 계산한다. 분수 sample 지연은 주파수 영역의 phase shift로 처리한다.
|
||||
복수 음원과 ambient를 선형 합산하되 clipping 전 peak와 최종 normalization을 기록한다.
|
||||
기준 자유음장은 음원 `s`와 마이크 `m` 사이 거리 `r`에 대해 지연 `r/c`와 최소 거리
|
||||
clamp를 둔 `1/r` 진폭을 사용한다. 선택적 shoebox는 직접음, 여섯 면의 1차 image-source,
|
||||
반사 에너지의 deterministic scattering, 축 정렬 occluder의 transmission·최단 corner 우회와
|
||||
tap 수가 제한된 RT60 tail만 추가한다. 경로를 source–microphone 전달함수 하나로 합치고 IFFT를
|
||||
한 번만 수행하며 위상 recurrence로 bin별 삼각함수 호출을 줄인다.
|
||||
|
||||
마이크별 2–64점 `frequencyResponse`는 dB와 wrapped phase를 보간해 수신 channel에 적용한다.
|
||||
기본 DAS는 sensitivity/phase와 frequency curve의 알려진 역응답을 적용하되 notch의 양의 boost는
|
||||
기본 24 dB로 제한하고, curve 보정만 끈 robustness 비교도 저장 가능하다. 복수 음원과 ambient를
|
||||
선형 합산하되 clipping 전 peak, 최종 normalization, 음향 모델과 실제 종류별 경로 수를 기록한다.
|
||||
|
||||
DAS는 STFT 후 각 scan point와 마이크 사이 거리로 steering phase를 만들고, 선택된 주파수 bin의
|
||||
정렬 합 power를 계산한다. 여러 주파수 구간은 각각 독립 map과 색을 갖는다. CPU reference를 먼저
|
||||
작성한 뒤 같은 buffer layout으로 WGSL 결과를 대조한다. DAMAS, CLEAN-SC는 인터페이스만 마련하고
|
||||
초기 완료 범위에서는 제외한다.
|
||||
|
||||
연산량의 주항은 대략 `O(P × M × F)`다. `M=192`, `P=50²=2,500`일 때 주파수 bin 하나당
|
||||
연산량의 주항은 대략 `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하여 기록하며 이론치만으로 약속하지 않는다.
|
||||
|
||||
실측·반복 분석용 CPU 계획은 거리 행렬, bin calibration, 임시 map과 허용되는 steering matrix를
|
||||
재사용한다. 192채널·50×50·2 bins를 10회 분석하는 기준에서 거리 계산은 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을 버리고 호출당 결과 수를 제한한다.
|
||||
각 portable 분석은 `processingTimeMs`를 기록하고 streaming은 hop 기반
|
||||
`deadlineMet`, `deadlineMisses`와 `droppedSamples`로 실제 장치의 지속
|
||||
처리 가능 여부를 측정한다. cache 수치만으로 실시간성을 보장하지 않는다.
|
||||
|
||||
## 5. UI 계획
|
||||
|
||||
`acoustic-camera`는 폭 600px 이상인 좌측 scene canvas에 기기 이미지, ground-truth hint,
|
||||
주파수별 acoustic overlay를 표시한다. 우측에는 배열 geometry를 좌표와 함께 정적 평면도로
|
||||
표시하고 그 아래에 제어 패널을 둔다. 작은 화면에서는 두 열을 세로로 쌓는다. 확대·회전 상호작용은
|
||||
향후 필요성 검증 뒤 추가한다.
|
||||
`acoustic-camera`는 **입력 → 설정 → 실행 → 확인**의 고정 작업 순서를 먼저 보여준다. 실행은
|
||||
정지 장면의 Snapshot과 trajectory의 Moving 카드로 나뉘며, 결과 canvas를 주 작업 영역으로 둔다.
|
||||
Scene·Array·Experiment·Assets 준비 상태와 실행 가능 여부를 가까이 표시하고, 비교 기록과 저장
|
||||
기능은 필요할 때 펼치는 보조 영역으로 내린다. 작은 화면에서는 한 열로 쌓는다.
|
||||
|
||||
필수 입력은 음원 수와 위치, AC 거리, 다중 주파수 구간과 색, ambient 파일, scan grid 크기,
|
||||
후처리 선택이다. 여기에 sample rate, 음속/온도, STFT window·hop, scan 영역, dynamic range,
|
||||
colormap/opacity, 음원별 gain·시작 시간, random seed, 정규화 방법을 추가한다. 잘못된 단위,
|
||||
Nyquist 초과 대역, WebGPU buffer 한도는 실행 전에 검증한다.
|
||||
일상적으로 바꾸는 backend, grid, 대역과 peak 감도는 설정 단계에 유지한다. 음속, seed, scan
|
||||
범위, STFT, 음향 환경 preset과 마이크 응답 보상은 **고급 설정**에 묶는다. 장문의 개발 요구나
|
||||
미구현 기능 홍보는 작업 화면에서 제거하고, 물리 모델·데이터 계약·포팅 설명은 문서로 연결한다.
|
||||
잘못된 단위, Nyquist 초과 대역, room 포함 관계와 CPU/GPU·신호 working-set 한도는 실행 전에
|
||||
검증한다.
|
||||
|
||||
## 6. 평가 방법
|
||||
|
||||
@@ -158,26 +178,31 @@ flattening이 모델 타당성을 훼손하는지 청취 및 spectrum 통계로
|
||||
|
||||
완료 조건: 사용자가 코드 수정 없이 배열 JSON 두 개를 같은 장면에서 비교할 수 있다.
|
||||
|
||||
### 단계 5 — 실제 데이터와 고도화 · 향후
|
||||
### 단계 5 — 현실성·포팅과 실제 데이터 · 부분 완료
|
||||
|
||||
- 라이선스 검토를 통과한 DCASE/MIMII adapter와 batch workflow 추가
|
||||
- microphone calibration·noise 모델 검토
|
||||
- 실제 192채널 장치의 channel/clock/sample 형식이 정해진 뒤 capture·stream adapter 추가
|
||||
- 필요 시 DAMAS/CLEAN-SC를 플러그인형 후처리로 추가
|
||||
- image-source method 또는 room impulse response 기반 잔향 모듈 추가
|
||||
- [x] 마이크별 gain·phase·frequency-response 합성 및 선택적 DAS 역보정
|
||||
- [x] 자유음장을 보존하는 선택형 shoebox 1차 반사·scattering·회절·RT60 tail
|
||||
- [x] Scene 합성에서 분리한 block/streaming multichannel 위치추정 API
|
||||
- [x] 반복 거리·calibration·steering cache, bounded ring과 메모리 hard cap
|
||||
- [ ] 라이선스 검토를 통과한 DCASE/MIMII adapter와 batch workflow
|
||||
- [ ] 실제 장치의 channel/clock/sample 형식에 맞춘 capture·transport adapter와 현장 검증
|
||||
- [ ] 필요 시 센서 self-noise·위치 공차와 DAMAS/CLEAN-SC 후처리
|
||||
|
||||
완료 조건: 자유음장 결과를 훼손하지 않고 propagation model을 선택해 재실행할 수 있다.
|
||||
현재 완료 조건은 기존 자유음장 결과를 유지한 채 propagation model과 응답 보상을 선택해
|
||||
재실행하고, 같은 CPU DAS를 실측 `Float32Array` channel에 직접 적용하는 것이다. 실제
|
||||
hardware deadline과 정확도는 장치 adapter가 정해진 뒤 별도 측정한다.
|
||||
|
||||
## 9. 주요 위험과 대응
|
||||
|
||||
- WebGPU 미지원/장치 한도: 시작 시 capability를 표시하고 CPU fallback 및 grid 상한을 둔다.
|
||||
- 좌표·phase 부호 오류: analytic tone test와 배열 대칭성 test를 먼저 만든다.
|
||||
- 브라우저 메모리: snapshot은 전체 192채널 signal buffer에 512 MiB 한도를 적용하고, 이동
|
||||
미리보기는 최대 48개·2,500 points의 짧은 시간창으로 제한한다. 실제 장시간 capture는 향후
|
||||
chunk/STFT streaming backend로 확장한다.
|
||||
- 브라우저 메모리: snapshot signal·최대 sparse delay FFT에 512 MiB 한도를 적용하고, 이동
|
||||
미리보기는 최대 48개·2,500 points의 짧은 시간창으로 제한한다. 장시간 입력은 256 MiB 기본
|
||||
한도의 fixed ring streaming core에서 처리하고 overload의 `droppedSamples`를 감시한다.
|
||||
- 데이터셋 부적합/라이선스: 로컬 asset 계약을 유지하고 synthetic fixture를 기본 test 입력으로
|
||||
쓴다. 실제 데이터 adapter는 단계 5의 라이선스 검토 뒤 추가한다.
|
||||
- 실제 AC와 simulation gap: 감도, 위상, 위치 오차를 이후 calibration/noise model로 주입 가능하게 한다.
|
||||
- 실제 AC와 simulation gap: gain·phase·주파수 응답과 희소 실내 환경을 비교 조건으로 제공하되,
|
||||
위치 공차·self-noise·ADC·clock은 실측 adapter와 현장 검증으로 다룬다.
|
||||
- post-processing이 기본 결과를 가림: raw DAS map과 후처리 map을 항상 별도로 보존한다.
|
||||
|
||||
## 10. 구현 결과와 다음 백로그
|
||||
@@ -193,11 +218,16 @@ flattening이 모델 타당성을 훼손하는지 청취 및 spectrum 통계로
|
||||
- [x] 이동 trajectory 계약·편집·시간창 합성·Camera 라이브 재생
|
||||
- [x] 비확률 beam spread RMS contour와 Result export
|
||||
- [x] 복수 음원용 Experiment peak 검출 감도
|
||||
- [x] 마이크 주파수 응답 합성·CPU/WebGPU 역보정 비교
|
||||
- [x] 자유음장·shoebox 희소 반사/산란/회절/RT60 환경 선택
|
||||
- [x] portable block/streaming localizer와 반복 DAS cache·메모리 상한
|
||||
- [x] Camera 입력→설정→실행→확인의 Snapshot/Moving UI 재구성
|
||||
- [x] `test.cmd` one-command headless browser 검증
|
||||
- [ ] DCASE/MIMII 라이선스 검토와 adapter
|
||||
- [ ] 실제 192채널 capture/stream adapter
|
||||
- [ ] 잔향·calibration·noise model
|
||||
- [ ] 실제 192채널 capture/transport adapter와 deadline·정확도 검증
|
||||
- [ ] 센서 self-noise·위치 공차·지향성 모델
|
||||
- [ ] DAMAS/CLEAN-SC 후처리
|
||||
|
||||
정확성 test를 먼저 통과시킨 뒤 UI 또는 최적화를 진행한다는 원칙은 유지한다. 단계 5 기능은
|
||||
자유음장 DAS 결과를 바꾸지 않는 선택형 확장으로 추가한다.
|
||||
정확성 test를 먼저 통과시킨 뒤 UI 또는 최적화를 진행한다는 원칙은 유지한다. 추가 현실성 모델은
|
||||
자유음장 DAS 결과를 바꾸지 않는 선택형 확장으로, 장치 연결은 portable localizer 앞의 adapter로
|
||||
추가한다.
|
||||
|
||||
+43
-18
@@ -18,6 +18,7 @@
|
||||
<a href="#quick-start">빠른 시작</a>
|
||||
<a href="#moving-live">이동 라이브</a>
|
||||
<a href="#full-workflow">내 데이터</a>
|
||||
<a href="#acoustic-model">환경·실측</a>
|
||||
<a href="#results">결과 해석</a>
|
||||
<a href="#troubleshooting">문제 해결</a>
|
||||
</nav>
|
||||
@@ -25,11 +26,10 @@
|
||||
|
||||
<main class="guide-shell">
|
||||
<section class="guide-hero">
|
||||
<p class="eyebrow">Local user guide</p>
|
||||
<h1>설치 없이 시작하는<br />음향 카메라 실험</h1>
|
||||
<h1>micarrsim 사용 설명서</h1>
|
||||
<p>
|
||||
Windows 10/11과 최신 Chrome 또는 Edge만 있으면 됩니다. 모든 입력과 계산은 이
|
||||
컴퓨터의 브라우저 안에서 처리되며 외부 서버로 업로드되지 않습니다.
|
||||
<code>start.cmd</code>로 열고, 장면을 실행하고, 결과를 저장하는 순서만 먼저 확인하세요.
|
||||
입력과 계산은 로컬 브라우저에서 처리됩니다.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
<ol class="guide-steps">
|
||||
<li><code>start.cmd</code>를 더블클릭합니다.</li>
|
||||
<li>홈에서 <a href="/apps/acoustic-camera/">Acoustic Camera</a>를 엽니다.</li>
|
||||
<li><strong>▶ 기본 예제 실행</strong>을 누릅니다.</li>
|
||||
<li>Snapshot 카드의 <strong>▶ Snapshot 실행</strong>을 누릅니다.</li>
|
||||
<li>완료되면 <strong>Result JSON</strong>을 눌러 다운로드 폴더에 저장합니다.</li>
|
||||
</ol>
|
||||
<aside class="callout">
|
||||
@@ -59,8 +59,8 @@
|
||||
<p class="eyebrow">Moving-source demo</p>
|
||||
<h2>이동 음원을 바로 재생하기</h2>
|
||||
<ol class="guide-steps">
|
||||
<li><a href="/apps/acoustic-camera/">Acoustic Camera</a>에서 <strong>이동 예제</strong>를 누릅니다.</li>
|
||||
<li><strong>라이브 준비</strong>를 눌러 기본 2 Hz map을 계산합니다.</li>
|
||||
<li><a href="/apps/acoustic-camera/">Acoustic Camera</a>에서 <strong>이동 예제 열기</strong>를 누릅니다.</li>
|
||||
<li>Moving 카드의 <strong>Map 준비</strong>를 눌러 기본 2 Hz map을 계산합니다.</li>
|
||||
<li><strong>재생</strong>을 누르거나 시간 slider를 움직입니다.</li>
|
||||
</ol>
|
||||
<aside class="callout">
|
||||
@@ -91,7 +91,7 @@
|
||||
Assets에서 Builder에 사용한 원본 이미지와 오디오를 한 번에 다시 선택합니다.
|
||||
준비 상태가 모두 초록색인지 확인합니다.
|
||||
</li>
|
||||
<li><strong>Auto (GPU→CPU)</strong>로 실행한 뒤 Result JSON을 저장합니다.</li>
|
||||
<li>Snapshot 또는 Moving을 선택해 <strong>Auto (GPU→CPU)</strong>로 실행한 뒤 Result JSON을 저장합니다.</li>
|
||||
</ol>
|
||||
|
||||
<aside class="callout callout--important" id="asset-reconnect">
|
||||
@@ -120,7 +120,7 @@
|
||||
<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>동일한 자유음장 DAS를 CPU에서 계산</td><td>호환성 확인·기준 비교</td></tr>
|
||||
<tr><td>CPU 강제</td><td>동일한 conventional DAS를 CPU에서 계산</td><td>호환성 확인·기준 비교</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -136,8 +136,29 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="guide-section" id="results">
|
||||
<section class="guide-section" id="acoustic-model">
|
||||
<div class="section-number">05</div>
|
||||
<div>
|
||||
<p class="eyebrow">Acoustics & measured input</p>
|
||||
<h2>환경과 마이크 응답 선택</h2>
|
||||
<p class="guide-copy">
|
||||
고급 설정에서 자유음장 또는 흡음·일반·반사 강한 실내 preset을 고릅니다. 실내 모델은
|
||||
1차 반사, scattering, occluder 회절과 제한된 RT60 tail을 희소 경로로 근사합니다.
|
||||
Array의 마이크 응답 curve는 수신 신호에 적용하며 DAS 역보정 유무를 비교할 수 있습니다.
|
||||
값과 한계는 <a href="/docs/acoustic-model.md">음향 모델 설명</a>에서 확인하세요.
|
||||
</p>
|
||||
<p class="guide-copy">
|
||||
실제 동기식 마이크 채널은 Scene 합성 없이 portable CPU DAS에 넣을 수 있습니다.
|
||||
장치별 channel·clock·transport adapter는 별도로 필요하며 실시간 deadline은 해당
|
||||
장치에서 <code>processingTimeMs ≤ deadlineMs</code>와 miss·drop 누계로 확인해야
|
||||
합니다. <a href="/docs/realtime-porting.md">포팅 설명서</a>에 입력 형식, ring buffer,
|
||||
메모리·취소와 backpressure 계약이 있습니다.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="guide-section" id="results">
|
||||
<div class="section-number">06</div>
|
||||
<div>
|
||||
<p class="eyebrow">Result JSON</p>
|
||||
<h2>무엇이 저장되나요?</h2>
|
||||
@@ -147,6 +168,7 @@
|
||||
<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>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>inputHash</dt><dd>Array·Scene·Experiment 입력을 묶은 SHA-256 재현성 식별자</dd></div>
|
||||
<div><dt>comparison-history</dt><dd>세션의 배열별 시간·위치 오차·PSR 요약을 한 번에 저장</dd></div>
|
||||
@@ -169,7 +191,7 @@
|
||||
</section>
|
||||
|
||||
<section class="guide-section" id="troubleshooting">
|
||||
<div class="section-number">06</div>
|
||||
<div class="section-number">07</div>
|
||||
<div>
|
||||
<p class="eyebrow">Troubleshooting</p>
|
||||
<h2>막혔을 때 확인할 것</h2>
|
||||
@@ -198,22 +220,23 @@
|
||||
<p>두 음원이 같은 주파수 대역인지 확인하고 Peak 검출 임계값을 낮추세요. gain 차이, main-lobe 폭보다 짧은 거리, grid 간격과 sidelobe는 threshold만으로 해결되지 않을 수 있습니다.</p>
|
||||
</details>
|
||||
<details>
|
||||
<summary>라이브 준비 버튼이 비활성화됩니다.</summary>
|
||||
<p>Scene에 시간 순서가 올바른 trajectory keyframe이 두 개 이상 있어야 합니다. Builder에서 시간에 따라 음원 이동을 켜거나 Camera의 이동 예제를 사용하세요.</p>
|
||||
<summary>Map 준비 버튼이 비활성화됩니다.</summary>
|
||||
<p>Scene에 시간 순서가 올바른 trajectory keyframe이 두 개 이상 있어야 합니다. Builder에서 시간에 따라 음원 이동을 켜거나 Camera의 이동 예제 열기를 사용하세요.</p>
|
||||
</details>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="guide-section">
|
||||
<div class="section-number">07</div>
|
||||
<div class="section-number">08</div>
|
||||
<div>
|
||||
<p class="eyebrow">Scope & privacy</p>
|
||||
<h2>현재 모델의 범위</h2>
|
||||
<p class="guide-copy">
|
||||
micarrsim 1.0은 점 음원, 구면 감쇠·분수 지연을 쓰는 자유음장 모델과 conventional
|
||||
DAS를 제공합니다. 실내 반사·잔향, 실제 마이크 위치 오차와 비선형성, DAMAS,
|
||||
CLEAN-SC, DCASE/MIMII 자동 adapter와 실제 192채널 capture stream은 현재 범위에 포함되지 않습니다. 결과는 실제
|
||||
장비의 절대 성능 보증이 아니라 같은 조건에서 배열을 비교하는 재현 가능한 기준입니다.
|
||||
micarrsim 1.0은 점 음원의 자유음장과 계산량을 제한한 shoebox 희소 경로, 마이크
|
||||
주파수 응답과 conventional DAS를 제공합니다. 파동 방정식·고차 반사·재질별 흡음,
|
||||
복잡한 mesh, sensor self-noise·비선형, ADC·clock, DAMAS/CLEAN-SC와 자동 장치
|
||||
capture adapter는 포함하지 않습니다. 결과는 장비의 절대 성능 보증이 아니라 같은
|
||||
조건에서 배열과 알고리즘을 비교하는 재현 가능한 기준입니다.
|
||||
</p>
|
||||
<p class="guide-copy">
|
||||
원본 asset은 브라우저 메모리에서만 읽고 서버나 외부 네트워크로 보내지 않습니다.
|
||||
@@ -226,6 +249,8 @@
|
||||
<a href="/README.md"><strong>전체 README</strong><span>설치·실행·구조·제약</span></a>
|
||||
<a href="/apps/dataset-builder/README.md"><strong>Builder 설명서</strong><span>source·ambient·Preview WAV</span></a>
|
||||
<a href="/apps/acoustic-camera/README.md"><strong>Camera 설명서</strong><span>backend·결과·비교</span></a>
|
||||
<a href="/docs/acoustic-model.md"><strong>음향 모델</strong><span>마이크 응답·반사·산란·회절</span></a>
|
||||
<a href="/docs/realtime-porting.md"><strong>실측 포팅</strong><span>block·streaming 위치추정 API</span></a>
|
||||
<a href="/docs/data-contracts.md"><strong>데이터 계약</strong><span>JSON Schema와 재현성</span></a>
|
||||
<a href="/docs/execution-plan.md"><strong>실행 계획</strong><span>구현 상태와 향후 단계</span></a>
|
||||
<a href="/tests/"><strong>브라우저 테스트</strong><span>단위·통합 테스트 실행</span></a>
|
||||
|
||||
@@ -0,0 +1,332 @@
|
||||
# 실측 다채널 위치추정 코어 포팅
|
||||
|
||||
`packages/acoustics-core/realtime-localizer.js`는 Scene 생성이나 음향 합성 없이 이미 sampling된
|
||||
동기식 마이크 채널을 conventional CPU DAS에 직접 넣는다. 브라우저 DOM과 외부 package에
|
||||
의존하지 않는 ES module이므로, 실제 장치 adapter 뒤에 붙이거나 다른 프로젝트로 분리할 수 있다.
|
||||
|
||||
이 API는 ADC와 transport를 제어하지 않는다. 입력 channel을 획득·정렬·동기화한 다음 위치 map과
|
||||
대역별 local peak 후보를 계산하는 경계부터 담당한다.
|
||||
|
||||
## 최소 복사 단위
|
||||
|
||||
직접 경로로 `realtime-localizer.js`를 import한다면 다음 여덟 파일과 상대 경로를 함께
|
||||
복사하면 된다.
|
||||
|
||||
```text
|
||||
acoustics-core/
|
||||
realtime-localizer.js
|
||||
das.js
|
||||
stft.js
|
||||
fft.js
|
||||
math.js
|
||||
frequency-response.js
|
||||
cooperative-yield.js
|
||||
evaluation.js
|
||||
```
|
||||
|
||||
`index.js`는 전체 core의 편의 export이므로 최소 포팅에는 필요하지 않다. 현재 portable API는
|
||||
CPU 기준 구현이며 Camera의 WebGPU worker나 Scene·propagation 모듈을 요구하지 않는다.
|
||||
|
||||
## 입력 계약
|
||||
|
||||
`channels`는 다음 조건을 만족해야 한다.
|
||||
|
||||
- `array.microphones`와 같은 순서·개수의 JavaScript 배열
|
||||
- 모든 원소가 `Float32Array`
|
||||
- 한 호출 또는 chunk 안에서 모든 채널의 sample 수가 동일
|
||||
- `sampleRate`가 모든 채널에서 동일하고 sample index가 같은 시각을 의미함
|
||||
- 값은 선형 진폭 sample이며, 장치 integer PCM은 adapter에서 일관된 scale의 float로 변환
|
||||
|
||||
코어는 channel timestamp, interleaved frame, endian, packet loss 또는 sample clock을 추론하지
|
||||
않는다. 잘못된 channel 순서나 채널 사이의 sample slip은 위치 오차로 그대로 나타난다.
|
||||
|
||||
Array에는 최소한 다음 정보가 필요하다.
|
||||
|
||||
```js
|
||||
const array = {
|
||||
microphones: [
|
||||
{
|
||||
id: "mic-0",
|
||||
position: [-0.10, 0.10, 0],
|
||||
sensitivityDb: 0,
|
||||
phaseOffsetDeg: 0,
|
||||
frequencyResponse: [
|
||||
{ frequencyHz: 100, gainDb: -6, phaseDeg: -5 },
|
||||
{ frequencyHz: 1000, gainDb: 0, phaseDeg: 0 },
|
||||
{ frequencyHz: 10000, gainDb: -2, phaseDeg: 8 },
|
||||
],
|
||||
},
|
||||
// 실제 capture channel 순서대로 계속
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
`frequencyResponse`는 선택 사항이다. 있으면 2–64개 점의 주파수가 엄격히 증가해야 한다.
|
||||
`compensateFrequencyResponse`의 기본값은 `true`이며, 알려진 curve와
|
||||
`sensitivityDb`/`phaseOffsetDeg`의 역보정을 steering에 포함한다. 응답 notch의
|
||||
양의 보상 boost는 top-level `maxCompensationDb` 또는
|
||||
`beamforming.maxCompensationDb`로 제한하며 기본 24 dB, 허용 범위 0–120 dB다.
|
||||
응답 peak를 줄이는 attenuation과 phase 보상은 이 boost 상한에 의해 잘리지 않는다.
|
||||
|
||||
## 공통 계획 만들기
|
||||
|
||||
Array, scan grid, band와 STFT 설정이 바뀌지 않는 동안 계획을 한 번 만들고 재사용한다.
|
||||
|
||||
```js
|
||||
import {
|
||||
createLocalizationPlan,
|
||||
localizeMultichannel,
|
||||
} from "./acoustics-core/realtime-localizer.js";
|
||||
|
||||
const plan = createLocalizationPlan({
|
||||
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 },
|
||||
speedOfSound: 343,
|
||||
normalization: "peak",
|
||||
compensateFrequencyResponse: true,
|
||||
maxCompensationDb: 24,
|
||||
peakDetection: {
|
||||
thresholdRatio: 0.5,
|
||||
// minimumPeakDistanceM을 생략하면 scan cell 대각선 길이
|
||||
maximumDetections: 8,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
주요 검증 조건은 다음과 같다.
|
||||
|
||||
- `fftSize`는 2 이상의 2의 거듭제곱이고 `hopSize <= fftSize`
|
||||
- band는 하나 이상이며 `0 <= minHz < maxHz < sampleRate / 2`
|
||||
- scan의 `columns`와 `rows`는 양의 정수
|
||||
- `speedOfSound`는 양수
|
||||
- `normalization`은 `peak` 또는 `none`
|
||||
- `maxCompensationDb`는 0–120 dB
|
||||
- `peakDetection.thresholdRatio`는 0–1,
|
||||
`minimumPeakDistanceM`는 0 이상,
|
||||
`maximumDetections`는 1–128의 정수
|
||||
|
||||
계획은 거리 행렬, bin calibration, 임시 map과 허용되는 경우 steering matrix를 유지하는
|
||||
stateful 객체다. 같은 계획으로 두 분석을 동시에 실행하지 말고, worker마다 별도 계획을 만든다.
|
||||
Array·scan·band·STFT를 바꾸면 새 계획을 만들어야 한다.
|
||||
|
||||
portable 계획의 기본 compute batch는 scan point 256개와 STFT transform 128개 단위로 event
|
||||
loop에 양보한다. `computeOptions.pointBatchSize`와
|
||||
`computeOptions.stftTransformBatchSize`로 조정할 수 있지만, 큰 batch는 throughput과
|
||||
취소·UI 응답성의 trade-off가 있으므로 장치에서 함께 측정한다.
|
||||
|
||||
## 한 블록 분석
|
||||
|
||||
짧은 녹음이나 adapter가 이미 만든 고정 길이 frame은 `localizeMultichannel`로 처리한다.
|
||||
|
||||
```js
|
||||
const controller = new AbortController();
|
||||
|
||||
const result = await localizeMultichannel({
|
||||
plan,
|
||||
channels, // Array<Float32Array>
|
||||
signal: controller.signal,
|
||||
onProgress(progress) {
|
||||
updateProgress(progress);
|
||||
},
|
||||
});
|
||||
|
||||
for (const location of result.locations) {
|
||||
console.log(location.bandId, location.position, location.rawPower);
|
||||
}
|
||||
```
|
||||
|
||||
`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`로 묶어 해석한다.
|
||||
|
||||
`peakDetection`은 후처리 위치 목록만 바꾸며 `result.bands[n].rawMap`과
|
||||
`displayMap`은 그대로 보존한다. 같은 대역의 두 음원이 main lobe보다 가까우면 local
|
||||
peak가 하나로 합쳐질 수 있고, threshold를 낮추면 sidelobe 후보도 늘 수 있다.
|
||||
`sampleCount`와 `duration`도 결과에 함께 반환된다.
|
||||
|
||||
Block 결과의 `processingTimeMs`는 DAS 계산과 local-peak 검출에 걸린 monotonic wall
|
||||
time이다. capture, adapter 변환과 결과 rendering 시간은 포함하지 않으므로 end-to-end latency는
|
||||
호출자가 별도로 측정한다.
|
||||
|
||||
계획을 전달하지 않고 `localizeMultichannel({ channels, ...config })` 형태로도 호출할 수
|
||||
있지만, 매번 geometry cache를 다시 만들므로 반복·실시간 처리에는 권장하지 않는다.
|
||||
|
||||
## 연속 chunk 처리
|
||||
|
||||
`StreamingDasLocalizer` 또는 같은 factory인 `createStreamingLocalizer`는 고정 용량
|
||||
ring buffer와 분석 workspace를 갖는다.
|
||||
|
||||
```js
|
||||
import { createStreamingLocalizer } from "./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: 2_400,
|
||||
maximumBufferedSamples: 19_200,
|
||||
maximumResultsPerCall: 1,
|
||||
});
|
||||
|
||||
function onCaptureChunk(channels) {
|
||||
// append()는 동기식으로 복사하고 즉시 상태를 반환한다.
|
||||
const state = localizer.append(channels);
|
||||
if (state.droppedSamples > 0) reportBackpressure(state);
|
||||
}
|
||||
|
||||
async function analysisLoop(signal) {
|
||||
const results = await localizer.analyzeAvailable({ signal });
|
||||
for (const result of results) {
|
||||
renderMap(result.bands[0].displayMap, result.centerTime);
|
||||
if (!result.deadlineMet) {
|
||||
reportOverload(result.processingTimeMs, result.deadlineMs);
|
||||
}
|
||||
}
|
||||
const state = localizer.getState();
|
||||
if (state.deadlineMisses > 0 || state.droppedSamples > 0) reportBackpressure(state);
|
||||
}
|
||||
```
|
||||
|
||||
`push(channels, options)`는 `append` 후 `analyzeAvailable`을 한 번 호출하는
|
||||
편의 메서드다. 임의의 capture chunk 경계로 나누어 넣어도 보존된 분석창의 결과는 동일하다.
|
||||
|
||||
각 streaming 결과에는 `sequence`, `startSample`, `endSample`,
|
||||
`startTime`, `endTime`과 `centerTime`이 추가된다. sample 위치는 localizer를
|
||||
만든 뒤 받은 첫 sample을 0으로 하는 절대 index다. 또한
|
||||
`deadlineMs = analysisHopSamples / sampleRate × 1000`과
|
||||
`deadlineMet = processingTimeMs <= deadlineMs`를 반환한다.
|
||||
|
||||
## 지연과 backpressure 조정
|
||||
|
||||
서로 다른 hop이 두 층에 있다.
|
||||
|
||||
- `stft.hopSize`: 한 분석창 안에서 spectrum을 평균하는 STFT frame 간격
|
||||
- `analysisHopSamples`: 다음 위치 map을 시작하는 간격
|
||||
|
||||
`analysisWindowSamples`는 `fftSize` 이상이어야 한다. 긴 창은 안정적인 spectrum
|
||||
평균에 유리하지만 결과 지연과 연산량이 늘어난다. 주파수 bin 간격은 주로 `fftSize`로
|
||||
결정된다.
|
||||
`analysisHopSamples`는 창보다 클 수 없으며 작게 할수록 map update rate와 중첩 연산량이
|
||||
늘어난다.
|
||||
|
||||
`maximumBufferedSamples`는 최소 한 분석창 이상이어야 한다. 새 chunk가 용량을 넘으면 가장
|
||||
오래된 sample부터 버리고 `droppedSamples`, `bufferStartSample`을 갱신한다. 호출마다
|
||||
반환할 결과 수는 기본 1개이고 구성 상한은 32개다. 한 번에 긴 backlog를 모두 계산하지 않도록
|
||||
`maximumResultsPerCall`을 작게 유지하고 event loop나 worker scheduler가 다시 호출하게 한다.
|
||||
|
||||
`getState()`으로 다음 값을 감시할 수 있다.
|
||||
|
||||
- `bufferedSamples`, `availableAnalysisCount`, `bufferStartSample`
|
||||
- `totalReceivedSamples`와 `droppedSamples`
|
||||
- `analysisAttempts`, `completedAnalyses`, `failedAnalyses`
|
||||
- `analysisDeadlineMs`, `lastProcessingTimeMs`,
|
||||
`maximumProcessingTimeMs`와 `deadlineMisses`
|
||||
- `busy`, `retainedInputBytes`, 다음 `sequence`
|
||||
|
||||
`deadlineMisses`는 완료됐지만 deadline을 넘긴 결과의 누계다. 취소·계산 오류는
|
||||
`failedAnalyses`에서 별도로 확인한다.
|
||||
|
||||
`analyzeAvailable()`을 같은 instance에서 중복 호출하면 오류가 난다. 분석 중에도
|
||||
`append()`는 안전하다. 분석기는 먼저 현재 창을 별도 workspace에 복사하며, 해당 hop은
|
||||
위치추정이 성공한 뒤에만 ring에서 소비한다.
|
||||
|
||||
## 메모리와 연산 최적화
|
||||
|
||||
기본 한도는 다음과 같다.
|
||||
|
||||
- CPU 계획 전체 `maximumPlanBytes`: 512 MiB
|
||||
- steering cache `maximumSteeringCacheBytes`: 64 MiB
|
||||
- streaming 입력 `maximumInputBufferBytes`: 256 MiB
|
||||
|
||||
입력 retained byte는
|
||||
`채널 수 × (maximumBufferedSamples + analysisWindowSamples) × 4`이며 할당 전에 검사한다.
|
||||
계획은 거리 행렬, bin calibration, Hann window, Float64 FFT workspace와 임시 map을 재사용한다.
|
||||
기본 retained data가 `maximumPlanBytes`를 넘으면 계획 생성을 거절한다. 전체 steering
|
||||
matrix가 64 MiB 또는 계획 전체 한도를 넘으면 선택 bin 앞쪽부터 예산에 들어가는 만큼만 cache하고
|
||||
나머지는 고정 microphone workspace에서 필요할 때 계산한다.
|
||||
|
||||
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
|
||||
수가 들어가므로 설정별 최적화 효과를 계측할 수 있다.
|
||||
|
||||
실시간 목표를 맞추는 조정 순서는 보통 다음과 같다.
|
||||
|
||||
1. scan grid를 50×50 이하로 시작한다.
|
||||
2. 실제 필요한 band만 좁게 두어 선택 FFT bin 수를 줄인다.
|
||||
3. 필요한 시간 분해능에 맞춰 `analysisHopSamples`을 정한다.
|
||||
4. 낮은 주파수 분해능이 필요하지 않다면 FFT와 분석창을 줄인다.
|
||||
5. 같은 장치에서 각 결과의 `processingTimeMs <= deadlineMs`가 지속되는지 확인한다.
|
||||
6. `getState()`의 `deadlineMisses`, backlog와 `droppedSamples`가
|
||||
증가하지 않는지 확인한다.
|
||||
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에서 별도로 검증한다.
|
||||
|
||||
## 취소와 오류 복구
|
||||
|
||||
`localizeMultichannel`과 `analyzeAvailable`에 `AbortSignal`을 전달하면 STFT와
|
||||
DAS batch 경계에서 `AbortError`를 발생시키고 계획 lock을 해제한다.
|
||||
|
||||
Streaming 분석이 취소되거나 실패하면 `failedAnalyses`가 증가하지만 해당 hop을
|
||||
소비하지 않는다. 실패한 창은 별도 analysis workspace에 보존되며, 다음
|
||||
`analyzeAvailable()` 호출이 같은 `startSample`과 sample을 먼저 재시도한다. 계산 중
|
||||
capture overflow로 ring의 시작점이 전진해도 이 pending 창은 workspace에 남는다. 성공하면 그때
|
||||
hop을 commit하고 sequence를 증가시킨다.
|
||||
|
||||
`onResult` callback은 성공 결과와 hop을 commit한 뒤 호출된다. consumer callback의 예외는
|
||||
호출자에게 전달되지만 성공한 창을 `failedAnalyses`로 세거나 재시도 상태로 되돌리지 않는다.
|
||||
|
||||
`reset()`은 idle 상태에서 ring, 시간 원점과 통계를 초기화한다. 분석 중에는 호출할 수 없다.
|
||||
|
||||
## 장치 adapter가 책임질 부분
|
||||
|
||||
포팅 대상 시스템은 코어 앞에서 다음을 구현해야 한다.
|
||||
|
||||
- 장치 발견, sample rate·channel 수 설정과 capture lifecycle
|
||||
- interleaved integer/float frame을 channel별 `Float32Array`로 변환
|
||||
- `array.microphones`와 물리 channel 번호의 고정 mapping
|
||||
- 공통 sample clock, channel framing, timestamp와 drift/slip 감시
|
||||
- packet loss·overflow 정책과 `droppedSamples` telemetry 결합
|
||||
- 필요 시 DC 제거, anti-alias/장치 filter와 일관된 amplitude scale
|
||||
- 실측한 gain·phase·주파수 응답을 Array 정의에 연결
|
||||
- worker scheduling, `AbortController`, UI/네트워크로의 결과 전달
|
||||
|
||||
코어 뒤에서는 `locations`를 제어에 바로 쓰기 전에 현장 데이터로 검출 임계값, false peak,
|
||||
latency와 위치 오차를 검증해야 한다. 현재 `locations`는 통계적 신뢰구간이 아니라 각 band
|
||||
DAS map에서 threshold와 거리 억제를 통과한 grid local peak 후보들이다.
|
||||
Reference in New Issue
Block a user