GeoMagneticFieldFactory.java
/* Copyright 2011-2012 Space Applications Services
* Licensed to CS Communication & Systèmes (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.models.earth;
import java.util.Collection;
import java.util.SortedMap;
import java.util.TreeMap;
import org.hipparchus.util.FastMath;
import org.orekit.data.DataProvidersManager;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
/** Factory for different {@link GeoMagneticField} models.
* <p>
* This is a utility class, so its constructor is private.
* </p>
* @author Thomas Neidhart
*/
public class GeoMagneticFieldFactory {
/** The currently supported geomagnetic field models. */
public enum FieldModel {
/** World Magnetic Model. */
WMM,
/** International Geomagnetic Reference Field. */
IGRF
}
/** Loaded IGRF models. */
private static TreeMap<Integer, GeoMagneticField> igrfModels = null;
/** Loaded WMM models. */
private static TreeMap<Integer, GeoMagneticField> wmmModels = null;
/** Private constructor.
* <p>
* This class is a utility class, it should neither have a public nor a
* default constructor. This private constructor prevents the compiler from
* generating one automatically.
* </p>
*/
private GeoMagneticFieldFactory() {
}
/** Get the {@link GeoMagneticField} for the given model type and year.
* @param type the field model type
* @param year the decimal year
* @return a {@link GeoMagneticField} for the given year and model
* @throws OrekitException if the models could not be loaded
* @see GeoMagneticField#getDecimalYear(int, int, int)
*/
public static GeoMagneticField getField(final FieldModel type, final double year)
throws OrekitException {
switch (type) {
case WMM:
return getWMM(year);
case IGRF:
return getIGRF(year);
default:
throw new OrekitException(OrekitMessages.NON_EXISTENT_GEOMAGNETIC_MODEL, type.name(), year);
}
}
/** Get the IGRF model for the given year.
* @param year the decimal year
* @return a {@link GeoMagneticField} for the given year
* @throws OrekitException
* if the IGRF models could not be loaded
* @see GeoMagneticField#getDecimalYear(int, int, int)
*/
public static GeoMagneticField getIGRF(final double year) throws OrekitException {
synchronized (GeoMagneticFieldFactory.class) {
if (igrfModels == null) {
igrfModels = loadModels("^IGRF\\.COF$");
}
return getModel(FieldModel.IGRF, igrfModels, year);
}
}
/** Get the WMM model for the given year.
* @param year the decimal year
* @return a {@link GeoMagneticField} for the given year
* @throws OrekitException if the WMM models could not be loaded
* @see GeoMagneticField#getDecimalYear(int, int, int)
*/
public static GeoMagneticField getWMM(final double year) throws OrekitException {
synchronized (GeoMagneticFieldFactory.class) {
if (wmmModels == null) {
wmmModels = loadModels("^WMM\\.COF$");
}
return getModel(FieldModel.WMM, wmmModels, year);
}
}
/** Loads the geomagnetic model files from the given filename. The loaded
* models are inserted in a {@link TreeMap} with their epoch as key in order
* to retrieve them in a sorted manner.
* @param supportedNames a regular expression for valid filenames
* @return a {@link TreeMap} of all loaded models
* @throws OrekitException if the models could not be loaded
*/
private static TreeMap<Integer, GeoMagneticField> loadModels(final String supportedNames)
throws OrekitException {
TreeMap<Integer, GeoMagneticField> loadedModels = null;
final GeoMagneticModelLoader loader = new GeoMagneticModelLoader();
DataProvidersManager.getInstance().feed(supportedNames, loader);
if (!loader.stillAcceptsData()) {
final Collection<GeoMagneticField> models = loader.getModels();
if (models != null) {
loadedModels = new TreeMap<Integer, GeoMagneticField>();
for (GeoMagneticField model : models) {
// round to a precision of two digits after the comma
final int epoch = (int) FastMath.round(model.getEpoch() * 100d);
loadedModels.put(epoch, model);
}
}
}
// if no models could be loaded -> throw exception
if (loadedModels == null || loadedModels.size() == 0) {
throw new OrekitException(OrekitMessages.UNABLE_TO_FIND_RESOURCE, supportedNames);
}
return loadedModels;
}
/** Gets a geomagnetic field model for the given year. In case the specified
* year does not match an existing model epoch, the resulting field is
* generated by either time-transforming an existing model using its secular
* variation coefficients, or by linear interpolating two existing models.
* @param type the type of the field (e.g. WMM or IGRF)
* @param models all loaded field models, sorted by their epoch
* @param year the epoch of the resulting field model
* @return a {@link GeoMagneticField} model for the given year
* @throws OrekitException if the specified year is out of range of the available models
*/
private static GeoMagneticField getModel(final FieldModel type,
final TreeMap<Integer, GeoMagneticField> models,
final double year)
throws OrekitException {
final int epochKey = (int) (year * 100d);
final SortedMap<Integer, GeoMagneticField> head = models.headMap(epochKey, true);
if (head.isEmpty()) {
throw new OrekitException(OrekitMessages.NON_EXISTENT_GEOMAGNETIC_MODEL, type.name(), year);
}
GeoMagneticField model = models.get(head.lastKey());
if (model.getEpoch() < year) {
if (model.supportsTimeTransform()) {
model = model.transformModel(year);
} else {
final SortedMap<Integer, GeoMagneticField> tail = models.tailMap(epochKey, false);
if (tail.isEmpty()) {
throw new OrekitException(OrekitMessages.NON_EXISTENT_GEOMAGNETIC_MODEL, type.name(), year);
}
final GeoMagneticField secondModel = models.get(tail.firstKey());
if (secondModel != model) {
model = model.transformModel(secondModel, year);
}
}
}
return model;
}
}