Spaces:
Sleeping
Sleeping
| """Test phần TOÁN THUẦN của poolcoach_cv.resim (lát A2 phần 2) — chạy | |
| trên venv app KHÔNG pooltool thật (conftest stub): mọi test ở đây chỉ đụng | |
| sample_traj / rmse_mm / align_t0 / các đường ra sớm của resim_result. | |
| Pipeline pooltool thật nghiệm thu bằng probe tự-nhất-quán trên venv CV | |
| (sim → lấy chính sim làm quan sát → RMSE 0.0mm, 13/08) + rerun rack_a1. | |
| """ | |
| from __future__ import annotations | |
| import numpy as np | |
| import pytest | |
| from poolcoach_cv import resim as rs | |
| def test_sample_traj_noi_suy_va_kep_hai_dau(): | |
| sim_t = np.array([0.0, 1.0, 2.0]) | |
| sim_xy = np.array([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]]) | |
| out = rs.sample_traj(sim_t, sim_xy, np.array([-0.5, 0.5, 1.5, 9.0])) | |
| assert out == pytest.approx(np.array( | |
| [[0.0, 0.0], # trước t=0 — kẹp điểm đầu (bi chưa đánh) | |
| [0.5, 0.0], # nội suy tuyến tính | |
| [1.0, 0.5], | |
| [1.0, 1.0]])) # sau khi sim dừng — kẹp điểm cuối (lệch là thật) | |
| def test_rmse_mm(): | |
| obs = np.array([[0.0, 0.0], [1.0, 0.0]]) | |
| sim = np.array([[0.0, 0.003], [1.0, -0.004]]) | |
| assert rs.rmse_mm(obs, sim) == pytest.approx( | |
| np.sqrt((3.0 ** 2 + 4.0 ** 2) / 2)) | |
| def test_align_t0_chon_dung_nac_lech(): | |
| """Quan sát = chính quỹ đạo sim dịch sớm 1 bước frame (motion_start | |
| trễ sau cú chạm) — lưới dò phải chọn Δ ≈ dt và RMSE về ~0.""" | |
| dt = 1 / 30.0 | |
| sim_t = np.arange(0.0, 2.0, 0.01) | |
| sim_xy = np.stack([0.3 + 1.5 * sim_t, 0.4 + 0.9 * sim_t], axis=1) | |
| ms = 0.5 | |
| true_t0 = ms - dt # cú chạm sớm hơn motion_start 1 frame | |
| obs_t = np.arange(ms, 1.8, dt) | |
| obs_xy = rs.sample_traj(sim_t, sim_xy, obs_t - true_t0) | |
| t0, delta, err = rs.align_t0(obs_t, obs_xy, sim_t, sim_xy, ms, dt) | |
| assert delta == pytest.approx(dt, abs=dt / rs.ALIGN_STEPS + 1e-9) | |
| assert err < 2.0 # mm — lưới dò hữu hạn, không đòi 0 tuyệt đối | |
| assert t0 == pytest.approx(ms - delta) | |
| def test_resim_result_khong_motion_start_ra_error_ca_hai_bo(): | |
| out = rs.resim_result({"metrics": {"motion_start_s": None}, | |
| "track": [], "shotnet": None}, state={}) | |
| assert set(out["sets"]) == {"shotnet", "analytic"} | |
| for blk in out["sets"].values(): | |
| assert "motion_start" in blk["error"] | |
| assert "rmse_mm" not in blk | |
| assert out["table_w_m"] == 1.27 and out["table_l_m"] == 2.54 | |
| def test_resim_result_track_thieu_ra_error(): | |
| out = rs.resim_result( | |
| {"metrics": {"motion_start_s": 0.5}, | |
| "track": [{"t_s": 0.6, "x_m": 0.5, "y_m": 0.5}]}, state={}) | |
| for blk in out["sets"].values(): | |
| assert "track" in blk["error"] | |
| def test_obs_arrays_cue_nghi_la_median_truoc_motion_start(): | |
| res = {"metrics": {"motion_start_s": 0.5}, | |
| "track": [{"t_s": 0.1, "x_m": 0.60, "y_m": 0.70}, | |
| {"t_s": 0.2, "x_m": 0.62, "y_m": 0.70}, | |
| {"t_s": 0.3, "x_m": 0.61, "y_m": 0.72}, | |
| {"t_s": 0.6, "x_m": 0.80, "y_m": 0.90}, | |
| {"t_s": 0.7, "x_m": 0.90, "y_m": 1.00}]} | |
| obs_t, obs_xy, cue0 = rs._obs_arrays(res) | |
| assert list(obs_t) == [0.6, 0.7] | |
| assert cue0 == pytest.approx([0.61, 0.70]) | |