docs: 5개 md 일괄 sync (버전/날짜/package path/V2 6-stage/CH3 flicker)

- USER_GUIDE.md
  * versionCode 24 → 26, App 1.0.0-design → 1.2.0-demo
  * Last Updated 2026-05-26 → 2026-07-13
  * Recommended FW VBTFW0116 → VBTFW0120+ (mim FIFO 필수)
  * PinView.kt / AppState.kt link path com.example → com.medithings.vesiscan
- docs/BLE_PROTOCOL_REFERENCE.md
  * 작성일 2026-06-05 → 2026-07-13, 검증 FW VBTFW0116 → VBTFW0120+
  * package path 52건 com.example → com.medithings.vesiscan (모든 코드 링크 복구)
- docs/ALGORITHM_COMPARISON.md
  * 3-Stage 를 "V1 (일반 진입)" 로 명시
  * "V2 6-Stage Guide (AlignGuide4Stage)" 신규 섹션: phase 표 + CH3 flicker
    3-Layer (majority / relaxed / soft-hint) + Python replay 검증 요약
  * 헤더에 2026-07-13 update 표시
- docs/FLAVOR_DEMO_STABLE.md
  * example source path 표기 com/example/... → com/medithings/vesiscan/...
- VesiScan_Android_Pipeline_Summary.md
  * 남아있던 com/example/ 경로 1건 정리

기능 변화 없음. 문서-코드 일관성 확보 (기존 링크가 리네임 이후 broken 상태였음).
This commit is contained in:
2026-07-13 15:28:43 +09:00
parent eb25c01c97
commit 1cf45df80c
5 changed files with 92 additions and 62 deletions
+5 -5
View File
@@ -1,8 +1,8 @@
# VesiScan-Basic User Guide
## App Version: 1.0.0-design (versionCode 24)
## Last Updated: 2026-05-26
## Recommended Firmware: **VBTFW0116** (pending slot 1→8 확장, ADC drop 안정)
## App Version: 1.2.0-demo (versionCode 26)
## Last Updated: 2026-07-13
## Recommended Firmware: **VBTFW0120+** (mim FIFO 지원 필수. VBTFW0121 은 `mls mode 0` handler 취약)
---
@@ -15,7 +15,7 @@
4. ~~Set a 4-digit PIN~~ — **현재 빌드는 PIN 데모 자동통과**: PinView 진입 시 즉시 verified 처리 (시연용)
5. You will arrive at the **Home** screen
> ⚠️ **DEMO 빌드 표시**: 현재 빌드는 PIN 잠금이 우회됩니다 ([PinView.kt:23-29](app/src/main/java/com/example/medilightv2android/ui/views/pin/PinView.kt#L23-L29)). 원본 UI는 `if (false) { ... }` 안에 보존되어 있어 배포 시 1줄 제거로 복구 가능. 배포 전 정리 체크리스트 참조.
> ⚠️ **DEMO 빌드 표시**: 현재 빌드는 PIN 잠금이 우회됩니다 ([PinView.kt:23-29](app/src/main/java/com/medithings/vesiscan/ui/views/pin/PinView.kt#L23-L29)). 원본 UI는 `if (false) { ... }` 안에 보존되어 있어 배포 시 1줄 제거로 복구 가능. 배포 전 정리 체크리스트 참조.
### Home Screen
- 큰 **Bladdy 캐릭터** (3× 확대, 최대 660dp, 작은 화면에선 90% 캡)
@@ -292,7 +292,7 @@ dividerPadding = if (isTablet) 12.dp else 0.dp
- **Personalization → Placement → Monitoring** (정방향): Placement에서 Back = Personalization으로 복귀
- **Monitoring → Placement** (재정렬): Placement에서 Back = Monitoring으로 복귀
코드: [AppState.kt](app/src/main/java/com/example/medilightv2android/AppState.kt) 의 `enterPlacementFromMonitoring()` / `backFromPlacementGuide()`.
코드: [AppState.kt](app/src/main/java/com/medithings/vesiscan/AppState.kt) 의 `enterPlacementFromMonitoring()` / `backFromPlacementGuide()`.
### startFromHome() 라우팅 fix
+1 -1
View File
@@ -225,7 +225,7 @@ Central Link Layer ACK 속도가 향상됨 → 펌웨어 SoftDevice TX queue 포
이전: 시간 단독 `now - lastMaaSentMs < 800` 차단 → 펌웨어 응답이 1.2s 걸릴 때
`startMultiChannel(6)`이 응답 도중 호출되어 잔여 reb 패킷이 폐기되는 race 발생.
신규: [BleManager.kt:464-481](app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L464-L481) `canSendMaa()`
신규: [BleManager.kt:464-481](app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L464-L481) `canSendMaa()`
```kotlin
private fun canSendMaa(caller: String): Boolean {
val now = System.currentTimeMillis()
+31 -1
View File
@@ -1,5 +1,7 @@
# Method A / B / C Algorithm Comparison & Placement Test
_마지막 업데이트: 2026-07-13 — V2 6-stage / CH3 flicker 3-Layer 반영._
## Wall Detection Comparison
### Pipeline
@@ -175,12 +177,40 @@ Screen exit → LED OFF
**Boundary**: up → detect boundary (max drops) → come back → LATERAL
**SWEEP** (CKLaw): navel → slide down → sagittalArgMaxCh → LATERAL → GREEN
### 3-Stage Guide
### 3-Stage Guide (V1 — 일반 사용자 진입, `computePlacementGuide`)
**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)
### 6-Stage Guide (V2 — Clinical Alignment 세션, `AlignGuide4Stage`)
`ClinicalHome → Sensor Alignment` 진입 시 자동 활성화. Method D chord 기반
CV 로 정밀 vertical 조정 + CH3 flicker 대응 sliding-window majority.
| # | Phase | 조건 |
|:---:|---|---|
| 1 | INITIAL_ACCUM | baseline 10 cycle 누적 |
| 2 | VERTICAL_CLIMB | CH3 sliding window majority — 최근 6 프레임 중 hit ≥ 3 |
| 3 | CH3_STABILIZE | 10 cycle 위치 유지, lost 판정도 sliding (window=4, majority=3) |
| 4 | CENTER_OPTIMIZE | CH0~CH3 chord CV ≤ 0.15 (3연속 hysteresis) |
| 5 | LR_BALANCE | \|u4−u5\| ≤ 8 samples, lost 채널 3연속 hysteresis |
| 6 | FINAL_CONFIRM | buf clear → 5 fresh 프레임 재확인 통과 → GREEN |
**CH3 flicker 대응 3-Layer** (2026-07-06 이후, commit e808b5b / 9261bc7 / 4f8d536):
- **Layer 1 — Sliding-Window Majority**: 3 연속 hit 대체. 인체 flick 패턴
(YYNNYY 등) 통과 가능.
- **Layer 2 — Stuck Detection + Relaxed Mode**: VERTICAL_CLIMB 20 프레임 (~5초)
갇힘 + CH3 hit 저조 + CH0~CH2 all detected → CH3 없이 상단 3채널로 CENTER_OPT
진입 허용. `AlignmentConstants.VERTICAL_STUCK_THRESHOLD`.
- **Layer 3 — UX Soft-Hint**: 12 프레임 (~3초) 반복 시 "↑ 위로" →
"CH3 신호 확인 중 · 위치 유지" 로 문구 자동 완화.
**Python replay 검증** (`tools/align_analyze.py`, 2026-07-10~13): data123/ 인체
4 세션 (Phantom + Human 0/1/3 CM). NEW vs LEGACY 병행 replay 결과 Human 0CM
(CH3 31.8%) 회귀 방지 + Human 1CM (77.3%) 조기 진입 확인, 팬텀 false-positive
없음. 상세 표: `tools/README.md`.
### Measurement Modes (v6 업데이트)
- **Auto Scan**: **min 600ms gap** (state-based throttle: `!isComplete` + 600ms), 10-sample trimmed mean (max/min removed, 8 avg)
+53 -53
View File
@@ -1,8 +1,8 @@
# BLE Protocol Reference — VBT Device
VesiScan Basic Android 앱이 VBT 디바이스(VBTFW0116+ 펌웨어)와 주고받는 BLE 명령/응답 전체 정리. 코드 위치는 모두 `app/src/main/java/com/example/medilightv2android/ble/` 기준.
VesiScan Basic Android 앱이 VBT 디바이스(VBTFW0120+ 펌웨어)와 주고받는 BLE 명령/응답 전체 정리. 코드 위치는 모두 `app/src/main/java/com/medithings/vesiscan/ble/` 기준.
작성일 / 검증 FW: 2026-06-05 / **VBTFW0116** (reb 신구조 적용)
작성일 / 검증 FW: 2026-07-13 / **VBTFW0120+** (mim FIFO + mtb queue overrun 3-layer guard 반영)
---
@@ -48,7 +48,7 @@ VesiScan Basic Android 앱이 VBT 디바이스(VBTFW0116+ 펌웨어)와 주고
## 2. BLE 연결 Lifecycle
### 2.1 UUID (Nordic UART Service)
파일: [`BleManager.kt:45-48`](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L45)
파일: [`BleManager.kt:45-48`](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L45)
| 역할 | UUID |
|---|---|
@@ -88,7 +88,7 @@ VesiScan Basic Android 앱이 VBT 디바이스(VBTFW0116+ 펌웨어)와 주고
- 두 협상 모두 디스커버리 + CCCD 활성화 이후에 진행. 너무 일찍 호출하면 reject.
### 2.4 자동 재연결 (silent)
파일: [`BleManager.kt:601` scheduleAutoReconnect](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L601)
파일: [`BleManager.kt:601` scheduleAutoReconnect](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L601)
- 의도하지 않은 disconnect → `scheduleAutoReconnect()` 자동 호출
- 마지막 연결 디바이스 주소를 `SharedPreferences`에 저장 → scan + reconnect 시도
@@ -144,7 +144,7 @@ val isMtbBusy: Boolean
## 3. 명령 빌더 — CRC16-CCITT
파일: [`CRC16.kt`](../app/src/main/java/com/example/medilightv2android/ble/CRC16.kt)
파일: [`CRC16.kt`](../app/src/main/java/com/medithings/vesiscan/ble/CRC16.kt)
```
Polynomial: 0x1021
@@ -189,26 +189,26 @@ fun verify(data: ByteArray): Boolean
| 명령 | 빌더 | Send 함수 | 응답 prefix | 용도 |
|---|---|---|---|---|
| `msr?` | ASCII " " | `disconnectAndUnbond()` 내부 ([L276](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L276)) | — | 본딩 wipe + reboot |
| `mpa?` | BE [freq, cycles] | `sendPiezoPowerOn(freq=2, cycles=5)` ([L404](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L404)) | `rpa:` | Piezo power ON |
| `mps?` | ASCII " " | `sendPiezoStop()` ([L408](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L408)) | — | Piezo stop |
| `mec?` | BE [freq, delay, 140, cycles, 1, ch] | `sendBurst(...)` ([L412](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L412)) | `reb:` 단일 채널 | Single-channel burst |
| `maa?` | ASCII " " | `sendAllChannels()` / `sendChannelsOnly()` ([L416, L513](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L416)) | reb×6 + raa | 6채널 sweep (도넛/placement) |
| `mtb?` | ASCII " " | `sendMtb()` ([L430](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L430)) | reb×6 + raa + **rim** | 6채널 + IMU (★ Clinical) |
| `mbb?` | ASCII " " | `sendFullMeasurement()` ([L480](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L480)) | rbb + reb×6 + raa | 측정 헤더(batt+temp+IMU) + 6채널 |
| `mta?` | BE [0 or 1] | `sendNirsPowerOn()/Off()` ([L441/445](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L441)) | `rta:` | NIRS power |
| `mqq?` | ASCII " " | `sendMqqQuery()` ([L449](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L449)) | `rqq:` | NIRS sensor activate |
| `mag?` | ASCII " " | `sendMagQuery()` ([L453](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L453)) | `rag:` | NIRS gain calibration |
| `mcj?` | ASCII " " | `sendMcjQuery()` ([L457](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L457)) | `rcj:` | NIRS MCJ raw |
| `msn?` | BE [0] | `sendBatteryQuery()` ([L463](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L463)) | `rsn:` | 배터리 mV 조회 |
| `mid?` | ASCII " " | `sendDeviceInfoQuery()` ([L467](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L467)) | `rid:` | FW/HW/Serial 조회 |
| `mls?` | BE [state] | `sendLedMode(state)` ([L471](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L471)) | `rls:` | LED 모드 변경 |
| `msr?` | ASCII " " | `disconnectAndUnbond()` 내부 ([L276](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L276)) | — | 본딩 wipe + reboot |
| `mpa?` | BE [freq, cycles] | `sendPiezoPowerOn(freq=2, cycles=5)` ([L404](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L404)) | `rpa:` | Piezo power ON |
| `mps?` | ASCII " " | `sendPiezoStop()` ([L408](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L408)) | — | Piezo stop |
| `mec?` | BE [freq, delay, 140, cycles, 1, ch] | `sendBurst(...)` ([L412](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L412)) | `reb:` 단일 채널 | Single-channel burst |
| `maa?` | ASCII " " | `sendAllChannels()` / `sendChannelsOnly()` ([L416, L513](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L416)) | reb×6 + raa | 6채널 sweep (도넛/placement) |
| `mtb?` | ASCII " " | `sendMtb()` ([L430](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L430)) | reb×6 + raa + **rim** | 6채널 + IMU (★ Clinical) |
| `mbb?` | ASCII " " | `sendFullMeasurement()` ([L480](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L480)) | rbb + reb×6 + raa | 측정 헤더(batt+temp+IMU) + 6채널 |
| `mta?` | BE [0 or 1] | `sendNirsPowerOn()/Off()` ([L441/445](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L441)) | `rta:` | NIRS power |
| `mqq?` | ASCII " " | `sendMqqQuery()` ([L449](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L449)) | `rqq:` | NIRS sensor activate |
| `mag?` | ASCII " " | `sendMagQuery()` ([L453](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L453)) | `rag:` | NIRS gain calibration |
| `mcj?` | ASCII " " | `sendMcjQuery()` ([L457](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L457)) | `rcj:` | NIRS MCJ raw |
| `msn?` | BE [0] | `sendBatteryQuery()` ([L463](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L463)) | `rsn:` | 배터리 mV 조회 |
| `mid?` | ASCII " " | `sendDeviceInfoQuery()` ([L467](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L467)) | `rid:` | FW/HW/Serial 조회 |
| `mls?` | BE [state] | `sendLedMode(state)` ([L471](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L471)) | `rls:` | LED 모드 변경 |
| ~~`msp?`~~ | ~~ASCII " "~~ | ~~`sendImuQuery()`~~ | `rsp:` | ⚠ **2026-07-08 완전 제거** — 신 firmware 는 `mim` 만 사용. `rsp:` 파서는 legacy 응답 대비 유지. |
| `mim?` | ASCII " " | `sendImuFifoQuery()` | `rim:` (15 sample) | ★ IMU FIFO — piezo 무음, walking detector 시계열 확보용. `imuCollector.reset()` 을 먼저 호출하므로 mtb 진행 중 (isMtbBusy=true) 이면 caller 가 반드시 skip 해야 함 (mtb 의 rim 파괴 방지). |
### 4.1 maa / mtb / maa 송신 throttle (canSendMaa)
파일: [`BleManager.kt:494-509`](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L494)
파일: [`BleManager.kt:494-509`](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L494)
`maa?`, `mtb?`, `sendChannelsOnly()`는 한 채널 그룹 응답이 280~420ms 소요되므로 다음 가드를 통과해야만 송신:
@@ -241,28 +241,28 @@ fun sendMtb(): Boolean {
`onCharacteristicChanged` → `processReceivedData(data)` → prefix 4글자 추출 → when 분기.
파일: [`BleManager.kt:986-1097`](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L986)
파일: [`BleManager.kt:986-1097`](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L986)
| Prefix | 의미 | 패킷 길이 | 파싱 위치 | 콜백 |
|---|---|---|---|---|
| `rta:` `rta!` | NIRS power ON ack | — | [L991](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L991) | `onNirsPowerOnReceived` |
| `rsh:` `rsh!` `rqq:` `rqq!` | Sensor activated | — | [L995](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L995) | `onNirsSensorActivated` |
| `rag:` `rag!` | NIRS gain calibration | data.size - 6 = N×2 | [L999](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L999) | `onNirsMagReceived` |
| `rcj:` `rcj!` | NIRS MCJ raw | — | [L1003](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1003) | `onNirsMcjReceived` |
| `rpa:` | Piezo ack | — | [L1007](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1007) | `onPiezoDataReceived` |
| `rer:` | Preliminary header | — | [L1011](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1011) | (로그만) |
| `reb:` | Piezo ADC raw (1채널) | **210B** (신구조) | [L1014](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1014) | `piezoCollector.addPacket` |
| `red:` | Piezo continuation | — | [L1022](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1022) | `piezoCollector.addPacket` |
| `ree:` | Single-ch end | 8B | [L1026](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1026) | `piezoCollector.addPacket` |
| `raa:` | **All-ch complete** marker | 8B | [L1026](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1026) | `piezoCollector.addPacket` → set finalize |
| `rsn:` | Battery (mV) | ≥6B | [L1031](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1031) | `batteryLevel.value` 갱신 |
| `rid:` | Device info (FW/HW/SN) | 가변 | [L1045](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1045) | `firmwareVersion/...value` 갱신 |
| `rls:` | LED state | ≥5B | [L1067](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1067) | (로그만) |
| `rxs:` | Cmd not supported (echo) | 가변 | [L1071](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1071) | (로그만) |
| `rbb:` | Full measurement header | 22B | [L1075](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1075) | `onMbbHeaderReceived` + collector |
| `rsp:` | IMU 단일 샘플 | ≥16B | [L1080](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1080) | `onImuReceived` |
| `rim:` | **IMU 15 samples** | 188B | [L1086](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1086) | `imuCollector.parseRim` |
| `???` | Unknown | — | [L1091](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1091) | hex 로그 + warn |
| `rta:` `rta!` | NIRS power ON ack | — | [L991](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L991) | `onNirsPowerOnReceived` |
| `rsh:` `rsh!` `rqq:` `rqq!` | Sensor activated | — | [L995](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L995) | `onNirsSensorActivated` |
| `rag:` `rag!` | NIRS gain calibration | data.size - 6 = N×2 | [L999](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L999) | `onNirsMagReceived` |
| `rcj:` `rcj!` | NIRS MCJ raw | — | [L1003](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1003) | `onNirsMcjReceived` |
| `rpa:` | Piezo ack | — | [L1007](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1007) | `onPiezoDataReceived` |
| `rer:` | Preliminary header | — | [L1011](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1011) | (로그만) |
| `reb:` | Piezo ADC raw (1채널) | **210B** (신구조) | [L1014](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1014) | `piezoCollector.addPacket` |
| `red:` | Piezo continuation | — | [L1022](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1022) | `piezoCollector.addPacket` |
| `ree:` | Single-ch end | 8B | [L1026](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1026) | `piezoCollector.addPacket` |
| `raa:` | **All-ch complete** marker | 8B | [L1026](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1026) | `piezoCollector.addPacket` → set finalize |
| `rsn:` | Battery (mV) | ≥6B | [L1031](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1031) | `batteryLevel.value` 갱신 |
| `rid:` | Device info (FW/HW/SN) | 가변 | [L1045](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1045) | `firmwareVersion/...value` 갱신 |
| `rls:` | LED state | ≥5B | [L1067](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1067) | (로그만) |
| `rxs:` | Cmd not supported (echo) | 가변 | [L1071](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1071) | (로그만) |
| `rbb:` | Full measurement header | 22B | [L1075](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1075) | `onMbbHeaderReceived` + collector |
| `rsp:` | IMU 단일 샘플 | ≥16B | [L1080](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1080) | `onImuReceived` |
| `rim:` | **IMU 15 samples** | 188B | [L1086](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1086) | `imuCollector.parseRim` |
| `???` | Unknown | — | [L1091](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1091) | hex 로그 + warn |
⚠️ prefix 매칭은 **`String(data, 0, 4, US_ASCII)`** 로 정확히 4글자 (`"reb:"`처럼 colon 포함).
@@ -270,7 +270,7 @@ fun sendMtb(): Boolean {
## 6. Piezo 응답 상세 (reb / red / ree / raa)
파일: [`PiezoPacketCollector.kt`](../app/src/main/java/com/example/medilightv2android/ble/PiezoPacketCollector.kt)
파일: [`PiezoPacketCollector.kt`](../app/src/main/java/com/medithings/vesiscan/ble/PiezoPacketCollector.kt)
### 6.1 reb 신구조 (210B, FW 2026-06-02+)
@@ -332,7 +332,7 @@ state: 펌웨어 상태 코드. 현재 클라이언트는 사용 안 함 — 마
## 7. IMU 응답 상세 (rim)
파일: [`ImuPacketCollector.kt`](../app/src/main/java/com/example/medilightv2android/ble/ImuPacketCollector.kt)
파일: [`ImuPacketCollector.kt`](../app/src/main/java/com/medithings/vesiscan/ble/ImuPacketCollector.kt)
### 7.1 패킷 구조 (188B)
```
@@ -380,7 +380,7 @@ if (payloadBytes < expectedBytes) {
## 8. 디바이스 정보 (rid)
파일: [`BleManager.kt:1045-1066`](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1045)
파일: [`BleManager.kt:1045-1066`](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1045)
### 8.1 패킷 구조
```
@@ -412,7 +412,7 @@ val sn = parts.firstOrNull { it.startsWith("VBT") && !it.startsWith("VBTHW") &&
## 9. 배터리 (rsn)
파일: [`BleManager.kt:1031-1043`](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1031)
파일: [`BleManager.kt:1031-1043`](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1031)
### 9.1 패킷 구조
```
@@ -442,7 +442,7 @@ if (pct <= batteryLevel.value || batteryLevel.value == 0) {
- ⚠️ 충전 중 상승은 반영 안 됨 — 의도된 동작
### 9.4 Polling 정책 (★ 2026-06-05 변경)
파일: [`BleManager.kt:517-565`](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L517)
파일: [`BleManager.kt:517-565`](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L517)
```
1. 연결 직후 즉시 1회 msn? 송신
@@ -456,7 +456,7 @@ if (pct <= batteryLevel.value || batteryLevel.value == 0) {
## 10. 측정 헤더 (rbb)
파일: [`BleManager.kt:1075-1078`](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1075)
파일: [`BleManager.kt:1075-1078`](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1075)
```
[tag 4B "rbb:"][batt 2B][IMU 2B][temp 2B][crc 2B] = 22B
@@ -485,7 +485,7 @@ if (pct <= batteryLevel.value || batteryLevel.value == 0) {
`!` 변형은 펌웨어 측 에러/특수 상태. 처리 자체는 `:` 와 동일하지만 디버그 로그에 prefix 그대로 기록.
NIRS 자세한 파싱은 [`NirsManager`](../app/src/main/java/com/example/medilightv2android/managers/NirsManager.kt) 참고 (Vivamyo 화면 전용).
NIRS 자세한 파싱은 [`NirsManager`](../app/src/main/java/com/medithings/vesiscan/managers/NirsManager.kt) 참고 (Vivamyo 화면 전용).
---
@@ -495,7 +495,7 @@ NIRS 자세한 파싱은 [`NirsManager`](../app/src/main/java/com/example/medili
표준 GATT 연결만으로 끝. 별도 페어링 명령 없음. CCCD enable 완료 시 service ready.
### 12.2 Unpair (`disconnectAndUnbond()`)
파일: [`BleManager.kt:262-310`](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L262)
파일: [`BleManager.kt:262-310`](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L262)
```
1. isUserDisconnect = true ← auto-reconnect 차단 flag
@@ -540,7 +540,7 @@ NIRS 자세한 파싱은 [`NirsManager`](../app/src/main/java/com/example/medili
- `ImuCollector` — rim sample count
### 14.2 ble.log (텍스트 디버그 로그)
파일: [`BleDebugLogger.kt`](../app/src/main/java/com/example/medilightv2android/services/BleDebugLogger.kt)
파일: [`BleDebugLogger.kt`](../app/src/main/java/com/medithings/vesiscan/services/BleDebugLogger.kt)
각 세션 폴더에 `ble.log` 자동 작성:
```
@@ -610,10 +610,10 @@ T+600 App delay(600 - 290 = 310ms) 끝 → 다시 sendMtb()
---
**원본 소스**: `app/src/main/java/com/example/medilightv2android/ble/`
- [`BleManager.kt`](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt)
- [`CRC16.kt`](../app/src/main/java/com/example/medilightv2android/ble/CRC16.kt)
- [`PiezoPacketCollector.kt`](../app/src/main/java/com/example/medilightv2android/ble/PiezoPacketCollector.kt)
- [`ImuPacketCollector.kt`](../app/src/main/java/com/example/medilightv2android/ble/ImuPacketCollector.kt)
**원본 소스**: `app/src/main/java/com/medithings/vesiscan/ble/`
- [`BleManager.kt`](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt)
- [`CRC16.kt`](../app/src/main/java/com/medithings/vesiscan/ble/CRC16.kt)
- [`PiezoPacketCollector.kt`](../app/src/main/java/com/medithings/vesiscan/ble/PiezoPacketCollector.kt)
- [`ImuPacketCollector.kt`](../app/src/main/java/com/medithings/vesiscan/ble/ImuPacketCollector.kt)
**관련 문서**: [iOS Porting Clinical Measurement](iOS_PORTING_CLINICAL_MEASUREMENT.md), [labdb.md](../labdb.md)
+2 -2
View File
@@ -67,8 +67,8 @@ dev flavor 빌드 → src/main/* + src/dev/*
**예시:** V4.1 wall detection 알고리즘을 2026-06-08 시점에 동결하려면:
```
src/main/java/com/example/.../walldetect/V41Detector.kt ← 계속 변경됨 (dev에서 사용)
src/demo/java/com/example/.../walldetect/V41Detector.kt ← 2026-06-08 시점 복사본 (demo에서 사용)
src/main/java/com/medithings/vesiscan/.../walldetect/V41Detector.kt ← 계속 변경됨 (dev에서 사용)
src/demo/java/com/medithings/vesiscan/.../walldetect/V41Detector.kt ← 2026-06-08 시점 복사본 (demo에서 사용)
```
main의 V41Detector를 리팩토링/실험해도 demo 빌드는 항상 격리본을 컴파일.