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="https://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="https://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 }