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.frames.Frame;
28 import org.orekit.propagation.Propagator;
29 import org.orekit.propagation.SpacecraftState;
30 import org.orekit.propagation.sampling.OrekitFixedStepHandler;
31 import org.orekit.time.AbsoluteDate;
32 import org.orekit.utils.TimeStampedPVCoordinates;
33
34 /**
35 * A writer for OEM files.
36 *
37 * <p> Each instance corresponds to a single OEM file. A new OEM ephemeris segment is
38 * started by calling {@link #newSegment()}.
39 *
40 * <p> This class can be used as a step handler for a {@link Propagator}.
41 *
42 * <pre>{@code
43 * Propagator propagator = ...; // pre-configured propagator
44 * OEMWriter aemWriter = ...; // pre-configured writer
45 * try (Generator out = ...; // set-up output stream
46 * StreamingOemWriter sw = new StreamingOemWriter(out, oemWriter)) { // set-up streaming writer
47 *
48 * // write segment 1
49 * propagator.getMultiplexer().add(step, sw.newSegment());
50 * propagator.propagate(startDate1, stopDate1);
51 *
52 * ...
53 *
54 * // write segment n
55 * propagator.getMultiplexer().clear();
56 * propagator.getMultiplexer().add(step, sw.newSegment());
57 * propagator.propagate(startDateN, stopDateN);
58 *
59 * }
60 * }</pre>
61 *
62 *
63 * @author Evan Ward
64 * @see <a href="https://public.ccsds.org/Pubs/502x0b2c1.pdf">CCSDS 502.0-B-2 Orbit Data
65 * Messages</a>
66 * @see <a href="https://public.ccsds.org/Pubs/500x0g4.pdf">CCSDS 500.0-G-4 Navigation
67 * Data Definitions and Conventions</a>
68 * @see OemWriter
69 */
70 public class StreamingOemWriter implements AutoCloseable {
71
72 /** Generator for OEM output. */
73 private final Generator generator;
74
75 /** Writer for the OEM message format. */
76 private final OemWriter writer;
77
78 /** Header. */
79 private final Header header;
80
81 /** Current metadata. */
82 private final OemMetadata metadata;
83
84 /** If the propagator's frame should be used. */
85 private final boolean useAttitudeFrame;
86
87 /** If acceleration should be included in the output. */
88 private final boolean includeAcceleration;
89
90 /** Indicator for writing header. */
91 private boolean headerWritePending;
92
93 /**
94 * Construct a writer that for each segment uses the reference frame of the
95 * first state's attitude.
96 *
97 * @param generator generator for OEM output
98 * @param writer writer for the AEM message format
99 * @param header file header (may be null)
100 * @param template template for metadata
101 * @since 11.0
102 * @see #StreamingOemWriter(Generator, OemWriter, Header, OemMetadata, boolean)
103 */
104 public StreamingOemWriter(final Generator generator, final OemWriter writer,
105 final Header header, final OemMetadata template) {
106 this(generator, writer, header, template, true);
107 }
108
109 /**
110 * Construct a writer that writes position, velocity, and acceleration at
111 * each time step.
112 *
113 * @param generator generator for OEM output
114 * @param writer writer for the AEM message format
115 * @param header file header (may be null)
116 * @param template template for metadata
117 * @param useAttitudeFrame if {@code true} then the reference frame for each
118 * segment is taken from the first state's attitude.
119 * Otherwise the {@code template}'s reference frame
120 * is used, {@link OemMetadata#getReferenceFrame()}.
121 * @see #StreamingOemWriter(Generator, OemWriter, Header, OemMetadata,
122 * boolean, boolean)
123 * @since 11.1.2
124 */
125 public StreamingOemWriter(final Generator generator, final OemWriter writer,
126 final Header header, final OemMetadata template,
127 final boolean useAttitudeFrame) {
128 this(generator, writer, header, template, useAttitudeFrame, true);
129 }
130
131 /**
132 * Simple constructor.
133 *
134 * @param generator generator for OEM output
135 * @param writer writer for the AEM message format
136 * @param header file header (may be null)
137 * @param template template for metadata
138 * @param useAttitudeFrame if {@code true} then the reference frame for
139 * each segment is taken from the first state's
140 * attitude. Otherwise the {@code template}'s
141 * reference frame is used, {@link
142 * OemMetadata#getReferenceFrame()}.
143 * @param includeAcceleration if {@code true} then acceleration is included
144 * in the OEM file produced. Otherwise only
145 * position and velocity is included.
146 * @since 11.1.2
147 */
148 public StreamingOemWriter(final Generator generator, final OemWriter writer,
149 final Header header, final OemMetadata template,
150 final boolean useAttitudeFrame,
151 final boolean includeAcceleration) {
152 this.generator = generator;
153 this.writer = writer;
154 this.header = header;
155 this.metadata = template.copy(header == null ? writer.getDefaultVersion() : header.getFormatVersion());
156 this.useAttitudeFrame = useAttitudeFrame;
157 this.includeAcceleration = includeAcceleration;
158 this.headerWritePending = true;
159 }
160
161 /**
162 * Create a writer for a new OEM ephemeris segment.
163 * <p> The returned writer can only write a single ephemeris segment in an OEM.
164 * This method must be called to create a writer for each ephemeris segment.
165 * @return a new OEM segment writer, ready for use.
166 */
167 public SegmentWriter newSegment() {
168 return new SegmentWriter();
169 }
170
171 /** {@inheritDoc} */
172 @Override
173 public void close() throws IOException {
174 writer.writeFooter(generator);
175 }
176
177 /** A writer for a segment of an OEM. */
178 public class SegmentWriter implements OrekitFixedStepHandler {
179
180 /** Reference frame of this segment. */
181 private Frame frame;
182
183 /**
184 * {@inheritDoc}
185 *
186 * <p> Sets the {@link OemMetadataKey#START_TIME} and {@link OemMetadataKey#STOP_TIME} in this
187 * segment's metadata if not already set by the user. Then calls {@link OemWriter#writeHeader(Generator, Header)
188 * writeHeader} if it is the first segment) and {@link OemWriter#writeMetadata(Generator, OemMetadata)}
189 * to start the segment.
190 */
191 @Override
192 public void init(final SpacecraftState s0, final AbsoluteDate t, final double step) {
193 try {
194 final AbsoluteDate date = s0.getDate();
195 if (t.isBefore(date)) {
196 throw new OrekitException(OrekitMessages.NON_CHRONOLOGICALLY_SORTED_ENTRIES,
197 date, t, date.durationFrom(t));
198 }
199
200 if (headerWritePending) {
201 // we write the header only for the first segment
202 writer.writeHeader(generator, header);
203 headerWritePending = false;
204 }
205
206 metadata.setStartTime(date);
207 metadata.setUseableStartTime(null);
208 metadata.setUseableStopTime(null);
209 metadata.setStopTime(t);
210 if (useAttitudeFrame) {
211 frame = s0.getAttitude().getReferenceFrame();
212 metadata.setReferenceFrame(FrameFacade.map(frame));
213 } else {
214 frame = metadata.getFrame();
215 }
216 writer.writeMetadata(generator, metadata);
217 writer.startData(generator);
218 } catch (IOException e) {
219 throw new OrekitException(e, LocalizedCoreFormats.SIMPLE_MESSAGE, e.getLocalizedMessage());
220 }
221 }
222
223 /** {@inheritDoc}. */
224 @Override
225 public void handleStep(final SpacecraftState currentState) {
226 try {
227 final TimeStampedPVCoordinates pv =
228 currentState.getPVCoordinates(frame);
229 writer.writeOrbitEphemerisLine(generator, metadata, pv, includeAcceleration);
230 } catch (IOException e) {
231 throw new OrekitException(e, LocalizedCoreFormats.SIMPLE_MESSAGE, e.getLocalizedMessage());
232 }
233 }
234
235 /** {@inheritDoc}. */
236 @Override
237 public void finish(final SpacecraftState finalState) {
238 try {
239 writer.endData(generator);
240 } catch (IOException e) {
241 throw new OrekitException(e, LocalizedCoreFormats.SIMPLE_MESSAGE, e.getLocalizedMessage());
242 }
243 }
244
245 }
246
247 }