Skip to content

feat(core): atmospheric measurement propagation — refraction + ionospheric delay biases with correction hooks - #145

Merged
montge merged 2 commits into
developfrom
feature/atmos-prop-impl
Jul 17, 2026
Merged

feat(core): atmospheric measurement propagation — refraction + ionospheric delay biases with correction hooks#145
montge merged 2 commits into
developfrom
feature/atmos-prop-impl

Conversation

@montge

@montge montge commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements atmospheric-measurement-propagation (#144 spec; 15/15 tasks) — the final change of the propagation tier. Radar measurements gain the deterministic bias physics they were missing (refraction bending, ionospheric group delay), trackers gain the correction hooks, and the eval-consistency layer gets its closed-loop demonstration. All additive: default generation, every constructor/TOML, and all calibrated benchmarks byte-for-byte unchanged.

thresh-core/propagation

  • Refraction: Bean–Dutton/CRPL exponential-profile ray integral (Bouguer's rule, fixed-count Simpson) → elevation bending + excess range; ITU-R P.834-8 worked values reproduced; zenith excess matches the canonical ~2.31 m dry delay to < 0.1 mm; 4/3-Earth cheap tier (with the cos φ obliquity factor) cross-checked within [0.90, 0.98] of the exponential tier; explicit 1° validity floor; targets at/below station altitude yield exactly zero bias
  • Iono delay: Δr = 40.308193·STEC/f² (constant provenance fetched + bit-pinned), 450 km thin-shell obliquity (GLONASS/IGS convention), exact 1/f² scaling, L/S/X magnitudes (3.18 m / 0.60 m / 0.054 m at 10 TECU, 45°), Chapman→VTEC bridge verified against the analytic N_max·H·√(2πe) integral
  • Corrections: composed correct_atmosphere at the RAE→Cartesian seam; honesty measured — ±10% N_s / ±25% VTEC mis-sets leave ≈ the mis-set fraction (the spec states the truthful near-linear guarantee)

thresh-synth

Opt-in generate_radar_biased (bias-then-noise); the iono leg is gated on target altitude ≥ shell height — the documented step-function, so sub-ionospheric aircraft accumulate zero phantom TEC delay.

The demonstration (what the tier was built toward)

Three runs through the real benchmark runner, same seed and noise realisation:

run MOTA MOTP (m) ANEES ANIS
honest 0.9512 34.808 1.5440 1.5947
biased, uncorrected 0.9512 84.648 6.2900 1.5948
biased, corrected 0.9512 34.803 1.5438 1.5947
  • MOTA bit-identical across all three — the bias class is invisible to association metrics
  • MOTP 2.43× and ANEES 4.07× inflated uncorrected; correction recovers to within 0.02%
  • ANIS is measured structurally blind (1.5947→1.5948): a smoothly-varying bias is absorbed into the filter state, so innovation self-consistency cannot see what truth-referenced ANEES sees — Bar-Shalom's NEES-vs-NIS distinction as a live measurement, recorded in design.md

Benchmark invariance (task 4.5)

All four calibrated scenarios digit-for-digit identical to the pre-change baseline (diff empty): orbital-iss 0.8926/981.4041/0.9461/0.9474 · ANEES 1.4529 / ANIS 2.2042 — orbital-starlink-train 0.9248/1602.0943/0.9622/0.9629 · 3.6243/2.5441 — ballistic-mrbm 0.9978/397.6703/0.9989/0.9989 · 2.7569/2.5442 — synth-cv-clean 0.9355/56.4910/0.9667/0.9672 · 3.2636/3.2760.

Verification

  • Workspace tests (48 binaries, 0 failures), clippy -D warnings, fmt, rustdoc -Dwarnings, openspec validate --all --strict --no-interactive 46/46
  • Every constant fetched in-session with citations + bit-pinned tripwires; every spec scenario has a named covering test
  • Adversarially reviewed (4 lenses → 3-skeptic votes): 5 findings confirmed, 0 rejected, all fixed — a major sign bug (targets below an elevated station produced kilometre-scale negative excess range), the missing cos φ obliquity in the 4/3 tier (whose absence had forced the cross-check band wide enough to mask real regressions), unconditional iono application to sub-ionospheric targets, a spec/test mismatch on the residual guarantee, and a false ANIS doc claim

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added atmospheric propagation modeling for radar measurements, including tropospheric refraction and ionospheric delay.
    • Added configurable correction of apparent radar range and elevation measurements.
    • Added optional atmospheric bias simulation for synthetic radar data.
    • Added a low-elevation benchmark scenario demonstrating bias and correction effects.
  • Documentation

    • Updated specifications and implementation notes with completed propagation details, benchmark results, and correction behavior.
  • Tests

    • Added comprehensive validation for modeling accuracy, correction round trips, deterministic results, configuration handling, and benchmark performance.

…elay biases with correction hooks (atmospheric-measurement-propagation)

Completes the atmospheric-measurement-propagation change (15/15 tasks),
the final change of the propagation tier. All additive: default radar
generation, every constructor/TOML, and all calibrated benchmarks are
byte-for-byte unchanged.

- thresh-core/propagation/refraction: Bean-Dutton/CRPL exponential-
  profile ray integral (Bouguer's rule, fixed-count Simpson) returning
  elevation bending + excess range; ITU-R P.834-8 worked values
  reproduced (zenith excess matches the canonical ~2.31 m dry delay to
  <0.1 mm); 4/3-Earth cheap tier with the cos-phi obliquity factor,
  cross-checked within [0.90, 0.98] of the exponential tier; explicit
  1-degree validity floor; correct_refraction one-step fixed-point
  inverse; targets at/below station altitude yield exactly zero bias
- thresh-core/propagation/iono_delay: dr = 40.308193*STEC/f^2 (constant
  provenance fetched + bit-pinned), thin-shell obliquity (450 km IGS/
  GLONASS convention, configurable), exact 1/f^2 tests, L/S/X band
  magnitudes, closed-form correction, Chapman->VTEC bridge verified
  against the analytic N_max*H*sqrt(2*pi*e) integral
- thresh-synth: opt-in generate_radar_biased (bias-then-noise, azimuth
  untouched); iono leg gated on target altitude >= shell height (the
  documented step-function: sub-ionospheric targets accumulate none of
  the TEC) — no phantom iono bias on aircraft
- Corrections compose via correct_atmosphere at the RAE->Cartesian seam;
  mismatched-parameter honesty measured: +-10% N_s / +-25% VTEC leave
  approximately the mis-set fraction (spec amended to state the truthful
  near-linear guarantee)
- The demonstration the tier was built toward, through the real
  benchmark runner (three runs, same seed/noise): honest MOTP 34.81 /
  ANEES 1.544; biased-uncorrected 84.65 / 6.290 (2.43x / 4.07x, MOTA
  bit-identical — the bias is invisible to association); corrected
  34.80 / 1.544 (within 0.02%). ANIS measured structurally blind
  (1.5947 -> 1.5948): smooth bias absorbed into the filter state — the
  NEES-vs-NIS distinction as a live measurement
- Benchmark invariance: all four calibrated scenarios digit-for-digit
  identical to the pre-change baseline

Adversarially reviewed (4 lenses, 3-skeptic votes): 5 findings
confirmed, 0 rejected, all fixed — a major sign bug (negative excess
for targets below an elevated station), the missing cos-phi obliquity
in the 4/3 tier (cross-check band tightened from the masking [0.85,
1.35] to [0.90, 0.98]), the unconditional iono application, the
spec/test residual-guarantee mismatch, and a false ANIS doc claim.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 16, 2026 23:48

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@montge, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 44 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f5c2fc87-fdaa-4fd5-9062-2446df6a01a8

📥 Commits

Reviewing files that changed from the base of the PR and between 2fa7a80 and 7d1e56d.

📒 Files selected for processing (4)
  • crates/thresh-core/src/propagation/iono_delay.rs
  • crates/thresh-core/src/propagation/refraction.rs
  • crates/thresh-data/src/benchmark.rs
  • openspec/changes/atmospheric-measurement-propagation/design.md
📝 Walkthrough

Walkthrough

Changes

Atmospheric propagation

Layer / File(s) Summary
Ionospheric delay model
crates/thresh-core/src/lib.rs, crates/thresh-core/src/propagation/iono_delay.rs
Adds thin-shell ionospheric delay APIs, configurable TEC and shell height, Chapman-to-VTEC integration, and validation tests.
Refraction model and inversion
crates/thresh-core/src/propagation/refraction.rs
Adds exponential-profile ray integration, effective-Earth bending, bias application, correction, and numerical tests.
Combined atmospheric correction
crates/thresh-core/src/propagation/mod.rs
Exports propagation modules and applies ionospheric correction before optional refraction correction.
Synthetic radar bias generation
crates/thresh-synth/src/measurement_gen.rs
Adds configurable atmospheric biasing before measurement noise and a biased radar-generation entry point.
Benchmark demonstration and correction wiring
crates/thresh-data/src/benchmark.rs
Adds opt-in bias and correction parameters, low-elevation scenarios, corrected Cartesian conversion, and deterministic end-to-end tests.
Resolved propagation design and tasks
openspec/changes/atmospheric-measurement-propagation/...
Records implementation choices, residual expectations, and completed verification tasks.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ScenarioRunner
  participant RadarGenerator
  participant PropagationModels
  participant CartesianConverter
  ScenarioRunner->>RadarGenerator: generate biased radar measurements
  RadarGenerator->>PropagationModels: apply refraction and ionospheric delay
  PropagationModels-->>RadarGenerator: apparent range and elevation
  RadarGenerator-->>ScenarioRunner: noisy measurements
  ScenarioRunner->>CartesianConverter: convert measurements with correction enabled
  CartesianConverter->>PropagationModels: remove modeled atmospheric bias
  PropagationModels-->>CartesianConverter: corrected geometry
Loading

Possibly related PRs

  • Chaffgold/thresh#48: Both changes modify benchmark scenario configuration and dispatch paths.

Suggested reviewers: copilot

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding atmospheric propagation biases and correction hooks for refraction and ionospheric delay.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/atmos-prop-impl

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/thresh-core/src/propagation/iono_delay.rs`:
- Around line 125-135: Validate both public serde configuration structs at
deserialization: in crates/thresh-core/src/propagation/iono_delay.rs lines
125-135, enforce finite non-negative vtec_tecu, positive shell_height_m, and
positive frequency_hz; in crates/thresh-core/src/propagation/refraction.rs lines
145-155, enforce finite non-negative refractivity, positive scale height, and
minimum elevation within (0, π/2]. Reject invalid values before either
configuration can be used for propagation.

In `@crates/thresh-data/src/benchmark.rs`:
- Around line 253-270: Update the feature-gated manifest constructors
adsb_manifest and nuscenes_manifest to initialize the new atmosphere_bias field
as None and atmosphere_correction as false, preserving their existing defaults
and ensuring both feature builds type-check.
- Around line 2572-2591: Extend the invariant assertions in the benchmark test
to verify corrected.idf1 matches honest.idf1 using the existing bitwise
comparison style, and assert that corrected ANIS remains effectively unchanged
from honest ANIS using the documented tolerance. Keep the existing MOTA, IDF1,
MOTP, and ANEES checks intact.

In `@openspec/changes/atmospheric-measurement-propagation/design.md`:
- Around line 68-74: Update the benchmark results table near the documented
atmospheric bias comparison to include an IDF1 column with values for the
honest, uncorrected, and corrected runs, using the values produced by
atmospheric_bias_demonstration_through_runner. Keep the existing claim that IDF1
is bit-identical only if those recorded values verify it; otherwise remove the
claim.
- Line 81: Resolve the shell-height default consistently across the design
document: update the earlier 400 km decision and all dependent specifications to
450 km, or explicitly mark 400 km as superseded. Correct the decision
cross-reference so the default is attributed to the appropriate Decision
3/Decision 4 entry, preserving reproducibility with
IonoDelayConfig::shell_height_m.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 138a8ec6-1033-4c7d-83c0-3c4f584cbcd6

📥 Commits

Reviewing files that changed from the base of the PR and between b270f3d and 2fa7a80.

📒 Files selected for processing (9)
  • crates/thresh-core/src/lib.rs
  • crates/thresh-core/src/propagation/iono_delay.rs
  • crates/thresh-core/src/propagation/mod.rs
  • crates/thresh-core/src/propagation/refraction.rs
  • crates/thresh-data/src/benchmark.rs
  • crates/thresh-synth/src/measurement_gen.rs
  • openspec/changes/atmospheric-measurement-propagation/design.md
  • openspec/changes/atmospheric-measurement-propagation/specs/tropospheric-refraction/spec.md
  • openspec/changes/atmospheric-measurement-propagation/tasks.md

Comment thread crates/thresh-core/src/propagation/iono_delay.rs
Comment thread crates/thresh-data/src/benchmark.rs
Comment thread crates/thresh-data/src/benchmark.rs
Comment thread openspec/changes/atmospheric-measurement-propagation/design.md Outdated
Comment thread openspec/changes/atmospheric-measurement-propagation/design.md Outdated
…sserts, design consistency

- RefractionConfig/IonoDelayConfig gain validate() called at every public
  evaluation entry point (finite/sign checks with clear panics — the
  DpConfig/ForceModelConfig pattern); should_panic tests added
- adsb/nuscenes test manifests gain the four ScenarioParameters fields
  added since #136 (measurement_model, tracker_noise_sigma,
  atmosphere_bias, atmosphere_correction) — these cfg(test)+cfg(feature)
  literals were latently broken because no CI job runs cargo test with
  those features; verified with cargo test -p thresh-data --features adsb
- Demonstration test now asserts corrected-IDF1 bit-equality and that
  ANIS stays within 0.01 of honest in both biased runs (the structural-
  blindness claim is asserted, not just recorded)
- design.md: three-run table gains the IDF1 column (0.9750 across all
  runs); Decision 3's stale 400 km shell default corrected to the
  implemented 450 km with supersession note; divergence bullet anchored
  to Decision 3 explicitly

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@montge
montge merged commit 77b8b02 into develop Jul 17, 2026
28 checks passed
montge added a commit that referenced this pull request Jul 17, 2026
…hive completed change (#146)

Implementation merged in #145 (15/15 tasks). Syncs the three delta specs
into main specs — tropospheric-refraction, ionospheric-group-delay, and
biased-measurement-generation — then moves the change to
openspec/changes/archive/2026-07-17-atmospheric-measurement-propagation.
This closes the five-change propagation tier.

openspec validate --all --strict --no-interactive: 48/48 post-archive.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants