Commit Graph

229 Commits

Author SHA1 Message Date
dw.jang e9c5e4536b fix: 도넛차트 캐릭터 옆 ml을 max volume으로 변경
lastVolumeMl (마지막 측정) → maxVolumeMl (최대 측정값)
세션 중 가장 높은 측정값을 표시

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 15:07:13 +09:00
dw.jang b750b22406 docs: CSV 포맷 업데이트 (threshold/dps/sg_filter 컬럼) + Endian 안정화
- CSV: threshold, dps, sg_filter 컬럼 추가 설명
- Python 예시: 설정값 기반 필터링 추가
- BLE 로그 대조 방법 설명 (scan_id 기반)
- Endian 자동 감지: VBT=BE 기본, forceBigEndian fallback 설명
- 향후 과제: Endian 안정화 완료 항목 추가

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 12:24:11 +09:00
dw.jang fb8204994f feat: CSV에 threshold, dps, sg_filter 컬럼 추가
scan_id,timestamp,volume_ml,lr_ratio,threshold,dps,sg_filter,channel,s0,...
1,14:30:12,305.2,1.20,1150,1.771,ON,CH0,1823,...

매 측정 시점의 설정값이 ADC와 함께 기록되어
추후 분석 시 어떤 파라미터로 측정했는지 추적 가능

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 12:17:59 +09:00
dw.jang fecb02dfde fix: Endian 감지 간헐적 실패 수정 (19회 중 5회 ADC 값 폭발)
근본 원인: CH0 첫 샘플이 LE/BE 둘 다 4095 이하일 때
  감지 불가 → LE로 잘못 판정 → 전체 채널 값 폭발 (30000~65000)

수정:
- 확실한 경우: 기존 로직 유지 (LE>4095→BE, BE>4095→LE)
- 애매한 경우: forceBigEndian 기본값 사용
- VBT* 기기: forceBigEndian=true (기기 연결 시 자동 설정)
- 2025MEDIP: forceBigEndian=false
- Endian 결정 로그 추가

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 12:09:57 +09:00
dw.jang 7125e79fb9 feat: scan_id를 BLE 로그에도 추가 (CSV와 대조 가능)
- MEASUREMENT scan=3 vol=305.2ml lr=1.20 valid=4/6
- BV_SKIP scan=4 center=2/4 total=2/6
- BV_FAIL scan=5 no valid channels

AdcCsvLogger.lastScanId 공개 → BleDebugLogger에서 참조

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 11:24:57 +09:00
dw.jang 099fcfaf97 docs: SG 토글 + DPS 직접입력 + ADC CSV 저장 문서 추가
- 19. Settings: SG Filter 토글 설명, DPS 슬라이더→직접입력 반영
- 21. ADC CSV: 파일 포맷, Python 읽기 예시, 저장 시점 설명
- 향후 과제: ADC CSV 완료 항목 추가

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 11:06:36 +09:00
dw.jang f563b20659 feat: ADC 데이터 일별 CSV 저장 (Downloads/VesiScan_ADC/)
AdcCsvLogger: 측정마다 6채널 ADC를 CSV에 append
- 파일: Downloads/VesiScan_ADC/YYYY-MM-DD.csv
- 행: scan_id × 6채널 (1회 측정 = 6행)
- 열: scan_id, timestamp, volume_ml, lr_ratio, channel, s0~s99
- BV 성공/실패 모두 ADC 저장 (volume_ml 빈칸이면 실패)
- Python: pd.read_csv() → df[df.scan_id==1] 으로 1회 측정 추출

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 10:47:33 +09:00
dw.jang abc70623fa feat: Distance/Sample 슬라이더→숫자 직접 입력으로 변경
OutlinedTextField + Decimal 키보드로 정밀 입력 가능
범위: 0.5~5.0mm, 기본값 1.771mm
입력 즉시 PiezoHW.distancePerSample 반영

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 10:20:09 +09:00
dw.jang 7dea5b2eca feat: SG Filter on/off 토글 추가 (Settings 패널)
- PiezoEchoAnalyzer.useSgFilter: true/false로 SG 필터 on/off
- OFF 시 raw ADC 그대로 low-echo detection에 사용
- Settings 패널에 Switch 토글 추가 (기본 ON)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 17:34:21 +09:00
dw.jang 36da75b1f0 docs: 런타임 캘리브레이션 + BLE Endian 통일 + 향후과제 업데이트
- 19. 런타임 캘리브레이션: Threshold/DPS 슬라이더, lr_ratio 하한
- 20. BLE Endian 통일: 테스터 exe 분석 결과 (BE 파라미터 + 공백)
- 21. 향후 과제: 20+ 완료 항목 업데이트

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 17:11:17 +09:00
dw.jang f391f404b4 feat: Settings에 Low Echo Threshold + Distance/Sample 슬라이더 추가
1. Low Echo Threshold: 800~2000 슬라이더 (기본 1150, 주황색)
   → GreenZoneConstants.lowEchoAmp 런타임 변경
   → CH4/CH5 threshold 조절로 lateral 감지 개선

2. Distance/Sample: 1.5~2.5mm 슬라이더 (기본 1.771, 초록색)
   → PiezoHW.distancePerSample 런타임 변경
   → 기기별 캘리브레이션 가능

3. 도넛차트 ml 실시간 표시: estimatedVolume → lastVolumeMl 직접 표시
   → 측정 즉시 정확한 ml 반영 (기존: 레벨 변환 후 재계산으로 지연)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 16:50:31 +09:00
dw.jang 208de17152 fix: 모든 BLE 명령어를 테스터 exe와 동일 포맷으로 통일
테스터(vsbtester) buildPacket() 분석 결과:
- 파라미터 있는 명령: 16비트 Big Endian (buildCommandBE)
- 파라미터 없는 명령: 공백 0x20 추가 (buildCommandASCII " ")

변경:
- mpa, mec, msn, mta, mls → buildCommandBE (BE 파라미터)
- mps, maa, mqq, mag, mcj, mid, msp, mbb → buildCommandASCII " " (공백)
- maa sendChannelsOnly도 공백 방식으로 통일 (기존 LE [0] 제거)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 16:42:02 +09:00
dw.jang fdc4047c5c fix: mls LED 명령 Big Endian 파라미터로 변경 (테스터 exe와 동일)
테스터 코드 분석 결과:
  exe: mls? + [0x00, 0x05] (16비트 Big Endian) → LED 바뀜
  앱: mls? + [0x05, 0x00] (16비트 Little Endian) → LED 안 바뀜

CRC16.buildCommandBE() 추가: 파라미터를 Big Endian으로 직렬화
sendLedMode(): buildCommandBE("mls", [state]) 사용

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 16:38:35 +09:00
dw.jang 3cebaff6a0 fix: mls 명령 포맷 'mls? 5' (공백+ASCII) 시도
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 16:28:29 +09:00
dw.jang e6e9c296f2 fix: mls LED 명령 ASCII 포맷으로 변경 (buildCommand→buildCommandASCII)
기존: mls? + [0x05, 0x00] (2바이트 LE 파라미터) → LED 안 바뀜
변경: mls?5 (ASCII 숫자) → exe 프로그램과 동일 포맷

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 16:04:44 +09:00
dw.jang 73ceb40ed4 fix: lr_ratio 하한 1.0 제한 (500ml 팬텀에서 100ml로 과소평가 수정)
원인: CH4/CH5 lateral이 center 대비 짧아서 lr_ratio=0.32 계산
→ S = π/4 × D² × 0.32 → 용적 68% 과소평가

수정: computeLrRatio() 반환값에 .coerceAtLeast(1.0) 추가
원형(1.0)보다 좁은 방광 단면은 물리적으로 불가능하므로 하한 제한

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 15:53:16 +09:00
dw.jang c334b5f018 fix: CV threshold 단계적 완화 (5회 단위, 최대 0.30)
기존: cvThr = 0.15 + scanCount × 0.02 (무한 증가)
변경: 5회 단위 단계적 완화, 상한 0.30
  1~5회:  0.15 (원래 기준)
  6~10회: 0.20
  11~15회: 0.25
  16회+:  0.30 (최대)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 15:35:29 +09:00
dw.jang 1beb0737c6 feat: Placement 판정 결과 BLE 디버그 로거에 기록 + README 대폭 업데이트
BLE 로그 추가:
- DETACHED mean=12.3 std=8.7
- PLACEMENT_SEARCHING cv=0.234 dir=MOVE UP moderate score=35
- PLACEMENT_PASS cv=0.112 lr_dev=0.089 score=78

README 추가 섹션:
- 17. 미부착 감지 알고리즘 상세 (DetachmentDetection)
  - 원리, 상수, 실측 안전 마진 설명
- 18. Placement 전체 파이프라인 흐름도
  - Detachment → Echo → CV → LR → LED 판정
  - BLE 로그 출력 예시
- 향후 과제에 미부착 감지 완료 항목 추가

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 15:27:39 +09:00
dw.jang ea19b74c00 feat: DetachmentDetection 포팅 + Placement LED 4/5/6 동적 제어
DetachmentDetection (detachment_detection.py 1:1 포팅):
- ADC [20:80] 구간에서 baseline([80:100] median) 차감
- mean(|y|) < 100 AND std(y) < 100 → 미부착(detached)
- center 4채널(CH0~CH3) 기준

PlacementGuide LED 제어:
- 미부착 → mls?(4) DETACH_WARNING + "Sensor detached" 메시지
- 탐색 중 (CV > threshold) → mls?(5) ALIGN_SEARCHING
- 최적 위치 (PASS) → mls?(6) ALIGN_COMPLETE
- 스캔 종료 → mls?(0) OFF

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 15:21:23 +09:00
dw.jang 994d5e0c46 fix: adaptive threshold 비활성화 (기본 false, 고정 1150 복원)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 15:06:13 +09:00
dw.jang 6889136783 feat: adaptive threshold 구현 (percentile + 고정값 blended)
detectLowEcho: 고정 1150 → adaptive threshold (기본 활성화)
  1. ringSkip(3) 이후 신호에서 q25/q50/q75 계산
  2. percentile 기반: q25 + iqr*0.3 ~ q50 - iqr*0.3
  3. 고정값(1150) 범위 제한: ×0.7 ~ ×1.5 (805~1725)
  4. 가중 평균: 고정 40% + adaptive 60%
  → 신호 레벨에 자동 적응하되 급격한 변동 방지

WaveformChart: threshold 점선도 adaptive 값으로 표시
useAdaptiveThreshold 플래그로 on/off 전환 가능

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 14:58:28 +09:00
dw.jang a02562eb31 docs: BLE 명령어 포맷, 자동측정 루프, LED 모드, 향후 과제 업데이트
- BLE 명령어 포맷: 공백 필요 패턴 (buildCommandASCII) 문서화
- 자동측정: IMU 1초 × 9 + 6ch 측정 10초 루프 설명
- LED 모드: state 코드 + Placement 연동 흐름
- 기기 정보: mid? → rid: 자동 조회 설명
- 향후 과제: 17개 완료 항목 + 5개 예정 항목

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 13:55:38 +09:00
dw.jang 76ec271352 fix: msp/mbb 명령어 뒤에 공백 추가 (mid?와 동일 포맷)
기기가 파라미터 없는 명령에도 공백을 요구함 (펌웨어 파서 특성)
- sendImuQuery: buildCommand("msp") → buildCommandASCII("msp", " ")
- sendFullMeasurement: buildCommand("mbb") → buildCommandASCII("mbb", " ")
- IMU 1초 쿼리 재활성화

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 13:49:30 +09:00
dw.jang a3c80bd074 fix: IMU(msp) 쿼리 비활성화 — 현재 기기(VBTFW0111) 미지원
msp? → rxs: cmd_not_supported 응답 확인
매 1초 불필요한 msp 전송 제거, 10초 간격 maa 측정만 유지
IMU 지원 기기 확인 후 재활성화 예정

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 13:48:28 +09:00
dw.jang be0264d24c fix: Spot 측정 maa 복원 (mbb는 rbb 파싱 검증 후 전환)
mbb 응답에서 rbb: 패킷 구조가 불명확하여 collector 동작 불안정.
Spot/자동측정 모두 maa(sendChannelsOnly)로 안정화.
mbb는 로그로 rbb: 바이트 구조 확인 후 전환 예정.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 12:30:17 +09:00
dw.jang 9842644e23 fix: mbb 측정 복원 — PiezoPacketCollector가 rbb: 헤더를 skip하도록 수정
근본 원인: mbb? 응답에서 rbb: 헤더가 먼저 오는데 collector가 모르고 무시
→ onMultiChannelComplete 콜백 안 불림 → isMeasuring 영구 true → 버튼 비활성화

수정:
- PiezoPacketCollector.addPacket(): rbb: prefix 인식 → skip + 로그
- BleManager: rbb: 수신 시 piezoCollector.addPacket()에도 전달
- PiezoMonitoringView: sendChannelsOnly→sendFullMeasurement(mbb) 복원

흐름: mbb? → rbb:(skip) → reb:×6(수집) → raa:(완료) → 콜백 정상

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 12:14:27 +09:00
dw.jang bf2f16b207 fix: Spot 측정 mbb→maa 복원 (rbb 헤더가 collector 혼동 유발)
mbb 응답(rbb:→reb:×6→raa:)에서 rbb: 헤더가 piezoCollector를
혼동시켜 그래프가 안 뜨는 문제 발생.

- Spot 단일 측정: sendChannelsOnly(maa) 사용
- 자동 측정 10초째: measure()→maa 사용 (mbb는 파싱 검증 후 전환)
- IMU 전송 시 isMeasuring 체크 추가 (측정 중 충돌 방지)
- sendChannelsOnly() 함수 추가 (기존 sendAllChannels와 동일)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 12:09:02 +09:00
dw.jang fb9b950a1c feat: Piezo 상단바에 Placement Guide 복귀 버튼 추가
MyLocation 아이콘으로 Placement Guide로 돌아갈 수 있는 버튼 추가
(Home 버튼 옆, 센서 재배치가 필요할 때 사용)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 11:49:45 +09:00
dw.jang af6071e18b fix: mid? 명령 포맷 변경 — buildCommandASCII("mid", " ") 공백 포함
기기에서 rxs:mid? (unknown command) 응답 → 포맷 문제 가능성
buildCommand("mid") → buildCommandASCII("mid", " ") 로 변경하여 테스트

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 11:45:47 +09:00
dw.jang eb5affc6b8 feat: 자동측정 IMU 1초 + 전체측정(mbb) 10초 루프
자동측정 변경 (기존: 30초 maa → 신규: 1초 msp + 10초 mbb):
- 1~9초: msp(IMU query) → rsp(accel+gyro) 1초 간격
- 10초째: mbb(전체측정) → rbb(헤더) + reb×6 + raa
- 기존 maa → mbb(sendFullMeasurement)로 전환
- mbb 응답은 rbb: 헤더 후 reb:/raa: 동일 파이프라인

BLE 명령어 추가:
- sendImuQuery(): msp 전송
- sendFullMeasurement(): mbb 전송 (piezoCollector 연동)
- sendLedMode(state): mls 전송

응답 파싱 추가:
- rxs: → "cmd_not_supported" (mid? 미지원 기기 대응)
- rbb: → full measurement header
- rsp: → IMU data

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 11:31:03 +09:00
dw.jang e6e339379b feat: ADC 로그 저장 + 기기정보 mid? + LED 모드 + 무한 3초 reconnect
1. ADC 로그: 측정 완료 시 MeasurementLogService.logMeasurement() 호출
   → 6채널 raw ADC + 채널별 ant/post/score 저장 (JSONL)

2. 기기 정보: 연결 직후 mid? 전송 → rid: 응답 파싱 → 로그 기록
   (HW Version, Serial Number, FW Version 일괄 조회)

3. LED 모드: Placement Guide 연동
   - 스캔 시작 → mls?(5) ALIGN_SEARCHING
   - PASS → mls?(6) ALIGN_COMPLETE
   - 스캔 종료 → mls?(0) OFF

4. Reconnect: 3/5/10초 5회 → 3초 간격 무한 재시도
   (연결됐다가 끊어진 경우, 사용자가 Home 돌아갈 때까지)

5. BLE 로그: mid/mls TX 파싱, rid:/rls: RX 파싱 추가

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 10:55:29 +09:00
dw.jang fde4d0ad05 docs: low echo detection + Placement CV threshold 문서 업데이트
- Low echo: VALLEY_STOP_RISE=50, EDGE_DIST_DECAY=0.12, edge boundary, score 공식
- Placement: CV threshold 방식 전체 설명 (CV 계산, SI/LR 판정, severity, relaxation)
- 기존 urineLen score 방식 → CV threshold 방식으로 전면 교체

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 10:30:41 +09:00
dw.jang 4cdd69ef3d feat: low echo detection + Placement 알고리즘 업데이트 (원은지님 공유)
Low Echo Detection 업데이트:
- VALLEY_STOP_RISE: 10→50 (valley 스캔 early stop 임계)
- EDGE_DIST_DECAY: 0.12 (신규, edge 거리 penalty)
- Edge boundary 확장: left [leftLo:edge+1], right [edge-1:rightHi+1]
- Peak scoring: prominence → prominence / (1 + 0.12 × dist)
  (low echo에 가까운 peak이 더 높은 점수)

Placement 알고리즘 전면 교체 (CV threshold 방식):
- 기존 urineLen score → cos-corrected depth의 CV(변동계수)
- CV ≤ 0.15 → PASS (Best Placement)
- CV > 0.15 → FAIL → weakest channel 기반 방향 안내
  - CH0/CH1(top) weakest → MOVE DOWN
  - CH2/CH3(bottom) weakest → MOVE UP
  - severity: slight(<10%) / moderate(10~25%) / severe(≥25%)
- LR deviation: CH4/CH5 cos-corrected depth 비교
  - |lr_dev| ≤ 0.15 → PASS
  - lr_dev > 0 → MOVE RIGHT, < 0 → MOVE LEFT
- 반복 시 CV threshold 완화 (+0.02/회)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 10:22:05 +09:00
dw.jang 98d3b11a46 docs: Python↔Android 1:1 검증 결과 + BLE 재연결 시스템 상세 문서화
- 전수 상수 비교 테이블 (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) <noreply@anthropic.com>
2026-04-23 16:18:49 +09:00
dw.jang df42a47d7d feat: BLE 로그에 파싱된 값 추가 (TX 명령어 + RX 응답 의미)
TX: "maa 6ch measure", "msn battery query", "mpa piezo power ON" 등
RX: "rsn battery=3850mV (100%)", "reb 100samples", "raa all-ch complete",
    "rpa piezo ack", "rag gain calibration (24 values)" 등

펌웨어팀에 보낼 때 raw hex 없이도 통신 흐름을 이해할 수 있음

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 15:40:23 +09:00
dw.jang 796bce19a2 cleanup: WATCHDOG_CHECK 디버그 로그 제거 (이벤트성 로그만 유지)
WATCHDOG_STARTED/TIMEOUT/GATT_ERROR/RECONNECT 등 이벤트 로그는 유지.
5초마다 찍히던 WATCHDOG_CHECK/THREAD 상태 로그 제거.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 15:35:19 +09:00
dw.jang a474935e3c fix: GATT 에러로 끊길 때도 자동 reconnect 시작 (핵심 버그 수정)
근본 원인: Android BLE가 disconnect 시 STATUS_DISCONNECTED가 아닌
GATT 에러(status≠0)로 콜백을 보냄. 기존 에러 경로에서는
reconnectAttempt>0일 때만 재연결 → 첫 끊김 시 재연결 안 됨.

수정:
- wasConnected 체크: 이전에 연결됨 + 사용자 disconnect 아님 → 자동 reconnect
- GATT_ERROR 로그 + DISCONNECTED(gatt_error) 로그 기록
- stopBatteryPolling/stopWatchdog 호출 추가
- watchdog: connected=false 감지 시 무한 루프 대신 break

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 15:13:11 +09:00
dw.jang d3006d91dc debug: watchdog Thread 매 체크마다 로그 기록 (WATCHDOG_CHECK)
watchdog이 실제로 돌고 있는지, 어디서 멈추는지 진단용.
매 5초: WATCHDOG_CHECK silence=Xms connected=true/false
interrupt 시: WATCHDOG_THREAD interrupted
flag 종료 시: WATCHDOG_THREAD stopped

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 14:59:07 +09:00
dw.jang c99eb068a5 fix: watchdog GATT close + 로그를 watchdog Thread에서 직접 실행
기존: handler.post 안에서 로그+disconnect → handler 블로킹 시 둘 다 안 됨
변경:
- debugLogger.error() → watchdog Thread에서 직접 호출 (CopyOnWriteArrayList 스레드 안전)
- gatt.disconnect()/close() → watchdog Thread에서 직접 호출 (GATT는 any thread OK)
- UI 업데이트 + reconnect만 handler.post로 전달

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 14:39:54 +09:00
dw.jang 7ab3d878e7 fix: watchdog 누락 경로 수정 + 시작 로그 추가
- CCCD 없는 경로(no CCCD, no RX)에서도 startWatchdog() + startBatteryPolling() 호출
- watchdog 시작 시 WATCHDOG_STARTED 로그 기록 (실제 동작 확인용)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 14:22:40 +09:00
dw.jang 988fbd9dbd fix: watchdog을 별도 Thread로 분리 (handler 블로킹 방지)
기존: handler.postDelayed로 watchdog 실행 → BLE 스택이 handler를
블로킹하면 watchdog도 멈춤 (좀비 상태 감지 불가)

변경: 별도 daemon Thread에서 5초마다 lastRxTimestamp 체크
→ BLE handler가 블로킹되어도 독립적으로 15초 타임아웃 감지
→ 감지 시 handler.post로 메인 스레드에서 forceDisconnectAndReconnect 실행

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 14:09:16 +09:00
dw.jang 9d32d13452 fix: BLE reconnect를 fresh scan→connect 방식으로 변경
기존: connectGatt(autoConnect=true) — Android에서 매우 불안정, 수분 걸림
변경: BLE 스캔으로 기기를 다시 찾은 후 connectGatt(autoConnect=false)로 빠르게 연결

재연결 흐름:
1. watchdog 15초 타임아웃 → forceDisconnectAndReconnect()
2. scheduleAutoReconnect() → 3s/5s/10s 백오프
3. attemptAutoReconnect():
   - BLE 스캔 시작 (기기 이름 or MAC 주소 매칭)
   - 발견 시 즉시 connectGatt(autoConnect=false) → 빠른 연결
   - 8초 스캔 타임아웃 → 다음 attempt
   - 최대 5회 시도 후 실패 처리
4. 모든 단계 BleDebugLogger에 기록

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 14:02:08 +09:00
dw.jang d4e2627efe fix: BLE watchdog — 좀비 연결 감지 + 강제 reconnect
Android BLE가 disconnect 콜백을 안 보내는 경우(좀비 연결) 대응:
- 마지막 RX로부터 15초간 응답 없으면 강제 disconnect → auto reconnect
- processReceivedData()에서 lastRxTimestamp 갱신
- 5초마다 watchdog 체크
- WATCHDOG_TIMEOUT 이벤트 BleDebugLogger에 기록
- 수동 disconnect 시 watchdog 해제

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 12:27:13 +09:00
dw.jang 506248655d fix: BLE 로그 Downloads 폴더에 저장 (파일 탐색기 접근 가능)
- exportToDownloads(): Downloads/VesiScan_BLE_날짜.log 로 저장
- 실패 시 fallback으로 내부 저장소에 저장
- Toast로 저장 경로 안내

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 12:22:07 +09:00
dw.jang 6f4488f1ae fix: BV 측정 최소 center 3채널 이상 요구 + 실패 시 사용자 안내
- center 채널 3개 미만이면 BV 계산 스킵 (신뢰도 부족 방지)
- 1~2채널: "측정 부족 (N/4채널) — 센서 위치를 조절해주세요"
- 0채널: "방광이 감지되지 않았습니다. 센서 접촉을 확인해주세요."
- BleDebugLogger에 BV_SKIP/BV_FAIL 기록

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 12:08:52 +09:00
dw.jang f220cca187 feat: BLE 디버그 로거 — 펌웨어 디버깅용 상세 통신 로그
BleDebugLogger 신규:
- TX/RX 패킷 기록 (타임스탬프 ms, 데이터 크기)
- RSSI 실시간 추적 (5초마다 readRemoteRssi)
- 연결/끊김/재연결 이벤트 + GATT status code
- 패킷 타임아웃, 채널 누락, Endian 감지 로그
- 측정 결과 (volumeMl, lrRatio, validChannels)
- 파일 내보내기 (.log / .jsonl) + 클립보드 복사
- 세션 통계 (TX/RX/ERR/RSSI avg/min/max)

BleManager 연동:
- sendRaw() → TX 로그
- processReceivedData() → RX 로그
- connected/disconnected/reconnecting/reconnectFailed 이벤트
- onReadRemoteRssi 콜백 → RSSI 기록

PiezoMonitoringView:
- Developer Tools 하단에 BLE Debug Panel 추가
- 2초 자동 갱신, 색상 코딩 (TX=파랑, RX=초록, ERR=빨강, RSSI=노랑, CONN=보라)
- 클립보드 복사 / 파일 저장 / 클리어 버튼

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 11:47:36 +09:00
dw.jang 079774c869 feat: Placement Guide 실시간 연속 스캔 (3초 간격)
- 수동 1회 → 자동 반복 스캔 (Start/Stop 토글)
- 3초마다 maa 전송 → Score 실시간 갱신
- 사용자가 센서 움직이면서 점수 변화를 즉시 확인 가능
- "Scanning every 3s — move sensor and watch the score" 안내 문구

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 11:29:02 +09:00
dw.jang c114592ab1 feat: 6ch 알고리즘 포팅 + urineLen 기반 정밀 Placement Guide
6ch 알고리즘 (config_6ch.py / bv_estimation.py 포팅):
- PiezoHW: V0/V1/V2 프리셋 (Snell's law 굴절 각도)
- computeLrRatio(): CH4/CH5 lateral에서 타원 단면 비율 동적 계산
- estimateBladderVolume6ch(): center 4ch + lateral lr_ratio
- denoise(): TVD 제거, SG 필터만 사용
- LOW_ECHO_AMP: 1600→1150, distancePerSample: 1.771

Placement Guide 2단계:
- 1단계: 이진 판정 (잡힘/안잡힘) → 기본 방향
- 2단계: urineLen 기반 정밀 가이드 (대칭+균형+크기)
  - 세로: CH0/CH3 비율 1.5배 이상이면 방향 제시
  - 가로: CH4/CH5 차이 5칸 이상이면 방향 제시
- Placement Score = 세로대칭(30%) + 가로균형(30%) + 중앙크기(40%)
- 각 채널에 urineLen 숫자 표시

VesiScan_Android_Pipeline_Summary.md 문서 작성

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 10:22:57 +09:00
dw.jang a95815c42d fix: Piezo 진입 시 bladderLevel 0으로 초기화 (이전 세션 값 잔류 방지)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 17:46:16 +09:00
dw.jang 6f5801b4b6 feat: BLE 자동 reconnect + 배터리 폴링 5초 (keep-alive)
- BleManager: 배터리 폴링 30초→5초 (펌웨어 15초 타임아웃 방지, iOS 동일)
- BleManager: 자동 reconnect with 백오프 (3s→5s→10s, 최대 5회)
  - scheduleAutoReconnect() + attemptAutoReconnect()
  - GATT 에러 시에도 백오프 재시도
  - 수동 disconnect 시 자동 reconnect 취소
- PiezoMonitoringView: 끊김 시 즉시 파란 배너(RECONNECTING)
  - 성공 시 배너 자동 숨김 + 배터리 폴링 재시작
  - 5회 실패 시 빨간 배너 + 수동 Reconnect 버튼
  - 측정 중 끊김 → isMeasuring 리셋 + 자동측정 중지

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 17:40:00 +09:00