docs: bilingual comments for the atmosphere and coordinate validation scripts
This commit is contained in:
parent
ff329e5a8d
commit
b3d7762d0e
Binary file not shown.
|
|
@ -17,6 +17,23 @@ Usage:
|
|||
|
||||
Exit status is zero when every value agrees within tolerance and one
|
||||
otherwise.
|
||||
|
||||
用 ITU-R P.676-12 交叉核对 ApsisRS 的气体衰减。
|
||||
|
||||
读取由 ``cargo run -p apsis-rs --features rf --example dump_gas`` 写出的
|
||||
值,并用 ``itur`` 包逐个重算,后者直接实现 ITU-R 各建议书,然后报告每列
|
||||
的最大差异。
|
||||
|
||||
``itur`` 包是作为 ITU-R 研究计划一部分维护的建议书参考实现,故它是对线表
|
||||
与各公式的独立核对,而非对它们的复述。
|
||||
|
||||
本脚本不需要网络访问。
|
||||
|
||||
用法:
|
||||
|
||||
pixi run python validation/python/atmosphere/check_gas.py [path]
|
||||
|
||||
每个值都在容差内一致时退出状态为零,否则为一。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -31,6 +48,7 @@ import astropy.units as u
|
|||
from itur.models.itu676 import _ITU676_12_
|
||||
|
||||
# The reference atmosphere of the recommendation, matching the Rust side.
|
||||
# 该建议书的参考大气,与 Rust 侧一致。
|
||||
PRESSURE_HPA = 1013.25
|
||||
WATER_VAPOUR_DENSITY = 7.5
|
||||
TEMPERATURE_K = 288.15
|
||||
|
|
@ -42,6 +60,11 @@ TEMPERATURE_K = 288.15
|
|||
# contributions, so a pure relative test is too strict there. The check
|
||||
# passes when either the relative difference is small or the absolute
|
||||
# difference is negligible.
|
||||
#
|
||||
# 容差。
|
||||
#
|
||||
# 比衰减跨越数个数量级,且低频处的水汽项是较大贡献之间的小差值,故纯粹
|
||||
# 的相对判据在那里过严。当相对差异很小或绝对差异可忽略时,检查即通过。
|
||||
RELATIVE_TOLERANCE = 1e-8
|
||||
ABSOLUTE_TOLERANCE = 1e-10
|
||||
|
||||
|
|
@ -49,7 +72,10 @@ DEFAULT_INPUT = Path("validation/data/expected/rust_gas.csv")
|
|||
|
||||
|
||||
def read_rows(path: Path) -> list[dict[str, str]]:
|
||||
"""Reads the CSV rows."""
|
||||
"""Reads the CSV rows.
|
||||
|
||||
读取 CSV 各行。
|
||||
"""
|
||||
with path.open(newline="") as handle:
|
||||
reader = csv.DictReader(handle)
|
||||
return [row for row in reader if row.get("frequency_ghz")]
|
||||
|
|
@ -103,6 +129,8 @@ def main(argv: list[str]) -> int:
|
|||
# The column passes on either measure, so the reported figure is
|
||||
# the relative one and the acceptance test below also allows the
|
||||
# absolute difference to be negligible.
|
||||
# 该列以两种度量之一通过即可,故所报告的数字是相对值,而下面的
|
||||
# 接受判据也允许绝对差异可忽略。
|
||||
if relative > worst[column]:
|
||||
worst[column] = relative
|
||||
worst_at[column] = frequency
|
||||
|
|
|
|||
|
|
@ -14,6 +14,21 @@ Usage:
|
|||
|
||||
Exit status is zero when every value agrees within tolerance and one
|
||||
otherwise.
|
||||
|
||||
用 ITU-R P.676-12 交叉核对 ApsisRS 的精确斜路径。
|
||||
|
||||
读取由 ``cargo run -p apsis-rs --features rf --example dump_gas_exact``
|
||||
写出的值,并用 ``itur`` 包逐个重算,后者直接实现该建议书的精确方法。精确
|
||||
方法在分层标准大气中积分比衰减并追踪折射后的射线,故它是低仰角下宜用的
|
||||
形式。
|
||||
|
||||
本脚本不需要网络访问。
|
||||
|
||||
用法:
|
||||
|
||||
pixi run python validation/python/atmosphere/check_gas_exact.py [path]
|
||||
|
||||
每个值都在容差内一致时退出状态为零,否则为一。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -25,19 +40,24 @@ from pathlib import Path
|
|||
from itur.models.itu676 import _ITU676_12_
|
||||
|
||||
# The reference atmosphere, matching the Rust side.
|
||||
# 参考大气,与 Rust 侧一致。
|
||||
PRESSURE_HPA = 1013.25
|
||||
WATER_VAPOUR_DENSITY = 7.5
|
||||
TEMPERATURE_K = 288.15
|
||||
|
||||
# The implementation reproduces the reference to about nine significant
|
||||
# digits, so this allows for the rounding of the recorded values.
|
||||
# 该实现把参考值复现到约九位有效数字,故此值容许所记录值的舍入。
|
||||
RELATIVE_TOLERANCE = 1e-7
|
||||
|
||||
DEFAULT_INPUT = Path("validation/data/expected/rust_gas_exact.csv")
|
||||
|
||||
|
||||
def read_rows(path: Path) -> list[dict[str, str]]:
|
||||
"""Reads the CSV rows."""
|
||||
"""Reads the CSV rows.
|
||||
|
||||
读取 CSV 各行。
|
||||
"""
|
||||
with path.open(newline="") as handle:
|
||||
reader = csv.DictReader(handle)
|
||||
return [row for row in reader if row.get("frequency_ghz")]
|
||||
|
|
|
|||
|
|
@ -18,6 +18,23 @@ Usage:
|
|||
|
||||
Exit status is zero when every compared value agrees within tolerance and one
|
||||
otherwise.
|
||||
|
||||
用 ITU-R P.839-4 交叉核对 ApsisRS 的雨顶高度。
|
||||
|
||||
读取由 ``cargo run -p apsis-rs --features rf --example dump_p839`` 写出的
|
||||
值,并用 ``itur`` 包逐个重算,后者直接实现该建议书。
|
||||
|
||||
参考实现在其双线性插值器所覆盖区域之外返回非数(NaN),这发生在两极附近
|
||||
与接缝处。那些点被分开报告而不计为失败,因为所列网格确实覆盖它们,而
|
||||
Rust 模型是由该网格插值的。
|
||||
|
||||
本脚本不需要网络访问。
|
||||
|
||||
用法:
|
||||
|
||||
pixi run python validation/python/atmosphere/check_p839.py [path]
|
||||
|
||||
每个被比较的值都在容差内一致时退出状态为零,否则为一。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -33,13 +50,17 @@ from itur.models.itu839 import _ITU839_4_
|
|||
|
||||
# The table is stored in metres, so the interpolation reproduces the
|
||||
# tabulated values to that resolution.
|
||||
# 该表以米存储,故插值把所列值复现到该分辨率。
|
||||
TOLERANCE_KM = 1e-9
|
||||
|
||||
DEFAULT_INPUT = Path("validation/data/expected/rust_p839.csv")
|
||||
|
||||
|
||||
def read_rows(path: Path) -> list[dict[str, str]]:
|
||||
"""Reads the CSV rows."""
|
||||
"""Reads the CSV rows.
|
||||
|
||||
读取 CSV 各行。
|
||||
"""
|
||||
with path.open(newline="") as handle:
|
||||
reader = csv.DictReader(handle)
|
||||
return [row for row in reader if row.get("latitude_deg")]
|
||||
|
|
@ -79,6 +100,7 @@ def main(argv: list[str]) -> int:
|
|||
)
|
||||
if math.isnan(reference):
|
||||
# The reference interpolator does not cover this point.
|
||||
# 参考插值器不覆盖该点。
|
||||
outside += 1
|
||||
continue
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,19 @@ Usage:
|
|||
|
||||
Exit status is zero when every value agrees within tolerance and one
|
||||
otherwise.
|
||||
|
||||
用 ITU-R P.838-3 交叉核对 ApsisRS 的降雨系数。
|
||||
|
||||
读取由 ``cargo run -p apsis-rs --features rf --example dump_rain`` 写出的
|
||||
值,并用 ``itur`` 包逐个重算,后者直接实现该建议书。
|
||||
|
||||
本脚本不需要网络访问。
|
||||
|
||||
用法:
|
||||
|
||||
pixi run python validation/python/atmosphere/check_rain.py [path]
|
||||
|
||||
每个值都在容差内一致时退出状态为零,否则为一。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -26,13 +39,18 @@ from itur.models.itu838 import _ITU838_3_
|
|||
# digits. The Rust output is written with twelve decimal places, which limits
|
||||
# the comparison for the smallest coefficients, so the tolerance is set to
|
||||
# one part in ten to the eighth.
|
||||
# 该实现把参考值复现到约十二位有效数字。Rust 输出以十二位小数写出,这限制
|
||||
# 了最小系数的比较,故容差设为十的八次方分之一。
|
||||
RELATIVE_TOLERANCE = 1e-8
|
||||
|
||||
DEFAULT_INPUT = Path("validation/data/expected/rust_rain.csv")
|
||||
|
||||
|
||||
def read_rows(path: Path) -> list[dict[str, str]]:
|
||||
"""Reads the CSV rows."""
|
||||
"""Reads the CSV rows.
|
||||
|
||||
读取 CSV 各行。
|
||||
"""
|
||||
with path.open(newline="") as handle:
|
||||
reader = csv.DictReader(handle)
|
||||
return [row for row in reader if row.get("frequency_ghz")]
|
||||
|
|
|
|||
|
|
@ -14,6 +14,20 @@ Usage:
|
|||
|
||||
Exit status is zero when every value agrees within tolerance and one
|
||||
otherwise.
|
||||
|
||||
用 ITU-R P.618-13 交叉核对 ApsisRS 的降雨路径。
|
||||
|
||||
读取由 ``cargo run -p apsis-rs --features rf --example dump_rain_path``
|
||||
写出的值,并从该建议书的各式逐个重算。比衰减系数来自 ITU-R P.838-3 的
|
||||
``itur`` 实现,路径几何与两个缩减因子则抄自建议书 P.618-13。
|
||||
|
||||
本脚本不需要网络访问。
|
||||
|
||||
用法:
|
||||
|
||||
pixi run python validation/python/atmosphere/check_rain_path.py [path]
|
||||
|
||||
每个值都在容差内一致时退出状态为零,否则为一。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -26,28 +40,37 @@ from pathlib import Path
|
|||
from itur.models.itu838 import _ITU838_3_
|
||||
|
||||
# Tolerance on the path length and the attenuation, in their own units.
|
||||
# 路径长度与衰减的容差,各以其自身单位计。
|
||||
LENGTH_TOLERANCE_KM = 1e-9
|
||||
ATTENUATION_TOLERANCE_DB = 1e-9
|
||||
|
||||
# The effective radius of the Earth used by the path geometry, in
|
||||
# kilometers, from Recommendation P.618-13.
|
||||
# 路径几何所用的地球有效半径,单位为千米,取自建议书 P.618-13。
|
||||
EFFECTIVE_EARTH_RADIUS_KM = 8500.0
|
||||
|
||||
# The rain layer height used by the Rust example, in kilometers.
|
||||
# Rust 示例所用的雨层高度,单位为千米。
|
||||
RAIN_HEIGHT_KM = 3.0
|
||||
|
||||
DEFAULT_INPUT = Path("validation/data/expected/rust_rain_path.csv")
|
||||
|
||||
|
||||
def read_rows(path: Path) -> list[dict[str, str]]:
|
||||
"""Reads the CSV rows."""
|
||||
"""Reads the CSV rows.
|
||||
|
||||
读取 CSV 各行。
|
||||
"""
|
||||
with path.open(newline="") as handle:
|
||||
reader = csv.DictReader(handle)
|
||||
return [row for row in reader if row.get("rain_rate_mm_per_h")]
|
||||
|
||||
|
||||
def reference(row: dict[str, str], coefficients) -> tuple[float, float]:
|
||||
"""Recomputes the path length and the attenuation."""
|
||||
"""Recomputes the path length and the attenuation.
|
||||
|
||||
重算路径长度与衰减。
|
||||
"""
|
||||
rain_rate = float(row["rain_rate_mm_per_h"])
|
||||
frequency = float(row["frequency_ghz"])
|
||||
elevation = float(row["elevation_deg"])
|
||||
|
|
@ -60,6 +83,7 @@ def reference(row: dict[str, str], coefficients) -> tuple[float, float]:
|
|||
specific = float(k) * rain_rate ** float(alpha)
|
||||
|
||||
# Equation 2: the slant path length.
|
||||
# 式 2:斜路径长度。
|
||||
height_difference = RAIN_HEIGHT_KM - station_height
|
||||
sine = math.sin(math.radians(elevation))
|
||||
slant_length = 2.0 * height_difference / (
|
||||
|
|
@ -68,9 +92,11 @@ def reference(row: dict[str, str], coefficients) -> tuple[float, float]:
|
|||
)
|
||||
|
||||
# Equation 3: the horizontal projection.
|
||||
# 式 3:水平投影。
|
||||
horizontal_length = abs(slant_length * math.cos(math.radians(elevation)))
|
||||
|
||||
# Equation 6: the horizontal reduction factor.
|
||||
# 式 6:水平缩减因子。
|
||||
horizontal_reduction = 1.0 / (
|
||||
1.0
|
||||
+ 0.78 * math.sqrt(horizontal_length * specific / frequency)
|
||||
|
|
@ -78,11 +104,13 @@ def reference(row: dict[str, str], coefficients) -> tuple[float, float]:
|
|||
)
|
||||
|
||||
# Equation 7: the angle at which the path meets the rain layer.
|
||||
# 式 7:路径与雨层相交的角度。
|
||||
eta = math.degrees(
|
||||
math.atan2(height_difference, horizontal_length * horizontal_reduction)
|
||||
)
|
||||
|
||||
# Equation 8: the longer of the two path estimates.
|
||||
# 式 8:两个路径估计中较长者。
|
||||
if eta > elevation:
|
||||
path_length = (
|
||||
horizontal_length * horizontal_reduction / math.cos(math.radians(elevation))
|
||||
|
|
@ -92,6 +120,7 @@ def reference(row: dict[str, str], coefficients) -> tuple[float, float]:
|
|||
|
||||
# Equation 9: the vertical adjustment factor. The exponential takes the
|
||||
# elevation in degrees.
|
||||
# 式 9:垂直调整因子。指数以度接收仰角。
|
||||
xi = 36.0 - abs(latitude) if abs(latitude) < 36.0 else 0.0
|
||||
vertical_adjustment = 1.0 / (
|
||||
1.0
|
||||
|
|
|
|||
|
|
@ -14,6 +14,20 @@ Usage:
|
|||
|
||||
Exit status is zero when every position agrees within the tolerance and one
|
||||
otherwise.
|
||||
|
||||
交叉核对 ApsisRS 的大地坐标到地固坐标转换。
|
||||
|
||||
读取由 ``cargo run -p apsis-rs --features physics --example dump_geodesy``
|
||||
写出的网格,并用 astropy 逐个重算位置,然后报告最大差异。
|
||||
|
||||
本脚本不需要网络访问。它从命令行给出的文件路径读取输入,缺省为标准位置,
|
||||
并把报告写到标准输出。
|
||||
|
||||
用法:
|
||||
|
||||
pixi run python validation/python/coord/check_geodetic.py [path]
|
||||
|
||||
每个位置都在容差内一致时退出状态为零,否则为一。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -27,13 +41,17 @@ from astropy.coordinates import EarthLocation
|
|||
|
||||
# Position tolerance in meters. Astropy evaluates the same closed form, so
|
||||
# the only difference should be floating point rounding.
|
||||
# 位置容差,单位为米。Astropy 求的是同一个闭式,故唯一差异应是浮点舍入。
|
||||
TOLERANCE_M = 1e-6
|
||||
|
||||
DEFAULT_INPUT = Path("validation/data/expected/rust_geodetic.csv")
|
||||
|
||||
|
||||
def rust_positions(path: Path) -> list[tuple[float, float, float, tuple[float, float, float]]]:
|
||||
"""Reads the Rust output rows."""
|
||||
"""Reads the Rust output rows.
|
||||
|
||||
读取 Rust 输出各行。
|
||||
"""
|
||||
rows = []
|
||||
with path.open(newline="") as handle:
|
||||
reader = csv.DictReader(handle)
|
||||
|
|
@ -50,7 +68,10 @@ def rust_positions(path: Path) -> list[tuple[float, float, float, tuple[float, f
|
|||
|
||||
|
||||
def astropy_position(latitude_deg: float, longitude_deg: float, height_m: float):
|
||||
"""Recomputes a position with astropy."""
|
||||
"""Recomputes a position with astropy.
|
||||
|
||||
用 astropy 重算一个位置。
|
||||
"""
|
||||
location = EarthLocation.from_geodetic(
|
||||
lon=longitude_deg * u.deg,
|
||||
lat=latitude_deg * u.deg,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,25 @@ Usage:
|
|||
|
||||
Exit status is zero when every position agrees within tolerance and one
|
||||
otherwise.
|
||||
|
||||
交叉核对 ApsisRS 的 TEME 到地固坐标变换。
|
||||
|
||||
读取由 ``cargo run -p apsis-rs --features physics --example dump_teme``
|
||||
写出的位置,并用 ERFA 逐个重算,施加相同的格林尼治平恒星时与相同的零极移,
|
||||
使该检查比较的是实现而非输入的选择。
|
||||
|
||||
参考值是 1982 年的恒星时与 IAU SOFA 库的极移合成,由 ERFA 实现。注意
|
||||
astropy 与该变换只相符到数百米,因为 astropy 施加实测极移与实测
|
||||
UT1 - UTC,而本检查对两者都用零。该相符情况为提供信息而报告,但并非接受
|
||||
判据。
|
||||
|
||||
本脚本不需要网络访问。
|
||||
|
||||
用法:
|
||||
|
||||
pixi run python validation/python/coord/check_teme.py [path]
|
||||
|
||||
每个位置都在容差内一致时退出状态为零,否则为一。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -33,33 +52,45 @@ import erfa
|
|||
# Tolerance in kilometers. The transformation is a product of two rotations
|
||||
# evaluated in the same order, so the agreement is at the level of the last
|
||||
# bits of a double.
|
||||
# 容差,单位为千米。该变换是两个按相同次序求值的转动之积,故相符程度在
|
||||
# 双精度最后几位的水平上。
|
||||
TOLERANCE_KM = 1e-9
|
||||
|
||||
# The two-part Julian date used by the Rust example for the whole days.
|
||||
# Rust 示例为整数天数所用的两部分儒略日。
|
||||
DAY_PART = 2400000.5
|
||||
|
||||
DEFAULT_INPUT = Path("validation/data/expected/rust_teme.csv")
|
||||
|
||||
|
||||
def read_rows(path: Path) -> list[dict[str, str]]:
|
||||
"""Reads the CSV rows."""
|
||||
"""Reads the CSV rows.
|
||||
|
||||
读取 CSV 各行。
|
||||
"""
|
||||
with path.open(newline="") as handle:
|
||||
reader = csv.DictReader(handle)
|
||||
return [row for row in reader if row.get("mjd")]
|
||||
|
||||
|
||||
def reference_position(mjd: float, position: list[float]) -> list[float]:
|
||||
"""Recomputes the Earth-fixed position with ERFA."""
|
||||
"""Recomputes the Earth-fixed position with ERFA.
|
||||
|
||||
用 ERFA 重算地固位置。
|
||||
"""
|
||||
# The sidereal time, from the two-part Julian date.
|
||||
# 恒星时,由两部分儒略日得出。
|
||||
sidereal = erfa.gmst82(DAY_PART, mjd)
|
||||
# The rotation of the frame about its third axis, composed as the
|
||||
# reference `rz` does, and the identity polar motion.
|
||||
# 坐标系绕其第三轴的转动,按参考 `rz` 的方式合成,以及单位极移。
|
||||
rotation = erfa.rz(sidereal, erfa.ir())
|
||||
polar = erfa.pom00(0.0, 0.0, 0.0)
|
||||
matrix = erfa.rxr(polar, rotation)
|
||||
|
||||
# The rotation maps the frame, so the position is the matrix applied to
|
||||
# the components.
|
||||
# 该转动映射坐标系,故位置是矩阵施加于各分量。
|
||||
return [
|
||||
sum(matrix[i][j] * position[j] for j in range(3)) for i in range(3)
|
||||
]
|
||||
|
|
@ -82,6 +113,7 @@ def main(argv: list[str]) -> int:
|
|||
return 2
|
||||
|
||||
# The TEME inputs used by the example, in the same order.
|
||||
# 示例所用的 TEME 输入,次序相同。
|
||||
inputs = [
|
||||
[7000.0, 1000.0, 2000.0],
|
||||
[7000.0, -3000.0, 1000.0],
|
||||
|
|
|
|||
Loading…
Reference in New Issue