LazyLoadedGravityFields.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 java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.hipparchus.util.FastMath;
import org.orekit.data.DataProvidersManager;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
import org.orekit.time.TimeScale;
/**
* Loads gravity fields when first requested and can be configured until then. Designed to
* match the behavior of {@link GravityFieldFactory} in Orekit 10.0.
*
* @author Evan Ward
* @author Fabien Maussion
* @author Pascal Parraud
* @author Luc Maisonobe
* @see GravityFieldFactory
* @since 10.1
*/
public class LazyLoadedGravityFields implements GravityFields {
/** Potential readers. */
private final List<PotentialCoefficientsReader> readers = new ArrayList<>();
/** Ocean tides readers. */
private final List<OceanTidesReader> oceanTidesReaders =
new ArrayList<>();
/** Ocean load deformation coefficients. */
private OceanLoadDeformationCoefficients oceanLoadDeformationCoefficients =
OceanLoadDeformationCoefficients.IERS_2010;
/** Provides access to auxiliary data files for loading gravity field files. */
private final DataProvidersManager dataProvidersManager;
/** Time scale for parsing dates. */
private final TimeScale timeScale;
/**
* Create a factory for gravity fields that uses the given data manager to load the
* gravity field files.
*
* @param dataProvidersManager provides access to auxiliary data files.
* @param timeScale use to parse dates for the {@link #addDefaultPotentialCoefficientsReaders()}.
* In Orekit 10.0 it is TT.
*/
public LazyLoadedGravityFields(final DataProvidersManager dataProvidersManager,
final TimeScale timeScale) {
this.dataProvidersManager = dataProvidersManager;
this.timeScale = timeScale;
}
/** Add a reader for gravity fields.
* @param reader custom reader to add for the gravity field
* @see #addDefaultPotentialCoefficientsReaders()
* @see #clearPotentialCoefficientsReaders()
*/
public void addPotentialCoefficientsReader(final PotentialCoefficientsReader reader) {
synchronized (readers) {
readers.add(reader);
}
}
/**
* Add the default readers for gravity fields.
*
* <p> The default readers support ICGEM, SHM, EGM and GRGS formats with the
* default names {@link GravityFieldFactory#ICGEM_FILENAME}, {@link
* GravityFieldFactory#SHM_FILENAME}, {@link GravityFieldFactory#EGM_FILENAME}, {@link
* GravityFieldFactory#GRGS_FILENAME} and don't allow missing coefficients.
*
* @see #addPotentialCoefficientsReader(PotentialCoefficientsReader)
* @see #clearPotentialCoefficientsReaders()
*/
public void addDefaultPotentialCoefficientsReaders() {
synchronized (readers) {
readers.add(new ICGEMFormatReader(GravityFieldFactory.ICGEM_FILENAME, false, timeScale));
readers.add(new SHMFormatReader(GravityFieldFactory.SHM_FILENAME, false, timeScale));
readers.add(new EGMFormatReader(GravityFieldFactory.EGM_FILENAME, false));
readers.add(new GRGSFormatReader(GravityFieldFactory.GRGS_FILENAME, false, timeScale));
}
}
/** Clear gravity field readers.
* @see #addPotentialCoefficientsReader(PotentialCoefficientsReader)
* @see #addDefaultPotentialCoefficientsReaders()
*/
public void clearPotentialCoefficientsReaders() {
synchronized (readers) {
readers.clear();
}
}
/** Add a reader for ocean tides.
* @param reader custom reader to add for the gravity field
* @see #addDefaultPotentialCoefficientsReaders()
* @see #clearPotentialCoefficientsReaders()
*/
public void addOceanTidesReader(final OceanTidesReader reader) {
synchronized (oceanTidesReaders) {
oceanTidesReaders.add(reader);
}
}
/** Configure ocean load deformation coefficients.
* @param oldc ocean load deformation coefficients
* @see #getOceanLoadDeformationCoefficients()
*/
public void configureOceanLoadDeformationCoefficients(final OceanLoadDeformationCoefficients oldc) {
oceanLoadDeformationCoefficients = oldc;
}
/** Get the configured ocean load deformation coefficients.
* <p>
* If {@link #configureOceanLoadDeformationCoefficients(OceanLoadDeformationCoefficients)
* configureOceanLoadDeformationCoefficients} has never been called, the default
* value will be the {@link OceanLoadDeformationCoefficients#IERS_2010 IERS 2010}
* coefficients.
* </p>
* @return ocean load deformation coefficients
* @see #configureOceanLoadDeformationCoefficients(OceanLoadDeformationCoefficients)
*/
public OceanLoadDeformationCoefficients getOceanLoadDeformationCoefficients() {
return oceanLoadDeformationCoefficients;
}
/** Add the default readers for ocean tides.
* <p>
* The default readers support files similar to the fes2004_Cnm-Snm.dat and
* fes2004.dat as published by IERS, using the {@link
* #configureOceanLoadDeformationCoefficients(OceanLoadDeformationCoefficients)
* configured} ocean load deformation coefficients, which by default are the
* IERS 2010 coefficients, which are limited to degree 6. If higher degree
* coefficients are needed, the {@link
* #configureOceanLoadDeformationCoefficients(OceanLoadDeformationCoefficients)
* configureOceanLoadDeformationCoefficients} method can be called prior to
* loading the ocean tides model with the {@link
* OceanLoadDeformationCoefficients#GEGOUT high degree coefficients} computed
* by Pascal Gégout.
* </p>
* <p>
* WARNING: the files referenced in the published conventions have some errors.
* These errors have been corrected and the updated files can be found here:
* <a href="http://tai.bipm.org/iers/convupdt/convupdt_c6.html">
* http://tai.bipm.org/iers/convupdt/convupdt_c6.html</a>.
* </p>
* @see #addPotentialCoefficientsReader(PotentialCoefficientsReader)
* @see #clearPotentialCoefficientsReaders()
* @see #configureOceanLoadDeformationCoefficients(OceanLoadDeformationCoefficients)
* @see #getOceanLoadDeformationCoefficients()
*/
public void addDefaultOceanTidesReaders() {
synchronized (oceanTidesReaders) {
oceanTidesReaders.add(new FESCnmSnmReader(GravityFieldFactory.FES_CNM_SNM_FILENAME, 1.0e-11));
final AstronomicalAmplitudeReader aaReader =
new AstronomicalAmplitudeReader(GravityFieldFactory.FES_HF_FILENAME, 5, 2, 3, 1.0);
dataProvidersManager.feed(aaReader.getSupportedNames(), aaReader);
final Map<Integer, Double> map = aaReader.getAstronomicalAmplitudesMap();
oceanTidesReaders.add(new FESCHatEpsilonReader(GravityFieldFactory.FES_CHAT_EPSILON_FILENAME,
0.01, FastMath.toRadians(1.0),
getOceanLoadDeformationCoefficients(),
map));
}
}
/** Clear ocean tides readers.
* @see #addPotentialCoefficientsReader(PotentialCoefficientsReader)
* @see #addDefaultPotentialCoefficientsReaders()
*/
public void clearOceanTidesReaders() {
synchronized (oceanTidesReaders) {
oceanTidesReaders.clear();
}
}
/** Read a gravity field coefficients provider from the first supported file.
* <p>
* If no {@link PotentialCoefficientsReader} has been added by calling {@link
* #addPotentialCoefficientsReader(PotentialCoefficientsReader)
* addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
* clearPotentialCoefficientsReaders} has been called afterwards, the {@link
* #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
* method will be called automatically.
* </p>
* @param maxParseDegree maximal degree to parse
* @param maxParseOrder maximal order to parse
* @return a reader containing already loaded data
* @since 6.0
*/
public PotentialCoefficientsReader readGravityField(final int maxParseDegree,
final int maxParseOrder) {
synchronized (readers) {
if (readers.isEmpty()) {
addDefaultPotentialCoefficientsReaders();
}
// test the available readers
for (final PotentialCoefficientsReader reader : readers) {
reader.setMaxParseDegree(maxParseDegree);
reader.setMaxParseOrder(maxParseOrder);
dataProvidersManager.feed(reader.getSupportedNames(), reader);
if (!reader.stillAcceptsData()) {
return reader;
}
}
}
throw new OrekitException(OrekitMessages.NO_GRAVITY_FIELD_DATA_LOADED);
}
/**
* {@inheritDoc}
*
* <p> If no {@link PotentialCoefficientsReader} has been added by calling {@link
* #addPotentialCoefficientsReader(PotentialCoefficientsReader)
* addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
* clearPotentialCoefficientsReaders} has been called afterwards, the {@link
* #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
* method will be called automatically.
*/
@Override
public NormalizedSphericalHarmonicsProvider getConstantNormalizedProvider(final int degree,
final int order) {
final RawSphericalHarmonicsProvider provider;
synchronized (readers) {
final PotentialCoefficientsReader reader = readGravityField(degree, order);
provider = reader.getProvider(true, degree, order);
}
final ConstantSphericalHarmonics frozen = new ConstantSphericalHarmonics(provider.getReferenceDate(), provider);
return new WrappingNormalizedProvider(frozen);
}
/**
* {@inheritDoc}
*
* <p>If no {@link PotentialCoefficientsReader} has been added by calling {@link
* #addPotentialCoefficientsReader(PotentialCoefficientsReader)
* addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
* clearPotentialCoefficientsReaders} has been called afterwards, the {@link
* #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
* method will be called automatically.
*/
@Override
public NormalizedSphericalHarmonicsProvider getNormalizedProvider(final int degree,
final int order) {
final RawSphericalHarmonicsProvider provider;
synchronized (readers) {
final PotentialCoefficientsReader reader = readGravityField(degree, order);
provider = reader.getProvider(true, degree, order);
}
return new WrappingNormalizedProvider(provider);
}
/**
* {@inheritDoc}
*
* <p>If no {@link PotentialCoefficientsReader} has been added by calling {@link
* #addPotentialCoefficientsReader(PotentialCoefficientsReader)
* addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
* clearPotentialCoefficientsReaders} has been called afterwards, the {@link
* #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
* method will be called automatically.
*/
@Override
public UnnormalizedSphericalHarmonicsProvider getConstantUnnormalizedProvider(final int degree,
final int order) {
final RawSphericalHarmonicsProvider provider;
synchronized (readers) {
final PotentialCoefficientsReader reader = readGravityField(degree, order);
provider = reader.getProvider(false, degree, order);
}
final ConstantSphericalHarmonics frozen = new ConstantSphericalHarmonics(provider.getReferenceDate(), provider);
return new WrappingUnnormalizedProvider(frozen);
}
/**
* {@inheritDoc}
*
* <p>If no {@link PotentialCoefficientsReader} has been added by calling {@link
* #addPotentialCoefficientsReader(PotentialCoefficientsReader)
* addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
* clearPotentialCoefficientsReaders} has been called afterwards, the {@link
* #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
* method will be called automatically.
*/
@Override
public UnnormalizedSphericalHarmonicsProvider getUnnormalizedProvider(final int degree,
final int order) {
final RawSphericalHarmonicsProvider provider;
synchronized (readers) {
final PotentialCoefficientsReader reader = readGravityField(degree, order);
provider = reader.getProvider(false, degree, order);
}
return new WrappingUnnormalizedProvider(provider);
}
/**
* {@inheritDoc}
*
* <p>If no {@link OceanTidesReader} has been added by calling {@link
* #addOceanTidesReader(OceanTidesReader)
* addOceanTidesReader} or if {@link #clearOceanTidesReaders()
* clearOceanTidesReaders} has been called afterwards, the {@link
* #addDefaultOceanTidesReaders() addDefaultOceanTidesReaders}
* method will be called automatically.
*/
@Override
public List<OceanTidesWave> getOceanTidesWaves(final int degree, final int order) {
synchronized (oceanTidesReaders) {
if (oceanTidesReaders.isEmpty()) {
addDefaultOceanTidesReaders();
}
// test the available readers
for (final OceanTidesReader reader : oceanTidesReaders) {
reader.setMaxParseDegree(degree);
reader.setMaxParseOrder(order);
dataProvidersManager.feed(reader.getSupportedNames(), reader);
if (!reader.stillAcceptsData()) {
return reader.getWaves();
}
}
}
throw new OrekitException(OrekitMessages.NO_OCEAN_TIDE_DATA_LOADED);
}
}