EphemerisOemWriter.java

  1. /* Copyright 2016 Applied Defense Solutions (ADS)
  2.  * Licensed to CS GROUP (CS) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * ADS licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *   http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.orekit.files.ccsds.ndm.odm.oem;

  18. import java.io.IOException;
  19. import java.util.List;

  20. import org.orekit.errors.OrekitException;
  21. import org.orekit.errors.OrekitIllegalArgumentException;
  22. import org.orekit.errors.OrekitMessages;
  23. import org.orekit.files.ccsds.definitions.FrameFacade;
  24. import org.orekit.files.ccsds.ndm.odm.OdmHeader;
  25. import org.orekit.files.ccsds.utils.FileFormat;
  26. import org.orekit.files.ccsds.utils.generation.Generator;
  27. import org.orekit.files.ccsds.utils.generation.KvnGenerator;
  28. import org.orekit.files.ccsds.utils.generation.XmlGenerator;
  29. import org.orekit.files.general.EphemerisFile;
  30. import org.orekit.files.general.EphemerisFile.SatelliteEphemeris;
  31. import org.orekit.files.general.EphemerisFileWriter;
  32. import org.orekit.utils.CartesianDerivativesFilter;
  33. import org.orekit.utils.TimeStampedPVCoordinates;

  34. /** An {@link EphemerisFileWriter} generating {@link Oem OEM} files.
  35.  * @author Hank Grabowski
  36.  * @author Evan Ward
  37.  * @since 9.0
  38.  * @see <a href="https://public.ccsds.org/Pubs/502x0b2c1.pdf">CCSDS 502.0-B-2 Orbit Data
  39.  *      Messages</a>
  40.  * @see <a href="https://public.ccsds.org/Pubs/500x0g4.pdf">CCSDS 500.0-G-4 Navigation
  41.  *      Data Definitions and Conventions</a>
  42.  * @see StreamingOemWriter
  43.  */
  44. public class EphemerisOemWriter implements EphemerisFileWriter {

  45.     /** Underlying writer. */
  46.     private final OemWriter writer;

  47.     /** Header. */
  48.     private final OdmHeader header;

  49.     /** Current metadata. */
  50.     private final OemMetadata metadata;

  51.     /** File format to use. */
  52.     private final FileFormat fileFormat;

  53.     /** Output name for error messages. */
  54.     private final String outputName;

  55.     /** Maximum offset for relative dates.
  56.      * @since 12.0
  57.      */
  58.     private final double maxRelativeOffset;

  59.     /** Column number for aligning units. */
  60.     private final int unitsColumn;

  61.     /**
  62.      * Constructor used to create a new OEM writer configured with the necessary parameters
  63.      * to successfully fill in all required fields that aren't part of a standard object.
  64.      * <p>
  65.      * If the mandatory header entries are not present (or if header is null),
  66.      * built-in defaults will be used
  67.      * </p>
  68.      * <p>
  69.      * The writer is built from the complete header and partial metadata. The template
  70.      * metadata is used to initialize and independent local copy, that will be updated
  71.      * as new segments are written (with at least the segment start and stop will change,
  72.      * but some other parts may change too). The {@code template} argument itself is not
  73.      * changed.
  74.      * </p>
  75.      * @param writer underlying writer
  76.      * @param header file header (may be null)
  77.      * @param template template for metadata
  78.      * @param fileFormat file format to use
  79.      * @param outputName output name for error messages
  80.      * @param maxRelativeOffset maximum offset in seconds to use relative dates
  81.      * (if a date is too far from reference, it will be displayed as calendar elements)
  82.      * @param unitsColumn columns number for aligning units (if negative or zero, units are not output)
  83.      * @since 12.0
  84.      */
  85.     public EphemerisOemWriter(final OemWriter writer,
  86.                               final OdmHeader header, final OemMetadata template,
  87.                               final FileFormat fileFormat, final String outputName,
  88.                               final double maxRelativeOffset, final int unitsColumn) {
  89.         this.writer            = writer;
  90.         this.header            = header;
  91.         this.metadata          = template.copy(header == null ? writer.getDefaultVersion() : header.getFormatVersion());
  92.         this.fileFormat        = fileFormat;
  93.         this.outputName        = outputName;
  94.         this.maxRelativeOffset = maxRelativeOffset;
  95.         this.unitsColumn       = unitsColumn;
  96.     }

  97.     /** {@inheritDoc}
  98.      * <p>
  99.      * As {@code EphemerisFile.SatelliteEphemeris} does not have all the entries
  100.      * from {@link OemMetadata}, the only values that will be extracted from the
  101.      * {@code ephemerisFile} will be the start time, stop time, reference frame, interpolation
  102.      * method and interpolation degree. The missing values (like object name, local spacecraft
  103.      * body frame...) will be inherited from the template  metadata set at writer
  104.      * {@link #EphemerisOemWriter(OemWriter, OdmHeader, OemMetadata, FileFormat, String, double, int) construction}.
  105.      * </p>
  106.      */
  107.     @Override
  108.     public <C extends TimeStampedPVCoordinates, S extends EphemerisFile.EphemerisSegment<C>>
  109.         void write(final Appendable appendable, final EphemerisFile<C, S> ephemerisFile)
  110.         throws IOException {

  111.         if (appendable == null) {
  112.             throw new OrekitIllegalArgumentException(OrekitMessages.NULL_ARGUMENT, "writer");
  113.         }

  114.         if (ephemerisFile == null) {
  115.             return;
  116.         }

  117.         final SatelliteEphemeris<C, S> satEphem = ephemerisFile.getSatellites().get(metadata.getObjectID());
  118.         if (satEphem == null) {
  119.             throw new OrekitIllegalArgumentException(OrekitMessages.VALUE_NOT_FOUND,
  120.                                                      metadata.getObjectID(), "ephemerisFile");
  121.         }

  122.         // Get ephemeris segments to output.
  123.         final List<S> segments = satEphem.getSegments();
  124.         if (segments.isEmpty()) {
  125.             // No data -> No output
  126.             return;
  127.         }

  128.         try (Generator generator = fileFormat == FileFormat.KVN ?
  129.                                    new KvnGenerator(appendable, OemWriter.KVN_PADDING_WIDTH, outputName,
  130.                                                     maxRelativeOffset, unitsColumn) :
  131.                                    new XmlGenerator(appendable, XmlGenerator.DEFAULT_INDENT, outputName,
  132.                                                     maxRelativeOffset, unitsColumn > 0, null)) {

  133.             writer.writeHeader(generator, header);

  134.             // Loop on segments
  135.             for (final S segment : segments) {
  136.                 writeSegment(generator, segment);
  137.             }

  138.             writer.writeFooter(generator);

  139.         }

  140.     }

  141.     /** Write one segment.
  142.      * @param generator generator to use for producing output
  143.      * @param segment segment to write
  144.      * @param <C> type of the Cartesian coordinates
  145.      * @param <S> type of the segment
  146.      * @throws IOException if any buffer writing operations fails
  147.      */
  148.     public <C extends TimeStampedPVCoordinates, S extends EphemerisFile.EphemerisSegment<C>>
  149.         void writeSegment(final Generator generator, final S segment) throws IOException {

  150.         // override template metadata with segment values
  151.         if (segment instanceof OemSegment) {
  152.             final OemSegment oemSegment = (OemSegment) segment;
  153.             metadata.setReferenceFrame(oemSegment.getMetadata().getReferenceFrame());
  154.         } else {
  155.             metadata.setReferenceFrame(FrameFacade.map(segment.getFrame()));
  156.         }
  157.         metadata.setStartTime(segment.getStart());
  158.         metadata.setStopTime(segment.getStop());
  159.         metadata.setInterpolationDegree(segment.getInterpolationSamples() - 1);
  160.         writer.writeMetadata(generator, metadata);

  161.         // we enter data section
  162.         writer.startData(generator);

  163.         if (segment instanceof OemSegment) {
  164.             // write data comments
  165.             generator.writeComments(((OemSegment) segment).getData().getComments());
  166.         }

  167.         // Loop on orbit data
  168.         final CartesianDerivativesFilter filter = segment.getAvailableDerivatives();
  169.         if (filter == CartesianDerivativesFilter.USE_P) {
  170.             throw new OrekitException(OrekitMessages.MISSING_VELOCITY);
  171.         }
  172.         final boolean useAcceleration = filter.equals(CartesianDerivativesFilter.USE_PVA);
  173.         for (final TimeStampedPVCoordinates coordinates : segment.getCoordinates()) {
  174.             writer.writeOrbitEphemerisLine(generator, metadata, coordinates, useAcceleration);
  175.         }

  176.         if (segment instanceof OemSegment) {
  177.             // output covariance data
  178.             writer.writeCovariances(generator, metadata, ((OemSegment) segment).getCovarianceMatrices());
  179.         }

  180.         // we exit data section
  181.         writer.endData(generator);

  182.     }

  183. }