버그: all_ais 를 vessel_dfs(classification 통과 500척)만 대상으로 구성 →
허가선 906척 중 실제 AIS 존재 866척(upper bound 95.8%) 임에도 매칭률
9.4% 에 머물렀던 진짜 원인.
수정: vessel_store._tracks 전체에서 중국 MID(412/413/414) 활성 선박을
대상으로 match_ais_to_registry 호출. 매칭률 upper bound 95.8% 까지 회복 기대.
검증: 이번 AIS 실제 샘플 조사로 판명:
- AIS 고유 정규화 이름 411,908 개
- 허가선 정규화 이름 904 개
- 교집합 866 개 (95.8%)
→ 정규화 로직은 정상 작동. 문제는 호출 범위였음.
버그 원인: 초기 정규화가 선박번호(suffix)까지 제거 → '浙岭渔20865' → '浙岭渔' 로
축약 → 동명이 수십 개 발생 → len(unassigned)>1 조건에 전부 탈락 → FUZZY=0건.
중국/한국 어선명은 업체명+선박번호가 고유 식별자이므로 숫자 자체는 보존해야 함.
정규화는 공백/구두점/대소문자/'NO.' 마커만 통일:
'ZHE LING YU 20865' ↔ 'zhelingyu20865' ↔ 'ZHE-LING-YU-20865' 모두 일치
FUZZY 매칭 key 는 name_en 만 등록 (AIS 보고 이름이 영문이 주류).
두 가지 근본 버그를 동시에 해결:
1. Join key 버그 — raw AIS timestamp(ms 단위) inner join 은 두 선박 간 우연히
일치하는 확률이 거의 0. vessel_store._tracks 의 time_bucket(5분 리샘플)
컬럼을 우선 사용. _pair_join_key() 헬퍼로 fallback 지원.
2. AND 게이트 0건 문제 — 스펙 100%(2h 연속 + 500m + SOG 2-4 + sog_delta 0.5 +
cog 10°)를 전부 요구하면 실제 공조 페어를 놓침. Tier 분류로 재설계:
- STRONG : 스펙 100% (24 cycles, 기존 조건)
- PROBABLE: 800m / SOG 1.5-5 / sog_delta 1.0 / cog 20° / 12 cycles + 0.6 ratio
- SUSPECT : 동일 완화 조건 / 6 cycles + 0.3 ratio (플래그만)
G-06 판정은 STRONG/PROBABLE 만. SUSPECT 는 약한 신호로 노출.
거부 사유 카운터(REJECT_COUNTERS) + tier 카운트를 사이클별 로그 출력.
'조건이 엄격한건지 실제 페어가 없는건지' 원인 구분 가능.
피드백 메모리: feedback_detection_tier.md
- pair_trawl._ensure_sog_cog(): _trajectory_similarity 진입 시 sog/cog 없으면
vessel_store._compute_sog_cog() 로 haversine 계산 (tracks + timestamp 만 있으면 OK)
- pool 을 vessel_store._tracks 전체(55k)로 원복: 한국 440xxx/러시아 273xxx 페어 탐색 가능
- base 필터 중국 MID 확장: 412 → 412/413/414 (본토/홍콩/마카오)
- df_targets groupby 우회 제거 (불필요한 결합)
버그: vessel_store._tracks 는 raw_sog 만 보유 → _trajectory_similarity 가
sog/cog 컬럼 부재로 항상 0 반환 → candidates=0.
df_targets(select_analysis_targets 결과, 412* 전체 8k+ 에 sog/cog 계산)
를 mmsi 별로 groupby 하여 pool_tracks dict 생성. base 확장 필터의
sog 컬럼도 동일하게 적용.
첫 사이클 로그: base=512, pool=54825 → candidates=0 (2026-04-16 09:25).
수정 후 재검증 예정.