From b3d7762d0efcaf837254268ed9af502b34211702 Mon Sep 17 00:00:00 2001 From: ApsisRS contributors Date: Fri, 2 Oct 2026 01:41:29 +0800 Subject: [PATCH] docs: bilingual comments for the atmosphere and coordinate validation scripts --- scripts/__pycache__/bilingual.cpython-314.pyc | Bin 1966 -> 2584 bytes validation/python/atmosphere/check_gas.py | 30 ++++++++++++++- .../python/atmosphere/check_gas_exact.py | 22 ++++++++++- validation/python/atmosphere/check_p839.py | 24 +++++++++++- validation/python/atmosphere/check_rain.py | 20 +++++++++- .../python/atmosphere/check_rain_path.py | 33 +++++++++++++++- validation/python/coord/check_geodetic.py | 25 +++++++++++- validation/python/coord/check_teme.py | 36 +++++++++++++++++- 8 files changed, 180 insertions(+), 10 deletions(-) diff --git a/scripts/__pycache__/bilingual.cpython-314.pyc b/scripts/__pycache__/bilingual.cpython-314.pyc index 31c25ce5fac2c173d6fde1dd14b7767c0cda0c5a..ac051003ca4c4a5d4390d3c1676ae3bbb5ae158b 100644 GIT binary patch delta 684 zcmX|8J8#oa6n09|N)`b5ILk zzWcP55TS2jNpWUlEX_f!JEjD7eI;oub4hhb4d7T~{VtB$o}#X#tXutzXG_d}WsUYq zIutp8Gt*(J^FOAH=?kp%3C1ff9dqc~CAP?~Z4LX!RK!lhJDQ`a@^kMeOZY4SIGmTU zV8NGD$D9W2)Bz_s%(Wb|OJYR26D9kWB!B>q$o+w)m&~s1lruMI*fE>L<7PVUMJCX? zw$sqFu%d%9^$Mq4PEp2glO+~1sNZ4I0(n9_SQrNZh`}6(PnF3jpYZb`J)V*!&LA|Z zgEdCA1$h;XG&HL)$fNNCG|Q8-BzAM&PnA?SJujD17})2<@)~@wX7a*(Na`cB$t- Dqx+m= delta 138 zcmbOsvW}lmn~#@^0SG2{?aLBjoyZr$oLJ4YF}0RyGb<}Eqn0>Os#qCFG%$Q;;ANEl z2qGr1d;n1wWQ`!SxeriyvkhA_BU6>b 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 diff --git a/validation/python/atmosphere/check_gas_exact.py b/validation/python/atmosphere/check_gas_exact.py index c7e0440..44e76dd 100644 --- a/validation/python/atmosphere/check_gas_exact.py +++ b/validation/python/atmosphere/check_gas_exact.py @@ -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")] diff --git a/validation/python/atmosphere/check_p839.py b/validation/python/atmosphere/check_p839.py index d1c2f5f..3b6b56b 100644 --- a/validation/python/atmosphere/check_p839.py +++ b/validation/python/atmosphere/check_p839.py @@ -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 diff --git a/validation/python/atmosphere/check_rain.py b/validation/python/atmosphere/check_rain.py index f554ce4..496037b 100644 --- a/validation/python/atmosphere/check_rain.py +++ b/validation/python/atmosphere/check_rain.py @@ -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")] diff --git a/validation/python/atmosphere/check_rain_path.py b/validation/python/atmosphere/check_rain_path.py index 30954e5..248ba6b 100644 --- a/validation/python/atmosphere/check_rain_path.py +++ b/validation/python/atmosphere/check_rain_path.py @@ -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 diff --git a/validation/python/coord/check_geodetic.py b/validation/python/coord/check_geodetic.py index 1064be0..a01f044 100644 --- a/validation/python/coord/check_geodetic.py +++ b/validation/python/coord/check_geodetic.py @@ -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, diff --git a/validation/python/coord/check_teme.py b/validation/python/coord/check_teme.py index c202400..36d87c6 100644 --- a/validation/python/coord/check_teme.py +++ b/validation/python/coord/check_teme.py @@ -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],