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  
19  import java.io.IOException;
20  
21  import org.hipparchus.exception.LocalizedCoreFormats;
22  import org.orekit.errors.OrekitException;
23  import org.orekit.errors.OrekitMessages;
24  import org.orekit.files.ccsds.definitions.FrameFacade;
25  import org.orekit.files.ccsds.section.Header;
26  import org.orekit.files.ccsds.utils.generation.Generator;
27  import org.orekit.propagation.Propagator;
28  import org.orekit.propagation.SpacecraftState;
29  import org.orekit.propagation.sampling.OrekitFixedStepHandler;
30  import org.orekit.time.AbsoluteDate;
31  
32  /**
33   * A writer for OEM files.
34   *
35   * <p> Each instance corresponds to a single OEM file. A new OEM ephemeris segment is
36   * started by calling {@link #newSegment()}.
37   *
38   * <p> This class can be used as a step handler for a {@link Propagator}.
39   *
40   * <pre>{@code
41   * Propagator propagator = ...; // pre-configured propagator
42   * OEMWriter  aemWriter  = ...; // pre-configured writer
43   *   try (Generator out = ...;  // set-up output stream
44   *        StreamingOemWriter sw = new StreamingOemWriter(out, oemWriter)) { // set-up streaming writer
45   *
46   *     // write segment 1
47   *     propagator.getMultiplexer().add(step, sw.newSegment());
48   *     propagator.propagate(startDate1, stopDate1);
49   *
50   *     ...
51   *
52   *     // write segment n
53   *     propagator.getMultiplexer().clear();
54   *     propagator.getMultiplexer().add(step, sw.newSegment());
55   *     propagator.propagate(startDateN, stopDateN);
56   *
57   *   }
58   * }</pre>
59   *
60   *
61   * @author Evan Ward
62   * @see <a href="https://public.ccsds.org/Pubs/502x0b2c1.pdf">CCSDS 502.0-B-2 Orbit Data
63   *      Messages</a>
64   * @see <a href="https://public.ccsds.org/Pubs/500x0g4.pdf">CCSDS 500.0-G-4 Navigation
65   *      Data Definitions and Conventions</a>
66   * @see OemWriter
67   */
68  public class StreamingOemWriter implements AutoCloseable {
69  
70      /** Generator for OEM output. */
71      private final Generator generator;
72  
73      /** Writer for the OEM message format. */
74      private final OemWriter writer;
75  
76      /** Header. */
77      private final Header header;
78  
79      /** Current metadata. */
80      private final OemMetadata metadata;
81  
82      /** Indicator for writing header. */
83      private boolean headerWritePending;
84  
85      /** Simple constructor.
86       * @param generator generator for OEM output
87       * @param writer writer for the AEM message format
88       * @param header file header (may be null)
89       * @param template template for metadata
90       * @since 11.0
91       */
92      public StreamingOemWriter(final Generator generator, final OemWriter writer,
93                                final Header header, final OemMetadata template) {
94          this.generator          = generator;
95          this.writer             = writer;
96          this.header             = header;
97          this.metadata           = template.copy(header == null ? writer.getDefaultVersion() : header.getFormatVersion());
98          this.headerWritePending = true;
99      }
100 
101     /**
102      * Create a writer for a new OEM ephemeris segment.
103      * <p> The returned writer can only write a single ephemeris segment in an OEM.
104      * This method must be called to create a writer for each ephemeris segment.
105      * @return a new OEM segment writer, ready for use.
106      */
107     public SegmentWriter newSegment() {
108         return new SegmentWriter();
109     }
110 
111     /** {@inheritDoc} */
112     @Override
113     public void close() throws IOException {
114         writer.writeFooter(generator);
115     }
116 
117     /** A writer for a segment of an OEM. */
118     public class SegmentWriter implements OrekitFixedStepHandler {
119 
120         /**
121          * {@inheritDoc}
122          *
123          * <p> Sets the {@link OemMetadataKey#START_TIME} and {@link OemMetadataKey#STOP_TIME} in this
124          * segment's metadata if not already set by the user. Then calls {@link OemWriter#writeHeader(Generator, Header)
125          * writeHeader} if it is the first segment) and {@link OemWriter#writeMetadata(Generator, OemMetadata)}
126          * to start the segment.
127          */
128         @Override
129         public void init(final SpacecraftState s0, final AbsoluteDate t, final double step) {
130             try {
131                 final AbsoluteDate date = s0.getDate();
132                 if (t.isBefore(date)) {
133                     throw new OrekitException(OrekitMessages.NON_CHRONOLOGICALLY_SORTED_ENTRIES,
134                             date, t, date.durationFrom(t));
135                 }
136 
137                 if (headerWritePending) {
138                     // we write the header only for the first segment
139                     writer.writeHeader(generator, header);
140                     headerWritePending = false;
141                 }
142 
143                 metadata.setStartTime(date);
144                 metadata.setUseableStartTime(null);
145                 metadata.setUseableStopTime(null);
146                 metadata.setStopTime(t);
147                 metadata.setReferenceFrame(FrameFacade.map(s0.getAttitude().getReferenceFrame()));
148                 writer.writeMetadata(generator, metadata);
149                 writer.startData(generator);
150             } catch (IOException e) {
151                 throw new OrekitException(e, LocalizedCoreFormats.SIMPLE_MESSAGE, e.getLocalizedMessage());
152             }
153         }
154 
155         /** {@inheritDoc}. */
156         @Override
157         public void handleStep(final SpacecraftState currentState) {
158             try {
159                 writer.writeOrbitEphemerisLine(generator, metadata, currentState.getPVCoordinates(), true);
160             } catch (IOException e) {
161                 throw new OrekitException(e, LocalizedCoreFormats.SIMPLE_MESSAGE, e.getLocalizedMessage());
162             }
163         }
164 
165         /** {@inheritDoc}. */
166         @Override
167         public void finish(final SpacecraftState finalState) {
168             try {
169                 writer.endData(generator);
170             } catch (IOException e) {
171                 throw new OrekitException(e, LocalizedCoreFormats.SIMPLE_MESSAGE, e.getLocalizedMessage());
172             }
173         }
174 
175     }
176 
177 }