StreamingCpfWriter.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.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 A1 = "%1s";

  52.     /** String A2 Format. */
  53.     private static final String A2 = "%2s";

  54.     /** String A3 Format. */
  55.     private static final String A3 = "%3s";

  56.     /** String A4 Format. */
  57.     private static final String A4 = "%4s";

  58.     /** String A8 Format. */
  59.     private static final String A8 = "%8s";

  60.     /** String A10 Format. */
  61.     private static final String A10 = "%10s";

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

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

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

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

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

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

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

  76.     /** Space. */
  77.     private static final String SPACE = " ";

  78.     /** Empty string. */
  79.     private static final String EMPTY_STRING = "";

  80.     /** File format. */
  81.     private static final String FORMAT = "CPF";

  82.     /** Default locale. */
  83.     private static final Locale STANDARDIZED_LOCALE = Locale.US;

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

  86.     /** Output stream. */
  87.     private final Appendable writer;

  88.     /** Time scale for all dates. */
  89.     private final TimeScale timeScale;

  90.     /** Container for header data. */
  91.     private final CPFHeader header;

  92.     /**
  93.      * Create a CPF writer than streams data to the given output stream.
  94.      *
  95.      * @param writer     the output stream for the CPF file.
  96.      * @param timeScale  for all times in the CPF
  97.      * @param header     container for header data
  98.      */
  99.     public StreamingCpfWriter(final Appendable writer,
  100.                               final TimeScale timeScale,
  101.                               final CPFHeader header) {

  102.         this.writer     = writer;
  103.         this.timeScale  = timeScale;
  104.         this.header     = header;
  105.     }

  106.     /**
  107.      * Writes the CPF header for the file.
  108.      * @throws IOException if the stream cannot write to stream
  109.      */
  110.     public void writeHeader() throws IOException {

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

  114.         // Write H2
  115.         HeaderLineWriter.H2.write(header, writer, timeScale);
  116.         writer.append(NEW_LINE);

  117.         // End of header
  118.         writer.append("H9");
  119.         writer.append(NEW_LINE);

  120.     }

  121.     /**
  122.      * Write end of file.
  123.      * @throws IOException if the stream cannot write to stream
  124.      */
  125.     public void writeEndOfFile() throws IOException {
  126.         writer.append("99");
  127.     }

  128.     /**
  129.      * Create a writer for a new CPF ephemeris segment.
  130.      * <p>
  131.      * The returned writer can only write a single ephemeris segment in a CPF.
  132.      * </p>
  133.      * @param frame the reference frame to use for the segment.
  134.      * @return a new CPF segment, ready for writing.
  135.      */
  136.     public Segment newSegment(final Frame frame) {
  137.         return new Segment(frame);
  138.     }

  139.     /**
  140.      * Write a String value in the file.
  141.      * @param cpfWriter writer
  142.      * @param format format
  143.      * @param value value
  144.      * @param withSpace true if a space must be added
  145.      * @throws IOException if value cannot be written
  146.      */
  147.     private static void writeValue(final Appendable cpfWriter, final String format,
  148.                                    final String value, final boolean withSpace)
  149.         throws IOException {
  150.         cpfWriter.append(String.format(STANDARDIZED_LOCALE, format, value)).append(withSpace ? SPACE : EMPTY_STRING);
  151.     }

  152.     /**
  153.      * Write a integer value in the file.
  154.      * @param cpfWriter writer
  155.      * @param format format
  156.      * @param value value
  157.      * @param withSpace true if a space must be added
  158.      * @throws IOException if value cannot be written
  159.      */
  160.     private static void writeValue(final Appendable cpfWriter, final String format,
  161.                                    final int value, final boolean withSpace)
  162.         throws IOException {
  163.         cpfWriter.append(String.format(STANDARDIZED_LOCALE, format, value)).append(withSpace ? SPACE : EMPTY_STRING);
  164.     }

  165.     /**
  166.      * Write a real value in the file.
  167.      * @param cpfWriter writer
  168.      * @param format format
  169.      * @param value value
  170.      * @param withSpace true if a space must be added
  171.      * @throws IOException if value cannot be written
  172.      */
  173.     private static void writeValue(final Appendable cpfWriter, final String format,
  174.                                    final double value, final boolean withSpace)
  175.         throws IOException {
  176.         cpfWriter.append(String.format(STANDARDIZED_LOCALE, format, value)).append(withSpace ? SPACE : EMPTY_STRING);
  177.     }

  178.     /**
  179.      * Write a String value in the file.
  180.      * @param cpfWriter writer
  181.      * @param format format
  182.      * @param value value
  183.      * @param withSpace true if a space must be added
  184.      * @throws IOException if value cannot be written
  185.      */
  186.     private static void writeValue(final Appendable cpfWriter, final String format,
  187.                                    final boolean value, final boolean withSpace)
  188.         throws IOException {
  189.         // Change to an integer value
  190.         final int intValue = value ? 1 : 0;
  191.         writeValue(cpfWriter, format, intValue, withSpace);
  192.     }

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

  195.         /** Reference frame of the output states. */
  196.         private final Frame frame;

  197.         /**
  198.          * Create a new segment writer.
  199.          *
  200.          * @param frame    for the output states. Used by {@link #handleStep(SpacecraftState,
  201.          *                 boolean)}.
  202.          */
  203.         private Segment(final Frame frame) {
  204.             this.frame = frame;
  205.         }

  206.         /** {@inheritDoc}. */
  207.         @Override
  208.         public void handleStep(final SpacecraftState currentState) {
  209.             try {

  210.                 // Write ephemeris line
  211.                 writeEphemerisLine(currentState.getPVCoordinates(frame));

  212.             } catch (IOException e) {
  213.                 throw new OrekitException(e, LocalizedCoreFormats.SIMPLE_MESSAGE,
  214.                                           e.getLocalizedMessage());
  215.             }

  216.         }

  217.         /** {@inheritDoc}. */
  218.         @Override
  219.         public void finish(final SpacecraftState finalState) {
  220.             try {
  221.                 // Write ephemeris line
  222.                 writeEphemerisLine(finalState.getPVCoordinates(frame));

  223.                 // Write end of file
  224.                 writeEndOfFile();

  225.             } catch (IOException e) {
  226.                 throw new OrekitException(e, LocalizedCoreFormats.SIMPLE_MESSAGE,
  227.                                           e.getLocalizedMessage());
  228.             }

  229.         }

  230.         /**
  231.          * Write a single ephemeris line This method does not
  232.          * write the velocity terms.
  233.          *
  234.          * @param pv the time, position, and velocity to write.
  235.          * @throws IOException if the output stream throws one while writing.
  236.          */
  237.         public void writeEphemerisLine(final TimeStampedPVCoordinates pv)
  238.             throws IOException {

  239.             // Record type and direction flag
  240.             writeValue(writer, A2, "10",                                    true);
  241.             writeValue(writer, I1, DEFAULT_DIRECTION_FLAG,                  true);

  242.             // Epoch
  243.             final AbsoluteDate epoch = pv.getDate();
  244.             final DateTimeComponents dtc = epoch.getComponents(timeScale);
  245.             writeValue(writer, I5, dtc.getDate().getMJD(),                  true);
  246.             writeValue(writer, F13_6, dtc.getTime().getSecondsInLocalDay(), true);

  247.             // Leap second flag (default 0)
  248.             writeValue(writer, I2, 0, true);

  249.             // Position
  250.             final Vector3D position = pv.getPosition();
  251.             writeValue(writer, F17_3, position.getX(), true);
  252.             writeValue(writer, F17_3, position.getY(), true);
  253.             writeValue(writer, F17_3, position.getZ(), false);

  254.             // New line
  255.             writer.append(NEW_LINE);

  256.         }

  257.     }

  258.     /** Writer for specific header lines. */
  259.     public enum HeaderLineWriter {

  260.         /** Header first line. */
  261.         H1("H1") {

  262.             /** {@inheritDoc} */
  263.             @Override
  264.             public void write(final CPFHeader cpfHeader, final Appendable cpfWriter, final TimeScale timescale)
  265.                 throws IOException {

  266.                 // write first keys
  267.                 writeValue(cpfWriter, A2, getIdentifier(),                           true);
  268.                 writeValue(cpfWriter, A3, FORMAT,                                    true);
  269.                 writeValue(cpfWriter, I2, cpfHeader.getVersion(),                    true);
  270.                 writeValue(cpfWriter, A1, SPACE, false); // One additional column, see CPF v1 format
  271.                 writeValue(cpfWriter, A3, cpfHeader.getSource(),                     true);
  272.                 writeValue(cpfWriter, I4, cpfHeader.getProductionEpoch().getYear(),  true);
  273.                 writeValue(cpfWriter, I2, cpfHeader.getProductionEpoch().getMonth(), true);
  274.                 writeValue(cpfWriter, I2, cpfHeader.getProductionEpoch().getDay(),   true);
  275.                 writeValue(cpfWriter, I2, cpfHeader.getProductionHour(),             true);
  276.                 writeValue(cpfWriter, A1, SPACE, false); // One additional column, see CPF v1 format
  277.                 writeValue(cpfWriter, I3, cpfHeader.getSequenceNumber(),             true);

  278.                 // check file version
  279.                 if (cpfHeader.getVersion() == 2) {
  280.                     writeValue(cpfWriter, I2, cpfHeader.getSubDailySequenceNumber(), true);
  281.                 }

  282.                 // write target name from official list
  283.                 writeValue(cpfWriter, A10, cpfHeader.getName(),                      true);

  284.                 // write notes (not supported yet)
  285.                 writeValue(cpfWriter, A10, SPACE,                                    false);
  286.             }

  287.         },

  288.         /** Header second line. */
  289.         H2("H2") {

  290.             /** {@inheritDoc} */
  291.             @Override
  292.             public void write(final CPFHeader cpfHeader, final Appendable cpfWriter, final TimeScale timescale)
  293.                 throws IOException {

  294.                 // write identifiers
  295.                 writeValue(cpfWriter, A2, getIdentifier(),                                 true);
  296.                 writeValue(cpfWriter, A8, cpfHeader.getIlrsSatelliteId(),                  true);
  297.                 writeValue(cpfWriter, A4, cpfHeader.getSic(),                              true);
  298.                 writeValue(cpfWriter, A8, cpfHeader.getNoradId(),                          true);

  299.                 // write starting epoch
  300.                 final AbsoluteDate starting = cpfHeader.getStartEpoch();
  301.                 final DateTimeComponents dtcStart = starting.getComponents(timescale);
  302.                 writeValue(cpfWriter, I4, dtcStart.getDate().getYear(),                    true);
  303.                 writeValue(cpfWriter, I2, dtcStart.getDate().getMonth(),                   true);
  304.                 writeValue(cpfWriter, I2, dtcStart.getDate().getDay(),                     true);
  305.                 writeValue(cpfWriter, I2, dtcStart.getTime().getHour(),                    true);
  306.                 writeValue(cpfWriter, I2, dtcStart.getTime().getMinute(),                  true);
  307.                 writeValue(cpfWriter, I2, (int) dtcStart.getTime().getSecond(),            true);

  308.                 // write ending epoch
  309.                 final AbsoluteDate ending = cpfHeader.getEndEpoch();
  310.                 final DateTimeComponents dtcEnd = ending.getComponents(timescale);
  311.                 writeValue(cpfWriter, I4, dtcEnd.getDate().getYear(),                      true);
  312.                 writeValue(cpfWriter, I2, dtcEnd.getDate().getMonth(),                     true);
  313.                 writeValue(cpfWriter, I2, dtcEnd.getDate().getDay(),                       true);
  314.                 writeValue(cpfWriter, I2, dtcEnd.getTime().getHour(),                      true);
  315.                 writeValue(cpfWriter, I2, dtcEnd.getTime().getMinute(),                    true);
  316.                 writeValue(cpfWriter, I2, (int)  dtcEnd.getTime().getSecond(),             true);

  317.                 // write last keys
  318.                 writeValue(cpfWriter, I5, cpfHeader.getStep(),                             true);
  319.                 writeValue(cpfWriter, I1, cpfHeader.isCompatibleWithTIVs(),                true);
  320.                 writeValue(cpfWriter, I1, cpfHeader.getTargetClass(),                      true);
  321.                 writeValue(cpfWriter, I2, cpfHeader.getRefFrameId(),                       true);
  322.                 writeValue(cpfWriter, I1, cpfHeader.getRotationalAngleType(),              true);
  323.                 if (cpfHeader.getVersion() == 1) {
  324.                     writeValue(cpfWriter, I1, cpfHeader.isCenterOfMassCorrectionApplied(), false);
  325.                 } else {
  326.                     writeValue(cpfWriter, I1, cpfHeader.isCenterOfMassCorrectionApplied(), true);
  327.                     writeValue(cpfWriter, I2, cpfHeader.getTargetLocation(),               false);
  328.                 }

  329.             }

  330.         };

  331.         /** Identifier. */
  332.         private final String identifier;

  333.         /** Simple constructor.
  334.          * @param identifier regular expression for identifying line (i.e. first element)
  335.          */
  336.         HeaderLineWriter(final String identifier) {
  337.             this.identifier = identifier;
  338.         }

  339.         /** Write a line.
  340.          * @param cpfHeader container for header data
  341.          * @param cpfWriter writer
  342.          * @param timescale time scale for dates
  343.          * @throws IOException
  344.          *             if any buffer writing operations fail or if the underlying
  345.          *             format doesn't support a configuration in the file
  346.          */
  347.         public abstract void write(CPFHeader cpfHeader, Appendable cpfWriter, TimeScale timescale)  throws IOException;

  348.         /**
  349.          * Get the regular expression for identifying line.
  350.          * @return the regular expression for identifying line
  351.          */
  352.         public String getIdentifier() {
  353.             return identifier;
  354.         }

  355.     }

  356. }