move to new version

This commit is contained in:
awe
2026-09-17 17:58:36 +03:00
parent e113ef60ab
commit e202f57ef8
16 changed files with 864 additions and 67 deletions
@@ -0,0 +1,101 @@
"""Notebook-formula reference for the C++ `gpr_timing_check` tool.
Recomputes the same synthetic 4x4 scenario with the Multi_antenna_3D notebook
formulas (compute_pair_timestamps_for_snapshot + compute_frequency_sample_times
+ apply_intra_sweep_phase_correction, 'segmented_interleaved' branch) and
prints the motion phase at the same probe indices in the same format. Run:
make gpr_timing_check && build/bin/gpr_timing_check > /tmp/cpp.txt
python python_app/scripts/gpr_timing_check_reference.py > /tmp/ref.txt
The `int_minus` / `int_focus` lines of both outputs must match to ~1e-9 rad
(phases are compared wrapped to (-pi, pi]).
"""
from __future__ import annotations
import numpy as np
SPEED_OF_LIGHT = 299_792_458.0 # C++ kSpeedOfLightMetersPerSec
SPEED_M_S = 1.42
LOOK_ANGLE_DEG = 19.5
DIRECTION_SIGN = 1.0
F_START = 1.5e9
F_STOP = 6.0e9
N_FREQ = 11
# Trace windows of the synthetic collection (absolute seconds): segment A is
# Tx0/Tx1, segment B is Tx2/Tx3 (260 ms, stretched).
WINDOWS = {0: (1.000, 1.223), 1: (1.000, 1.223), 2: (1.223, 1.483), 3: (1.223, 1.483)}
TX_ORDER = [0, 1, 2, 3]
PROBE_INDICES = (0, 5, 10)
def wrap(phase: np.ndarray) -> np.ndarray:
"""Wrap phases to (-pi, pi] the way -arg(exp(-1j*phi)) reports them."""
return -np.angle(np.exp(-1j * phase))
def main() -> None:
freqs = np.linspace(F_START, F_STOP, N_FREQ)
first = min(start for start, _ in WINDOWS.values())
last = max(end for _, end in WINDOWS.values())
acq_s = last - first
t_ref = 0.5 * acq_s
# Group Tx with identical windows into segments (snapshot_acquisition_timing
# + compute_pair_timestamps_for_snapshot).
groups: dict[tuple[float, float], list[int]] = {}
for tx in TX_ORDER:
groups.setdefault(WINDOWS[tx], []).append(tx)
seg_of = {}
for window in sorted(groups):
for slot, tx in enumerate(groups[window]):
seg_of[tx] = (window, slot, len(groups[window]))
cos_theta = np.cos(np.radians(LOOK_ANGLE_DEG))
motion_factor = DIRECTION_SIGN * SPEED_M_S * cos_theta
print(f"reference: frame_center_s={t_ref:.9f} acq_s={acq_s:.9f}")
phi_by_tx = {}
for tx in TX_ORDER:
(t0_abs, t1_abs), slot, n_slots = seg_of[tx]
seg_start = t0_abs - first
seg_end = t1_abs - first
t_seg = seg_end - seg_start
t_center = 0.5 * (seg_start + seg_end)
# compute_frequency_sample_times, segmented branch.
dt_event = t_seg / (n_slots * N_FREQ)
t_abs = seg_start + (n_slots * np.arange(N_FREQ) + slot) * dt_event
dt_intra = t_abs - t_center
dtau_intra = 2.0 * motion_factor * dt_intra / SPEED_OF_LIGHT
dt_pair = t_center - t_ref
dtau_pair = 2.0 * motion_factor * dt_pair / SPEED_OF_LIGHT
phi = 2.0 * np.pi * freqs * (dtau_intra + dtau_pair)
phi_by_tx[tx] = phi
wrapped = wrap(phi)
print(
f"int_minus tx o{tx}: "
+ " ".join(f"phi(k={k})={wrapped[k]:.12e}" for k in PROBE_INDICES)
)
for tx in TX_ORDER:
phi = phi_by_tx[tx]
# int_focus: remove the constant + linear-in-frequency component
# (least squares on [1, f_rel], notebook intra_phase_focus_only).
f_rel = freqs - freqs.mean()
design = np.column_stack([np.ones_like(f_rel), f_rel])
beta, *_ = np.linalg.lstsq(design, phi, rcond=None)
residual = phi - design @ beta
wrapped = wrap(residual)
print(
f"int_focus tx o{tx}: "
+ " ".join(f"phi(k={k})={wrapped[k]:.12e}" for k in PROBE_INDICES)
)
if __name__ == "__main__":
main()