Introduction

The package com.luciad.format.asdi provides the ability to decode and display ASDI data. It enables to decode ASDI data from a file or from a live stream. It enables to replay ASDI data from a file as if it came from a live stream. Finally it offers access to the parsed messages directly, offering fine-grained control.

The FAA provides airlines and other aviation-related organizations with access to near real-time air traffic data from the National Airspace System (NAS) through the ASDI feed of the Enhanced Traffic Management System (ETMS). More information on ASDI can be obtained from http://www.fly.faa.gov/ASDI/asdi.html.

About this guide

This book is a guide for developers who want to use ASDI data in a LuciadLightspeed application. It explains how data can be loaded and accessed.

The LuciadLightspeed ASDI functionality depends on the LuciadLightspeed Real-time Engine component. The documentation of the Real-time Engine component is considered prerequisite to reading this manual. Knowledge about air-traffic control is not required, but can be helpful.

Content overview

ASDI data modeling

Structure of ASDI data

The FAA provides members of the aviation industry with access to near real-time air traffic data from the National Airspace System (NAS) through the ASDI feed generated by the Enhanced Traffic Management System (ETMS).

An ASDI feed may contain two types of messages :

  • NAS messages : messages generated by the NAS and passed by the ETMS.

  • ETMS messages : messages generated by the ETMS.

Following NAS messages can occur in an ASDI feed :

  • TZ : Flow Control Track/Flight Data Block Information messages are used to provide a position update along with other information.

  • FZ : Flight Plan Information Messages contain flight plan data.

  • AF : Flow Control Amendment Information messages provide revised flight plan data whenever a flight plan is amended.

  • DZ : Departure messages are transmitted for all initially activated flight plans.

  • UZ : Flow Control Update Information messages are used to provide current flight plan information on active flights that enter an ARTCC.

  • AZ : Flow Control Arrival Information messages contain arrival data for all arriving flights.

  • RZ : Flow Control Cancellation messages are used to provide cancellation data for all flight plans.

Following ETMS messages can occur in an ASDI feed :

  • TO : TO messages are generated by the ETMS when Oceanic position reports are received via the ARINC network.

  • RT : The purpose of RT messages is to provide data from ETMS that is not otherwise available, for instance they contain predictions of the wheels-up and wheels-down times for a flight.

Each message is divided into fields. Fields can contain a simple value or can contain more complex data.

All messages in the ASDI feed are framed with a header containing some extra fields :

  • a sequence number : a number indicating the order in which messages were sent.

  • a time : the timestamp assigned to the message when it was received or generated by the ETMS.

  • a facility identifier : the facility that generated the message.

Not supported :

  • BZ : NAS Flow Control Beacon Code Information messages contain beacon code data for flight plans. They are however not included in the ASDI feed that goes to private industry and are therefore not supported in the com.luciad.format.asdi package.

  • HB : The ASDI server at the ETMS generates heartbeat messages at regular times. These are ignored in the com.luciad.format.asdi package.

Concepts modeled by ASDI data

An ASDI feed contains air traffic data. The most important concepts modeled by the ASDI data are discussed in this section. They correspond to the types of data that are decoded by the LuciadLightspeed ASDI decoders.

Track

Conceptually a track is a point (i.e., a location) together with a timestamp. A track is, e.g., used to represent the position of an aircraft at a certain point in time. On a map, a track is typically represented as an icon with a label containing identification (e.g., flight id), and possibly other indications (e.g., history, speed).

tracks
Figure 1. Map showing a track layer

Figure 1, “Map showing a track layer” shows a map with several tracks at a certain point in time. On the map tracks are represented by blue squares. The directions of the tracks are indicated by the yellow lines. The map shows the situation in the air at a particular point in time.

Trajectory

Conceptually a trajectory is a sequence of tracks of the same flight. It is ordered in ascending order of the tracks' timestamps. A trajectory is for instance used to represent an aircraft’s path to fly from point A to point B.

A non-empty trajectory has a begin time and an end time. The begin time is the timestamp of its first track; the end time is the timestamp of its last track.

On a map, a trajectory is typically represented as a polyline connecting the tracks.

trajectories
Figure 2. Map showing a trajectory (grey line) and track layer

Figure 2, “Map showing a trajectory (grey line) and track layer” shows a map with tracks and a trajectory layer showing in grey the trajectory of the selected track.

Flight plan

Conceptually a flight plan is the intended path of an aircraft, together with additional data as (estimated) departure time, (estimated) arrival time, status,…​ at a certain timestamp. On a map, a flight plan is typically represented as a polyline.

flightplans
Figure 3. Map showing a track, trajectory (grey line) and flight plan (black dashed line) layer

Figure 3, “Map showing a track, trajectory (grey line) and flight plan (black dashed line) layer” shows a map with a track and a flight plan layer showing the intended route of the selected track as a black dashed line, the trajectory layer shows in grey the trajectory of the selected track.

Flight plan history

As flight plans can change over time, the concept of a flight plan history was introduced. Conceptually a flight plan history is a sequence of flight plans, representing the flight plan of the same aircraft at different timestamps. The sequence of flight plans is ordered on ascending timestamps.

On a map, a flight plan history can for instance be represented by its last flight plan, so as a polyline.

LuciadLightspeed classes

This section describes the LuciadLightspeed classes used to model ASDI data, and their relationship with standard LuciadLightspeed classes and interfaces. Two types of classes can be distinguished. The classes TLcdASDITrajectory and TLcdASDIFlightPlanHistory are the main classes used to model ASDI data. The classes TLcdASDITrack and TLcdASDIFlightPlan are convenience classes derived from the main classes. This is explained below. All ASDI classes are part of the package com.luciad.format.asdi. The samples samples.decoder.asdi.file.MainPanel and samples.decoder.asdi.live.MainPanel show examples of how to use the classes and their various methods.

Main classes

TLcdASDITrajectory

The class TLcdASDITrajectory models a trajectory, i.e., it models a sequence of tracks, in ascending order of time. For memory efficiency reasons the class TLcdASDITrajectory has no methods to construct or retrieve individual track objects. All trajectory and individual track information can be retrieved through the following interfaces and methods implemented by the class TLcdASDITrajectory:

  • The class TLcdASDITrajectory implements com.luciad.shape.ILcdPointList, where the individual points are defined by the positions of the tracks. Points and corresponding tracks are ordered in ascending order of time.

  • The class TLcdASDITrajectory implements com.luciad.datamodel.ILcdDataObject. The properties of trajectories are available through this interface. A trajectory has one property: its id. The value of this id links all the tracks of the trajectory. It corresponds with the aircraft id available in all ASDI messages. The ASDI format does not provide unique aircraft ids however : different trajectories can have the same id at the same time.

  • The methods getBeginTime and getEndTime can be used to retrieve the valid time interval of the trajectory.

  • The time associated with a particular track can be retrieved by using the getTime( int aTrackIndex ) method. The argument aTrackIndex is the index of the track in the trajectory seen as an ILcdPointList.

  • All ASDI data related to the trajectory is available through the properties of the tracks. They can be retrieved using the getTrackValue(int aTrackIndex, TLcdDataProperty aProperty). The argument aProperty is property of which the value needs to be retrieved. These properties can be found in the TLcdDataType instances of the TLcdASDITrackTODataTypes and TLcdASDITrackTZDataTypes classes, depending on the type of tracks represented by the trajectory. The data type of the tracks matching a trajectory can be found in the model descriptor of the trajectory, using the TLcdASDITrajectoryModelDescriptor.getTrackDataType() method. The mapping of ASDI data to properties is explained in Message properties, track properties are discussed in more detail in ASDI Trajectory Messages.

  • The method getIndexForTimeStamp( long aTimeStamp ) can be used to get the index of the track in the trajectory that corresponds to the given timestamp : it returns the index of the track with the largest timestamp that is smaller than (or equal to) aTimeStamp, provided that aTimeStamp lies within the getBeginTime() and getEndTime() interval. When the timestamp could not be located -1 is returned.

  • The method getFlightPlanHistory can be used to get the flight plan history that corresponds with the trajectory.

TLcdASDIFlightPlanHistory

The class TLcdASDIFlightPlanHistory models a flight plan history, i.e., a flight plan that changes over time. It is implemented as a sequence of flight plans, in ascending order of time. The flight plan at each timestamp is represented by the points of its intended path and the additional information at that time stamp. For memory efficiency reasons the class TLcdASDIFlightPlanHistory has no methods to construct or retrieve individual flight plan objects. All history and individual flight plan information can be retrieved through the following interfaces and methods implemented by the class TLcdASDIFlightPlanHistory:

  • The class TLcdASDIFlightPlanHistory implements ILcdPointList, where the points are defined by the intended path of the flight plan with the most recent timestamp.

  • The class TLcdASDIFlightPlanHistory implements ILcdDataObject. The proprerties of a flight plan history are available through this interface. A flight plan history one property: its id. This id links all the data in a TLcdASDIFlightPlanHistory. It corresponds with the aircraft id available in all ASDI messages. The ASDI format does not provide unique aircraft ids however : different flight plan history objects can have the same id at the same time.

  • The methods getBeginTime and getEndTime can be used to retrieve the time interval for which the flight plan history contains flight plan information. The getBeginTime corresponds with the timestamp of the first flight plan in the history, the getEndTime with the last time the flight plans in the history are known to be active, which is at least the timestamp of the last flight plan in the history.

  • The method getFlightPlanCount can be used to retrieve the number of flight plans in a TLcdASDIFlightPlanHistory. It corresponds to the number of times the flight plan has changed + 1.

  • The time associated with a particular flight plan can be retrieved by using the getTime( int aFlightPlanIndex ) method. The argument aFlightPlanIndex is the index of the flight plan in the history. If the time for a particular flight plan is not known -1 is returned.

  • The points of the intended path of a particular flight plan can be retrieved using the methods getPointCount( int aFlightPlanIndex ) and getPoint( int aFlightPlanIndex, int aPointIndex ).

  • The additional ASDI data related to flight plans is available through the properties of the flight plans. They can be retrieved using the getFlightPlanValue(int aFlightPlanIndex, TLcdDataProperty aProperty) method. The aProperty parameter must be a property declared by the TLcdDataType found in the TLcdASDIFlightPlanDataTypes class. The mapping of ASDI data to properties is explained in Message properties, flight plan properties are discussed in more detail in ASDI Flight Plan History Messages.

  • The method getIndexForTimeStamp( long aTimeStamp ) can be used to get the index of the flight plan in the history that corresponds to the given timestamp : it returns the index of the flight plan with the largest timestamp that is smaller than (or equal to) aTimeStamp, provided that aTimeStamp lies within the getBeginTime() and getEndTime() interval. When the timestamp could not be located -1 is returned.

  • It is also possible to retrieve the messages on which a TLcdASDIFlightPlanHistory is based through the methods getMessageCount(), getMessageDataType(int aMessageIndex) and getMessageValue( int aMessageIndex, TLcdDataProperty aProperty ). The different data types for ASDI messages can be found in the TLcdASDIMessageDataTypes class. These methods offer an alternative way to get the additional flight plan related ASDI data that would normally not be needed. The mapping of ASDI data to properties is explained in Message properties, flight plan message properties are discussed in more detail in ASDI Flight Plan History Messages.

Convenience classes

TLcdASDITrack

The class TLcdASDITrack models a track by representing a TLcdASDITrajectory at a specific point index. A TLcdASDITrajectory contains data for all its tracks. A TLcdASDITrack is just a window on a TLcdASDITrajectory, showing only the data of one track of the TLcdASDITrajectory. A TLcdASDITrack extracts its data from the TLcdASDITrajectory on which it is a window.

All track and associated trajectory information can be retrieved through the following interfaces and methods implemented by the class TLcdASDITrack:

  • The class TLcdASDITrack has a constructor that needs the TLcdASDITrajectory for which it represents a window as input parameter. The specified TLcdASDITrajectory may not be null. The point index for which the TLcdASDITrack represents the TLcdASDITrajectory is -1 by default and must be set explicitly with updateForIndex or setTrajectoryPointIndex.

  • The trajectory associated with the TLcdASDITrack can be retrieved by calling the getTrajectory method on the TLcdASDITrack.

  • The point index for which the TLcdASDITrack represents the TLcdASDITrajectory can be retrieved using the method getTrajectoryPointIndex.

  • The class TLcdASDITrack extends the class com.luciad.shape.shape3D.TLcdLonLatHeightPoint. The point defines the position of the track.

  • The class TLcdASDITrack implements ILcdDataObject. All ASDI information associated with the track is available through the properties of the TLcdASDITrack. The properties The mapping of ASDI data to properties is explained in Message properties, track properties are discussed in more detail in ASDI Track Messages.

  • The method isActive allows to check if a TLcdASDITrack is active. A TLcdASDITrack is active if its timestamp lies within the getTrajectory().getBeginTime and getTrajectory().getEndTime, this is, if it represents the trajectory at a certain valid index. The data of an inactive track should not be used or interpreted in any way.

  • Use the updateForIndex( int aTrajectoryPointIndex ) method to update the state of the track so that it reflects the state of the trajectory at the given index, this is, the properties and the location of the track correspond to the given index. Specify an index -1 to mark a track as inactive. Note that the method getTrajectory().getIndexForTimeStamp(long aTimeStamp) can be used to retrieve the index for a given time value.

  • The setTrajectoryPointIndex( int aTrajectoryPointIndex ) sets the index for which this track represents the trajectory. It is similar to the updateForIndex method except that it does not change the location of the track. This must be done separately when using this method.

  • The setTrajectoryPointIndex and updateForIndex methods are typically used in the implementation of a com.luciad.realtime.ALcdTimeIndexedSimulatorModel class for TLcdASDITrack objects to update the state of the TLcdASDITrack objects to a specific timestamp (see the method ALcdTimeIndexedSimulatorModel.updateTrackForDateSFCT). The existence of the two methods enables to choose whether the location of the track should be updated automatically or not. In case interpolation should be done for instance one could use the setTrajectoryPointIndex method and move the tracks to the calculated interpolated positions afterward. This is demonstrated in the samples.decoder.asdi.TrackSimulatorModel class of the sample code.

TLcdASDIFlightPlan

The class TLcdASDIFlightPlan models a flight plan by representing a TLcdASDIFlightPlanHistory at a specific index. A TLcdASDIFlightPlanHistory contains data for all its flight plans. A TLcdASDIFlightPlan is just a window on a TLcdASDIFlightPlanHistory, showing only the data of one flight plan. A TLcdASDIFlightPlan extracts its data from the TLcdASDIFlightPlanHistory on which it is a window.

All flight plan and associated history information can be retrieved through the following interfaces and methods implemented by the class TLcdASDIFlightPlan:

  • The class TLcdASDIFlightPlan has a constructor that needs the TLcdASDIFlightPlanHistory for which it represents a window as input parameter. The specified TLcdASDIFlightPlanHistory may not be null. The index for which the TLcdASDIFlightPlan represents the TLcdASDIFlightPlanHistory is -1 by default and must be set explicitly with setFlightPlanHistoryIndex.

  • The flight plan history associated with the TLcdASDIFlightPlan can be retrieved by calling the method getFlightPlanHistory on the TLcdASDIFlightPlan.

  • The index for which the TLcdASDIFlightPlan represents the TLcdASDIFlightPlanHistory can be retrieved using the method getFlightPlanHistoryIndex.

  • The class TLcdASDIFlightPlan implements ILcdPointList. The points define the intended path of the flight plan. These are the same points as returned from getFlightPlanHistory().getPoint(getFlightPlanHistoryIndex(), int aPointIndex).

  • The class TLcdASDIFlightPlan implements ILcdDataObject. All ASDI information associated with the flight plan is available through the properties of the TLcdASDIFlightPlan. These are the same properties as returned from getFlightPlanHistory().getFlightPlanValue(getFlightPlanHistoryIndex(), TLcdDataProperty aProperty). The mapping of ASDI data to properties is explained in Message properties, flight plan properties are discussed in more detail in ASDI Flight Plan Messages.

  • The method isActive allows to check if a TLcdASDIFlightPlan is active. A TLcdASDIFlightPlan is considered active if its timestamp lies within the getFlightPlanHistory().getBeginTime and getFlightPlanHistory().getEndTime, this is, it represents the flight plan history at a certain valid index. The data of an inactive flight plan should not be used or interpreted in any way.

  • The setFlightPlanHistoryIndex( int aFlightPlanHistoryIndex ) updates the state of the flight plan so that it reflects the state of the flight plan at the given index in the history. The properties and intended paths of the flight plans always correspond to this index. Specify an index -1 to mark a flight plan as inactive. This method is typically used in the implementation of a com.luciad.realtime.ALcdTimeIndexedSimulatorModel class for TLcdASDIFlightPlan objects to update the state of the TLcdASDIFlightPlan objects to a specific timestamp (see the method ALcdTimeIndexedSimulatorModel.updateTrackForDateSFCT). This is demonstrated in the samples.decoder.asdi.FlightPlanSimulatorModel class of the sample code. Note that the method getFlightPlanHistory().getIndexForTimeStamp(long aTimeStamp) can be used to retrieve the index for a given time value.

  • It is also possible to retrieve the properties corresponding with the messages on which a particular TLcdASDIFlightPlan is based through the methods getMessageCount(), getMessageDataType(int aMessageIndex) and getMessageValue( int aMessageIndex, TLcdDataProperty aProperty ). These are the same properties as returned from getFlightPlanHistory().getMessageValue(getFlightPlanHistoryIndex(), TLcdDataProperty aProperty). These methods offer an alternative way to get the additional flight plan related ASDI data that would normally not be needed. The mapping of ASDI data to properties is explained in Message properties, flight plan message properties are discussed in more detail in ASDI Flight Plan Messages.

ASDI data decoding

ASDI format

ASDI data can be decoded from file or from a live stream of data. Data consists of a sequence of ASDI messages. The file extension is assumed to be .asdi. A file can contain a first line specifying the start time of the ASDI message feed. The format of this line must be '<set time> MM/dd/yyyy HH:mm:ss'.

How to decode ASDI data

Decoders

In the com.luciad.format.asdi package two decoders are available. Use a TLcdASDIModelDecoder to decode ASDI data from a file and a TLcdASDILiveDecoder to decode ASDI data from a live stream of data.

The class TLcdASDIModelDecoder implements com.luciad.model.ILcdModelDecoder. The decode method produces a com.luciad.model.ILcdModel which is in fact a com.luciad.model.TLcdModelList containing ILcdModel objects depending on the type of messages in the ASDI feed. The ILcdModel objects contain either trajectories or flight plan history objects. All info of the file is available through the objects of the different ILcdModel objects in the TLcdModelList.

The class TLcdASDILiveDecoder does not implement ILcdModelDecoder. Call the method decodeSFCT( InputStream aInputStream, int aFireEventMode, TLcdModelList aModelListSFCT ) to start reading data from a live stream. The decodeSFCT method does not return a TLcdModelList but in stead requires a TLcdModelList argument. During the decode ILcdModel objects are added to the TLcdModelList depending on the type of messages in the ASDI feed. The decodeSFCT creates ILcdModel objects containing either tracks or flight plans and updates them with the latest info in the ASDI feed. History is available through the tracks' trajectories and the flight plan’s history. The input stream from which the data is to be read can be specified through the argument aInputStream of the decodeSFCT method. The argument aFireEventMode of the decodeSFCT allows to specify when and if any model events need to be fired when the ILcdModel objects in the TLcdModelList are changed by the decoder. The aFireEventMode must be one of the values defined in com.luciad.util.ILcdFireEventMode.

As a TLcdASDIModelDecoder incorporates all info of a file in a TLcdModelList it creates trajectories and flight plan history objects, the TLcdASDILiveDecoder creates and updates tracks and flight plans. Following table gives an overview of the exact models and domain objects that are created :

ASDI messages Model Domain Objects created by TLcdASDIModelDecoder Domain Objects created by TLcdASDILiveDecoder

TZ

TZ tracks

TLcdASDITrajectory

TLcdASDITrack

TO

TO tracks

TLcdASDITrajectory

TLcdASDITrack

FZ, AF, DZ, UZ, AZ, RZ and RT

Flight plan data

TLcdASDIFlightPlanHistory

TLcdASDIFlightPlan

The model descriptors of the individual models depend on the type of domain object in the model. They can be used to check the type of domain object. They can also be used to check the type of model. Refer to Retrieving meta-data for more information on model descriptors.

The decoders can be configured in several ways:

  • The TLcdASDIModelDecoder class has a method setInputStreamFactory to specify the input stream factory for creating input streams from source file names. By default the com.luciad.io.TLcdInputStreamFactory is used.

  • ASDI messages contain time information that is not absolute. By default the decoders create absolute times from these relative times by assuming that the current time is the start time of the message feed. This behaviour can be overwritten. The method setStartTime allows to specify the start time of the message feed manually. If a file contains a <set time> command at the first line the TLcdASDIModelDecoder class uses the specified time as start time, ignoring the start time set with setStartTime.

  • By default the decoders add all supported ASDI messages to the corresponding ILcdModel of the TLcdModelList. The setMessageFilter method can be used to filter out messages, so that they are not added to the ILcdModel. Refer to Filtering ASDI messages for more information on message filters.

  • The TLcdModelList filled by the TLcdASDILiveDecoder always represents the most recent situation. You can use the setHistoryLength method to specify the maximum history to keep in memory for the trajectories associated with the TLcdASDITrack domain objects (obtained by TLcdASDITrack.getTrajectory). Note that if more history is available than the specified maximum, the oldest information is purged. For TLcdASDIFlightPlan objects the TLcdASDILiveDecoder always keeps all history.

Decoder samples

Decoding from file

The sample class samples.decoder.asdi.file.MainPanel is a sample that allows to decode ASDI data from a file and to display that data on a map. It starts with creating a TLcdASDIModelDecoder and registering it as one of the ILcdModelDecoder objects that must be checked when a file is opened.

Program: Creating an ASDI model decoder (from samples/decoder/asdi/file/MainPanel)
private ILcdModelDecoder createASDIModelDecoder(
    ILcdInputStreamFactory aInputStreamFactory
                                               ) {
  TLcdASDIModelDecoder decoder = new TLcdASDIModelDecoder();
  decoder.setMessageFilter(new FinalMessageFilter());
  decoder.setInputStreamFactory(aInputStreamFactory);
  return decoder;
}
private static class FinalMessageFilter implements ILcdASDIMessageFilter {
  @Override
  public boolean accept(
      ILcdDataObject aMessageDataObject
  ) {
    TLcdASDIFacility facility = (TLcdASDIFacility) aMessageDataObject.getValue("Facility");
    return (!(facility != null && facility.getCode().equals("KGT*")));
  }
}

Program: Creating an ASDI model decoder shows the implementation of the method createASDIModelDecoder which is used to create the TLcdASDIModelDecoder. The argument aInputStreamFactory represents the com.luciad.io.ILcdInputStreamFactory that the decoder must use for creating input streams from source file names. The method first creates a TLcdASDIModelDecoder. Then it adds a message filter that filters out messages coming from certain facilities. Finally it attaches aInputStreamFactory to it. Note that no start time is set : it is assumed that the files loaded in the sample contain a <set time> command at the first line or that the current time may be used.

Program: Using an open action to load ASDI files (from samples/decoder/asdi/file/MainPanel)
//create and initialize the ASDI model decoder.
fModelDecoder = createASDIModelDecoder(new TLcdInputStreamFactory());
//create an action to open an ASDI file.
OpenSupport openSupport = new OpenSupport(this, Collections.singletonList(fModelDecoder));
getView().setTransferHandler(openSupport.createDragAndDropTransferHandler());
openSupport.addStatusListener(getStatusBar());
openSupport.addModelProducerListener(new ASDIModelProducerListener());
OpenAction standaloneOpenAction = new OpenAction(openSupport);

Program: Using an open action to load ASDI files uses the method createASDIModelDecoder to create fModelDecoder. Since the created TLcdASDIModelDecoder is an ILcdModelDecoder it can be assigned to a samples.common.formatsupport.OpenAction. An OpenAction is a java.awt.event.ActionListener that can be attached to a menu item for loading a file. To load a file the OpenAction tries to find the correct ILcdModelDecoder for the file by calling the canDecode method on all registered ILcdModelDecoder. If an ILcdModelDecoder is found, its decode method is called to load the file. Program: Using an open action to load ASDI files shows how an OpenAction can be configured to load ASDI files. In the example only a TLcdASDIModelDecoder is registered for the OpenAction.

The complete sample code can be found in the packages samples.decoder.asdi.file and samples.decoder.asdi.

Decoding from a live stream

The sample class samples.decoder.asdi.live.MainPanel is a sample that loads live ASDI data and displays it continuously on a map. The code for creating the TLcdASDILiveDecoder and to start reading data from the live stream is explained below.

Program: Starting an ASDI live decoder (from samples/decoder/asdi/live/MainPanel)
private void startLiveDecoder(
    long aStartTime,
    int aHistoryLength,
    final InputStream aInputStream,
    final TLcdModelList aModelList,
    final String aSuccessMessage,
    final String aErrorMessage
) {
  final TLcdASDILiveDecoder live_decoder = new TLcdASDILiveDecoder();
  live_decoder.setStartTime(aStartTime);
  live_decoder.setHistoryLength(aHistoryLength);

  Runnable runnable = new Runnable() {
    @Override
    public void run() {
      try {
        live_decoder.decodeSFCT(aInputStream, ILcdFireEventMode.FIRE_LATER, aModelList);
        aInputStream.close();
        showMessage(aSuccessMessage, JOptionPane.PLAIN_MESSAGE);
      } catch (IOException e) {
        sLogger.error("decodeSFCT:" + aErrorMessage, e);
        showMessage(aErrorMessage, JOptionPane.ERROR_MESSAGE);
      }
    }
  };
  fUpdateThread = new Thread(runnable, "ASDI Update Thread");
  fUpdateThread.setPriority(Thread.MIN_PRIORITY);
  fUpdateThread.start();
}

Program: Starting an ASDI live decoder shows the implementation of the method startLiveDecoder that is used to create and start a TLcdASDILiveDecoder. The argument aInputStream represents the input stream with live data. The argument aModelList is the TLcdModelList that must be filled by this live decoder. The method first creates a TLcdASDILiveDecoder. Then it passes the aStartTime and aHistoryLength arguments to it. The decoder is now configured and can be started. The TLcdASDILiveDecoder is not an ILcdModelDecoder, hence there is no decode method. Since the TLcdASDILiveDecoder reads data continuously and the sample does not want to be blocked by that the TLcdASDILiveDecoder is started from another thread. First a java.lang.Runnable is created which calls the method decodeSFCT with the specified input stream and model list in its run method. Note that the decodeSFCT method also has an argument ILcdFireEventMode.FIRE_LATER : the reason for this is explained in the next paragraph. Next a java.lang.Thread is created for the Runnable. In the last line the thread is started. This starts the decoding from the live stream.

Program: Using a timer to fire model changes (from samples/decoder/asdi/live/MainPanel)
  fTimer = new Timer(DELAY, new TimerActionListener(model_list));
  fTimer.setInitialDelay(INITIAL_DELAY);
  fTimer.setRepeats(true);
  fTimer.setDelay(DELAY);
  fTimer.start();

private class TimerActionListener implements ActionListener {
  private final TLcdModelList fModelList;

  public TimerActionListener(TLcdModelList aModelList) {
    fModelList = aModelList;
  }

  @Override
  public void actionPerformed(ActionEvent e) {
    fModelList.fireCollectedModelChanges();

    // Firing the collected model changes automatically refreshes the map,
    // but the label decluttering algorithm (TLcdContinuousDeclutteringLabelPainter,
    // see SimulatorGXYLayerFactory) needs a refresh from time to time,
    // to make the labels move away gently if overlap is about to occur.
    getView().getLayers().stream()
             .filter(layer -> fSimulatorLayerFactory.accept(layer.getModel()))
             .filter(ILcdGXYLayer::isLabeled)
             .forEach(layer -> getView().invalidateGXYLayer(layer, true, this, "Invalidating labels of track layers"));
  }
}

In our sample we do not want the decoder to fire model changed events constantly, otherwise the map would be updating constantly. That is why we pass a ILcdFireEventMode.FIRE_LATER argument to the decodeSFCT method: it indicates that the events related to changes in the TLcdModelList during the decode must be collected but may not be fired immediately. We add a javax.swing.Timer to effectively fire the model changed events (see Program: Using a timer to fire model changes). The Timer regularly calls the fireCollectedModelChanges method on the TLcdModelList which results in an update of the map. The timer is also used to make the labels move fluently (e.g. if the user dragged a label away, it gently moves back). To that end, the track and flight plan layers (layers accepted by the fSimulatorLayerFactory.accept method) are refreshed, if they are labeled, using the method invalidateGXYLayer.

The complete sample code can be found in the packages samples.decoder.asdi.live and samples.decoder.asdi.

Reading messages

Sometimes the standard decoders do not offer the exact functionality that is required and they need to be customized or a custom decoder needs to be written. In these cases the class TLcdASDIMessageInputStream of the com.luciad.format.asdi package can come to help. A TLcdASDIMessageInputStream is a java.io.FilterInputStream dedicated to read ASDI messages from an underlying java.io.InputStream. It has a method readMessage that reads an ASDI message and returns an ILcdDataObject containing the properties of the message read. The data types for these messages can be found in the TLcdASDIMessageDataTypes class. It throws a TLcdASDIParseException if a supported message could not be parsed for any reason. Refer to Message properties for more information on message properties. Note that the TLcdASDIModelDecoder and TLcdASDILiveDecoder also use a TLcdASDIMessageInputStream to read the ASDI messages.

A TLcdASDIMessageInputStream can be configured in several ways:

  • ASDI messages contain time information that is not absolute. By default a TLcdASDIMessageInputStream creates absolute times from these relative times by assuming the current time is the start time of the message feed. This behaviour can be changed using the method setStartTime which allows to specify the start time of the message feed manually.

  • The setMessageFilter method can be used to skip messages. Refer to Filtering ASDI messages for more information on message filters.

Message properties

The information in the ASDI messages is exposed through data objects (represented by ILcdDataObject) in LuciadLightspeed. ASDI messages contain a number of fields grouping information that belongs together. Each message field is mapped to a property of a message TLcdDataType in LuciadLightspeed. The standard decoders TLcdASDIModelDecoder and TLcdASDILiveDecoder create domain objects offering the data objects and properties of the messages they are based on, the TLcdASDIMessageInputStream.readMessage method returns the data objects of the message read. Classes exist that contain the various data types and data properties used in ASDI. This is explained in following sections.

Mapping of ASDI message fields to properties

Each ASDI message consists of some common fields (sequence number, message time, facility) and fields specific for each message type. Refer to Structure of ASDI data for more information on the ASDI message types. Fields can contain one type of information or can group information that belongs together. Depending on its content a field can be mapped to a property in different ways:

  • A field that contains one type of information is mapped to a value of a simple type:

    • This can be any standard data type as java.lang.String, java.lang.Double, java.lang.Integer,…​

    • A numeric field that contains a measured value expressed in a certain unit is mapped to a property of the type com.luciad.util.iso19103.TLcdISO19103Measure. This class is an extension of the standard java.lang.Number class that has a method ILcdISO19103UnitOfMeasure getUnitOfMeasure() to retrieve the unit associated with the value. The unit is an ILcdISO19103UnitOfMeasure which has various methods to retrieve the (short) name, type, conversion to a standard unit, …​

    • Sometimes a field contains an integer value that can be chosen from a list of possibilities each described by a String. Such values are mapped to properties of type TLcdASDIMappedInteger. The TLcdASDIMappedInteger class is an extension from the standard Number class. The method intValue returns the actual integer value. The methods getValueDescription and toString return the descriptive String that corresponds with the integer value. In the cases where the integer values of a TLcdASDIMappedInteger are not defined in the ASDI specification, the class TLcdASDIMappedInteger contains constants for the allowed integer values. This allows to work with them without depending on the exact values.

    • A field can also contain a code-like String value that can be chosen from a list of possibilities each described by a more descriptive String. Such values are mapped to properties of type TLcdASDIMappedString. The method stringValue returns the code-like String value. The methods getValueDescription and toString return the corresponding descriptive String.

    • Fields that contain facility information are translated to properties of type TLcdASDIFacility. The methods getShortCode and getCode allow to retrieve the codes that correspond with the facility; getDescription and toString return the description of the facility.

  • A field that groups information that belongs together is mapped to a property of type ILcdDataObject. All properties of this data object are of the type described above or can again be ILcdDataObject. For instance a field that represents a flight id is mapped to a property of type ILcdDataObject, containing 2 properties, each of type String, one for the aircraft id and one for the computer id.

    Note that some complex fields are mapped to simple String properties containing the String as extracted from the message. This is done in cases where it does not seem useful to split the String in sub properties.

How to retrieve the message properties

Message properties of domain objects

All domain objects offer the message properties of the messages they are based on, literally or in a derived form :

TLcdASDITrajectory

A TLcdASDITrajectory consists of a number of tracks, each track is based on one track message, hence a TLcdASDITrajectory has a set of message properties corresponding to each track. All tracks of a TLcdASDITrajectory are based on messages of the same type (either TZ or TO), hence all tracks of a TLcdASDITrajectory have the same message data type. The methods getTrackDataType() and getTrackValue( int aTrackIndex, TLcdDataProperty aProperty ) must be used to retrieve the track message property values, where aTrackIndex indicates the track of interest and aProperty the property of of which the value needs to be. Since there is a one-to-one relation between tracks and track messages, track properties and track message properties are the same.

The TLcdDataType and TLcdDataProperty instances can be found in the TLcdASDITrackTODataTypes and TLcdASDITrackTZDataTypes classes, depending on whether a trajectory represents a TZ or a TO data type. The data type can be retrieved through the TLcdASDITrajectoryModelDescriptor.getTrackDataType() method.

TLcdASDITrack

A TLcdASDITrack represents one track of a TLcdASDITrajectory, hence it is also based on one track message (either TZ or TO). The properties of the track message are available through the ILcdDataObject interface of the TLcdASDITrack class.

TLcdASDIFlightPlanHistory

A TLcdASDIFlightPlanHistory contains a number of flight plans, representing the flight plan of a flight at different timestamps. A TLcdASDIFlightPlanHistory is based on a group of flight plan messages (a mix of FZ, DZ, AZ, AF, RT, …​ messages) that relate to the same flight and that are ordered in ascending order of message time. A flight plan of a TLcdASDIFlightPlanHistory is based on a subset of these flight plan messages : each time a flight plan message is added to a TLcdASDIFlightPlanHistory a new flight plan is added too since each message adds extra flight plan information or updates the information set by the previous messages. Hence the fligth plan at index i in the history is based on the messages at indexes 0..i, so there’s a one-to-many relation between flight plans and flight plan messages. The flight plan messages of a flight plan are a mix of messages of different types that are ordered on message time. Each message type has its own set of message properties, different message data types can have properties in common. The data types are available in the TLcdASDIMessageDataTypes class. This class has an abstract super-type for all messages, as well as a concrete representation of the different message types. For more information, refere to the API reference of the class.

The most recent values of the union of all different message properties of a flight plan (which we call the flight plan properties) are available through the methods getFlightPlanProperty() and getFlightPlanValue( int aFlightPlanIndex, TLcdDataProperty aProperty ), where aFlightPlanIndex indicates the flight plan of interest and aProperty the property of which the value should be retrieved. The properties for flight plan are available in the TLcdASDIFlightPlanDataTypes class. Figure 4, “Flight plan properties and message properties” visualizes the relation between flight plan properties and message properties.

flightplanfeatures
Figure 4. Flight plan properties and message properties

The set of properties corresponding with the messages of the flight plans of a TLcdASDIFlightPlanHistory can be retrieved using the methods getMessageDataType(int aMessageIndex) and getMessageValue( int aMessageIndex, TLcdDataProperty aProperty ). They allow to retrieve the properties that are otherwise hidden by the flight plan properties, for instance Property 2 of Message 0 in Figure 4, “Flight plan properties and message properties”.

TLcdASDIFlightPlan

A TLcdASDIFlightPlan represents a flight plan of a TLcdASDIFlightPlanHistory, hence it is also based on a group of flight plan messages (a mix of FZ, DZ, AZ, AF, RT, …​ messages). Here the flight plan properties are available through the ILcdDataObject interface of the TLcdASDIFlightPlan class. The set of message properties corresponding to the messages of the flight plan can be retrieved using the methods getMessageCount(), getMessageDataType(int aMessageIndex) and getMessageValue( int aMessageIndex, TLcdDataProperty aProperty ), where aMessageIndex indicates the message of interest and aProperty is the property of which the value should be retrieved. The various message data types can be found in the TLcdASDIMessageDataTypes class.

All ILcdModel in the TLcdModelList filled by the ASDI decoders have model descriptors that implement ILcdDataModelDescriptor, so they describe the data types of the model elements of the corresponding model. In ASDI all models have a single model element data type. This model element data type can be retrieved using the ILcdDataModelDescriptor.getModelElementTypes() method. You can use this data type to identify the type of tracks you are dealing with.

If extra methods are available to retrieve the property values of a helper class, there are also extra methods on the model descriptors for retrieving the corresponding TLcdDataType corresponding to that helper class. For instance the TLcdASDITrajectory class has a method getTrackValue to obtain the track properties, so a model containing TLcdASDITrajectory objects has a model descriptor of type TLcdASDITrajectoryModelDescriptor that has a method getTrackDataType() to retrieve the TLcdDataType for the track properties. If this TLcdDataType matches TLcdASDITrackTODataTypes.getTrackTODataType() then the tracks are of type TO, and the properties used to retrieve property values can be found in the TLcdASDITrackTODataTypes class. Otherwise, if the TLcdDataType matches TLcdASDITrackTZDataTypes.getTrackTZDataType() then the tracks are of type TZ, and the properties used to retrieve property values can be found in the TLcdASDITrackTZDataTypes class.

The sample package samples.decoder.asdi delivered with the ASDI Industry Specific Component shows an example of how domain object properties can be retrieved in order to show them in a tree view.

Message properties of read messages

When reading messages from a TLcdASDIMessageInputStream the message properties of the read message are available through the ILcdDataObject returned by the method TLcdASDIMessageInputStream.readMessage.

Retrieving meta-data

The meta-data for the models in the model list can be obtained through the model descriptors of the models. The model descriptor classes depend on the type of domain objects in the model. All model descriptor classes extend the abstract class ALcdASDIModelDescriptor. Following table gives an overview :

Domain Object Model Descriptor

TLcdASDITrajectory

TLcdASDITrajectoryModelDescriptor

TLcdASDITrack

TLcdASDITrackModelDescriptor

TLcdASDIFlightPlanHistory

TLcdASDIFlightPlanHistoryModelDescriptor

TLcdASDIFlightPlan

TLcdASDIFlightPlanModelDescriptor

ALcdASDIModelDescriptor

Following information is always available through the interfaces and methods implemented by the class ALcdASDIModelDescriptor:

  • An ALcdASDIModelDescriptor is an ILcdDataModelDescriptor. This interface can be used to retrieve the TLcdDataType of the model elements, using the getModelElementTypes() method. For ASDI, this will always return a set containing a single data type, that will be one of: TLcdASDIFlightPlanDataTypes.getFlightPlanDataType(), TLcdASDITrackTODataTypes.getTrackTODataType(), TLcdASDITrackTZDataTypes.getTrackTZDataType(), TLcdASDIDataTypes.getTrajectoryDataType() or TLcdASDIDataTypes.getFlightPlanHistoryDataType().

  • The method getDataType can also be used to retrieve the type of the model data. It refers to the type of the messages the model data is originating from. Possible data types are described by constants in the ALcdASDIModelDescriptor class, for instance ALcdASDIModelDescriptor.DATA_TYPE_TZ_TRACK refers to a model with track or trajectory data that originates from TZ messages. This can for instance be used in the implementation of a com.luciad.view.gxy.ILcdGXYLayerFactory to distinguish between layers containing TO models and layers containing TZ models.

TLcdASDITrajectoryModelDescriptor

Models containing TLcdASDITrajectory objects have a TLcdASDITrajectoryModelDescriptor. The method getTrackDataType retrieves the TLcdDataType for the track matching the trajectory model. The track property values themselves can be retrieved using the TLcdASDITrajectory.getTrackValue method.

TLcdASDITrackModelDescriptor

Models containing TLcdASDITrack objects have a TLcdASDITrackModelDescriptor. The trajectory properties themselves can be retrieved using the methods of the ILcdDataObject interface for the TLcdASDITrajectory returned by TLcdASDITrack.getTrajectory().

TLcdASDIFlightPlanHistoryModelDescriptor

TLcdASDIFlightPlanModelDescriptor

Models containing TLcdASDIFlightPlan objects have a TLcdASDIFlightPlanModelDescriptor.

Filtering ASDI messages

Often a user will only be interested in a subset of the decoded messages, for instance a user is only interested in messages that come from certain facilities. The classes TLcdASDIModelDecoder, TLcdASDILiveDecoder and TLcdASDIMessageInputStream allow to filter messages by specifying an ILcdASDIMessageFilter.

A filter must implement ILcdASDIMessageFilter. This interface contains the accept(ILcdDataObject aMessage) method. It returns only true for the messages of interest. The aMessage argument contains the data object representation of the message to concider. For more information on message properties refer to Message properties. To use the filter, attach it to the decoder or message input stream.

Replaying ASDI data from file

Replay input stream

It is also possible to replay the data from a file in ASDI format as if it came from a live stream of data.

For this purpose a class TLcdASDIFileReplayInputStream is introduced. It reads an ASDI file and uses the message time to replay the data: the data is only made available after the time between 2 messages has passed.

Using a TLcdASDILiveDecoder with a TLcdASDIFileReplayInputStream allows to replay data from an ASDI file.

The class TLcdASDIFileReplayInputStream can be configured through the following classes and methods:

  • The class TLcdASDIFileReplayInputStream is an extension of the class java.io.PipedInputStream.

  • The setExceptionHandler method allows to specify an exception handler that is called whenever a non recoverable exception occurs. It can for example be used to pop up a dialog to inform the user about it.

Sample

The sample class samples.decoder.asdi.live.MainPanel implements a sample that creates a TLcdASDILiveDecoder using a TLcdASDIFileReplayInputStream.

Program: Create a replay stream (from samples/decoder/asdi/live/MainPanel)
private InputStream createFileReplayInputStream(String aASDIFile) {
  try {
    TLcdInputStreamFactory input_stream_factory = new TLcdInputStreamFactory();
    InputStream input_stream = input_stream_factory.createInputStream(aASDIFile);
    return new TLcdASDIFileReplayInputStream(input_stream);
  } catch (IOException e) {
    sLogger.error(TLcdASDIFileReplayInputStream.class.getName() + "<init>", "The file [" + aASDIFile + "] could not be loaded.", e);
    JOptionPane.showMessageDialog(MainPanel.this,
                                  "The file [" + aASDIFile + "] could not be loaded.", "Could not load file", JOptionPane.ERROR_MESSAGE);
    return null;
  }
}

Program: Create a replay stream shows the implementation of the method createFileReplayInputStream(String aASDIFile) that is used to create the TLcdASDIFileReplayInputStream. It has an argument aASDIFile that is the name of the file containing the ASDI data to replay. The method first creates an InputStream on the specified file using the createInputStream method of the TLcdInputStreamFactory class. In the next step a TLcdASDIFileReplayInputStream is created for this InputStream, which is returned as result of the method.

The resulting InputStream can be used in the startLiveDecoder of Program: Starting an ASDI live decoder.

The complete sample code can be found in the packages samples.decoder.asdi.live and samples.decoder.asdi.