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.nio.charset.StandardCharsets;
  23. import java.text.ParseException;
  24. import java.util.Collection;
  25. import java.util.LinkedList;
  26. import java.util.List;

  27. import org.orekit.data.DataLoader;

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

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

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

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

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

  88.         // open data file and parse values
  89.         final StreamTokenizer str = new StreamTokenizer(new InputStreamReader(input, StandardCharsets.UTF_8));

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

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

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

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

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

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

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

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

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

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

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

  145.         stream.nextToken();
  146.         stream.nextToken();

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

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

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

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

  168.             stream.nextToken();
  169.             stream.nextToken();

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

  172.         return model;
  173.     }

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

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

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

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

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

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

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

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

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

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

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

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

  225.         return model;
  226.     }

  227. }