# -*- coding: utf-8 -*- """BG30 Bước 1.1 — bảng đối chiếu quy ước φ trên 5 điểm dữ liệu thật. Câu hỏi (BRIEF 30): Δφ model↔analytic chụm ~140–149° trên clip thật có phải lỗi quy ước/mirror không? So φ model với 4 phép biến đổi của φ analytic: {φ, −φ mod 360, φ+180, 180−φ} (−φ = lật trục x; 180−φ = lật trục y; φ+180 = xoay 180° — xoay 180° là nhập nhằng VÔ HẠI duy nhất của quy ước chấm góc, homography.py:48-49). 5 điểm dữ liệu — KHÔNG đo mới, chỉ đọc số đã công bố: - 2 job BG28 (model c3): đọc từ JSON đã lưu ở ``poolcoach-docs\\results\\2026-08-12_shotnet_p2\\cu1?_app_bg28.json`` (chỉ ĐỌC — file thuộc repo docs, không sửa). - 3 job smoke BG29b (model c4b): Redis hết TTL, số chép NGUYÊN VĂN từ HANDOFF 13/08/2026 10:25 (bảng "Số 2 cột") — đúng nguồn BRIEF chỉ định. Chạy (venv app, không cần GPU/torch): python scripts/broadcast/diag_phi_conventions.py Artifact: ``D:\\Khoa luan\\bb9_diag30_phi_conventions.csv`` (mới, không đè gì) — mỗi dòng một (điểm × phép biến đổi), kèm commit hash lúc chạy. Caveat đọc kết quả (ghi sẵn để khỏi đọc quá tay): φ analytic của cả 5 điểm chỉ trải 258.0–261.6° (3.6°) — bảng này thực chất chỉ thử các phép biến đổi tại MỘT hướng đánh, nên "phép nào khớp nhất" không tự nó phân biệt được mirror với một offset cố định ~±140°; phải đọc KÈM audit code (Bước 1.2). """ from __future__ import annotations import csv import json import subprocess import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[2] WORKSPACE = ROOT.parent BG28_DIR = WORKSPACE / "poolcoach-docs" / "results" / "2026-08-12_shotnet_p2" OUT_CSV = WORKSPACE / "bb9_diag30_phi_conventions.csv" # 3 job smoke BG29b — nguyên văn HANDOFF 13/08/2026 10:25 (model c4b, # commit lúc smoke a65787a); Redis TTL đã hết nên đây là nguồn duy nhất. BG29B_POINTS = [ {"diem": "cu11_lan1_bg29b", "model": "c4b", "phi_model": 109.3, "phi_analytic": 258.3}, {"diem": "cu11_lan2_bg29b", "model": "c4b", "phi_model": 110.7, "phi_analytic": 258.0}, {"diem": "cu12_bg29b", "model": "c4b", "phi_model": 121.4, "phi_analytic": 261.6}, ] TRANSFORMS = [ ("phi", lambda p: p % 360.0), ("neg_phi", lambda p: (-p) % 360.0), ("phi_plus_180", lambda p: (p + 180.0) % 360.0), ("180_minus_phi", lambda p: (180.0 - p) % 360.0), ] def circ_diff_signed(a: float, b: float) -> float: """a − b về (−180, 180] — dấu để thấy phần dư lệch về phía nào.""" return ((a - b + 180.0) % 360.0) - 180.0 def load_bg28_points() -> list[dict]: pts = [] for cu in (11, 12): p = BG28_DIR / f"cu{cu}_app_bg28.json" r = json.loads(p.read_text(encoding="utf-8"))["result"] pts.append({"diem": f"cu{cu}_bg28", "model": "c3", "phi_model": float(r["shotnet"]["phi_deg"]), "phi_analytic": float(r["metrics"]["phi_deg"])}) return pts def git_head() -> str: try: return subprocess.run(["git", "-C", str(ROOT), "rev-parse", "--short", "HEAD"], capture_output=True, text=True, check=True).stdout.strip() except Exception: return "unknown" def main() -> int: points = load_bg28_points() + BG29B_POINTS commit = git_head() rows = [] for pt in points: for name, fn in TRANSFORMS: tv = fn(pt["phi_analytic"]) resid = circ_diff_signed(pt["phi_model"], tv) rows.append({"diem": pt["diem"], "model": pt["model"], "phi_model": pt["phi_model"], "phi_analytic": pt["phi_analytic"], "phep": name, "phi_analytic_bien_doi": round(tv, 1), "du_deg": round(resid, 1), "abs_du_deg": round(abs(resid), 1), "commit_luc_chay": commit}) if OUT_CSV.exists(): print(f"BO QUA ghi CSV: {OUT_CSV} da ton tai (luat khong de " f"artifact cu) -- xoa tay neu muon sinh lai.") else: with OUT_CSV.open("w", newline="", encoding="utf-8") as f: wr = csv.DictWriter(f, fieldnames=list(rows[0].keys())) wr.writeheader() wr.writerows(rows) print(f"da ghi {OUT_CSV} ({len(rows)} dong, commit {commit})") # ----- bảng tóm tắt ra console (ASCII — bẫy cp1252) print(f"\n{'diem':<18}{'model':<7}{'phi_mod':>8}{'phi_ana':>8}" + "".join(f"{n:>15}" for n, _ in TRANSFORMS)) for pt in points: cells = "" for name, fn in TRANSFORMS: resid = circ_diff_signed(pt["phi_model"], fn(pt["phi_analytic"])) cells += f"{resid:>+15.1f}" print(f"{pt['diem']:<18}{pt['model']:<7}{pt['phi_model']:>8.1f}" f"{pt['phi_analytic']:>8.1f}{cells}") print("\nmedian |du| theo phep (5 diem / rieng c4b):") for name, fn in TRANSFORMS: alls = sorted(abs(circ_diff_signed(pt["phi_model"], fn(pt["phi_analytic"]))) for pt in points) c4b = sorted(abs(circ_diff_signed(pt["phi_model"], fn(pt["phi_analytic"]))) for pt in points if pt["model"] == "c4b") print(f" {name:<15} all={alls[len(alls) // 2]:>6.1f} " f"c4b={c4b[len(c4b) // 2]:>6.1f}") print("\nCAVEAT: phi analytic 5 diem chi trai 258.0-261.6 (3.6 deg) — " "bang chi thu duoc MOT huong danh; doc kem audit code Buoc 1.2.") return 0 if __name__ == "__main__": sys.exit(main())