content(app-F): 补全未知参考率对角度界的影响条件

在既有 K/U 消元关系中分离完整信息损失、角度协方差及合成指标。
明确单星冻结可分模型的正交条件,并用消参考 Doppler 后的耦合解释多星近重合。
保留有限距离相对率的角导数和完整协方差变换,不扩大模型或实验范围。

复用现有确定性脚本核对协方差更新、SS 正交性与非零信息损失反例。
自检及 SyncTeX 多遍编译通过,无断引用或新增溢出;实际图值以代码侧回灌为准。
This commit is contained in:
2026-10-12 18:46:50 +08:00
parent a2713e80cf
commit 6f977c11cf
2 changed files with 133 additions and 4 deletions
+48 -1
View File
@@ -1,4 +1,4 @@
"""Deterministic formula checks for Appendix G; run: python check_finite_range_bearing.py.
"""Deterministic formula checks for Appendices F/G; run: python check_finite_range_bearing.py.
Synthetic sphere and known trajectories only: no constellation or MC validation.
"""
@@ -182,6 +182,53 @@ def main():
assert np.linalg.matrix_rank(projected) == 4
cov = np.linalg.inv(info)
check("full covariance reparameterization", T@np.linalg.inv(T.T@info@T)@T.T, cov)
# Appendix F: structured CP has real frame amplitudes and one phase per star.
for selected in [[0], [0, 1]]:
Jcp = np.vstack([jac[ell] for ell in selected])
N = np.zeros((len(Jcp), len(selected)*(len(TIMES)+1)), dtype=complex)
block_size = len(TIMES)*len(ARRAY)
for star, ell in enumerate(selected):
rows = slice(star*block_size, (star+1)*block_size)
N[rows, star*(len(TIMES)+1)] = 1j*means[ell]
for frame in range(len(TIMES)):
block = slice(star*block_size+frame*len(ARRAY),
star*block_size+(frame+1)*len(ARRAY))
N[block, star*(len(TIMES)+1)+frame+1] = means[ell][frame*len(ARRAY):(frame+1)*len(ARRAY)]
real_N = np.vstack((N.real, N.imag))
scores = np.vstack((Jcp.real, Jcp.imag))*np.sqrt(2)
residual_scores = scores-real_N@np.linalg.lstsq(real_N, scores, rcond=None)[0]
I = residual_scores.T@residual_scores
K, coupling, rate_info = I[:3, :3], I[:3, 3], I[3, 3]
CK = np.linalg.inv(K)
CU = np.linalg.inv(K-np.outer(coupling, coupling)/rate_info)
v = CK@coupling
h = rate_info-coupling@v
assert h > 0, "K/U covariance identities require identifiable rate"
check("CP K/U full covariance update", CU-CK, np.outer(v, v)/h, 1e-9)
q = coupling[:2]-K[:2, 2]*coupling[2]/K[2, 2]
G = K[:2, :2]-np.outer(K[:2, 2], K[:2, 2])/K[2, 2]
check("partial angle-rate coupling", v[:2], np.linalg.solve(G, q), 1e-9)
metric = np.diag([np.cos(x[1])**2, 1.])
check("CP K/U angular metric update", np.trace(metric@(CU[:2, :2]-CK[:2, :2])),
v[:2]@metric@v[:2]/h, 1e-9)
if len(selected) == 1:
check("SS spatial-temporal orthogonality", I[:2, 2:], np.zeros((2, 2)), 1e-10)
check("SS exact angular subblock equality", CU[:2, :2], CK[:2, :2], 1e-10)
assert CU[2, 2] > CK[2, 2], "First-sample time origin couples frequency and rate"
# Nonzero EFIM loss can affect only Doppler after the covariance update.
K = np.array([[3., .3, .5], [.3, 2., -.4], [.5, -.4, 4.]])
CK = np.linalg.inv(K)
for v in [np.array([0., 0., .7]), np.array([.2, -.3, .7])]:
coupling = K@v
rate_info = coupling@v+2.
CU = np.linalg.inv(K-np.outer(coupling, coupling)/rate_info)
check("synthetic rate-only covariance update", CU-CK, np.outer(v, v)/2., 1e-12)
assert np.linalg.norm(coupling[:2]) > 0
if np.linalg.norm(v[:2]) == 0:
check("nonzero EFIM loss with equal angle bounds", CU[:2, :2], CK[:2, :2], 1e-12)
assert CU[2, 2] > CK[2, 2]
else:
assert np.trace(metric@(CU[:2, :2]-CK[:2, :2])) > 0
delta = .19
shifted = TIMES-delta
for ell in range(2):