Java tutorial
/* Copyright 2002-2012 Space Applications Services * Licensed to CS Systmes d'Information (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.files.sp3; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Locale; import java.util.Scanner; import org.apache.commons.math3.exception.util.DummyLocalizable; import org.apache.commons.math3.geometry.euclidean.threed.Vector3D; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.files.general.OrbitFileParser; import org.orekit.files.general.SatelliteInformation; import org.orekit.files.general.SatelliteTimeCoordinate; import org.orekit.files.general.OrbitFile.TimeSystem; import org.orekit.files.sp3.SP3File.SP3FileType; import org.orekit.files.sp3.SP3File.SP3OrbitType; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeScale; import org.orekit.time.TimeScalesFactory; import org.orekit.utils.PVCoordinates; /** A parser for the SP3 orbit file format. It supports the original format as * well as the latest SP3-c version. * <p> * <b>Note:</b> this parser is thread-safe, so calling {@link #parse} from * different threads is allowed. * </p> * @see <a href="http://igscb.jpl.nasa.gov/igscb/data/format/sp3_docu.txt">SP3-a file format</a> * @see <a href="http://igscb.jpl.nasa.gov/igscb/data/format/sp3c.txt">SP3-c file format</a> * @author Thomas Neidhart */ public class SP3Parser implements OrbitFileParser { /** {@inheritDoc} */ public SP3File parse(final String fileName) throws OrekitException { InputStream stream = null; try { stream = new FileInputStream(fileName); return parse(stream); } catch (FileNotFoundException e) { throw new OrekitException(OrekitMessages.UNABLE_TO_FIND_FILE, fileName); } finally { try { if (stream != null) { stream.close(); } } catch (IOException e) { // ignore } } } /** {@inheritDoc} */ public SP3File parse(final InputStream stream) throws OrekitException { try { return parseInternal(stream); } catch (IOException e) { throw new OrekitException(e, new DummyLocalizable(e.getMessage())); } } /** Parses the SP3 file from the given {@link InputStream} and * returns a {@link SP3File} object. * @param stream the stream to read the SP3File from * @return the parsed {@link SP3File} object * @throws OrekitException if the file could not be parsed successfully * @throws IOException if an error occurs while reading from the stream */ private SP3File parseInternal(final InputStream stream) throws OrekitException, IOException { final BufferedReader reader = new BufferedReader(new InputStreamReader(stream, "UTF-8")); // initialize internal data structures final ParseInfo pi = new ParseInfo(); String line = null; int lineNumber = 1; try { while (lineNumber < 23) { line = reader.readLine(); if (line == null) { throw new OrekitException(OrekitMessages.SP3_UNEXPECTED_END_OF_FILE, lineNumber - 1); } else { parseHeaderLine(lineNumber++, line, pi); } } // now handle the epoch/position/velocity entries boolean done = false; do { line = reader.readLine(); if (line != null) { if ("EOF".equalsIgnoreCase(line)) { done = true; } else if (line.length() > 0) { parseContentLine(line, pi); } } } while (!done); } finally { try { reader.close(); } catch (IOException e1) { // ignore } } return pi.file; } /** Parses a header line from the SP3 file (line number 1 - 22). * @param lineNumber the current line number * @param line the line as read from the SP3 file * @param pi the current {@link ParseInfo} object * @throws OrekitException if a non-supported construct is found */ private void parseHeaderLine(final int lineNumber, final String line, final ParseInfo pi) throws OrekitException { final SP3File file = pi.file; Scanner scanner = null; try { scanner = new Scanner(line).useDelimiter("\\s+").useLocale(Locale.US); // CHECKSTYLE: stop FallThrough check switch (lineNumber) { // version, epoch, data used and agency information case 1: { scanner.skip("#"); final String v = scanner.next(); final char version = v.substring(0, 1).toLowerCase().charAt(0); if (version != 'a' && version != 'b' && version != 'c') { throw new OrekitException(OrekitMessages.SP3_UNSUPPORTED_VERSION, version); } pi.hasVelocityEntries = "V".equals(v.substring(1, 2)); final int year = Integer.parseInt(v.substring(2)); final int month = scanner.nextInt(); final int day = scanner.nextInt(); final int hour = scanner.nextInt(); final int minute = scanner.nextInt(); final double second = scanner.nextDouble(); final AbsoluteDate epoch = new AbsoluteDate(year, month, day, hour, minute, second, TimeScalesFactory.getGPS()); file.setEpoch(epoch); final int numEpochs = scanner.nextInt(); file.setNumberOfEpochs(numEpochs); // data used indicator file.setDataUsed(scanner.next()); file.setCoordinateSystem(scanner.next()); file.setOrbitType(SP3OrbitType.valueOf(scanner.next())); file.setAgency(scanner.next()); break; } // additional date/time references in gps/julian day notation case 2: { scanner.skip("##"); // gps week file.setGpsWeek(scanner.nextInt()); // seconds of week file.setSecondsOfWeek(scanner.nextDouble()); // epoch interval file.setEpochInterval(scanner.nextDouble()); // julian day file.setJulianDay(scanner.nextInt()); // day fraction file.setDayFraction(scanner.nextDouble()); break; } // line 3 contains the number of satellites case 3: pi.maxSatellites = Integer.parseInt(line.substring(4, 6).trim()); // fall-through intended - the line contains already the first entries // the following 4 lines contain additional satellite ids case 4: case 5: case 6: case 7: { final int lineLength = line.length(); int count = file.getSatelliteCount(); int startIdx = 9; while (count++ < pi.maxSatellites && (startIdx + 3) <= lineLength) { final String satId = line.substring(startIdx, startIdx + 3).trim(); file.addSatellite(satId); startIdx += 3; } break; } // the following 5 lines contain general accuracy information for each satellite case 8: case 9: case 10: case 11: case 12: { final int lineLength = line.length(); int satIdx = (lineNumber - 8) * 17; int startIdx = 9; while (satIdx < pi.maxSatellites && (startIdx + 3) <= lineLength) { final SatelliteInformation satInfo = file.getSatellite(satIdx++); final int exponent = Integer.parseInt(line.substring(startIdx, startIdx + 3).trim()); // the accuracy is calculated as 2**exp (in m) -> can be safely // converted to an integer as there will be no fraction satInfo.setAccuracy((int) Math.pow(2d, exponent)); startIdx += 3; } break; } case 13: { file.setType(getFileType(line.substring(3, 5).trim())); // now identify the time system in use final String tsStr = line.substring(9, 12).trim(); final TimeSystem ts = TimeSystem.GPS; if (!tsStr.equalsIgnoreCase("ccc")) { TimeSystem.valueOf(tsStr); } file.setTimeSystem(ts); switch (ts) { case GPS: pi.timeScale = TimeScalesFactory.getGPS(); break; case GAL: pi.timeScale = TimeScalesFactory.getGST(); break; case GLO: case QZS: throw new OrekitException(OrekitMessages.SP3_UNSUPPORTED_TIMESYSTEM, ts.name()); case TAI: pi.timeScale = TimeScalesFactory.getTAI(); break; case UTC: pi.timeScale = TimeScalesFactory.getUTC(); break; default: pi.timeScale = TimeScalesFactory.getGPS(); break; } break; } case 14: // ignore additional custom fields break; // load base numbers for the standard deviations of // position/velocity/clock components case 15: { // String base = line.substring(3, 13).trim(); // if (!base.equals("0.0000000")) { // // (mm or 10**-4 mm/sec) // pi.posVelBase = Double.valueOf(base); // } // base = line.substring(14, 26).trim(); // if (!base.equals("0.000000000")) { // // (psec or 10**-4 psec/sec) // pi.clockBase = Double.valueOf(base); // } break; } case 16: case 17: case 18: // ignore additional custom parameters break; case 19: case 20: case 21: case 22: // ignore comment lines break; default: // ignore -> method should only be called up to line 22 break; } // CHECKSTYLE: resume FallThrough check } finally { if (scanner != null) { scanner.close(); } } } /** Parses a single content line as read from the SP3 file. * @param line a string containing the line * @param pi the current {@link ParseInfo} object */ private void parseContentLine(final String line, final ParseInfo pi) { // EP and EV lines are ignored so far final SP3File file = pi.file; switch (line.charAt(0)) { case '*': { final int year = Integer.parseInt(line.substring(3, 7).trim()); final int month = Integer.parseInt(line.substring(8, 10).trim()); final int day = Integer.parseInt(line.substring(11, 13).trim()); final int hour = Integer.parseInt(line.substring(14, 16).trim()); final int minute = Integer.parseInt(line.substring(17, 19).trim()); final double second = Double.parseDouble(line.substring(20, 31).trim()); pi.latestEpoch = new AbsoluteDate(year, month, day, hour, minute, second, pi.timeScale); break; } case 'P': { final String satelliteId = line.substring(1, 4).trim(); if (!file.containsSatellite(satelliteId)) { pi.latestPosition = null; } else { final double x = Double.parseDouble(line.substring(4, 18).trim()); final double y = Double.parseDouble(line.substring(18, 32).trim()); final double z = Double.parseDouble(line.substring(32, 46).trim()); // the position values are in km and have to be converted to m pi.latestPosition = new Vector3D(x * 1000, y * 1000, z * 1000); // clock (microsec) pi.latestClock = Double.parseDouble(line.substring(46, 60).trim()); // the additional items are optional and not read yet // if (line.length() >= 73) { // // x-sdev (b**n mm) // int xStdDevExp = Integer.valueOf(line.substring(61, // 63).trim()); // // y-sdev (b**n mm) // int yStdDevExp = Integer.valueOf(line.substring(64, // 66).trim()); // // z-sdev (b**n mm) // int zStdDevExp = Integer.valueOf(line.substring(67, // 69).trim()); // // c-sdev (b**n psec) // int cStdDevExp = Integer.valueOf(line.substring(70, // 73).trim()); // // pi.posStdDevRecord = // new PositionStdDevRecord(Math.pow(pi.posVelBase, xStdDevExp), // Math.pow(pi.posVelBase, // yStdDevExp), Math.pow(pi.posVelBase, zStdDevExp), // Math.pow(pi.clockBase, cStdDevExp)); // // String clockEventFlag = line.substring(74, 75); // String clockPredFlag = line.substring(75, 76); // String maneuverFlag = line.substring(78, 79); // String orbitPredFlag = line.substring(79, 80); // } if (!pi.hasVelocityEntries) { final SatelliteTimeCoordinate coord = new SatelliteTimeCoordinate(pi.latestEpoch, pi.latestPosition, pi.latestClock); file.addSatelliteCoordinate(satelliteId, coord); } } break; } case 'V': { final String satelliteId = line.substring(1, 4).trim(); if (file.containsSatellite(satelliteId)) { final double xv = Double.parseDouble(line.substring(4, 18).trim()); final double yv = Double.parseDouble(line.substring(18, 32).trim()); final double zv = Double.parseDouble(line.substring(32, 46).trim()); // the velocity values are in dm/s and have to be converted to m/s final Vector3D velocity = new Vector3D(xv / 10d, yv / 10d, zv / 10d); final double clockRateChange = Double.parseDouble(line.substring(46, 60).trim()); // the additional items are optional and not read yet // if (line.length() >= 73) { // // xvel-sdev (b**n 10**-4 mm/sec) // int xVstdDevExp = Integer.valueOf(line.substring(61, // 63).trim()); // // yvel-sdev (b**n 10**-4 mm/sec) // int yVstdDevExp = Integer.valueOf(line.substring(64, // 66).trim()); // // zvel-sdev (b**n 10**-4 mm/sec) // int zVstdDevExp = Integer.valueOf(line.substring(67, // 69).trim()); // // clkrate-sdev (b**n 10**-4 psec/sec) // int clkStdDevExp = Integer.valueOf(line.substring(70, // 73).trim()); // } final SatelliteTimeCoordinate coord = new SatelliteTimeCoordinate(pi.latestEpoch, new PVCoordinates(pi.latestPosition, velocity), pi.latestClock, clockRateChange); file.addSatelliteCoordinate(satelliteId, coord); } break; } default: // ignore everything else break; } } /** Returns the {@link SP3FileType} that corresponds to a given string in a SP3 file. * @param fileType file type as string * @return file type as enum */ private SP3FileType getFileType(final String fileType) { SP3FileType type = SP3FileType.UNDEFINED; if ("G".equalsIgnoreCase(fileType)) { type = SP3FileType.GPS; } else if ("M".equalsIgnoreCase(fileType)) { type = SP3FileType.MIXED; } else if ("R".equalsIgnoreCase(fileType)) { type = SP3FileType.GLONASS; } else if ("L".equalsIgnoreCase(fileType)) { type = SP3FileType.LEO; } else if ("E".equalsIgnoreCase(fileType)) { type = SP3FileType.GALILEO; } else if ("C".equalsIgnoreCase(fileType)) { type = SP3FileType.COMPASS; } else if ("J".equalsIgnoreCase(fileType)) { type = SP3FileType.QZSS; } return type; } /** Transient data used for parsing a sp3 file. The data is kept in a * separate data structure to make the parser thread-safe. * <p><b>Note</b>: The class intentionally does not provide accessor * methods, as it is only used internally for parsing a SP3 file.</p> */ private static class ParseInfo { /** The corresponding SP3File object. */ private SP3File file; /** The latest epoch as read from the SP3 file. */ private AbsoluteDate latestEpoch; /** The latest position as read from the SP3 file. */ private Vector3D latestPosition; /** The latest clock value as read from the SP3 file. */ private double latestClock; /** Indicates if the SP3 file has velocity entries. */ private boolean hasVelocityEntries; /** The timescale used in the SP3 file. */ private TimeScale timeScale; /** The number of satellites as contained in the SP3 file. */ private int maxSatellites; /** The base for pos/vel. */ //private double posVelBase; /** The base for clock/rate. */ //private double clockBase; /** Create a new {@link ParseInfo} object. */ protected ParseInfo() { file = new SP3File(); latestEpoch = null; latestPosition = null; latestClock = 0.0d; hasVelocityEntries = false; timeScale = TimeScalesFactory.getGPS(); maxSatellites = 0; //posVelBase = 2d; //clockBase = 2d; } } }