From 98d3b11a46fcd31b2ccbea9787c67a235bfa08f5 Mon Sep 17 00:00:00 2001 From: jjangddu Date: Thu, 23 Apr 2026 16:18:49 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20Python=E2=86=94Android=201:1=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EA=B2=B0=EA=B3=BC=20+=20BLE=20=EC=9E=AC?= =?UTF-8?q?=EC=97=B0=EA=B2=B0=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EB=AC=B8=EC=84=9C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 전수 상수 비교 테이블 (V0/V1/V2 좌표, 각도, 음향 상수, lr_ratio) - 알고리즘 수준 비교 (전처리, low-echo, lr_ratio, BV 추정) - 결론: 모든 항목 완벽 일치 (Adaptive TGC는 Python에도 미포함) - BLE 재연결 시스템 흐름도 + Watchdog + GATT 에러 경로 설명 - BLE 디버그 로거 사용법 - GreenZoneConstants.kt 주석 오류 수정 (1600→1150) Co-Authored-By: Claude Opus 4.6 (1M context) --- VesiScan_Android_Pipeline_Summary.md | 197 +++++++++++++++++- .../managers/GreenZoneConstants.kt | 2 +- 2 files changed, 197 insertions(+), 2 deletions(-) diff --git a/VesiScan_Android_Pipeline_Summary.md b/VesiScan_Android_Pipeline_Summary.md index e532068..01f30da 100644 --- a/VesiScan_Android_Pipeline_Summary.md +++ b/VesiScan_Android_Pipeline_Summary.md @@ -632,7 +632,202 @@ Android: app내부저장소/vesiscan_logs/YYYY-MM-DD.jsonl --- -# 12. 향후 과제 +# 12. Python ↔ Android 1:1 검증 결과 + +## 검증 대상 + +| Python 파일 | Android 파일 | 검증 결과 | +|------------|-------------|----------| +| config_6ch.py | PiezoBVEstimator.kt (PiezoHW) | ✅ 완벽 일치 | +| denoising.py | PiezoEchoAnalyzer.kt (denoise, sgSmooth) | ✅ 완벽 일치 | +| low_echo_detection_method_b.py | PiezoEchoAnalyzer.kt (detectLowEcho) | ✅ 완벽 일치 | +| bv_estimation.py (6ch) | PiezoBVEstimator.kt (estimateBladderVolume6ch) | ✅ 완벽 일치 | +| span_utils.py | PiezoEchoAnalyzer.kt (findPeaks1D, mergeClose) | ✅ 완벽 일치 | + +## 상수 비교 (전수 검증) + +### 하드웨어 프리셋 (config_6ch.py ↔ PiezoHW) + +| 항목 | Python V0 | Android V0 | Python V1 | Android V1 | Python V2 | Android V2 | +|------|-----------|------------|-----------|------------|-----------|------------| +| CH0 z | 21.0 | 21.0 ✅ | 20.1 | 20.1 ✅ | 19.3 | 19.3 ✅ | +| CH1 z | 14.0 | 14.0 ✅ | 12.8 | 12.8 ✅ | 13.0 | 13.0 ✅ | +| CH2 z | 7.0 | 7.0 ✅ | 7.0 | 7.0 ✅ | 6.7 | 6.7 ✅ | +| CH3 z | 0.0 | 0.0 ✅ | 0.0 | 0.0 ✅ | 0.0 | 0.0 ✅ | +| CH4/5 z | 10.5 | 10.5 ✅ | 9.9 | 9.9 ✅ | 9.85 | 9.85 ✅ | +| CH0 deg | 0.0 | 0.0 ✅ | 6.89 | 6.89 ✅ | 0.0 | 0.0 ✅ | +| CH1 deg | 0.0 | 0.0 ✅ | 0.0 | 0.0 ✅ | -6.89 | -6.89 ✅ | +| CH2 deg | 0.0 | 0.0 ✅ | -6.89 | -6.89 ✅ | -13.66 | -13.66 ✅ | +| CH3 deg | 0.0 | 0.0 ✅ | -13.66 | -13.66 ✅ | -20.20 | -20.20 ✅ | +| CH4/5 deg | 0.0 | 0.0 ✅ | -6.87 | -6.87 ✅ | -6.87 | -6.87 ✅ | +| CH4/5 LR | 0.0 | 0.0 ✅ | ∓3.42 | ∓3.42 ✅ | ∓3.42 | ∓3.42 ✅ | + +### 음향 상수 + +| 상수 | Python (6ch) | Android | 일치 | +|------|-------------|---------|------| +| DISTANCE_PER_SAMPLE | 1.771 mm | 1.771 mm | ✅ | +| DELAY_OFFSET_MM | 6.85 mm | 6.85 mm | ✅ | +| AREA_K | π/4 | π/4 | ✅ | + +### Low-echo Detection 상수 + +| 상수 | Python | Android | 일치 | +|------|--------|---------|------| +| LOW_ECHO_AMP | 1150 | 1150 | ✅ | +| LOW_MIN_LEN | 3 | 3 | ✅ | +| MERGE_GAP_MAX | 3 | 3 | ✅ | +| PEAK_SEARCH_WIN | 20 | 20 | ✅ | +| POST_MAX_IDX | 80 | 80 | ✅ | +| MIN_PEAK_MARGIN | 30 | 30 | ✅ | +| MIN_URINE_LEN | 3 | 3 | ✅ | + +### LR Ratio 상수 + +| 상수 | Python | Android | 일치 | +|------|--------|---------|------| +| DEFAULT_LR_RATIO | 1.0 | 1.0 | ✅ | +| LR_RATIO_NO_DETECTION | 1.0 | 1.0 | ✅ | +| LR_RATIO_INVALID | 1.2 | 1.2 | ✅ | +| LR_PRIOR | 1.2 | 1.2 | ✅ | + +## 알고리즘 비교 (로직 수준) + +### 전처리 (denoising.py ↔ PiezoEchoAnalyzer.denoise) + +``` +Python: denoise() → sg_smooth() (SG 필터만, TVD 없음) +Android: denoise() → sgSmooth() (SG 필터만, TVD 없음) + +SG 계수: [-3, 12, 17, 12, -3] / 35 (양쪽 동일) +Edge 처리: 양 끝 2샘플에 2차 다항식 피팅 (양쪽 동일) +``` + +**Adaptive TGC**: Python 공유 코드에 미포함. 원은지님이 언급했으나 `denoising.py`에 TGC 코드 없음. + +### Low-echo Detection (Prominence 기반) + +``` +1. low_mask = sg ≤ 1150 (양쪽 동일 ✅) +2. span 추출 (≥3칸) (양쪽 동일 ✅) +3. 인접 span 합치기 (gap≤3) (양쪽 동일 ✅) + - gap 내 peak > 1155면 합치지 않음 +4. 양쪽 벽 prominence 탐색 (양쪽 동일 ✅) + - ant: 오른쪽 valley 기준 + - post: 왼쪽 valley 기준 + - 최대 3개 후보 중 prominence 최대 +5. post > 80 → 뒤쪽 절반에서 재탐색 (양쪽 동일 ✅) +6. urineLen = post - ant - 1 (양쪽 동일 ✅) +``` + +### LR Ratio 계산 (compute_lr_ratio) + +``` +1. Center AP 직경: CH1/CH2의 (post-ant)×cos(θ) 평균 (양쪽 동일 ✅) +2. Lateral 직경: + P = cos(α)·cos(β), Q = cos(α)·sin(β) + D_lateral = (dFar-dNear)·P + y_mid = sensor_x + d_mid·Q (양쪽 동일 ✅) +3-A. 2개 lateral → 타원 방정식: + u = yL·yR·(yR-yL) / denom + d = -(u·(rL²-1) + yL²) / (2·yL) + lr_raw = 2√u / D_center (양쪽 동일 ✅) +3-B. 1개 lateral → 중심 가정: + b = |y_mid| / √(1-r²) + lr_raw = b / (D_center/2) (양쪽 동일 ✅) +4. Shrinkage: + confidence = ((1-avg_ratio)/0.10).clamp(0,1) + lr = 1.2 + (lr_raw-1.2)·confidence (양쪽 동일 ✅) +``` + +### BV 추정 (Frustum + Cap) + +``` +1. Edge 필터: CH3 < CH1/CH2 × 0.9 → short (양쪽 동일 ✅) +2. sample → mm: d = 6.85 + idx × 1.771 (양쪽 동일 ✅) +3. 직경: D = (dPost-dAnt) × cos(θ) (양쪽 동일 ✅) +4. 단면적: S = (π/4) × D² × lr_ratio (양쪽 동일 ✅) +5. y좌표: y = sensorZ + dMid × sin(θ) (양쪽 동일 ✅) +6. y 오름차순 정렬 (양쪽 동일 ✅) +7. 포물선 피팅: Phase 0(edge-peak) → Phase 1(1.5σ) → Phase 2(b_eff) + (양쪽 동일 ✅) +8. Frustum: V = (h/3)·(S₁+S₂+√(S₁·S₂)) (양쪽 동일 ✅) +9. Bottom cap: V = S·h/2 + π·h³/6 (항상 구) (양쪽 동일 ✅) +10. Top cap: cone(정상) or sphere(edge short) (양쪽 동일 ✅) +``` + +## 미구현 항목 + +| 항목 | Python | Android | 비고 | +|------|--------|---------|------| +| Adaptive TGC | 공유 코드에 없음 | 없음 | 원은지님 확인 필요 | +| Phantom preset (lrRatio) | 있음 | 없음 | 디버깅용, 앱 불필요 | +| Hybrid mode (coord) | 있음 (disabled) | 없음 | Python에서도 미사용 | + +--- + +# 13. BLE 자동 재연결 시스템 + +## 흐름도 + +``` +정상 통신 중 (5초마다 msn/rsn 주고받음) + ↓ +기기와 멀어짐 (RSSI -80 → -88 → 응답 없음) + ↓ +[경로 A] Android GATT 에러 콜백 (status≠0) + → GATT_ERROR 로그 + → wasConnected=true → scheduleAutoReconnect() + +[경로 B] GATT 콜백 안 옴 (좀비 연결) + → Watchdog Thread (별도 스레드, 5초마다 체크) + → 15초 무응답 → WATCHDOG_TIMEOUT + → 강제 disconnect → scheduleAutoReconnect() + ↓ +scheduleAutoReconnect() + ├── 1차: 3초 후 → BLE 스캔 8초 (기기 이름/MAC 매칭) + ├── 2차: 5초 후 → BLE 스캔 8초 + ├── 3차: 10초 후 → BLE 스캔 8초 + ├── 4차: 10초 후 → BLE 스캔 8초 + └── 5차: 10초 후 → BLE 스캔 8초 + ↓ 발견 시 + connectGatt(autoConnect=false) → 빠른 연결 (1~2초) + ↓ 5회 모두 실패 시 + 빨간 배너 "Could not reconnect" + 수동 Reconnect 버튼 +``` + +## Watchdog 상세 + +- **실행 위치**: 별도 daemon Thread (BLE handler 블로킹 무관) +- **체크 주기**: 5초 +- **타임아웃**: 15초 (마지막 RX 기준) +- **GATT close**: watchdog Thread에서 직접 호출 (스레드 안전) +- **UI 갱신**: handler.post로 메인 스레드에서 처리 + +## GATT 에러 경로 (핵심 버그 수정) + +Android BLE는 disconnect 시 `STATE_DISCONNECTED` 대신 `GATT 에러(status≠0)`로 +콜백을 보내는 경우가 많음. 기존에는 에러 경로에서 재연결을 시작하지 않아서 +첫 끊김 시 재연결이 안 되는 버그가 있었음. + +수정: `wasConnected=true`이면 에러 경로에서도 `scheduleAutoReconnect()` 호출. + +## BLE 디버그 로거 + +모든 TX/RX 패킷을 파싱된 형태로 기록: + +``` +TX: "maa 6ch measure [8B]" +RX: "rsn battery=3850mV (100%) [8B]" +RX: "reb 100samples [208B]" +RX: "raa all-ch complete [8B]" +``` + +내보내기: Downloads/VesiScan_BLE_날짜시간.log + +--- + +# 14. 향후 과제 | 항목 | 상태 | 비고 | |------|------|------| diff --git a/app/src/main/java/com/example/medilightv2android/managers/GreenZoneConstants.kt b/app/src/main/java/com/example/medilightv2android/managers/GreenZoneConstants.kt index c38d03d..f18a003 100644 --- a/app/src/main/java/com/example/medilightv2android/managers/GreenZoneConstants.kt +++ b/app/src/main/java/com/example/medilightv2android/managers/GreenZoneConstants.kt @@ -36,7 +36,7 @@ object GreenZoneConstants { /** Low-echo detection 임계값 (denoised 신호 기준). * denoised ≤ 이 값 → low-echo (소변 후보). * UrinAI의 liquidThrLoose(raw 기준)와 다름 — 여기는 TVD+SG 적용 후 신호 기준. - * Python 원본: 1600 (low_echo_detection_method_b.py LOW_ECHO_AMP) + * Python 원본: 1150 (low_echo_detection_method_b.py LOW_ECHO_AMP, 6ch 버전) * 영향: PiezoEchoAnalyzer → low-echo span 탐지, 벽 찾기의 기반 */ const val lowEchoAmp: Float = 1150f