fix+UX: BLE 중복수신 수정, peak detection 개선, UI 대폭 정리
BLE: - GATT 다중연결 race condition 수정 (reconnectConnecting 플래그) - onCharacteristicChanged API 레벨 분기 (dual callback 방지) - DeviceScan: isConnected → isServiceReady 전환 (서비스 준비 후 화면전환) - Placement: mpa/maa 500ms 딜레이 (power on 대기) - Disconnect & Unbond 기능 추가 (msr? + removeBond + 저장정보 삭제) - autoConnect 삭제 (HomeView) Algorithm: - 후위벽 피크 >= threshold 조건 추가 (false positive 차단) - low-echo 분석범위 80샘플 제한 (뒤쪽 노이즈 무시) - urineLen: post-ant-1 → post-ant 수정 - Placement: UrinAI fallback 제거 (PiezoEchoAnalyzer만 사용) - 기기 프리셋 autoDetect index 7→8 수정 (VBT26040Nxx) - dps 기본값 1.771 → 1.968 UI: - 도넛차트: Home버튼 삭제, Auto/Catheter count/History 삭제 - 3버튼 레이아웃 (Catheterize/Auto/Spot) - 배터리 아이콘 (60+초록/30+주황/빨강) + 설정 톱니바퀴 분리 - SensorSelect/Consent 페이지 스킵 - 온보딩: VesiScan-Basic, NIRS 페이지 삭제 - MaxVolume: Step 표시 삭제, mL 오른쪽 배치 - BackHandler 전체 재정리 (자연스러운 뒤로가기) - BLE 신호 기준 완화 (Good≥-80, Fair≥-90) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -35,8 +35,8 @@ object PiezoHW {
|
||||
var activePreset: DevicePreset = DevicePreset.V0
|
||||
|
||||
fun autoDetectPreset(deviceName: String) {
|
||||
if (deviceName.startsWith("VBT2604") && deviceName.length >= 9) {
|
||||
when (deviceName[7]) {
|
||||
if (deviceName.startsWith("VBT2604") && deviceName.length >= 10) {
|
||||
when (deviceName[8]) {
|
||||
'0' -> activePreset = DevicePreset.V0
|
||||
'2' -> activePreset = DevicePreset.V1
|
||||
'3' -> activePreset = DevicePreset.V2
|
||||
@@ -91,7 +91,7 @@ object PiezoHW {
|
||||
val degreeLR: DoubleArray get() = centerCh.map { degreeLRAll[it] }.toDoubleArray()
|
||||
|
||||
/** Acoustic calibration (런타임 조절 가능) */
|
||||
var distancePerSample: Double = 1.771 // mm/sample
|
||||
var distancePerSample: Double = 1.968 // mm/sample
|
||||
const val delayOffsetMm: Double = 6.85
|
||||
|
||||
/** Volume model */
|
||||
|
||||
@@ -17,7 +17,7 @@ data class LowEchoResult(
|
||||
val lowStart: Int, // low-echo span 시작
|
||||
val lowEnd: Int, // low-echo span 끝 (inclusive)
|
||||
val lowMean: Double, // low-echo 구간 평균 진폭
|
||||
val urineLen: Int, // post - ant - 1
|
||||
val urineLen: Int, // post - ant
|
||||
val score: Double, // low_depth * urine_len
|
||||
val innerPeaks: List<Int> // (ant, post) 내부 local peak indices
|
||||
) {
|
||||
@@ -373,8 +373,9 @@ class PiezoEchoAnalyzer private constructor() {
|
||||
private fun detectLowEchoCore(sg: DoubleArray, threshold: Double): LowEchoResult? {
|
||||
if (sg.size < 10) return null
|
||||
|
||||
// 1) low-echo span 추출
|
||||
val lowMask = BooleanArray(sg.size) { sg[it] <= threshold }
|
||||
// 1) low-echo span 추출 (postMaxIdx까지만 — 이후는 노이즈)
|
||||
val analysisLimit = min(sg.size, postMaxIdx + 1)
|
||||
val lowMask = BooleanArray(sg.size) { it < analysisLimit && sg[it] <= threshold }
|
||||
val rawSpans = contiguousTrueSpans(lowMask).filter { it.second - it.first + 1 >= lowMinLen }
|
||||
|
||||
// 2) 병합 — gap 내부에 벽 후보(threshold + 5 초과 peak)가 있으면 병합하지 않음
|
||||
@@ -383,7 +384,7 @@ class PiezoEchoAnalyzer private constructor() {
|
||||
val firstSpan = spans.firstOrNull() ?: return null
|
||||
|
||||
val s = max(0, firstSpan.first)
|
||||
val e = min(sg.size - 1, firstSpan.second)
|
||||
val e = min(analysisLimit - 1, firstSpan.second)
|
||||
if (s > e) return null
|
||||
|
||||
val lowSlice = safeSlice(sg, from = s, to = e) ?: return null
|
||||
@@ -397,20 +398,23 @@ class PiezoEchoAnalyzer private constructor() {
|
||||
var post = selectWallByProminence(sg = sg, edge = e, searchWin = peakSearchWin,
|
||||
peakMin = peakMin, side = WallSide.POST, otherEdge = s) ?: return null
|
||||
|
||||
// 4) post > POST_MAX_IDX: prominence 기반 재탐색
|
||||
if (post > postMaxIdx) {
|
||||
// 4) post가 분석 범위를 넘으면 재탐색
|
||||
if (post >= analysisLimit) {
|
||||
val backHalfEdge = (s + e) / 2
|
||||
post = selectWallByProminence(
|
||||
sg = sg, edge = backHalfEdge,
|
||||
searchWin = postMaxIdx - backHalfEdge,
|
||||
searchWin = analysisLimit - 1 - backHalfEdge,
|
||||
peakMin = peakMin, side = WallSide.POST, otherEdge = null
|
||||
) ?: return null
|
||||
}
|
||||
|
||||
val antH = sg[ant]
|
||||
val postH = sg[post]
|
||||
|
||||
if (postH < threshold) return null
|
||||
|
||||
val lowDepth = ((antH + postH) / 2.0) - lowMean
|
||||
val urineLen = post - ant - 1
|
||||
val urineLen = post - ant
|
||||
|
||||
if (urineLen < minUrineLen) return null
|
||||
|
||||
|
||||
Reference in New Issue
Block a user