StreamingOemWriter.java

  1. /* Contributed in the public domain.
  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.  * CS 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 org.hipparchus.exception.LocalizedCoreFormats;
  20. import org.orekit.errors.OrekitException;
  21. import org.orekit.errors.OrekitMessages;
  22. import org.orekit.files.ccsds.definitions.FrameFacade;
  23. import org.orekit.files.ccsds.ndm.odm.OdmHeader;
  24. import org.orekit.files.ccsds.utils.generation.Generator;
  25. import org.orekit.frames.Frame;
  26. import org.orekit.propagation.Propagator;
  27. import org.orekit.propagation.SpacecraftState;
  28. import org.orekit.propagation.sampling.OrekitFixedStepHandler;
  29. import org.orekit.time.AbsoluteDate;
  30. import org.orekit.utils.TimeStampedPVCoordinates;

  31. /**
  32.  * A writer for OEM files.
  33.  *
  34.  * <p> Each instance corresponds to a single OEM file. A new OEM ephemeris segment is
  35.  * started by calling {@link #newSegment()}.
  36.  *
  37.  * <p>
  38.  * The segments returned by this class can be used as step handlers for a {@link Propagator}.
  39.  * </p>
  40.  *
  41.  * <pre>{@code
  42.  * Propagator propagator = ...; // pre-configured propagator
  43.  * OEMWriter  aemWriter  = ...; // pre-configured writer
  44.  *   try (Generator out = ...;  // set-up output stream
  45.  *        StreamingOemWriter sw = new StreamingOemWriter(out, oemWriter, header, metadata)) { // set-up streaming writer
  46.  *
  47.  *     // write segment 1
  48.  *     propagator.getMultiplexer().add(step, sw.newSegment());
  49.  *     propagator.propagate(startDate1, stopDate1);
  50.  *
  51.  *     ...
  52.  *
  53.  *     // write segment n
  54.  *     propagator.getMultiplexer().clear();
  55.  *     propagator.getMultiplexer().add(step, sw.newSegment());
  56.  *     propagator.propagate(startDateN, stopDateN);
  57.  *
  58.  *   }
  59.  * }</pre>
  60.  *
  61.  *
  62.  * @author Evan Ward
  63.  * @see <a href="https://public.ccsds.org/Pubs/502x0b2c1.pdf">CCSDS 502.0-B-2 Orbit Data
  64.  *      Messages</a>
  65.  * @see <a href="https://public.ccsds.org/Pubs/500x0g4.pdf">CCSDS 500.0-G-4 Navigation
  66.  *      Data Definitions and Conventions</a>
  67.  * @see OemWriter
  68.  */
  69. public class StreamingOemWriter implements AutoCloseable {

  70.     /** Generator for OEM output. */
  71.     private final Generator generator;

  72.     /** Writer for the OEM message format. */
  73.     private final OemWriter writer;

  74.     /** Header. */
  75.     private final OdmHeader header;

  76.     /** Current metadata. */
  77.     private final OemMetadata metadata;

  78.     /** If the propagator's frame should be used. */
  79.     private final boolean useAttitudeFrame;

  80.     /** If acceleration should be included in the output. */
  81.     private final boolean includeAcceleration;

  82.     /** Indicator for writing header. */
  83.     private boolean headerWritePending;

  84.     /**
  85.      * Construct a writer that for each segment uses the reference frame of the
  86.      * first state's attitude.
  87.      *
  88.      * @param generator generator for OEM output
  89.      * @param writer    writer for the AEM message format
  90.      * @param header    file header (may be null)
  91.      * @param template  template for metadata
  92.      * @since 11.0
  93.      * @see #StreamingOemWriter(Generator, OemWriter, OdmHeader, OemMetadata, boolean)
  94.      */
  95.     public StreamingOemWriter(final Generator generator, final OemWriter writer,
  96.                               final OdmHeader header, final OemMetadata template) {
  97.         this(generator, writer, header, template, true);
  98.     }

  99.     /**
  100.      * Construct a writer that writes position, velocity, and acceleration at
  101.      * each time step.
  102.      *
  103.      * @param generator        generator for OEM output
  104.      * @param writer           writer for the AEM message format
  105.      * @param header           file header (may be null)
  106.      * @param template         template for metadata
  107.      * @param useAttitudeFrame if {@code true} then the reference frame for each
  108.      *                         segment is taken from the first state's attitude.
  109.      *                         Otherwise the {@code template}'s reference frame
  110.      *                         is used, {@link OemMetadata#getReferenceFrame()}.
  111.      * @see #StreamingOemWriter(Generator, OemWriter, OdmHeader, OemMetadata, boolean, boolean)
  112.      * @since 11.1.2
  113.      */
  114.     public StreamingOemWriter(final Generator generator, final OemWriter writer,
  115.                               final OdmHeader header, final OemMetadata template,
  116.                               final boolean useAttitudeFrame) {
  117.         this(generator, writer, header, template, useAttitudeFrame, true);
  118.     }

  119.     /**
  120.      * Simple constructor.
  121.      *
  122.      * @param generator           generator for OEM output
  123.      * @param writer              writer for the AEM message format
  124.      * @param header              file header (may be null)
  125.      * @param template            template for metadata
  126.      * @param useAttitudeFrame    if {@code true} then the reference frame for
  127.      *                            each segment is taken from the first state's
  128.      *                            attitude. Otherwise the {@code template}'s
  129.      *                            reference frame is used, {@link
  130.      *                            OemMetadata#getReferenceFrame()}.
  131.      * @param includeAcceleration if {@code true} then acceleration is included
  132.      *                            in the OEM file produced. Otherwise only
  133.      *                            position and velocity is included.
  134.      * @since 11.1.2
  135.      */
  136.     public StreamingOemWriter(final Generator generator, final OemWriter writer,
  137.                               final OdmHeader header, final OemMetadata template,
  138.                               final boolean useAttitudeFrame,
  139.                               final boolean includeAcceleration) {
  140.         this.generator          = generator;
  141.         this.writer             = writer;
  142.         this.header             = header;
  143.         this.metadata           = template.copy(header == null ? writer.getDefaultVersion() : header.getFormatVersion());
  144.         this.useAttitudeFrame   = useAttitudeFrame;
  145.         this.includeAcceleration = includeAcceleration;
  146.         this.headerWritePending = true;
  147.     }

  148.     /**
  149.      * Create a writer for a new OEM ephemeris segment.
  150.      * <p> The returned writer can only write a single ephemeris segment in an OEM.
  151.      * This method must be called to create a writer for each ephemeris segment.
  152.      * @return a new OEM segment writer, ready for use.
  153.      */
  154.     public SegmentWriter newSegment() {
  155.         return new SegmentWriter();
  156.     }

  157.     /** {@inheritDoc} */
  158.     @Override
  159.     public void close() throws IOException {
  160.         writer.writeFooter(generator);
  161.     }

  162.     /** A writer for a segment of an OEM. */
  163.     public class SegmentWriter implements OrekitFixedStepHandler {

  164.         /** Reference frame of this segment. */
  165.         private Frame frame;

  166.         /** Empty constructor.
  167.          * <p>
  168.          * This constructor is not strictly necessary, but it prevents spurious
  169.          * javadoc warnings with JDK 18 and later.
  170.          * </p>
  171.          * @since 12.0
  172.          */
  173.         public SegmentWriter() {
  174.             // nothing to do
  175.         }

  176.         /**
  177.          * {@inheritDoc}
  178.          *
  179.          * <p>Writes the header automatically on first segment.
  180.          * Sets the {@link OemMetadataKey#START_TIME} and {@link OemMetadataKey#STOP_TIME} in this
  181.          * segment's metadata if not already set by the user.
  182.          */
  183.         @Override
  184.         public void init(final SpacecraftState s0, final AbsoluteDate t, final double step) {
  185.             try {
  186.                 final AbsoluteDate date = s0.getDate();
  187.                 if (t.isBefore(date)) {
  188.                     throw new OrekitException(OrekitMessages.NON_CHRONOLOGICALLY_SORTED_ENTRIES,
  189.                             date, t, date.durationFrom(t));
  190.                 }

  191.                 if (headerWritePending) {
  192.                     // we write the header only for the first segment
  193.                     writer.writeHeader(generator, header);
  194.                     headerWritePending = false;
  195.                 }

  196.                 metadata.setStartTime(date);
  197.                 metadata.setUseableStartTime(null);
  198.                 metadata.setUseableStopTime(null);
  199.                 metadata.setStopTime(t);
  200.                 if (useAttitudeFrame) {
  201.                     frame = s0.getAttitude().getReferenceFrame();
  202.                     metadata.setReferenceFrame(FrameFacade.map(frame));
  203.                 } else {
  204.                     frame = metadata.getFrame();
  205.                 }
  206.                 writer.writeMetadata(generator, metadata);
  207.                 writer.startData(generator);
  208.             } catch (IOException e) {
  209.                 throw new OrekitException(e, LocalizedCoreFormats.SIMPLE_MESSAGE, e.getLocalizedMessage());
  210.             }
  211.         }

  212.         /** {@inheritDoc}. */
  213.         @Override
  214.         public void handleStep(final SpacecraftState currentState) {
  215.             try {
  216.                 final TimeStampedPVCoordinates pv =
  217.                         currentState.getPVCoordinates(frame);
  218.                 writer.writeOrbitEphemerisLine(generator, metadata, pv, includeAcceleration);
  219.             } catch (IOException e) {
  220.                 throw new OrekitException(e, LocalizedCoreFormats.SIMPLE_MESSAGE, e.getLocalizedMessage());
  221.             }
  222.         }

  223.         /** {@inheritDoc}. */
  224.         @Override
  225.         public void finish(final SpacecraftState finalState) {
  226.             try {
  227.                 writer.endData(generator);
  228.             } catch (IOException e) {
  229.                 throw new OrekitException(e, LocalizedCoreFormats.SIMPLE_MESSAGE, e.getLocalizedMessage());
  230.             }
  231.         }

  232.     }

  233. }