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.     /** Empty constructor.
  74.      * <p>
  75.      * This constructor is not strictly necessary, but it prevents spurious
  76.      * javadoc warnings with JDK 18 and later.
  77.      * </p>
  78.      * @since 12.0
  79.      */
  80.     public GeoMagneticModelLoader() {
  81.         // nothing to do
  82.     }

  83.     /** Returns a {@link Collection} of the {@link GeoMagneticField} models that
  84.      * have been successfully loaded. The {@link Collection} is in
  85.      * insertion-order, thus it may not be sorted in order of the model epoch.
  86.      * @return a {@link Collection} of {@link GeoMagneticField} models
  87.      */
  88.     public Collection<GeoMagneticField> getModels() {
  89.         return models;
  90.     }

  91.     /** {@inheritDoc} */
  92.     public boolean stillAcceptsData() {
  93.         return models == null || models.isEmpty();
  94.     }

  95.     /** {@inheritDoc} */
  96.     public void loadData(final InputStream input, final String name)
  97.         throws IOException, ParseException {

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

  100.         while (true) {
  101.             final GeoMagneticField model = readModel(str);
  102.             if (model != null) {
  103.                 models.add(model);
  104.             } else {
  105.                 break;
  106.             }
  107.         }
  108.     }

  109.     /** Read the model from the given {@link StreamTokenizer}.
  110.      * @param stream the stream to read the model from
  111.      * @return the parsed geomagnetic field model
  112.      * @throws IOException if an I/O error occurs
  113.      */
  114.     private GeoMagneticField readModel(final StreamTokenizer stream) throws IOException {

  115.         // check whether there is another model available in the stream
  116.         final int ttype = stream.nextToken();
  117.         if (ttype == StreamTokenizer.TT_EOF) {
  118.             return null;
  119.         }

  120.         if (ttype == StreamTokenizer.TT_WORD) {
  121.             return readCombinedFormat(stream);
  122.         } else {
  123.             return readOriginalWMMFormat(stream);
  124.         }
  125.     }

  126.     /** Read a magnetic field from combined format.
  127.      * @param stream the stream to read the model from
  128.      * @return magnetic field
  129.      * @throws IOException if some read error occurs
  130.      */
  131.     private GeoMagneticField readCombinedFormat(final StreamTokenizer stream)
  132.         throws IOException {
  133.         final String modelName = stream.sval;
  134.         stream.nextToken();
  135.         final double epoch = stream.nval;
  136.         stream.nextToken();
  137.         final int nMax = (int) stream.nval;
  138.         stream.nextToken();
  139.         final int nMaxSecVar = (int) stream.nval;

  140.         // ignored
  141.         stream.nextToken();

  142.         stream.nextToken();
  143.         final double startYear = stream.nval;

  144.         stream.nextToken();
  145.         final double endYear = stream.nval;

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

  148.         // the rest is ignored
  149.         stream.nextToken();
  150.         @SuppressWarnings("unused")
  151.         final double altmin = stream.nval;

  152.         stream.nextToken();
  153.         @SuppressWarnings("unused")
  154.         final double altmax = stream.nval;

  155.         stream.nextToken();
  156.         stream.nextToken();

  157.         // loop to get model data from file
  158.         boolean done = false;
  159.         int n;
  160.         int m;

  161.         do {
  162.             stream.nextToken();
  163.             n = (int) stream.nval;
  164.             stream.nextToken();
  165.             m = (int) stream.nval;

  166.             stream.nextToken();
  167.             final double gnm = stream.nval;
  168.             stream.nextToken();
  169.             final double hnm = stream.nval;
  170.             stream.nextToken();
  171.             final double dgnm = stream.nval;
  172.             stream.nextToken();
  173.             final double dhnm = stream.nval;

  174.             model.setMainFieldCoefficients(n, m, gnm, hnm);
  175.             if (n <= nMaxSecVar && m <= nMaxSecVar) {
  176.                 model.setSecularVariationCoefficients(n, m, dgnm, dhnm);
  177.             }

  178.             stream.nextToken();
  179.             stream.nextToken();

  180.             done = n == nMax && m == nMax;
  181.         } while (!done);

  182.         return model;
  183.     }

  184.     /** Read a magnetic field from original WMM files.
  185.      * @param stream the stream to read the model from
  186.      * @return magnetic field
  187.      * @throws IOException if some read error occurs
  188.      */
  189.     private GeoMagneticField readOriginalWMMFormat(final StreamTokenizer stream)
  190.         throws IOException {

  191.         // hard-coded values in original WMM format
  192.         final int nMax = 12;
  193.         final int nMaxSecVar = 12;

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

  197.         final double epoch = stream.nval;
  198.         stream.nextToken();
  199.         final String modelName = stream.sval;
  200.         stream.nextToken();
  201.         final double month = stream.nval;
  202.         stream.nextToken();
  203.         final double day = stream.nval;
  204.         stream.nextToken();
  205.         final double year = stream.nval;

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

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

  209.         // loop to get model data from file
  210.         boolean done = false;
  211.         int n;
  212.         int m;

  213.         do {
  214.             stream.nextToken();
  215.             n = (int) stream.nval;
  216.             stream.nextToken();
  217.             m = (int) stream.nval;

  218.             stream.nextToken();
  219.             final double gnm = stream.nval;
  220.             stream.nextToken();
  221.             final double hnm = stream.nval;
  222.             stream.nextToken();
  223.             final double dgnm = stream.nval;
  224.             stream.nextToken();
  225.             final double dhnm = stream.nval;

  226.             model.setMainFieldCoefficients(n, m, gnm, hnm);
  227.             if (n <= nMaxSecVar && m <= nMaxSecVar) {
  228.                 model.setSecularVariationCoefficients(n, m, dgnm, dhnm);
  229.             }

  230.             done = n == nMax && m == nMax;
  231.         } while (!done);

  232.         // the original format closes with two delimiting lines of '9's
  233.         stream.nextToken();
  234.         stream.nextToken();

  235.         return model;
  236.     }

  237. }