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