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>
This commit is contained in:
2026-04-24 16:50:31 +09:00
parent 208de17152
commit f391f404b4
3 changed files with 48 additions and 6 deletions
@@ -38,7 +38,7 @@ object GreenZoneConstants {
* UrinAI의 liquidThrLoose(raw 기준)와 다름 — 여기는 TVD+SG 적용 후 신호 기준.
* Python 원본: 1150 (low_echo_detection_method_b.py LOW_ECHO_AMP, 6ch 버전)
* 영향: PiezoEchoAnalyzer → low-echo span 탐지, 벽 찾기의 기반 */
const val lowEchoAmp: Float = 1150f
var lowEchoAmp: Float = 1150f
/** 구조 이진화 임계값: raw < 이 값 → 액체(liquid), ≥ → 조직(tissue).
* 낮출수록 엄격 (더 확실한 액체만 인정), 높일수록 관대.
@@ -90,8 +90,8 @@ object PiezoHW {
val degree: DoubleArray get() = centerCh.map { degreeAll[it] }.toDoubleArray()
val degreeLR: DoubleArray get() = centerCh.map { degreeLRAll[it] }.toDoubleArray()
/** Acoustic calibration */
const val distancePerSample: Double = 1.771 // mm/sample
/** Acoustic calibration (런타임 조절 가능) */
var distancePerSample: Double = 1.771 // mm/sample
const val delayOffsetMm: Double = 6.85
/** Volume model */
@@ -42,6 +42,8 @@ import com.example.medilightv2android.ble.BleManager
import com.example.medilightv2android.ble.PiezoChannelData
import com.example.medilightv2android.managers.PiezoEchoAnalyzer
import com.example.medilightv2android.managers.PiezoConstants
import com.example.medilightv2android.managers.GreenZoneConstants
import com.example.medilightv2android.managers.PiezoHW
import com.example.medilightv2android.managers.estimateBladderVolume6ch
import com.example.medilightv2android.models.BladderLevel
import com.example.medilightv2android.models.UrgencyLevel
@@ -624,7 +626,8 @@ fun PiezoMonitoringView(appState: AppState) {
colors = CardDefaults.cardColors(containerColor = MlCardBackground),
modifier = Modifier.shadow(8.dp, RoundedCornerShape(16.dp), spotColor = Color.Black.copy(alpha = 0.06f))
) {
Column(modifier = Modifier.padding(20.dp)) {
Column(modifier = Modifier.padding(20.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
// Catheterization Threshold
Row(verticalAlignment = Alignment.CenterVertically) {
Text("Catheterization Threshold", fontSize = 14.sp, fontWeight = FontWeight.SemiBold)
Spacer(modifier = Modifier.weight(1f))
@@ -637,8 +640,47 @@ fun PiezoMonitoringView(appState: AppState) {
steps = 8,
colors = SliderDefaults.colors(thumbColor = MlPrimary, activeTrackColor = MlPrimary)
)
HorizontalDivider()
Spacer(modifier = Modifier.height(8.dp))
// Low Echo Threshold
var currentThreshold by remember { mutableStateOf(GreenZoneConstants.lowEchoAmp) }
Row(verticalAlignment = Alignment.CenterVertically) {
Text("Low Echo Threshold", fontSize = 14.sp, fontWeight = FontWeight.SemiBold)
Spacer(modifier = Modifier.weight(1f))
Text("${"%.0f".format(currentThreshold)}", fontSize = 14.sp, fontWeight = FontWeight.Bold, color = Color(0xFFFF9500))
}
Slider(
value = currentThreshold,
onValueChange = {
currentThreshold = it
GreenZoneConstants.lowEchoAmp = it
},
valueRange = 800f..2000f,
colors = SliderDefaults.colors(thumbColor = Color(0xFFFF9500), activeTrackColor = Color(0xFFFF9500))
)
HorizontalDivider()
// Distance Per Sample
var currentDps by remember { mutableStateOf(PiezoHW.distancePerSample.toFloat()) }
Row(verticalAlignment = Alignment.CenterVertically) {
Text("Distance/Sample", fontSize = 14.sp, fontWeight = FontWeight.SemiBold)
Spacer(modifier = Modifier.weight(1f))
Text("${"%.3f".format(currentDps)} mm", fontSize = 14.sp, fontWeight = FontWeight.Bold, color = Color(0xFF4CAF50))
}
Slider(
value = currentDps,
onValueChange = {
currentDps = it
PiezoHW.distancePerSample = it.toDouble()
},
valueRange = 1.5f..2.5f,
colors = SliderDefaults.colors(thumbColor = Color(0xFF4CAF50), activeTrackColor = Color(0xFF4CAF50))
)
HorizontalDivider()
Row {
Text("Max Volume", fontSize = 14.sp, fontWeight = FontWeight.SemiBold)
Spacer(modifier = Modifier.weight(1f))
@@ -673,7 +715,7 @@ fun PiezoMonitoringView(appState: AppState) {
BladdyRiveView(urgency = urgency, size = if (isCompact) 70.dp else 100.dp)
Spacer(modifier = Modifier.height(4.dp))
Text(
"${appState.estimatedVolume} ml",
if (lastVolumeMl > 0) "${"%.0f".format(lastVolumeMl)} ml" else "${appState.estimatedVolume} ml",
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
color = urgency.accentColor,