Click or drag to resize

Data Interoperability

Much of the power of DME Component Libraries depends, of course, on the ability to use your own data as input to the library algorithms and to get answers back out in a useful form. This topic explains some of the techniques you can use to achieve data interoperability as well as addresses some difficulties you may encounter.

STK Data Files

DME Component Libraries provides convenient classes to read and write ephemeris and attitude data in STK *.e and *.a file formats. The following example shows how to read a *.e file and access the times, positions and velocities that it contains. It also shows how to access the interpolator that can be used to interpolate over the data in the file.

Java
try (BufferedReader reader = Files.newBufferedReader(Paths.get(path))) {
    StkEphemerisFile ephemerisFile = StkEphemerisFile.readFrom(reader);
    StkEphemerisFile.Ephemeris ephemeris = ephemerisFile.getData();
    if (ephemeris instanceof StkEphemerisFile.EphemerisTimePosVel) {
        StkEphemerisFile.EphemerisTimePosVel posVelData = (StkEphemerisFile.EphemerisTimePosVel) ephemeris;

        DateMotionCollection1<Cartesian> ephemerisData = posVelData.getEphemerisData();
        List<JulianDate> times = ephemerisData.getDates();
        List<Cartesian> positions = ephemerisData.getValues();
        List<Cartesian> velocities = ephemerisData.getFirstDerivatives();

        TranslationalMotionInterpolator interpolator = posVelData.getInterpolator();
    }
}

In addition to reading *.e files, DME Component Libraries can also write *.e files, as demonstrated in the following example:

Java
// Use any valid TLE to create a propagator, and use that propagator to create a MotionEvaluator.
TwoLineElementSet inputTwoLineElementSet = new TwoLineElementSet(
        "1 20813U 90084A   07157.52757149 -.00000978  00000-0  10000-3 0  8139" +
        "2 20813  62.3585 177.5907 7234421 261.2958  18.3008  2.01001663122427");
Sgp4Propagator propagator = new Sgp4Propagator(inputTwoLineElementSet);
MotionEvaluator1<Cartesian> motionEvaluator = propagator.getEvaluator();

// Define start time, stop time, and time step that will be used to sample the motion evaluator for ephemeris data.
JulianDate startDate = new JulianDate(new GregorianDate(2007, 6, 1));
JulianDate stopDate = new JulianDate(new GregorianDate(2007, 6, 2));
Duration timeStep = Duration.fromSeconds(60.0);

// Using the MotionEvaluator created above, output ephemeris data in a format that can be processed by
// the StkEphemerisFile object. Passing null for the ITrackCalculationProgress indicates that the Evaluate step
// should not track progress.
DateMotionCollection1<Cartesian> rawEphemerisData = motionEvaluator.evaluate(startDate, stopDate, timeStep, 0, null);

// The data obtained from the MotionEvaluator is in meters.  Convert to desired units, kilometers in this case.
DateMotionCollection1<Cartesian> rawEphemerisDataKm = new DateMotionCollection1<>();
double toKilometers = 1.0 / 1000.0;
for (int i = 0; i < rawEphemerisData.getCount(); i++) {
    rawEphemerisDataKm.add(rawEphemerisData.getDates().get(i),
            Cartesian.multiply(rawEphemerisData.getMotions().get(i).getValue(), toKilometers));
}

// Once the raw data has been obtained from the MotionEvaluator, place it into an StkEphemerisFile
// and specify the appropriate coordinate system.
StkEphemerisFile.EphemerisTimePos ephemerisData = new StkEphemerisFile.EphemerisTimePos();
ephemerisData.setCoordinateSystem(propagator.getReferenceFrame());
ephemerisData.setEphemerisData(rawEphemerisDataKm);
StkEphemerisFile ephemerisFile = new StkEphemerisFile();
ephemerisFile.setData(ephemerisData);

// Use the "Properties" element of the STKEphemerisFile to specify any additional supported properties
// such as the distance unit of the data.
ephemerisFile.getProperties().put("DistanceUnit", "Kilometers");

// Finally, write the StkEphemerisFile out.
try (Writer ephemerisWriter = Files.newBufferedWriter(Paths.get(path), StandardCharsets.US_ASCII)) {
    ephemerisFile.writeTo(ephemerisWriter);
}

See the reference documentation for StkEphemerisFile and StkAttitudeFile for more information.

STK Database Files

It is also possible to load information from STK database files. To load positions and additional metadata for spacecraft, see the Satellite Database Files topic. To load positions and additional metadata for ground stations and other facilities, see the Facility Database Files topic. Lastly, for information on cities see the City Database Files topic.

CCSDS OEM Files

DME Component Libraries provides convenient classes to read and write ephemeris data in the Orbit Ephemeris Message (OEM) file format from The Consultative Committee for Space Data Systems (CCSDS). The following example shows how to read an OEM file and access information from the file.

Java
try (BufferedReader reader = Files.newBufferedReader(Paths.get(path))) {
    CcsdsOrbitEphemerisMessageFile oemFile = CcsdsOrbitEphemerisMessageFile.readFrom(reader);

    // This particular file has only one segment at index 0. Files may contain more than one segment.
    CcsdsOrbitEphemerisMessageSegment oemSegment = oemFile.get(0);

    // The ephemeris and covariance data are available.
    DateMotionCollection1<Cartesian> ephemeris = oemSegment.getEphemerisData();
    DateMotionCollection1<Matrix6By6Symmetric> covariance = oemSegment.getCovarianceData();

    // The reference frame can be inferred.
    // If this or any of the other inferences fails, null is returned.
    ReferenceFrame referenceFrame = oemSegment.getReferenceFrame();

    // An interpolator can be inferred.
    TranslationalMotionInterpolator interpolator = oemSegment.getInterpolator();

    // The axes in which the covariance is expressed can also be inferred.
    // These are often the same as the axes of the reference frame.
    DateMotionCollection1<Axes> covarianceAxes = oemSegment.getCovarianceAxes();
    // Finally, a point can be created if the ephemeris, reference frame,
    // and interpolator are not null.
    Point point = oemSegment.createPoint();
}

In addition to reading OEM files, DME Component Libraries can also write OEM files, as demonstrated in the following example:

Java
CcsdsOrbitEphemerisMessageFile oemFile = new CcsdsOrbitEphemerisMessageFile();
oemFile.getComments().add("TLE Element Propagation.");
oemFile.setOriginator("DME Component Libraries");
oemFile.setVersion("2.0");

// Oem files must have at least one segment.
CcsdsOrbitEphemerisMessageSegment oemSegment = new CcsdsOrbitEphemerisMessageSegment();

// Use any valid TLE to create a propagator, and use that propagator to create a MotionEvaluator.
TwoLineElementSet inputTwoLineElementSet = new TwoLineElementSet(
        "1 20813U 90084A   07157.52757149 -.00000978  00000-0  10000-3 0  8139" +
        "2 20813  62.3585 177.5907 7234421 261.2958  18.3008  2.01001663122427");
Sgp4Propagator propagator = new Sgp4Propagator(inputTwoLineElementSet);
MotionEvaluator1<Cartesian> motionEvaluator = propagator.getEvaluator();

// Define start time, stop time, and time step that will be used to sample the motion evaluator for ephemeris data.
JulianDate startTime = new GregorianDate(2007, 6, 1).toJulianDate();
JulianDate stopTime = new GregorianDate(2007, 6, 2).toJulianDate();
Duration timeStep = Duration.fromSeconds(60.0);

// Using the MotionEvaluator created above, output ephemeris data in a format that can be processed by
// the CcsdsOrbitEphemerisMessageSegment object.
DateMotionCollection1<Cartesian> rawEphemerisData = motionEvaluator.evaluate(startTime, stopTime, timeStep, 2);
oemSegment.setEphemerisData(rawEphemerisData);

oemSegment.setObjectName("MOLNIYA 3-39");
oemSegment.setObjectId("20813");

oemSegment.setStartTime(startTime);
oemSegment.setUseableStartTime(startTime);
oemSegment.setUseableStopTime(stopTime);
oemSegment.setStopTime(stopTime);

oemSegment.setTimeStandard(TimeStandard.getCoordinatedUniversalTime());
oemSegment.setCenter("Earth");

// The TLE propagator uses the True Equator Mean Equinox Frame, which is abbreviated TEMEOFDATE.
oemSegment.setReferenceFrameName("TEMEOFDATE");

// Add Covariance data at the start time in the radial, in-track, and cross-track frame,
// which is abbreviated RSW_ROTATING.
oemSegment.getCovarianceData().add(startTime, Matrix6By6Symmetric.diagonalMatrix(100.0 * 100.0, // m^2, radial.
                                                                                 1000.0 * 1000.0, // m^2, in-track.
                                                                                 200.0 * 200.0, // m^2, cross-track.
                                                                                 1.0, // m^2/s^2, radial velocity.
                                                                                 2.0e-2 * 2.0e-2, // m^2/s^2, in-track velocity.
                                                                                 1.0e-3 * 1.0e-3)); // m^2/s^2, cross-track velocity.
oemSegment.getCovarianceAxesNames().add(startTime, "RSW_ROTATING");

// Add the CCSDS segment to the CCSDS file.
oemFile.add(oemSegment);

// The creation date will be set to DateTime.Now.
final boolean overrideCreationDate = true;

// OEM files can be written and read in the ASCII KVN (key-value notation) format,
// a namespace qualified XML format (with ndm as the namespace), or
// an unqualified XML format. (NDM stands for navigation data message).
try (Writer kvnWriter = Files.newBufferedWriter(Paths.get(kvnPath), StandardCharsets.US_ASCII)) {
    oemFile.writeTo(kvnWriter, new CcsdsOemFileWritingOptions(overrideCreationDate, CcsdsOemFileOutputFormat.KVN_FORMAT));
}
try (Writer qualifiedWriter = Files.newBufferedWriter(Paths.get(qualifiedXmlPath), StandardCharsets.US_ASCII)) {
    oemFile.writeTo(qualifiedWriter, new CcsdsOemFileWritingOptions(overrideCreationDate, CcsdsOemFileOutputFormat.NAMESPACE_QUALIFIED_XML_FORMAT));
}
try (Writer unqualifiedWriter = Files.newBufferedWriter(Paths.get(unqualifiedXmlPath), StandardCharsets.US_ASCII)) {
    oemFile.writeTo(unqualifiedWriter, new CcsdsOemFileWritingOptions(overrideCreationDate, CcsdsOemFileOutputFormat.UNQUALIFIED_XML_FORMAT));
}

See the reference documentation for CcsdsOrbitEphemerisMessageFile, and CcsdsOrbitEphemerisMessageSegment for more information.

Custom Data Formats

Using custom or proprietary data formats with DME Component Libraries requires a bit more work. Fundamentally, any data that can be read or accessed from Java can be used in DME Component Libraries calculations. Whether your data is in a database or an XML file, whether it is on disk or on a web server, Java provides plenty of support for reading and parsing nearly any type of data. Of course, it is not enough to simply read the data. You must also represent it in memory using data structures that DME Component Libraries understands. Generally, this means using DME Component Libraries primitive types such as JulianDate and Cartesian.

Dates and times in other applications and data files are represented in a variety of ways. DME Component Libraries makes it easy to convert one of these other representations to a JulianDate. One common representation is an actual Julian date. For example, the number 2454258.5 represents midnight on June 7, 2007. You can pass this number directly to the JulianDate-- constructor to get a JulianDate instance representing that Julian date. Be sure to consider the time standard of your data source and construct the JulianDate with the same time standard.

Your data source may contain a modified Julian date, instead. In that case, simply add ModifiedJulianDateDifference to the number read from your data source before passing it to the JulianDate constructor.

STK and other applications commonly use the notion of "epoch seconds" to describe a date and time. Essentially, an epoch is defined as a Gregorian year, month, day, hour, minute and second (and often fractions of seconds) and then each date and time is represented as the number of seconds since that epoch. Read the Gregorian epoch using the GregorianDate.parse method. Then, construct a JulianDate from the parsed GregorianDate. For example:

Java
String epochAsString = "06/05/2007 12:00:00.00";
GregorianDate epochAsDateTime = GregorianDate.parse(epochAsString);
JulianDate epoch = new JulianDate(epochAsDateTime);

Then, read each date and time by adding the number of seconds read to the epoch. For example:

Java
double secondsSinceEpoch = 123.45;
JulianDate date = epoch.addSeconds(secondsSinceEpoch);

Reading other primitive types, such as Cartesian and Spherical vectors, is straightforward. Simply read and parse the components (X, Y, and Z for a Cartesian vector and Clock, Cone, and Magnitude for a Spherical vector) and then construct an instance of the appropriate type. DME Component Libraries has methods for converting between primitive types. The following example shows how to create a Spherical vector and then convert it to an equivalent Cartesian vector:

Java
// Initialize a set of spherical coordinates.
double clockAngle = Math.PI / 4;
double coneAngle = Math.PI / 8;
double magnitude = 100.0;
Spherical spherical = new Spherical(clockAngle, coneAngle, magnitude);

// Convert the set of spherical coordinates to a set of Cartesian coordinates.
Cartesian cartesian = new Cartesian(spherical);

Many methods in DME Component Libraries expect a Motion1<Cartesian> as an input. This type holds not only a Cartesian position vector but also can optionally hold additional instantaneous derivatives of that vector, such as velocity and acceleration. See the Motion1<T> and Motion2<T, TDerivative> topic for more information. The following example shows how to create a Motion1<Cartesian> instance from a position and velocity:

Java
Cartesian position = new Cartesian(12.0, 19.5, -6.2);
Cartesian velocity = new Cartesian(-1.2, 0.5, 2.3);
Motion1<Cartesian> positionAndVelocity = new Motion1<>(position, velocity);