Data API
Performaar's Data API is a subset of the GraphQL API that offers a format-agnostic method of accessing recorded data, be it DIS, HLA, MAVLink or any other supported data type.
Usage
The Data API can be accessed using the data query on the GraphQL endpoint (see API Usage for more
details). When provided with the path to a completed Recording's debrief.yaml file the query will return a list of
entities and common properties for each entity, like position, orientation, relevant events and so on. Each property can
then be queried in a variety of units and, where relevant, coordinate systems.
For in-progress recordings, the watchData subscription can be used to get updated results at set intervals as new data
is recorded over time.
Like with the rest of the GraphQL API, reference documentation for the Data API can be found in the GraphiQL playground embedded into Performaar at /api/graphql.
Core Concepts
Entity
The Data API organizes raw data into entities. These can be platforms, munitions, lifeforms or anything else that would generally have the kinds of properties supported by the API. Events (e.g. fire, detonation, emissions etc.) are associated with the entity they originate from.
Top-level entity properties in the Data API (e.g. Display Name, Force ID, Color and so on) are derived from the data
where possible but can also be explicitly set using the updateEntityDetails mutation.
Source
Entities have a sources property which represents raw files from which contain the data for that particular entity.
The same entity could, for example, have positional and electronic warfare data presented as part of a DIS broadcast
while also having finer-grained telemetry being recorded from a flight dynamics model like JSBSIM. The end result would
be two files that both contain information about the same entity. In such cases, the updateEntityDetails mutation can
be used to provide mapping information which is then utilized by the Data API to consolidate all the data for each
entity from multiple files into the sources property.
Entity properties like position, orientation etc. are provided per source, though not all properties are guaranteed to exits for all sources.
Operations & Pipelines
The entityFilter input on the data query (and watchData subscription), and inputs on various properties under
sources (e.g. position, orientation etc.) can take a list of operations, collectively called a pipeline, to
perform complex calculations on the raw data before it is returned by the API.
Supported operations are listed below. As a note on terminology, a "list" is an array of multiple data points of a given type (e.g. a list of position coordinates) while a "single value" is one data point (e.g. a single position coordinate). For operations that take a list or value as input and return a list of value as output the underlying type does not change.
| Name | Description | Input | Output |
|---|---|---|---|
| Filter | Evaluate the described condition on each element in the input list, outputting only elements that pass the condition. | list | list |
| Aggregate | Combine the input list into a single value using the provided method | list | single value |
| Count | The number of items in the input list | list | integer |
| Check | Check if a value or list matches the given condition | single value or list | boolean (true or false) |
The pipeline input provided to any property is run in order over the data under that property. It is, therefore, important to ensure that the outputs of each operation match the inputs expected by the next operation. For example, Filtering then Aggregating is valid, but Aggregating first then Filtering will result in an error.
This reasoning also applies to the final values of each of the filterable properties, and is why they are represented by
a GraphQL Union Type. If the last element in the operations pipeline for the orientation property is a Check, then the
value returned by it will be a PipelineResultBoolean. A property with no pipeline returns a list of all the
data points.
Examples
Get all position data for each entity when its mean sea level altitude is above 1000 meters
query DataApi{
data(
debriefPath: "recording/debrief.yaml",
) {
displayName
sources {
id
position(pipeline:{
operations: [
{
filter: {
value: {
geodetic: {
altitudeMeanSeaLevel: {
gt: {
meters: 1000
}
}
}
}
}
},
]
}) {
... on PositionPipelineResultList {
list {
relativeTimestamp {
milliseconds
}
value {
geodetic {
latitude {
degrees
}
longitude {
degrees
}
altitude {
meanSeaLevel {
meters
}
}
}
}
}
}
}
}
}
}
Get the total quantity of munitions fired by each entity
query DataApi{
data(
debriefPath: "recording/debrief.yaml",
) {
displayName
sources {
file
id
events(
pipeline:{
operations: [
{
filter: {
value: {
eventType: {
eq: MUNITION_FIRE
}
}
}
},
{
aggregate: {
method: SUM
}
}
]
}
) {
... on EntityEventPipelineResultSingle {
single {
... on MunitionFireEvent {
munition {
quantity
}
}
}
}
}
}
}
}
Get the names of only the entities which have an airspeed greater than 1200 knots at any point in the data
query DataApi{
data(
debriefPath: "recording/debrief.yaml",
entityFilter: {
linearVelocity: {
operations: [
{
check: {
any:{
value: {
airSpeed: {
gt: {
knots: 1200
}
}
}
}
}
}
]
}
}
) {
displayName
}
}