feat: Method C (V4.1 CharlesKWON) 통합 + Placement 설정 패널

Method C (walldetect/ 패키지, 38파일):
- V41Detector: OS-CFAR threshold + wavelet denoise + anatomical gate + B-mode score
- Multi-method BV dispatch: frustum(≥2ch) / Verathon(1ch) / sphere fit(cross-check)
- Kasa→LM sphere fitting, sub-sample parabolic refinement
- 패키지 이동: com.medithings.charleskwonslaw → com.example.medilightv2android

통합:
- 설정 UI: A/B 스위치 → A/B/C 버튼 그룹 (A=보라, B=파랑, C=주황)
- Monitoring: Method C 선택 시 V41Detector.detect() → BV dispatch 결과 사용
- Placement: 선택된 Method(A/B/C) 반영 + 톱니바퀴 설정 패널 추가

Placement 그래프 개선:
- Threshold 수평선 (주황 점선)
- Y축 눈금 (0, 1k, 2k)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-29 11:06:22 +09:00
parent dc340608ee
commit c9cd3cb3ce
41 changed files with 4235 additions and 68 deletions
@@ -0,0 +1,23 @@
/*
* Copyright (c) 2026 Medithings Co., Ltd.
* Author: Charles KWON OhJun <charleskwon@medithings.co.kr>
* Project: CharlesKWONsLaw — wall-detect live compare
*
* Multi-channel BV estimation result (4-layer dispatch — see
* docs/BV-CALCULATION-DESIGN.md). Replaces the sphere-only `v41Sphere.bvMl`
* for the primary "BV" metric while sphere fit stays for cross-check.
*/
package com.example.medilightv2android.walldetect.dto
data class BvDispatchResult(
val bvMl: Float?, // null = no estimate
val rMm: Float?, // equivalent sphere radius (sphere or chord-derived)
val method: String, // FrustumLR | FrustumNoLR | SphereLM | ConeFallback | Verathon | None
val confidence: Float, // 0..1
val nCenter: Int, // gated center channels (CH0..CH3)
val nLateral: Int, // gated lateral channels (CH4, CH5)
val lrRatio: Float, // applied LR/AP ratio (1.0 if no lateral)
val warnings: List<String> = emptyList(),
val sphereCrossCheckBvMl: Float? = null,
)
@@ -0,0 +1,27 @@
/*
* Copyright (c) 2026 Medithings Co., Ltd.
* Author: Charles KWON OhJun <charleskwon@medithings.co.kr>
* Project: CharlesKWONsLaw — wall-detect live compare
*
* Per-channel detector result. Schema is identical for V2 and V4.1.
* V2 결과는 v41Diag 가 항상 null.
*/
package com.example.medilightv2android.walldetect.dto
data class ChannelResult(
val ch: Int,
val sg: List<Float>,
val threshold: List<Float>,
val antIdx: Int?,
val postIdx: Int?,
val antRefined: Float?,
val postRefined: Float?,
val antMm: Float?,
val postMm: Float?,
val lumenStart: Int?,
val lumenEnd: Int?,
val chordMm: Float?,
val clipping: Boolean? = null,
val v41Diag: V41Diagnostics? = null
)
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2026 Medithings Co., Ltd.
* Author: Charles KWON OhJun <charleskwon@medithings.co.kr>
* Project: CharlesKWONsLaw — wall-detect live compare
*
* Top-level detector output. Identical schema for V2 and V4.1.
* Only `algorithm` / `algorithmVersion` differ; all other field names match.
*/
package com.example.medilightv2android.walldetect.dto
data class DetectionResult(
val requestId: String,
val timestampMs: Long,
val algorithm: String,
val algorithmVersion: String,
val processingMs: Double,
val perChannel: List<ChannelResult>,
val summary: DetectionSummary
)
@@ -0,0 +1,24 @@
/*
* Copyright (c) 2026 Medithings Co., Ltd.
* Author: Charles KWON OhJun <charleskwon@medithings.co.kr>
* Project: CharlesKWONsLaw — wall-detect live compare
*
* Sweep-level summary. Schema identical for V2 and V4.1.
* V4.1-only fields are null in V2 results.
*/
package com.example.medilightv2android.walldetect.dto
data class DetectionSummary(
val matchCount: Int,
val chordMmMean: Float?,
val chordMmStd: Float?,
val tier: String? = null,
val scoreMean: Float? = null,
val scoreMin: Float? = null,
val scoreMax: Float? = null,
val gatedCount: Int? = null,
val v41Sphere: V41SphereFit? = null,
/** Multi-channel BV dispatch (PR-13). Replaces sphere-only BV as primary value. */
val bvDispatch: BvDispatchResult? = null,
)
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2026 Medithings Co., Ltd.
* Author: Charles KWON OhJun <charleskwon@medithings.co.kr>
* Project: CharlesKWONsLaw — wall-detect live compare
*
* Common input DTO for both V2 and V4.1 detectors.
* 두 detector 가 동일 입력을 받는다는 drift-zero 보장의 단일 source.
*/
package com.example.medilightv2android.walldetect.dto
data class SweepInput(
val requestId: String,
val timestampMs: Long,
val deviceId: String,
val sweepSeq: Int,
val probe: ProbeProfileDto,
val adc: List<List<Int>>,
val gainDb: Int = 0
)
data class ProbeProfileDto(
val name: String,
val rMm: Double,
val anteriorAnchorMm: Double,
val fsHz: Long,
val cMmPerUs: Double,
val anglesDeg: List<Double>
)
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2026 Medithings Co., Ltd.
* Author: Charles KWON OhJun <charleskwon@medithings.co.kr>
* Project: CharlesKWONsLaw — wall-detect live compare
*
* V4.1-only per-channel diagnostics carrier.
* V2 결과에서는 ChannelResult.v41Diag = null. 스키마 자체는 양쪽 동일.
*/
package com.example.medilightv2android.walldetect.dto
data class V41Diagnostics(
val waveletDenoised: List<Float>,
val waveletEnergyRatio: WaveletEnergyRatio,
val waveletCoefs: WaveletCoefs,
val peaksAll: List<Int>,
val spans: List<IntRange2>,
val score: Float,
val scoreSub: ScoreSubscores,
val gated: Boolean,
val tier: String,
val sContrast: Float,
val sContrastTier: String,
val singleChannelBvMl: Float?
)
data class WaveletEnergyRatio(val l1: Float, val l2: Float, val l3: Float)
data class WaveletCoefs(
val l1: List<Float>,
val l2: List<Float>,
val l3: List<Float>,
val a3: List<Float>
)
data class ScoreSubscores(
val uFarPost: Float,
val uLumDark: Float,
val uAntGrad: Float,
val uPostGrad: Float
)
@@ -0,0 +1,22 @@
/*
* Copyright (c) 2026 Medithings Co., Ltd.
* Author: Charles KWON OhJun <charleskwon@medithings.co.kr>
* Project: CharlesKWONsLaw — wall-detect live compare
*
* V4.1-only sweep-level sphere fit + BV result.
* Q5: gated < 4 인 sweep 에서는 null (JS sphere_fit_2step.js 와 동일).
*/
package com.example.medilightv2android.walldetect.dto
data class V41SphereFit(
val mode: String,
val center: Vec3,
val radiusMm: Float,
val bvMl: Float,
val residualStdMm: Float,
val wallPoints: List<Vec3>,
val deltaRMm: Float? = null,
val deltaBvMl: Float? = null,
val nPoints: Int
)
@@ -0,0 +1,11 @@
/*
* Copyright (c) 2026 Medithings Co., Ltd.
* Author: Charles KWON OhJun <charleskwon@medithings.co.kr>
* Project: CharlesKWONsLaw — wall-detect live compare
*/
package com.example.medilightv2android.walldetect.dto
data class Vec3(val x: Float, val y: Float, val z: Float)
data class IntRange2(val start: Int, val end: Int)