diff --git a/monitoring/monitorlib/geo.py b/monitoring/monitorlib/geo.py index 5d5bc3974d..098b4a4112 100644 --- a/monitoring/monitorlib/geo.py +++ b/monitoring/monitorlib/geo.py @@ -5,6 +5,7 @@ from enum import Enum import numpy as np +import pyproj import s2sphere import shapely.geometry from implicitdict import ImplicitDict @@ -696,6 +697,33 @@ def egm96_geoid_offset(p: s2sphere.LatLng) -> float: return _egm96.ev(-lat, lng) +def egm2008_geoid_offset(p: s2sphere.LatLng) -> float: + """Estimate the EGM2008 geoid height above the WGS84 ellipsoid. + + Args: + p: Point where offset should be estimated. + + Returns: Meters above WGS84 ellipsoid of the EGM2008 geoid at p. + """ + + if not pyproj.network.is_network_enabled(): # pyright:ignore[reportAttributeAccessIssue] + raise Exception(""" +To enable EGM2008 conversions, you must allow pyproj to download files to do the conversion. For that, please set PROJ_NETWORK=TRUE in your environment variables. + +Please ensure this comply with your policies, and avoid multiple downloads by mounting the directoy '/root/.local/share/proj/' to a docker volume. +""") + + transformer = pyproj.Transformer.from_crs( + "EPSG:4979", # WGS 84 -- 3D + "EPSG:4326+3855", # WGS 84 (2D) + EGM2008 height (vertical) + always_xy=True, + ) + + _, _, ortho_height = transformer.transform(p.lng().degrees, p.lat().degrees, 0) + + return -ortho_height + + def center_of_mass(in_points: list[LatLng]) -> LatLng: """Compute the center of mass of a polygon defined by a list of points.""" if len(in_points) == 0: diff --git a/monitoring/uss_qualifier/run_locally.sh b/monitoring/uss_qualifier/run_locally.sh index 206673cbf5..b614338344 100755 --- a/monitoring/uss_qualifier/run_locally.sh +++ b/monitoring/uss_qualifier/run_locally.sh @@ -78,6 +78,7 @@ docker run ${docker_args} --name uss_qualifier \ -e PYTHONBUFFERED=1 \ -e AUTH_SPEC=${AUTH_SPEC} \ -e AUTH_SPEC_2=${AUTH_SPEC_2} \ + -e PROJ_NETWORK \ ${PRIVATE_REPOS_ENV_FLAG} \ -e MONITORING_GITHUB_ROOT=${MONITORING_GITHUB_ROOT:-} \ -v "$(pwd)/$OUTPUT_DIR:/app/$OUTPUT_DIR" \ diff --git a/monitoring/uss_qualifier/scenarios/uspace/netrid/msl.py b/monitoring/uss_qualifier/scenarios/uspace/netrid/msl.py index ccc9d211fa..3e3774f216 100644 --- a/monitoring/uss_qualifier/scenarios/uspace/netrid/msl.py +++ b/monitoring/uss_qualifier/scenarios/uspace/netrid/msl.py @@ -6,7 +6,7 @@ ) from monitoring.monitorlib.fetch import Query, QueryType -from monitoring.monitorlib.geo import egm96_geoid_offset +from monitoring.monitorlib.geo import egm96_geoid_offset, egm2008_geoid_offset from monitoring.uss_qualifier.configurations.configuration import ParticipantID from monitoring.uss_qualifier.resources.netrid import NetRIDObserversResource from monitoring.uss_qualifier.scenarios.astm.netrid.common.nominal_behavior import ( @@ -110,12 +110,31 @@ def _evaluate_msl_altitude(self, queries: list[Query]): and flight.most_recent_position is not None ): with self.check("MSL altitude is correct", participant_id) as check: - geoid_offset = egm96_geoid_offset( - s2sphere.LatLng.from_degrees( - flight.most_recent_position.lat, - flight.most_recent_position.lng, + if ( + flight.most_recent_position.msl_alt.reference_datum + == AltitudeReference.EGM96 + ): + geoid_offset = egm96_geoid_offset( + s2sphere.LatLng.from_degrees( + flight.most_recent_position.lat, + flight.most_recent_position.lng, + ) ) - ) + elif ( + flight.most_recent_position.msl_alt.reference_datum + == AltitudeReference.EGM2008 + ): + geoid_offset = egm2008_geoid_offset( + s2sphere.LatLng.from_degrees( + flight.most_recent_position.lat, + flight.most_recent_position.lng, + ) + ) + else: + raise Exception( + "Internal error: ACCEPTABLE_DATUMS of netrid/msl.py don't match the datum we can check" + ) + expected_msl_alt = ( flight.most_recent_position.alt - geoid_offset )