StreamingCpfWriter.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.ilrs;

  18. import java.io.IOException;
  19. import java.util.Locale;

  20. import org.hipparchus.exception.LocalizedCoreFormats;
  21. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  22. import org.orekit.errors.OrekitException;
  23. import org.orekit.frames.Frame;
  24. import org.orekit.propagation.Propagator;
  25. import org.orekit.propagation.SpacecraftState;
  26. import org.orekit.propagation.sampling.OrekitFixedStepHandler;
  27. import org.orekit.time.AbsoluteDate;
  28. import org.orekit.time.DateTimeComponents;
  29. import org.orekit.time.TimeScale;
  30. import org.orekit.utils.TimeStampedPVCoordinates;

  31. /**
  32.  * A writer for CPF files.
  33.  *
  34.  * <p> Each instance corresponds to a single CPF file.
  35.  *
  36.  * <p> This class can be used as a step handler for a {@link Propagator}.
  37.  * The following example shows its use as a step handler.
  38.  *
  39.  * <p>
  40.  * <b>Note:</b> By default, only required header keys are wrote (H1 and H2). Furthermore, only position data can be written.
  41.  * Other keys (optionals) are simply ignored.
  42.  * Contributions are welcome to support more fields in the format.
  43.  *
  44.  * @author Bryan Cazabonne
  45.  * @since 10.3
  46.  */
  47. public class StreamingCpfWriter {

  48.     /** New line separator for output file. */
  49.     private static final String NEW_LINE = "\n";

  50.     /** String A2 Format. */
  51.     private static final String A2 = "%2s";

  52.     /** String A3 Format. */
  53.     private static final String A3 = "%3s";

  54.     /** String A4 Format. */
  55.     private static final String A4 = "%4s";

  56.     /** String A8 Format. */
  57.     private static final String A8 = "%8s";

  58.     /** String A10 Format. */
  59.     private static final String A10 = "%10s";

  60.     /** Integer I1 Format. */
  61.     private static final String I1 = "%1d";

  62.     /** Integer I2 Format. */
  63.     private static final String I2 = "%2d";

  64.     /** Integer I3 Format. */
  65.     private static final String I3 = "%3d";

  66.     /** Integer I4 Format. */
  67.     private static final String I4 = "%4d";

  68.     /** Integer I5 Format. */
  69.     private static final String I5 = "%5d";

  70.     /** Real 13.6 Format. */
  71.     private static final String F13_6 = "%13.6f";

  72.     /** Real 17.3 Format. */
  73.     private static final String F17_3 = "%17.3f";

  74.     /** Space. */
  75.     private static final String SPACE = " ";

  76.     /** File format. */
  77.     private static final String FORMAT = "CPF";

  78.     /** Default locale. */
  79.     private static final Locale STANDARDIZED_LOCALE = Locale.US;

  80.     /** Default value for direction flag in position record. */
  81.     private static final int DEFAULT_DIRECTION_FLAG = 0;

  82.     /** Output stream. */
  83.     private final Appendable writer;

  84.     /** Time scale for all dates. */
  85.     private final TimeScale timeScale;

  86.     /** Container for header data. */
  87.     private final CPFHeader header;

  88.     /**
  89.      * Create an OEM writer than streams data to the given output stream.
  90.      *
  91.      * @param writer     the output stream for the CPF file.
  92.      * @param timeScale  for all times in the CPF
  93.      * @param header     container for header data
  94.      */
  95.     public StreamingCpfWriter(final Appendable writer,
  96.                               final TimeScale timeScale,
  97.                               final CPFHeader header) {

  98.         this.writer     = writer;
  99.         this.timeScale  = timeScale;
  100.         this.header     = header;
  101.     }

  102.     /**
  103.      * Writes the CPF header for the file.
  104.      * @throws IOException if the stream cannot write to stream
  105.      */
  106.     public void writeHeader() throws IOException {

  107.         // Write H1
  108.         HeaderLineWriter.H1.write(header, writer, timeScale);
  109.         writer.append(NEW_LINE);

  110.         // Write H2
  111.         HeaderLineWriter.H2.write(header, writer, timeScale);
  112.         writer.append(NEW_LINE);

  113.         // End of header
  114.         writer.append("H9");
  115.         writer.append(NEW_LINE);

  116.     }

  117.     /**
  118.      * Write end of file.
  119.      * @throws IOException if the stream cannot write to stream
  120.      */
  121.     public void writeEndOfFile() throws IOException {
  122.         writer.append("99");
  123.     }

  124.     /**
  125.      * Create a writer for a new CPF ephemeris segment.
  126.      * <p>
  127.      * The returned writer can only write a single ephemeris segment in a CPF.
  128.      * </p>
  129.      * @param frame the reference frame to use for the segment. If this value is
  130.      *              {@code null} then {@link Segment#handleStep(SpacecraftState,
  131.      *              boolean)} will throw a {@link NullPointerException}.
  132.      * @return a new CPF segment, ready for writing.
  133.      */
  134.     public Segment newSegment(final Frame frame) {
  135.         return new Segment(frame);
  136.     }

  137.     /**
  138.      * Write a String value in the file.
  139.      * @param cpfWriter writer
  140.      * @param format format
  141.      * @param value value
  142.      * @throws IOException if value cannot be written
  143.      */
  144.     private static void writeValue(final Appendable cpfWriter, final String format, final String value)
  145.         throws IOException {
  146.         cpfWriter.append(String.format(STANDARDIZED_LOCALE, format, value)).append(SPACE);
  147.     }

  148.     /**
  149.      * Write a integer value in the file.
  150.      * @param cpfWriter writer
  151.      * @param format format
  152.      * @param value value
  153.      * @throws IOException if value cannot be written
  154.      */
  155.     private static void writeValue(final Appendable cpfWriter, final String format, final int value)
  156.         throws IOException {
  157.         cpfWriter.append(String.format(STANDARDIZED_LOCALE, format, value)).append(SPACE);
  158.     }

  159.     /**
  160.      * Write a real value in the file.
  161.      * @param cpfWriter writer
  162.      * @param format format
  163.      * @param value value
  164.      * @throws IOException if value cannot be written
  165.      */
  166.     private static void writeValue(final Appendable cpfWriter, final String format, final double value)
  167.         throws IOException {
  168.         cpfWriter.append(String.format(STANDARDIZED_LOCALE, format, value)).append(SPACE);
  169.     }

  170.     /**
  171.      * Write a String value in the file.
  172.      * @param cpfWriter writer
  173.      * @param format format
  174.      * @param value value
  175.      * @throws IOException if value cannot be writtent
  176.      */
  177.     private static void writeValue(final Appendable cpfWriter, final String format, final boolean value)
  178.         throws IOException {
  179.         // Change to an integer value
  180.         final int intValue = value ? 1 : 0;
  181.         writeValue(cpfWriter, format, intValue);
  182.     }

  183.     /** A writer for a segment of a CPF. */
  184.     public class Segment implements OrekitFixedStepHandler {

  185.         /** Reference frame of the output states. */
  186.         private final Frame frame;

  187.         /**
  188.          * Create a new segment writer.
  189.          *
  190.          * @param frame    for the output states. Used by {@link #handleStep(SpacecraftState,
  191.          *                 boolean)}.
  192.          */
  193.         private Segment(final Frame frame) {
  194.             this.frame = frame;
  195.         }

  196.         /** {@inheritDoc}. */
  197.         @Override
  198.         public void handleStep(final SpacecraftState currentState, final boolean isLast) {
  199.             try {

  200.                 // Write ephemeris line
  201.                 writeEphemerisLine(currentState.getPVCoordinates(frame));

  202.                 // Write end of file
  203.                 if (isLast) {
  204.                     writeEndOfFile();
  205.                 }

  206.             } catch (IOException e) {
  207.                 throw new OrekitException(e, LocalizedCoreFormats.SIMPLE_MESSAGE,
  208.                                           e.getLocalizedMessage());
  209.             }

  210.         }

  211.         /**
  212.          * Write a single ephemeris line This method does not
  213.          * write the velocity terms.
  214.          *
  215.          * @param pv the time, position, and velocity to write.
  216.          * @throws IOException if the output stream throws one while writing.
  217.          */
  218.         public void writeEphemerisLine(final TimeStampedPVCoordinates pv)
  219.             throws IOException {

  220.             // Record type and direction flag
  221.             writeValue(writer, A2, "10");
  222.             writeValue(writer, I1, DEFAULT_DIRECTION_FLAG);

  223.             // Epoch
  224.             final AbsoluteDate epoch = pv.getDate();
  225.             final DateTimeComponents dtc = epoch.getComponents(timeScale);
  226.             writeValue(writer, I5, dtc.getDate().getMJD());
  227.             writeValue(writer, F13_6, dtc.getTime().getSecondsInLocalDay());

  228.             // Leap second flag (default 0)
  229.             writeValue(writer, I2, 0);

  230.             // Position
  231.             final Vector3D position = pv.getPosition();
  232.             writeValue(writer, F17_3, position.getX());
  233.             writeValue(writer, F17_3, position.getY());
  234.             writeValue(writer, F17_3, position.getZ());

  235.             // New line
  236.             writer.append(NEW_LINE);

  237.         }

  238.     }

  239.     /** Writer for specific header lines. */
  240.     public enum HeaderLineWriter {

  241.         /** Header first line. */
  242.         H1("H1") {

  243.             /** {@inheritDoc} */
  244.             @Override
  245.             public void write(final CPFHeader cpfHeader, final Appendable cpfWriter,
  246.                               final TimeScale utc) throws IOException {

  247.                 // write first keys
  248.                 writeValue(cpfWriter, A2, getIdentifier());
  249.                 writeValue(cpfWriter, A3, FORMAT);
  250.                 writeValue(cpfWriter, I2, cpfHeader.getVersion());
  251.                 writeValue(cpfWriter, A3, cpfHeader.getSource());
  252.                 writeValue(cpfWriter, I4, cpfHeader.getProductionEpoch().getYear());
  253.                 writeValue(cpfWriter, I2, cpfHeader.getProductionEpoch().getMonth());
  254.                 writeValue(cpfWriter, I2, cpfHeader.getProductionEpoch().getDay());
  255.                 writeValue(cpfWriter, I2, cpfHeader.getProductionHour());
  256.                 writeValue(cpfWriter, I3, cpfHeader.getSequenceNumber());

  257.                 // check file version
  258.                 if (cpfHeader.getVersion() == 2) {
  259.                     writeValue(cpfWriter, I2, cpfHeader.getSubDailySequenceNumber());
  260.                 }

  261.                 // write last key
  262.                 writeValue(cpfWriter, A10, cpfHeader.getName());

  263.             }

  264.         },

  265.         /** Header second line. */
  266.         H2("H2") {

  267.             /** {@inheritDoc} */
  268.             @Override
  269.             public void write(final CPFHeader cpfHeader, final Appendable cpfWriter,
  270.                               final TimeScale utc) throws IOException {

  271.                 // write identifiers
  272.                 writeValue(cpfWriter, A2, getIdentifier());
  273.                 writeValue(cpfWriter, A8, cpfHeader.getIlrsSatelliteId());
  274.                 writeValue(cpfWriter, A4, cpfHeader.getSic());
  275.                 writeValue(cpfWriter, A8, cpfHeader.getNoradId());

  276.                 // write starting epoch
  277.                 final AbsoluteDate starting = cpfHeader.getStartEpoch();
  278.                 final DateTimeComponents dtcStart = starting.getComponents(utc);
  279.                 writeValue(cpfWriter, I4, dtcStart.getDate().getYear());
  280.                 writeValue(cpfWriter, I2, dtcStart.getDate().getMonth());
  281.                 writeValue(cpfWriter, I2, dtcStart.getDate().getDay());
  282.                 writeValue(cpfWriter, I2, dtcStart.getTime().getHour());
  283.                 writeValue(cpfWriter, I2, dtcStart.getTime().getMinute());
  284.                 writeValue(cpfWriter, I2, (int) dtcStart.getTime().getSecond());

  285.                 // write ending epoch
  286.                 final AbsoluteDate ending = cpfHeader.getStartEpoch();
  287.                 final DateTimeComponents dtcEnd = ending.getComponents(utc);
  288.                 writeValue(cpfWriter, I4, dtcEnd.getDate().getYear());
  289.                 writeValue(cpfWriter, I2, dtcEnd.getDate().getMonth());
  290.                 writeValue(cpfWriter, I2, dtcEnd.getDate().getDay());
  291.                 writeValue(cpfWriter, I2, dtcEnd.getTime().getHour());
  292.                 writeValue(cpfWriter, I2, dtcEnd.getTime().getMinute());
  293.                 writeValue(cpfWriter, I2, (int)  dtcEnd.getTime().getSecond());

  294.                 // write last keys
  295.                 writeValue(cpfWriter, I5, cpfHeader.getStep());
  296.                 writeValue(cpfWriter, I1, cpfHeader.isCompatibleWithTIVs());
  297.                 writeValue(cpfWriter, I1, cpfHeader.getTargetClass());
  298.                 writeValue(cpfWriter, I2, cpfHeader.getRefFrameId());
  299.                 writeValue(cpfWriter, I1, cpfHeader.getRotationalAngleType());
  300.                 writeValue(cpfWriter, I1, cpfHeader.isCenterOfMassCorrectionApplied());
  301.                 if (cpfHeader.getVersion() == 2) {
  302.                     writeValue(cpfWriter, I1, cpfHeader.getTargetLocation());
  303.                 }

  304.             }

  305.         };

  306.         /** Identifier. */
  307.         private final String identifier;

  308.         /** Simple constructor.
  309.          * @param identifier regular expression for identifying line (i.e. first element)
  310.          */
  311.         HeaderLineWriter(final String identifier) {
  312.             this.identifier = identifier;
  313.         }

  314.         /** Write a line.
  315.          * @param cpfHeader container for header data
  316.          * @param cpfWriter writer
  317.          * @param utc utc time scale for dates
  318.          * @throws IOException
  319.          *             if any buffer writing operations fail or if the underlying
  320.          *             format doesn't support a configuration in the file
  321.          */
  322.         public abstract void write(CPFHeader cpfHeader, Appendable cpfWriter, TimeScale utc)  throws IOException;

  323.         /**
  324.          * Get the regular expression for identifying line.
  325.          * @return the regular expression for identifying line
  326.          */
  327.         public String getIdentifier() {
  328.             return identifier;
  329.         }

  330.     }

  331. }