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.      * @see GeoMagneticField#getDecimalYear(int, int, int)
  57.      */
  58.     public static GeoMagneticField getField(final FieldModel type, final double year) {

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

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

  81.     /** Get the WMM model for the given year.
  82.      * @param year the decimal year
  83.      * @return a {@link GeoMagneticField} for the given year
  84.      * @see GeoMagneticField#getDecimalYear(int, int, int)
  85.      */
  86.     public static GeoMagneticField getWMM(final double year) {
  87.         synchronized (GeoMagneticFieldFactory.class) {
  88.             if (wmmModels == null) {
  89.                 wmmModels = loadModels("^WMM\\.COF$");
  90.             }
  91.             return getModel(FieldModel.WMM, wmmModels, year);
  92.         }
  93.     }

  94.     /** Loads the geomagnetic model files from the given filename. The loaded
  95.      * models are inserted in a {@link TreeMap} with their epoch as key in order
  96.      * to retrieve them in a sorted manner.
  97.      * @param supportedNames a regular expression for valid filenames
  98.      * @return a {@link TreeMap} of all loaded models
  99.      */
  100.     private static TreeMap<Integer, GeoMagneticField> loadModels(final String supportedNames) {

  101.         TreeMap<Integer, GeoMagneticField> loadedModels = null;
  102.         final GeoMagneticModelLoader loader = new GeoMagneticModelLoader();
  103.         DataProvidersManager.getInstance().feed(supportedNames, loader);

  104.         if (!loader.stillAcceptsData()) {
  105.             final Collection<GeoMagneticField> models = loader.getModels();
  106.             if (models != null) {
  107.                 loadedModels = new TreeMap<Integer, GeoMagneticField>();
  108.                 for (GeoMagneticField model : models) {
  109.                     // round to a precision of two digits after the comma
  110.                     final int epoch = (int) FastMath.round(model.getEpoch() * 100d);
  111.                     loadedModels.put(epoch, model);
  112.                 }
  113.             }
  114.         }

  115.         // if no models could be loaded -> throw exception
  116.         if (loadedModels == null || loadedModels.size() == 0) {
  117.             throw new OrekitException(OrekitMessages.UNABLE_TO_FIND_RESOURCE, supportedNames);
  118.         }

  119.         return loadedModels;
  120.     }

  121.     /** Gets a geomagnetic field model for the given year. In case the specified
  122.      * year does not match an existing model epoch, the resulting field is
  123.      * generated by either time-transforming an existing model using its secular
  124.      * variation coefficients, or by linear interpolating two existing models.
  125.      * @param type the type of the field (e.g. WMM or IGRF)
  126.      * @param models all loaded field models, sorted by their epoch
  127.      * @param year the epoch of the resulting field model
  128.      * @return a {@link GeoMagneticField} model for the given year
  129.      */
  130.     private static GeoMagneticField getModel(final FieldModel type,
  131.                                              final TreeMap<Integer, GeoMagneticField> models,
  132.                                              final double year) {

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

  135.         if (head.isEmpty()) {
  136.             throw new OrekitException(OrekitMessages.NON_EXISTENT_GEOMAGNETIC_MODEL, type.name(), year);
  137.         }

  138.         GeoMagneticField model = models.get(head.lastKey());
  139.         if (model.getEpoch() < year) {
  140.             if (model.supportsTimeTransform()) {
  141.                 model = model.transformModel(year);
  142.             } else {
  143.                 final SortedMap<Integer, GeoMagneticField> tail = models.tailMap(epochKey, false);
  144.                 if (tail.isEmpty()) {
  145.                     throw new OrekitException(OrekitMessages.NON_EXISTENT_GEOMAGNETIC_MODEL, type.name(), year);
  146.                 }
  147.                 final GeoMagneticField secondModel = models.get(tail.firstKey());
  148.                 if (secondModel != model) {
  149.                     model = model.transformModel(secondModel, year);
  150.                 }
  151.             }
  152.         }
  153.         return model;
  154.     }
  155. }