docs: add quick start and user guides
This commit is contained in:
@@ -1,27 +1,175 @@
|
|||||||
# micarrsim
|
# micarrsim
|
||||||
|
|
||||||
192채널 MEMS 마이크 배열의 배치와 음향 영상 성능을 평가하기 위한 WebGPU 기반 시뮬레이터입니다.
|
192채널 MEMS 마이크 배열의 배치와 자유음장 DAS 음향 영상을 비교하는 로컬 브라우저
|
||||||
|
시뮬레이터입니다. Dataset Builder에서 재현 가능한 음향 장면을 만들고 Acoustic Camera에서
|
||||||
|
CPU 또는 WebGPU로 배열 응답, acoustic map과 위치 검출 지표를 계산합니다.
|
||||||
|
|
||||||
현재는 설계 단계입니다. 프로젝트 목표는 [`goal.md`](goal.md), 구현 로드맵과 기술 결정은
|
별도 프로그램이나 패키지를 설치하지 않습니다. 모든 앱과 계산은 이 저장소와 브라우저 안에서
|
||||||
[`docs/execution-plan.md`](docs/execution-plan.md)를 참고하세요.
|
실행됩니다.
|
||||||
|
|
||||||
## 디렉터리
|
## 실행 환경
|
||||||
|
|
||||||
```text
|
- Windows 10/11
|
||||||
apps/
|
- 최신 Microsoft Edge 또는 Google Chrome
|
||||||
dataset-builder/ 음원 합성 및 ground truth 생성 UI
|
- 저장소에 포함된 Windows PowerShell과 브라우저 외 추가 런타임 불필요
|
||||||
acoustic-camera/ 전파, DAC 빔포밍, 평가 및 시각화 UI
|
- WebGPU가 없거나 장치를 열 수 없어도 CPU backend로 실행 가능
|
||||||
packages/
|
|
||||||
acoustics-core/ 공통 좌표계, 음향 모델, 평가 로직
|
## 가장 간단하게 시작하기
|
||||||
webgpu-kernels/ WGSL 기반 steering/DAC 연산
|
|
||||||
schemas/ 배열·장면·실험 JSON Schema
|
1. 저장소 최상위의 `start.cmd`를 더블클릭합니다.
|
||||||
assets/
|
2. 기본 브라우저에서 micarrsim 시작 화면이 열립니다.
|
||||||
array-geometries/ 마이크 배열 예제
|
3. 사용을 마치면 함께 열린 콘솔에서 `Ctrl+C`를 눌러 로컬 서버를 종료합니다.
|
||||||
sample-scenes/ 재배포 가능한 소규모 예제 장면
|
|
||||||
tests/
|
브라우저가 자동으로 열리지 않으면 <http://127.0.0.1:4173/>을 직접 여세요. HTML module과
|
||||||
unit/ 수치 모델 단위 테스트
|
Worker를 사용하므로 `index.html` 파일을 탐색기에서 직접 열어서는 안 됩니다.
|
||||||
integration/ 앱 간 데이터 계약과 기준 시나리오 테스트
|
|
||||||
docs/ 설계 및 실행 계획
|
다른 포트를 쓰거나 브라우저를 자동으로 열지 않으려면 명령 프롬프트에서 실행합니다.
|
||||||
|
|
||||||
|
```bat
|
||||||
|
start.cmd -Port 4174 -NoBrowser
|
||||||
```
|
```
|
||||||
|
|
||||||
원본 DCASE/MIMII 데이터와 생성된 대용량 결과는 저장소에 커밋하지 않습니다.
|
PowerShell에서 서버를 직접 실행할 수도 있습니다.
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File .\scripts\serve.ps1 -Port 4174 -NoBrowser
|
||||||
|
```
|
||||||
|
|
||||||
|
이 명령의 `ExecutionPolicy Bypass`는 실행 중인 PowerShell 프로세스에만 적용되며 시스템 정책을
|
||||||
|
영구 변경하지 않습니다. 해당 명령을 사용했다면 <http://127.0.0.1:4174/>을 여세요.
|
||||||
|
|
||||||
|
실행 후 브라우저용 빠른 시작과 문제 해결은 <http://127.0.0.1:4173/docs/>에서도 볼 수 있습니다.
|
||||||
|
|
||||||
|
### 30초 기본 예제
|
||||||
|
|
||||||
|
1. 시작 화면에서 **Acoustic Camera**를 엽니다.
|
||||||
|
2. 기본 192채널 rectangular 배열, single-tone scene과 50×50 experiment가 준비될 때까지
|
||||||
|
기다립니다.
|
||||||
|
3. **기본 예제 실행**을 누릅니다.
|
||||||
|
4. 계산이 끝나면 overlay와 평가 지표를 확인하고 **Result JSON**을 누릅니다.
|
||||||
|
|
||||||
|
실제 완료 시간은 브라우저와 장치에 따라 다릅니다. CPU fallback에서는 WebGPU보다 오래 걸릴 수
|
||||||
|
있습니다.
|
||||||
|
|
||||||
|
## 내 장면으로 전체 흐름 실행하기
|
||||||
|
|
||||||
|
### 1. Dataset Builder에서 Scene 만들기
|
||||||
|
|
||||||
|
1. 시작 화면에서 **Dataset Builder**를 엽니다.
|
||||||
|
2. 선택적으로 배경 이미지와 오디오를 추가합니다.
|
||||||
|
3. 음원을 추가해 canvas 또는 x/y/z 입력으로 위치를 정하고, tone·noise·audio asset과 ambient를
|
||||||
|
설정합니다.
|
||||||
|
4. 상태가 **저장 가능한 장면**이면 **Scene JSON 저장**을 누릅니다.
|
||||||
|
|
||||||
|
**Preview WAV 저장**은 source와 ambient를 공간 정보 없이 하나의 mono channel로 합친 청취
|
||||||
|
확인용 파일입니다. 192채널 마이크 신호나 beamforming 결과가 아닙니다.
|
||||||
|
|
||||||
|
### 2. Acoustic Camera에서 실행하기
|
||||||
|
|
||||||
|
1. 홈으로 돌아가 **Acoustic Camera**를 엽니다.
|
||||||
|
2. 다음 순서로 입력합니다: **Scene → Array → Experiment → Assets**.
|
||||||
|
3. **Scene**에서 Builder가 저장한 `.scene.json`을 선택합니다.
|
||||||
|
4. 기본 배열을 사용하지 않을 때만 **Array**에서 `.array.json`을 선택합니다.
|
||||||
|
5. 별도 실험 문서를 사용할 때만 **Experiment**에서 `.experiment.json`을 선택합니다.
|
||||||
|
6. Scene에 이미지나 audio asset이 있다면 **Assets**에서 Builder에 사용했던 원본 파일을 다시
|
||||||
|
선택합니다. 여러 파일을 한 번에 선택할 수 있습니다.
|
||||||
|
7. 입력 검증이 통과하면 backend와 scan grid를 정하고 실행합니다.
|
||||||
|
8. 완료 후 **Result JSON**을 저장합니다.
|
||||||
|
|
||||||
|
Array와 Scene을 먼저 불러와야 Experiment의 `arrayId`와 `sceneId` 관계를 안전하게 검증할 수
|
||||||
|
있습니다. Scene을 다시 불러오면 이전 asset 연결은 해제됩니다.
|
||||||
|
|
||||||
|
## Scene JSON과 로컬 assets
|
||||||
|
|
||||||
|
Scene JSON에는 원본 이미지나 오디오 byte가 들어가지 않습니다. 대신 asset id, 종류, 파일명과
|
||||||
|
SHA-256 manifest를 저장합니다. 브라우저는 새 페이지나 세션에서 로컬 파일 접근 권한을 유지할 수
|
||||||
|
없으므로 원본 파일을 다시 선택해야 합니다.
|
||||||
|
|
||||||
|
- Camera는 SHA-256으로 선택한 파일을 manifest에 다시 연결합니다.
|
||||||
|
- 같은 이름이라도 내용이 달라 hash가 일치하지 않으면 거절합니다.
|
||||||
|
- 로컬 asset 한 파일은 최대 128 MiB, 연결된 파일 합계는 최대 512 MiB입니다.
|
||||||
|
- 실제 decode되어 메모리에 유지되는 오디오 합계는 최대 192 MiB입니다.
|
||||||
|
- Scene JSON에는 원본 파일명이 기록될 수 있으므로 파일을 외부에 공유하기 전에 확인하세요.
|
||||||
|
|
||||||
|
## Backend와 scan grid
|
||||||
|
|
||||||
|
- **Auto (GPU→CPU)**: 권장값입니다. WebGPU adapter나 device를 사용할 수 없거나 지원되는 장치
|
||||||
|
한계를 넘으면 CPU로 계속합니다.
|
||||||
|
- **WebGPU 강제**: GPU 경로만 사용합니다. 장치를 열 수 없거나 한계를 넘으면 fallback하지 않고
|
||||||
|
오류를 표시합니다.
|
||||||
|
- **CPU 강제**: 비교와 호환성 확인을 위해 CPU DAS만 사용합니다.
|
||||||
|
|
||||||
|
WebGPU 경로도 신호 합성과 STFT 일부는 CPU에서 수행합니다. grid의 point 수는 50×50을 1로 보면
|
||||||
|
100×100은 4배, 200×200은 16배입니다. 처음에는 50×50을 사용하고 해상도가 필요할 때만 높이세요.
|
||||||
|
실행 시간은 microphone 수, scene 길이, FFT, 대역 폭과 장치에 함께 좌우됩니다.
|
||||||
|
|
||||||
|
## Result JSON
|
||||||
|
|
||||||
|
Result JSON은 계약 버전 `1.0.0`의 `micarrsim.result` 문서입니다. 주요 내용은 다음과 같습니다.
|
||||||
|
|
||||||
|
- 실행한 `experimentId`, `arrayId`, `sceneId`, 실제 `backend`와 생성 시각
|
||||||
|
- Array·Scene·Experiment의 canonical SHA-256인 `inputHash`
|
||||||
|
- 정규화 전 peak, 적용 scale과 clipping 수를 담은 `signalStats`
|
||||||
|
- 전체·신호 생성·beamforming 시간인 `timingMs`
|
||||||
|
- 주파수 대역별 선형 power `rawMap`, 표시용 `displayMap`, 검출 `peaks`
|
||||||
|
- 위치 오차, precision/recall, truth 반경 내 energy, peak-to-sidelobe, main-lobe 폭을 계산하기
|
||||||
|
위한 평가 파라미터와 `metrics`
|
||||||
|
|
||||||
|
평가에서 tone source는 tone 주파수가 포함되는 band의 ground truth로만 사용합니다. Audio asset과
|
||||||
|
noise source는 스펙트럼을 미리 알 수 없으므로 모든 band의 ground truth로 취급합니다. 이 가정은
|
||||||
|
결과를 실제 데이터와 해석할 때 고려해야 합니다.
|
||||||
|
|
||||||
|
## 개인정보와 모델의 한계
|
||||||
|
|
||||||
|
서버는 `127.0.0.1` loopback에만 열리고 외부 서비스로 파일을 업로드하거나 telemetry를 보내지
|
||||||
|
않습니다. 선택한 파일은 브라우저 메모리에서 읽으며 생성한 JSON/WAV는 브라우저 다운로드로만
|
||||||
|
저장됩니다.
|
||||||
|
|
||||||
|
현재 음향 모델은 직접 경로와 거리 감쇠를 사용하는 자유음장 모델입니다. 반사, 잔향, 회절,
|
||||||
|
차폐, 바람, 실제 MEMS 비선형과 장착 구조는 모델링하지 않습니다. Beamformer는 conventional
|
||||||
|
DAS이며 UI의 DAMAS와 CLEAN-SC는 아직 사용할 수 없습니다. 따라서 결과는 배열·grid·대역의 상대
|
||||||
|
비교와 소프트웨어 검증용이며, 현장 성능 인증이나 안전 판단을 대신하지 않습니다.
|
||||||
|
|
||||||
|
## 배열 예제
|
||||||
|
|
||||||
|
- `assets/array-geometries/rectangular-16x12.array.json`: 0.45 m 정사각 aperture의 16×12 기준 배열
|
||||||
|
- `assets/array-geometries/fermat-spiral-192.array.json`: 0.45 m 원형 aperture의 192채널 Fermat spiral
|
||||||
|
|
||||||
|
Camera의 **비교 배열** 선택으로 두 fixture를 같은 scene과 experiment에서 연속 실행할 수 있습니다.
|
||||||
|
상세한 생성법은 [`assets/array-geometries/README.md`](assets/array-geometries/README.md)를 참고하세요.
|
||||||
|
|
||||||
|
## 테스트와 benchmark
|
||||||
|
|
||||||
|
서버를 실행한 뒤 다음 주소를 브라우저에서 엽니다.
|
||||||
|
|
||||||
|
- 전체 browser test runner: <http://127.0.0.1:4173/tests/>
|
||||||
|
- WebGPU scaling benchmark: <http://127.0.0.1:4173/benchmarks/webgpu-scaling.html>
|
||||||
|
|
||||||
|
포트를 바꿨다면 URL의 포트도 같은 값으로 바꾸세요. 테스트 범위는
|
||||||
|
[`tests/unit/README.md`](tests/unit/README.md)와
|
||||||
|
[`tests/integration/README.md`](tests/integration/README.md), 기준 실측은
|
||||||
|
[`docs/benchmarks/README.md`](docs/benchmarks/README.md)에 설명되어 있습니다.
|
||||||
|
|
||||||
|
## 저장 형식과 개발 문서
|
||||||
|
|
||||||
|
- [`docs/data-contracts.md`](docs/data-contracts.md): Array·Scene·Experiment·Result 계약
|
||||||
|
- [`schemas/README.md`](schemas/README.md): JSON Schema 구성
|
||||||
|
- [`docs/execution-plan.md`](docs/execution-plan.md): 구현 단계와 기술 결정
|
||||||
|
- [`goal.md`](goal.md): 프로젝트 목표
|
||||||
|
|
||||||
|
원본 DCASE/MIMII 데이터, 사용자가 선택한 assets와 생성된 대용량 결과는 저장소에 커밋하지
|
||||||
|
않습니다.
|
||||||
|
|
||||||
|
## 디렉터리 구조
|
||||||
|
|
||||||
|
```text
|
||||||
|
apps/ Dataset Builder와 Acoustic Camera UI
|
||||||
|
assets/ 192채널 배열과 synthetic Scene/Experiment fixture
|
||||||
|
benchmarks/ 현재 브라우저에서 실행하는 WebGPU scaling 측정
|
||||||
|
docs/ 사용자 안내, 데이터 계약, 실행 계획과 기준 실측
|
||||||
|
packages/ 음향 수치 모델, 계약 검증, UI, WebGPU kernel
|
||||||
|
schemas/ Array·Scene·Experiment·Result JSON Schema
|
||||||
|
scripts/ 무설치 로컬 서버와 fixture 생성기
|
||||||
|
tests/ 브라우저 unit·integration·end-to-end test
|
||||||
|
start.cmd Windows 원클릭 실행 진입점
|
||||||
|
```
|
||||||
|
|||||||
@@ -1,3 +1,92 @@
|
|||||||
# Acoustic Camera
|
# Acoustic Camera
|
||||||
|
|
||||||
scene과 마이크 배열을 불러와 자유음장 전파, WGSL DAS 빔포밍, overlay 및 평가를 수행하는 앱입니다.
|
Scene과 마이크 배열로 자유음장 microphone signal을 합성하고, STFT와 conventional DAS를 통해
|
||||||
|
주파수 대역별 acoustic map과 ground-truth 평가 지표를 만드는 브라우저 앱입니다.
|
||||||
|
|
||||||
|
## 열기와 기본 예제
|
||||||
|
|
||||||
|
저장소의 `start.cmd`를 실행한 뒤 시작 화면에서 **Acoustic Camera**를 선택하거나
|
||||||
|
<http://127.0.0.1:4173/apps/acoustic-camera/>을 엽니다. `index.html`을 직접 열지 마세요.
|
||||||
|
|
||||||
|
가장 빠른 확인 방법은 다음과 같습니다.
|
||||||
|
|
||||||
|
1. 기본 fixture가 준비될 때까지 기다립니다.
|
||||||
|
2. 192채널 rectangular 배열, single-tone scene과 50×50 grid를 그대로 둡니다.
|
||||||
|
3. **기본 예제 실행**을 누릅니다.
|
||||||
|
4. 완료 후 overlay, 위치 오차와 timing을 확인하고 **Result JSON**을 저장합니다.
|
||||||
|
|
||||||
|
## 사용자 입력 불러오기
|
||||||
|
|
||||||
|
문서는 다음 순서로 불러오는 것이 안전합니다.
|
||||||
|
|
||||||
|
1. **Scene**: `.scene.json`
|
||||||
|
2. **Array**: 선택 사항인 `.array.json`; 또는 **비교 배열** preset 사용
|
||||||
|
3. **Experiment**: 선택 사항인 `.experiment.json`
|
||||||
|
4. **Assets**: Scene이 참조하는 원본 이미지와 오디오
|
||||||
|
|
||||||
|
Scene을 불러오면 현재 Experiment의 `sceneId`와 scan plane을 Scene에 맞추고 기존 asset 연결을
|
||||||
|
해제합니다. Array를 불러오면 현재 Experiment의 `arrayId`를 맞춥니다. 별도 Experiment는 현재
|
||||||
|
Scene과 Array를 참조해야 하므로 마지막에 불러오세요.
|
||||||
|
|
||||||
|
Scene JSON에는 원본 파일 byte가 없습니다. **Assets**에서 Builder에 사용한 파일을 다시 선택하면
|
||||||
|
Camera가 SHA-256을 계산해 manifest에 연결합니다. 같은 이름이라도 내용이 바뀌면 거절됩니다.
|
||||||
|
브라우저를 다시 열거나 Scene을 바꿀 때도 재선택해야 합니다.
|
||||||
|
|
||||||
|
- asset 한 파일: 최대 128 MiB
|
||||||
|
- 연결된 asset 합계: 최대 512 MiB
|
||||||
|
- decode되어 메모리에 유지되는 오디오 합계: 최대 192 MiB
|
||||||
|
|
||||||
|
파일은 외부로 업로드되지 않고 브라우저 메모리에서만 처리됩니다.
|
||||||
|
|
||||||
|
## Backend 선택
|
||||||
|
|
||||||
|
- **Auto (GPU→CPU)**: 권장. WebGPU를 먼저 시도하고 adapter/device 부재나 지원되는 장치 한계가
|
||||||
|
있으면 CPU로 계속합니다.
|
||||||
|
- **WebGPU 강제**: GPU만 사용하며 운용 오류가 나도 CPU로 fallback하지 않습니다.
|
||||||
|
- **CPU 강제**: CPU 수치 기준이나 WebGPU가 없는 환경에서 사용합니다.
|
||||||
|
|
||||||
|
WebGPU가 감지돼도 실제 장치는 실행 시 확인합니다. 계산 중 **계산 취소**를 누르면 Worker를
|
||||||
|
종료하며, 이미 완료된 이전 결과가 있으면 그대로 남습니다.
|
||||||
|
|
||||||
|
## Grid, STFT와 대역
|
||||||
|
|
||||||
|
50×50은 2,500 points입니다. 이를 1로 보면 100×100은 4배, 200×200은 16배 points를 계산합니다.
|
||||||
|
처음에는 50×50을 권장합니다. 실행량은 microphone 수, scene 길이, FFT/hop과 선택 band의 bin
|
||||||
|
수에도 비례하므로 실제 시간 배율은 장치마다 다릅니다.
|
||||||
|
|
||||||
|
각 band는 id, 최소/최대 Hz와 overlay 색을 가집니다. Nyquist를 넘거나 겹치는 id 등 계약 오류는
|
||||||
|
실행 전에 표시됩니다. 현재 후처리는 raw DAS만 지원하며 DAMAS와 CLEAN-SC 항목은 비활성화되어
|
||||||
|
있습니다.
|
||||||
|
|
||||||
|
## 화면과 배열 비교
|
||||||
|
|
||||||
|
- acoustic overlay의 투명도와 표시할 band를 선택할 수 있습니다.
|
||||||
|
- **Ground truth**를 켜면 source 위치와 검출 peak를 비교할 수 있습니다.
|
||||||
|
- rectangular와 Fermat spiral preset을 같은 Scene/Experiment에서 연속 실행하면 **배열 비교 기록**에
|
||||||
|
backend, timing과 평가 요약이 남습니다.
|
||||||
|
- 입력을 바꾼 뒤 화면에 남은 결과는 이전 실행 snapshot이며 UI가 이전 설정 결과임을 표시합니다.
|
||||||
|
|
||||||
|
## Result JSON
|
||||||
|
|
||||||
|
**Result JSON**은 검증을 통과한 `micarrsim.result` 문서만 저장합니다.
|
||||||
|
|
||||||
|
- `experimentId`, `arrayId`, `sceneId`, `createdAt`, 실제 `backend`
|
||||||
|
- 실행 입력 전체의 canonical SHA-256인 `inputHash`
|
||||||
|
- `signalStats`: 정규화 전 peak, 적용 scale, clipped sample 수
|
||||||
|
- `timingMs`: 전체, signal generation과 beamforming 시간
|
||||||
|
- band별 `rawMap` 선형 power, `displayMap`, 검출 `peaks`
|
||||||
|
- 평가 설정과 `peakToSidelobeDb`, `energyInsideTruthRatio`, `precision`, `recall`,
|
||||||
|
`mainLobeWidthM`
|
||||||
|
|
||||||
|
Tone source는 그 주파수가 band 범위 안에 있을 때만 해당 band의 ground truth입니다. Audio asset과
|
||||||
|
noise source는 스펙트럼을 사전에 판별하지 않으므로 모든 band의 ground truth로 평가합니다. Source
|
||||||
|
z가 scan z와 다르면 평면에 투영한 x/y를 사용하므로 UI 경고와 위치 오차를 함께 해석하세요.
|
||||||
|
|
||||||
|
## 해석 한계
|
||||||
|
|
||||||
|
신호 모델은 직접 경로와 거리 감쇠를 사용하는 자유음장입니다. 반사, 잔향, 회절, 차폐, 바람,
|
||||||
|
센서 비선형과 장착 구조는 포함하지 않습니다. 배열 JSON의 sensitivity/phase calibration은 적용하지만
|
||||||
|
결과는 상대 배열 비교와 알고리즘 검증용이며 실측 인증을 대신하지 않습니다.
|
||||||
|
|
||||||
|
WebGPU 성능을 별도로 측정하려면 서버 실행 후
|
||||||
|
<http://127.0.0.1:4173/benchmarks/webgpu-scaling.html>을 여세요.
|
||||||
|
|||||||
@@ -31,11 +31,14 @@
|
|||||||
<h1 id="workspace-title">음향 장면</h1>
|
<h1 id="workspace-title">음향 장면</h1>
|
||||||
<p class="workspace-meta" id="scene-meta">장면을 불러오고 있습니다.</p>
|
<p class="workspace-meta" id="scene-meta">장면을 불러오고 있습니다.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="workspace-tools" aria-label="입력 파일">
|
<div class="workspace-import">
|
||||||
<label class="button button--ghost file-button">Scene<input id="scene-input" type="file" accept="application/json,.json" /></label>
|
<div class="workspace-tools" aria-label="입력 파일">
|
||||||
<label class="button button--ghost file-button">Array<input id="array-input" type="file" accept="application/json,.json" /></label>
|
<label class="button button--ghost file-button" title="1. Scene JSON을 먼저 선택하세요.">Scene<input id="scene-input" type="file" accept="application/json,.json" /></label>
|
||||||
<label class="button button--ghost file-button">Experiment<input id="experiment-input" type="file" accept="application/json,.json" /></label>
|
<label class="button button--ghost file-button" title="2. 사용자 배열이 있을 때 Array JSON을 선택하세요.">Array<input id="array-input" type="file" accept="application/json,.json" /></label>
|
||||||
<label class="button button--ghost file-button">Assets<input id="asset-input" type="file" accept="audio/*,image/*,.wav,.flac,.mp3,.ogg,.m4a,.png,.jpg,.jpeg,.webp,.svg" multiple /></label>
|
<label class="button button--ghost file-button" title="3. 현재 Scene과 Array를 참조하는 Experiment JSON을 선택하세요.">Experiment<input id="experiment-input" type="file" accept="application/json,.json" /></label>
|
||||||
|
<label class="button button--ghost file-button" title="4. Scene에 사용한 원본 파일을 다시 선택하세요.">Assets<input id="asset-input" type="file" accept="audio/*,image/*,.wav,.flac,.mp3,.ogg,.m4a,.png,.jpg,.jpeg,.webp,.svg" multiple /></label>
|
||||||
|
</div>
|
||||||
|
<p class="import-order">권장 순서 · Scene → Array → Experiment → Assets</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,9 @@
|
|||||||
.workspace-heading .eyebrow, .section-heading .eyebrow, .array-heading .eyebrow { margin-bottom: 7px; }
|
.workspace-heading .eyebrow, .section-heading .eyebrow, .array-heading .eyebrow { margin-bottom: 7px; }
|
||||||
.workspace-heading h1 { margin-bottom: 4px; font-size: clamp(1.9rem, 4vw, 3.2rem); letter-spacing: -.055em; }
|
.workspace-heading h1 { margin-bottom: 4px; font-size: clamp(1.9rem, 4vw, 3.2rem); letter-spacing: -.055em; }
|
||||||
.workspace-meta, .section-copy { margin: 0; color: var(--muted); font-size: .76rem; line-height: 1.55; }
|
.workspace-meta, .section-copy { margin: 0; color: var(--muted); font-size: .76rem; line-height: 1.55; }
|
||||||
|
.workspace-import { display: grid; gap: 7px; justify-items: end; }
|
||||||
.workspace-tools { justify-content: flex-end; }
|
.workspace-tools { justify-content: flex-end; }
|
||||||
|
.import-order { margin: 0; color: var(--muted); font-size: .64rem; }
|
||||||
.scene-canvas-card { position: relative; overflow: hidden; min-height: 520px; border: 1px solid var(--line); border-radius: 22px; background: #07110f; box-shadow: var(--shadow); }
|
.scene-canvas-card { position: relative; overflow: hidden; min-height: 520px; border: 1px solid var(--line); border-radius: 22px; background: #07110f; box-shadow: var(--shadow); }
|
||||||
#scene-canvas { display: block; width: 100%; height: clamp(520px, 64vh, 760px); }
|
#scene-canvas { display: block; width: 100%; height: clamp(520px, 64vh, 760px); }
|
||||||
.canvas-message { position: absolute; top: 32px; left: 50%; display: grid; width: min(440px, calc(100% - 48px)); gap: 6px; color: var(--muted); text-align: center; pointer-events: none; transform: translateX(-50%); }
|
.canvas-message { position: absolute; top: 32px; left: 50%; display: grid; width: min(440px, calc(100% - 48px)); gap: 6px; color: var(--muted); text-align: center; pointer-events: none; transform: translateX(-50%); }
|
||||||
@@ -109,6 +111,7 @@
|
|||||||
.camera-header, .workspace-heading { align-items: stretch; }
|
.camera-header, .workspace-heading { align-items: stretch; }
|
||||||
.camera-header { position: static; display: flex; flex-direction: column; }
|
.camera-header { position: static; display: flex; flex-direction: column; }
|
||||||
.header-status, .header-actions, .workspace-tools { justify-content: flex-start; width: 100%; }
|
.header-status, .header-actions, .workspace-tools { justify-content: flex-start; width: 100%; }
|
||||||
|
.workspace-import { justify-items: start; }
|
||||||
.header-actions .button, .workspace-tools .button { flex: 1 1 calc(50% - 5px); }
|
.header-actions .button, .workspace-tools .button { flex: 1 1 calc(50% - 5px); }
|
||||||
.scene-workspace { padding: 18px; }
|
.scene-workspace { padding: 18px; }
|
||||||
.workspace-heading { flex-direction: column; }
|
.workspace-heading { flex-direction: column; }
|
||||||
|
|||||||
@@ -1,3 +1,65 @@
|
|||||||
# Dataset Builder
|
# Dataset Builder
|
||||||
|
|
||||||
음원, ambient audio, 이미지상의 위치를 조합해 scene audio와 ground truth manifest를 만드는 앱입니다.
|
이미지 평면에 source ground truth를 배치하고 tone, noise, 로컬 audio asset과 ambient를 조합해
|
||||||
|
재현 가능한 `micarrsim.scene` JSON을 만드는 브라우저 앱입니다.
|
||||||
|
|
||||||
|
## 열기
|
||||||
|
|
||||||
|
저장소의 `start.cmd`를 실행한 뒤 시작 화면에서 **Dataset Builder**를 선택하거나
|
||||||
|
<http://127.0.0.1:4173/apps/dataset-builder/>을 엽니다. `index.html`을 직접 열면 module과 Worker가
|
||||||
|
동작하지 않습니다.
|
||||||
|
|
||||||
|
## 기본 사용법
|
||||||
|
|
||||||
|
1. **배경 이미지**를 선택하거나 이미지 없이 grid를 사용합니다.
|
||||||
|
2. **오디오 추가**로 source 또는 ambient에 사용할 파일을 연결합니다.
|
||||||
|
3. **+ 음원 추가**를 누르고 카드를 선택한 뒤 canvas를 클릭하거나 x/y/z를 입력합니다.
|
||||||
|
4. source 종류를 설정합니다.
|
||||||
|
- `Tone`: 주파수와 위상을 갖는 합성 정현파
|
||||||
|
- `Noise`: seed로 재현되는 white 또는 pink noise
|
||||||
|
- `Audio asset`: 선택한 파일의 channel을 사용하며 짧은 파일은 반복 가능
|
||||||
|
5. 필요하면 ambient 종류와 gain을 설정합니다.
|
||||||
|
6. 검증 카드가 **저장 가능한 장면**이면 **Scene JSON 저장**을 누릅니다.
|
||||||
|
|
||||||
|
길이, sample rate, 음속, seed와 mix 정규화도 Scene JSON에 저장됩니다. 이미지가 있으면 표시
|
||||||
|
평면의 x/y 범위, z와 image pixel 크기가 ground truth 좌표 변환에 사용됩니다.
|
||||||
|
|
||||||
|
## 미리 듣기와 Preview WAV
|
||||||
|
|
||||||
|
**미리 듣기**는 현재 mix를 브라우저에서 재생합니다. **Preview WAV 저장**은 모든 source와
|
||||||
|
ambient를 공간 정보 없이 합친 mono PCM16 확인용 파일입니다. 마이크별 전파 지연이 적용된
|
||||||
|
192채널 녹음이나 acoustic-camera 결과가 아닙니다.
|
||||||
|
|
||||||
|
클리핑이 예상되면 저장 전에 경고합니다. 출력이 너무 크면 scene 길이 또는 sample rate를 줄이거나
|
||||||
|
source gain과 mix 정규화를 조절하세요.
|
||||||
|
|
||||||
|
## Scene JSON과 assets
|
||||||
|
|
||||||
|
Scene JSON은 오디오·이미지 원본을 포함하지 않고 asset manifest만 기록합니다. 각 descriptor에는
|
||||||
|
id, 종류, 파일명과 SHA-256이 들어갑니다.
|
||||||
|
|
||||||
|
- 파일 한 개는 최대 128 MiB, 연결 파일 합계는 최대 512 MiB입니다.
|
||||||
|
- decode된 오디오의 합계는 최대 192 MiB입니다.
|
||||||
|
- Scene을 다시 불러오면 브라우저 권한이 없으므로 원본 이미지와 오디오를 다시 선택해야 합니다.
|
||||||
|
- 같은 이름의 파일도 SHA-256이 manifest와 다르면 기존 asset으로 연결되지 않습니다.
|
||||||
|
- 사용 중인 asset은 먼저 source, ambient 또는 image 참조를 바꾼 뒤 제거할 수 있습니다.
|
||||||
|
|
||||||
|
원본 파일은 외부로 업로드되지 않고 브라우저 메모리에서만 처리됩니다. 단, JSON에는 파일명이
|
||||||
|
남을 수 있으므로 공유 전에 확인하세요.
|
||||||
|
|
||||||
|
## Acoustic Camera로 넘기기
|
||||||
|
|
||||||
|
1. Scene JSON과 사용한 원본 assets를 함께 보관합니다.
|
||||||
|
2. Acoustic Camera에서 **Scene**으로 JSON을 선택합니다.
|
||||||
|
3. 사용자 문서가 있다면 **Array**, **Experiment** 순서로 선택합니다.
|
||||||
|
4. **Assets**에서 원본 이미지와 오디오를 다시 선택해 SHA-256 연결 상태를 확인합니다.
|
||||||
|
5. 검증이 통과하면 시뮬레이션을 실행합니다.
|
||||||
|
|
||||||
|
안전한 전체 import 순서는 **Scene → Array → Experiment → Assets**입니다. 자세한 계산과 Result
|
||||||
|
설명은 [`../acoustic-camera/README.md`](../acoustic-camera/README.md)를 참고하세요.
|
||||||
|
|
||||||
|
## 모델 범위
|
||||||
|
|
||||||
|
Builder의 mix는 재현 가능한 source recipe와 위치 ground truth를 만드는 단계입니다. 자유음장 전파,
|
||||||
|
마이크 calibration과 DAS는 Acoustic Camera에서 수행합니다. 실제 방의 반사·잔향·차폐를 Builder가
|
||||||
|
추정하지 않습니다.
|
||||||
|
|||||||
@@ -1,3 +1,40 @@
|
|||||||
# Array Geometries
|
# Array Geometries
|
||||||
|
|
||||||
재현 가능한 마이크 배열 JSON 예제를 둡니다.
|
Camera에서 즉시 사용하고 테스트에서 재현할 수 있는 계약 버전 `1.0.0`의 192채널 Array JSON
|
||||||
|
fixture입니다. 좌표 단위는 m이고 모든 microphone은 배열 평면 `z = 0`에 있습니다. `+x`는 오른쪽,
|
||||||
|
`+y`는 위, `+z`는 scene 방향입니다.
|
||||||
|
|
||||||
|
## 포함된 배열
|
||||||
|
|
||||||
|
### `rectangular-16x12.array.json`
|
||||||
|
|
||||||
|
- 16 columns × 12 rows = 192 microphones
|
||||||
|
- 폭과 높이가 각각 0.45 m인 정사각 aperture
|
||||||
|
- x/y 양 끝을 포함해 균일한 간격으로 배치
|
||||||
|
- 기본 single-tone experiment와 CPU/WebGPU 기준 fixture가 참조하는 baseline
|
||||||
|
|
||||||
|
### `fermat-spiral-192.array.json`
|
||||||
|
|
||||||
|
- 192 microphones
|
||||||
|
- 반지름 0.225 m, 지름 0.45 m인 원형 aperture
|
||||||
|
- golden angle과 면적 균등 반지름으로 만든 Fermat spiral
|
||||||
|
- rectangular baseline과 sidelobe·집중도·위치 오차를 비교하기 위한 preset
|
||||||
|
|
||||||
|
Acoustic Camera의 **비교 배열**에서 두 배열을 선택해 같은 Scene/Experiment 결과를 세션 기록에
|
||||||
|
나란히 추가할 수 있습니다.
|
||||||
|
|
||||||
|
## fixture 다시 생성하기
|
||||||
|
|
||||||
|
저장소 최상위에서 다음 명령을 실행하면 두 JSON을 결정론적으로 다시 생성합니다.
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File .\scripts\generate-array-fixture.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
이 스크립트는 기존 `rectangular-16x12.array.json`과 `fermat-spiral-192.array.json`을 덮어씁니다.
|
||||||
|
`ExecutionPolicy Bypass`는 이 PowerShell 프로세스에만 적용됩니다. 생성 뒤
|
||||||
|
<http://127.0.0.1:4173/tests/>에서 schema와 192채널 배치 검증을 실행하세요.
|
||||||
|
|
||||||
|
사용자 배열은 [`../../schemas/array.schema.json`](../../schemas/array.schema.json) 구조를 따르고
|
||||||
|
고유한 `id`, aperture와 microphone별 `[x, y, z]`를 가져야 합니다. 선택적인 `sensitivityDb`와
|
||||||
|
`phaseOffsetDeg`는 합성 channel 편차와 beamforming 역 calibration에 사용됩니다.
|
||||||
|
|||||||
@@ -1,3 +1,12 @@
|
|||||||
# Sample Scenes
|
# Sample Scenes
|
||||||
|
|
||||||
재배포 가능한 최소 synthetic scene과 fixture를 둡니다.
|
재배포 가능한 최소 synthetic fixture입니다. 외부 asset이 없어 서버를 시작한 직후 Camera에서
|
||||||
|
바로 실행할 수 있습니다.
|
||||||
|
|
||||||
|
- `single-tone.scene.json`: 48 kHz, 0.25 s, 1 kHz tone 하나를 z=2 m 평면에 배치한 Scene
|
||||||
|
- `single-tone.experiment.json`: rectangular 192채널 배열, 900–1100 Hz band, 50×50 scan,
|
||||||
|
FFT 512 / hop 256, Auto backend를 쓰는 Experiment
|
||||||
|
|
||||||
|
두 문서는 계약 버전 `1.0.0`이며 기본 Camera 실행, CPU golden test, CPU/WebGPU 비교와 benchmark가
|
||||||
|
같은 조건을 공유하도록 고정되어 있습니다. fixture를 바꿀 때는 <http://127.0.0.1:4173/tests/>의
|
||||||
|
전체 테스트와 benchmark 입력 의미를 함께 확인하세요.
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ distance 생성, CPU STFT가 포함된다. 200×200은 64 MiB steering buffer
|
|||||||
쓰지 않는다.
|
쓰지 않는다.
|
||||||
|
|
||||||
같은 환경의 자동 검증에서 192채널·50×50 GPU raw map은 CPU raw map 대비 peak-normalized RMS
|
같은 환경의 자동 검증에서 192채널·50×50 GPU raw map은 CPU raw map 대비 peak-normalized RMS
|
||||||
오차 `3.008e-5`를 기록했고, 모든 37개 테스트를 통과했다. 상세 원자료는
|
오차 `3.008e-5`를 기록했고, 측정 당시 제공되던 37개 테스트를 모두 통과했다. 이후 end-to-end와
|
||||||
`webgpu-intel-gen9-2026-08-28.json`에 있다. 사용자는 `/benchmarks/webgpu-scaling.html`에서 자신의
|
서버 보안 테스트가 추가되었으므로 현재 전체 suite 상태는 `/tests/`에서 별도로 확인한다. 상세
|
||||||
장치 결과를 다시 측정해 JSON으로 저장할 수 있다.
|
원자료는 `webgpu-intel-gen9-2026-08-28.json`에 있다. 사용자는
|
||||||
|
`/benchmarks/webgpu-scaling.html`에서 자신의 장치 결과를 다시 측정해 JSON으로 저장할 수 있다.
|
||||||
|
|||||||
+36
-3
@@ -11,14 +11,27 @@ micarrsim의 저장 형식은 JSON Schema Draft 2020-12와 추가 관계 검증
|
|||||||
- `micarrsim.experiment`: scan plane/grid, 주파수 대역, STFT와 DAS 설정
|
- `micarrsim.experiment`: scan plane/grid, 주파수 대역, STFT와 DAS 설정
|
||||||
- `micarrsim.result`: raw/display acoustic map, 복수 peak와 source 매칭, 평가·성능 정보
|
- `micarrsim.result`: raw/display acoustic map, 복수 peak와 source 매칭, 평가·성능 정보
|
||||||
|
|
||||||
scene JSON에는 192채널 원시 sample을 넣지 않는다. 합성 tone/noise는 seed가 포함된 recipe로
|
scene JSON에는 192채널 원시 sample이나 원본 asset byte를 넣지 않는다. 합성 tone/noise는 seed가
|
||||||
재생성하고, 녹음 파일과 이미지는 manifest 기준의 안전한 상대 경로로 참조한다. 사용자 컴퓨터의
|
포함된 recipe로 재생성하고, 녹음 파일과 이미지는 `id`, `kind`, `fileName`, `mimeType`,
|
||||||
절대 경로, URL, `..` 경로는 저장하지 않는다.
|
`sha256` manifest로 참조한다. 사용자 컴퓨터의 절대 경로, URL, `..` 경로는 저장하지 않는다.
|
||||||
|
|
||||||
array의 `sensitivityDb`와 `phaseOffsetDeg`는 각 마이크 수신 channel에 적용되는 편차다. 합성기는
|
array의 `sensitivityDb`와 `phaseOffsetDeg`는 각 마이크 수신 channel에 적용되는 편차다. 합성기는
|
||||||
이를 신호에 주입하고, beamformer는 알려진 calibration 값의 역 gain/phase를 적용한다. 따라서
|
이를 신호에 주입하고, beamformer는 알려진 calibration 값의 역 gain/phase를 적용한다. 따라서
|
||||||
두 값이 0이 아닌 배열도 오류 없이 조용히 무시되지 않는다.
|
두 값이 0이 아닌 배열도 오류 없이 조용히 무시되지 않는다.
|
||||||
|
|
||||||
|
## 로컬 asset 재연결
|
||||||
|
|
||||||
|
브라우저 보안상 Scene JSON을 새 페이지에서 열었을 때 이전 로컬 파일 권한은 복원되지 않는다.
|
||||||
|
Camera는 사용자가 다시 선택한 파일을 SHA-256으로 계산해 같은 descriptor에만 연결한다. 이름이
|
||||||
|
같아도 byte가 다르면 거절하고, 원본은 브라우저 메모리에서만 decode하며 서버에 업로드하지 않는다.
|
||||||
|
|
||||||
|
- 파일 하나: 최대 128 MiB
|
||||||
|
- 한 Scene에 연결된 원본 파일 합계: 최대 512 MiB
|
||||||
|
- 동시에 유지하는 decode 오디오 channel 합계: 최대 192 MiB
|
||||||
|
|
||||||
|
Builder가 내보낸 Scene JSON과 사용한 원본 이미지·오디오는 함께 보관해야 한다. Preview WAV는
|
||||||
|
scene recipe를 mono로 합친 청취 확인용 파일이며 마이크 배열의 192채널 수신 신호가 아니다.
|
||||||
|
|
||||||
## 오디오와 map 정규화
|
## 오디오와 map 정규화
|
||||||
|
|
||||||
`scene.mix.normalization`은 마이크 신호 합성 전에 적용할 오디오 mix 정책이다. `none`은 원래
|
`scene.mix.normalization`은 마이크 신호 합성 전에 적용할 오디오 mix 정책이다. `none`은 원래
|
||||||
@@ -32,6 +45,25 @@ raw DAS map을 덮어쓰지 않는다.
|
|||||||
map은 row-major 배열이다. 첫 행은 `yMax`, 마지막 행은 `yMin`, 각 행은 `xMin`에서 `xMax`
|
map은 row-major 배열이다. 첫 행은 `yMax`, 마지막 행은 `yMin`, 각 행은 `xMin`에서 `xMax`
|
||||||
방향이다. `values.length`는 반드시 `rows × columns`와 같아야 한다.
|
방향이다. `values.length`는 반드시 `rows × columns`와 같아야 한다.
|
||||||
|
|
||||||
|
## Result 평가 정보
|
||||||
|
|
||||||
|
각 band의 `peaks`는 위치와 power 값, 선택적 `sourceId`, `localizationErrorM`,
|
||||||
|
`localizationErrorPixels`를 저장한다. `metrics`에는 다음 유한값만 기록한다.
|
||||||
|
|
||||||
|
- `peakToSidelobeDb`
|
||||||
|
- `energyInsideTruthRatio`
|
||||||
|
- `precision`, `recall`
|
||||||
|
- `mainLobeWidthM`
|
||||||
|
|
||||||
|
`evaluation`은 지표를 다시 해석할 수 있도록 truth 반경, source 매칭 tolerance, peak 검출
|
||||||
|
threshold와 최소 거리, sidelobe 제외 반경, half-power 비율, dynamic range, 최대 검출 수를
|
||||||
|
버전과 함께 저장한다. `environment`에는 실제 backend, microphone/grid 수, scan bounds와 z,
|
||||||
|
가능한 경우 GPU adapter 및 fallback 정보를 기록한다.
|
||||||
|
|
||||||
|
대역별 ground truth는 tone의 주파수가 band 안에 있을 때 그 tone source를 포함한다. noise와
|
||||||
|
asset source는 현재 모든 band의 truth로 간주한다. 즉 녹음 asset의 실제 spectrum energy를
|
||||||
|
대역별로 판정하는 계약은 아직 없으며, 이 가정은 실제 데이터 결과를 해석할 때 고려해야 한다.
|
||||||
|
|
||||||
## 재현성 hash
|
## 재현성 hash
|
||||||
|
|
||||||
`result.inputHash`는 실행에 사용한 `{ array, scene, experiment }`를 객체 key 사전순으로 재귀
|
`result.inputHash`는 실행에 사용한 `{ array, scene, experiment }`를 객체 key 사전순으로 재귀
|
||||||
@@ -48,6 +80,7 @@ SHA-256을 계산해 scene의 해당 asset `sha256` 필드를 채운 상태로 c
|
|||||||
- asset 종류와 안전한 상대 경로
|
- asset 종류와 안전한 상대 경로
|
||||||
- Nyquist 및 시간·scan 범위
|
- Nyquist 및 시간·scan 범위
|
||||||
- map shape와 source peak 매칭
|
- map shape와 source peak 매칭
|
||||||
|
- band별 evaluation 파라미터와 허용 metric
|
||||||
- 현재 비스트리밍 CPU backend의 512 MiB channel-buffer 예산
|
- 현재 비스트리밍 CPU backend의 512 MiB channel-buffer 예산
|
||||||
|
|
||||||
지원하지 않는 schemaVersion은 묵시적으로 변환하지 않고 오류로 알린다.
|
지원하지 않는 schemaVersion은 묵시적으로 변환하지 않고 오류로 알린다.
|
||||||
|
|||||||
+32
-22
@@ -1,5 +1,10 @@
|
|||||||
# micarrsim 실행 계획
|
# micarrsim 실행 계획
|
||||||
|
|
||||||
|
> 구현 상태 · 2026-08-28
|
||||||
|
>
|
||||||
|
> 단계 0–4는 완료되어 설치 없는 브라우저 앱, CPU/WebGPU DAS, Builder→Camera asset 재연결,
|
||||||
|
> 배열 비교, 평가·Result 내보내기와 53개 자동 테스트로 검증했다. 단계 5는 향후 고도화 범위다.
|
||||||
|
|
||||||
## 1. 목표와 성공 기준
|
## 1. 목표와 성공 기준
|
||||||
|
|
||||||
이 프로젝트는 정방형 평면에 배치할 192개 MEMS 마이크의 배열을 비교하기 위한 재현 가능한
|
이 프로젝트는 정방형 평면에 배치할 192개 MEMS 마이크의 배열을 비교하기 위한 재현 가능한
|
||||||
@@ -8,7 +13,7 @@
|
|||||||
|
|
||||||
1. 기계음 데이터와 ambient audio를 조합해 장면 및 ground truth를 생성한다.
|
1. 기계음 데이터와 ambient audio를 조합해 장면 및 ground truth를 생성한다.
|
||||||
2. 배열 JSON과 장면 정보를 사용해 각 마이크에 도달하는 신호를 합성한다.
|
2. 배열 JSON과 장면 정보를 사용해 각 마이크에 도달하는 신호를 합성한다.
|
||||||
3. WGSL로 conventional delay-and-sum(DAS/DAC) 빔포밍을 수행한다.
|
3. WGSL로 conventional delay-and-sum(DAS) 빔포밍을 수행한다.
|
||||||
4. 주파수 구간별 acoustic image를 ground truth 위에 표시하고 정량 평가한다.
|
4. 주파수 구간별 acoustic image를 ground truth 위에 표시하고 정량 평가한다.
|
||||||
5. 배열, 거리, 음원, 주파수, grid 해상도를 바꾼 실험을 저장하고 비교한다.
|
5. 배열, 거리, 음원, 주파수, grid 해상도를 바꾼 실험을 저장하고 비교한다.
|
||||||
|
|
||||||
@@ -18,10 +23,12 @@ CPU 기준 구현과 GPU 결과의 수치 오차가 합의된 허용 범위 안
|
|||||||
|
|
||||||
## 2. 기술 접근 결정
|
## 2. 기술 접근 결정
|
||||||
|
|
||||||
TypeScript 기반의 브라우저 앱 2개와 WebGPU/WGSL을 권장한다. 브라우저는 배열·장면 편집,
|
최종 구현은 빌드 단계와 패키지 설치가 없는 브라우저 네이티브 ES module 2개와 WebGPU/WGSL을
|
||||||
오디오 미리 듣기, 이미지 overlay에 적합하고 WebGPU는 192 × scan point × frequency bin 형태의
|
선택했다. 초기 계획의 TypeScript 권고보다 Windows 사용자가 `start.cmd` 하나로 바로 실행하는
|
||||||
병렬 연산에 잘 맞는다. WGSL 커널을 독립 패키지로 두면 이후 GLSL 또는 네이티브 GPU 구현으로
|
목표를 우선한 결정이다. JSON Schema와 브라우저 자동 테스트가 런타임 계약을 보완한다.
|
||||||
옮길 때 데이터 배치와 수식을 재사용하기 쉽다.
|
브라우저는 배열·장면 편집, 오디오 미리 듣기, 이미지 overlay에 적합하고 WebGPU는
|
||||||
|
192 × scan point × frequency bin 형태의 병렬 연산에 잘 맞는다. WGSL 커널은 독립 package로
|
||||||
|
두어 데이터 배치와 수식을 CPU 기준 구현과 대조할 수 있게 했다.
|
||||||
|
|
||||||
데이터셋 전체를 브라우저에 적재하지 않는다. 사용자가 로컬 파일을 선택해 장면을 만들고,
|
데이터셋 전체를 브라우저에 적재하지 않는다. 사용자가 로컬 파일을 선택해 장면을 만들고,
|
||||||
결과를 manifest와 오디오 파일로 내보내는 방식을 우선 구현한다. 대규모 batch 전처리가 필요해질
|
결과를 manifest와 오디오 파일로 내보내는 방식을 우선 구현한다. 대규모 batch 전처리가 필요해질
|
||||||
@@ -105,7 +112,7 @@ flattening이 모델 타당성을 훼손하는지 청취 및 spectrum 통계로
|
|||||||
|
|
||||||
## 8. 단계별 구현
|
## 8. 단계별 구현
|
||||||
|
|
||||||
### 단계 0 — 기반과 계약
|
### 단계 0 — 기반과 계약 · 완료
|
||||||
|
|
||||||
- workspace, formatter, linter, unit test, WebGPU feature detection 구성
|
- workspace, formatter, linter, unit test, WebGPU feature detection 구성
|
||||||
- 네 가지 JSON Schema와 최소 예제 작성
|
- 네 가지 JSON Schema와 최소 예제 작성
|
||||||
@@ -114,7 +121,7 @@ flattening이 모델 타당성을 훼손하는지 청취 및 spectrum 통계로
|
|||||||
|
|
||||||
완료 조건: 예제 JSON이 schema validation을 통과하고 두 앱이 동일 manifest를 읽는다.
|
완료 조건: 예제 JSON이 schema validation을 통과하고 두 앱이 동일 manifest를 읽는다.
|
||||||
|
|
||||||
### 단계 1 — CPU 기준 모델
|
### 단계 1 — CPU 기준 모델 · 완료
|
||||||
|
|
||||||
- 자유음장 구면 전파, 분수 지연, STFT, steering, DAS의 CPU reference 구현
|
- 자유음장 구면 전파, 분수 지연, STFT, steering, DAS의 CPU reference 구현
|
||||||
- 단일 음원의 지연·감쇠·peak 위치를 analytic result와 비교
|
- 단일 음원의 지연·감쇠·peak 위치를 analytic result와 비교
|
||||||
@@ -122,15 +129,15 @@ flattening이 모델 타당성을 훼손하는지 청취 및 spectrum 통계로
|
|||||||
|
|
||||||
완료 조건: 자동화된 golden test가 통과하고 sign/좌표 convention이 문서화된다.
|
완료 조건: 자동화된 golden test가 통과하고 sign/좌표 convention이 문서화된다.
|
||||||
|
|
||||||
### 단계 2 — Dataset Builder
|
### 단계 2 — Dataset Builder · 완료
|
||||||
|
|
||||||
- 이미지와 음원 파일 불러오기, 위치·시간·gain 편집, ambient mix와 미리 듣기
|
- 이미지와 음원 파일 불러오기, 위치·시간·gain 편집, ambient mix와 미리 듣기
|
||||||
- deterministic seed, clipping 경고, manifest/audio 내보내기
|
- deterministic seed, clipping 경고, Scene JSON과 mono Preview WAV 내보내기
|
||||||
- 데이터셋 adapter 인터페이스와 synthetic adapter 구현
|
- 데이터셋 adapter 인터페이스와 synthetic adapter 구현
|
||||||
|
|
||||||
완료 조건: 만든 scene을 다시 열었을 때 음원, 위치, 파형, 정답이 동일하다.
|
완료 조건: 만든 scene을 다시 열었을 때 음원, 위치, 파형, 정답이 동일하다.
|
||||||
|
|
||||||
### 단계 3 — WebGPU DAS
|
### 단계 3 — WebGPU DAS · 완료
|
||||||
|
|
||||||
- CPU와 동일한 buffer layout 및 WGSL kernel 구현
|
- CPU와 동일한 buffer layout 및 WGSL kernel 구현
|
||||||
- frequency band별 map 생성, device limit 대응, CPU fallback
|
- frequency band별 map 생성, device limit 대응, CPU fallback
|
||||||
@@ -138,7 +145,7 @@ flattening이 모델 타당성을 훼손하는지 청취 및 spectrum 통계로
|
|||||||
|
|
||||||
완료 조건: CPU/GPU map 오차 기준을 통과하고 192채널 50×50 실행 timing이 기록된다.
|
완료 조건: CPU/GPU map 오차 기준을 통과하고 192채널 50×50 실행 timing이 기록된다.
|
||||||
|
|
||||||
### 단계 4 — Acoustic Camera UI와 평가
|
### 단계 4 — Acoustic Camera UI와 평가 · 완료
|
||||||
|
|
||||||
- scene/ground truth/acoustic overlay, 배열 geometry, parameter panel 구현
|
- scene/ground truth/acoustic overlay, 배열 geometry, parameter panel 구현
|
||||||
- 결과 metric, 실행 환경, 입력 hash와 map 내보내기
|
- 결과 metric, 실행 환경, 입력 hash와 map 내보내기
|
||||||
@@ -146,7 +153,7 @@ flattening이 모델 타당성을 훼손하는지 청취 및 spectrum 통계로
|
|||||||
|
|
||||||
완료 조건: 사용자가 코드 수정 없이 배열 JSON 두 개를 같은 장면에서 비교할 수 있다.
|
완료 조건: 사용자가 코드 수정 없이 배열 JSON 두 개를 같은 장면에서 비교할 수 있다.
|
||||||
|
|
||||||
### 단계 5 — 실제 데이터와 고도화
|
### 단계 5 — 실제 데이터와 고도화 · 향후
|
||||||
|
|
||||||
- 라이선스 검토를 통과한 DCASE/MIMII adapter와 batch workflow 추가
|
- 라이선스 검토를 통과한 DCASE/MIMII adapter와 batch workflow 추가
|
||||||
- microphone calibration·noise 모델 검토
|
- microphone calibration·noise 모델 검토
|
||||||
@@ -164,15 +171,18 @@ flattening이 모델 타당성을 훼손하는지 청취 및 spectrum 통계로
|
|||||||
- 실제 AC와 simulation gap: 감도, 위상, 위치 오차를 이후 calibration/noise model로 주입 가능하게 한다.
|
- 실제 AC와 simulation gap: 감도, 위상, 위치 오차를 이후 calibration/noise model로 주입 가능하게 한다.
|
||||||
- post-processing이 기본 결과를 가림: raw DAS map과 후처리 map을 항상 별도로 보존한다.
|
- post-processing이 기본 결과를 가림: raw DAS map과 후처리 map을 항상 별도로 보존한다.
|
||||||
|
|
||||||
## 10. 첫 구현 백로그
|
## 10. 구현 결과와 다음 백로그
|
||||||
|
|
||||||
1. TypeScript workspace와 두 앱 shell을 생성한다.
|
- [x] 설치 없는 ES module workspace와 두 앱 shell
|
||||||
2. array/scene/experiment/result schema와 fixture를 만든다.
|
- [x] array/scene/experiment/result schema와 synthetic fixture
|
||||||
3. 좌표 변환, 거리, 구면 감쇠, 주파수 phase delay 단위 테스트를 작성한다.
|
- [x] 좌표·거리·구면 감쇠·분수 지연·STFT 단위 테스트
|
||||||
4. CPU DAS golden implementation과 synthetic scene을 만든다.
|
- [x] CPU DAS golden implementation
|
||||||
5. WGSL kernel 및 CPU/GPU 비교 harness를 만든다.
|
- [x] WGSL kernel, CPU/GPU 비교와 50²/100²/200² benchmark
|
||||||
6. scene canvas, array view, parameter controls를 연결한다.
|
- [x] scene canvas, array view, parameter controls와 Result export
|
||||||
7. benchmark와 평가 지표를 결과 manifest에 기록한다.
|
- [x] Builder→Camera SHA-256 asset 재연결 왕복 테스트
|
||||||
|
- [ ] DCASE/MIMII 라이선스 검토와 adapter
|
||||||
|
- [ ] 잔향·calibration·noise model
|
||||||
|
- [ ] DAMAS/CLEAN-SC 후처리
|
||||||
|
|
||||||
각 단계는 정확성 test를 먼저 통과시킨 뒤 UI 또는 최적화를 진행한다. 초기 milestone에서는 잔향,
|
정확성 test를 먼저 통과시킨 뒤 UI 또는 최적화를 진행한다는 원칙은 유지한다. 단계 5 기능은
|
||||||
DAMAS/CLEAN-SC, 대규모 dataset batch 변환을 완료 조건으로 삼지 않는다.
|
자유음장 DAS 결과를 바꾸지 않는 선택형 확장으로 추가한다.
|
||||||
|
|||||||
+192
@@ -0,0 +1,192 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ko">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="micarrsim 빠른 시작, 전체 데이터 흐름, 결과 해석과 문제 해결"
|
||||||
|
/>
|
||||||
|
<title>micarrsim 사용 설명서</title>
|
||||||
|
<link rel="stylesheet" href="/packages/ui/app-shell.css" />
|
||||||
|
<link rel="stylesheet" href="/docs/styles.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="guide-header">
|
||||||
|
<a class="guide-brand" href="/">← micarrsim 홈</a>
|
||||||
|
<nav aria-label="문서 목차">
|
||||||
|
<a href="#quick-start">빠른 시작</a>
|
||||||
|
<a href="#full-workflow">내 데이터</a>
|
||||||
|
<a href="#results">결과 해석</a>
|
||||||
|
<a href="#troubleshooting">문제 해결</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="guide-shell">
|
||||||
|
<section class="guide-hero">
|
||||||
|
<p class="eyebrow">Local user guide</p>
|
||||||
|
<h1>설치 없이 시작하는<br />음향 카메라 실험</h1>
|
||||||
|
<p>
|
||||||
|
Windows 10/11과 최신 Chrome 또는 Edge만 있으면 됩니다. 모든 입력과 계산은 이
|
||||||
|
컴퓨터의 브라우저 안에서 처리되며 외부 서버로 업로드되지 않습니다.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="guide-section" id="quick-start">
|
||||||
|
<div class="section-number">01</div>
|
||||||
|
<div>
|
||||||
|
<p class="eyebrow">30초 확인</p>
|
||||||
|
<h2>기본 결과 하나 저장하기</h2>
|
||||||
|
<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>완료되면 <strong>Result JSON</strong>을 눌러 다운로드 폴더에 저장합니다.</li>
|
||||||
|
</ol>
|
||||||
|
<aside class="callout">
|
||||||
|
브라우저가 자동으로 열리지 않으면
|
||||||
|
<a href="http://127.0.0.1:4173/">http://127.0.0.1:4173/</a>을 직접 여세요.
|
||||||
|
종료할 때는 서버 콘솔에서 <kbd>Ctrl</kbd>+<kbd>C</kbd>를 누릅니다.
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="guide-section" id="full-workflow">
|
||||||
|
<div class="section-number">02</div>
|
||||||
|
<div>
|
||||||
|
<p class="eyebrow">5분 전체 흐름</p>
|
||||||
|
<h2>내 장면을 만들고 Camera에서 실행하기</h2>
|
||||||
|
<ol class="guide-steps">
|
||||||
|
<li>
|
||||||
|
<a href="/apps/dataset-builder/">Dataset Builder</a>에서 이미지와 오디오를
|
||||||
|
선택하고 음원 위치, gain, 시간, ambient를 설정합니다.
|
||||||
|
</li>
|
||||||
|
<li><strong>Scene JSON 저장</strong>을 누르고 사용한 원본 파일과 함께 보관합니다.</li>
|
||||||
|
<li><strong>Scene 저장 후 Camera 열기</strong>를 누릅니다.</li>
|
||||||
|
<li>
|
||||||
|
Camera에서 파일을 <strong>Scene → Array → Experiment → Assets</strong> 순서로
|
||||||
|
가져옵니다. 내 Array/Experiment가 없다면 내장 기본값을 그대로 둡니다.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Assets에서 Builder에 사용한 원본 이미지와 오디오를 한 번에 다시 선택합니다.
|
||||||
|
준비 상태가 모두 초록색인지 확인합니다.
|
||||||
|
</li>
|
||||||
|
<li><strong>Auto (GPU→CPU)</strong>로 실행한 뒤 Result JSON을 저장합니다.</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<aside class="callout callout--important" id="asset-reconnect">
|
||||||
|
<strong>Scene JSON에는 원본 파일이 들어 있지 않습니다.</strong>
|
||||||
|
파일명·종류·SHA-256 manifest만 저장됩니다. 브라우저는 새 페이지에서 로컬 파일
|
||||||
|
권한을 유지할 수 없으므로 같은 원본을 다시 선택해야 합니다. 이름이 같아도 내용의
|
||||||
|
SHA-256이 다르면 연결되지 않습니다.
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<div class="limit-grid" aria-label="asset 한도">
|
||||||
|
<div><strong>128 MiB</strong><span>원본 파일 하나</span></div>
|
||||||
|
<div><strong>512 MiB</strong><span>연결 파일 합계</span></div>
|
||||||
|
<div><strong>192 MiB</strong><span>decode된 오디오 합계</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="guide-section">
|
||||||
|
<div class="section-number">03</div>
|
||||||
|
<div>
|
||||||
|
<p class="eyebrow">Compute backend</p>
|
||||||
|
<h2>속도와 격자 선택</h2>
|
||||||
|
<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>동일한 자유음장 DAS를 CPU에서 계산</td><td>호환성 확인·기준 비교</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<p class="guide-copy">
|
||||||
|
먼저 50×50을 권장합니다. 100×100은 4배, 200×200은 16배의 scan point를 계산합니다.
|
||||||
|
실행 중에는 취소할 수 있으며, 취소하거나 실패해도 이전 완료 결과는 유지됩니다.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="guide-section" id="results">
|
||||||
|
<div class="section-number">04</div>
|
||||||
|
<div>
|
||||||
|
<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>peaks</dt><dd>검출 위치, source 매칭, m·pixel 위치 오차</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>timing / environment</dt><dd>backend, grid·scan 범위, 장치 정보와 구간별 시간</dd></div>
|
||||||
|
<div><dt>inputHash</dt><dd>Array·Scene·Experiment 입력을 묶은 SHA-256 재현성 식별자</dd></div>
|
||||||
|
</dl>
|
||||||
|
<aside class="callout">
|
||||||
|
평가는 tone 음원을 해당 주파수 대역이 tone 주파수를 포함할 때만 정답으로 사용합니다.
|
||||||
|
noise와 asset 음원은 현재 모든 대역의 정답으로 간주합니다. 녹음 파일의 실제 대역별
|
||||||
|
에너지를 자동 판정하는 기능은 아직 없습니다.
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="guide-section" id="troubleshooting">
|
||||||
|
<div class="section-number">05</div>
|
||||||
|
<div>
|
||||||
|
<p class="eyebrow">Troubleshooting</p>
|
||||||
|
<h2>막혔을 때 확인할 것</h2>
|
||||||
|
<details open>
|
||||||
|
<summary>화면이 비거나 module/Worker 오류가 납니다.</summary>
|
||||||
|
<p><code>index.html</code>을 직접 열지 말고 반드시 <code>start.cmd</code>로 실행하세요.</p>
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>4173 포트를 사용할 수 없다고 나옵니다.</summary>
|
||||||
|
<p><code>start.cmd -Port 4174</code>처럼 다른 포트를 지정하고 <code>http://127.0.0.1:4174/</code>을 여세요.</p>
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>Experiment를 가져올 수 없습니다.</summary>
|
||||||
|
<p>Scene과 Array를 먼저 가져오세요. Experiment의 <code>sceneId</code>와 <code>arrayId</code>가 현재 문서와 같아야 합니다.</p>
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>Assets가 준비되지 않습니다.</summary>
|
||||||
|
<p>Builder에서 사용한 것과 바이트가 같은 원본 파일을 다시 선택하세요. 파일명뿐 아니라 SHA-256도 일치해야 합니다.</p>
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>WebGPU가 없거나 실행이 너무 느립니다.</summary>
|
||||||
|
<p>Backend를 Auto 또는 CPU로 두고 50×50 grid부터 실행하세요. 최신 Chrome/Edge와 그래픽 드라이버도 확인하세요.</p>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="guide-section">
|
||||||
|
<div class="section-number">06</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는 현재 범위에 포함되지 않습니다. 결과는 실제
|
||||||
|
장비의 절대 성능 보증이 아니라 같은 조건에서 배열을 비교하는 재현 가능한 기준입니다.
|
||||||
|
</p>
|
||||||
|
<p class="guide-copy">
|
||||||
|
원본 asset은 브라우저 메모리에서만 읽고 서버나 외부 네트워크로 보내지 않습니다.
|
||||||
|
다만 Scene JSON에는 원본 파일명이 남을 수 있으므로 외부 공유 전 확인하세요.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="reference-grid" aria-label="상세 문서">
|
||||||
|
<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/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>
|
||||||
|
<a href="/benchmarks/webgpu-scaling.html"><strong>WebGPU 측정</strong><span>현재 장치 benchmark</span></a>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+291
@@ -0,0 +1,291 @@
|
|||||||
|
.guide-header {
|
||||||
|
position: sticky;
|
||||||
|
z-index: 10;
|
||||||
|
top: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
min-height: 64px;
|
||||||
|
padding: 12px clamp(18px, 4vw, 48px);
|
||||||
|
border-bottom: 1px solid var(--line);
|
||||||
|
background: rgb(8 17 15 / 92%);
|
||||||
|
backdrop-filter: blur(18px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-brand,
|
||||||
|
.guide-header nav a {
|
||||||
|
color: inherit;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
font-weight: 700;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-header nav {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 18px;
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-shell {
|
||||||
|
width: min(980px, calc(100% - 40px));
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: clamp(48px, 8vw, 96px) 0 72px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-hero {
|
||||||
|
max-width: 820px;
|
||||||
|
margin-bottom: clamp(64px, 10vw, 112px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-hero h1 {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
font-size: clamp(2.8rem, 8vw, 6.4rem);
|
||||||
|
letter-spacing: -0.07em;
|
||||||
|
line-height: 0.98;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-hero > p:last-child,
|
||||||
|
.guide-copy {
|
||||||
|
color: #c8d9d3;
|
||||||
|
line-height: 1.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-hero > p:last-child {
|
||||||
|
max-width: 720px;
|
||||||
|
font-size: clamp(1rem, 2vw, 1.2rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-section {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 70px minmax(0, 1fr);
|
||||||
|
gap: clamp(18px, 4vw, 42px);
|
||||||
|
padding: clamp(36px, 7vw, 68px) 0;
|
||||||
|
border-top: 1px solid var(--line);
|
||||||
|
scroll-margin-top: 72px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-number {
|
||||||
|
color: var(--mint);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 760;
|
||||||
|
letter-spacing: 0.12em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-section h2 {
|
||||||
|
margin-bottom: 28px;
|
||||||
|
font-size: clamp(1.8rem, 4vw, 3rem);
|
||||||
|
letter-spacing: -0.05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-steps {
|
||||||
|
display: grid;
|
||||||
|
gap: 14px;
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 1.35rem;
|
||||||
|
color: #c8d9d3;
|
||||||
|
line-height: 1.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-steps li {
|
||||||
|
padding-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-steps a,
|
||||||
|
.callout a {
|
||||||
|
color: var(--mint);
|
||||||
|
}
|
||||||
|
|
||||||
|
code,
|
||||||
|
kbd {
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 6px;
|
||||||
|
color: var(--text);
|
||||||
|
background: rgb(255 255 255 / 5%);
|
||||||
|
font-family: "Cascadia Code", Consolas, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
padding: 0.12em 0.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
kbd {
|
||||||
|
padding: 2px 6px;
|
||||||
|
box-shadow: 0 2px 0 var(--line);
|
||||||
|
font-size: 0.78rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.callout {
|
||||||
|
margin-top: 26px;
|
||||||
|
padding: 18px 20px;
|
||||||
|
border-left: 3px solid var(--cyan);
|
||||||
|
border-radius: 0 12px 12px 0;
|
||||||
|
color: var(--muted);
|
||||||
|
background: rgb(88 199 232 / 7%);
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.callout--important {
|
||||||
|
border-color: var(--amber);
|
||||||
|
background: rgb(255 197 110 / 7%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.callout strong {
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.limit-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.limit-grid div {
|
||||||
|
padding: 18px;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 12px;
|
||||||
|
background: rgb(255 255 255 / 2%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.limit-grid strong,
|
||||||
|
.limit-grid span {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.limit-grid strong {
|
||||||
|
color: var(--mint);
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.limit-grid span {
|
||||||
|
margin-top: 5px;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 0.78rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-table-wrap {
|
||||||
|
overflow-x: auto;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-table-wrap table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
color: #c8d9d3;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-table-wrap th,
|
||||||
|
.guide-table-wrap td {
|
||||||
|
padding: 14px 16px;
|
||||||
|
border-bottom: 1px solid var(--line);
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-table-wrap th {
|
||||||
|
color: var(--text);
|
||||||
|
background: rgb(255 255 255 / 4%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-table-wrap tr:last-child td {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-list {
|
||||||
|
display: grid;
|
||||||
|
gap: 10px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-list div {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(150px, 0.4fr) minmax(0, 1fr);
|
||||||
|
gap: 18px;
|
||||||
|
padding: 14px 0;
|
||||||
|
border-bottom: 1px solid var(--line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-list dt {
|
||||||
|
color: var(--mint);
|
||||||
|
font-family: "Cascadia Code", Consolas, monospace;
|
||||||
|
font-size: 0.86rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-list dd {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--muted);
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
details {
|
||||||
|
padding: 16px 0;
|
||||||
|
border-bottom: 1px solid var(--line);
|
||||||
|
}
|
||||||
|
|
||||||
|
summary {
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
details p {
|
||||||
|
margin: 12px 0 0;
|
||||||
|
color: var(--muted);
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reference-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
padding-top: 48px;
|
||||||
|
border-top: 1px solid var(--line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.reference-grid a {
|
||||||
|
padding: 20px;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 14px;
|
||||||
|
background: var(--surface);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reference-grid a:hover,
|
||||||
|
.reference-grid a:focus-visible {
|
||||||
|
border-color: var(--mint);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reference-grid strong,
|
||||||
|
.reference-grid span {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reference-grid span {
|
||||||
|
margin-top: 8px;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 0.78rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 720px) {
|
||||||
|
.guide-header nav {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guide-section {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.limit-grid,
|
||||||
|
.reference-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-list div {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
+43
-4
@@ -25,6 +25,26 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section class="workflow-card" aria-labelledby="workflow-title">
|
||||||
|
<div class="workflow-card__heading">
|
||||||
|
<div>
|
||||||
|
<p class="eyebrow">Local workflow</p>
|
||||||
|
<h2 id="workflow-title">파일이 흐르는 방식</h2>
|
||||||
|
</div>
|
||||||
|
<a href="/docs/#asset-reconnect">왜 원본을 다시 선택하나요?</a>
|
||||||
|
</div>
|
||||||
|
<ol class="workflow-list">
|
||||||
|
<li><strong>Dataset Builder</strong><span>음원과 위치 만들기</span></li>
|
||||||
|
<li><strong>Scene JSON + 원본 assets</strong><span>같이 보관하기</span></li>
|
||||||
|
<li><strong>Acoustic Camera</strong><span>배열·DAS 실행하기</span></li>
|
||||||
|
<li><strong>Result JSON</strong><span>map·지표 저장하기</span></li>
|
||||||
|
</ol>
|
||||||
|
<p class="workflow-note">
|
||||||
|
원본 파일은 JSON에 포함되거나 외부로 업로드되지 않습니다. Camera에서 같은 파일을 다시
|
||||||
|
선택하면 SHA-256으로 확인한 뒤 연결합니다.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section class="launcher-grid" aria-label="도구 선택">
|
<section class="launcher-grid" aria-label="도구 선택">
|
||||||
<a class="launcher-card" href="/apps/dataset-builder/">
|
<a class="launcher-card" href="/apps/dataset-builder/">
|
||||||
<span class="launcher-card__number">01</span>
|
<span class="launcher-card__number">01</span>
|
||||||
@@ -32,7 +52,7 @@
|
|||||||
<h2>Dataset Builder</h2>
|
<h2>Dataset Builder</h2>
|
||||||
<p>기기 이미지에 음원을 배치하고 ambient를 혼합해 재현 가능한 장면을 만듭니다.</p>
|
<p>기기 이미지에 음원을 배치하고 ambient를 혼합해 재현 가능한 장면을 만듭니다.</p>
|
||||||
</div>
|
</div>
|
||||||
<span class="launcher-card__action">장면 만들기 →</span>
|
<span class="launcher-card__action">새 Scene 만들기 →</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a class="launcher-card launcher-card--accent" href="/apps/acoustic-camera/">
|
<a class="launcher-card launcher-card--accent" href="/apps/acoustic-camera/">
|
||||||
@@ -41,14 +61,33 @@
|
|||||||
<h2>Acoustic Camera</h2>
|
<h2>Acoustic Camera</h2>
|
||||||
<p>배열 응답을 계산하고 주파수 대역별 DAS 결과를 ground truth와 비교합니다.</p>
|
<p>배열 응답을 계산하고 주파수 대역별 DAS 결과를 ground truth와 비교합니다.</p>
|
||||||
</div>
|
</div>
|
||||||
<span class="launcher-card__action">시뮬레이션 열기 →</span>
|
<span class="launcher-card__action">기본 예제로 바로 실행 →</span>
|
||||||
</a>
|
</a>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section class="quick-start-grid" aria-label="빠른 시작">
|
||||||
|
<article class="quick-start-card">
|
||||||
|
<p class="eyebrow">30초 확인</p>
|
||||||
|
<h2>설정 없이 첫 결과 만들기</h2>
|
||||||
|
<p>Acoustic Camera를 열어 <strong>▶ 기본 예제 실행</strong>을 누르고, 완료되면 <strong>Result JSON</strong>을 저장하세요.</p>
|
||||||
|
<a href="/apps/acoustic-camera/">기본 예제 열기 →</a>
|
||||||
|
</article>
|
||||||
|
<article class="quick-start-card">
|
||||||
|
<p class="eyebrow">내 데이터</p>
|
||||||
|
<h2>Builder부터 끝까지</h2>
|
||||||
|
<p>Scene을 저장한 뒤 Camera에서 <strong>Scene → Array → Experiment → Assets</strong> 순서로 가져오면 참조 오류를 줄일 수 있습니다.</p>
|
||||||
|
<a href="/docs/#full-workflow">5분 안내 보기 →</a>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
|
||||||
<footer class="landing-footer">
|
<footer class="landing-footer">
|
||||||
<a href="/docs/execution-plan.md">실행 계획</a>
|
<a href="/docs/">사용 설명서</a>
|
||||||
<span aria-hidden="true">·</span>
|
<span aria-hidden="true">·</span>
|
||||||
<a href="https://github.com/" rel="noreferrer">프로젝트는 로컬에서만 실행됩니다</a>
|
<a href="/tests/">테스트</a>
|
||||||
|
<span aria-hidden="true">·</span>
|
||||||
|
<a href="/benchmarks/webgpu-scaling.html">성능 측정</a>
|
||||||
|
<span aria-hidden="true">·</span>
|
||||||
|
<a href="/docs/execution-plan.md">실행 계획</a>
|
||||||
</footer>
|
</footer>
|
||||||
</main>
|
</main>
|
||||||
<script type="module" src="/packages/ui/landing.js"></script>
|
<script type="module" src="/packages/ui/landing.js"></script>
|
||||||
|
|||||||
+118
-2
@@ -114,11 +114,92 @@ p {
|
|||||||
background: rgb(255 197 110 / 8%);
|
background: rgb(255 197 110 / 8%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.workflow-card {
|
||||||
|
margin-top: clamp(42px, 7vw, 72px);
|
||||||
|
padding: clamp(22px, 4vw, 34px);
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 22px;
|
||||||
|
background: rgb(255 255 255 / 2%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-card__heading {
|
||||||
|
display: flex;
|
||||||
|
align-items: end;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-card__heading h2,
|
||||||
|
.quick-start-card h2 {
|
||||||
|
margin-bottom: 0;
|
||||||
|
letter-spacing: -0.035em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-card__heading a,
|
||||||
|
.quick-start-card a {
|
||||||
|
color: var(--mint);
|
||||||
|
font-size: 0.84rem;
|
||||||
|
font-weight: 700;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
|
gap: 10px;
|
||||||
|
margin: 24px 0 16px;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
counter-reset: workflow;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-list li {
|
||||||
|
position: relative;
|
||||||
|
min-height: 104px;
|
||||||
|
padding: 18px;
|
||||||
|
border: 1px solid rgb(101 242 191 / 18%);
|
||||||
|
border-radius: 14px;
|
||||||
|
background: rgb(101 242 191 / 5%);
|
||||||
|
counter-increment: workflow;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-list li::before {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
color: var(--mint);
|
||||||
|
content: "0" counter(workflow);
|
||||||
|
font-size: 0.7rem;
|
||||||
|
font-weight: 760;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-list strong,
|
||||||
|
.workflow-list span {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-list strong {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-list span {
|
||||||
|
margin-top: 6px;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 0.78rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-note {
|
||||||
|
margin-bottom: 0;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 0.82rem;
|
||||||
|
line-height: 1.65;
|
||||||
|
}
|
||||||
|
|
||||||
.launcher-grid {
|
.launcher-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
gap: 18px;
|
gap: 18px;
|
||||||
margin-top: clamp(56px, 9vw, 104px);
|
margin-top: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.launcher-card {
|
.launcher-card {
|
||||||
@@ -181,6 +262,30 @@ p {
|
|||||||
color: var(--cyan);
|
color: var(--cyan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.quick-start-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 18px;
|
||||||
|
margin-top: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-start-card {
|
||||||
|
padding: clamp(22px, 4vw, 32px);
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 20px;
|
||||||
|
background: var(--surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-start-card p:not(.eyebrow) {
|
||||||
|
margin: 18px 0;
|
||||||
|
color: var(--muted);
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-start-card strong {
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
.landing-footer {
|
.landing-footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
@@ -259,10 +364,21 @@ p {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 720px) {
|
@media (max-width: 720px) {
|
||||||
.launcher-grid {
|
.launcher-grid,
|
||||||
|
.quick-start-grid,
|
||||||
|
.workflow-list {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.workflow-card__heading {
|
||||||
|
align-items: start;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-list li {
|
||||||
|
min-height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.launcher-card {
|
.launcher-card {
|
||||||
min-height: 260px;
|
min-height: 260px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,30 @@
|
|||||||
# Integration Tests
|
# Integration Tests
|
||||||
|
|
||||||
두 앱 사이 manifest 계약과 end-to-end golden scenario를 검증합니다.
|
실제 브라우저 API, Worker, 다운로드와 앱 간 데이터 계약을 함께 검증합니다.
|
||||||
|
|
||||||
|
## 실행
|
||||||
|
|
||||||
|
1. 저장소의 `start.cmd`를 실행합니다.
|
||||||
|
2. 최신 Edge 또는 Chrome에서 <http://127.0.0.1:4173/tests/>을 엽니다.
|
||||||
|
3. 모든 항목이 끝날 때까지 페이지를 열어 둡니다.
|
||||||
|
|
||||||
|
Unit과 integration test가 같은 페이지에서 실행됩니다. WebGPU 장치 초기화와 192채널 baseline은
|
||||||
|
수 초 이상 걸릴 수 있습니다. 페이지 제목과 요약이 `PASS`인지 확인하고 실패 항목의 stack을
|
||||||
|
확인하세요. `index.html`을 직접 열면 HTTP, module과 Worker 테스트가 올바르게 동작하지 않습니다.
|
||||||
|
|
||||||
|
## 범위
|
||||||
|
|
||||||
|
- `cpu-baseline.test.js`: 192채널·50×50 synthetic fixture의 CPU DAS와 위치 기준
|
||||||
|
- `webgpu.test.js`: 실제 adapter가 있으면 CPU/GPU map 오차, peak와 강제 bin/point batching;
|
||||||
|
adapter가 없으면 Auto CPU fallback
|
||||||
|
- `server.test.js`: 숨김 저장소 경로 차단, GET/HEAD 제한과 보안 response headers
|
||||||
|
- `builder-ui.test.js`: 이미지·오디오 연결, canvas 편집, Preview WAV/Scene 다운로드, SHA 재연결과
|
||||||
|
잘못된 import 보존
|
||||||
|
- `camera-ui.test.js`: Dedicated Worker CPU/WebGPU 실행, 취소, Result schema, rectangular/Fermat 비교
|
||||||
|
- `workflow-ui.test.js`: Dataset Builder의 Scene JSON과 원본 assets를 Acoustic Camera에 넘겨
|
||||||
|
Result JSON을 저장하는 end-to-end 흐름
|
||||||
|
|
||||||
|
WebGPU adapter가 없는 환경에서는 장치가 필요한 비교 항목을 건너뛰거나 Auto fallback을 검증하므로
|
||||||
|
CPU-only PASS도 가능합니다. 실제 장치의 scaling과 benchmark JSON 저장은
|
||||||
|
<http://127.0.0.1:4173/benchmarks/webgpu-scaling.html>에서 별도로 실행하세요. 기존 실측 조건과
|
||||||
|
해석은 [`../../docs/benchmarks/README.md`](../../docs/benchmarks/README.md)에 있습니다.
|
||||||
|
|||||||
+24
-1
@@ -1,3 +1,26 @@
|
|||||||
# Unit Tests
|
# Unit Tests
|
||||||
|
|
||||||
음향 수식, 좌표 변환, CPU/GPU 수치 일치 테스트를 둡니다.
|
브라우저에서 실행되는 결정론적 수치·계약 테스트입니다. 별도 Node.js test runner가 필요하지
|
||||||
|
않습니다.
|
||||||
|
|
||||||
|
## 실행
|
||||||
|
|
||||||
|
1. 저장소의 `start.cmd`를 실행합니다.
|
||||||
|
2. 최신 Edge 또는 Chrome에서 <http://127.0.0.1:4173/tests/>을 엽니다.
|
||||||
|
3. 페이지 상단의 통과/실패 합계와 각 테스트 결과를 확인합니다.
|
||||||
|
|
||||||
|
Unit과 integration module은 같은 browser runner에서 순서대로 실행됩니다. 다시 실행하려면 페이지를
|
||||||
|
새로 고치세요. 다른 포트를 사용했다면 URL의 포트도 바꾸며, `tests/index.html`을 파일로 직접 열면
|
||||||
|
안 됩니다.
|
||||||
|
|
||||||
|
## 범위
|
||||||
|
|
||||||
|
- `contracts.test.js`: Array·Scene·Experiment·Result schema, 관계 검증, 안전한 asset 경로와 fixture
|
||||||
|
- `acoustics.test.js`: seed 재현성, mix/정규화, 자유음장 전파, calibration, STFT와 CPU DAS
|
||||||
|
- `builder.test.js`: Builder scene model, source/asset 조작, 오디오 크기와 WAV 사전 검증
|
||||||
|
- `evaluation.test.js`: local peak, truth matching, 위치 오차, energy, sidelobe와 main-lobe 지표
|
||||||
|
- `camera.test.js`: canonical input hash, workload 추정, 거리 이동, Result 생성과 schema 검증
|
||||||
|
- `webgpu.test.js`: WGSL buffer packing, bin/point batching, 장치 한계와 typed CPU fallback 정책
|
||||||
|
|
||||||
|
WebGPU unit test 대부분은 실제 GPU 없이 planner와 binary layout을 검증합니다. 실제 GPU 수치 일치는
|
||||||
|
integration test에서 adapter가 있을 때 수행합니다.
|
||||||
|
|||||||
Reference in New Issue
Block a user