SHAFormatReader.java
/* Contributed in the public domain.
* Licensed to CS GROUP (CS) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* CS licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.orekit.forces.gravity.potential;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.util.regex.Pattern;
/** Reader for the SHA gravity field format.
*
* <p> This format is used by some lunar gravity models distributed by
* NASA's Planetary Geology, Geophysics and Geochemistry Laboratory such as
* GRGM1200B and GRGM1200L. It is a simple ASCII format, described in
* <a href="https://pgda.gsfc.nasa.gov/products/75"> the GRGM1200B model product site</a>.
* The first line contains 4 constants: model GM, mean radius, maximum degree and maximum order.
* All other lines contain 6 entries: degree, order, Clm, Slm, sigma Clm and sigma Slm
* (formal errors of Clm and Slm).
*
* <p> The proper way to use this class is to call the {@link GravityFieldFactory}
* which will determine which reader to use with the selected gravity field file.</p>
*
* @see GravityFields
* @author Rafael Ayala
*/
public class SHAFormatReader extends PotentialCoefficientsReader {
/** Default "0" text value. */
private static final String ZERO = "0.0";
/** Default "1" text value. */
private static final String ONE = "1.0";
/** Expression for multiple spaces. */
private static final String SPACES = "\\s+";
/** Pattern for delimiting regular expressions. */
private static final Pattern SEPARATOR = Pattern.compile(SPACES);
/** Pattern for real numbers. */
private static final Pattern REAL = Pattern.compile("[-+]?\\d?\\.\\d+[eEdD][-+]\\d\\d");
/** Pattern for header line. */
private static final Pattern HEADER_LINE = Pattern.compile("^\\s*" + REAL + SPACES + REAL + "\\s+\\d+\\s+\\d+\\s*$");
/** Pattern for data lines. */
private static final Pattern DATA_LINE = Pattern.compile("^\\s*\\d+\\s+\\d+\\s+" + REAL + SPACES + REAL + SPACES + REAL + SPACES + REAL + "\\s*$");
/** Start degree and order for coefficients container. */
private static final int START_DEGREE_ORDER = 120;
/** Simple constructor.
* @param supportedNames regular expression for supported files names
* @param missingCoefficientsAllowed if true, allows missing coefficients in the input data
* @since 12.2
*/
public SHAFormatReader(final String supportedNames, final boolean missingCoefficientsAllowed) {
super(supportedNames, missingCoefficientsAllowed, null);
}
/** {@inheritDoc} */
public void loadData(final InputStream input, final String name) throws IOException, ParseException, OrekitException {
// reset the indicator before loading any data
setReadComplete(false);
setTideSystem(TideSystem.UNKNOWN);
int lineNumber = 0;
int maxDegree;
int maxOrder;
String line = null;
TemporaryCoefficientsContainer container = new TemporaryCoefficientsContainer(START_DEGREE_ORDER, START_DEGREE_ORDER,
missingCoefficientsAllowed() ? 0.0 : Double.NaN);
try (BufferedReader r = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) {
for (line = r.readLine(); line != null; line = r.readLine()) {
++lineNumber;
if (lineNumber == 1) {
// match the pattern of the header line
if (!HEADER_LINE.matcher(line).matches()) {
throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
lineNumber, name, line);
}
final String[] headerFields = SEPARATOR.split(line.trim());
setMu(Double.parseDouble(headerFields[0]));
setAe(Double.parseDouble(headerFields[1]));
maxDegree = Integer.parseInt(headerFields[2]);
maxOrder = Integer.parseInt(headerFields[3]);
container = container.resize(maxDegree, maxOrder);
parseCoefficient(ONE, container.getFlattener(), container.getC(), 0, 0, "C", name);
parseCoefficient(ZERO, container.getFlattener(), container.getS(), 0, 0, "S", name);
parseCoefficient(ZERO, container.getFlattener(), container.getS(), 1, 0, "C", name);
parseCoefficient(ZERO, container.getFlattener(), container.getS(), 1, 0, "S", name);
parseCoefficient(ZERO, container.getFlattener(), container.getS(), 1, 1, "C", name);
parseCoefficient(ZERO, container.getFlattener(), container.getS(), 1, 1, "S", name);
} else if (lineNumber > 1) {
// match the pattern of the data lines
if (!DATA_LINE.matcher(line).matches()) {
throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
lineNumber, name, line);
}
final String[] dataFields = SEPARATOR.split(line.trim());
// we want to assign the values of the data fields to the corresponding variables
final int n = Integer.parseInt(dataFields[0]);
final int m = Integer.parseInt(dataFields[1]);
parseCoefficient(dataFields[2], container.getFlattener(), container.getC(), n, m, "C", name);
parseCoefficient(dataFields[3], container.getFlattener(), container.getS(), n, m, "S", name);
}
}
} catch (NumberFormatException nfe) {
throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
lineNumber, name, line);
}
setRawCoefficients(true, container.getFlattener(), container.getC(), container.getS(), name);
setReadComplete(true);
}
/** Provider for read spherical harmonics coefficients.
* Like EGM fields, SHA fields don't include time-dependent parts,
* so this method returns directly a constant provider.
* @param wantNormalized if true, the provider will provide normalized coefficients,
* otherwise it will provide un-normalized coefficients
* @param degree maximal degree
* @param order maximal order
* @return a new provider
* @since 12.2
*/
public RawSphericalHarmonicsProvider getProvider(final boolean wantNormalized,
final int degree, final int order) {
return getBaseProvider(wantNormalized, degree, order);
}
}