GeoMagneticModelLoader.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.io.IOException;
  19. import java.io.InputStream;
  20. import java.io.InputStreamReader;
  21. import java.io.StreamTokenizer;
  22. import java.text.ParseException;
  23. import java.util.Collection;
  24. import java.util.LinkedList;
  25. import java.util.List;

  26. import org.orekit.data.DataLoader;

  27. /** Loads geomagnetic field models from a given input stream. A stream may contain multiple
  28.  * models, the loader reads all available models in consecutive order.
  29.  * <p>
  30.  * The format of the expected model file is either:
  31.  * <ul>
  32.  *   <li>combined format as used by the geomag software, available from the
  33.  *       <a href="http://www.ngdc.noaa.gov/IAGA/vmod/igrf.html">IGRF model site</a>;
  34.  *       supports multiple epochs per file</li>
  35.  *   <li>original format as used by the
  36.  *       <a href="http://www.ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml">WMM model site</a>.
  37.  * </ul>
  38.  * <p>
  39.  * <b>Combined Format</b>
  40.  * <pre>
  41.  *     {model name} {epoch} {nMax} {nMaxSec} {nMax3} {validity start} {validity end} {minAlt} {maxAlt} {model name} {line number}
  42.  * {n} {m} {gnm} {hnm} {dgnm} {dhnm} {model name} {line number}
  43.  * </pre>
  44.  * <p>
  45.  * Example:
  46.  * </p>
  47.  * <pre>
  48.  *    WMM2010  2010.00 12 12  0 2010.00 2015.00   -1.0  600.0          WMM2010   0
  49.  * 1  0  -29496.6       0.0      11.6       0.0                        WMM2010   1
  50.  * 1  1   -1586.3    4944.4      16.5     -25.9                        WMM2010   2
  51.  * </pre>
  52.  * <p>
  53.  * <b>Original WMM Format</b>
  54.  * <pre>
  55.  *    {epoch} {model name} {validity start}
  56.  * {n} {m} {gnm} {hnm} {dgnm} {dhnm}
  57.  * </pre>
  58.  * <p>
  59.  * Example:
  60.  * </p>
  61.  * <pre>
  62.  *    2015.0            WMM-2015        12/15/2014
  63.  *  1  0  -29438.5       0.0       10.7        0.0
  64.  *  1  1   -1501.1    4796.2       17.9      -26.8
  65.  * </pre>
  66.  *
  67.  * @author Thomas Neidhart
  68.  */
  69. public class GeoMagneticModelLoader implements DataLoader {

  70.     /** The loaded models. */
  71.     private List<GeoMagneticField> models = new LinkedList<GeoMagneticField>();

  72.     /** Returns a {@link Collection} of the {@link GeoMagneticField} models that
  73.      * have been successfully loaded. The {@link Collection} is in
  74.      * insertion-order, thus it may not be sorted in order of the model epoch.
  75.      * @return a {@link Collection} of {@link GeoMagneticField} models
  76.      */
  77.     public Collection<GeoMagneticField> getModels() {
  78.         return models;
  79.     }

  80.     /** {@inheritDoc} */
  81.     public boolean stillAcceptsData() {
  82.         return models == null || models.isEmpty();
  83.     }

  84.     /** {@inheritDoc} */
  85.     public void loadData(final InputStream input, final String name)
  86.         throws IOException, ParseException {

  87.         // open data file and parse values
  88.         final StreamTokenizer str = new StreamTokenizer(new InputStreamReader(input, "UTF-8"));

  89.         while (true) {
  90.             final GeoMagneticField model = readModel(str);
  91.             if (model != null) {
  92.                 models.add(model);
  93.             } else {
  94.                 break;
  95.             }
  96.         }
  97.     }

  98.     /** Read the model from the given {@link StreamTokenizer}.
  99.      * @param stream the stream to read the model from
  100.      * @return the parsed geomagnetic field model
  101.      * @throws IOException if an I/O error occurs
  102.      */
  103.     private GeoMagneticField readModel(final StreamTokenizer stream) throws IOException {

  104.         // check whether there is another model available in the stream
  105.         final int ttype = stream.nextToken();
  106.         if (ttype == StreamTokenizer.TT_EOF) {
  107.             return null;
  108.         }

  109.         if (ttype == StreamTokenizer.TT_WORD) {
  110.             return readCombinedFormat(stream);
  111.         } else {
  112.             return readOriginalWMMFormat(stream);
  113.         }
  114.     }

  115.     /** Read a magnetic field from combined format.
  116.      * @param stream the stream to read the model from
  117.      * @return magnetic field
  118.      * @throws IOException if some read error occurs
  119.      */
  120.     private GeoMagneticField readCombinedFormat(final StreamTokenizer stream)
  121.         throws IOException {
  122.         final String modelName = stream.sval;
  123.         stream.nextToken();
  124.         final double epoch = stream.nval;
  125.         stream.nextToken();
  126.         final int nMax = (int) stream.nval;
  127.         stream.nextToken();
  128.         final int nMaxSecVar = (int) stream.nval;

  129.         // ignored
  130.         stream.nextToken();

  131.         stream.nextToken();
  132.         final double startYear = stream.nval;

  133.         stream.nextToken();
  134.         final double endYear = stream.nval;

  135.         final GeoMagneticField model = new GeoMagneticField(modelName, epoch, nMax, nMaxSecVar,
  136.                                                             startYear, endYear);

  137.         // the rest is ignored
  138.         stream.nextToken();
  139.         @SuppressWarnings("unused")
  140.         final double altmin = stream.nval;

  141.         stream.nextToken();
  142.         @SuppressWarnings("unused")
  143.         final double altmax = stream.nval;

  144.         stream.nextToken();
  145.         stream.nextToken();

  146.         // loop to get model data from file
  147.         boolean done = false;
  148.         int n;
  149.         int m;

  150.         do {
  151.             stream.nextToken();
  152.             n = (int) stream.nval;
  153.             stream.nextToken();
  154.             m = (int) stream.nval;

  155.             stream.nextToken();
  156.             final double gnm = stream.nval;
  157.             stream.nextToken();
  158.             final double hnm = stream.nval;
  159.             stream.nextToken();
  160.             final double dgnm = stream.nval;
  161.             stream.nextToken();
  162.             final double dhnm = stream.nval;

  163.             model.setMainFieldCoefficients(n, m, gnm, hnm);
  164.             if (n <= nMaxSecVar && m <= nMaxSecVar) {
  165.                 model.setSecularVariationCoefficients(n, m, dgnm, dhnm);
  166.             }

  167.             stream.nextToken();
  168.             stream.nextToken();

  169.             done = n == nMax && m == nMax;
  170.         } while (!done);

  171.         return model;
  172.     }

  173.     /** Read a magnetic field from original WMM files.
  174.      * @param stream the stream to read the model from
  175.      * @return magnetic field
  176.      * @throws IOException if some read error occurs
  177.      */
  178.     private GeoMagneticField readOriginalWMMFormat(final StreamTokenizer stream)
  179.         throws IOException {

  180.         // hard-coded values in original WMM format
  181.         final int nMax = 12;
  182.         final int nMaxSecVar = 12;

  183.         // the validity start is encoded in format MM/dd/yyyy
  184.         // use the slash as whitespace character to get separate tokens
  185.         stream.whitespaceChars('/', '/');

  186.         final double epoch = stream.nval;
  187.         stream.nextToken();
  188.         final String modelName = stream.sval;
  189.         stream.nextToken();
  190.         final double month = stream.nval;
  191.         stream.nextToken();
  192.         final double day = stream.nval;
  193.         stream.nextToken();
  194.         final double year = stream.nval;

  195.         final double startYear = GeoMagneticField.getDecimalYear((int) day, (int) month, (int) year);

  196.         final GeoMagneticField model = new GeoMagneticField(modelName, epoch, nMax, nMaxSecVar,
  197.                                                             startYear, epoch + 5.0);

  198.         // loop to get model data from file
  199.         boolean done = false;
  200.         int n;
  201.         int m;

  202.         do {
  203.             stream.nextToken();
  204.             n = (int) stream.nval;
  205.             stream.nextToken();
  206.             m = (int) stream.nval;

  207.             stream.nextToken();
  208.             final double gnm = stream.nval;
  209.             stream.nextToken();
  210.             final double hnm = stream.nval;
  211.             stream.nextToken();
  212.             final double dgnm = stream.nval;
  213.             stream.nextToken();
  214.             final double dhnm = stream.nval;

  215.             model.setMainFieldCoefficients(n, m, gnm, hnm);
  216.             if (n <= nMaxSecVar && m <= nMaxSecVar) {
  217.                 model.setSecularVariationCoefficients(n, m, dgnm, dhnm);
  218.             }

  219.             done = n == nMax && m == nMax;
  220.         } while (!done);

  221.         // the original format closes with two delimiting lines of '9's
  222.         stream.nextToken();
  223.         stream.nextToken();

  224.         return model;
  225.     }

  226. }