ODMParser.java

  1. /* Copyright 2002-2016 CS Systèmes d'Information
  2.  * Licensed to CS Systèmes d'Information (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;

  18. import java.io.FileInputStream;
  19. import java.io.FileNotFoundException;
  20. import java.io.IOException;
  21. import java.io.InputStream;
  22. import java.util.List;
  23. import java.util.regex.Matcher;
  24. import java.util.regex.Pattern;

  25. import org.apache.commons.math3.util.FastMath;
  26. import org.orekit.errors.OrekitException;
  27. import org.orekit.errors.OrekitInternalError;
  28. import org.orekit.errors.OrekitMessages;
  29. import org.orekit.files.general.OrbitFile;
  30. import org.orekit.time.AbsoluteDate;
  31. import org.orekit.time.DateTimeComponents;
  32. import org.orekit.time.TimeScalesFactory;
  33. import org.orekit.utils.Constants;
  34. import org.orekit.utils.IERSConventions;

  35. /** Base class for all CCSDS Orbit Data Message parsers.
  36.  * <p>
  37.  * This base class is immutable, and hence thread safe. When parts
  38.  * must be changed, such as reference date for Mission Elapsed Time or
  39.  * Mission Relative Time time systems, or the gravitational coefficient or
  40.  * the IERS conventions, the various {@code withXxx} methods must be called,
  41.  * which create a new immutable instance with the new parameters. This
  42.  * is a combination of the <a href="">builder design pattern</a> and
  43.  * a <a href="http://en.wikipedia.org/wiki/Fluent_interface">fluent
  44.  * interface</a>.
  45.  * </p>
  46.  * @author Luc Maisonobe
  47.  * @since 6.1
  48.  */
  49. public abstract class ODMParser {

  50.     /** Pattern for international designator. */
  51.     private static final Pattern INTERNATIONAL_DESIGNATOR = Pattern.compile("(\\p{Digit}{4})-(\\p{Digit}{3})(\\p{Upper}{1,3})");

  52.     /** Reference date for Mission Elapsed Time or Mission Relative Time time systems. */
  53.     private final AbsoluteDate missionReferenceDate;

  54.     /** Gravitational coefficient. */
  55.     private final  double mu;

  56.     /** IERS Conventions. */
  57.     private final  IERSConventions conventions;

  58.     /** Indicator for simple or accurate EOP interpolation. */
  59.     private final  boolean simpleEOP;

  60.     /** Launch Year. */
  61.     private int launchYear;

  62.     /** Launch number. */
  63.     private int launchNumber;

  64.     /** Piece of launch (from "A" to "ZZZ"). */
  65.     private String launchPiece;

  66.     /** Complete constructor.
  67.      * @param missionReferenceDate reference date for Mission Elapsed Time or Mission Relative Time time systems
  68.      * @param mu gravitational coefficient
  69.      * @param conventions IERS Conventions
  70.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  71.      * @param launchYear launch year for TLEs
  72.      * @param launchNumber launch number for TLEs
  73.      * @param launchPiece piece of launch (from "A" to "ZZZ") for TLEs
  74.      */
  75.     protected ODMParser(final AbsoluteDate missionReferenceDate, final double mu,
  76.                         final IERSConventions conventions, final boolean simpleEOP,
  77.                         final int launchYear, final int launchNumber, final String launchPiece) {
  78.         this.missionReferenceDate = missionReferenceDate;
  79.         this.mu                   = mu;
  80.         this.conventions          = conventions;
  81.         this.simpleEOP            = simpleEOP;
  82.         this.launchYear           = launchYear;
  83.         this.launchNumber         = launchNumber;
  84.         this.launchPiece          = launchPiece;
  85.     }

  86.     /** Set initial date.
  87.      * @param newMissionReferenceDate mission reference date to use while parsing
  88.      * @return a new instance, with mission reference date replaced
  89.      * @see #getMissionReferenceDate()
  90.      */
  91.     public abstract ODMParser withMissionReferenceDate(final AbsoluteDate newMissionReferenceDate);

  92.     /** Get initial date.
  93.      * @return mission reference date to use while parsing
  94.      * @see #withMissionReferenceDate(AbsoluteDate)
  95.      */
  96.     public AbsoluteDate getMissionReferenceDate() {
  97.         return missionReferenceDate;
  98.     }

  99.     /** Set gravitational coefficient.
  100.      * @param newMu gravitational coefficient to use while parsing
  101.      * @return a new instance, with gravitational coefficient date replaced
  102.      * @see #getMu()
  103.      */
  104.     public abstract ODMParser withMu(final double newMu);

  105.     /** Get gravitational coefficient.
  106.      * @return gravitational coefficient to use while parsing
  107.      * @see #withMu(double)
  108.      */
  109.     public double getMu() {
  110.         return mu;
  111.     }

  112.     /** Set IERS conventions.
  113.      * @param newConventions IERS conventions to use while parsing
  114.      * @return a new instance, with IERS conventions replaced
  115.      * @see #getConventions()
  116.      */
  117.     public abstract ODMParser withConventions(final IERSConventions newConventions);

  118.     /** Get IERS conventions.
  119.      * @return IERS conventions to use while parsing
  120.      * @see #withConventions(IERSConventions)
  121.      */
  122.     public IERSConventions getConventions() {
  123.         return conventions;
  124.     }

  125.     /** Set EOP interpolation method.
  126.      * @param newSimpleEOP if true, tidal effects are ignored when interpolating EOP
  127.      * @return a new instance, with EOP interpolation method replaced
  128.      * @see #isSimpleEOP()
  129.      */
  130.     public abstract ODMParser withSimpleEOP(final boolean newSimpleEOP);

  131.     /** Get EOP interpolation method.
  132.      * @return true if tidal effects are ignored when interpolating EOP
  133.      * @see #withSimpleEOP(boolean)
  134.      */
  135.     public boolean isSimpleEOP() {
  136.         return simpleEOP;
  137.     }

  138.     /** Set international designator.
  139.      * <p>
  140.      * This method may be used to ensure the launch year number and pieces are
  141.      * correctly set if they are not present in the CCSDS file header in the
  142.      * OBJECT_ID in the form YYYY-NNN-P{PP}. If they are already in the header,
  143.      * they will be parsed automatically regardless of this method being called
  144.      * or not (i.e. header information override information set here).
  145.      * </p>
  146.      * @param newLaunchYear launch year
  147.      * @param newLaunchNumber launch number
  148.      * @param newLaunchPiece piece of launch (from "A" to "ZZZ")
  149.      * @return a new instance, with TLE settings replaced
  150.      */
  151.     public abstract ODMParser withInternationalDesignator(final int newLaunchYear,
  152.                                                           final int newLaunchNumber,
  153.                                                           final String newLaunchPiece);

  154.     /** Get the launch year.
  155.      * @return launch year
  156.      */
  157.     public int getLaunchYear() {
  158.         return launchYear;
  159.     }

  160.     /** Get the launch number.
  161.      * @return launch number
  162.      */
  163.     public int getLaunchNumber() {
  164.         return launchNumber;
  165.     }

  166.     /** Get the piece of launch.
  167.      * @return piece of launch
  168.      */
  169.     public String getLaunchPiece() {
  170.         return launchPiece;
  171.     }

  172.     /** Parse a CCSDS Orbit Data Message.
  173.      * @param fileName name of the file containing the message
  174.      * @return parsed orbit
  175.      * @exception OrekitException if orbit message cannot be parsed
  176.      */
  177.     public ODMFile parse(final String fileName)
  178.         throws OrekitException {

  179.         InputStream stream = null;

  180.         try {
  181.             stream = new FileInputStream(fileName);
  182.             return parse(stream, fileName);
  183.         } catch (FileNotFoundException e) {
  184.             throw new OrekitException(OrekitMessages.UNABLE_TO_FIND_FILE, fileName);
  185.         } finally {
  186.             try {
  187.                 if (stream != null) {
  188.                     stream.close();
  189.                 }
  190.             } catch (IOException e) {
  191.                 // ignore
  192.             }
  193.         }

  194.     }

  195.     /** Parse a CCSDS Orbit Data Message.
  196.      * @param stream stream containing message
  197.      * @return parsed orbit
  198.      * @exception OrekitException if orbit message cannot be parsed
  199.      */
  200.     public ODMFile parse(final InputStream stream)
  201.         throws OrekitException {
  202.         return parse(stream, "<unknown>");
  203.     }

  204.     /** Parse a CCSDS Orbit Data Message.
  205.      * @param stream stream containing message
  206.      * @param fileName name of the file containing the message (for error messages)
  207.      * @return parsed orbit
  208.      * @exception OrekitException if orbit message cannot be parsed
  209.      */
  210.     public abstract ODMFile parse(final InputStream stream, final String fileName)
  211.         throws OrekitException;

  212.     /** Parse a comment line.
  213.      * @param keyValue key=value pair containing the comment
  214.      * @param comment placeholder where the current comment line should be added
  215.      * @return true if the line was a comment line and was parsed
  216.      */
  217.     protected boolean parseComment(final KeyValue keyValue, final List<String> comment) {
  218.         if (keyValue.getKeyword() == Keyword.COMMENT) {
  219.             comment.add(keyValue.getValue());
  220.             return true;
  221.         } else {
  222.             return false;
  223.         }
  224.     }

  225.     /** Parse an entry from the header.
  226.      * @param keyValue key = value pair
  227.      * @param odmFile instance to update with parsed entry
  228.      * @param comment previous comment lines, will be emptied if used by the keyword
  229.      * @return true if the keyword was a header keyword and has been parsed
  230.      * @exception OrekitException if UTC time scale cannot be retrieved to parse creation date
  231.      */
  232.     protected boolean parseHeaderEntry(final KeyValue keyValue,
  233.                                        final ODMFile odmFile, final List<String> comment)
  234.         throws OrekitException {
  235.         switch (keyValue.getKeyword()) {

  236.             case CREATION_DATE:
  237.                 if (!comment.isEmpty()) {
  238.                     odmFile.setHeaderComment(comment);
  239.                     comment.clear();
  240.                 }
  241.                 odmFile.setCreationDate(new AbsoluteDate(keyValue.getValue(), TimeScalesFactory.getUTC()));
  242.                 return true;

  243.             case ORIGINATOR:
  244.                 odmFile.setOriginator(keyValue.getValue());
  245.                 return true;

  246.             default:
  247.                 return false;

  248.         }

  249.     }

  250.     /** Parse a meta-data key = value entry.
  251.      * @param keyValue key = value pair
  252.      * @param metaData instance to update with parsed entry
  253.      * @param comment previous comment lines, will be emptied if used by the keyword
  254.      * @return true if the keyword was a meta-data keyword and has been parsed
  255.      * @exception OrekitException if center body or frame cannot be retrieved
  256.      */
  257.     protected boolean parseMetaDataEntry(final KeyValue keyValue,
  258.                                          final ODMMetaData metaData, final List<String> comment)
  259.         throws OrekitException {
  260.         switch (keyValue.getKeyword()) {
  261.             case OBJECT_NAME:
  262.                 if (!comment.isEmpty()) {
  263.                     metaData.setComment(comment);
  264.                     comment.clear();
  265.                 }
  266.                 metaData.setObjectName(keyValue.getValue());
  267.                 return true;

  268.             case OBJECT_ID: {
  269.                 metaData.setObjectID(keyValue.getValue());
  270.                 final Matcher matcher = INTERNATIONAL_DESIGNATOR.matcher(keyValue.getValue());
  271.                 if (matcher.matches()) {
  272.                     metaData.setLaunchYear(Integer.parseInt(matcher.group(1)));
  273.                     metaData.setLaunchNumber(Integer.parseInt(matcher.group(2)));
  274.                     metaData.setLaunchPiece(matcher.group(3));
  275.                 }
  276.                 return true;
  277.             }

  278.             case CENTER_NAME:
  279.                 metaData.setCenterName(keyValue.getValue());
  280.                 final String canonicalValue;
  281.                 if (keyValue.getValue().equals("SOLAR SYSTEM BARYCENTER") || keyValue.getValue().equals("SSB")) {
  282.                     canonicalValue = "SOLAR_SYSTEM_BARYCENTER";
  283.                 } else if (keyValue.getValue().equals("EARTH MOON BARYCENTER") || keyValue.getValue().equals("EARTH-MOON BARYCENTER") ||
  284.                         keyValue.getValue().equals("EARTH BARYCENTER") || keyValue.getValue().equals("EMB")) {
  285.                     canonicalValue = "EARTH_MOON";
  286.                 } else {
  287.                     canonicalValue = keyValue.getValue();
  288.                 }
  289.                 for (final CenterName c : CenterName.values()) {
  290.                     if (c.name().equals(canonicalValue)) {
  291.                         metaData.setHasCreatableBody(true);
  292.                         metaData.setCenterBody(c.getCelestialBody());
  293.                         metaData.getODMFile().setMuCreated(c.getCelestialBody().getGM());
  294.                     }
  295.                 }
  296.                 return true;

  297.             case REF_FRAME:
  298.                 metaData.setRefFrame(parseCCSDSFrame(keyValue.getValue()).getFrame(getConventions(), isSimpleEOP()));
  299.                 return true;

  300.             case REF_FRAME_EPOCH:
  301.                 metaData.setFrameEpochString(keyValue.getValue());
  302.                 return true;

  303.             case TIME_SYSTEM:
  304.                 final OrbitFile.TimeSystem timeSystem = OrbitFile.TimeSystem.valueOf(keyValue.getValue());
  305.                 metaData.setTimeSystem(timeSystem);
  306.                 if (metaData.getFrameEpochString() != null) {
  307.                     metaData.setFrameEpoch(parseDate(metaData.getFrameEpochString(), timeSystem));
  308.                 }
  309.                 return true;

  310.             default:
  311.                 return false;
  312.         }
  313.     }

  314.     /** Parse a general state data key = value entry.
  315.      * @param keyValue key = value pair
  316.      * @param general instance to update with parsed entry
  317.      * @param comment previous comment lines, will be emptied if used by the keyword
  318.      * @return true if the keyword was a meta-data keyword and has been parsed
  319.      * @exception OrekitException if center body or frame cannot be retrieved
  320.      */
  321.     protected boolean parseGeneralStateDataEntry(final KeyValue keyValue,
  322.                                                  final OGMFile general, final List<String> comment)
  323.         throws OrekitException {
  324.         switch (keyValue.getKeyword()) {

  325.             case EPOCH:
  326.                 general.setEpochComment(comment);
  327.                 comment.clear();
  328.                 general.setEpoch(parseDate(keyValue.getValue(), general.getTimeSystem()));
  329.                 return true;

  330.             case SEMI_MAJOR_AXIS:
  331.                 general.setKeplerianElementsComment(comment);
  332.                 comment.clear();
  333.                 general.setA(keyValue.getDoubleValue() * 1000);
  334.                 general.setHasKeplerianElements(true);
  335.                 return true;

  336.             case ECCENTRICITY:
  337.                 general.setE(keyValue.getDoubleValue());
  338.                 return true;

  339.             case INCLINATION:
  340.                 general.setI(FastMath.toRadians(keyValue.getDoubleValue()));
  341.                 return true;

  342.             case RA_OF_ASC_NODE:
  343.                 general.setRaan(FastMath.toRadians(keyValue.getDoubleValue()));
  344.                 return true;

  345.             case ARG_OF_PERICENTER:
  346.                 general.setPa(FastMath.toRadians(keyValue.getDoubleValue()));
  347.                 return true;

  348.             case TRUE_ANOMALY:
  349.                 general.setAnomalyType("TRUE");
  350.                 general.setAnomaly(FastMath.toRadians(keyValue.getDoubleValue()));
  351.                 return true;

  352.             case MEAN_ANOMALY:
  353.                 general.setAnomalyType("MEAN");
  354.                 general.setAnomaly(FastMath.toRadians(keyValue.getDoubleValue()));
  355.                 return true;

  356.             case GM:
  357.                 general.setMuParsed(keyValue.getDoubleValue() * 1e9);
  358.                 return true;

  359.             case MASS:
  360.                 comment.addAll(0, general.getSpacecraftComment());
  361.                 general.setSpacecraftComment(comment);
  362.                 comment.clear();
  363.                 general.setMass(keyValue.getDoubleValue());
  364.                 return true;

  365.             case SOLAR_RAD_AREA:
  366.                 comment.addAll(0, general.getSpacecraftComment());
  367.                 general.setSpacecraftComment(comment);
  368.                 comment.clear();
  369.                 general.setSolarRadArea(keyValue.getDoubleValue());
  370.                 return true;

  371.             case SOLAR_RAD_COEFF:
  372.                 comment.addAll(0, general.getSpacecraftComment());
  373.                 general.setSpacecraftComment(comment);
  374.                 comment.clear();
  375.                 general.setSolarRadCoeff(keyValue.getDoubleValue());
  376.                 return true;

  377.             case DRAG_AREA:
  378.                 comment.addAll(0, general.getSpacecraftComment());
  379.                 general.setSpacecraftComment(comment);
  380.                 comment.clear();
  381.                 general.setDragArea(keyValue.getDoubleValue());
  382.                 return true;

  383.             case DRAG_COEFF:
  384.                 comment.addAll(0, general.getSpacecraftComment());
  385.                 general.setSpacecraftComment(comment);
  386.                 comment.clear();
  387.                 general.setDragCoeff(keyValue.getDoubleValue());
  388.                 return true;

  389.             case COV_REF_FRAME:
  390.                 general.setCovarianceComment(comment);
  391.                 comment.clear();
  392.                 final CCSDSFrame covFrame = parseCCSDSFrame(keyValue.getValue());
  393.                 if (covFrame.isLof()) {
  394.                     general.setCovRefLofType(covFrame.getLofType());
  395.                 } else {
  396.                     general.setCovRefFrame(covFrame.getFrame(getConventions(), isSimpleEOP()));
  397.                 }
  398.                 return true;

  399.             case CX_X:
  400.                 general.createCovarianceMatrix();
  401.                 general.setCovarianceMatrixEntry(0, 0, keyValue.getDoubleValue() * 1.0e6);
  402.                 return true;

  403.             case CY_X:
  404.                 general.setCovarianceMatrixEntry(0, 1, keyValue.getDoubleValue() * 1.0e6);
  405.                 return true;

  406.             case CY_Y:
  407.                 general.setCovarianceMatrixEntry(1, 1, keyValue.getDoubleValue() * 1.0e6);
  408.                 return true;

  409.             case CZ_X:
  410.                 general.setCovarianceMatrixEntry(0, 2, keyValue.getDoubleValue() * 1.0e6);
  411.                 return true;

  412.             case CZ_Y:
  413.                 general.setCovarianceMatrixEntry(1, 2, keyValue.getDoubleValue() * 1.0e6);
  414.                 return true;

  415.             case CZ_Z:
  416.                 general.setCovarianceMatrixEntry(2, 2, keyValue.getDoubleValue() * 1.0e6);
  417.                 return true;

  418.             case CX_DOT_X:
  419.                 general.setCovarianceMatrixEntry(0, 3, keyValue.getDoubleValue() * 1.0e6);
  420.                 return true;

  421.             case CX_DOT_Y:
  422.                 general.setCovarianceMatrixEntry(1, 3, keyValue.getDoubleValue() * 1.0e6);
  423.                 return true;

  424.             case CX_DOT_Z:
  425.                 general.setCovarianceMatrixEntry(2, 3, keyValue.getDoubleValue() * 1.0e6);
  426.                 return true;

  427.             case CX_DOT_X_DOT:
  428.                 general.setCovarianceMatrixEntry(3, 3, keyValue.getDoubleValue() * 1.0e6);
  429.                 return true;

  430.             case CY_DOT_X:
  431.                 general.setCovarianceMatrixEntry(0, 4, keyValue.getDoubleValue() * 1.0e6);
  432.                 return true;

  433.             case CY_DOT_Y:
  434.                 general.setCovarianceMatrixEntry(1, 4, keyValue.getDoubleValue() * 1.0e6);
  435.                 return true;

  436.             case CY_DOT_Z:
  437.                 general.setCovarianceMatrixEntry(2, 4, keyValue.getDoubleValue() * 1.0e6);
  438.                 return true;

  439.             case CY_DOT_X_DOT:
  440.                 general.setCovarianceMatrixEntry(3, 4, keyValue.getDoubleValue() * 1.0e6);
  441.                 return true;

  442.             case CY_DOT_Y_DOT:
  443.                 general.setCovarianceMatrixEntry(4, 4, keyValue.getDoubleValue() * 1.0e6);
  444.                 return true;

  445.             case CZ_DOT_X:
  446.                 general.setCovarianceMatrixEntry(0, 5, keyValue.getDoubleValue() * 1.0e6);
  447.                 return true;

  448.             case CZ_DOT_Y:
  449.                 general.setCovarianceMatrixEntry(1, 5, keyValue.getDoubleValue() * 1.0e6);
  450.                 return true;

  451.             case CZ_DOT_Z:
  452.                 general.setCovarianceMatrixEntry(2, 5, keyValue.getDoubleValue() * 1.0e6);
  453.                 return true;

  454.             case CZ_DOT_X_DOT:
  455.                 general.setCovarianceMatrixEntry(3, 5, keyValue.getDoubleValue() * 1.0e6);
  456.                 return true;

  457.             case CZ_DOT_Y_DOT:
  458.                 general.setCovarianceMatrixEntry(4, 5, keyValue.getDoubleValue() * 1.0e6);
  459.                 return true;

  460.             case CZ_DOT_Z_DOT:
  461.                 general.setCovarianceMatrixEntry(5, 5, keyValue.getDoubleValue() * 1.0e6);
  462.                 return true;

  463.             case USER_DEFINED_X:
  464.                 general.setUserDefinedParameters(keyValue.getKey(), keyValue.getValue());
  465.                 return true;

  466.             default:
  467.                 return false;
  468.         }
  469.     }

  470.     /** Parse a CCSDS frame.
  471.      * @param frameName name of the frame, as the value of a CCSDS key=value line
  472.      * @return CCSDS frame corresponding to the name
  473.      */
  474.     protected CCSDSFrame parseCCSDSFrame(final String frameName) {
  475.         return CCSDSFrame.valueOf(frameName.replaceAll("-", ""));
  476.     }

  477.     /** Parse a date.
  478.      * @param date date to parse, as the value of a CCSDS key=value line
  479.      * @param timeSystem time system to use
  480.      * @return parsed date
  481.      * @exception OrekitException if some time scale cannot be retrieved
  482.      */
  483.     protected AbsoluteDate parseDate(final String date, final OrbitFile.TimeSystem timeSystem)
  484.         throws OrekitException {
  485.         switch (timeSystem) {
  486.             case GMST:
  487.                 return new AbsoluteDate(date, TimeScalesFactory.getGMST(conventions, false));
  488.             case GPS:
  489.                 return new AbsoluteDate(date, TimeScalesFactory.getGPS());
  490.             case TAI:
  491.                 return new AbsoluteDate(date, TimeScalesFactory.getTAI());
  492.             case TCB:
  493.                 return new AbsoluteDate(date, TimeScalesFactory.getTCB());
  494.             case TDB:
  495.                 return new AbsoluteDate(date, TimeScalesFactory.getTDB());
  496.             case TCG:
  497.                 return new AbsoluteDate(date, TimeScalesFactory.getTCG());
  498.             case TT:
  499.                 return new AbsoluteDate(date, TimeScalesFactory.getTT());
  500.             case UT1:
  501.                 return new AbsoluteDate(date, TimeScalesFactory.getUT1(conventions, false));
  502.             case UTC:
  503.                 return new AbsoluteDate(date, TimeScalesFactory.getUTC());
  504.             case MET: {
  505.                 final DateTimeComponents clock = DateTimeComponents.parseDateTime(date);
  506.                 final double offset = clock.getDate().getYear() * Constants.JULIAN_YEAR +
  507.                         clock.getDate().getDayOfYear() * Constants.JULIAN_DAY +
  508.                         clock.getTime().getSecondsInDay();
  509.                 return missionReferenceDate.shiftedBy(offset);
  510.             }
  511.             case MRT: {
  512.                 final DateTimeComponents clock = DateTimeComponents.parseDateTime(date);
  513.                 final double offset = clock.getDate().getYear() * Constants.JULIAN_YEAR +
  514.                         clock.getDate().getDayOfYear() * Constants.JULIAN_DAY +
  515.                         clock.getTime().getSecondsInDay();
  516.                 return missionReferenceDate.shiftedBy(offset);
  517.             }
  518.             default:
  519.                 throw new OrekitInternalError(null);
  520.         }
  521.     }

  522. }