docs(detach): 원본과 값이 다른 이유를 파일에 남긴다
헤더가 "detachment_detection.py 1:1 포팅"에 "둘 다 threshold 미만이면 미부착"이라고 돼 있었는데 둘 다 사실이 아니다. 임계는 100→30, 규칙은 AND→OR 로 바뀌었고 근거는 커밋 메시지에만 있었다 — 파일만 보면 왜 다른지 알 수 없고, 다음 사람이 "원본과 맞추자" 며 되돌리기 딱 좋은 상태였다. 두 변경 다 실기기 증상을 보고 내린 결정이다: · 100→30 (258bad9) — 원본의 100 은 팬텀에서 미부착 vs incorrect 두 조건으로 뽑은 값이다. 실기기에는 "붙었는데 신호가 약한" 세 번째 조건이 있고, 100 을 쓰면 그것까지 미부착으로 잡아 측정 중인 사용자에게 경고가 계속 뜬다. · AND→OR (b2c1ba2) — 분리된 상태인데 공기 중 EMI 로 std 가 30 을 넘어 판정이 지연. parity 규칙이 여기 걸리지 않는다는 것도 적었다. SPEC 의 대조 계약은 §5·§8 등재 항목에 적용되는데 detachment 는 SPEC 에 없고, Python 쪽에서도 이 모듈을 부르는 코드가 하나도 없다(파이프라인 밖 독립 유틸). 남은 숙제도 같이 적었다 — 두 변경이 서로를 가린다. OR 의 근거("std 가 30 이상인 경우가 흔하다")는 임계가 30 이라서 생긴 문제라, 100 이었다면 AND 로도 즉시 판정됐을 것이다. 세 조합을 실기기에서 비교한 적이 없으므로, 채널 접촉 칸의 mean·std 실측값을 모아 정하기로 한다. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,12 +4,44 @@ import kotlin.math.abs
|
||||
import kotlin.math.sqrt
|
||||
|
||||
/**
|
||||
* 센서 미부착 감지 — detachment_detection.py 1:1 포팅
|
||||
* 센서 미부착 감지.
|
||||
*
|
||||
* ADC 신호의 [20:80] 구간에서 baseline([80:100] median)을 빼고,
|
||||
* mean(|y|)과 std(y)가 모두 threshold 미만이면 미부착으로 판정.
|
||||
* 원본: `vesiscan_test/library/alignment/detachment_detection.py`
|
||||
*
|
||||
* 미부착 시 LED state 4 (DETACH_WARNING) 전송.
|
||||
* ADC 신호의 [20:80] 구간에서 baseline([80:100] median)을 빼고, CH0~CH3 의
|
||||
* mean(|y|)·std(y) 를 평균 내 임계와 비교한다. 미부착 시 LED state 4
|
||||
* (DETACH_WARNING) 전송.
|
||||
*
|
||||
* ## ⚠ 원본과 값이 다르다 — 되돌리지 말 것
|
||||
*
|
||||
* | | Python | 여기 |
|
||||
* |---|---|---|
|
||||
* | 임계 | 100 | **30** |
|
||||
* | 규칙 | AND | **OR** |
|
||||
*
|
||||
* 최초 포팅(`ea19b74`, 2026-04-24)은 100·AND 로 원본과 같았다. 두 번 바뀌었고
|
||||
* **둘 다 실기기 증상을 보고 내린 결정**이다.
|
||||
*
|
||||
* **100 → 30** (`258bad9`, 2026-05-04) — "완전 탈착만 감지, 거리 약화 무시".
|
||||
* 원본의 100 은 **팬텀** 실측 분포에서 나온 값이고, 그 두 조건은 *미부착* 과
|
||||
* *incorrect(잘못 붙음)* 이다. 실기기에는 조건이 하나 더 있다 — **붙었는데 신호가
|
||||
* 약한 경우**(밀착 부족·체형·젤 상태). 그건 미부착이 아닌데 100 을 쓰면 그것까지
|
||||
* 잡아서, 멀쩡히 측정 중인 사용자에게 경고가 계속 뜬다.
|
||||
*
|
||||
* **AND → OR** (`b2c1ba2`, 2026-08-11) — 사용자 보고. 센서가 아예 분리됐는데 공기 중
|
||||
* EMI/RF 로 std 가 30 을 넘어, 둘 다 30 미만을 요구하는 AND 에서는 판정이 몇 초 늦었다.
|
||||
*
|
||||
* ## parity 규칙이 여기엔 걸리지 않는다
|
||||
* `SPEC.md` 의 Python↔Kotlin 대조 계약은 §5(파이프라인)·§8(정렬) 같은 등재 항목에
|
||||
* 적용된다. detachment 는 SPEC 에 없고, **Python 쪽에서도 이 모듈을 부르는 코드가
|
||||
* 하나도 없다**(파이프라인 밖 독립 유틸). 검증도 팬텀에서만 했다.
|
||||
*
|
||||
* ## 남은 숙제
|
||||
* 두 변경이 서로를 가린다. OR 로 바꾼 근거("std 가 30 이상인 경우가 흔하다")는
|
||||
* **임계가 30 이라서** 생긴 문제다 — 100 이었다면 std<100 은 쉽게 걸려 AND 로도
|
||||
* 즉시 판정됐을 것이다. 30+OR / 30+AND / 100+AND 중 실기기에서 무엇이 나은지는
|
||||
* 비교된 적이 없다. 임상 화면의 채널 접촉 칸이 mean·std 실측값을 띄우므로
|
||||
* ([DetachWatcher]), 실제로 뜬 순간과 약하게 붙은 순간의 숫자를 모아 정할 것.
|
||||
*/
|
||||
object DetachmentDetection {
|
||||
|
||||
@@ -17,8 +49,14 @@ object DetachmentDetection {
|
||||
const val WINDOW_END = 80
|
||||
const val BASELINE_WINDOW_START = 80
|
||||
const val BASELINE_WINDOW_END = 100
|
||||
|
||||
/** Python `DEFAULT_THR_MEAN` = 100. 위 "원본과 값이 다르다" 참조. */
|
||||
const val THR_MEAN = 30.0
|
||||
|
||||
/** Python `DEFAULT_THR_STD` = 100. 위 "원본과 값이 다르다" 참조. */
|
||||
const val THR_STD = 30.0
|
||||
|
||||
/** Python `N_CENTER_CH`. CH0~CH3 만 본다 — lateral 은 임계 근거가 없다. */
|
||||
const val N_CENTER_CH = 4
|
||||
|
||||
data class DetachmentResult(
|
||||
|
||||
Reference in New Issue
Block a user