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 }