SP3Header.java

  1. /* Copyright 2022-2025 Luc Maisonobe
  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.sp3;

  18. import java.util.ArrayList;
  19. import java.util.Collections;
  20. import java.util.List;

  21. import org.orekit.errors.OrekitException;
  22. import org.orekit.errors.OrekitMessages;
  23. import org.orekit.gnss.TimeSystem;
  24. import org.orekit.time.AbsoluteDate;
  25. import org.orekit.utils.CartesianDerivativesFilter;

  26. /** Header for SP3 files.
  27.  * @author Luc Maisonobe
  28.  * @since 12.0
  29.  */
  30. public class SP3Header {

  31.     /** String representation of the center of ephemeris coordinate system. **/
  32.     public static final String SP3_FRAME_CENTER_STRING = "EARTH";

  33.     /** Name for pos/vel accuracy base header entry. */
  34.     private static final String POS_VEL_ACCURACY_BASE = "pos/vel accuracy base";

  35.     /** Name for clock accuracy base header entry. */
  36.     private static final String CLOCK_ACCURACY_BASE = "clock accuracy base";

  37.     /** Name for comments header entry. */
  38.     private static final String COMMENTS = "comments";

  39.     /** File version. */
  40.     private char version;

  41.     /** File type. */
  42.     private SP3FileType type;

  43.     /** Time system. */
  44.     private TimeSystem timeSystem;

  45.     /** Epoch of the file. */
  46.     private AbsoluteDate epoch;

  47.     /** GPS week. */
  48.     private int gpsWeek;

  49.     /** Seconds of the current GPS week. */
  50.     private double secondsOfWeek;

  51.     /** Julian day. */
  52.     private int modifiedJulianDay;

  53.     /** Day fraction. */
  54.     private double dayFraction;

  55.     /** Time-interval between epochs. */
  56.     private double epochInterval;

  57.     /** Number of epochs. */
  58.     private int numberOfEpochs;

  59.     /** Coordinate system. */
  60.     private String coordinateSystem;

  61.     /** Data used indicator. */
  62.     private List<DataUsed> dataUsed;

  63.     /** Orbit type. */
  64.     private SP3OrbitType orbitType;

  65.     /** Key for orbit type. */
  66.     private String orbitTypeKey;

  67.     /** Agency providing the file. */
  68.     private String agency;

  69.     /** Indicates if data contains velocity or not. */
  70.     private CartesianDerivativesFilter filter;

  71.     /** Base for position/velocity accuracy. */
  72.     private double posVelBase;

  73.     /** Base for clock/clock-rate accuracy. */
  74.     private double clockBase;

  75.     /** Satellite identifiers. */
  76.     private List<String> satIds;

  77.     /** Satellite accuracies. */
  78.     private double[] accuracies;

  79.     /** Comments. */
  80.     private final List<String> comments;

  81.     /** Create a new SP3 header.
  82.      */
  83.     public SP3Header() {
  84.         this.version    = '?';
  85.         this.satIds     = new ArrayList<>();
  86.         this.accuracies = null;
  87.         this.comments   = new ArrayList<>();
  88.     }

  89.     /** Check header is valid.
  90.      * @param parsing if true, we are parsing an existing file, and are more lenient
  91.      * in order to accept some common errors (like between 86 and 99 satellites
  92.      * in SP3a, SP3b or SP3c files)
  93.      * @param hasAccuracy if true, there are accuracy data in the file
  94.      * @param fileName file name to generate the error message
  95.      * @exception OrekitException if file is not valid
  96.      */
  97.     void validate(final boolean parsing, final boolean hasAccuracy, final String fileName) throws OrekitException {

  98.         // check version
  99.         if ("abcd".indexOf(getVersion()) < 0) {
  100.             throw new OrekitException(OrekitMessages.SP3_UNSUPPORTED_VERSION, getVersion());
  101.         }

  102.         if (getVersion() == 'a') {
  103.             // in SP3 version a, the base accuracy must be set to 0
  104.             if (getPosVelBase() != 0.0) {
  105.                 throw new OrekitException(OrekitMessages.SP3_INVALID_HEADER_ENTRY,
  106.                                           POS_VEL_ACCURACY_BASE, getPosVelBase(), fileName, getVersion());
  107.             }
  108.             if (getClockBase() != 0.0) {
  109.                 throw new OrekitException(OrekitMessages.SP3_INVALID_HEADER_ENTRY,
  110.                                           CLOCK_ACCURACY_BASE, getClockBase(), fileName, getVersion());
  111.             }
  112.         } else if (hasAccuracy) {
  113.             // in SP3 versions after version a, the base accuracy must be set if entries specify accuracy
  114.             if (getPosVelBase() <= 0.0) {
  115.                 throw new OrekitException(OrekitMessages.SP3_INVALID_HEADER_ENTRY,
  116.                                           POS_VEL_ACCURACY_BASE, getPosVelBase(), fileName, getVersion());
  117.             }
  118.             if (getClockBase() <= 0.0) {
  119.                 throw new OrekitException(OrekitMessages.SP3_INVALID_HEADER_ENTRY,
  120.                                           CLOCK_ACCURACY_BASE, getClockBase(), fileName, getVersion());
  121.             }
  122.         }
  123.         if (getVersion() < 'd') {
  124.             // in SP3 versions a, b, and c, there are exactly 4 comments with max length 57
  125.             // (60 minus first three characters)
  126.             if (comments.size() != 4 ||
  127.                 comments.get(0).length() > 57 ||
  128.                 comments.get(1).length() > 57 ||
  129.                 comments.get(2).length() > 57 ||
  130.                 comments.get(3).length() > 57) {
  131.                 throw new OrekitException(OrekitMessages.SP3_INVALID_HEADER_ENTRY,
  132.                                           COMMENTS, "/* …", fileName, getVersion());
  133.             }
  134.         } else {
  135.             // starting with SP3 version d, there is an unspecified number of comments with max length 77
  136.             // (80 minus first three characters)
  137.             for (final String c : comments) {
  138.                 if (c.length() > 77) {
  139.                     throw new OrekitException(OrekitMessages.SP3_INVALID_HEADER_ENTRY,
  140.                                               COMMENTS, c, fileName, getVersion());
  141.                 }
  142.             }
  143.         }

  144.     }

  145.     /** Set the file version.
  146.      * @param version file version
  147.      */
  148.     public void setVersion(final char version) {
  149.         this.version = version;
  150.     }

  151.     /** Get the file version.
  152.      * @return file version
  153.      */
  154.     public char getVersion() {
  155.         return version;
  156.     }

  157.     /** Set the derivatives filter.
  158.      * @param filter that indicates which derivatives of position are available.
  159.      */
  160.     public void setFilter(final CartesianDerivativesFilter filter) {
  161.         this.filter = filter;
  162.     }

  163.     /** Get the derivatives filter.
  164.      * @return filter with available derivatives
  165.      */
  166.     public CartesianDerivativesFilter getFilter() {
  167.         return filter;
  168.     }

  169.     /** Returns the {@link SP3FileType} associated with this SP3 file.
  170.      * @return the file type for this SP3 file
  171.      */
  172.     public SP3FileType getType() {
  173.         return type;
  174.     }

  175.     /** Set the file type for this SP3 file.
  176.      * @param fileType the file type to be set
  177.      */
  178.     public void setType(final SP3FileType fileType) {
  179.         this.type = fileType;
  180.     }

  181.     /** Returns the {@link TimeSystem} used to time-stamp position entries.
  182.      * @return the {@link TimeSystem} of the orbit file
  183.      */
  184.     public TimeSystem getTimeSystem() {
  185.         return timeSystem;
  186.     }

  187.     /** Set the time system used in this SP3 file.
  188.      * @param system the time system to be set
  189.      */
  190.     public void setTimeSystem(final TimeSystem system) {
  191.         this.timeSystem = system;
  192.     }

  193.     /** Returns the data used indicator from the SP3 file.
  194.      * @return the data used indicator
  195.      */
  196.     public List<DataUsed> getDataUsed() {
  197.         return dataUsed;
  198.     }

  199.     /** Set the data used indicator for this SP3 file.
  200.      * @param dataUsed the data used indicator to be set
  201.      */
  202.     public void setDataUsed(final List<DataUsed> dataUsed) {
  203.         this.dataUsed = dataUsed;
  204.     }

  205.     /** Returns the start epoch of the orbit file.
  206.      * @return the start epoch
  207.      */
  208.     public AbsoluteDate getEpoch() {
  209.         return epoch;
  210.     }

  211.     /** Set the epoch of the SP3 file.
  212.      * @param time the epoch to be set
  213.      */
  214.     public void setEpoch(final AbsoluteDate time) {
  215.         this.epoch = time;
  216.     }

  217.     /** Returns the GPS week as contained in the SP3 file.
  218.      * @return the GPS week of the SP3 file
  219.      */
  220.     public int getGpsWeek() {
  221.         return gpsWeek;
  222.     }

  223.     /** Set the GPS week of the SP3 file.
  224.      * @param week the GPS week to be set
  225.      */
  226.     public void setGpsWeek(final int week) {
  227.         this.gpsWeek = week;
  228.     }

  229.     /** Returns the seconds of the GPS week as contained in the SP3 file.
  230.      * @return the seconds of the GPS week
  231.      */
  232.     public double getSecondsOfWeek() {
  233.         return secondsOfWeek;
  234.     }

  235.     /** Set the seconds of the GPS week for this SP3 file.
  236.      * @param seconds the seconds to be set
  237.      */
  238.     public void setSecondsOfWeek(final double seconds) {
  239.         this.secondsOfWeek = seconds;
  240.     }

  241.     /** Returns the modified julian day for this SP3 file.
  242.      * @return the modified julian day
  243.      */
  244.     public int getModifiedJulianDay() {
  245.         return modifiedJulianDay;
  246.     }

  247.     /** Set the modified julian day for this SP3 file.
  248.      * @param day the modified julian day to be set
  249.      */
  250.     public void setModifiedJulianDay(final int day) {
  251.         this.modifiedJulianDay = day;
  252.     }

  253.     /** Returns the day fraction for this SP3 file.
  254.      * @return the day fraction
  255.      */
  256.     public double getDayFraction() {
  257.         return dayFraction;
  258.     }

  259.     /** Set the day fraction for this SP3 file.
  260.      * @param fraction the day fraction to be set
  261.      */
  262.     public void setDayFraction(final double fraction) {
  263.         this.dayFraction = fraction;
  264.     }

  265.     /** Returns the time interval between epochs (in seconds).
  266.      * @return the time interval between epochs
  267.      */
  268.     public double getEpochInterval() {
  269.         return epochInterval;
  270.     }

  271.     /** Set the epoch interval for this SP3 file.
  272.      * @param interval the interval between orbit entries
  273.      */
  274.     public void setEpochInterval(final double interval) {
  275.         this.epochInterval = interval;
  276.     }

  277.     /** Returns the number of epochs contained in this orbit file.
  278.      * @return the number of epochs
  279.      */
  280.     public int getNumberOfEpochs() {
  281.         return numberOfEpochs;
  282.     }

  283.     /** Set the number of epochs as contained in the SP3 file.
  284.      * @param epochCount the number of epochs to be set
  285.      */
  286.     public void setNumberOfEpochs(final int epochCount) {
  287.         this.numberOfEpochs = epochCount;
  288.     }

  289.     /** Returns the coordinate system of the entries in this orbit file.
  290.      * @return the coordinate system
  291.      */
  292.     public String getCoordinateSystem() {
  293.         return coordinateSystem;
  294.     }

  295.     /** Set the coordinate system used for the orbit entries.
  296.      * @param system the coordinate system to be set
  297.      */
  298.     public void setCoordinateSystem(final String system) {
  299.         this.coordinateSystem = system;
  300.     }

  301.     /** Returns the {@link SP3OrbitType} for this SP3 file.
  302.      * @return the orbit type
  303.      */
  304.     public SP3OrbitType getOrbitType() {
  305.         return orbitType;
  306.     }

  307.     /** Returns the orbit type key for this SP3 file.
  308.      * @return the orbit type key
  309.      */
  310.     public String getOrbitTypeKey() {
  311.         return orbitTypeKey;
  312.     }

  313.     /** Set the orbit type key for this SP3 file.
  314.      * @param oTypeKey the orbit type key to be set
  315.      */
  316.     public void setOrbitTypeKey(final String oTypeKey) {
  317.         this.orbitTypeKey = oTypeKey;
  318.         this.orbitType    = SP3OrbitType.parseType(oTypeKey);
  319.     }

  320.     /** Returns the agency that prepared this SP3 file.
  321.      * @return the agency
  322.      */
  323.     public String getAgency() {
  324.         return agency;
  325.     }

  326.     /** Set the agency string for this SP3 file.
  327.      * @param agencyStr the agency string to be set
  328.      */
  329.     public void setAgency(final String agencyStr) {
  330.         this.agency = agencyStr;
  331.     }

  332.     /** Set the base for position/velocity accuracy.
  333.      * @param posVelBase base for position/velocity accuracy
  334.      */
  335.     public void setPosVelBase(final double posVelBase) {
  336.         this.posVelBase = posVelBase;
  337.     }

  338.     /** Get the base for position/velocity accuracy.
  339.      * @return base for position/velocity accuracy
  340.      */
  341.     public double getPosVelBase() {
  342.         return posVelBase;
  343.     }

  344.     /** Set the base for clock/clock-rate accuracy.
  345.      * @param clockBase base for clock/clock-rate accuracy
  346.      */
  347.     public void setClockBase(final double clockBase) {
  348.         this.clockBase = clockBase;
  349.     }

  350.     /** Get the base for clock/clock-rate accuracy.
  351.      * @return base for clock/clock-rate accuracy
  352.      */
  353.     public double getClockBase() {
  354.         return clockBase;
  355.     }

  356.     /** Add a satellite identifier.
  357.      * @param satId satellite identifier
  358.      */
  359.     public void addSatId(final String satId) {
  360.         satIds.add(satId);
  361.     }

  362.     /** Get the satellite identifiers.
  363.      * @return satellites identifiers
  364.      */
  365.     public List<String> getSatIds() {
  366.         return Collections.unmodifiableList(satIds);
  367.     }

  368.     /** Set the accuracy.
  369.      * @param index satellite index in {@link #getSatIds()}
  370.      * @param accuracy in m
  371.      */
  372.     public void setAccuracy(final int index, final double accuracy) {
  373.         if (accuracies == null) {
  374.             // lazy allocation of the array
  375.             accuracies = new double[satIds.size()];
  376.         }
  377.         accuracies[index] = accuracy;
  378.     }

  379.     /** Get the formal accuracy.
  380.      * <p>
  381.      * The accuracy is limited by the SP3 standard to be a power of 2 in mm.
  382.      * The value returned here is in meters.
  383.      * </p>
  384.      * @param satId satellite identifier
  385.      * @return magnitude of one standard deviation, in m.
  386.      */
  387.     public double getAccuracy(final String satId) {
  388.         for (int i = 0; i < satIds.size(); ++i) {
  389.             if (satIds.get(i).equals(satId)) {
  390.                 return accuracies[i];
  391.             }
  392.         }
  393.         return Double.NaN;
  394.     }

  395.     /** Get the comments.
  396.      * @return an unmodifiable view of comments
  397.      */
  398.     public List<String> getComments() {
  399.         return Collections.unmodifiableList(comments);
  400.     }

  401.     /** Add a comment.
  402.      * @param comment comment to add
  403.      */
  404.     public void addComment(final String comment) {
  405.         comments.add(comment);
  406.     }

  407. }