feat(algo): 데모 piezophantomtest ea3f15c/b252de0 이식 — post peak-only + apply_cross flag

## 이식 대상
알고리즘팀 main : 6229825 → ea3f15c (fast-forward)
- b252de0 : post wall 후보는 peak-only 로 탐색 (shoulder 제외)
- 4afda38 : method_d(apply_cross) 옵션 추가

## MethodDWallSelect 변경
1. BOUNDARY_PEAK_MARGIN = 2 (post 전용)
   - post 탐색창 hi 를 2 샘플 밖으로 넓혀 view 확보 → 경계에 걸린 peak 이
     shoulder 로 오분류되던 케이스 해결. 수용 범위 [lo, hi] 유지 → farther
     peak 새로 안 받음 (d_max 전역 확대 부작용 회피).
2. side=POST 는 peak-only
   - shoulder(d2 변곡) 는 후벽을 깊은쪽으로 과확장 → BV 과대 (특히 30° 채널).
   - ant 는 shoulder 유지 (near-field 전벽은 shoulder 로 잡히는 게 정상).

## MethodDRunner 변경
- detectMultichannel(applyCross: Boolean = true) 파라미터 추가.
  false 면 교차채널 3종 (ant_tiebreak / neighbor_top_validate / inward_post) 건너뜀.

## AlignmentAdvisorV2 (RollingAligner) 변경
- 정렬 위치 선택은 applyCross=false 로 base 검출 사용.
  이유: 교차채널 보정이 nch 를 바꿔 위치 선택이 흔들리는 것을 차단.
  BV 산출은 PiezoMonitoringView 경로에서 별도로 applyCross=true 로 진행.

## 컴파일
:app:compileDevDebugKotlin / :compileDemoDebugKotlin / UnitTest 모두 통과.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-07-06 10:22:56 +09:00
parent 097d086bce
commit 15db5ec8bf
3 changed files with 42 additions and 8 deletions
@@ -54,12 +54,19 @@ object MethodDRunner {
/**
* 6채널 (또는 N채널) raw 신호 → 채널별 MethodDResult? 리스트.
* raw[ch] 길이가 다르면 그대로 처리 (각 채널 독립).
*
* @param applyCross false 면 교차채널 보정을 건너뛴 **base 검출**만 반환.
* 2026-07-06 이식 (piezophantomtest ea3f15c):
* 정렬 위치 선택 (`AlignGuide` / `select_alignment_position`) 은 base 검출로 수행해야 함
* — 교차채널 보정이 nch 를 바꿔 위치 선택이 흔들리는 것을 차단.
* BV 산출은 applyCross=true 로 별도 수행.
*/
fun detectMultichannel(
signals: List<DoubleArray>,
params: MethodDParams = MethodDParams.DEFAULT,
beamAnglesDeg: DoubleArray? = null,
applyTgc: Boolean = true,
applyCross: Boolean = true,
): List<MethodDResult?> {
val angles = beamAnglesDeg ?: PiezoHW.degreeAll
// 1) per-channel SG denoise (heavy + light)
@@ -81,7 +88,9 @@ object MethodDRunner {
)
}
// 5) cross-channel 후처리 (Python `apply_cross_channel` 순서 고정)
applyCrossChannel(results, angles, lightTgc, heavyTgc, signals, params)
if (applyCross) {
applyCrossChannel(results, angles, lightTgc, heavyTgc, signals, params)
}
return results
}