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.AccurateFormatter;
  33. import org.orekit.utils.CartesianDerivativesFilter;
  34. import org.orekit.utils.Formatter;
  35. import org.orekit.utils.TimeStampedPVCoordinates;

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

  47.     /** Underlying writer. */
  48.     private final OemWriter writer;

  49.     /** Header. */
  50.     private final OdmHeader header;

  51.     /** Current metadata. */
  52.     private final OemMetadata metadata;

  53.     /** File format to use. */
  54.     private final FileFormat fileFormat;

  55.     /** Output name for error messages. */
  56.     private final String outputName;

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

  61.     /** Column number for aligning units. */
  62.     private final int unitsColumn;

  63.     /** Used to format dates and doubles to string. */
  64.     private final Formatter formatter;

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

  103.     /**
  104.      * Constructor used to create a new OEM writer configured with the necessary parameters
  105.      * to successfully fill in all required fields that aren't part of a standard object.
  106.      * <p>
  107.      * If the mandatory header entries are not present (or if header is null),
  108.      * built-in defaults will be used
  109.      * </p>
  110.      * <p>
  111.      * The writer is built from the complete header and partial metadata. The template
  112.      * metadata is used to initialize and independent local copy, that will be updated
  113.      * as new segments are written (with at least the segment start and stop will change,
  114.      * but some other parts may change too). The {@code template} argument itself is not
  115.      * changed.
  116.      * </p>
  117.      * @param writer underlying writer
  118.      * @param header file header (may be null)
  119.      * @param template template for metadata
  120.      * @param fileFormat file format to use
  121.      * @param outputName output name for error messages
  122.      * @param maxRelativeOffset maximum offset in seconds to use relative dates
  123.      * (if a date is too far from reference, it will be displayed as calendar elements)
  124.      * @param unitsColumn columns number for aligning units (if negative or zero, units are not output)
  125.      * @since 12.0
  126.      */
  127.     public EphemerisOemWriter(final OemWriter writer,
  128.                               final OdmHeader header, final OemMetadata template,
  129.                               final FileFormat fileFormat, final String outputName,
  130.                               final double maxRelativeOffset, final int unitsColumn) {
  131.         this(writer, header, template, fileFormat, outputName, maxRelativeOffset, unitsColumn, new AccurateFormatter());
  132.     }

  133.     /** {@inheritDoc}
  134.      * <p>
  135.      * As {@code EphemerisFile.SatelliteEphemeris} does not have all the entries
  136.      * from {@link OemMetadata}, the only values that will be extracted from the
  137.      * {@code ephemerisFile} will be the start time, stop time, reference frame, interpolation
  138.      * method and interpolation degree. The missing values (like object name, local spacecraft
  139.      * body frame...) will be inherited from the template  metadata set at writer
  140.      * {@link #EphemerisOemWriter(OemWriter, OdmHeader, OemMetadata, FileFormat, String, double, int) construction}.
  141.      * </p>
  142.      */
  143.     @Override
  144.     public <C extends TimeStampedPVCoordinates, S extends EphemerisFile.EphemerisSegment<C>>
  145.         void write(final Appendable appendable, final EphemerisFile<C, S> ephemerisFile)
  146.         throws IOException {

  147.         if (appendable == null) {
  148.             throw new OrekitIllegalArgumentException(OrekitMessages.NULL_ARGUMENT, "writer");
  149.         }

  150.         if (ephemerisFile == null) {
  151.             return;
  152.         }

  153.         final SatelliteEphemeris<C, S> satEphem = ephemerisFile.getSatellites().get(metadata.getObjectID());
  154.         if (satEphem == null) {
  155.             throw new OrekitIllegalArgumentException(OrekitMessages.VALUE_NOT_FOUND,
  156.                                                      metadata.getObjectID(), "ephemerisFile");
  157.         }

  158.         // Get ephemeris segments to output.
  159.         final List<S> segments = satEphem.getSegments();
  160.         if (segments.isEmpty()) {
  161.             // No data -> No output
  162.             return;
  163.         }

  164.         try (Generator generator = fileFormat == FileFormat.KVN ?
  165.                                    new KvnGenerator(appendable, OemWriter.KVN_PADDING_WIDTH, outputName,
  166.                                                     maxRelativeOffset, unitsColumn, formatter) :
  167.                                    new XmlGenerator(appendable, XmlGenerator.DEFAULT_INDENT, outputName,
  168.                                                     maxRelativeOffset, unitsColumn > 0, null, formatter)) {

  169.             writer.writeHeader(generator, header);

  170.             // Loop on segments
  171.             for (final S segment : segments) {
  172.                 writeSegment(generator, segment);
  173.             }

  174.             writer.writeFooter(generator);

  175.         }

  176.     }

  177.     /** Write one segment.
  178.      * @param generator generator to use for producing output
  179.      * @param segment segment to write
  180.      * @param <C> type of the Cartesian coordinates
  181.      * @param <S> type of the segment
  182.      * @throws IOException if any buffer writing operations fails
  183.      */
  184.     public <C extends TimeStampedPVCoordinates, S extends EphemerisFile.EphemerisSegment<C>>
  185.         void writeSegment(final Generator generator, final S segment) throws IOException {

  186.         // override template metadata with segment values
  187.         if (segment instanceof OemSegment) {
  188.             final OemSegment oemSegment = (OemSegment) segment;
  189.             metadata.setReferenceFrame(oemSegment.getMetadata().getReferenceFrame());
  190.         } else {
  191.             metadata.setReferenceFrame(FrameFacade.map(segment.getFrame()));
  192.         }
  193.         metadata.setStartTime(segment.getStart());
  194.         metadata.setStopTime(segment.getStop());
  195.         metadata.setInterpolationDegree(segment.getInterpolationSamples() - 1);
  196.         writer.writeMetadata(generator, metadata);

  197.         // we enter data section
  198.         writer.startData(generator);

  199.         if (segment instanceof OemSegment) {
  200.             // write data comments
  201.             generator.writeComments(((OemSegment) segment).getData().getComments());
  202.         }

  203.         // Loop on orbit data
  204.         final CartesianDerivativesFilter filter = segment.getAvailableDerivatives();
  205.         if (filter == CartesianDerivativesFilter.USE_P) {
  206.             throw new OrekitException(OrekitMessages.MISSING_VELOCITY);
  207.         }
  208.         final boolean useAcceleration = filter.equals(CartesianDerivativesFilter.USE_PVA);
  209.         for (final TimeStampedPVCoordinates coordinates : segment.getCoordinates()) {
  210.             writer.writeOrbitEphemerisLine(generator, metadata, coordinates, useAcceleration);
  211.         }

  212.         if (segment instanceof OemSegment) {
  213.             // output covariance data
  214.             writer.writeCovariances(generator, metadata, ((OemSegment) segment).getCovarianceMatrices());
  215.         }

  216.         // we exit data section
  217.         writer.endData(generator);

  218.     }

  219. }