Validation Methodology & Reference Implementation
HAPIS Link Budget API — four-tier validation pyramid: code verification, ITU-R / 3GPP reference cases, a mandatory cross-tool oracle, and continuous integration.
What this document publishes. For the ITU-R P.676-13 family of cases — the surface area most exposed to catalogue-side error (see § 11) — the itur reference Python library runs as a mandatory per-commit pytest oracle: every commit asserts |HAPIS − itur| ≤ per-case tolerance, with tight tolerances (±0.05 dB typical, ±2.0 dB at the 60 GHz O₂ complex peak, where independent spec-compliant implementations routinely differ). For the other atmospheric standards (P.525, P.838, P.840, P.2108), the same itur cross-check is run during development. The document includes a candid case study (§ 11) of how the methodology caught and corrected up to 29 dB of catalogue error during audit. All four tiers — code verification, reference cases, the P.676 oracle, and continuous integration — are operational.
Executive Summary
This document describes how the HAPIS Link Budget API is verified and validated. It is written for an RF engineer deciding whether HAPIS outputs can be trusted as the basis for an engineering decision, and comparing HAPIS against STK, MATLAB's Satellite Communications Toolbox, open-source Python libraries, or a spreadsheet.
The methodology is a four-tier validation pyramid, adapted from ANSI/ASME V&V 20, NASA-STD-7009A, and ITU-R P.311. Every model is verified against its source equation and validated against the worked examples in that standard. For ITU-R P.676-13 (atmospheric gases) — the standard most exposed to catalogue-side error — HAPIS is cross-checked at every commit against the itur reference library by a parametrised pytest oracle; for P.525, P.838, P.840, and P.2108 the same cross-check is run during development.
Measured accuracy for gaseous attenuation is ±0.05 dB against the itur oracle on six of eight P.676-13 cases, and ±2.0 dB at the 60 GHz O₂ complex peak, where two spec-compliant implementations routinely differ. Accuracy for free-space path loss, rain, cloud, and clutter is summarised in § 6.2; limitations and parameter envelopes are in § 7.
The Cat 4 reclassification of 2026-05-26 — where the methodology caught and corrected up to 29 dB of catalogue error that had been passing under loose tolerances — is documented in full in § 11. It is included because the only honest test of a validation methodology is what it does when something is wrong.
1. Introduction
1.1 Purpose
The purpose of this document is to provide a transparent, auditable description of how HAPIS validates the correctness of its RF propagation and link budget calculations. It serves three audiences:
- Engineering reviewers evaluating HAPIS for adoption in a project requiring traceable, defensible RF analysis.
- Regulatory and certification bodies assessing whether HAPIS-derived analyses meet documentation standards for spectrum and aviation filings.
- Internal developers maintaining HAPIS, who require a stable specification of validation obligations across versions.
1.2 Scope
This methodology applies to all RF computation modules of HAPIS, specifically:
- Free-space path loss (FSPL)
- Atmospheric gaseous absorption (oxygen and water vapour)
- Rain attenuation (specific and statistical)
- Cloud and fog attenuation
- Clutter loss for terrestrial-component links
- Geometry (slant range, elevation, ground footprint) for stratospheric platforms
- 3GPP NTN reference scenario calculations (link budget, Doppler, propagation delay)
Out of scope for this version: ionospheric scintillation models, multipath / fading time-series synthesis, and end-to-end protocol-level simulation (modelled separately in the NTN Simulator product, validated under its own document).
1.3 Terminology
| Term | Definition |
|---|---|
| Verification | “Are we building the model right?” — the implementation correctly reproduces the specification. |
| Validation | “Are we building the right model?” — the model adequately represents physical reality. |
| Tolerance | The maximum acceptable absolute or relative deviation between a HAPIS output and its reference value. |
| Reference case | A specific input/output pair documented in an authoritative source (ITU-R annex, 3GPP TR, textbook, peer-reviewed paper). |
| Code-to-code | Cross-validation between HAPIS and an independent software implementation of the same standard. |
| Oracle test | A parametrised pytest that asserts HAPIS output agrees, within an implementation-difference tolerance, with an independent reference implementation. Different from Tier 2 (which compares to a published spot value) and from Tier 3 informational matrices (which only report deltas). An oracle test fails the build on disagreement. |
| Category 1–5 | Failure classification: (1) engine bug, (2) tolerance too tight, (3) defensible alternative, (4) catalogue error, (5) standard not yet implemented. |
2. Validation Philosophy
HAPIS is built on four commitments that shape the validation methodology. The first three are foundational principles inherited from V&V literature; § 2.4 (errors as first-class artefacts) was added in light of the pre-publication Cat 4 audit (see § 11).
2.1 Traceability over opacity
Every numerical output of the API is computable from a published equation in a named, versioned standard. The mapping between API response fields and source standards is documented and machine-readable. No proprietary corrections or unpublished approximations are applied to ITU-R or 3GPP models without explicit disclosure. Design choices are judged by fidelity to the source standard and by whether they survive independent review.
2.2 Transparency over marketing claims
Where HAPIS deviates from a reference (e.g., a numerical implementation choice for an integral, a piecewise-linear interpolation between tabulated values, an out-of-range extrapolation), the deviation is documented in the source code, in this whitepaper, and in the validation manifest. Honest reporting of edge-case deviations is treated as more valuable than uniform “passing” claims.
2.3 Reproducibility over assertion
Every numerical claim in this document is independently reproducible from the artefacts published with the corresponding API release. See § 8.
2.4 Errors are first-class artefacts
When HAPIS gets something wrong — a transcription error in a coefficient table, a misclassified test case, an over-generous tolerance — the recovery story becomes part of the record. We do not silently fix incidents. The “Cat 4 reclassification” case study in § 11 is the canonical example: a multi-dB catalogue error that was hidden by overly wide tolerances was caught not because someone noticed but because the methodology forced the comparison. We then documented (a) what was suspected, (b) what turned out to be true, (c) what changed, (d) what the methodology should do differently next time. A whitepaper that reports only successes is a marketing document; a whitepaper that reports incidents honestly is an audit artefact.
3. The HAPIS Validation Pyramid
The validation methodology is organized into four implemented tiers, each addressing a different class of risk. Lower tiers are necessary preconditions for higher tiers: a model that fails Tier 1 cannot be trusted in Tier 3, regardless of how favourable the comparison looks.
+------------------------------------+
| TIER 4 - Continuous Validation | Implemented
+------------------------------------+
| TIER 3 - Code-to-Code (MANDATORY) | Implemented — itur oracle pytest
+------------------------------------+
| TIER 2 - Reference Cases | Implemented
+------------------------------------+
| TIER 1 - Code Verification | Implemented
+------------------------------------+
3.1 Tier 1 — Code Verification Implemented
Question answered: Does the implementation faithfully reproduce the mathematical specification of the source standard?
Methods applied today:
- Formula-by-formula unit tests. Each equation in each source standard (ITU-R P.525 Eq. 1, P.676 Annex 1 slant-path integration, P.838-3 specific-attenuation coefficients, etc.) is implemented in its own pure function and tested independently with hand-computed input/output pairs derived directly from the equation.
- Edge-case tests. Boundary conditions: zero distance, zero frequency, 90° elevation (zenith), 0° elevation (horizontal), maximum specified rain rate, dry atmosphere, fully saturated atmosphere.
- Numerical stability tests. Inputs that approach singularities (e.g., 0° elevation in slant-path integrals) are tested for graceful degradation and explicit error messaging rather than silent NaN propagation.
- Branch coverage. A 70 % coverage floor for the
apiandhapis_corepackages is enforced in CI viapytest --cov-fail-under=70.
Current deliverable: 212 product unit tests in the engine repository, with 100 % pass rate required for any release. The 8 oracle tests added under § 3.3 are included in this count.
3.2 Tier 2 — Reference Case Validation Implemented
Question answered: Does HAPIS reproduce the worked examples published in the source standards themselves?
ITU-R and 3GPP recommendations frequently include annex tables of “test results” or “example calculations” intended exactly for this purpose. These are the closest thing to ground truth that exists for these models.
Methods applied:
- The reference-case catalogue lives in the engine repository as
tests/validation/reference_cases.yaml(machine-readable; redacted copy available under NDA — see § 8). - The catalogue is consumed by (a) the audit script
audit/audit_hapis_core.pyfor direct library-level verification of every case, and (b) the parametrised pytest oracletests/test_p676_itur_oracle.pyfor the P.676 family of cases, which runs on every CI commit. - Aggregate statistics (bias, RMSE, max deviation, percentage within tolerance) are computed per standard and recorded in the audit gap report.
- Tolerances are defined per standard (see § 6.2).
Provenance of expected values. In the pre-publication draft the gas-attenuation cases used Fig. 4 visual reads from the P.676-13 PDF; during the pre-publication audit they were reclassified Cat 4 and replaced with values computed by the itur reference library (an Annex-1-correct analytic source) — see § 11. Catalogue provenance is documented in the source: field of each case and in the notes: blocks for Cat-4-corrected cases.
Reference sources currently covered:
| Standard | Reference cases sourced from | Status |
|---|---|---|
| ITU-R P.525-4 | FSPL worked examples at multiple frequencies / slant ranges | Implemented |
| ITU-R P.676-13 | itur oracle values at 5 zenith + 3 slant frequencies | Implemented |
| ITU-R P.835-5 | MAGRA atmosphere profile (T, P, ρ with § 1.3 floor) | Implemented in P.676 path |
| ITU-R P.838-3 | Coefficient tables for specific rain attenuation | Implemented |
| ITU-R P.840-8 | Cloud / fog specific attenuation; cloud-type presets | Implemented |
| ITU-R P.2108-1 | § 3.3 Eq. 7 statistical clutter at zenith and slant | Implemented |
| 3GPP TR 38.811 v15.4.0 | HAPS scenario reference geometry & KPIs | Implemented |
| 3GPP TR 38.821 v16.2.0 | NTN solution reference budgets (Set-1 GEO / LEO-600 / LEO-1200) | Implemented |
The full per-case input/output table with deviation statistics is produced locally by audit/audit_hapis_core.py and recorded in the audit gap report (NDA-only — see § 8).
3.3 Tier 3 — Code-to-Code Cross-Validation Implemented — mandatory itur oracle
Question answered: Do independent implementations of the same standards converge on the same numerical answer? And does this check run automatically on every change, or only as a quarterly review artefact?
Design. In v1.0, itur is a per-commit oracle for ITU-R P.676-13: the pytest file tests/test_p676_itur_oracle.py iterates every GAS-* case in the catalogue, runs both HAPIS and itur with identical inputs, and asserts |HAPIS − itur| ≤ per-case tolerance. CI fails on any disagreement above the per-case tolerance (0.05 dB typical; 2.0 dB at the 60 GHz O₂ peak; 0.25 dB at 5° elevation). This is the strongest automatic third-party witness HAPIS publishes for P.676. An earlier internal draft treated itur as a per-release informational comparison only; the pre-publication Cat 4 audit (see § 11) demonstrated that informational comparison is insufficient — a finding only blocks action if it blocks CI — so v1.0 ships with the oracle promoted to mandatory pytest for the standard that was reclassified (P.676).
Other standards (P.525, P.838, P.840, P.2108): Tier 3 cross-validation against itur is performed during development. Cat 1 / Cat 4 discrepancies found in pre-publication audit have been corrected, and HAPIS-itur agreement on those was verified at the same time.
Boundary rule (preserves originality of HAPIS). itur is declared in requirements-dev.txt only. The HAPIS engine package hapis_core/ does not import itur. The oracle test is the only file in the repository that imports both. The engine's line-by-line implementation, its spectroscopic tables, its atmosphere profile, and its slant-integration scheme are independent — they share only the underlying spec (ITU-R P.676-13 and P.835-5). This is intentional: a regression that drifted HAPIS toward itur's implementation quirks (which itself is not the spec) would be invisible to a test that imported itur into production code. Independence-of-implementations is what makes the oracle meaningful.
Reference tools:
| Tool | Category | Role | Status |
|---|---|---|---|
itur Python library | Open source, peer-developed (I. del Portillo, MIT) | MANDATORY per-commit oracle (pytest) for P.676; cross-check for the other standards | Implemented |
| Hand calculation in Mathematica / SciPy | Symbolic | Used for closed-form verifiable formulas (FSPL, simple geometry) | Implemented |
Why mandatory and not just informational? An earlier internal design treated the cross-tool matrix as a per-release informational artefact; a disagreement was a finding but did not block CI. In practice, when an over-generous Tier 2 tolerance was masking a Tier 2 catalogue error, the disagreement against itur was the only signal that anything was wrong, and it was discovered only when someone ran the audit by hand. Mandatory oracle pytest turns that disagreement into a build failure, which is what catches it on the first commit instead of months later. § 11.4 explains the v1.0 design.
3.4 Tier 4 — Continuous Validation Implemented
Question answered: Does HAPIS remain valid across releases, dependency updates, and platform changes?
A model that was correct at release N and silently regressed by release N+3 is worse than no model. HAPIS runs a GitHub Actions pipeline on every push and pull request to the main branch with the following jobs:
| Job | What it does |
|---|---|
| Lint | ruff check api hapis_core tests — style and obvious-error linting on all production and test code. |
| Test + coverage | pytest tests/ -v --cov=api --cov=hapis_core --cov-fail-under=70 — full test suite (unit + reference-case validation + itur oracle) with coverage gate. The itur oracle pytest (tests/test_p676_itur_oracle.py) is collected by this job; failure of any oracle case blocks merge. |
| Security scan | pip-audit against requirements.txt for known CVEs, plus bandit static analysis on api and hapis_core. |
| Build + SBOM + sign image | On main: builds the production Docker image, publishes to GHCR with SHA / branch / latest tags, generates an SPDX SBOM via Anchore, and signs the image keyless with Sigstore cosign (OIDC). SBOM is attested to the image digest. |
Any deviation in test results, coverage drop below 70 %, new high-severity CVE, or oracle case exceeding tolerance blocks the build.
4. Per-Standard Validation Plan
This section details how each standard is validated under the framework above. For the standards with the most implementation depth (P.676 atmospheric, P.2108 clutter), the discussion goes further into the implementation choices and how each is justified.
4.1 ITU-R P.525-4 — Free-Space Path Loss
Implemented equation (P.525-4 Eq. 1):
L_bf = 20 log10(4πd/λ) = 32.44 + 20 log10(f_MHz) + 20 log10(d_km)
# ITU-R P.525-4 free-space basic transmission loss d_km = altitude_km / sin(elevation) # slant range FSPL_dB = 92.45 + 20*log10(f_GHz) + 20*log10(d_km) # the 92.45 constant is the f-in-GHz, d-in-km form of P.525-4 Eq. 1
Tolerance: ±0.01 dB (numerical round-off). This is closed-form arithmetic; any deviation beyond floating-point round-off indicates a bug.
Validation status: Tier 1 unit tests at 1, 14, 28, 40, and 100 GHz across multiple slant ranges; Tier 2 P.525 Annex 1 worked-example reproduction; Tier 3 cross-check against itur.models.itu525 during development (FSPL is closed-form arithmetic, agreeing to ≤ 0.01 dB).
4.2 ITU-R P.676-13 — Atmospheric Gaseous Absorption
The P.676 implementation is tight and oracle-anchored. An earlier internal draft described it in lower-resolution terms (“P.676 oxygen and water-vapour line absorption plus slant-path integration with an exponential atmosphere”); the implementation behind that description was correct, but the documented tolerance posture and atmosphere profile were imprecise. Both are corrected here: expected values are anchored to itur, and the atmosphere profile is documented against P.835-5.
Implemented:
- Annex 1 line-by-line (Eqs. 1–9, Tables 1 & 2 verbatim): 44 O₂ + 35 H₂O spectroscopic lines including line strength, Van Vleck-Weisskopf line shape (Eq. 5 with δ-correction for O₂ line mixing), Zeeman + Doppler width corrections (Eqs. 6a/6b), oxygen interference δ (Eq. 7), and dry continuum (Eqs. 8–9).
- P.835-5 § 1.1 MAGRA atmosphere for T(h) and P(h): US Standard 1976 piecewise lapse with breakpoints at 11/20/32/47/51/71 km. Hydrostatic pressure analytic per layer.
- P.835-5 § 1.3 H₂O mixing-ratio floor for ρ(h): the simple exponential
ρ₀·exp(-h/2 km)from § 1.2, with the volume mixing ratio held constant at 2×10⁻⁶ above the altitude where it first crosses that value. The floor matters above ~21 km for ρ₀ = 7.5 g/m³. - P.676-13 Annex 2 § 2.2 reference layer scheme for slant integration: δᵢ = 0.0001·exp((i−1)/100) km, default N = 922 layers. Surface layer is 0.1 m thick; top layer ~1 km; cumulative top altitude ~100 km.
- Straight-line slant geometry per layer. No Snell-law / Bouguer ray-bending; accurate for elevation ≥ 5° and accordingly flagged with a
low_elevation_warningbelow that.
# ITU-R P.676-13 gaseous specific attenuation
for line in oxygen_lines + water_vapour_lines:
S = line_strength(line, T, p, e) # temperature/pressure dependent
F = line_shape(f, line.f0, width(line, p, e))
sum += S * F
gamma = 0.182 * f * (sum + continuum(f, p, e)) # dB/km
A = integrate(gamma, along the slant path) # total dB
# exact line tables, widths, and continuum per ITU-R P.676-13
Tolerance (per case, vs the itur Annex-1 reference):
| Subset | Tolerance | Justification |
|---|---|---|
| Zenith, 14 / 22 / 50 / 94 GHz; slant 20/10°, 30/30° | ±0.05 dB | HAPIS-itur agreement is < 0.02 dB on every case; the tolerance is 2.5–50× safety headroom. |
| Zenith, 60 GHz O₂ complex peak (GAS-Z4) | ±2.0 dB | Genuinely sensitive frequency: HAPIS-itur disagree by 1.4 dB despite both being Annex-1-correct. Any two spec-compliant independent implementations routinely differ by 1–2 dB here. |
| Slant, 5° elevation (GAS-S3) | ±0.3 dB | Low-elevation geometry: HAPIS-itur disagree by 0.175 dB because itur includes Snell-law refraction and HAPIS does not. Tolerance covers the geometry-formalism difference. |
Implementation fidelity, and where it differs. The line-by-line implementation follows P.676-13 Annex 1, with Tables 1 and 2 transcribed from the recommendation, and the atmosphere follows P.835-5 § 1.1 (T, P) and § 1.3 (ρ floor). On six of eight catalogue cases HAPIS agrees with the itur reference to within 0.02 dB — bit-perfect modulo floating-point rounding. The 1.4 dB residual at the 60 GHz O₂ peak sits inside the 150–200 dB envelope of independent spec-compliant implementations and reflects choices the standard leaves to the implementer: higher-order line coupling, layer count, and refraction handling. Across the four HAPIS bands — S (2 GHz), Ka (28 GHz), Q (38 GHz), V (47 GHz), none near the 60 GHz peak — HAPIS and itur agree to ≤ 0.05 dB; the ±2 dB tolerance at 60 GHz is a calibration sentinel for the engine, not an accuracy claim for a served band. The five Annex-1 choices that drive the residual (Snell vs straight-line geometry; layer count; H₂O mixing-ratio floor; line-mixing depth; continuum coefficient set) are listed in each case's notes: block, so a reviewer comparing HAPIS to STK or a SciPy implementation can predict where the answers will differ before running the comparison.
Validation status: Tier 1 unit tests per equation; Tier 2 against itur oracle values at 14, 22.235, 50, 60, 94 GHz zenith + 20/10°, 30/30°, 40/5° slant; Tier 3 mandatory pytest oracle vs itur.models.itu676.gaseous_attenuation_slant_path on every commit (tests/test_p676_itur_oracle.py).
Implementation transparency. The slant-path attenuation factors exactly as A_gas(f, el) = A_zenith(f) / sin(el): the 922-layer line-by-line zenith integral is elevation-independent and is therefore computed once per (frequency, surface profile) and cached; the P.838 rain and P.840 cloud coefficients are likewise cached, and a coverage grid evaluates the whole link budget as a single vectorised pass rather than one call per cell. This is a performance factoring only — the result is bit-identical to the per-cell scalar reference, pinned by tests/test_p676_zenith_cache.py and tests/test_grid_vectorized_equiv.py (each asserts equality to the un-cached / per-cell computation). No tolerance, coefficient, or model behaviour changes; it is disclosed here per § 2.2.
4.3 ITU-R P.838-3 — Rain Attenuation
Implemented: P.838-3 specific attenuation γR = k Rα via the closed-form curve-fit functions of Eqs. 2–3 with Tables 1–4 coefficients (replaces an early-draft interpolated-table lookup; see the historical note below). Polarization-mixing per Eqs. 4–5.
# ITU-R P.838-3 rain specific attenuation kH, aH, kV, aV = coefficients(f_GHz) # frequency-interpolated tables k, a = combine_polarization(kH, aH, kV, aV, tau, elevation) gamma_R = k * R**a # dB/km, R = rain rate (mm/h) # k and a coefficient tables per ITU-R P.838-3
Tolerance: ±0.05 dB for the specific attenuation; statistical slant-path attenuation tolerances grow at low exceedance probabilities (an acknowledged limitation of the underlying P.618 model, not of HAPIS).
Validation status: Tier 1 closed-form coefficients tested against P.838-3 Tables 1–4; Tier 2 worked-example reproduction at 10 / 14 / 20 / 30 / 40 / 50 GHz with H, V, and circular polarization; Tier 3 cross-check against itur.models.itu838.rain_specific_attenuation during development.
4.4 ITU-R P.840-8 — Cloud and Fog Attenuation
Implemented: P.840-8 Debye double-pole model for the specific attenuation coefficient Kl(f, T) (replaces an early-draft tabulated-Kl interpolation, also a Cat 4 fix during pre-publication audit). Discrete cloud-type presets (clear, cumulus, stratus, nimbostratus) mapped to canonical LWC values.
# ITU-R P.840-8 cloud / fog attenuation Kl = liquid_water_specific_attenuation(f_GHz, T) # (dB/km)/(g/m^3) A_zenith = Kl * L # L = columnar liquid water A = A_zenith / sin(elevation) # slant path, dB # Kl from the double-Debye permittivity model per ITU-R P.840-8
Tolerance: ±0.3 dB at frequencies ≤100 GHz.
Validation status: Tier 1 specific-attenuation coefficient Kl verified at multiple frequencies and cloud temperatures; Tier 2 P.840 Annex 1 example reproduction; Tier 3 cross-check against itur.models.itu840.specific_attenuation_coefficients during development. (Cloud-type presets are HAPIS-specific and not covered by itur.)
4.5 ITU-R P.2108-1 — Clutter Loss
New in v1.0. Previously listed as Cat 5 (not implemented) in early-draft catalogues; v1.0 ships the implementation in hapis_core/atmosphere/itu_p2108.py.
Implemented: P.2108-1 § 3.3 Eq. 7 (Earth-space and aeronautical statistical clutter loss).
K₁ = 93 · f^0.175
L_ces(θ, p) = base - 1 - 0.6 · Q⁻¹(p)
where base = ( -K₁ · ln(1 - p) · cot(A₁(1 - θ/90) + π·θ/180) )^( 0.5(90 - θ)/90 )
A₁ = 0.05
p = not-exceeded probability (catalogue uses exceedance; wrapper handles conversion)
Includes Acklam's rational approximation for the inverse complementary normal CDF (Q⁻¹), so no SciPy dependency in hapis_core/.
# ITU-R P.2108-1 statistical clutter loss (single closed form) Lces_dB = clutter_loss(f_GHz, elevation_angle, p_percent) # Eq. 7 above # K1 = 93 * f^0.175; inverse-normal Q^-1 via the Acklam approximation # functional form and coefficients per ITU-R P.2108-1 3.3 Eq. 7
Tolerance: ±0.5 dB on the catalogue's three CLUT cases; HAPIS-itur agreement is ≤ 0.001 dB.
Implementation notes. The clutter loss is a single closed-form equation transcribed from P.2108-1 § 3.3; the Acklam Q⁻¹ approximation was verified against NTIA's reference C++ implementation (github.com/NTIA/p2108) before being trusted to remove the SciPy dependency. It supports HAPS-to-handheld terminal links in urban and suburban scenarios. Inputs outside the spec envelope (f < 10 GHz, f > 100 GHz, θ outside [0, 90°]) raise ValueError rather than silently returning NaN or extrapolating. The catalogue's CLUT-1 case at 2 GHz (below the spec lower bound) was reclassified Cat 4 during pre-publication audit and replaced with an in-envelope case at 10 GHz, with the original noted in the audit trail.
Validation status: Tier 1 against P.2108-1 § 3.3 Eq. 7 + NTIA reference; Tier 2 three CLUT cases anchored to itur-cross-validated values; Tier 3 cross-check against itur.models.itu2108.clutter_loss during development.
4.6 3GPP TR 38.811 / 38.821 / 38.822 — NTN Reference Scenarios
Implemented: TR 38.811 link-budget composition for HAPS (20 km) and reference NTN scenarios. Doppler shift and Doppler rate per § 7.3 (cosine formulation corrected from the v0.9 sine error, a Cat 1 fix). Propagation delay per § 6. Spherical-Earth slant range (corrected from v0.9 flat-Earth, also Cat 1). The Tier 2 catalogue covers the Set-1 GEO at 12.5° elevation, LEO-600 at 30°, LEO-1200 at 30°, plus HAPS reference geometry.
# 3GPP TR 38.811 / 38.821 NTN link geometry d_km = slant_range(altitude_km, elevation) # spherical-earth geometry FSPL_dB = 92.45 + 20*log10(f_GHz) + 20*log10(d_km) A_atm = gas + rain + cloud + scintillation # per TR 38.811 channel model # CNR = EIRP - FSPL - A_atm + G/T - k_boltzmann - 10*log10(B) # scenario tables and elevation-dependent parameters per 3GPP TR 38.811/38.821
Tolerance: each individual loss term within its standard-specific tolerance (above). Aggregated link budget within ±0.5 dB of TR 38.811/38.821 published reference budget tables.
Validation status: Tier 2 reproduction of TR 38.811 § 6.6 (HAPS) and TR 38.821 Table 6.1.1.1-1 (Set-1 GEO, LEO-600, LEO-1200). Cross-validation against itur for the propagation components (FSPL, gases, rain); 3GPP-specific NTN composition logic is not in itur, so the cross-check there is structural (slant geometry, Doppler maximum) plus the per-component witnesses from § 4.1–4.5.
Disclosure: TR 38.822 is referenced as a draft specification. All 38.822-derived requirements are marked draft and subject to change.
5. Competitive Positioning — Honest Comparison
This section is provided not as marketing but as the comparison that any technically literate prospect will perform on their own. It is more useful for HAPIS to publish it honestly than to leave the prospect to construct an unfavourable version.
| Capability | STK Comm + RF | MATLAB SatComm | itur (Python) | Spreadsheet | HAPIS v1.0.1 |
|---|---|---|---|---|---|
| ITU-R P.525 / P.676 / P.838 / P.840 | ✓ (in modules) | ✓ | ✓ | partial, manual | ✓ |
| ITU-R P.2108 clutter | partial | partial | ✓ | — | ✓ |
| 3GPP TR 38.811 NTN scenarios | partial (custom) | ✓ | — | — | ✓ (HAPS focus) |
| HAPS-specific geometry (18–25 km) | adaptable | adaptable | — | manual | purpose-built |
| Per-commit oracle pytest | n/a | n/a | n/a (is itself) | n/a | ✓ (vs itur) |
| Cross-tool validation matrix | proprietary | proprietary | community | n/a | public artefact |
| Incident disclosure (e.g., § 11) | partial | partial | community | n/a | whitepaper |
| RESTful API access | via plugin | via MATLAB Production Server | n/a | n/a | ✓ native |
| Cost (small team) | high (≥USD $10k+/seat) | high (MATLAB licence) | free | free | low (SaaS) |
| Reproducibility (versioned outputs) | partial | partial | ✓ | poor | ✓ |
| Coverage map / heatmap visualization | ✓ | partial | — | — | ✓ |
| Source-code auditability | closed | closed (Toolbox) | open | open | formulas open |
Where HAPIS does not yet compete:
- Orbital mechanics depth. STK remains the leader for high-fidelity orbital propagation, sensor pointing, and mission-design workflows. HAPIS does not attempt to replace SGP4 / numerical-integrator-class orbital modelling.
- Antenna-array beamforming simulation. STK and MATLAB Phased Array Toolbox provide capabilities HAPIS does not.
- Full physical-layer simulation (waveform-level). GNU Radio and MATLAB 5G / Comm Toolboxes are the appropriate tools for waveform-level link emulation; HAPIS operates at the link-budget level.
Honest disclosure of where HAPIS does not compete is a deliberate trust signal. An RF engineer who asks “but what about X?” and finds X already acknowledged in the methodology document treats the rest of the methodology with proportionally more confidence.
6. Metrics and Reporting Format
6.1 Statistical Metrics
For every cross-validation comparison (Tiers 1–4), HAPIS reports:
| Metric | Definition | Why it matters |
|---|---|---|
| Bias (mean error) | Σ(ŷ−y) / N | Systematic deviation from reference |
| RMSE | √(Σ(ŷ−y)2 / N) | Aggregate accuracy including variance |
| Max absolute error | max |ŷ−y| | Worst-case behaviour, critical for safety arguments |
| % within tolerance | fraction of cases with |ŷ−y| < τ | Coverage of the tolerance claim |
| CDF Kolmogorov–Smirnov D | supx|Fŷ(x)−Fy(x)| | For statistical (e.g., rain) model comparison |
6.2 Per-Standard Tolerance Table
Tightened for v1.0 publication. The P.676 zenith tolerance was reduced from ±0.3 dB (an early-draft posture sized to absorb Fig. 4 read variance) to ±0.05 dB (typical) after the Cat 4 catalogue reclassification (see § 11) removed the source of the prior tolerance bloat.
| Standard | Validation tolerance | Justification |
|---|---|---|
| ITU-R P.525 (FSPL) | ±0.01 dB | Closed-form arithmetic |
| ITU-R P.676 (gases), zenith | ±0.05 dB typical, ±2.0 dB at 60 GHz peak | HAPIS-itur agreement is ≤ 0.02 dB except at the genuinely sensitive O₂ complex peak (where independent spec-compliant impls routinely differ by 1–2 dB). |
| ITU-R P.676 (gases), slant ≥10° | ±0.05 dB | HAPIS-itur agreement is ≤ 0.01 dB |
| ITU-R P.676, slant 5° | ±0.3 dB + warning required | Straight-line vs Snell-law geometry difference (~0.18 dB) |
| ITU-R P.838-3 (rain specific) | ±0.05 dB | Closed-form curve fit (P.838-3 Eqs. 2–3) |
| ITU-R P.618 (rain stat. slant) | ±1.0 dB at 0.01 %, larger at < 0.01 % | Model-intrinsic uncertainty |
| ITU-R P.840 (cloud) | ±0.3 dB | Rayleigh approximation domain |
| ITU-R P.2108 (clutter) | ±0.5 dB | HAPIS-itur agreement is ≤ 0.001 dB; tolerance dominated by catalogue rounding |
| 3GPP TR 38.811 link budget | ±0.5 dB aggregate | Per-term tolerances accumulated |
Across the full catalogue, every scalar reference case (FSPL, gas, rain, cloud, clutter) is shown below as its deviation from the reference value, expressed as a fraction of that case's tolerance — everything inside the shaded band passes.
itur reference for all scalar reference cases, each shown as Δ relative to that case's tolerance. Every case falls inside the pass band. Generated by the same per-category audit dispatch described in § 8.Tolerance-setting policy:
Widening a tolerance to make a test pass is a smell. It is appropriate only when (1) the case is Cat 3, (2) the whitepaper § 4.x has been updated, (3) the widening is the minimum needed, (4) the new tolerance is still tight enough to catch real regressions.
The pre-publication reclassification of GAS-* from Cat 3 (tolerance widened) to Cat 4 (catalogue replaced, tolerance tightened) is the canonical example of why this discipline matters: had we widened tolerances instead of cross-validating against itur, the catalogue errors would still be hidden today. See § 11.4.
6.3 Public Reporting
The HAPIS validation surface as of v1.0.1:
Publicly available today:
- This methodology document.
- Signed container images on GHCR with SPDX SBOM attested keyless via Sigstore
cosignfor release-tagged builds (the engine release workflow signs every tagged image automatically).
Available to engineering reviewers under NDA:
- The full reference-case catalogue (
reference_cases.yaml) with all case IDs, inputs, expected values, tolerances, source citations, and Cat 4 audit-trail notes. - The oracle test source (
tests/test_p676_itur_oracle.py) and last 90 days of CI test-job results. - The audit script (
audit/audit_hapis_core.py) and the audit gap report (audit/GAP_REPORT_2026-05-26.md) — the source artefacts for the § 11 case study. - Engine GitHub Actions CI status (lint, test + coverage including oracle, security scan, signed image with SBOM).
7. Known Limitations
A whitepaper that omits its limitations is hiding something. The following are explicit limitations of HAPIS as of v1.0.1:
7.1 Model limitations inherited from source standards
- ITU-R P.618 rain attenuation at exceedance probabilities below 0.001 % has intrinsic uncertainty larger than ±1 dB; HAPIS does not improve on this.
- ITU-R P.676 line-by-line model is most accurate for clear sky; in heavy precipitation, gas + rain interaction is approximated as additive.
- ITU-R P.840 cloud attenuation uses Rayleigh approximation, valid for cloud droplets up to ~100 µm — not applicable to ice clouds at very high frequencies.
7.2 HAPS-specific empirical data scarcity
Stratospheric platforms at 18–25 km altitude lack the extensive multi-year beacon measurement campaigns available for GEO and LEO satellites. The closest available evidence is slant-path satellite measurements adapted to 20 km geometry, plus the limited published HAPS field-trial data. Operationally observed HAPS performance may therefore differ from predictions by amounts not yet bounded by published data.
7.3 Cross-validation scope
Tier 3 against itur is mandatory per-commit for all P.676 cases and is run during development for the other propagation models (FSPL, P.838, P.840, P.2108). It does not cover 3GPP NTN composition (link-budget aggregation, Doppler logic), because itur does not implement the 3GPP TRs; for the NTN scenarios the cross-check is structural (slant geometry, Doppler maximum) plus the per-component witnesses from § 4.1–4.5.
7.4 Out-of-envelope inputs
The API rejects requests outside the validity envelope of the underlying standards (e.g., frequencies outside 10–100 GHz for P.2108). Where the envelope is soft, the API returns the result with a warning flag indicating reduced confidence (Tier 2 case GAS-S3 asserts this behaviour at elevation = 5°).
7.5 Not modelled in v1.0
- Ionospheric scintillation
- Tropospheric scintillation time series (P.618 mean scintillation only)
- Faraday rotation (relevant only at sub-GHz frequencies, outside HAPS primary band)
- Multipath-fading time-series synthesis (handled separately by the NTN Simulator product)
- Snell-law ray-bending in the P.676 slant integration (straight-line geometry only; matters at elevations < 5° and is flagged with
low_elevation_warning) - The slant integral applies a numerical elevation floor of 0.5° purely to keep
1/sin(el)finite at the horizon; it is not a service policy. The operational minimum service elevation is a separate, user-set parameter that gates the coverage footprint (cells below it are out of service), independent of the attenuation physics. - Higher-order O₂ line-mixing coupling beyond what P.676-13 Eq. 7 δ-correction prescribes (see § 7.6)
These are out of scope for this validation cycle.
7.6 60 GHz O₂ complex peak — intrinsic sensitivity
At the 60 GHz oxygen complex peak, the integrated zenith attenuation is sensitive to several implementation choices that the P.676-13 specification leaves to the implementer: layer discretisation in the slant integral, whether higher-order Rosenkranz line-coupling coefficients are applied beyond the δ-correction of Eq. 7, whether Snell-law refraction is applied to the high-altitude ray geometry, and which dry-continuum coefficient set is used. Published spec-compliant implementations span approximately 150–200 dB at this frequency. HAPIS returns 153.66 dB; itur returns 155.05 dB; the catalogue's previous Fig. 4 read was 184 dB (since reclassified Cat 4 — see § 11).
Customer impact: none. 60 GHz is heavily restricted spectrum (terrestrial unlicensed band overlap; ITU notes), not a frequency at which HAPIS is sold against. The tolerance ±2 dB at this point is a calibration sentinel for the engine, not a customer-facing accuracy claim. Across the four HAPIS operational bands (S 2 GHz, Ka 28 GHz, Q 38 GHz, V 47 GHz), HAPIS-itur agreement is ≤ 0.05 dB.
8. Reproducibility — How to Verify Claims in This Document
HAPIS publishes signed, traceable evidence that the validation pipeline ran, rather than asking the reviewer to re-execute it independently. This follows the model used in safety-critical software audit (DO-178C, IEC 61508): the audit trail itself is the artefact, and re-execution is available on request.
Self-serve, publicly available today:
- Signed container images. Tagged release builds are pushed to GHCR with an SPDX SBOM and signed keyless via Sigstore
cosign(OIDC). Anyone can verify the signature attests both the SBOM and the build provenance to the image digest:cosign verify ghcr.io/hap-intel-systems/hapis-api@<digest> \ --certificate-identity-regexp 'https://github.com/HAP-Intel-Systems/.*' \ --certificate-oidc-issuer https://token.actions.githubusercontent.comThe SBOM attestation can be fetched separately withcosign download attestation. - Oracle test reproducibility. Any reviewer with the validation bundle (
reference_cases.yaml,test_reference_cases.py, and theituroracle test) and a Python environment withitur>=0.4.0installed can runpytest tests/test_p676_itur_oracle.py -vand reproduce the 8 P.676 oracle pass/fail outcomes locally. The expected values in the catalogue, the oracle tolerances, and the dev-only itur pin are all in source-controlled files.
Available to engineering reviewers under NDA (contact@hapintel.com):
- The full reference-case catalogue (
reference_cases.yaml) with all case IDs, inputs, expected values, tolerances, source citations, and Cat-4 audit-trail notes. - Full Tier 1 outputs (212 unit tests, pass / fail / numerical deltas).
- The audit script (
audit/audit_hapis_core.py) and its raw output for the v1.0 publication commit: per-case engine value, delta vs catalogue, pass/fail status. - The full incident artefacts referenced in § 11:
audit/GAP_REPORT_2026-05-26.md,audit/run_output_after_cat4_oracle.txt. - Coverage report from the most recent CI run.
- Live walkthrough — a recorded or live session with engineering executing the test suite, for reviewers who need to observe the pipeline running rather than read its output.
9. Versioning
This document describes v1.0.1 and covers HAPIS API 1.0.x. Material changes — a new standard, a tolerance revision, or a validation-suite restructuring — trigger a new version.
10. References
ITU-R Recommendations
- ITU-R Rec. P.525-4, “Calculation of free-space attenuation,” 2019.
- ITU-R Rec. P.618-13, “Propagation data and prediction methods required for the design of Earth-space telecommunication systems,” 2017.
- ITU-R Rec. P.676-13, “Attenuation by atmospheric gases and related effects,” 2022.
- ITU-R Rec. P.835-5, “Reference standard atmospheres,” 2018 — atmosphere profile used in the P.676 slant-integration path.
- ITU-R Rec. P.838-3, “Specific attenuation model for rain for use in prediction methods,” 2005.
- ITU-R Rec. P.840-8, “Attenuation due to clouds and fog,” 2019.
- ITU-R Rec. P.2108-1, “Prediction of clutter loss,” 2021.
- ITU-R Rec. P.311-19, “Acquisition, presentation and analysis of data in studies of radiowave propagation,” 2021.
- ITU-R Rec. P.837-7, “Characteristics of precipitation for propagation modelling,” 2017.
3GPP Technical Reports
- 3GPP TR 38.811 v15.4.0, “Study on New Radio (NR) to support non-terrestrial networks,” 2020.
- 3GPP TR 38.821 v16.2.0, “Solutions for NR to support non-terrestrial networks (NTN),” 2023.
- 3GPP TR 38.822 (draft), “NR support for high-altitude platform station (HAPS),” 2021.
V&V and Methodology Standards
- ANSI/ASME V&V 20-2009, “Standard for verification and validation in computational fluid dynamics and heat transfer.”
- NASA-STD-7009A, “Standard for models and simulations,” 2016.
- IEEE Std 1012-2016, “IEEE standard for system, software, and hardware verification and validation.”
Textbooks
- B. Sklar, Digital Communications: Fundamentals and Applications, 2nd ed., Prentice Hall, 2001.
- T. Pratt, C. W. Bostian, and J. E. Allnutt, Satellite Communications, 3rd ed., Wiley, 2019.
- G. Maral, M. Bousquet, and Z. Sun, Satellite Communications Systems, 6th ed., Wiley, 2020.
- S. R. Saunders and A. Aragón-Zavala, Antennas and Propagation for Wireless Communication Systems, 2nd ed., Wiley, 2007.
Open-Source Reference Implementations
- I. del Portillo, “ITU-Rpy (
itur): Python implementation of ITU-R recommendations for atmospheric propagation,” v0.4.0 — HAPIS Tier 3 oracle reference; declared inrequirements-dev.txtonly, never imported byhapis_core/. - B. Winkel et al., “pycraf: Spectrum-management compatibility studies in Python.”
- NTIA, “NTIA P.2108 reference C++ implementation,”
github.com/NTIA/p2108— used to verify HAPIS's Acklam Q⁻¹ application initu_p2108.py.
11. Case Study — The Cat 4 Reclassification Incident (2026-05-26)
This section documents an incident in the validation pipeline that occurred during pre-publication audit of v1.0: how it was discovered, what was first suspected, what turned out to be true, what changed in HAPIS as a result, and what the methodology should do differently next time. It is included in this whitepaper as the canonical instance of the § 2.4 principle (“errors are first-class artefacts”). v1.0 ships with the fix already applied; the historical pre-publication catalogue and tolerances are archived (see § 9) as the canonical example of what the methodology corrects.
The full primary artefacts are audit/GAP_REPORT_2026-05-26.md, audit/audit_hapis_core.py, and audit/run_output_after_cat4_oracle.txt, all available to NDA reviewers (see § 8). What follows is a narrative summary.
11.1 What happened
In the course of integrating the pre-publication validation bundle into the engine repository ahead of the v1.0 publication, a one-off audit script (audit/audit_hapis_core.py) ran every case in reference_cases.yaml directly against hapis_core/ (bypassing the HTTP layer). It surfaced three failing P.676 cases — GAS-Z3 at 50 GHz (Δ = +0.444 dB), GAS-Z4 at 60 GHz (Δ = −30.452 dB), GAS-Z5 at 94 GHz dry (Δ = −0.625 dB) — all outside the ±0.3 dB catalogue tolerance of the time.
11.2 What was first suspected — Cat 3 (defensible alternative)
The initial diagnosis classified all three as Cat 3 (“defensible alternative”): the engine implements P.676-13 Annex 1 line-by-line with Tables 1 & 2 verbatim and matches Fig. 4 of the same recommendation to visual read precision; the catalogue's spot values were assumed to come from a different but equally defensible implementation. The remediation was to widen the three tolerances to the minimum needed, document the choice in the YAML notes: block on each case, and defer a whitepaper § 4.2 update to explain the alternative.
The owner authorised that work and it was executed in commit 12980e5. Tolerances moved from ±0.3 / ±5.0 / ±0.3 dB to ±0.5 / ±35.0 / ±0.7 dB. The suite went green at 35/35.
This was wrong, but the engine work shipped alongside it (P.676 Annex 1 line-by-line, P.2108 Eq. 7, MAGRA atmosphere alignment) was correct and is retained in v1.0.
11.3 What turned out to be true — Cat 4 (catalogue error)
Before committing to a deeper engine investigation (the next planned step was implementing Rosenkranz O₂ line-mixing physics, an estimated 4–6 hour exercise that would have added several hundred lines of spectroscopic code), the owner asked: “do your own research to verify the catalog inflation.”
Installing itur v0.4.0 as a test-only dependency and running cross-validation revealed the actual picture:
| Case | f (GHz) | el° | Catalogue (Fig. 4) | itur (Annex 1) | HAPIS | cat−itur | HAPIS−itur |
|---|---|---|---|---|---|---|---|
| GAS-Z1 | 14.0 | 90 | 0.10 | 0.0725 | 0.072 | +0.027 | −0.001 |
| GAS-Z2 | 22.235 | 90 | 0.41 | 0.5221 | 0.522 | −0.112 | +0.000 |
| GAS-Z3 | 50.0 | 90 | 1.10 | 1.5648 | 1.545 | −0.465 | −0.020 |
| GAS-Z4 | 60.0 | 90 | 184.00 | 155.0524 | 153.660 | +28.948 | −1.393 |
| GAS-Z5 | 94.0 | 90 | 0.81 | 0.1861 | 0.185 | +0.624 | −0.001 |
| GAS-S1 | 20.0 | 10 | 1.40 | 1.4404 | 1.444 | −0.040 | +0.004 |
| GAS-S2 | 30.0 | 30 | 0.70 | 0.4633 | 0.459 | +0.737 | −0.005 |
| GAS-S3 | 40.0 | 5 | 4.50 | 4.3458 | 4.521 | −0.154 | +0.175 |
HAPIS agreed with itur to within 0.02 dB on six of eight cases and within 1.4 dB on the genuinely sensitive 60 GHz O₂ peak. The catalogue disagreed with itur by 0.03–29 dB on six of eight. Per the Cat 4 signal — “HAPIS disagrees with the catalogue but agrees with itur to high precision” — all 8 GAS cases were reclassified Cat 4 (catalogue error). The catalogue's expected values were derived from Fig. 4 visual reads of the P.676-13 PDF, a log-scale chart with limited read precision; the analytic Annex 1 output (which itur and HAPIS both compute) differed materially.
11.4 What changed
Following the Cat 4 reclassification, the following landed over three atomic commits:
- Engine spec-alignment hygiene (
b9e94c8). Implemented the P.835-5 § 1.3 mixing-ratio floor on ρ(h) and switched to the P.676-13 Annex 2 § 2.2 reference layer prescription (N = 922 exponentially-spaced layers). These changes brought HAPIS marginally closer to itur but did not close the catalogue gap — confirming the gap was on the catalogue side, not the engine side. - Catalogue Cat 4 corrections (
7efe3c5). All 8 GAS-* expected values replaced with itur-computed values; tolerances tightened to the implementation-difference scale (±0.05 dB typical, ±2.0 dB at 60 GHz peak, ±0.3 dB at 5° elevation); per-case audit-trail notes added. - Oracle pytest (
409e095). Newtests/test_p676_itur_oracle.pyasserts |HAPIS − itur| ≤ tolerance on every catalogue case at every commit.iturwas added torequirements-dev.txt(test-only);hapis_core/does not import it. This is the boundary that preserves HAPIS's implementation independence (see § 3.3) while making the disagreement a build-blocker.
Final state: 35 / 35 audit cases pass with tight tolerances (no wider than physically necessary); 212 / 212 pytest pass (up from 204 — the new 8 oracle tests added).
11.5 Lessons
Three lessons stand out.
First, two implementations of the same standard can give materially different answers at sensitive points such as the 60 GHz O₂ peak, and what looks like a “defensible alternative” disagreement can in fact be a catalogue error. The discipline is to cross-validate against an independent third implementation before writing more physics code: spending 4–6 hours implementing Rosenkranz line-mixing — only to find the engine would have stayed at 153 dB against the catalogue's 184 — would have been wasted effort. The 30-minute itur cross-check was the right next step.
Second, the pre-publication catalogue never reached production, so no user was exposed to the error; the four HAPIS bands are far from the 60 GHz peak and match itur to ≤ 0.05 dB there. But had the draft shipped as-is, it would have claimed ±0.3 dB at zenith — true under the wide tolerance, yet concealing that the tighter ±0.05 dB claim was available all along and simply was not being verified.
Third, and most important: the pre-publication Tier 3 design (“publish a cross-tool matrix per release”) generated the data that would have caught this, but a finding only blocks action if it blocks CI. Promoting itur from a per-release artefact to a per-commit pytest oracle closes that gap. And the misdiagnosis (Cat 3 plus a 30 dB tolerance widening) is preserved in the engine's git history (commit 12980e5) and in audit/GAP_REPORT_2026-05-26.md under “[SUPERSEDED]” markers, not silently overwritten — a reviewer years from now will find both the wrong path and the corrected one, with the same provenance trail.
11.6 What the methodology should do differently next time
- Always cross-validate against an independent reference before widening a tolerance.
- Promote informational cross-tool matrices to mandatory pytest oracles wherever an independent reference exists. The cost is one parametrised pytest module and one dev-only dependency. The benefit is a permanent regression detector that catches both engine drift and catalogue drift.
- Document incidents in the whitepaper, not just the internal audit log. This § 11 case study is the first published instance. Future incidents (when they happen) will appear here in the same form.
Appendix A — Glossary
| Term | Definition |
|---|---|
| BVLOS | Beyond Visual Line of Sight |
| CDF | Cumulative Distribution Function |
| FSPL | Free-Space Path Loss |
| HAPS | High-Altitude Platform System / Station |
| MAGRA | Mean Annual Global Reference Atmosphere (ITU-R P.835-5 § 1.1) |
| NTN | Non-Terrestrial Network |
| Oracle | An independent reference implementation used to assert agreement via automated test, not just comparison reporting (see § 1.3). |
| RMSE | Root Mean Square Error |
| V&V | Verification and Validation |