GeoMagneticModelLoader.java
/* Copyright 2011-2012 Space Applications Services
* Licensed to CS Communication & Systèmes (CS) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* CS licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.orekit.models.earth;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import org.orekit.data.DataLoader;
/** Loads geomagnetic field models from a given input stream. A stream may contain multiple
* models, the loader reads all available models in consecutive order.
* <p>
* The format of the expected model file is either:
* <ul>
* <li>combined format as used by the geomag software, available from the
* <a href="http://www.ngdc.noaa.gov/IAGA/vmod/igrf.html">IGRF model site</a>;
* supports multiple epochs per file</li>
* <li>original format as used by the
* <a href="http://www.ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml">WMM model site</a>.
* </ul>
* <p>
* <b>Combined Format</b>
* <pre>
* {model name} {epoch} {nMax} {nMaxSec} {nMax3} {validity start} {validity end} {minAlt} {maxAlt} {model name} {line number}
* {n} {m} {gnm} {hnm} {dgnm} {dhnm} {model name} {line number}
* </pre>
* <p>
* Example:
* </p>
* <pre>
* WMM2010 2010.00 12 12 0 2010.00 2015.00 -1.0 600.0 WMM2010 0
* 1 0 -29496.6 0.0 11.6 0.0 WMM2010 1
* 1 1 -1586.3 4944.4 16.5 -25.9 WMM2010 2
* </pre>
* <p>
* <b>Original WMM Format</b>
* <pre>
* {epoch} {model name} {validity start}
* {n} {m} {gnm} {hnm} {dgnm} {dhnm}
* </pre>
* <p>
* Example:
* </p>
* <pre>
* 2015.0 WMM-2015 12/15/2014
* 1 0 -29438.5 0.0 10.7 0.0
* 1 1 -1501.1 4796.2 17.9 -26.8
* </pre>
*
* @author Thomas Neidhart
*/
public class GeoMagneticModelLoader implements DataLoader {
/** The loaded models. */
private List<GeoMagneticField> models = new LinkedList<GeoMagneticField>();
/** Empty constructor.
* <p>
* This constructor is not strictly necessary, but it prevents spurious
* javadoc warnings with JDK 18 and later.
* </p>
* @since 12.0
*/
public GeoMagneticModelLoader() {
// nothing to do
}
/** Returns a {@link Collection} of the {@link GeoMagneticField} models that
* have been successfully loaded. The {@link Collection} is in
* insertion-order, thus it may not be sorted in order of the model epoch.
* @return a {@link Collection} of {@link GeoMagneticField} models
*/
public Collection<GeoMagneticField> getModels() {
return models;
}
/** {@inheritDoc} */
public boolean stillAcceptsData() {
return models == null || models.isEmpty();
}
/** {@inheritDoc} */
public void loadData(final InputStream input, final String name)
throws IOException, ParseException {
// open data file and parse values
final StreamTokenizer str = new StreamTokenizer(new InputStreamReader(input, StandardCharsets.UTF_8));
while (true) {
final GeoMagneticField model = readModel(str);
if (model != null) {
models.add(model);
} else {
break;
}
}
}
/** Read the model from the given {@link StreamTokenizer}.
* @param stream the stream to read the model from
* @return the parsed geomagnetic field model
* @throws IOException if an I/O error occurs
*/
private GeoMagneticField readModel(final StreamTokenizer stream) throws IOException {
// check whether there is another model available in the stream
final int ttype = stream.nextToken();
if (ttype == StreamTokenizer.TT_EOF) {
return null;
}
if (ttype == StreamTokenizer.TT_WORD) {
return readCombinedFormat(stream);
} else {
return readOriginalWMMFormat(stream);
}
}
/** Read a magnetic field from combined format.
* @param stream the stream to read the model from
* @return magnetic field
* @throws IOException if some read error occurs
*/
private GeoMagneticField readCombinedFormat(final StreamTokenizer stream)
throws IOException {
final String modelName = stream.sval;
stream.nextToken();
final double epoch = stream.nval;
stream.nextToken();
final int nMax = (int) stream.nval;
stream.nextToken();
final int nMaxSecVar = (int) stream.nval;
// ignored
stream.nextToken();
stream.nextToken();
final double startYear = stream.nval;
stream.nextToken();
final double endYear = stream.nval;
final GeoMagneticField model = new GeoMagneticField(modelName, epoch, nMax, nMaxSecVar,
startYear, endYear);
// the rest is ignored
stream.nextToken();
@SuppressWarnings("unused")
final double altmin = stream.nval;
stream.nextToken();
@SuppressWarnings("unused")
final double altmax = stream.nval;
stream.nextToken();
stream.nextToken();
// loop to get model data from file
boolean done = false;
int n;
int m;
do {
stream.nextToken();
n = (int) stream.nval;
stream.nextToken();
m = (int) stream.nval;
stream.nextToken();
final double gnm = stream.nval;
stream.nextToken();
final double hnm = stream.nval;
stream.nextToken();
final double dgnm = stream.nval;
stream.nextToken();
final double dhnm = stream.nval;
model.setMainFieldCoefficients(n, m, gnm, hnm);
if (n <= nMaxSecVar && m <= nMaxSecVar) {
model.setSecularVariationCoefficients(n, m, dgnm, dhnm);
}
stream.nextToken();
stream.nextToken();
done = n == nMax && m == nMax;
} while (!done);
return model;
}
/** Read a magnetic field from original WMM files.
* @param stream the stream to read the model from
* @return magnetic field
* @throws IOException if some read error occurs
*/
private GeoMagneticField readOriginalWMMFormat(final StreamTokenizer stream)
throws IOException {
// hard-coded values in original WMM format
final int nMax = 12;
final int nMaxSecVar = 12;
// the validity start is encoded in format MM/dd/yyyy
// use the slash as whitespace character to get separate tokens
stream.whitespaceChars('/', '/');
final double epoch = stream.nval;
stream.nextToken();
final String modelName = stream.sval;
stream.nextToken();
final double month = stream.nval;
stream.nextToken();
final double day = stream.nval;
stream.nextToken();
final double year = stream.nval;
final double startYear = GeoMagneticField.getDecimalYear((int) day, (int) month, (int) year);
final GeoMagneticField model = new GeoMagneticField(modelName, epoch, nMax, nMaxSecVar,
startYear, epoch + 5.0);
// loop to get model data from file
boolean done = false;
int n;
int m;
do {
stream.nextToken();
n = (int) stream.nval;
stream.nextToken();
m = (int) stream.nval;
stream.nextToken();
final double gnm = stream.nval;
stream.nextToken();
final double hnm = stream.nval;
stream.nextToken();
final double dgnm = stream.nval;
stream.nextToken();
final double dhnm = stream.nval;
model.setMainFieldCoefficients(n, m, gnm, hnm);
if (n <= nMaxSecVar && m <= nMaxSecVar) {
model.setSecularVariationCoefficients(n, m, dgnm, dhnm);
}
done = n == nMax && m == nMax;
} while (!done);
// the original format closes with two delimiting lines of '9's
stream.nextToken();
stream.nextToken();
return model;
}
}