StreamingAemWriter.java

  1. /* Copyright 2002-2022 CS GROUP
  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.adm.aem;

  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.section.Header;
  24. import org.orekit.files.ccsds.utils.generation.Generator;
  25. import org.orekit.propagation.Propagator;
  26. import org.orekit.propagation.SpacecraftState;
  27. import org.orekit.propagation.sampling.OrekitFixedStepHandler;
  28. import org.orekit.time.AbsoluteDate;

  29. /**
  30.  * A writer for AEM files.
  31.  *
  32.  * <p> Each instance corresponds to a single AEM file.
  33.  *
  34.  * <p> This class can be used as a step handler for a {@link Propagator}.
  35.  *
  36.  * <pre>{@code
  37.  * Propagator propagator = ...; // pre-configured propagator
  38.  * AEMWriter  aemWriter  = ...; // pre-configured writer
  39.  *   try (Generator out = ...;  // set-up output stream
  40.  *        StreamingAemWriter sw = new StreamingAemWriter(out, aemWriter)) { // set-up streaming writer
  41.  *
  42.  *     // write segment 1
  43.  *     propagator.getMultiplexer().add(step, sw.newSegment());
  44.  *     propagator.propagate(startDate1, stopDate1);
  45.  *
  46.  *     ...
  47.  *
  48.  *     // write segment n
  49.  *     propagator.getMultiplexer().clear();
  50.  *     propagator.getMultiplexer().add(step, sw.newSegment());
  51.  *     propagator.propagate(startDateN, stopDateN);
  52.  *
  53.  *   }
  54.  * }</pre>
  55.  * @author Bryan Cazabonne
  56.  * @author Luc Maisonobe
  57.  * @see <a href="https://public.ccsds.org/Pubs/504x0b1c1.pdf">CCSDS 504.0-B-1 Attitude Data Messages</a>
  58.  * @see AemWriter
  59.  * @since 10.2
  60.  */
  61. public class StreamingAemWriter implements AutoCloseable {

  62.     /** Generator for AEM output. */
  63.     private final Generator generator;

  64.     /** Writer for the AEM message format. */
  65.     private final AemWriter writer;

  66.     /** Header. */
  67.     private final Header header;

  68.     /** Current metadata. */
  69.     private final AemMetadata metadata;

  70.     /** Indicator for writing header. */
  71.     private boolean headerWritePending;

  72.     /** Simple constructor.
  73.      * @param generator generator for AEM output
  74.      * @param writer writer for the AEM message format
  75.      * @param header file header (may be null)
  76.      * @param template template for metadata
  77.      * @since 11.0
  78.      */
  79.     public StreamingAemWriter(final Generator generator, final AemWriter writer,
  80.                               final Header header, final AemMetadata template) {
  81.         this.generator          = generator;
  82.         this.writer             = writer;
  83.         this.header             = header;
  84.         this.metadata           = template.copy(header == null ? writer.getDefaultVersion() : header.getFormatVersion());
  85.         this.headerWritePending = true;
  86.     }

  87.     /**
  88.      * Create a writer for a new AEM attitude ephemeris segment.
  89.      * <p> The returned writer can only write a single attitude ephemeris segment in an AEM.
  90.      * This method must be called to create a writer for each attitude ephemeris segment.
  91.      * @return a new AEM segment writer, ready for use.
  92.      */
  93.     public SegmentWriter newSegment() {
  94.         return new SegmentWriter();
  95.     }

  96.     /** {@inheritDoc} */
  97.     @Override
  98.     public void close() throws IOException {
  99.         writer.writeFooter(generator);
  100.     }

  101.     /** A writer for a segment of an AEM. */
  102.     public class SegmentWriter implements OrekitFixedStepHandler {

  103.         /**
  104.          * {@inheritDoc}
  105.          *
  106.          * <p> Sets the {@link AemMetadataKey#START_TIME} and {@link AemMetadataKey#STOP_TIME} in this
  107.          * segment's metadata if not already set by the user. Then calls {@link AemWriter#writeHeader(Generator, Header)
  108.          * writeHeader} if it is the first segment) and {@link AemWriter#writeMetadata(Generator, AemMetadata)} to start the segment.
  109.          */
  110.         @Override
  111.         public void init(final SpacecraftState s0, final AbsoluteDate t, final double step) {
  112.             try {
  113.                 final AbsoluteDate date = s0.getDate();
  114.                 if (t.isBefore(date)) {
  115.                     throw new OrekitException(OrekitMessages.NON_CHRONOLOGICALLY_SORTED_ENTRIES,
  116.                             date, t, date.durationFrom(t));
  117.                 }

  118.                 if (headerWritePending) {
  119.                     // we write the header only for the first segment
  120.                     writer.writeHeader(generator, header);
  121.                     headerWritePending = false;
  122.                 }

  123.                 metadata.setStartTime(date);
  124.                 metadata.setUseableStartTime(null);
  125.                 metadata.setUseableStopTime(null);
  126.                 metadata.setStopTime(t);
  127.                 if (metadata.getEndpoints().getFrameA() == null ||
  128.                     metadata.getEndpoints().getFrameA().asSpacecraftBodyFrame() == null) {
  129.                     // the external frame must be frame A
  130.                     metadata.getEndpoints().setFrameA(FrameFacade.map(s0.getAttitude().getReferenceFrame()));
  131.                 } else {
  132.                     // the external frame must be frame B
  133.                     metadata.getEndpoints().setFrameB(FrameFacade.map(s0.getAttitude().getReferenceFrame()));
  134.                 }
  135.                 writer.writeMetadata(generator, metadata);
  136.                 writer.startAttitudeBlock(generator);
  137.             } catch (IOException e) {
  138.                 throw new OrekitException(e, LocalizedCoreFormats.SIMPLE_MESSAGE, e.getLocalizedMessage());
  139.             }
  140.         }

  141.         /** {@inheritDoc}. */
  142.         @Override
  143.         public void handleStep(final SpacecraftState currentState) {
  144.             try {
  145.                 writer.writeAttitudeEphemerisLine(generator, metadata, currentState.getAttitude().getOrientation());
  146.             } catch (IOException e) {
  147.                 throw new OrekitException(e, LocalizedCoreFormats.SIMPLE_MESSAGE, e.getLocalizedMessage());
  148.             }
  149.         }

  150.         /** {@inheritDoc}. */
  151.         @Override
  152.         public void finish(final SpacecraftState finalState) {
  153.             try {
  154.                 writer.endAttitudeBlock(generator);
  155.             } catch (IOException e) {
  156.                 throw new OrekitException(e, LocalizedCoreFormats.SIMPLE_MESSAGE, e.getLocalizedMessage());
  157.             }
  158.         }

  159.     }

  160. }