feat: developer mode, SWEEP placement, gradient guide, spot flicker fix
- Developer mode: 캐릭터 3탭 토글, dev-only settings/graphs/logs - SWEEP placement (CKLaw): BladderSphereSeek circle fit + RTracker + argmax + coronal lateral + R-peak gate GREEN lock - Gradient placement: weighted center gradient 기반 상하 판정, 방향 로직 수정 (ch0=위, ch3=아래), 초기 멘트 "치골 위에 배치" - Spot 측정: isSpotInProgress 플래그로 중간값 화면 깜빡임 제거 - 좌우 애매한 ↔ 멘트 제거 → 구체적 방향 또는 상태 안내 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
package com.example.medilightv2android.managers
|
||||
|
||||
import kotlin.math.abs
|
||||
|
||||
/**
|
||||
* R-fit trend tracker — watches per-frame sphere-fit radius and classifies:
|
||||
* RISING — sliding toward bladder centre (R increasing)
|
||||
* PEAK — at / near session maximum R
|
||||
* FALLING — sliding past centre (R decreasing)
|
||||
* NOISE — fluctuating without clear trend
|
||||
* IDLE — no valid fit (too few channels)
|
||||
*
|
||||
* Ported from CKLaw BladderSeekRTracker.kt.
|
||||
*/
|
||||
class BladderSeekRTracker(
|
||||
private val historySize: Int = 8,
|
||||
private val peakToleranceMm: Double = 1.5,
|
||||
private val noiseDeltaMm: Double = 0.4,
|
||||
) {
|
||||
enum class Trend { RISING, PEAK, FALLING, NOISE, IDLE }
|
||||
|
||||
data class Status(
|
||||
val currentR: Double?,
|
||||
val bestR: Double,
|
||||
val trend: Trend,
|
||||
val improvementMm: Double,
|
||||
val hintEn: String,
|
||||
)
|
||||
|
||||
private val history = ArrayDeque<Double>()
|
||||
var bestR: Double = 0.0
|
||||
private set
|
||||
|
||||
fun reset() {
|
||||
history.clear()
|
||||
bestR = 0.0
|
||||
}
|
||||
|
||||
fun submit(r: Double?): Status {
|
||||
if (r == null || r.isNaN() || r <= 0.0) {
|
||||
return Status(null, bestR, Trend.IDLE, 0.0,
|
||||
"Not enough channels — check signal")
|
||||
}
|
||||
history.addLast(r)
|
||||
while (history.size > historySize) history.removeFirst()
|
||||
if (r > bestR) bestR = r
|
||||
|
||||
val trend = computeTrend(r)
|
||||
val gap = r - bestR
|
||||
val en = when (trend) {
|
||||
Trend.RISING -> "Getting closer (R=${"%.0f".format(r)}mm)"
|
||||
Trend.PEAK -> "At peak (R=${"%.0f".format(r)}mm)"
|
||||
Trend.FALLING -> "Past peak — reverse (best ${"%.0f".format(bestR)}mm)"
|
||||
Trend.NOISE -> "Searching (R=${"%.0f".format(r)}mm)"
|
||||
Trend.IDLE -> ""
|
||||
}
|
||||
return Status(r, bestR, trend, gap, en)
|
||||
}
|
||||
|
||||
private fun computeTrend(currentR: Double): Trend {
|
||||
if (history.size < 3) return Trend.NOISE
|
||||
if (bestR - currentR < peakToleranceMm) return Trend.PEAK
|
||||
val prev = history.toList().dropLast(1).takeLast(3)
|
||||
val prevMean = prev.average()
|
||||
val delta = currentR - prevMean
|
||||
return when {
|
||||
abs(delta) < noiseDeltaMm -> Trend.NOISE
|
||||
delta > 0 -> Trend.RISING
|
||||
else -> Trend.FALLING
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user