docs: README 전체 업데이트 (v5, 2026-05-11 기준)
- Pipeline Summary: GREEN 상수 (CV preset별, LR 0.20, 7s hold, 3fail exit), 힌트 텍스트 표 추가, Spot 5회/Auto 10윈도우, 태블릿 fontScale, 향후 과제 갱신 (IMU 포팅 예정) - USER_GUIDE: 전체 재작성 — Start flow, 힌트 화살표(↑↓←→), 페어링 다이얼로그, Void/Catheterization, Single Scan/Auto Scan, Checking/Final check/In position! 텍스트 반영 - ALGORITHM_COMPARISON: Placement 섹션 현행화 — Start 버튼 필수, phase protection, 화살표 항상 갱신, GREEN 7s+3fail Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+58
-63
@@ -1,6 +1,7 @@
|
||||
# VesiScan-Basic User Guide
|
||||
|
||||
## App Version: VBTAND0101
|
||||
## Last Updated: 2026-05-11
|
||||
|
||||
---
|
||||
|
||||
@@ -23,17 +24,17 @@
|
||||
## 2. Connecting the Device
|
||||
|
||||
1. Tap **Start** on the Home screen
|
||||
2. The app will scan for nearby VesiScan devices
|
||||
2. The app will scan for nearby VesiScan devices (sorted alphabetically)
|
||||
3. When your device appears in the list, tap it to connect
|
||||
4. **Complete the Bluetooth pairing** when the system dialog appears
|
||||
- Do NOT press Cancel — the app will not proceed without pairing
|
||||
- A "Pairing Required" dialog will guide you through the process
|
||||
- Tap **Pair** on the system Bluetooth dialog — canceling will block the connection
|
||||
- The app waits up to 30 seconds for pairing to complete
|
||||
5. Once paired, you will move to the Personalization screen
|
||||
|
||||
### Personalization
|
||||
- Set your **posture** (standing/sitting/lying)
|
||||
- Set the **sensor position**
|
||||
- Set the **Max Volume** (default: 500 mL)
|
||||
- Tap **Next** to proceed to Sensor Alignment
|
||||
- Set your **Max Volume** (200-800 mL, default: 500 mL)
|
||||
- Tap **Continue** to proceed to Sensor Alignment
|
||||
|
||||
---
|
||||
|
||||
@@ -41,45 +42,49 @@
|
||||
|
||||
This step ensures the sensor is positioned correctly over your bladder for accurate measurement.
|
||||
|
||||
### Step 1: Vertical Alignment
|
||||
### Before Starting
|
||||
- Place VesiScan above the pubic bone
|
||||
- Apply ultrasound gel between the sensor and your skin
|
||||
- Tap the **Start Alignment** button (large orange button)
|
||||
|
||||
1. **Place the VesiScan-Basic device just above your pubic bone**
|
||||
2. Apply ultrasound gel between the sensor and your skin
|
||||
3. Tap the **Start Alignment** button
|
||||
4. The app will begin scanning and provide direction:
|
||||
- **"Raise the sensor up"** — slide the device upward toward your navel
|
||||
- **"Lower the sensor down"** — slide the device downward toward your pubic bone
|
||||
- **"Raise the sensor slightly"** / **"Lower the sensor slightly"** — make small adjustments
|
||||
5. Move the sensor **slowly, 1-2mm at a time**
|
||||
6. When the vertical position is correct, the app will show **Step 2/3**
|
||||
### Step 1/3: Vertical Alignment
|
||||
|
||||
> The hint text will show animated dots (e.g., "Raise the sensor up.", "Raise the sensor up..", "Raise the sensor up...") to indicate the system is actively scanning.
|
||||
The app will begin scanning and provide direction:
|
||||
- **"Slide up ↑"** — slide the device upward toward your navel
|
||||
- **"Slide down ↓"** — slide the device downward toward your pubic bone
|
||||
- **"Slide up slightly ↑"** / **"Slide down slightly ↓"** — make small adjustments
|
||||
- Move the sensor **slowly, 1-2mm at a time**
|
||||
- When the vertical position is correct, the app will show **Step 2/3**
|
||||
|
||||
### Step 2: Lateral Alignment
|
||||
> The hint text will show animated dots (e.g., "Slide up ↑.", "Slide up ↑..", "Slide up ↑...") to indicate the system is actively scanning.
|
||||
|
||||
1. The app will first show **"Checking left/right balance..."** for a few seconds
|
||||
### Step 2/3: Lateral Alignment
|
||||
|
||||
1. The app will first show **"Checking..."** for a few seconds
|
||||
2. Then it will guide you:
|
||||
- **"Move left"** — slide the device to the left
|
||||
- **"Move right"** — slide the device to the right
|
||||
- **"Slide left ←"** — slide the device to the left
|
||||
- **"Slide right →"** — slide the device to the right
|
||||
3. Adjust until both lateral sensors (CH4 and CH5) detect the bladder equally
|
||||
4. When balanced, the app will show **Step 3/3**
|
||||
|
||||
### Step 3: Final Check (Green Zone)
|
||||
### Step 3/3: Final Check (Green Zone)
|
||||
|
||||
1. The app checks overall alignment quality
|
||||
2. If everything is optimal, the hint will show **"Optimal position!"**
|
||||
3. The screen will hold this state for **7 seconds** to confirm stability
|
||||
4. Tap **Alignment Complete** to proceed to the measurement screen
|
||||
1. The app checks overall alignment quality (CV + LR deviation)
|
||||
2. If everything is optimal, the hint will show **"In position!"**
|
||||
3. The screen background turns green and holds this state for **7 seconds** to confirm stability
|
||||
4. The **Start Scanning** button (green) becomes active
|
||||
5. Tap **Start Scanning** to proceed to the measurement screen
|
||||
|
||||
### If Alignment is Lost
|
||||
- If the sensor shifts significantly after reaching Optimal:
|
||||
- The app will show **"Position lost. Place sensor above pubic bone, then press Start"**
|
||||
- If the sensor shifts significantly after reaching "In position!":
|
||||
- The app allows up to **3 consecutive failures** before exiting GREEN
|
||||
- After 3 failures: **"Position lost. Restart above pubic bone."**
|
||||
- The scanning will stop automatically
|
||||
- Re-place the sensor and tap **Start Alignment** again
|
||||
|
||||
### If the Sensor is Detached
|
||||
- If the sensor loses contact with skin:
|
||||
- The app will show **"Sensor detached. Place above pubic bone, then press Start"**
|
||||
- The app will show **"Sensor detached. Restart above pubic bone."**
|
||||
- Re-attach the sensor with gel and tap **Start Alignment**
|
||||
|
||||
### Tips
|
||||
@@ -111,7 +116,7 @@ After alignment, you will see the main measurement screen with a donut chart.
|
||||
Current Measurement | 65%
|
||||
280 mL | 325/500 mL
|
||||
|
||||
[Void/Catheterize] [Auto] [Spot]
|
||||
[Void/Catheterization] [Auto Scan] [Single Scan]
|
||||
```
|
||||
|
||||
### Understanding the Display
|
||||
@@ -127,48 +132,47 @@ After alignment, you will see the main measurement screen with a donut chart.
|
||||
|
||||
---
|
||||
|
||||
## 5. Auto Measurement
|
||||
## 5. Auto Scan
|
||||
|
||||
1. Tap the **Auto** button (orange)
|
||||
2. The device will measure continuously every ~1.5 seconds
|
||||
1. Tap the **Auto Scan** button (orange)
|
||||
2. The device will measure continuously (minimum 800ms interval)
|
||||
3. For the first 5 measurements, the display will show **"—"** (collecting data)
|
||||
4. After 5+ measurements, the **trimmed mean** value will be displayed
|
||||
4. After 5+ measurements, the **trimmed mean** value will be displayed (10-sample window, top/bottom excluded)
|
||||
5. The donut chart will update every 5 seconds
|
||||
6. Tap **Stop** (red) to end Auto measurement
|
||||
6. Tap **Stop Scan** (red) to end Auto measurement
|
||||
|
||||
### Auto Measurement Failure
|
||||
- If measurements fail **5 times in a row** (sensor shifted or lost contact):
|
||||
- A dialog will appear: **"Sensor Position Issue"**
|
||||
- Tap **Go to Alignment** to re-align the sensor
|
||||
- If 2+ channels fail **5 times in a row** (sensor shifted or lost contact):
|
||||
- A dialog will appear: **"Position lost. Restart from alignment"**
|
||||
- Tap **Realign** to re-align the sensor
|
||||
- Or tap **Dismiss** to stay on the measurement screen
|
||||
|
||||
---
|
||||
|
||||
## 6. Spot Measurement
|
||||
## 6. Single Scan (Spot)
|
||||
|
||||
1. Tap the **Spot** button
|
||||
2. The device will take **5 consecutive measurements** (~4 seconds total)
|
||||
1. Tap the **Single Scan** button
|
||||
2. The device will take **5 consecutive measurements** (~4 seconds total, max 8 attempts)
|
||||
3. A **spinner** will show during measurement
|
||||
4. The result (trimmed mean of 5 readings) will be displayed immediately
|
||||
5. The donut chart and Current Measurement card will update
|
||||
|
||||
> During Spot measurement, the Auto and Void buttons are disabled.
|
||||
> During Single Scan, the Auto Scan and Void buttons are disabled.
|
||||
|
||||
---
|
||||
|
||||
## 7. Void / Catheterize
|
||||
## 7. Void / Catheterization
|
||||
|
||||
1. Tap the **Void / Catheterize** button
|
||||
1. Tap the **Void / Catheterization** button
|
||||
2. The current measurement is recorded to the voiding diary
|
||||
3. The catheter count decreases by 1
|
||||
4. The bladder level resets to 0
|
||||
5. A toast message **"Voiding recorded"** will appear
|
||||
5. A toast message **"Recorded"** will appear
|
||||
|
||||
### Managing Catheters
|
||||
- The catheter count is shown at the top of the screen (hospital icon + number)
|
||||
- **Tap the catheter count** to add more catheters
|
||||
- Enter the number and tap **Add**
|
||||
- Default starting count: **15 catheters**
|
||||
|
||||
---
|
||||
|
||||
@@ -192,11 +196,6 @@ Tap the **gear icon** at the top right to open settings.
|
||||
| SG Filter | Savitzky-Golay noise filter on/off |
|
||||
| Post Max | Maximum sample index for wall detection (filters floor reflections) |
|
||||
|
||||
### Saving Settings
|
||||
- Adjust values using sliders and toggles
|
||||
- Tap **Save** to apply and close
|
||||
- Tap **Cancel** to close without changes (note: slider changes apply in real-time)
|
||||
|
||||
---
|
||||
|
||||
## 9. Navigation
|
||||
@@ -214,14 +213,13 @@ Tap the **gear icon** at the top right to open settings.
|
||||
|
||||
| Problem | Solution |
|
||||
|---------|----------|
|
||||
| "Place sensor above pubic bone" won't go away | Make sure the sensor is attached with gel, then press **Start Alignment** |
|
||||
| Alignment keeps showing "Raise/Lower" | Move the sensor **very slowly**, 1-2mm at a time |
|
||||
| "Place VesiScan above the pubic bone" won't go away | Make sure the sensor is attached with gel, then press **Start Alignment** |
|
||||
| Alignment keeps showing "Slide up/down" | Move the sensor **very slowly**, 1-2mm at a time |
|
||||
| Auto measurement shows "—" | Wait for at least 5 measurements to accumulate |
|
||||
| Donut chart value doesn't change | Max volume updates every 5 seconds |
|
||||
| Spot measurement takes too long | Each Spot takes ~4 seconds (5 readings) |
|
||||
| "Sensor Position Issue" dialog appears | The sensor may have shifted — go to Alignment |
|
||||
| Single Scan takes too long | Each scan takes ~4 seconds (5 readings) |
|
||||
| "Position lost" dialog appears | The sensor may have shifted — go to Alignment |
|
||||
| Bluetooth connection lost | The app will attempt auto-reconnect (up to 5 times) |
|
||||
| App freezes on measurement screen | Check BLE connection, try disconnecting and reconnecting |
|
||||
| Pairing dialog appears but app won't proceed | You must tap **Pair** — canceling will block the connection |
|
||||
|
||||
---
|
||||
@@ -230,13 +228,10 @@ Tap the **gear icon** at the top right to open settings.
|
||||
|
||||
| LED State | Meaning |
|
||||
|-----------|---------|
|
||||
| Green blink (0.5s) | Bluetooth scanning |
|
||||
| Green on 1s / off 3s | Device idle (not attached) |
|
||||
| Orange blink (1s) | Sensor Alignment in progress |
|
||||
| Green solid | Alignment complete (Optimal position) |
|
||||
| Orange fast blink (3Hz) | Error detected |
|
||||
| Orange solid | Charging |
|
||||
| Blue solid | Charging complete |
|
||||
| State 0 | OFF (default) |
|
||||
| State 4 | Sensor detached warning |
|
||||
| State 5 | Alignment searching (orange) |
|
||||
| State 6 | Alignment complete / In position (green) |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## 작성일: 2026-04-23
|
||||
## 작성자: dwjang
|
||||
## 마지막 업데이트: 2026-05-09 (v4) — VBTAND0101
|
||||
## 마지막 업데이트: 2026-05-11 (v5) — VBTAND0101
|
||||
|
||||
---
|
||||
|
||||
@@ -383,34 +383,46 @@ Android: estimateBladderVolume6ch(6채널)
|
||||
[CH0] 0° ← 가장 위 (배꼽쪽, y=21mm)
|
||||
```
|
||||
|
||||
## 방향 안내 로직 — 3모드 (2026-05-07 업데이트)
|
||||
## 방향 안내 로직 — 3모드 (2026-05-11 업데이트)
|
||||
|
||||
개발자 모드 Settings에서 전환 가능: **Gradient** (기본) / Boundary / **SWEEP**
|
||||
|
||||
### 조작 흐름 (Start 버튼 필수)
|
||||
|
||||
```
|
||||
화면 진입 → "Place VesiScan above the pubic bone, then press Start"
|
||||
↓ Start Alignment 버튼 탭 (96dp 대형)
|
||||
전체 상태 리셋 → 1초 간격 연속 스캔 시작
|
||||
↓
|
||||
VERTICAL → LATERAL → GREEN (순차, 건너뛰기 없음)
|
||||
↓ GREEN 7초 유지 + 3회 연속 실패 안 하면
|
||||
Start Scanning 버튼 활성화 (초록색)
|
||||
```
|
||||
|
||||
### Gradient 모드 (기본) — 신호 길이 비교
|
||||
|
||||
```
|
||||
시작: 치골 위 → 위로 올림
|
||||
시작: 치골 위
|
||||
|
||||
1단계 VERTICAL:
|
||||
0~1채널: ch0/ch1 감지→↑, ch2/ch3 감지→↓
|
||||
2+채널: weighted center gradient
|
||||
weightedCenter = Σ(ch_pos × len) / Σ(len)
|
||||
gradient = weightedCenter - 1.5
|
||||
gradient > 0 → ↓ (ch3쪽 길다 = 방광 아래)
|
||||
gradient < 0 → ↑ (ch0쪽 길다 = 방광 위)
|
||||
|gradient| < 0.4 + lenVariation < 0.5 → 상하 OK
|
||||
gradient > 0 → "Slide down ↓" (ch3쪽 길다 = 방광 아래)
|
||||
gradient < 0 → "Slide up ↑" (ch0쪽 길다 = 방광 위)
|
||||
|gradient| < 0.3 + centerDetected >= 4 → "Checking..." → LATERAL
|
||||
|
||||
2단계 LATERAL:
|
||||
|len4 - len5| ≤ 10 sample → 바로 GREEN
|
||||
lrSymmetry ≥ 0.5 → GREEN
|
||||
아니면 len 차이 방향으로 안내
|
||||
|len4 - len5| ≤ 10 sample → "Final check..." → GREEN
|
||||
lrSymmetry ≥ 0.5 → "Final check..." → GREEN
|
||||
아니면 "Slide left ←" / "Slide right →"
|
||||
|
||||
3단계 GREEN:
|
||||
CV ≤ 0.12 (10회마다 +0.03 완화)
|
||||
|lrDev| ≤ 0.35
|
||||
둘 다 통과 → Green!
|
||||
CV만 간당간당 → "Almost there..." (GREEN에 머물며 재시도)
|
||||
CV ≤ threshold (V0=0.07, V1/V2=0.10, 5회마다 +0.03 완화)
|
||||
|lrDev| ≤ 0.20
|
||||
둘 다 통과 → "In position!" (7초 hold)
|
||||
3회 연속 실패 → "Position lost. Restart above pubic bone." + 스캔 중지
|
||||
```
|
||||
|
||||
### SWEEP 모드 (CKLaw) — 위에서 아래로 쓸어내리기
|
||||
@@ -446,26 +458,44 @@ Android: estimateBladderVolume6ch(6채널)
|
||||
|
||||
| 상수 | 값 | 의미 |
|
||||
|------|-----|------|
|
||||
| DEFAULT_CV_THR | 0.12 | 기본 CV 임계 (10회마다 +0.03 완화) |
|
||||
| DEFAULT_LR_DEV_THR | 0.35 | LR 비대칭 임계 |
|
||||
| DEFAULT_CV_THR | V0=0.07, V1/V2=0.10, 기타=0.08 | 기본 CV 임계 |
|
||||
| CV 완화 | +0.03 per 5 scans | 5회마다 임계값 증가 |
|
||||
| DEFAULT_LR_DEV_THR | 0.20 | LR 비대칭 임계 |
|
||||
| 절대 LR 차이 | ≤10 sample | LATERAL 즉시 통과 |
|
||||
| R-peak tolerance | 1.5mm | SWEEP 모드 GREEN gate |
|
||||
| 디바운스 | 1.5초 | phase 전환은 즉시, 같은 phase 힌트만 디바운스 |
|
||||
| GREEN hold | 7초 | GREEN 진입 후 최소 유지 시간 |
|
||||
| GREEN exit | 3회 연속 실패 | hold 이후 이탈 조건 |
|
||||
| 디바운스 | 1.5초 (같은 phase) / 3초 (phase 전환) | |
|
||||
| Phase 보호 | 3스캔 | phase 전환 후 힌트 강제 유지 |
|
||||
|
||||
### 힌트 텍스트 (모든 방향 힌트에 화살표 포함)
|
||||
|
||||
| 상황 | 텍스트 |
|
||||
|------|--------|
|
||||
| 상하 이동 | "Slide up ↑" / "Slide down ↓" / "Slide up slightly ↑" / "Slide down slightly ↓" |
|
||||
| 좌우 이동 | "Slide left ←" / "Slide right →" |
|
||||
| 확인 중 | "Checking..." |
|
||||
| 최종 확인 | "Final check..." |
|
||||
| 최적 위치 | "In position!" |
|
||||
| 위치 이탈 | "Position lost. Restart above pubic bone." |
|
||||
| 센서 미부착 | "Sensor detached. Restart above pubic bone." |
|
||||
|
||||
## 연속 스캔 동작
|
||||
|
||||
```
|
||||
"Start Scanning" 버튼 탭
|
||||
"Start Alignment" 버튼 탭 (waitingForStart → false)
|
||||
↓
|
||||
전체 상태 리셋 (phase, score, debounce, greenFail 등)
|
||||
↓
|
||||
즉시 첫 스캔 (maa 6채널)
|
||||
↓ 결과: CV, 방향, Score 표시
|
||||
3초 대기
|
||||
↓ 결과: CV, 방향, Score 표시 (28sp 대형 힌트)
|
||||
1초 대기
|
||||
↓
|
||||
자동 재스캔 (repeatCount 증가 → threshold 완화)
|
||||
자동 재스캔 (scanCount 증가 → CV threshold 완화)
|
||||
↓
|
||||
... 반복 (사용자가 Stop 누를 때까지)
|
||||
... 반복 (GREEN 도달 or 이탈 시 자동 중지)
|
||||
|
||||
is_pass = true → Lock → "Start Measurement" 활성화
|
||||
is_pass = true → Lock → 7초 hold → "Start Scanning" 버튼 활성화
|
||||
```
|
||||
|
||||
---
|
||||
@@ -504,20 +534,22 @@ Spot 버튼 탭
|
||||
화면에 "305 ml" 표시! (도넛차트 + 레벨 업데이트)
|
||||
```
|
||||
|
||||
## Auto 모드 (2026-05-07 업데이트)
|
||||
## Auto Scan 모드 (2026-05-11 업데이트)
|
||||
|
||||
별도 Auto 버튼으로 자동 반복 측정:
|
||||
- 1.5초 간격 자동 측정
|
||||
- 첫 5회: "Measuring... (n/5)" 로딩 표시
|
||||
- 5회 이상: **sliding window trimmed mean** (10개 윈도우) → 안정된 값 표시
|
||||
- Stop 버튼으로 종료
|
||||
"Auto Scan" 버튼으로 자동 반복 측정:
|
||||
- 최소 800ms 간격 자동 측정 (BLE maa throttle 보장)
|
||||
- 첫 5회: "—" 표시 (데이터 수집 중)
|
||||
- 5회 이상: **sliding window trimmed mean** (10개 윈도우, 최대/최소 각 1개 제외 → 8개 평균)
|
||||
- "Stop Scan" 버튼으로 종료
|
||||
- Auto fail: 2ch+ missing 5회 연속 → "Position lost. Restart from alignment" 다이얼로그
|
||||
|
||||
## Spot 모드 (2026-05-07 업데이트)
|
||||
## Single Scan 모드 (2026-05-11 업데이트)
|
||||
|
||||
Spot 버튼 1탭 → **3회 측정 → median** (중간값 깜빡임 없음):
|
||||
- 최대 6회까지 재시도 (유효값 3개 수집 목표)
|
||||
- 측정 간격 300ms (BLE 겹침 방지)
|
||||
"Single Scan" 버튼 1탭 → **5회 측정 → trimmed mean**:
|
||||
- 최대 8회까지 재시도 (유효값 5개 수집 목표, 최소 3개)
|
||||
- 측정 간격: maa 800ms throttle이 간격 보장
|
||||
- 중간 값 화면 미표시 (isSpotInProgress 플래그)
|
||||
- 1초 쿨다운 후 재사용 가능
|
||||
|
||||
## 측정 실패 시 자동 재시도
|
||||
|
||||
@@ -1058,7 +1090,15 @@ Start Scanning (3초 간격 자동 반복)
|
||||
║ PASS → LED 6 (ALIGN_COMPLETE) + Lock ║
|
||||
╚═══════════════════════════════════════╝
|
||||
↓ Green!
|
||||
"Start Measurement" 버튼 활성화
|
||||
"Start Scanning" 버튼 활성화 (초록, 96dp)
|
||||
```
|
||||
|
||||
### 태블릿 대응
|
||||
|
||||
```
|
||||
fontScale = (screenWidth / 360f).coerceIn(1.0, 1.8)
|
||||
→ 폰(360dp)=1.0배, 태블릿(600dp+)=~1.6배
|
||||
버튼/힌트 텍스트, InfoCard, 배터리/카테터 표시 등 전체 적용
|
||||
```
|
||||
|
||||
### 패킷 드랍 대응
|
||||
@@ -1267,6 +1307,13 @@ ADC 파싱 시 Big/Little Endian을 자동 감지:
|
||||
| ADC CSV 저장 | ✅ 완료 | 세션별 파일 |
|
||||
| Canvas 하복부 실루엣 | ✅ 완료 | PNG 제거, CKLaw Bezier torso |
|
||||
| i18n (한국어/영어) | ✅ 완료 | strings.xml |
|
||||
| UI 텍스트 통일 | ✅ 완료 | revised 문서 반영 (Slide up ↑, In position! 등) |
|
||||
| 태블릿 대응 | ✅ 완료 | fontScale, 비율 기반 레이아웃, 힌트/버튼 확대 |
|
||||
| 페어링 다이얼로그 | ✅ 완료 | bonding 대기 중 안내 표시 |
|
||||
| 앱 아이콘 | ✅ 완료 | 방광이 캐릭터 PNG (5 density) |
|
||||
| Placement 화살표 수정 | ✅ 완료 | 디바운스/보호 중에도 화살표 항상 표시 |
|
||||
| GREEN 안정성 | ✅ 완료 | 7초 hold + 3회 연속 실패 exit + phase 보호 |
|
||||
| IMU 모션 감지 | 🔜 예정 | vesiscanbasicAndroid에서 포팅 예정 |
|
||||
| Placement 팬텀 테스트 | 🔜 예정 | Gradient vs SWEEP 비교 |
|
||||
| 5-Anchor calibration | 🔜 검토 | CKLaw 전체 포팅 여부 결정 |
|
||||
| 인체 임상 검증 | 🔜 예정 | 상수 재조정 |
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
# Method A / B / C Algorithm Comparison & Placement Test
|
||||
|
||||
## Wall Detection Comparison
|
||||
|
||||
### Pipeline
|
||||
|
||||
```
|
||||
Method A: raw → SG → composite(sg+|d1|+|d2|) → plateau score → plateau spans
|
||||
→ prominence wall (ANT=nearest, POST=edge_score)
|
||||
→ cross-validation → BV(frustum+ellipse cap, 1ch=sphere)
|
||||
※ 채널별 min_wall_contrast_ratio × cos(θ) 보정
|
||||
|
||||
Method B: raw → SG → TGC(multichannel log-fit) → initial detect(Otsu×0.9×cos(θ))
|
||||
→ BR candidate(per-ch, post-only, prominence≥50)
|
||||
→ consensus(median, outlier_tol=4, min_votes=3)
|
||||
→ is_reflection_only(shared+cluster, amp_ratio=1.5)
|
||||
→ resolve(suspicious post: post>ant×1.5)
|
||||
→ suppress(valley early-stop 50.0) → rerun detect
|
||||
→ choose(rerun vs initial) → BV(frustum+ellipse cap, 1ch=sphere)
|
||||
|
||||
Method C: raw → MedianFilter(win=7) → SG+wavelet
|
||||
→ ImpulseReject(2-pass Hampel, lumen clean)
|
||||
→ OS-CFAR(per-sample) → lumen mask
|
||||
→ prominence wall + subsample refine
|
||||
→ StaLta impulse purity check
|
||||
→ anatomical gate (antDepth≥10mm, chord 5-110mm)
|
||||
→ BModeScore V41 (wamp=0.45, stalta=0.20, far=0.15, dark=0.10)
|
||||
→ ChordConsensus (Tukey MAD + Fischler-Bolles pair test)
|
||||
→ BV dispatch(frustum/chord/sphere, trusted channels only)
|
||||
```
|
||||
|
||||
### Stage-by-Stage
|
||||
|
||||
| Stage | **A (Plateau)** | **B (Otsu+TGC+BR)** | **C (V4.1 CFAR)** |
|
||||
|-------|-----------------|----------------------|---------------------|
|
||||
| Pre-filter | none | none | MedianFilter(win=7) + ImpulseReject(Hampel) |
|
||||
| Denoising | SG(5,2) | SG(5,2) → log-TGC | SG(5,2) + db4 wavelet 3-level |
|
||||
| Threshold | sliding plateau score Q=0.5 | Otsu×0.9×cos(θ) | OS-CFAR (win=15, rank=40%, scale=1.05) |
|
||||
| Low-echo mask | platScore < thr | sg <= otsu (full) | sg <= median(CFAR) |
|
||||
| Span merge | gap≤5, simple | gap<3: unconditional, ≥3: wall check | mergeGapMax=5, gapPeakMargin=50 |
|
||||
| Span refine | none | refineRightEdge | none |
|
||||
| Wall select | ANT=nearest, POST=edge_score, decay=0.15 | ANT=nearest, POST=edge_score, decay=0.12 | prominence + subsample, MAX_CANDIDATES_POST=64 |
|
||||
| Valley logic | descending-first, rise=5 | descending-first, rise=50 | rise=50 |
|
||||
| Extra conditions | contrast×cos(θ)≥1.2, prom≥3 | peakMin=max(low+30,thr), sg[ant/post]≥thr, postProm≥30 | Kremkau ant fallback, antMinIdx=7 |
|
||||
| Score | sg[ant]+sg[post] | asymmetric ant_reliability | BModeScore V41 (wamp+stalta+far+dark+grad) |
|
||||
| FP filter | cross-validation | score<3000 | AnatomicalGate + ChordConsensus |
|
||||
| Impulse check | none | none | StaLta (STA/LTA ratio) |
|
||||
| Channel filter | none | none | ChordConsensus (MAD outlier + pair test) |
|
||||
| Back reflection | none | consensus multichannel BR | none |
|
||||
| Fallback | none | ant=max(0,s-(post-e)) | B-mode tier |
|
||||
|
||||
### BV Estimation
|
||||
|
||||
| | **A** | **B** | **C** |
|
||||
|---|---|---|---|
|
||||
| Model | shared with B | frustum + ellipse cap | Multi-dispatch (trusted ch only) |
|
||||
| 1-channel | sphere V=(4/3)πR³ | same | Verathon chord |
|
||||
| Channel filter | none | none | ChordConsensus (drop inconsistent) |
|
||||
| Cap height | 8-point ellipse LSQ | same | fixed: bottom=hemisphere, top=cone |
|
||||
| LR ratio | shared with B | 2-lateral ellipse solve + shrinkage | 1/avg_ratio, clamp[1.0,1.6] |
|
||||
| Confidence | none | none | 0.95(4C+2L) ~ 0.20(1ch) |
|
||||
| DPS | PiezoHW (1.968) | same | 1.9309 |
|
||||
|
||||
### Angle Correction (PR#20)
|
||||
|
||||
Both Method A and B apply per-channel beam angle correction:
|
||||
- **Method B**: `otsu_ratio = OTSU_RATIO × cos(θ)` per channel
|
||||
- **Method A**: `min_wall_contrast_ratio = 1.2 × cos(θ)` per channel
|
||||
- Larger angles → lower threshold → better detection of weak posterior walls
|
||||
|
||||
### Back Reflection System (Method B)
|
||||
|
||||
```
|
||||
Per-channel:
|
||||
1. initial detect → (ant, post)
|
||||
2. build_candidate: strongest peak AFTER post (prominence≥50)
|
||||
- global BR ≈ post (±3) → None (no separate BR)
|
||||
- else → strongest significant peak after post
|
||||
|
||||
Multichannel:
|
||||
3. consensus: median, outlier_tol=4, min_votes=3
|
||||
4. is_reflection_only: post > ant × 1.5 (amp_ratio)
|
||||
- Condition A: shared_idx based
|
||||
- Condition B: post cluster based
|
||||
|
||||
Per-channel resolve:
|
||||
5. is_suspicious_post: post > ant × 1.5 OR post ≈ shared (±2)
|
||||
6. select_idx → resolve → suppress(valley early-stop 50.0)
|
||||
7. rerun detect → choose(rerun vs initial, min_gap=8)
|
||||
```
|
||||
|
||||
### Method C V4.1 New Features (2026-05-04)
|
||||
|
||||
```
|
||||
Pre-processing:
|
||||
MedianFilter(win=7) → remove speckle bumps
|
||||
ImpulseReject(2-pass Hampel) → clean 1-3 sample impulses in lumen
|
||||
|
||||
Detection:
|
||||
DetectLumenFirst V41_PARAMS:
|
||||
mergeGapMax=5, gapPeakMargin=50
|
||||
maxCandidatesPost=64, medianWin=7, antMinIdx=7
|
||||
Kremkau half-amplitude ant fallback
|
||||
|
||||
Quality:
|
||||
StaLta: STA/LTA ratio → wall edge vs reverberation discrimination
|
||||
BModeScore V41_WEIGHTS:
|
||||
wamp=0.45 (wall-peak amplitude)
|
||||
stalta=0.20 (impulse purity)
|
||||
far=0.15, dark=0.10, ant=0.05, post=0.05
|
||||
|
||||
Channel Filtering:
|
||||
ChordConsensus: Tukey MAD outlier + Fischler-Bolles pair test
|
||||
→ drop geometrically inconsistent channels before BV
|
||||
|
||||
Anatomical Gate:
|
||||
antDepthMin: 22 → 10mm (Neyman-Pearson loose prior)
|
||||
strict threshold removed → delegated to B-mode score
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Python vs Kotlin Sync Status (2026-05-04)
|
||||
|
||||
### Method A/B: 18 functions compared — CRITICAL 0, MINOR 5
|
||||
|
||||
All config constants match: LOW_ECHO_AMP=1250, OTSU_RATIO=0.9, MIN_SCORE=3000,
|
||||
EDGE_DIST_DECAY=0.12, VALLEY_STOP_RISE=50, etc.
|
||||
|
||||
### Method C: Direct copy from CharlesKWONsLaw (45 files, package renamed)
|
||||
|
||||
---
|
||||
|
||||
## Placement Test Algorithm
|
||||
|
||||
### Overview (2026-05-11 updated)
|
||||
|
||||
```
|
||||
Screen entry → "Place VesiScan above the pubic bone, then press Start"
|
||||
↓ Start Alignment 버튼 탭 (waitingForStart=false)
|
||||
전체 상태 리셋
|
||||
↓
|
||||
[1-second interval loop]
|
||||
↓
|
||||
mpa (first time, 500ms delay) → maa → 6ch reb
|
||||
↓
|
||||
Detachment check (6ch received, THR=30) → LED 4 if detached
|
||||
↓
|
||||
Wall detection (Method A/B/C) → urineLen ≥ 12
|
||||
↓
|
||||
computePlacementGuide() → 3-stage guide (VERTICAL → LATERAL → GREEN)
|
||||
↓
|
||||
Debounce (2 consecutive + 1.5s/3s hold) + Phase protection (3 scans)
|
||||
↓
|
||||
Arrow direction: always updated (directionIcon synced with guideResult)
|
||||
↓
|
||||
LED: stage change only (4=detach, 5=searching, 6=complete)
|
||||
↓
|
||||
GREEN: 7s hold + 3 consecutive fail to exit
|
||||
↓
|
||||
Screen exit → LED OFF
|
||||
```
|
||||
|
||||
### Guide Modes
|
||||
|
||||
**Gradient** (default): weighted center gradient → LATERAL → GREEN
|
||||
**Boundary**: up → detect boundary (max drops) → come back → LATERAL
|
||||
**SWEEP** (CKLaw): navel → slide down → sagittalArgMaxCh → LATERAL → GREEN
|
||||
|
||||
### 3-Stage Guide
|
||||
|
||||
**Stage 1 VERTICAL**: detect count + gradient + "Slide up/down ↑↓"
|
||||
**Stage 2 LATERAL**: |len4-len5| ≤ 10 → GREEN, else "Slide left/right ←→"
|
||||
**Stage 3 GREEN**: CV ≤ threshold AND |LR dev| ≤ 0.20 → "In position!" (7s hold)
|
||||
|
||||
### Measurement Modes
|
||||
|
||||
- **Auto Scan**: min 800ms gap, 10-sample trimmed mean (max/min removed, 8 avg)
|
||||
- **Single Scan (Spot)**: 5회 측정 → trimmed mean (max 8 attempts, min 3)
|
||||
- valid ≥ 4 channels only
|
||||
- Analysis on coroutine (Dispatchers.Default)
|
||||
- Auto fail: 2ch+ missing 5 consecutive → realign dialog
|
||||
- Detachment → LED 4 (once), recovery → LED 0 (once)
|
||||
- Foreground Service: BLE connection maintained during screen off
|
||||
- BLE 로그: 실시간 파일 기록 (Downloads/VesiScan_BLE_*.log, 화면 잠금에도 보존)
|
||||
- CSV: 세션별 파일 (YYYY-MM-DD_HHmmss.csv)
|
||||
|
||||
### Placement GREEN 기준
|
||||
|
||||
```
|
||||
CV threshold = DEFAULT_CV_THR + (scanCount / 5) × 0.03
|
||||
DEFAULT_CV_THR: V0=0.07, V1/V2=0.10, 기타=0.08
|
||||
LR deviation ≤ 0.20
|
||||
GREEN hold: 7초 (greenLockedAtMs 기준)
|
||||
GREEN exit: 3회 연속 실패 (greenFailCount)
|
||||
```
|
||||
|
||||
### Test Results (2026-05-04, 500ml phantom, Method C V4.1)
|
||||
|
||||
```
|
||||
총 측정: 223회
|
||||
평균: 492.4ml (오차 -1.5%)
|
||||
±15% 이내: 223/223 (100%)
|
||||
범위: 441~537ml
|
||||
```
|
||||
Reference in New Issue
Block a user