feat(posture): 데모 idle 시 msp(IMU-only) polling — 사용자앱 동기화 + walking 검출 유지
사용자앱 (feature/tab-navigation b1aecfc, e3c21e6) 의 msp 전환을 데모에도 적용. demo 의 walking detector 유지 위해 폴링 주기 조정. 배경: - 기존: PiezoMonitoring idle 에서 mtb 1Hz 폴링 → 매 cycle piezo burst 가청음. - 사용자앱 실측 (VBTFW0120) 으로 rsp: 18B 정상 응답 확정. 데모 ImuPacketCollector 는 이미 parseRsp 가지고 있어 (이전에 defensive 작성) BleManager 가 자동 트리거. 수정: - GreenZoneConstants.posturePollIntervalMs: 1000 → 200ms (5Hz) walking detector window (1.5초) 안에 7~8 sample 확보 — mtb 의 15Hz 등가에는 못 미치지만 사람 보행 freq 1.5~2.5Hz 의 Nyquist 5Hz 충분. - PiezoMonitoringView LaunchedEffect: - idle (auto/single scan 비활성) → sendImuQuery() — 무음 - auto scan 중 → 기존 mtb 의 rim: 가 자동으로 posture 갱신 - imuCollector.onComplete 는 parseRim / parseRsp 둘 다에서 fire 되니 wiring 그대로. 부하 비교: mtb 1Hz ≈ 1200 B/s (가청음 발생) msp 5Hz ≈ 90 B/s (무음) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -36,8 +36,13 @@ object GreenZoneConstants {
|
|||||||
/** Posture acc magnitude gate max (g). 이 값 초과면 classifier UNKNOWN. */
|
/** Posture acc magnitude gate max (g). 이 값 초과면 classifier UNKNOWN. */
|
||||||
@Volatile var postureAccMagMax: Float = 1.2f
|
@Volatile var postureAccMagMax: Float = 1.2f
|
||||||
|
|
||||||
/** Posture polling 간격 (ms). PiezoMonitoring 에서 mtb 보내는 주기. dev 모드에서 조정 가능. */
|
/**
|
||||||
@Volatile var posturePollIntervalMs: Long = 1000L
|
* Posture polling 간격 (ms). PiezoMonitoring 에서 msp 보내는 주기.
|
||||||
|
* 6/30 변경: mtb (1Hz × 15sample) → msp (5Hz × 1sample) 로 전환. piezo burst 가청음 제거.
|
||||||
|
* Walking detector 의 1.5초 window 안에 7-8 sample 확보 위해 200ms 주기.
|
||||||
|
* dev 모드에서 조정 가능.
|
||||||
|
*/
|
||||||
|
@Volatile var posturePollIntervalMs: Long = 200L
|
||||||
|
|
||||||
// ── Walking detector 임계값 (flowchart 4-2 / 5-2) ─────────────────
|
// ── Walking detector 임계값 (flowchart 4-2 / 5-2) ─────────────────
|
||||||
/** Walking 인정 최소 pitch (deg). |pitch| > 이 값 이라야 walking 후보. */
|
/** Walking 인정 최소 pitch (deg). |pitch| > 이 값 이라야 walking 후보. */
|
||||||
|
|||||||
+13
-16
@@ -719,10 +719,11 @@ fun PiezoMonitoringView(appState: AppState) {
|
|||||||
com.example.medilightv2android.services.PostureEventLogger.startSession(context)
|
com.example.medilightv2android.services.PostureEventLogger.startSession(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IMU polling + 자세 분류 (Phase 1) — 10초 주기로 mtb 명령 → rim: 응답 받아 분류.
|
// IMU polling + 자세 분류 — msp 명령 (rsp: 응답, IMU-only).
|
||||||
// msp 가 펌웨어에서 rsp: 응답 안 보내는 듯해 mtb (alignment) 로 우회.
|
// 6/30: 이전엔 mtb (piezo 6ch + IMU) 폴링으로 매 cycle piezo burst 가청음 발생.
|
||||||
// mtb 는 piezo measurement 도 트리거하므로 piezoCollector.skipNextComplete 로
|
// 사용자앱에서 msp 의 rsp: 응답 정상 (실측 VBTFW0120, 18B = tag+12B sample+crc) 확정 후
|
||||||
// user 의 측정 callback 가 spurious fire 되는 거 차단.
|
// 데모도 동일 적용. WalkingDetector window (1.5초) 확보 위해 폴링 주기 1000→200ms.
|
||||||
|
// 둘 다 imuCollector.parseRsp/parseRim 가 같은 onComplete 콜백 트리거 → classifier 동일.
|
||||||
LaunchedEffect(isConnected, isDemoMode) {
|
LaunchedEffect(isConnected, isDemoMode) {
|
||||||
Log.d("ImuDbg", "LaunchedEffect start isConnected=$isConnected isDemoMode=$isDemoMode")
|
Log.d("ImuDbg", "LaunchedEffect start isConnected=$isConnected isDemoMode=$isDemoMode")
|
||||||
if (isDemoMode) {
|
if (isDemoMode) {
|
||||||
@@ -780,20 +781,16 @@ fun PiezoMonitoringView(appState: AppState) {
|
|||||||
Log.d("ImuDbg", "imuCollector log: $msg")
|
Log.d("ImuDbg", "imuCollector log: $msg")
|
||||||
}
|
}
|
||||||
while (isActive) {
|
while (isActive) {
|
||||||
// Auto Scan 의 cycle 사이 idle 시간에도 polling 가능 — isAutoMeasuring 조건 제외.
|
// msp 는 piezo burst 없는 IMU-only — Auto/Single Scan 중에도 충돌 없이 가능.
|
||||||
// isMeasuring 은 현재 measure() 호출 중인 짧은 시간만 true → 사이 idle 에서 mtb 가능.
|
// 단 isMeasuring (현재 measure() 호출 중) / isSpotInProgress 짧은 시간만 skip
|
||||||
// isSpotInProgress 는 5회 연속 측정이라 분리 (충돌 회피).
|
// (BLE 큐 충돌 방어).
|
||||||
// 이전: Auto Scan 켜두면 isAutoMeasuring=true 영원 유지 → mtb 영원 skip → posture UNKNOWN
|
// Auto Scan 자체는 mtb 응답에 IMU 가 들어있어 자동으로 posture 갱신됨 (parseRim).
|
||||||
if (isConnected && !isMeasuring && !isSpotInProgress) {
|
if (isConnected && !isMeasuring && !isSpotInProgress) {
|
||||||
Log.d("ImuDbg", "→ sendMtb (posture poll)")
|
if (!isAutoMeasuring) {
|
||||||
bleManager.piezoCollector.skipNextComplete = true
|
// idle — msp polling (무음)
|
||||||
val sent = bleManager.sendMtb()
|
bleManager.sendImuQuery()
|
||||||
if (!sent) {
|
|
||||||
bleManager.piezoCollector.skipNextComplete = false // throttled, no response coming
|
|
||||||
Log.d("ImuDbg", "mtb throttled — skip this cycle")
|
|
||||||
}
|
}
|
||||||
} else {
|
// isAutoMeasuring=true 면 mtb 의 rim: 가 알아서 posture 갱신, msp skip
|
||||||
Log.d("ImuDbg", "skip (connected=$isConnected measuring=$isMeasuring spot=$isSpotInProgress)")
|
|
||||||
}
|
}
|
||||||
kotlinx.coroutines.delay(GreenZoneConstants.posturePollIntervalMs)
|
kotlinx.coroutines.delay(GreenZoneConstants.posturePollIntervalMs)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user