PotentialCoefficientsReader.java

  1. /* Copyright 2002-2015 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.forces.gravity.potential;

  18. import java.io.IOException;
  19. import java.io.InputStream;
  20. import java.text.ParseException;
  21. import java.util.ArrayList;
  22. import java.util.Arrays;
  23. import java.util.List;
  24. import java.util.Locale;

  25. import org.apache.commons.math3.util.FastMath;
  26. import org.apache.commons.math3.util.Precision;
  27. import org.orekit.data.DataLoader;
  28. import org.orekit.errors.OrekitException;
  29. import org.orekit.errors.OrekitMessages;

  30. /**This abstract class represents a Gravitational Potential Coefficients file reader.
  31.  *
  32.  * <p> As it exits many different coefficients models and containers this
  33.  *  interface represents all the methods that should be implemented by a reader.
  34.  *  The proper way to use this interface is to call the {@link GravityFieldFactory}
  35.  *  which will determine which reader to use with the selected potential
  36.  *  coefficients file.<p>
  37.  *
  38.  * @see GravityFieldFactory
  39.  * @author Fabien Maussion
  40.  */
  41. public abstract class PotentialCoefficientsReader implements DataLoader {

  42.     /** Maximal degree to parse. */
  43.     private int maxParseDegree;

  44.     /** Maximal order to parse. */
  45.     private int maxParseOrder;

  46.     /** Regular expression for supported files names. */
  47.     private final String supportedNames;

  48.     /** Allow missing coefficients in the input data. */
  49.     private final boolean missingCoefficientsAllowed;

  50.     /** Indicator for complete read. */
  51.     private boolean readComplete;

  52.     /** Central body reference radius. */
  53.     private double ae;

  54.     /** Central body attraction coefficient. */
  55.     private double mu;

  56.     /** Raw tesseral-sectorial coefficients matrix. */
  57.     private double[][] rawC;

  58.     /** Raw tesseral-sectorial coefficients matrix. */
  59.     private double[][] rawS;

  60.     /** Indicator for normalized raw coefficients. */
  61.     private boolean normalized;

  62.     /** Tide system. */
  63.     private TideSystem tideSystem;

  64.     /** Simple constructor.
  65.      * <p>Build an uninitialized reader.</p>
  66.      * @param supportedNames regular expression for supported files names
  67.      * @param missingCoefficientsAllowed allow missing coefficients in the input data
  68.      */
  69.     protected PotentialCoefficientsReader(final String supportedNames,
  70.                                           final boolean missingCoefficientsAllowed) {
  71.         this.supportedNames             = supportedNames;
  72.         this.missingCoefficientsAllowed = missingCoefficientsAllowed;
  73.         this.maxParseDegree             = Integer.MAX_VALUE;
  74.         this.maxParseOrder              = Integer.MAX_VALUE;
  75.         this.readComplete               = false;
  76.         this.ae                         = Double.NaN;
  77.         this.mu                         = Double.NaN;
  78.         this.rawC                       = null;
  79.         this.rawS                       = null;
  80.         this.normalized                 = false;
  81.         this.tideSystem                 = TideSystem.UNKNOWN;
  82.     }

  83.     /** Get the regular expression for supported files names.
  84.      * @return regular expression for supported files names
  85.      */
  86.     public String getSupportedNames() {
  87.         return supportedNames;
  88.     }

  89.     /** Check if missing coefficients are allowed in the input data.
  90.      * @return true if missing coefficients are allowed in the input data
  91.      */
  92.     public boolean missingCoefficientsAllowed() {
  93.         return missingCoefficientsAllowed;
  94.     }

  95.     /** Set the degree limit for the next file parsing.
  96.      * @param maxParseDegree maximal degree to parse (may be safely
  97.      * set to {@link Integer#MAX_VALUE} to parse all available coefficients)
  98.      * @since 6.0
  99.      */
  100.     public void setMaxParseDegree(final int maxParseDegree) {
  101.         this.maxParseDegree = maxParseDegree;
  102.     }

  103.     /** Get the degree limit for the next file parsing.
  104.      * @return degree limit for the next file parsing
  105.      * @since 6.0
  106.      */
  107.     public int getMaxParseDegree() {
  108.         return maxParseDegree;
  109.     }

  110.     /** Set the order limit for the next file parsing.
  111.      * @param maxParseOrder maximal order to parse (may be safely
  112.      * set to {@link Integer#MAX_VALUE} to parse all available coefficients)
  113.      * @since 6.0
  114.      */
  115.     public void setMaxParseOrder(final int maxParseOrder) {
  116.         this.maxParseOrder = maxParseOrder;
  117.     }

  118.     /** Get the order limit for the next file parsing.
  119.      * @return order limit for the next file parsing
  120.      * @since 6.0
  121.      */
  122.     public int getMaxParseOrder() {
  123.         return maxParseOrder;
  124.     }

  125.     /** {@inheritDoc} */
  126.     public boolean stillAcceptsData() {
  127.         return !(readComplete &&
  128.                  getMaxAvailableDegree() >= getMaxParseDegree() &&
  129.                  getMaxAvailableOrder()  >= getMaxParseOrder());
  130.     }

  131.     /** Set the indicator for completed read.
  132.      * @param readComplete if true, a gravity field has been completely read
  133.      */
  134.     protected void setReadComplete(final boolean readComplete) {
  135.         this.readComplete = readComplete;
  136.     }

  137.     /** Set the central body reference radius.
  138.      * @param ae central body reference radius
  139.      */
  140.     protected void setAe(final double ae) {
  141.         this.ae = ae;
  142.     }

  143.     /** Get the central body reference radius.
  144.      * @return central body reference radius
  145.      */
  146.     protected double getAe() {
  147.         return ae;
  148.     }

  149.     /** Set the central body attraction coefficient.
  150.      * @param mu central body attraction coefficient
  151.      */
  152.     protected void setMu(final double mu) {
  153.         this.mu = mu;
  154.     }

  155.     /** Get the central body attraction coefficient.
  156.      * @return central body attraction coefficient
  157.      */
  158.     protected double getMu() {
  159.         return mu;
  160.     }

  161.     /** Set the {@link TideSystem} used in the gravity field.
  162.      * @param tideSystem tide system used in the gravity field
  163.      */
  164.     protected void setTideSystem(final TideSystem tideSystem) {
  165.         this.tideSystem = tideSystem;
  166.     }

  167.     /** Get the {@link TideSystem} used in the gravity field.
  168.      * @return tide system used in the gravity field
  169.      */
  170.     protected TideSystem getTideSystem() {
  171.         return tideSystem;
  172.     }

  173.     /** Set the tesseral-sectorial coefficients matrix.
  174.      * @param rawNormalized if true, raw coefficients are normalized
  175.      * @param c raw tesseral-sectorial coefficients matrix
  176.      * (a reference to the array will be stored)
  177.      * @param s raw tesseral-sectorial coefficients matrix
  178.      * (a reference to the array will be stored)
  179.      * @param name name of the file (or zip entry)
  180.      * @exception OrekitException if a coefficient is missing
  181.      */
  182.     protected void setRawCoefficients(final boolean rawNormalized,
  183.                                       final double[][] c, final double[][] s,
  184.                                       final String name)
  185.         throws OrekitException {

  186.         // normalization indicator
  187.         normalized = rawNormalized;

  188.         // set known constant values, if they were not defined in the file.
  189.         // See Hofmann-Wellenhof and Moritz, "Physical Geodesy",
  190.         // section 2.6 Harmonics of Lower Degree.
  191.         // All S_i,0 are irrelevant because they are multiplied by zero.
  192.         // C0,0 is 1, the central part, since all coefficients are normalized by GM.
  193.         setIfUnset(c, 0, 0, 1);
  194.         setIfUnset(s, 0, 0, 0);
  195.         // C1,0, C1,1, and S1,1 are the x,y,z coordinates of the center of mass,
  196.         // which are 0 since all coefficients are given in an Earth centered frame
  197.         setIfUnset(c, 1, 0, 0);
  198.         setIfUnset(s, 1, 0, 0);
  199.         setIfUnset(c, 1, 1, 0);
  200.         setIfUnset(s, 1, 1, 0);

  201.         // cosine part
  202.         for (int i = 0; i < c.length; ++i) {
  203.             for (int j = 0; j < c[i].length; ++j) {
  204.                 if (Double.isNaN(c[i][j])) {
  205.                     throw new OrekitException(OrekitMessages.MISSING_GRAVITY_FIELD_COEFFICIENT_IN_FILE,
  206.                                               'C', i, j, name);
  207.                 }
  208.             }
  209.         }
  210.         rawC = c;

  211.         // sine part
  212.         for (int i = 0; i < s.length; ++i) {
  213.             for (int j = 0; j < s[i].length; ++j) {
  214.                 if (Double.isNaN(s[i][j])) {
  215.                     throw new OrekitException(OrekitMessages.MISSING_GRAVITY_FIELD_COEFFICIENT_IN_FILE,
  216.                                               'S', i, j, name);
  217.                 }
  218.             }
  219.         }
  220.         rawS = s;

  221.     }

  222.     /**
  223.      * Set a coefficient if it has not been set already.
  224.      * <p>
  225.      * If {@code array[i][j]} is 0 or NaN this method sets it to {@code value} and returns
  226.      * {@code true}. Otherwise the original value of {@code array[i][j]} is preserved and
  227.      * this method return {@code false}.
  228.      * <p>
  229.      * If {@code array[i][j]} does not exist then this method returns {@code false}.
  230.      *
  231.      * @param array the coefficient array.
  232.      * @param i     degree, the first index to {@code array}.
  233.      * @param j     order, the second index to {@code array}.
  234.      * @param value the new value to set.
  235.      * @return {@code true} if the coefficient was set to {@code value}, {@code false} if
  236.      * the coefficient was not set to {@code value}. A {@code false} return indicates the
  237.      * coefficient has previously been set to a non-NaN, non-zero value.
  238.      */
  239.     private boolean setIfUnset(final double[][] array,
  240.                                final int i,
  241.                                final int j,
  242.                                final double value) {
  243.         if (array.length > i && array[i].length > j &&
  244.                 (Double.isNaN(array[i][j]) || Precision.equals(array[i][j], 0.0, 1))) {
  245.             // the coefficient was not already initialized
  246.             array[i][j] = value;
  247.             return true;
  248.         } else {
  249.             return false;
  250.         }
  251.     }

  252.     /** Get the maximal degree available in the last file parsed.
  253.      * @return maximal degree available in the last file parsed
  254.      * @since 6.0
  255.      */
  256.     public int getMaxAvailableDegree() {
  257.         return rawC.length - 1;
  258.     }

  259.     /** Get the maximal order available in the last file parsed.
  260.      * @return maximal order available in the last file parsed
  261.      * @since 6.0
  262.      */
  263.     public int getMaxAvailableOrder() {
  264.         return rawC[rawC.length - 1].length - 1;
  265.     }

  266.     /** {@inheritDoc} */
  267.     public abstract void loadData(InputStream input, String name)
  268.         throws IOException, ParseException, OrekitException;

  269.     /** Get a provider for read spherical harmonics coefficients.
  270.      * @param wantNormalized if true, the provider will provide normalized coefficients,
  271.      * otherwise it will provide un-normalized coefficients
  272.      * @param degree maximal degree
  273.      * @param order maximal order
  274.      * @return a new provider
  275.      * @exception OrekitException if the requested maximal degree or order exceeds the
  276.      * available degree or order or if no gravity field has read yet
  277.      * @see #getConstantProvider(boolean, int, int)
  278.      * @since 6.0
  279.      */
  280.     public abstract RawSphericalHarmonicsProvider getProvider(boolean wantNormalized, int degree, int order)
  281.         throws OrekitException;

  282.     /** Get a time-independent provider for read spherical harmonics coefficients.
  283.      * @param wantNormalized if true, the raw provider must provide normalized coefficients,
  284.      * otherwise it will provide un-normalized coefficients
  285.      * @param degree maximal degree
  286.      * @param order maximal order
  287.      * @return a new provider, with no time-dependent parts
  288.      * @exception OrekitException if the requested maximal degree or order exceeds the
  289.      * available degree or order or if no gravity field has read yet
  290.      * @see #getProvider(boolean, int, int)
  291.      * @since 6.0
  292.      */
  293.     protected ConstantSphericalHarmonics getConstantProvider(final boolean wantNormalized,
  294.                                                              final int degree, final int order)
  295.         throws OrekitException {

  296.         if (!readComplete) {
  297.             throw new OrekitException(OrekitMessages.NO_GRAVITY_FIELD_DATA_LOADED);
  298.         }

  299.         if (degree >= rawC.length) {
  300.             throw new OrekitException(OrekitMessages.TOO_LARGE_DEGREE_FOR_GRAVITY_FIELD,
  301.                                       degree, rawC.length - 1);
  302.         }

  303.         if (order >= rawC[rawC.length - 1].length) {
  304.             throw new OrekitException(OrekitMessages.TOO_LARGE_ORDER_FOR_GRAVITY_FIELD,
  305.                                       order, rawC[rawC.length - 1].length);
  306.         }

  307.         // fix normalization
  308.         final double[][] truncatedC = buildTriangularArray(degree, order, 0.0);
  309.         final double[][] truncatedS = buildTriangularArray(degree, order, 0.0);
  310.         rescale(1.0, normalized, rawC, rawS, wantNormalized, truncatedC, truncatedS);

  311.         return new ConstantSphericalHarmonics(ae, mu, tideSystem, truncatedC, truncatedS);

  312.     }

  313.     /** Build a coefficients triangular array.
  314.      * @param degree array degree
  315.      * @param order array order
  316.      * @param value initial value to put in array elements
  317.      * @return built array
  318.      */
  319.     protected static double[][] buildTriangularArray(final int degree, final int order, final double value) {
  320.         final double[][] array = new double[degree + 1][];
  321.         for (int k = 0; k < array.length; ++k) {
  322.             array[k] = buildRow(k, order, value);
  323.         }
  324.         return array;
  325.     }

  326.     /**
  327.      * Parse a double from a string. Accept the Fortran convention of using a 'D' or
  328.      * 'd' instead of an 'E' or 'e'.
  329.      *
  330.      * @param string to be parsed.
  331.      * @return the double value of {@code string}.
  332.      */
  333.     protected static double parseDouble(final String string) {
  334.         return Double.parseDouble(string.toUpperCase(Locale.ENGLISH).replace('D', 'E'));
  335.     }

  336.     /** Build a coefficients row.
  337.      * @param degree row degree
  338.      * @param order row order
  339.      * @param value initial value to put in array elements
  340.      * @return built row
  341.      */
  342.     protected static double[] buildRow(final int degree, final int order, final double value) {
  343.         final double[] row = new double[FastMath.min(order, degree) + 1];
  344.         Arrays.fill(row, value);
  345.         return row;
  346.     }

  347.     /** Extend a list of lists of coefficients if needed.
  348.      * @param list list of lists of coefficients
  349.      * @param degree degree required to be present
  350.      * @param order order required to be present
  351.      * @param value initial value to put in list elements
  352.      */
  353.     protected void extendListOfLists(final List<List<Double>> list, final int degree, final int order,
  354.                                      final double value) {
  355.         for (int i = list.size(); i <= degree; ++i) {
  356.             list.add(new ArrayList<Double>());
  357.         }
  358.         final List<Double> listN = list.get(degree);
  359.         final Double v = Double.valueOf(value);
  360.         for (int j = listN.size(); j <= order; ++j) {
  361.             listN.add(v);
  362.         }
  363.     }

  364.     /** Convert a list of list into an array.
  365.      * @param list list of lists of coefficients
  366.      * @return a new array
  367.      */
  368.     protected double[][] toArray(final List<List<Double>> list) {
  369.         final double[][] array = new double[list.size()][];
  370.         for (int i = 0; i < array.length; ++i) {
  371.             array[i] = new double[list.get(i).size()];
  372.             for (int j = 0; j < array[i].length; ++j) {
  373.                 array[i][j] = list.get(i).get(j);
  374.             }
  375.         }
  376.         return array;
  377.     }

  378.     /** Parse a coefficient.
  379.      * @param field text field to parse
  380.      * @param list list where to put the coefficient
  381.      * @param i first index in the list
  382.      * @param j second index in the list
  383.      * @param cName name of the coefficient
  384.      * @param name name of the file
  385.      * @exception OrekitException if the coefficient is already set
  386.      */
  387.     protected void parseCoefficient(final String field, final List<List<Double>> list,
  388.                                     final int i, final int j,
  389.                                     final String cName, final String name)
  390.         throws OrekitException {
  391.         final double value    = parseDouble(field);
  392.         final double oldValue = list.get(i).get(j);
  393.         if (Double.isNaN(oldValue) || Precision.equals(oldValue, 0.0, 1)) {
  394.             // the coefficient was not already initialized
  395.             list.get(i).set(j, value);
  396.         } else {
  397.             throw new OrekitException(OrekitMessages.DUPLICATED_GRAVITY_FIELD_COEFFICIENT_IN_FILE,
  398.                                       name, i, j, name);
  399.         }
  400.     }

  401.     /** Parse a coefficient.
  402.      * @param field text field to parse
  403.      * @param array array where to put the coefficient
  404.      * @param i first index in the list
  405.      * @param j second index in the list
  406.      * @param cName name of the coefficient
  407.      * @param name name of the file
  408.      * @exception OrekitException if the coefficient is already set
  409.      */
  410.     protected void parseCoefficient(final String field, final double[][] array,
  411.                                     final int i, final int j,
  412.                                     final String cName, final String name)
  413.         throws OrekitException {
  414.         final double value    = parseDouble(field);
  415.         final double oldValue = array[i][j];
  416.         if (Double.isNaN(oldValue) || Precision.equals(oldValue, 0.0, 1)) {
  417.             // the coefficient was not already initialized
  418.             array[i][j] = value;
  419.         } else {
  420.             throw new OrekitException(OrekitMessages.DUPLICATED_GRAVITY_FIELD_COEFFICIENT_IN_FILE,
  421.                                       name, i, j, name);
  422.         }
  423.     }

  424.     /** Rescale coefficients arrays.
  425.      * @param scale general scaling factor to apply to all elements
  426.      * @param normalizedOrigin if true, the origin coefficients are normalized
  427.      * @param originC cosine part of the origina coefficients
  428.      * @param originS sine part of the origin coefficients
  429.      * @param wantNormalized if true, the rescaled coefficients must be normalized
  430.      * @param rescaledC cosine part of the rescaled coefficients to fill in (may be the originC array)
  431.      * @param rescaledS sine part of the rescaled coefficients to fill in (may be the originS array)
  432.      * @exception OrekitException if normalization/unnormalization fails because of an underflow
  433.      * due to too high degree/order
  434.      */
  435.     protected static void rescale(final double scale,
  436.                                   final boolean normalizedOrigin, final double[][] originC,
  437.                                   final double[][] originS, final boolean wantNormalized,
  438.                                   final double[][] rescaledC, final double[][] rescaledS)
  439.         throws OrekitException {

  440.         if (wantNormalized == normalizedOrigin) {
  441.             // apply only the general scaling factor
  442.             for (int i = 0; i < rescaledC.length; ++i) {
  443.                 final double[] rCi = rescaledC[i];
  444.                 final double[] rSi = rescaledS[i];
  445.                 final double[] oCi = originC[i];
  446.                 final double[] oSi = originS[i];
  447.                 for (int j = 0; j < rCi.length; ++j) {
  448.                     rCi[j] = oCi[j] * scale;
  449.                     rSi[j] = oSi[j] * scale;
  450.                 }
  451.             }
  452.         } else {

  453.             // we have to re-scale the coefficients
  454.             // (we use rescaledC.length - 1 for the order instead of rescaledC[rescaledC.length - 1].length - 1
  455.             //  because typically trend or pulsation arrays are irregular, some test cases have
  456.             //  order 2 elements at degree 2, but only order 1 elements for higher degrees for example)
  457.             final double[][] factors = GravityFieldFactory.getUnnormalizationFactors(rescaledC.length - 1,
  458.                                                                                      rescaledC.length - 1);

  459.             if (wantNormalized) {
  460.                 // normalize the coefficients
  461.                 for (int i = 0; i < rescaledC.length; ++i) {
  462.                     final double[] rCi = rescaledC[i];
  463.                     final double[] rSi = rescaledS[i];
  464.                     final double[] oCi = originC[i];
  465.                     final double[] oSi = originS[i];
  466.                     final double[] fi  = factors[i];
  467.                     for (int j = 0; j < rCi.length; ++j) {
  468.                         final double factor = scale / fi[j];
  469.                         rCi[j] = oCi[j] * factor;
  470.                         rSi[j] = oSi[j] * factor;
  471.                     }
  472.                 }
  473.             } else {
  474.                 // un-normalize the coefficients
  475.                 for (int i = 0; i < rescaledC.length; ++i) {
  476.                     final double[] rCi = rescaledC[i];
  477.                     final double[] rSi = rescaledS[i];
  478.                     final double[] oCi = originC[i];
  479.                     final double[] oSi = originS[i];
  480.                     final double[] fi  = factors[i];
  481.                     for (int j = 0; j < rCi.length; ++j) {
  482.                         final double factor = scale * fi[j];
  483.                         rCi[j] = oCi[j] * factor;
  484.                         rSi[j] = oSi[j] * factor;
  485.                     }
  486.                 }
  487.             }

  488.         }
  489.     }

  490. }