Skip to content

Commit 9caf5c4

Browse files
Address comments
1 parent 0a8ad0e commit 9caf5c4

File tree

4 files changed

+118
-95
lines changed

4 files changed

+118
-95
lines changed

monitoring/monitorlib/geo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ def to_flight_planning_api(self) -> fp_api.Altitude:
208208
units=fp_api.AltitudeUnits.M,
209209
)
210210

211-
def to_w84_m(self):
211+
def to_w84_m(self) -> float:
212212
"""This altitude expressed in WGS84 meters, if possible to convert to it."""
213213
if self.reference != AltitudeDatum.W84:
214214
raise NotImplementedError(

monitoring/monitorlib/geotemporal.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,15 @@
1313
from uas_standards.interuss.automated_testing.scd.v1 import api as interuss_scd_api
1414

1515
from monitoring.monitorlib import geo
16-
from monitoring.monitorlib.geo import Altitude, Circle, LatLngPoint, Polygon, Volume3D
16+
from monitoring.monitorlib.geo import (
17+
Altitude,
18+
AltitudeDatum,
19+
Circle,
20+
DistanceUnits,
21+
LatLngPoint,
22+
Polygon,
23+
Volume3D,
24+
)
1725
from monitoring.monitorlib.temporal import (
1826
TestTime,
1927
TestTimeContext,
@@ -304,6 +312,26 @@ def time_end(self) -> Time | None:
304312
else None
305313
)
306314

315+
@property
316+
def altitude_lower(self) -> Altitude | None:
317+
return Altitude(
318+
value=min(v.volume.altitude_lower_wgs84_m() for v in self)
319+
if all(v.volume.altitude_lower_wgs84_m() is not None for v in self)
320+
else None,
321+
reference=AltitudeDatum.W84,
322+
units=DistanceUnits.M,
323+
)
324+
325+
@property
326+
def altitude_upper(self) -> Altitude | None:
327+
return Altitude(
328+
value=min(v.volume.altitude_upper_wgs84_m() for v in self)
329+
if all(v.volume.altitude_upper_wgs84_m() is not None for v in self)
330+
else None,
331+
reference=AltitudeDatum.W84,
332+
units=DistanceUnits.M,
333+
)
334+
307335
def offset_times(self, dt: timedelta) -> Volume4DCollection:
308336
return Volume4DCollection([v.offset_time(dt) for v in self])
309337

monitoring/uss_qualifier/scenarios/astm/utm/test_steps.py

Lines changed: 86 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -481,100 +481,95 @@ def _check_op_intent_details(
481481
)
482482

483483
with self._scenario.check(
484-
"Details 4D extents are within reference extents",
484+
"Operational intent details extents are contained within reference extents",
485485
[self._flight_planner.participant_id],
486486
) as check:
487-
all_volumes = oi_full.details.get("volumes", []) + oi_full.details.get(
488-
"off_nominal_volumes", []
487+
all_volumes = Volume4DCollection.from_f3548v21(
488+
oi_full.details.get("volumes", [])
489+
+ oi_full.details.get("off_nominal_volumes", [])
489490
)
490-
for v in all_volumes:
491-
# Time start check
492-
v_time_start = v.get("time_start")
493-
ref_time_start = oi_ref.get("time_start")
494-
if not v_time_start:
495-
if ref_time_start:
496-
check.record_failed(
497-
summary="Details volume starts before reference",
498-
details="A volume in the operational intent details has no start time (infinite past), but the operational intent reference specifies a start time.",
499-
query_timestamps=[oi_full_query.request.timestamp],
500-
)
501-
elif ref_time_start:
502-
if (
503-
v_time_start.value.datetime
504-
< ref_time_start.value.datetime - NUMERIC_PRECISION_TIME
505-
):
506-
check.record_failed(
507-
summary="Details volume starts before reference",
508-
details=f"A volume in the operational intent details starts at {v_time_start.value.datetime}, which is before the operational intent reference start time {ref_time_start.value.datetime}.",
509-
query_timestamps=[oi_full_query.request.timestamp],
510-
)
511-
# Time end check
512-
v_time_end = v.get("time_end")
513-
ref_time_end = oi_ref.get("time_end")
514-
if not v_time_end:
515-
if ref_time_end:
516-
check.record_failed(
517-
summary="Details volume ends after reference",
518-
details="A volume in the operational intent details has no end time (infinite future), but the operational intent reference specifies an end time.",
519-
query_timestamps=[oi_full_query.request.timestamp],
520-
)
521-
elif ref_time_end:
522-
if (
523-
v_time_end.value.datetime
524-
> ref_time_end.value.datetime + NUMERIC_PRECISION_TIME
525-
):
526-
check.record_failed(
527-
summary="Details volume ends after reference",
528-
details=f"A volume in the operational intent details ends at {v_time_end.value.datetime}, which is after the operational intent reference end time {ref_time_end.value.datetime}.",
529-
query_timestamps=[oi_full_query.request.timestamp],
530-
)
531-
# Altitude check (if reference specifies altitude, which it typically doesn't in F3548, but implemented defensively just in case)
532-
v_altitude_lower = (
533-
v.get("volume", {}).get("altitude_lower")
534-
if v.get("volume")
535-
else None
536-
)
537-
ref_altitude_lower = oi_ref.get("altitude_lower")
538-
if not v_altitude_lower:
539-
if ref_altitude_lower:
540-
check.record_failed(
541-
summary="Details volume lower altitude below reference",
542-
details="A volume in the operational intent details has no lower altitude bound (infinite downward), but the operational intent reference specifies a lower altitude.",
543-
query_timestamps=[oi_full_query.request.timestamp],
544-
)
545-
elif ref_altitude_lower:
546-
if (
547-
v_altitude_lower.value
548-
< ref_altitude_lower.value - NUMERIC_PRECISION_DISTANCE
549-
):
550-
check.record_failed(
551-
summary="Details volume lower altitude below reference",
552-
details=f"A volume in the operational intent details has lower altitude {v_altitude_lower.value}, which is below the operational intent reference lower altitude {ref_altitude_lower.value}.",
553-
query_timestamps=[oi_full_query.request.timestamp],
554-
)
555-
v_altitude_upper = (
556-
v.get("volume", {}).get("altitude_upper")
557-
if v.get("volume")
558-
else None
559-
)
560-
ref_altitude_upper = oi_ref.get("altitude_upper")
561-
if not v_altitude_upper:
562-
if ref_altitude_upper:
563-
check.record_failed(
564-
summary="Details volume upper altitude above reference",
565-
details="A volume in the operational intent details has no upper altitude bound (infinite upward), but the operational intent reference specifies an upper altitude.",
566-
query_timestamps=[oi_full_query.request.timestamp],
567-
)
568-
elif ref_altitude_upper:
569-
if (
570-
v_altitude_upper.value
571-
> ref_altitude_upper.value + NUMERIC_PRECISION_DISTANCE
572-
):
573-
check.record_failed(
574-
summary="Details volume upper altitude above reference",
575-
details=f"A volume in the operational intent details has upper altitude {v_altitude_upper.value}, which is above the operational intent reference upper altitude {ref_altitude_upper.value}.",
576-
query_timestamps=[oi_full_query.request.timestamp],
577-
)
491+
492+
# Time start check
493+
v_time_start = all_volumes.time_start
494+
ref_time_start = oi_ref.get("time_start")
495+
if not v_time_start:
496+
if ref_time_start:
497+
check.record_failed(
498+
summary="Details volume starts before reference",
499+
details="A volume in the operational intent details has no start time (infinite past), but the operational intent reference specifies a start time.",
500+
query_timestamps=[oi_full_query.request.timestamp],
501+
)
502+
elif ref_time_start:
503+
if (
504+
v_time_start.datetime
505+
< ref_time_start.value.datetime - NUMERIC_PRECISION_TIME
506+
):
507+
check.record_failed(
508+
summary="Details volume starts before reference",
509+
details=f"A volume in the operational intent details starts at {v_time_start}, which is before the operational intent reference start time {ref_time_start.value.datetime}.",
510+
query_timestamps=[oi_full_query.request.timestamp],
511+
)
512+
513+
# Time end check
514+
v_time_end = all_volumes.time_end
515+
ref_time_end = oi_ref.get("time_end")
516+
if not v_time_end:
517+
if ref_time_end:
518+
check.record_failed(
519+
summary="Details volume ends after reference",
520+
details="A volume in the operational intent details has no end time (infinite future), but the operational intent reference specifies an end time.",
521+
query_timestamps=[oi_full_query.request.timestamp],
522+
)
523+
elif ref_time_end:
524+
if (
525+
v_time_end.datetime
526+
> ref_time_end.value.datetime + NUMERIC_PRECISION_TIME
527+
):
528+
check.record_failed(
529+
summary="Details volume ends after reference",
530+
details=f"A volume in the operational intent details ends at {v_time_end.datetime}, which is after the operational intent reference end time {ref_time_end.value.datetime}.",
531+
query_timestamps=[oi_full_query.request.timestamp],
532+
)
533+
534+
# Altitude check (if reference specifies altitude, which it typically doesn't in F3548, but implemented defensively just in case)
535+
v_altitude_lower = all_volumes.altitude_lower
536+
ref_altitude_lower = oi_ref.get("altitude_lower")
537+
if not v_altitude_lower:
538+
if ref_altitude_lower:
539+
check.record_failed(
540+
summary="Details volume lower altitude below reference",
541+
details="A volume in the operational intent details has no lower altitude bound (infinite downward), but the operational intent reference specifies a lower altitude.",
542+
query_timestamps=[oi_full_query.request.timestamp],
543+
)
544+
elif ref_altitude_lower:
545+
if (
546+
v_altitude_lower.value
547+
< ref_altitude_lower.value - NUMERIC_PRECISION_DISTANCE
548+
):
549+
check.record_failed(
550+
summary="Details volume lower altitude below reference",
551+
details=f"A volume in the operational intent details has lower altitude {v_altitude_lower.value}, which is below the operational intent reference lower altitude {ref_altitude_lower.value}.",
552+
query_timestamps=[oi_full_query.request.timestamp],
553+
)
554+
v_altitude_upper = all_volumes.altitude_upper
555+
ref_altitude_upper = oi_ref.get("altitude_upper")
556+
if not v_altitude_upper:
557+
if ref_altitude_upper:
558+
check.record_failed(
559+
summary="Details volume upper altitude above reference",
560+
details="A volume in the operational intent details has no upper altitude bound (infinite upward), but the operational intent reference specifies an upper altitude.",
561+
query_timestamps=[oi_full_query.request.timestamp],
562+
)
563+
elif ref_altitude_upper:
564+
if (
565+
v_altitude_upper.value
566+
> ref_altitude_upper.value + NUMERIC_PRECISION_DISTANCE
567+
):
568+
check.record_failed(
569+
summary="Details volume upper altitude above reference",
570+
details=f"A volume in the operational intent details has upper altitude {v_altitude_upper.value}, which is above the operational intent reference upper altitude {ref_altitude_upper.value}.",
571+
query_timestamps=[oi_full_query.request.timestamp],
572+
)
578573

579574
with self._scenario.check(
580575
"Off-nominal volumes", [self._flight_planner.participant_id]

monitoring/uss_qualifier/scenarios/astm/utm/validate_shared_operational_intent.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ If any of the values in the operational intent reference reported by the USS do
3737

3838
If the operational intent details reported by the USS do not match the user's flight intent, this check will fail per **[interuss.automated_testing.flight_planning.ExpectedBehavior](../../../requirements/interuss/automated_testing/flight_planning.md)** and **[astm.f3548.v21.OPIN0025](../../../requirements/astm/f3548/v21.md)**.
3939

40-
## 🛑 Details 4D extents are within reference extents check
40+
## 🛑 Operational intent details extents are contained within reference extents check
4141

42-
If the 4D extents (start time, end time, and altitude if specified) of any of the detailed operational intent volumes are not fully contained within the 4D extents of the operational intent reference, this check will fail per **[astm.f3548.v21.USS0105,1](../../../requirements/astm/f3548/v21.md)**.
42+
If the 4D extents (start time, end time, and altitude if specified) of any of the operational intent detail volumes are not fully contained within the 4D extents of the operational intent reference, this check will fail per **[astm.f3548.v21.USS0105,1](../../../requirements/astm/f3548/v21.md)**.
4343

4444
## ⚠️ Off-nominal volumes check
4545

0 commit comments

Comments
 (0)