// this is a "class packet" factory which converts received packets into usable classes // (packet classes defined in MagellanProtoDef.cs). We assume that basicParse() was run // on this packet before calling fromString() public NmeaPacket fromString() { NmeaPacket ret = null; if (m_string == null) { logError("NmeaPacketReceived:fromString() -- m_string==null"); return(ret); } switch (m_header) { case "ADVER": case "PMGNVER": ret = this; // all we need is already in fields array break; case "PMGNRTE": // $PMGNRTE,14,2,c,1,T01P03,a,T01P04,a*35 case "PMGNTRK": // $PMGNTRK,3334.530,N,11739.837,W,00000,M,034414.24,A*62 case "PMGNWPL": // $PMGNWPL,3328.069,N,11738.812,W,0000000,M,Road to,GCF320 = Road to Wat,a*68 ret = this; // all we need is already in fields array break; case "PMGNST": case "GPGSV": case "GPGGA": case "GPRMC": case "GPGLL": ret = this; // all we need is already in fields array break; default: logError("NmeaPacketReceived:fromString() -- unknown header=" + m_header); ret = this; // may be all we need is already in fields array break; } return(ret); }
public override bool process(string url, string filename, string source) { bool ret = true; LibSys.StatusBar.Trace("IP: FileNmeaLog:process() filename=" + filename); int wpCount = 0; int lineno = 0; int packetsCount = 0; Cursor.Current = Cursors.WaitCursor; try { string line; double zoneShift = 0.0d; NmeaPacketReceived packetIn = null; CreateInfo createInfo = new CreateInfo(); // we will recycle it in place, filling every time. double lat = 0.0d; double lng = 0.0d; // we allow elev and dateTime to stay in the cycle and be promoted/inherited to points that do not have this data in NMEA sentence. double elev = 0.0d; // alt above mean sea level DateTime dateTime = DateTime.Now; StreamReader stream = new StreamReader(filename); while ((line = stream.ReadLine()) != null) { lineno++; // we are in a VERY long cycle here, need to make sure Windows events and memory are managed Application.DoEvents(); if (lineno % 1000 == 0) { GC.Collect(); } line = line.Trim(); if (string.IsNullOrEmpty(line)) { continue; } try { // first see if that was a proprietary, non-NMEA packet: if (line.IndexOf("*") < 0) { LibSys.StatusBar.Trace("proprietary packet: " + line); if (packetIn != null && packetIn.isGood) { LibSys.StatusBar.Trace("last good NMEA packet: " + packetIn); } if (line.StartsWith("$ADVER,")) { Project.trackId++; createInfo.init("trk"); createInfo.id = Project.trackId; createInfo.source = source; createInfo.name = "GPS NMEA Log"; insertWaypoint(createInfo); } } else { packetIn = new NmeaPacketReceived(); packetIn.isGood = false; packetIn.m_string = line; if (!NmeaPacket.checksumVerify(packetIn.m_string)) { packetIn.isGood = false; LibSys.StatusBar.Error("bad checksum, line " + lineno + " bad packet: " + line); } else { packetIn.isGood = packetIn.basicParse(packetIn.m_string); // packet is good and parsed, so the fields array is filled //LibSys.StatusBar.Trace("NMEA packet: " + packetIn); if (packetIn.header.EndsWith("GGA")) // NMEA GGA expected for location { //LibSys.StatusBar.Trace("process: " + packetIn.header + " packet, count=" + packetsCount + " content=" + packetIn); try { NmeaPacket pvt = packetIn.fromString(); /* * $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47 * * Where: * GGA Global Positioning System Fix Data * 0 123519 Fix taken at 12:35:19 UTC * 1 4807.038,N Latitude 48 deg 07.038' N * 3 01131.000,E Longitude 11 deg 31.000' E * 5 1 Fix quality: 0 = invalid * 1 = GPS fix * 2 = DGPS fix * 6 = estimated (2.3 feature) * 6 08 Number of satellites being tracked * 7 0.9 Horizontal dilution of position * 8 545.4,M Altitude, Meters, above mean sea level * 10 46.9,M Height of geoid (mean sea level) above WGS84 * ellipsoid * 12 (empty field) time in seconds since last DGPS update * 13 (empty field) DGPS station ID number * 47 the checksum data, always begins with * */ lat = pvt.parseLat(1); lng = pvt.parseLng(1); elev = pvt.parseElev(8); // alt above mean sea level // time component (don't know the date): dateTime = pvt.parseNmeaTime(0, dateTime); // UTC dateTime = dateTime.AddHours(-zoneShift); // errors and velocity: double posError = 0; double posErrorH = 0; double posErrorV = 0; double fix = 0; // failed integrity check switch ((string)pvt.fields[5]) { case "0": // invalid fix = 1; // invalid break; case "1": // GPS fix fix = 2; // two dimensional break; case "2": // DGPS fix fix = 4; // two dimensional differential break; case "6": // estimated fix = 6; // estimated break; } string comment = "received " + packetIn.header; //realtimeCallback(pvtData); createInfo.init("trkpt"); createInfo.id = Project.trackId; // relate waypoint to track createInfo.dateTime = dateTime; createInfo.lat = lat; createInfo.lng = lng; createInfo.elev = elev; createInfo.source = source; insertWaypoint(createInfo); wpCount++; packetsCount++; } catch (Exception ee) { LibSys.StatusBar.Error("StartRealTime: " + ee.Message); } } else if (packetIn.header.EndsWith("RMC")) // NMEA RMC may come { //LibSys.StatusBar.Trace("process: " + packetIn.header + " packet, count=" + packetsCount + " content=" + packetIn); try { NmeaPacket pvt = packetIn.fromString(); // $GPRMC,052458.73,V,3334.6441,N,11739.6622,W,00.0,000.0,290103,13,E*4F /* * see http://www.gpsinformation.org/dale/nmea.htm for NMEA reference * * RMC - NMEA has its own version of essential gps pvt (position, velocity, time) data. * It is called RMC, The Recommended Minimum, which might look like: * * $GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A * $GPRMC,050821.000,A,3334.5551,N,11739.7705,W,0.00,,251006,,,A*67 * * Where: * RMC Recommended Minimum sentence C * 0 123519 Fix taken at 12:35:19 UTC * 1 A Status A=active or V=Void. * 2 4807.038,N Latitude 48 deg 07.038' N * 4 01131.000,E Longitude 11 deg 31.000' E * 6 022.4 Speed over the ground in knots * 7 084.4 Track angle in degrees True * 8 230394 Date - 23rd of March 1994 * 9 003.1,W Magnetic Variation * 6A The checksum data, always begins with * * * Note that, as of the 2.3 release of NMEA, there is a new field in the RMC sentence * at the end just prior to the checksum. The value of the entry is * A=autonomous, D=differential, E=estimated, N=Data not valid. */ bool status = "A".Equals(pvt.fields[1]); if (status) { lat = pvt.parseLat(2); lng = pvt.parseLng(2); //double elev = 0.0d; // inherit elevation from a GGA //LibSys.StatusBar.Trace("coord=" + pvtData.location); // time component (only time part): dateTime = pvt.parseNmeaDateTime(0, 8); // UTC dateTime = dateTime.AddHours(-zoneShift); //if (!"".Equals(pvt.fields[6])) //{ // double knots = Convert.ToDouble(pvt.fields[6]); // pvtData.velocity = knots * Distance.METERS_PER_NAUTICAL_MILE / 3600.0d; // // calculate velocity vector based on track angle: // if (!"".Equals(pvt.fields[7])) // { // double trackAngleRad = Convert.ToDouble(pvt.fields[7]) * Math.PI / 180.0d; // pvtData.velocityNorth = pvtData.velocity * Math.Cos(trackAngleRad); // pvtData.velocityEast = pvtData.velocity * Math.Sin(trackAngleRad); // } //} //pvtData.fix = 2; // assume two-dimensional //pvtData.comment = "received " + received.header; createInfo.init("trkpt"); createInfo.id = Project.trackId; // relate waypoint to track createInfo.dateTime = dateTime; createInfo.lat = lat; createInfo.lng = lng; createInfo.elev = elev; createInfo.source = source; insertWaypoint(createInfo); wpCount++; } packetsCount++; } catch (Exception ee) { LibSys.StatusBar.Error("StartRealTime: " + ee.Message); } } else if (packetIn.header.EndsWith("WPL")) // NMEA WPL may come as a result of manually marking a location { //LibSys.StatusBar.Trace("process: " + packetIn.header + " packet, count=" + packetsCount + " content=" + packetIn); try { NmeaPacket pvt = packetIn.fromString(); // $GPWPL,3334.7038,N,11739.7796,W,00001*61 /* * see http://www.gpsinformation.org/dale/nmea.htm for NMEA reference * * WPL - Waypoint Location data provides essential waypoint data. It is output when navigating to indicate data about the destination * and is sometimes supported on input to redefine a waypoint location. Note that waypoint data as defined in the standard * does not define altitude, comments, or icon data. When a route is active, this sentence is sent once for each waypoint in * the route, in sequence. When all waypoints have been reported, the RTE sentence is sent in the next data set. * In any group of sentences, only one WPL sentence, or an RTE sentence, will be sent. * * $GPWPL,4807.038,N,01131.000,E,WPTNME*5C * * With an interpretation of: * * WPL Waypoint Location * 0 4807.038,N Latitude * 1 01131.000,E Longitude * 2 WPTNME Waypoint Name * 5C The checksum data, always begins with * * */ lat = pvt.parseLat(0); lng = pvt.parseLng(0); //elev = 0.0d; // inherit elevation //LibSys.StatusBar.Trace("coord=" + pvtData.location); createInfo.init("wpt"); createInfo.lat = lat; createInfo.lng = lng; createInfo.elev = elev; createInfo.dateTime = dateTime; createInfo.name = "" + pvt.fields[4]; createInfo.source = source; insertWaypoint(createInfo); wpCount++; packetsCount++; } catch (Exception ee) { LibSys.StatusBar.Error("StartRealTime: " + ee.Message); } } //else if (packetIn.header.EndsWith("GLL")) // NMEA GLL may come //{ // LibSys.StatusBar.Trace("process: " + packetIn.header + " packet, count=" + packetsCount + " content=" + packetIn); // try // { // NmeaPacket pvt = packetIn.fromString(); // // $GPGLL,3334.6464,N,11739.6583,W,052707.129,A*29 // /* // * see http://www.gpsinformation.org/dale/nmea.htm for NMEA reference // * // GLL - Geographic Latitude and Longitude is a holdover from Loran data and some old units // may not send the time and data valid information if they are emulating Loran data. // If a gps is emulating Loran data they may use the LC Loran prefix instead of GP. // $GPGLL,4916.45,N,12311.12,W,225444,A,*31 // Where: // GLL Geographic position, Latitude and Longitude // 0 4916.46,N Latitude 49 deg. 16.45 min. North // 2 12311.12,W Longitude 123 deg. 11.12 min. West // 4 225444 Fix taken at 22:54:44 UTC // 5 A Data valid or V (void) // *31 checksum data // */ // bool status = "A".Equals(pvt.fields[5]); // if (status) // { // double lat = pvt.parseLat(0); // double lng = pvt.parseLng(0); // double elev = 0.0d; // // time component (only time part): // //pvtData.time = pvt.parseNmeaTime(4); // UTC // //pvtData.fix = 2; // assume two-dimensional // //pvtData.comment = "received " + received.header; // createInfo.init("trkpt"); // createInfo.id = Project.trackId; // relate waypoint to track // //createInfo.dateTime = dateTime; // createInfo.lat = lat; // createInfo.lng = lng; // createInfo.elev = elev; // createInfo.source = source; // insertWaypoint(createInfo); // wpCount++; // } // packetsCount++; // } // catch (Exception ee) // { // LibSys.StatusBar.Error("StartRealTime: " + ee.Message); // } //} else if (packetIn.header.EndsWith("GSV")) // NMEA GSV may come for satellite reception status { //LibSys.StatusBar.Trace("process: " + packetIn.header + " packet, count=" + packetsCount + " content=" + packetIn); // /* // * see http://www.gpsinformation.org/dale/nmea.htm for NMEA reference // * // $GPGSV,3,1,08,14,69,204,,15,57,105,36,18,45,047,36,03,42,263,36*71 // $GPGSV,2,1,08,01,40,083,46,02,17,308,41,12,07,344,39,14,22,228,45*75 // Where: // GSV Satellites in view // 0 2 Number of sentences for full data // 1 1 sentence 1 of 2 // 2 08 Number of satellites in view // for up to 4 satellites per sentence { // 3 01 Satellite PRN number // 4 40 Elevation, degrees // 5 083 Azimuth, degrees // 6 46 Signal strength - higher is better // } // *75 the checksum data, always begins with * // */ // //MPacket pvt = received.fromString(); // //pvtData.comment = "received"; // //realtimeCallback(pvtData); packetsCount++; } else if (packetIn.header.EndsWith("VTG")) // NMEA GSA may come { //LibSys.StatusBar.Trace("process: " + packetIn.header + " packet, count=" + packetsCount + " content=" + packetIn); // $GPVTG,309.62,T,,M,0.13,N,0.2,K*6E /* * VTG - Course Over Ground and Ground Speed * * Name Example Units Description * Message ID $GPVTG VTG protocol header * Course 309.62 degrees Measured heading * Reference T True * Course degrees Measured heading * Reference M Magnetic * Speed 0.13 knots Measured horizontal speed * Units N Knots * Speed 0.2 Km/hr Measured horizontal speed * Units K Kilometers per hour */ packetsCount++; } else if (packetIn.header.EndsWith("GSA")) // NMEA GSA may come { //LibSys.StatusBar.Trace("process: " + packetIn.header + " packet, count=" + packetsCount + " content=" + packetIn); // $GPGSA,A,2,15,18,14,31,,,,,,,,,05.6,05.6,*17 /* * see http://www.gpsinformation.org/dale/nmea.htm for NMEA reference * * GSA - GPS DOP and active satellites. This sentence provides details on the nature of the * fix. It includes the numbers of the satellites being used in the current solution and * the DOP. DOP (dilution of precision) is an indication of the effect of satellite geometry * on the accuracy of the fix. It is a unitless number where smaller is better. For 3D fixes * using 4 satellites a 1.0 would be considered to be a perfect number. For overdetermined * solutions it is possible to see numbers below 1.0. There are differences in the way the * PRN's are presented which can effect the ability of some programs to display this data. * For example, in the example shown below there are 5 satellites in the solution and * the null fields are scattered indicating that the almanac would show satellites in * the null positions that are not being used as part of this solution. Other receivers * might output all of the satellites used at the beginning of the sentence with the * null field all stacked up at the end. This difference accounts for some satellite * display programs not always being able to display the satellites being tracked. * * $GPGSA,A,3,04,05,,09,12,,,24,,,,,2.5,1.3,2.1*39 * * Where: * GSA Satellite status * A Auto selection of 2D or 3D fix (M = manual) * 3 3D fix - other values include: 1 = no fix * 2 = 2D fix * 04,05... PRNs of satellites used for fix (space for 12) * 2.5 PDOP (dilution of precision) * 1.3 Horizontal dilution of precision (HDOP) * 2.1 Vertical dilution of precision (VDOP) * 39 the checksum data, always begins with * * */ packetsCount++; } else { LibSys.StatusBar.Trace("StartRealTime: good other (unrecognized) packet received, count=" + packetsCount + " content=" + packetIn); } } } } catch (Exception ee) { LibSys.StatusBar.Error("FileNmeaLog:process(): file=" + filename + " line=" + lineno + " " + ee.Message); } } } catch (Exception eee) { LibSys.StatusBar.Error("FileNmeaLog:process(): " + eee.Message); ret = false; } finally { Cursor.Current = Cursors.Default; Application.DoEvents(); } LibSys.StatusBar.Trace("OK: FileNmeaLog:process() filename=" + filename + " lines=" + lineno + ", packets=" + packetsCount + " waypoints=" + wpCount); return(ret); }