Entities |
Since the information available in modern data sources can vary from source to source, Tracking Library does not define a single concrete class to contain that data. Instead, it defines many small interfaces that each contain properties commonly shared among data sources. Any class implementing one or more of these interfaces is referred to as an entity. Examples of typical entities are ground vehicles, ships, aircraft, and satellites. To define an entity, the developer chooses which interfaces represent data available in their data source and then implements those interfaces in a new class. Any extra information not part of a built-in interface can be added to the class as additional properties. For this reason, most Tracking Library types are generic types which take the entity type as their generic parameter and are constrained on the interfaces they require. This allows for a concrete entity implementation which closely matches the data being processed, while the interfaces provide the means to connect those objects to the functionality requiring a specific piece of data.
Note |
---|
The functionality described in this topic requires a license for the Tracking Library. |
Interface | Inherited From | Properties |
---|---|---|
EntityIdentifier (get) | ||
LastUpdate (get) | ||
Position (get) | ||
Velocity (get) | ||
Acceleration (get) | ||
Orientation (get) | ||
RotationalVelocity (get) | ||
RotationalAcceleration (get) | ||
FieldOfView (get) |
The reference documentation for these interfaces have more details about the required data they must provide. IEntityIdentifier should return a unique identifier for each entity in the data source and IEntityLastUpdate should always reflect the last time that the entity was modified. The example entity implementation in the code sample below contains seven properties, but only three of them: Callsign, Affiliation, and SymbolId are unique to this type of entity. The rest are defined using the entity interfaces.
public class ExampleEntity implements IEntityIdentifier, IEntityLastUpdate, IEntityVelocity, IEntityOrientation { public static void registerEntityClass() { EntityDescriptor.setDefault(new TypeLiteral<ExampleEntity>() {}, new ExampleEntityDescriptor()); } public ExampleEntity(TransactionContext context, String callSign) { if (context == null) { throw new ArgumentNullException("context"); } if (callSign == null) { throw new ArgumentNullException("callSign"); } m_callSign = callSign; m_lastUpdate = new TransactedProperty<>(context, this); m_position = new TransactedProperty<>(context, this); m_velocity = new TransactedProperty<>(context, this); m_orientation = new TransactedProperty<>(context, this); m_force = new TransactedProperty<>(context, this, Force.UNKNOWN); m_symbolID = new TransactedProperty<>(context, this); } @Override public final Object getEntityIdentifier() { return m_callSign; } @Override public final TransactedProperty<JulianDate> getLastUpdate() { return m_lastUpdate; } @Override public final TransactedProperty<Cartesian> getPosition() { return m_position; } @Override public final TransactedProperty<Cartesian> getVelocity() { return m_velocity; } @Override public final TransactedProperty<UnitQuaternion> getOrientation() { return m_orientation; } public final TransactedProperty<String> getSymbolId() { return m_symbolID; } public final String getCallSign() { return m_callSign; } public final TransactedProperty<Force> getAffiliation() { return m_force; } private final String m_callSign; private final TransactedProperty<Cartesian> m_position; private final TransactedProperty<Cartesian> m_velocity; private final TransactedProperty<Force> m_force; private final TransactedProperty<JulianDate> m_lastUpdate; private final TransactedProperty<String> m_symbolID; private final TransactedProperty<UnitQuaternion> m_orientation; }
IEntityPosition is not directly implemented here, since it inherits from IEntityVelocity, which is implemented. With this architecture, only code that needs to handle the Callsign, Affiliation or SymbolId properties (those that are not part of a built-in interface) needs to know about the specific ExampleEntity type. All other information can be accessed generically through the interfaces. Entities are designed for use with Transactions, so all settable properties should be TransactedProperty<T> instances. This allows entities to be handled in a thread-safe manner using transactions, enabling use of all available processors for analysis and visualization.
After defining the entity type itself, there are still more details to consider. While IEntityPosition specifies the actual position of the entity, it does not specify the ReferenceFrame in which the position is defined. Information such as this is shared across all instances of a specific entity type, rather than being defined on a per-entity basis like position. This information is defined using an EntityDescriptor<TEntity>. A descriptor for ExampleEntity is shown in the code sample below:
public class ExampleEntityDescriptor extends EntityDescriptor<ExampleEntity> implements IEntityPositionDescriptor, IEntityOrientationDescriptor { @Override public final ReferenceFrame getPositionReferenceFrame() { return CentralBodiesFacet.getFromContext().getEarth().getFixedFrame(); } @Override public final Axes getOrientationAxes() { return getPositionReferenceFrame().getAxes(); } }
Like entities, descriptors implement a set of standard interfaces:
Interface | Properties & Methods |
---|---|
PositionReferenceFrame (get) | |
OrientationAxes (get) | |
Archivers (get), Schema (get), createEntity |
IEntityArchiveDescriptor<TEntity> is discussed in detail in the Archiving topic. Once an EntityDescriptor<TEntity> is defined, an instance of it must be registered, as shown in the following code sample:
EntityDescriptor.setDefault(new TypeLiteral<ExampleEntity>() {}, new ExampleEntityDescriptor());
Once registered, any class that needs access to any of the shared entity properties, such as the position's PositionReferenceFrame (get), can access it as shown in the following code sample:
// get entity descriptor for ExampleEntity EntityDescriptor<ExampleEntity> descriptor = EntityDescriptor.getDefault(new TypeLiteral<ExampleEntity>() {}); // get the reference frame from the descriptor ReferenceFrame frame = descriptor.get(new TypeLiteral<IEntityPositionDescriptor>() {}).getPositionReferenceFrame();
Typically, data sources provide information about a number of different entities. EntitySet<TEntity> is a built-in collection type for entities, which is used throughout the library. EntitySet<TEntity> is transaction-aware, making it thread-safe when used properly with transactions. It also provides events that are raised when an entity is added, changed or removed. Entities stored in an EntitySet<TEntity> must implement the IEntityIdentifier interface, to ensure that each entity exists only once in the set.
Classes that parse a data source and store it into an entity set usually do so with a Lookup, Create, Modify approach, as shown in the below code sample:
context.doTransactionally(Action1.of(transaction -> { ExampleEntity entity = entities.getEntityById(transaction, entityID); if (entity == null) { entity = new ExampleEntity(context, entityID); entities.add(transaction, entity); } entity.getPosition().setValue(transaction, updatedPosition); entity.getOrientation().setValue(transaction, updatedOrientation); }));
An EntitySet<TEntity> can be used by filters, described in the Filtering topic, and with visualizers, described in the Visualization with Insight3D® topic, to perform analysis and visualization.