GeoMagneticFieldFactory.java

  1. /* Copyright 2011-2012 Space Applications Services
  2.  * Licensed to CS Communication & Systèmes (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.models.earth;

  18. import java.util.Collection;
  19. import java.util.SortedMap;
  20. import java.util.TreeMap;

  21. import org.hipparchus.util.FastMath;
  22. import org.orekit.data.DataProvidersManager;
  23. import org.orekit.errors.OrekitException;
  24. import org.orekit.errors.OrekitMessages;

  25. /** Factory for different {@link GeoMagneticField} models.
  26.  * <p>
  27.  * This is a utility class, so its constructor is private.
  28.  * </p>
  29.  * @author Thomas Neidhart
  30.  */
  31. public class GeoMagneticFieldFactory {

  32.     /** The currently supported geomagnetic field models. */
  33.     public enum FieldModel {
  34.         /** World Magnetic Model. */
  35.         WMM,
  36.         /** International Geomagnetic Reference Field. */
  37.         IGRF
  38.     }

  39.     /** Loaded IGRF models. */
  40.     private static TreeMap<Integer, GeoMagneticField> igrfModels = null;

  41.     /** Loaded WMM models. */
  42.     private static TreeMap<Integer, GeoMagneticField> wmmModels = null;

  43.     /** Private constructor.
  44.      * <p>
  45.      * This class is a utility class, it should neither have a public nor a
  46.      * default constructor. This private constructor prevents the compiler from
  47.      * generating one automatically.
  48.      * </p>
  49.      */
  50.     private GeoMagneticFieldFactory() {
  51.     }

  52.     /** Get the {@link GeoMagneticField} for the given model type and year.
  53.      * @param type the field model type
  54.      * @param year the decimal year
  55.      * @return a {@link GeoMagneticField} for the given year and model
  56.      * @throws OrekitException if the models could not be loaded
  57.      * @see GeoMagneticField#getDecimalYear(int, int, int)
  58.      */
  59.     public static GeoMagneticField getField(final FieldModel type, final double year)
  60.         throws OrekitException {

  61.         switch (type) {
  62.             case WMM:
  63.                 return getWMM(year);
  64.             case IGRF:
  65.                 return getIGRF(year);
  66.             default:
  67.                 throw new OrekitException(OrekitMessages.NON_EXISTENT_GEOMAGNETIC_MODEL, type.name(), year);
  68.         }
  69.     }

  70.     /** Get the IGRF model for the given year.
  71.      * @param year the decimal year
  72.      * @return a {@link GeoMagneticField} for the given year
  73.      * @throws OrekitException
  74.      *             if the IGRF models could not be loaded
  75.      * @see GeoMagneticField#getDecimalYear(int, int, int)
  76.      */
  77.     public static GeoMagneticField getIGRF(final double year) throws OrekitException {
  78.         synchronized (GeoMagneticFieldFactory.class) {
  79.             if (igrfModels == null) {
  80.                 igrfModels = loadModels("^IGRF\\.COF$");
  81.             }
  82.             return getModel(FieldModel.IGRF, igrfModels, year);
  83.         }
  84.     }

  85.     /** Get the WMM model for the given year.
  86.      * @param year the decimal year
  87.      * @return a {@link GeoMagneticField} for the given year
  88.      * @throws OrekitException if the WMM models could not be loaded
  89.      * @see GeoMagneticField#getDecimalYear(int, int, int)
  90.      */
  91.     public static GeoMagneticField getWMM(final double year) throws OrekitException {
  92.         synchronized (GeoMagneticFieldFactory.class) {
  93.             if (wmmModels == null) {
  94.                 wmmModels = loadModels("^WMM\\.COF$");
  95.             }
  96.             return getModel(FieldModel.WMM, wmmModels, year);
  97.         }
  98.     }

  99.     /** Loads the geomagnetic model files from the given filename. The loaded
  100.      * models are inserted in a {@link TreeMap} with their epoch as key in order
  101.      * to retrieve them in a sorted manner.
  102.      * @param supportedNames a regular expression for valid filenames
  103.      * @return a {@link TreeMap} of all loaded models
  104.      * @throws OrekitException if the models could not be loaded
  105.      */
  106.     private static TreeMap<Integer, GeoMagneticField> loadModels(final String supportedNames)
  107.         throws OrekitException {

  108.         TreeMap<Integer, GeoMagneticField> loadedModels = null;
  109.         final GeoMagneticModelLoader loader = new GeoMagneticModelLoader();
  110.         DataProvidersManager.getInstance().feed(supportedNames, loader);

  111.         if (!loader.stillAcceptsData()) {
  112.             final Collection<GeoMagneticField> models = loader.getModels();
  113.             if (models != null) {
  114.                 loadedModels = new TreeMap<Integer, GeoMagneticField>();
  115.                 for (GeoMagneticField model : models) {
  116.                     // round to a precision of two digits after the comma
  117.                     final int epoch = (int) FastMath.round(model.getEpoch() * 100d);
  118.                     loadedModels.put(epoch, model);
  119.                 }
  120.             }
  121.         }

  122.         // if no models could be loaded -> throw exception
  123.         if (loadedModels == null || loadedModels.size() == 0) {
  124.             throw new OrekitException(OrekitMessages.UNABLE_TO_FIND_RESOURCE, supportedNames);
  125.         }

  126.         return loadedModels;
  127.     }

  128.     /** Gets a geomagnetic field model for the given year. In case the specified
  129.      * year does not match an existing model epoch, the resulting field is
  130.      * generated by either time-transforming an existing model using its secular
  131.      * variation coefficients, or by linear interpolating two existing models.
  132.      * @param type the type of the field (e.g. WMM or IGRF)
  133.      * @param models all loaded field models, sorted by their epoch
  134.      * @param year the epoch of the resulting field model
  135.      * @return a {@link GeoMagneticField} model for the given year
  136.      * @throws OrekitException if the specified year is out of range of the available models
  137.      */
  138.     private static GeoMagneticField getModel(final FieldModel type,
  139.                                              final TreeMap<Integer, GeoMagneticField> models,
  140.                                              final double year)
  141.         throws OrekitException {

  142.         final int epochKey = (int) (year * 100d);
  143.         final SortedMap<Integer, GeoMagneticField> head = models.headMap(epochKey, true);

  144.         if (head.isEmpty()) {
  145.             throw new OrekitException(OrekitMessages.NON_EXISTENT_GEOMAGNETIC_MODEL, type.name(), year);
  146.         }

  147.         GeoMagneticField model = models.get(head.lastKey());
  148.         if (model.getEpoch() < year) {
  149.             if (model.supportsTimeTransform()) {
  150.                 model = model.transformModel(year);
  151.             } else {
  152.                 final SortedMap<Integer, GeoMagneticField> tail = models.tailMap(epochKey, false);
  153.                 if (tail.isEmpty()) {
  154.                     throw new OrekitException(OrekitMessages.NON_EXISTENT_GEOMAGNETIC_MODEL, type.name(), year);
  155.                 }
  156.                 final GeoMagneticField secondModel = models.get(tail.firstKey());
  157.                 if (secondModel != model) {
  158.                     model = model.transformModel(secondModel, year);
  159.                 }
  160.             }
  161.         }
  162.         return model;
  163.     }
  164. }