/* Copyright (C) 2001, 2007 United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All Rights Reserved. */ package gov.nasa.worldwind.formats.nmea; import gov.nasa.worldwind.util.Logging; import gov.nasa.worldwind.tracks.Track; import gov.nasa.worldwind.tracks.TrackSegment; import gov.nasa.worldwind.tracks.TrackPoint; /** * @author dcollins * @version $Id: NmeaWriter.java 4862 2008-03-30 01:25:38Z dcollins $ */ public class NmeaWriter { private final java.io.PrintStream printStream; private final String encoding; @SuppressWarnings({"UnusedDeclaration"}) private int sentenceNumber = 0; private static final String DEFAULT_ENCODING = "US-ASCII"; public NmeaWriter(String path) throws java.io.IOException { this(path, DEFAULT_ENCODING); } public NmeaWriter(String path, String encoding) throws java.io.IOException { if (path == null) { String msg = Logging.getMessage("nullValue.PathIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } if (encoding == null) { String msg = Logging.getMessage("nullValue.StringIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } this.encoding = encoding; this.printStream = new java.io.PrintStream( new java.io.BufferedOutputStream(new java.io.FileOutputStream(path)), false, // Disable autoflush. this.encoding); // Character mapping from 16-bit UTF characters to bytes. } public NmeaWriter(java.io.OutputStream stream) throws java.io.IOException { this(stream, DEFAULT_ENCODING); } public NmeaWriter(java.io.OutputStream stream, String encoding) throws java.io.IOException { if (stream == null) { String msg = Logging.getMessage("nullValue.InputStreamIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } if (encoding == null) { String msg = Logging.getMessage("nullValue.StringIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } this.encoding = encoding; this.printStream = new java.io.PrintStream( new java.io.BufferedOutputStream(stream), false, // Disable autoflush. this.encoding); // Character mapping from 16-bit UTF characters to bytes. } public final String getEncoding() { return this.encoding; } public void writeTrack(Track track) { if (track == null) { String msg = Logging.getMessage("nullValue.TrackIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } doWriteTrack(track, this.printStream); doFlush(); } public void close() { doFlush(); this.printStream.close(); } private void doWriteTrack(Track track, java.io.PrintStream out) { if (track != null && track.getSegments() != null) { for (TrackSegment ts : track.getSegments()) doWriteTrackSegment(ts, out); } } private void doWriteTrackSegment(TrackSegment segment, java.io.PrintStream out) { if (segment != null && segment.getPoints() != null) { for (TrackPoint tp : segment.getPoints()) { if (tp instanceof NmeaTrackPoint) doWriteNmeaTrackPoint((NmeaTrackPoint) tp, out); else doWriteTrackPoint(tp, out); } } } private void doWriteTrackPoint(TrackPoint point, java.io.PrintStream out) { if (point != null) { writeGGASentence(point.getTime(), point.getLatitude(), point.getLongitude(), point.getElevation(), 0, out); } } private void doWriteNmeaTrackPoint(NmeaTrackPoint point, java.io.PrintStream out) { if (point != null) { // TODO: separate elevation and geoid-height writeGGASentence(point.getTime(), point.getLatitude(), point.getLongitude(), point.getElevation(), 0, out); } } private void writeGGASentence(String time, double lat, double lon, double altitude, double geoidHeight, java.io.PrintStream out) { this.sentenceNumber++; // Documentation for NMEA Standard 0183 // taken from http://www.gpsinformation.org/dale/nmea.htm#GGA StringBuilder sb = new StringBuilder(); sb.append("GP"); // Global Positioning System Fix Data sb.append("GGA"); sb.append(","); // Fix taken at "HHMMSS" UTC sb.append(formatTime(time)); sb.append(","); // Latitude "DDMM.MMM,[N|S]" sb.append(formatLatitude(lat)); sb.append(","); // Longitude "DDDMM.MMM,[N|S]" sb.append(formatLongitude(lon)); sb.append(","); // Fix quality: 0 = invalid // 1 = GPS fix (SPS) // 2 = DGPS fix // 3 = PPS fix // 4 = Real Time Kinematic // 5 = Float RTK // 6 = estimated (dead reckoning) (2.3 feature) // 7 = Manual input mode // 8 = Simulation mode sb.append(""); // Intentionally left blank. sb.append(","); // Number of satellites being tracked sb.append(""); // Intentionally left blank. sb.append(","); // Horizontal dilution of position sb.append(""); // Intentionally left blank sb.append(","); // Altitude, Meters, above mean sea level sb.append(formatElevation(altitude)); sb.append(","); // Height of geoid (mean sea level) above WGS84 ellipsoid sb.append(formatElevation(geoidHeight)); sb.append(","); // time in seconds since last DGPS update sb.append(""); // Intentionally left blank. sb.append(","); // DGPS station ID number sb.append(""); // Intentionally left blank. sb.append(","); // the checksum data, always begins with * int chksum = computeChecksum(sb, 0, sb.length()); sb.append("*"); sb.append(formatChecksum(chksum)); out.print("$"); out.print(sb); out.print("\r\n"); doFlush(); } private String formatTime(String time) { // Format time as "HHMMSS" return (time != null) ? time : ""; } private String formatLatitude(double degrees) { int d = (int) Math.floor(Math.abs(degrees)); double m = 60 * (Math.abs(degrees) - d); // Format latitude as "DDMM.MMM[N|S]" return String.format("%02d%06.3f,%s", d, m, degrees < 0 ? "S" : "N"); } private String formatLongitude(double degrees) { int d = (int) Math.floor(Math.abs(degrees)); double m = 60 * (Math.abs(degrees) - d); // Format longitude as "DDDMM.MMM[N|S]" return String.format("%03d%06.3f,%s", d, m, degrees < 0 ? "W" : "E"); } private String formatElevation(double metersElevation) { // Format elevation with 1 digit of precision. // This provides decimeter resolution. return String.format("%.1f,M", metersElevation); } private String formatChecksum(int checksum) { return Integer.toHexString(checksum); } private int computeChecksum(CharSequence s, int start, int end) { int chksum = 0; for (int i = start; i < end; i++) { int c = 0xFF & (int) s.charAt(i); chksum ^= c; } return chksum; } private void doFlush() { this.printStream.flush(); } }