iran prediction 47개 Python 파일을 prediction/ 디렉토리로 복제: - algorithms/ 14개 분석 알고리즘 (어구추론, 다크베셀, 스푸핑, 환적, 위험도 등) - pipeline/ 7단계 분류 파이프라인 - cache/vessel_store (24h 슬라이딩 윈도우) - db/ 어댑터 (snpdb 원본조회, kcgdb 결과저장) - chat/ AI 채팅 (Ollama, 후순위) - data/ 정적 데이터 (기선, 특정어업수역 GeoJSON) config.py를 kcgaidb로 재구성 (DB명, 사용자, 비밀번호) DB 연결 검증 완료 (kcgaidb 37개 테이블 접근 확인) Makefile에 dev-prediction / dev-all 타겟 추가 CLAUDE.md에 prediction 섹션 추가 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
60 lines
1.6 KiB
Python
60 lines
1.6 KiB
Python
import pandas as pd
|
|
from algorithms.location import haversine_nm
|
|
|
|
GAP_SUSPICIOUS_SEC = 1800 # 30분
|
|
GAP_HIGH_SUSPICIOUS_SEC = 3600 # 1시간
|
|
GAP_VIOLATION_SEC = 86400 # 24시간
|
|
|
|
|
|
def detect_ais_gaps(df_vessel: pd.DataFrame) -> list[dict]:
|
|
"""AIS 수신 기록에서 소실 구간 추출."""
|
|
if len(df_vessel) < 2:
|
|
return []
|
|
|
|
gaps = []
|
|
records = df_vessel.sort_values('timestamp').to_dict('records')
|
|
|
|
for i in range(1, len(records)):
|
|
prev, curr = records[i - 1], records[i]
|
|
prev_ts = pd.Timestamp(prev['timestamp'])
|
|
curr_ts = pd.Timestamp(curr['timestamp'])
|
|
gap_sec = (curr_ts - prev_ts).total_seconds()
|
|
|
|
if gap_sec < GAP_SUSPICIOUS_SEC:
|
|
continue
|
|
|
|
disp = haversine_nm(
|
|
prev['lat'], prev['lon'],
|
|
curr['lat'], curr['lon'],
|
|
)
|
|
|
|
if gap_sec >= GAP_VIOLATION_SEC:
|
|
severity = 'VIOLATION'
|
|
elif gap_sec >= GAP_HIGH_SUSPICIOUS_SEC:
|
|
severity = 'HIGH_SUSPICIOUS'
|
|
else:
|
|
severity = 'SUSPICIOUS'
|
|
|
|
gaps.append({
|
|
'gap_sec': int(gap_sec),
|
|
'gap_min': round(gap_sec / 60, 1),
|
|
'displacement_nm': round(disp, 2),
|
|
'severity': severity,
|
|
})
|
|
|
|
return gaps
|
|
|
|
|
|
def is_dark_vessel(df_vessel: pd.DataFrame) -> tuple[bool, int]:
|
|
"""다크베셀 여부 판정.
|
|
|
|
Returns: (is_dark, max_gap_duration_min)
|
|
"""
|
|
gaps = detect_ais_gaps(df_vessel)
|
|
if not gaps:
|
|
return False, 0
|
|
|
|
max_gap_min = max(g['gap_min'] for g in gaps)
|
|
is_dark = max_gap_min >= 30 # 30분 이상 소실
|
|
return is_dark, int(max_gap_min)
|