diff --git a/docs/changelog.md b/docs/changelog.md index 0d59e6d5c..0023f96ea 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -22,6 +22,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [Electron Kiosk Shell](electron-kiosk-shell.md#browser-local-state-breaking-on-first-electron-launch). ### Fixed +- **Simulator output tags estimated launch angles as estimated.** When the + server fills in a launch angle before the sim fan-out, the resolver tagged + it, and the carry built on it, as measured. It now reads the angle's source, + so the simulator badges, `sim_shot` event and `sim_send` log entries match + the data. - **Doc paths named from the Python package resolve again.** The K-LD7 OPS-bin penalty warning, the cloud client and session-logger comments, and the two-ray module docstring pointed at flat `docs/*.md` files that diff --git a/src/openflight/sim/resolver.py b/src/openflight/sim/resolver.py index 74b22c621..96c599ba3 100644 --- a/src/openflight/sim/resolver.py +++ b/src/openflight/sim/resolver.py @@ -71,14 +71,18 @@ def resolve_shot(shot: Shot, player_state: PlayerState) -> ResolvedShot: if shot.launch_angle_vertical is not None: vla = float(shot.launch_angle_vertical) - provenance["vla"] = "measured" + provenance["vla"] = ( + "estimated" if shot.launch_angle_vertical_source == "estimated" else "measured" + ) else: vla = get_club_physics(shot.club).optimal_launch_deg provenance["vla"] = "estimated" if shot.launch_angle_horizontal is not None: hla = float(shot.launch_angle_horizontal) - provenance["hla"] = "measured" + provenance["hla"] = ( + "estimated" if shot.launch_angle_horizontal_source == "estimated" else "measured" + ) else: hla = 0.0 provenance["hla"] = "estimated" @@ -114,7 +118,7 @@ def resolve_shot(shot: Shot, player_state: PlayerState) -> ResolvedShot: # means launch-angle-informed: the carry model was driven by a measured launch # angle rather than falling back to club-type defaults. The UI badge reflects # that distinction, not a claim that carry itself was measured (PR #115 review #6). - provenance["carry"] = "measured" if shot.has_launch_angle else "estimated" + provenance["carry"] = provenance["vla"] if shot.club_speed_mph is not None and shot.club_speed_mph > 0: club_speed = float(shot.club_speed_mph) diff --git a/tests/test_sim_resolver.py b/tests/test_sim_resolver.py index dfb6a7345..d02eb04d6 100644 --- a/tests/test_sim_resolver.py +++ b/tests/test_sim_resolver.py @@ -122,3 +122,31 @@ def test_carry_falls_back_to_table_when_nothing_committed(): assert resolve_shot(shot, PlayerState()).carry_yards == pytest.approx( shot.estimated_carry_yards ) + + +def test_server_estimated_launch_angles_are_tagged_estimated(): + # server._ensure_user_facing_launch_angles fills missing angles before the + # sim fan-out and marks them with source "estimated". + shot = _shot( + launch_angle_vertical=11.0, launch_angle_vertical_source="estimated", + launch_angle_horizontal=0.0, launch_angle_horizontal_source="estimated", + ) + r = resolve_shot(shot, PlayerState()) + assert r.vla == 11.0 + assert r.hla == 0.0 + assert r.provenance["vla"] == "estimated" + assert r.provenance["hla"] == "estimated" + assert r.provenance["carry"] == "estimated" + + +def test_measured_vla_keeps_measured_carry_when_only_hla_is_estimated(): + # A vertical angle radar alone gives a measured VLA, and the server fills in + # the neutral 0.0 HLA. + shot = _shot( + launch_angle_vertical=12.0, launch_angle_vertical_source="radar", + launch_angle_horizontal=0.0, launch_angle_horizontal_source="estimated", + ) + r = resolve_shot(shot, PlayerState()) + assert r.provenance["vla"] == "measured" + assert r.provenance["hla"] == "estimated" + assert r.provenance["carry"] == "measured"