Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[uss_qualifier/rid] NET0470 OperatorID, UAS ID, Operator Location, Operator Altitude, Operational Status, Current Position, Height, Timestamp, Track, Speed checks #150

Merged
merged 40 commits into from
Sep 20, 2023
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
237cb29
[rid] NET0470 Operator ID
barroco Jul 28, 2023
43a3995
[rid] NET0470 UAS ID, Operator Location, Operator Altitude and Operat…
barroco Jul 28, 2023
02d1209
Fix misused argument
barroco Aug 4, 2023
4b90cb0
format
barroco Aug 4, 2023
23eb42e
Reorganize version checks in common_dictionary
barroco Aug 4, 2023
dd73352
Add todo for location altitude to address it separately
barroco Aug 4, 2023
3e5e18c
Format and fix type of observed_details
barroco Aug 4, 2023
106db1e
Capture TODO
barroco Aug 4, 2023
4fd0a8b
Implement new observation api fields for mock details responses
barroco Sep 13, 2023
65d5a5e
Fix unit tests and format
barroco Sep 13, 2023
d68ae2d
Return from mock riddp the operational_status
barroco Sep 13, 2023
20193f5
Replace monitorlib observation_api by uas_standards
barroco Sep 13, 2023
09520f7
Rewording
barroco Sep 13, 2023
b89368e
Format
barroco Sep 13, 2023
e956133
Add check for astm.f3411.v22a.NET0470,Table1,5
barroco Sep 13, 2023
2f54ba1
Add check for astm.f3411.v22a.NET0470,Table1,20
barroco Sep 13, 2023
a0c1659
Add check for astm.f3411.v22a.NET0470,Table1,20 resolution
barroco Sep 13, 2023
7989eb9
Add check for astm.f3411.v22a.NET0470,Table1,19 track and use injecte…
barroco Sep 14, 2023
531fb03
Add check for astm.f3411.v22a.NET0470,Table1,14-15 height
barroco Sep 14, 2023
53be4af
Add check for astm.f3411.v22a.NET0470,Table1,10-11 current position
barroco Sep 14, 2023
8361656
Fix import
barroco Sep 14, 2023
903858d
format
barroco Sep 14, 2023
2e84910
Rename to _evaluate_arbitrary_uas_id and skip some tests if not v22a
barroco Sep 14, 2023
0c29941
Clean up
barroco Sep 14, 2023
cbb0bc1
format
barroco Sep 14, 2023
ac1353a
Update doc
barroco Sep 14, 2023
544f696
Update participants
barroco Sep 14, 2023
9ec0318
Prevent exception when number is not round
barroco Sep 14, 2023
7a72b2d
Merge branch 'main' into NET0470-operator-id
barroco Sep 14, 2023
d1caac8
Merge branch 'main' into NET0470-operator-id
mickmis Sep 19, 2023
6cae1fd
updates
mickmis Sep 19, 2023
69fa439
various fixes
mickmis Sep 19, 2023
135759f
Merge remote-tracking branch 'interuss/main' into NET0470-operator-id
mickmis Sep 19, 2023
ba022a5
fixes
mickmis Sep 19, 2023
2277e12
align
mickmis Sep 19, 2023
d5afadc
fixes
mickmis Sep 20, 2023
f6f08ea
cleanup
mickmis Sep 20, 2023
f3064cd
remove resolution limit for RID DP
mickmis Sep 20, 2023
43f8319
remove resolution checks
mickmis Sep 20, 2023
2082388
fix python test
mickmis Sep 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion monitoring/mock_uss/riddp/clustering.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
from implicitdict import ImplicitDict

from monitoring.monitorlib.rid import RIDVersion
from monitoring.monitorlib.rid_automated_testing import observation_api
from uas_standards.interuss.automated_testing.rid.v1 import (
observation as observation_api,
)


class Point(object):
Expand Down
67 changes: 58 additions & 9 deletions monitoring/mock_uss/riddp/routes_observation.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
from typing import Dict, List, Optional, Tuple

import arrow
import flask
from loguru import logger
import s2sphere
from uas_standards.astm.f3411.v19.api import ErrorResponse
from uas_standards.astm.f3411.v19.constants import Scope

from uas_standards.astm.f3411.v22a.constants import (
MaxSpeed,
MinHeightResolution,
MinTrackDirectionResolution,
)
from monitoring.monitorlib import geo
from monitoring.monitorlib.fetch import rid as fetch
from monitoring.monitorlib.fetch.rid import Flight, FetchedISAs
from monitoring.monitorlib.fetch.rid import Flight, FetchedISAs, Position
from monitoring.monitorlib.rid import RIDVersion
from monitoring.monitorlib.rid_automated_testing import observation_api
from uas_standards.interuss.automated_testing.rid.v1 import (
observation as observation_api,
)
from monitoring.mock_uss import webapp
from monitoring.mock_uss.auth import requires_scope
from . import clustering, database, utm_client
from .behavior import DisplayProviderBehavior
from .config import KEY_RID_VERSION
from .database import db
from monitoring.monitorlib.formatting import _limit_resolution


def _make_flight_observation(
Expand Down Expand Up @@ -55,10 +61,27 @@ def _make_flight_observation(
paths.append(current_path)

p = flight.most_recent_position
timestamp = p.time.replace(
microsecond=round(
_limit_resolution(p.time.microsecond, pow(10, 5))
) # Microsecond is very strict on the expected size and format, thus the round.
)
current_state = observation_api.CurrentState(
timestamp=timestamp.isoformat(),
operational_status=flight.operational_status,
track=_limit_resolution(flight.track, MinTrackDirectionResolution),
speed=_limit_resolution(flight.speed, MaxSpeed),
)
h = p.get("height")
if h:
h.distance = _limit_resolution(h.distance, MinHeightResolution)
return observation_api.Flight(
id=flight.id,
most_recent_position=observation_api.Position(lat=p.lat, lng=p.lng, alt=p.alt),
most_recent_position=observation_api.Position(
lat=p.lat, lng=p.lng, alt=p.alt, height=h
),
recent_paths=[observation_api.Path(positions=path) for path in paths],
current_state=current_state,
)


Expand Down Expand Up @@ -159,9 +182,35 @@ def riddp_display_data() -> Tuple[str, int]:
@requires_scope([Scope.Read])
def riddp_flight_details(flight_id: str) -> Tuple[str, int]:
"""Implements get flight details endpoint per automated testing API."""

tx = db.value
if flight_id not in tx.flights:
return 'Flight "{}" not found'.format(flight_id), 404
flight_info = tx.flights.get(flight_id)
if not flight_info:
return f'Flight "{flight_id}" not found', 404

return flask.jsonify(observation_api.GetDetailsResponse())
rid_version: RIDVersion = webapp.config[KEY_RID_VERSION]
flight_details = fetch.flight_details(
flight_info.flights_url, flight_id, True, rid_version, utm_client
)
if rid_version == RIDVersion.f3411_19:
# TODO: Implement details for F3411-19
return flask.jsonify(
observation_api.GetDetailsResponse(operator=observation_api.Operator())
)
elif rid_version == RIDVersion.f3411_22a:
details = flight_details.details
result = observation_api.GetDetailsResponse(
operator=observation_api.Operator(
id=details.operator_id,
location=details.operator_location.position,
altitude=observation_api.OperatorAltitude(
altitude=details.operator_location.get("altitude"),
altitude_type=details.operator_location.get("altitude_type"),
),
),
uas=observation_api.UAS(
id=details.arbitrary_uas_id,
),
)
return flask.jsonify(result)
else:
return f"Support for RID version {rid_version} not yet implemented", 501
99 changes: 95 additions & 4 deletions monitoring/monitorlib/fetch/rid.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import uas_standards.astm.f3411.v22a.api
import uas_standards.astm.f3411.v22a.constants
import yaml
from uas_standards.astm.f3411.v22a.api import RIDHeight
from yaml.representer import Representer

from monitoring.monitorlib import fetch, rid_v1, rid_v2, geo
Expand Down Expand Up @@ -149,17 +150,21 @@ class Position(ImplicitDict):
time: datetime.datetime
"""Timestamp for the position."""

height: Optional[RIDHeight]

@staticmethod
def from_v19_rid_aircraft_position(
p: v19.api.RIDAircraftPosition, t: v19.api.StringBasedDateTime
) -> Position:
return Position(lat=p.lat, lng=p.lng, alt=p.alt, time=t.datetime)
return Position(lat=p.lat, lng=p.lng, alt=p.alt, time=t.datetime, height=None)

@staticmethod
def from_v22a_rid_aircraft_position(
p: v22a.api.RIDAircraftPosition, t: v22a.api.StringBasedDateTime
) -> Position:
return Position(lat=p.lat, lng=p.lng, alt=p.alt, time=t.datetime)
return Position(
lat=p.lat, lng=p.lng, alt=p.alt, time=t.datetime, height=p.get("height")
)


class Flight(ImplicitDict):
Expand Down Expand Up @@ -211,7 +216,7 @@ def most_recent_position(
)
else:
raise NotImplementedError(
f"Cannot retrieve most recent position using RID version {self.rid_version}"
f"Cannot retrieve most_recent_position using RID version {self.rid_version}"
)
else:
return None
Expand All @@ -230,7 +235,42 @@ def recent_positions(self) -> List[Position]:
]
else:
raise NotImplementedError(
f"Cannot retrieve recent positions using RID version {self.rid_version}"
f"Cannot retrieve recent_positions using RID version {self.rid_version}"
)

@property
def operational_status(self) -> v22a.api.RIDOperationalStatus:
mickmis marked this conversation as resolved.
Show resolved Hide resolved
if self.rid_version == RIDVersion.f3411_19:
return v22a.api.RIDOperationalStatus(
self.v19_value.current_state.operational_status
)
elif self.rid_version == RIDVersion.f3411_22a:
return self.v22a_value.current_state.operational_status
else:
raise NotImplementedError(
f"Cannot retrieve operational_status using RID version {self.rid_version}"
)

@property
def track(self):
mickmis marked this conversation as resolved.
Show resolved Hide resolved
if self.rid_version == RIDVersion.f3411_19:
return self.v19_value.current_state.track
elif self.rid_version == RIDVersion.f3411_22a:
return self.v22a_value.current_state.track
else:
raise NotImplementedError(
f"Cannot retrieve track using RID version {self.rid_version}"
)

@property
def speed(self):
mickmis marked this conversation as resolved.
Show resolved Hide resolved
if self.rid_version == RIDVersion.f3411_19:
return self.v19_value.current_state.speed
elif self.rid_version == RIDVersion.f3411_22a:
return self.v22a_value.current_state.speed
else:
raise NotImplementedError(
f"Cannot retrieve speed using RID version {self.rid_version}"
)

def errors(self) -> List[str]:
Expand Down Expand Up @@ -321,6 +361,57 @@ def raw(
def id(self) -> str:
return self.raw.id

@property
def operator_id(self) -> str:
if self.rid_version == RIDVersion.f3411_19:
return self.v19_value.operator_id
elif self.rid_version == RIDVersion.f3411_22a:
return self.v22a_value.operator_id
else:
raise NotImplementedError(
f"Cannot retrieve operator_id using RID version {self.rid_version}"
)

@property
def arbitrary_uas_id(self) -> Optional[str]:
"""Returns a UAS id as a plain string without type hint.
If multiple are provided:
For v19, registration_number is returned if set, else it falls back to the serial_number.
For v20, the order of ASTM F3411-v19 Table 1 is used.
mickmis marked this conversation as resolved.
Show resolved Hide resolved
If no match, it returns None.
"""
if self.rid_version == RIDVersion.f3411_19:
registration_number = self.v19_value.registration_number
if registration_number:
return registration_number
else:
return self.v19_value.serial_number
elif self.rid_version == RIDVersion.f3411_22a:
uas_id = self.v22a_value.uas_id
if uas_id.serial_number:
return uas_id.serial_number
elif uas_id.registration_id:
return uas_id.registration_id
elif uas_id.utm_id:
return uas_id.utm_id
elif uas_id.specific_session_id:
return uas_id.specific_session_id
else:
raise NotImplementedError(
f"Cannot retrieve plain_uas_id using RID version {self.rid_version}"
)

@property
def operator_location(self) -> v22a.api.OperatorLocation:
mickmis marked this conversation as resolved.
Show resolved Hide resolved
if self.rid_version == RIDVersion.f3411_19:
return v22a.api.OperatorLocation(position=self.v19_value.operator_location)
elif self.rid_version == RIDVersion.f3411_22a:
return self.v22a_value.operator_location
else:
raise NotImplementedError(
f"Cannot retrieve operator_location using RID version {self.rid_version}"
)


class Subscription(ImplicitDict):
"""Version-independent representation of a F3411 subscription."""
Expand Down
5 changes: 5 additions & 0 deletions monitoring/monitorlib/formatting.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,8 @@ def make_datetime(t) -> datetime.datetime:
return arrow.get(t).datetime
else:
raise ValueError("Could not convert {} to datetime".format(str(type(t))))


def _limit_resolution(value: float, resolution: float):
mickmis marked this conversation as resolved.
Show resolved Hide resolved
"""Change resolution of a value"""
return round(value / resolution) * resolution
6 changes: 6 additions & 0 deletions monitoring/monitorlib/geo.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ class Altitude(ImplicitDict):
reference: AltitudeDatum
units: DistanceUnits

@staticmethod
def w84m(value: Optional[float]):
mickmis marked this conversation as resolved.
Show resolved Hide resolved
if not value:
return None
return Altitude(value=value, reference=AltitudeDatum.W84, units=DistanceUnits.M)


class Volume3D(ImplicitDict):
outline_circle: Optional[Circle] = None
Expand Down
38 changes: 1 addition & 37 deletions monitoring/monitorlib/rid_automated_testing/observation_api.py
Original file line number Diff line number Diff line change
@@ -1,37 +1 @@
from typing import List, Optional

from implicitdict import ImplicitDict


# Mirrors of types defined in remote ID automated testing observation API


class Position(ImplicitDict):
lat: float
lng: float
alt: Optional[float]


class Path(ImplicitDict):
positions: List[Position]


class Cluster(ImplicitDict):
corners: List[Position]
area_sqm: float
number_of_flights: int


class Flight(ImplicitDict):
id: str
most_recent_position: Optional[Position]
recent_paths: Optional[List[Path]]


class GetDetailsResponse(ImplicitDict):
pass


class GetDisplayDataResponse(ImplicitDict):
flights: List[Flight] = []
clusters: List[Cluster] = []
# Replaced by uas_standards package
7 changes: 5 additions & 2 deletions monitoring/uss_qualifier/resources/netrid/observers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
from monitoring.monitorlib.fetch import QueryType
from monitoring.monitorlib.infrastructure import UTMClientSession
from monitoring.monitorlib.rid import RIDVersion
from monitoring.monitorlib.rid_automated_testing import observation_api
from uas_standards.interuss.automated_testing.rid.v1 import (
observation as observation_api,
)
from monitoring.uss_qualifier.resources.resource import Resource
from monitoring.uss_qualifier.resources.communications import AuthAdapterResource

Expand Down Expand Up @@ -85,7 +87,8 @@ def observe_flight_details(
if query.status_code == 200
else None
)
except ValueError:
except ValueError as e:
logger.error("Error parsing observation details response: {}", e)
result = None
return result, query

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def generate_operation_description(self):
return self.random.choice(operation_description)

def generate_operator_location(self, centroid):
# TODO: Inject operator location altitude
operator_location = LatLngPoint(lat=centroid.y, lng=centroid.x)
return operator_location

Expand Down
Loading