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  
19  import org.orekit.data.DataSource;
20  
21  import java.io.IOException;
22  import java.io.StreamTokenizer;
23  import java.util.ArrayList;
24  import java.util.List;
25  
26  /** Loads geomagnetic field models from a given input stream. A stream may contain multiple
27   * models, the loader reads all available models in consecutive order.
28   * <p>
29   * The format of the expected model file is either:
30   * <ul>
31   *   <li>combined format as used by the geomag software, available from the
32   *       <a href="http://www.ngdc.noaa.gov/IAGA/vmod/igrf.html">IGRF model site</a>;
33   *       supports multiple epochs per file</li>
34   *   <li>original format as used by the
35   *       <a href="http://www.ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml">WMM model site</a>.
36   * </ul>
37   * <p>
38   * <b>Combined Format</b>
39   * <pre>
40   *     {model name} {epoch} {nMax} {nMaxSec} {nMax3} {validity start} {validity end} {minAlt} {maxAlt} {model name} {line number}
41   * {n} {m} {gnm} {hnm} {dgnm} {dhnm} {model name} {line number}
42   * </pre>
43   * <p>
44   * Example:
45   * </p>
46   * <pre>
47   *    WMM2010  2010.00 12 12  0 2010.00 2015.00   -1.0  600.0          WMM2010   0
48   * 1  0  -29496.6       0.0      11.6       0.0                        WMM2010   1
49   * 1  1   -1586.3    4944.4      16.5     -25.9                        WMM2010   2
50   * </pre>
51   * <p>
52   * <b>Original WMM Format</b>
53   * <pre>
54   *    {epoch} {model name} {validity start}
55   * {n} {m} {gnm} {hnm} {dgnm} {dhnm}
56   * </pre>
57   * <p>
58   * Example:
59   * </p>
60   * <pre>
61   *    2015.0            WMM-2015        12/15/2014
62   *  1  0  -29438.5       0.0       10.7        0.0
63   *  1  1   -1501.1    4796.2       17.9      -26.8
64   * </pre>
65   *
66   * @author Thomas Neidhart
67   * @author Luc Maisonobe
68   * @since 13.0
69   */
70  public class GeoMagneticModelParser {
71  
72      /** Empty constructor.
73       */
74      public GeoMagneticModelParser() {
75          // nothing to do
76      }
77  
78      /** Parse a model.
79       * @param dataSource source data
80       * @return parsed fields
81       * @throws IOException if an I/O error occurs
82       */
83      public List<GeoMagneticField> parse(final DataSource dataSource) throws IOException {
84  
85          // open data file and parse values
86          final StreamTokenizer str = new StreamTokenizer(dataSource.getOpener().openReaderOnce());
87  
88          // read all the models from source
89          final List<GeoMagneticField> fields = new ArrayList<>();
90          for (GeoMagneticField model = readModel(str); model != null; model = readModel(str)) {
91              fields.add(model);
92          }
93  
94          return fields;
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 
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 
111         if (ttype == StreamTokenizer.TT_WORD) {
112             return readCombinedFormat(stream);
113         } else {
114             return readOriginalWMMFormat(stream);
115         }
116     }
117 
118     /** Read a magnetic field from combined format.
119      * @param stream the stream to read the model from
120      * @return magnetic field
121      * @throws IOException if some read error occurs
122      */
123     private GeoMagneticField readCombinedFormat(final StreamTokenizer stream)
124         throws IOException {
125         final String modelName = stream.sval;
126         stream.nextToken();
127         final double epoch = stream.nval;
128         stream.nextToken();
129         final int nMax = (int) stream.nval;
130         stream.nextToken();
131         final int nMaxSecVar = (int) stream.nval;
132 
133         // ignored
134         stream.nextToken();
135 
136         stream.nextToken();
137         final double startYear = stream.nval;
138 
139         stream.nextToken();
140         final double endYear = stream.nval;
141 
142         final GeoMagneticField model = new GeoMagneticField(modelName, epoch, nMax, nMaxSecVar,
143                                                             startYear, endYear);
144 
145         // the rest is ignored
146         stream.nextToken();
147         @SuppressWarnings("unused")
148         final double altmin = stream.nval;
149 
150         stream.nextToken();
151         @SuppressWarnings("unused")
152         final double altmax = stream.nval;
153 
154         stream.nextToken();
155         stream.nextToken();
156 
157         // loop to get model data from file
158         boolean done;
159         int n;
160         int m;
161 
162         do {
163             stream.nextToken();
164             n = (int) stream.nval;
165             stream.nextToken();
166             m = (int) stream.nval;
167 
168             stream.nextToken();
169             final double gnm = stream.nval;
170             stream.nextToken();
171             final double hnm = stream.nval;
172             stream.nextToken();
173             final double dgnm = stream.nval;
174             stream.nextToken();
175             final double dhnm = stream.nval;
176 
177             model.setMainFieldCoefficients(n, m, gnm, hnm);
178             if (n <= nMaxSecVar && m <= nMaxSecVar) {
179                 model.setSecularVariationCoefficients(n, m, dgnm, dhnm);
180             }
181 
182             stream.nextToken();
183             stream.nextToken();
184 
185             done = n == nMax && m == nMax;
186         } while (!done);
187 
188         return model;
189     }
190 
191     /** Read a magnetic field from original WMM files.
192      * @param stream the stream to read the model from
193      * @return magnetic field
194      * @throws IOException if some read error occurs
195      */
196     private GeoMagneticField readOriginalWMMFormat(final StreamTokenizer stream) throws IOException {
197 
198         // hard-coded values in original WMM format
199         final int nMax = 12;
200         final int nMaxSecVar = 12;
201 
202         // the validity start is encoded in format MM/dd/yyyy
203         // use the slash as whitespace character to get separate tokens
204         stream.whitespaceChars('/', '/');
205 
206         final double epoch = stream.nval;
207         stream.nextToken();
208         final String modelName = stream.sval;
209         stream.nextToken();
210         final double month = stream.nval;
211         stream.nextToken();
212         final double day = stream.nval;
213         stream.nextToken();
214         final double year = stream.nval;
215 
216         final double startYear = GeoMagneticField.getDecimalYear((int) day, (int) month, (int) year);
217 
218         final GeoMagneticField model = new GeoMagneticField(modelName, epoch, nMax, nMaxSecVar,
219                                                             startYear, epoch + 5.0);
220 
221         // loop to get model data from file
222         boolean done;
223         int n;
224         int m;
225 
226         do {
227             stream.nextToken();
228             n = (int) stream.nval;
229             stream.nextToken();
230             m = (int) stream.nval;
231 
232             stream.nextToken();
233             final double gnm = stream.nval;
234             stream.nextToken();
235             final double hnm = stream.nval;
236             stream.nextToken();
237             final double dgnm = stream.nval;
238             stream.nextToken();
239             final double dhnm = stream.nval;
240 
241             model.setMainFieldCoefficients(n, m, gnm, hnm);
242             if (n <= nMaxSecVar && m <= nMaxSecVar) {
243                 model.setSecularVariationCoefficients(n, m, dgnm, dhnm);
244             }
245 
246             done = n == nMax && m == nMax;
247         } while (!done);
248 
249         // the original format closes with two delimiting lines of '9's
250         stream.nextToken();
251         stream.nextToken();
252 
253         return model;
254     }
255 
256 }