Click or drag to resize

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 amongst data sources. Any class implementing one or more of these interfaces is referred to as an entity. Examples of typical entities are aircraft, ground vehicles, 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 also 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 ingested, while the interfaces provide the means to connect those objects to the provided Tracking Library functionality which requires a specific piece of data.

Note Note

The functionality described in this topic requires a license for the Tracking Library.

Entity Interfaces

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.

Java
public enum Force implements Enumeration {
    FRIENDLY(0),
    HOSTILE(1),
    NEUTRAL(2),
    UNKNOWN(3);
    private final int value;

    Force(int value) {
        this.value = value;
    }

    /**
     * Get the numeric value associated with this enum constant.
     * @return A numeric value.
     */
    @Override
    public int getValue() {
        return value;
    }

    /**
     * Get the enum constant that is associated with the given numeric value.
     * @return The enum constant associated with value.
     * @param value a numeric value.
     */
    public static Force getFromValue(int value) {
        switch (value) {
        case 0:
            return FRIENDLY;
        case 1:
            return HOSTILE;
        case 2:
            return NEUTRAL;
        case 3:
            return UNKNOWN;
        default:
            throw new IllegalArgumentException("Undefined enum value.");
        }
    }

    /**
     * Get the enum constant that is considered to be the default.
     * @return The default enum constant.
     */
    public static Force getDefault() {
        return FRIENDLY;
    }
}

public static 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 for entities to be handled in a thread-safe manner using transactions, which allows use of all available processors for analysis and visualization.

Entity Descriptors

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 a EntityDescriptor<TEntity>. A descriptor for ExampleEntity is shown in the code sample below:

Java
public static 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

IEntityPositionDescriptor

PositionReferenceFrame (get)

IEntityOrientationDescriptor

OrientationAxes (get)

IEntityArchiveDescriptor<TEntity>

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:

Java
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:

Java
// 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();
Entity Sets

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:

Java
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.