public static string GetCot(LocationObject location) { var lastTime = DateTime.Parse(location.Lasttime); var timeString = lastTime.ToString("yyyy-MM-ddTHH:mm:ssZ"); // Do we want to add anything to the callsign for the ID? //"<?xml version=\"1.0\" standalone=\"yes\"?><event version=\"2.0\" uid=\"{0}\" type=\"a-f-A\" how=\"m-r\" time=\"{1}\" start=\"{1}\" stale=\"{2}\"><point lat=\"{3}\" lon=\"{4}\" ce=20\" le=\"20\" hae=\"{5}\" /></event>\""; return string.Format(_cotTemplate, location.Srccall, timeString, GetStaleTime(), location.Lat, location.Lng, location.Hae); }
private static LocationObject ParseBody(string source, string body) { LocationObject loc = new LocationObject(); loc.Srccall = source; if (source == "IW7DGY") { } var bodyChars = body.ToCharArray(); var packetType = bodyChars[0]; if (packetType == '=' || packetType =='/' || packetType =='@' ) { // regular position packet if (body.Length < 10) { loc.Error = "Packet is less than 10 characters. Too short."; } // Normal or compressed location packet, with or without // timestamp, with or without messaging capability // ! and / have messaging, / and @ have a prepended // timestamp var cursor = 1; if (packetType == '/' || packetType == '@') { //Process timestamp. loc.Lasttime = ParseTime(body); cursor += 7; } char posChar = (char) bodyChars[cursor]; if ('0' <= posChar && posChar <= '9') { // Latitude: /*Latitude is expressed as a fixed 8-character field, in degrees and decimal minutes (to two decimal places), followed by the letter N for north or S for south. Latitude degrees are in the range 00 to 90. Latitude minutes are expressed as whole minutes and hundredths of a minute, separated by a decimal point. For example: 4903.50N is 49 degrees 3 minutes 30 seconds north. In generic format examples, the latitude is shown as the 8-character string ddmm.hhN (i.e. degrees, minutes and hundredths of a minute north).*/ var rawLat = body.Substring(cursor, 8); loc.Lat = rawLat.Insert(2, " "); //Longitude /*Longitude is expressed as a fixed 9-character field, in degrees and decimal minutes (to two decimal places), followed by the letter E for east or W for west. Longitude degrees are in the range 000 to 180. Longitude minutes are expressed as whole minutes and hundredths of a minute, separated by a decimal point. For example: 07201.75W is 72 degrees 1 minute 45 seconds west. In generic format examples, the longitude is shown as the 9-character string dddmm.hhW (i.e. degrees, minutes and hundredths of a minute west). */ var rawLng = body.Substring(cursor + 9, 9); loc.Lng = rawLng.Insert(3, " "); } else // compressed packet { // if (validSymTableCompressed(posChar)) { /* [\/\\A-Za-j] */ // compressed position packet // position = PositionParser.parseCompressed(msgBody, cursor); // this.extension = PositionParser.parseCompressedExtension(msgBody, cursor); //// positionSource = "Compressed"; loc.Error = "Compressed packet. I don't know how to handle that yet."; } } else if(packetType == '`' || packetType == '\\') { // MIC-e packet loc.Error = "MIC-e packet. I don't know how to handle that yet."; } else if (packetType == '!') { //Ultimeter 2000 weather // instrument packet loc.Error = "Ultimeter 2000 packet. I don't think I want to know how to handle that packet type."; } else if (packetType == '$') { // NMEA packet loc.Error = "NMEA packet. I don't know how to handle that yet."; } else { // Not a position message loc.Error = "Packet does not appear to be a position message."; } return loc; }
// Monitors log file generated by APRSIS32 APRS radio software void _fileSystemWatcher_Changed(object sender, FileSystemEventArgs e) { try { if (e.Name == "APRSIS32.log") { var lines = File.ReadAllLines(e.FullPath); int counter = 0; string line = ""; string uid = ""; string time = ""; string lat = ""; string lng = ""; string hae = ""; string callsign = ""; string comment = ""; if (line.Contains(textBoxCallsign.Text)) { var elements = line.Split('/'); // If there are 8 elements, we hope this is an IGated position message... if (elements.Length == 8) { // This is what is in my sample file... /* [0] "IGate:WinMain:2014-05-10T20:22:03.526 RFtoIS:[Mobilinkd]IGated KG6PPZ-11>KG6PPZ,WIDE2-1,qAR,KG6PPZ:" [1] "202201h3635.49N" [2] "08716.31WO000" [3] "000" [4] "A=000553" [5] "Ti=32" [6] "Te=70" [7] "V=11429 ForceX high altitude balloon test" */ //TODO: figure out how to get timestamp from additional msgs we might process. // This works for IGATE and KISS log entries var dateIndex = elements[0].IndexOf(DateTime.Now.Year.ToString()); if (dateIndex >= 0) { // TODO: if we are going to process the line, log it UpdateOutput("Processing log file line: " + line); //Get callsign - need entire callsign because chase car and balloon use same one // with different extension var callsignIndex = elements[0].IndexOf(textBoxCallsign.Text); if (callsignIndex >= 0) { var endCallsignIndex = elements[0].IndexOf('>'); callsign = elements[0].Substring(callsignIndex, endCallsignIndex - callsignIndex); } var index = elements[1].IndexOf('h'); if (index > 0) { var rawTime = elements[1].Substring(0, index); var rawLat = elements[1].Substring(index + 1); // Get raw lat into a decimal string var degrees = int.Parse(rawLat.Substring(0, 2)); var decimalMinutes = decimal.Parse(rawLat.Substring(2, rawLat.Length - 3)); // limit to 5 decimal places var decimalDegrees = degrees + (decimalMinutes / 60); lat = decimalDegrees.ToString("F5", System.Globalization.CultureInfo.InvariantCulture); if (rawTime.Length == 6) { var hour = int.Parse(rawTime.Substring(0, 2)); var minute = int.Parse(rawTime.Substring(2, 2)); var second = int.Parse(rawTime.Substring(4, 2)); DateTime msgTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, hour, minute, second); time = msgTime.ToString("yyyy-MM-ddTHH:mm:ssZ"); } } // Assuming western hemisphere... index = elements[2].IndexOf('W'); if (index > 0) { var rawLon = elements[2].Substring(0, index); var degrees = int.Parse(rawLon.Substring(0, 3)); var decimalMinutes = decimal.Parse(rawLon.Substring(3)); var decimalDegrees = degrees + (decimalMinutes / 60); //limit to 5 decimal places lng = decimalDegrees.ToString("F5", System.Globalization.CultureInfo.InvariantCulture); } // parse altitude if (!string.IsNullOrEmpty(elements[4])) { hae = elements[4].Substring(elements[4].IndexOf("A=") + 2); } //TODO: looks like there is another field before the comment... what is it? comment = elements[7]; // Srccall, Lat, Lng, Hae, Lasttime, Comment); var location = new LocationObject { Srccall = callsign, Lat = lat, Lng = lng, Hae = hae, Lasttime = time, Comment = comment }; EmitCot(location); } } } } } finally { } }
// Parses a line from the log file generated by APRSIS32 APRS radio software void ProcessLine(string line) { if (!line.Contains(textBoxCallsign.Text)) return; try { int counter = 0; string uid = ""; string time = ""; string lat = ""; string lng = ""; string hae = ""; string callsign = ""; string comment = ""; var elements = line.Split('/'); if (elements.Length == 2) { /* [0] "MyCall:WinMain:2014-05-31T19:03:00.367 [TransmitAPRS] KG6PPZ>APWW10,WIDE1-1,WIDE2-1:@190300h3635.50N" [1] "08716.35WkAPRS-IS for Win32" */ var dateIndex = elements[0].IndexOf(DateTime.Now.Year.ToString()); if (dateIndex >= 0) { var spaceIndex = elements[0].IndexOf(' '); var thisTime = elements[0].Substring(dateIndex, elements[0].IndexOf(' ') - dateIndex); //if we are going to process the line, log it UpdateOutput("Processing log file line: " + line); //Get callsign - need entire callsign because chase car and balloon use same one // with different extension var callsignIndex = elements[0].IndexOf(textBoxCallsign.Text); if (callsignIndex >= 0) { var endCallsignIndex = elements[0].IndexOf('>'); callsign = elements[0].Substring(callsignIndex, endCallsignIndex - callsignIndex); } // messages that parse into 4 elements have the time before the first '/' var index = elements[0].IndexOf('@'); if (index > 0) { var timeLatPart = elements[0].Substring(index); var timeSeparatorIndex = timeLatPart.IndexOf('h'); if (timeSeparatorIndex > 0) { var rawTime = timeLatPart.Substring(1, timeSeparatorIndex - 1); var rawLat = timeLatPart.Substring(timeSeparatorIndex + 1); // Get raw lat into a decimal string var degrees = int.Parse(rawLat.Substring(0, 2)); var decimalMinutes = decimal.Parse(rawLat.Substring(2, rawLat.Length - 3)); // limit to 5 decimal places var decimalDegrees = degrees + (decimalMinutes / 60); lat = decimalDegrees.ToString("F5", System.Globalization.CultureInfo.InvariantCulture); if (rawTime.Length == 6) { var hour = int.Parse(rawTime.Substring(0, 2)); var minute = int.Parse(rawTime.Substring(2, 2)); var second = int.Parse(rawTime.Substring(4, 2)); DateTime msgTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, hour, minute, second); time = msgTime.ToString("yyyy-MM-ddTHH:mm:ssZ"); } } } // Assuming western hemisphere... index = elements[1].IndexOf('W'); if (index > 0) { var rawLon = elements[1].Substring(0, index); var degrees = int.Parse(rawLon.Substring(0, 3)); var decimalMinutes = decimal.Parse(rawLon.Substring(3)); var decimalDegrees = degrees + (decimalMinutes / 60); // make longitude negative for western hemisphere decimalDegrees = decimalDegrees - (2 * decimalDegrees); //limit to 5 decimal places lng = decimalDegrees.ToString("F5", System.Globalization.CultureInfo.InvariantCulture); // parse comment (this size message doesn't have altitude. if (elements[1].Length > index) { comment = elements[1].Substring(index + 1); } } // Srccall, Lat, Lng, Hae, Lasttime, Comment); var location = new LocationObject { Srccall = callsign, Lat = lat, Lng = lng, Hae = hae, Lasttime = time, Comment = comment }; EmitCot(location); } } else if (elements.Length == 4) { // Sample of own location from file /* [0] "MyCall:WinMain:2014-06-14T15:29:55.108 [TransmitAPRS] KG6PPZ>APWW10,WIDE1-1,WIDE2-1:@152953h3635.52N" [1] "08716.29Wk056" [2] "000" [3] "A=000708APRS-IS for Win32" */ var dateIndex = elements[0].IndexOf(DateTime.Now.Year.ToString()); if (dateIndex >= 0) { var spaceIndex = elements[0].IndexOf(' '); var thisTime = elements[0].Substring(dateIndex, elements[0].IndexOf(' ') - dateIndex); //if we are going to process the line, log it UpdateOutput("Processing log file line: " + line); //Get callsign - need entire callsign because chase car and balloon use same one // with different extension var callsignIndex = elements[0].IndexOf(textBoxCallsign.Text); if (callsignIndex >= 0) { var endCallsignIndex = elements[0].IndexOf('>'); callsign = elements[0].Substring(callsignIndex, endCallsignIndex - callsignIndex); } // messages that parse into 4 elements have the time before the first '/' var index = elements[0].IndexOf('@'); if (index > 0) { var timeLatPart = elements[0].Substring(index); var timeSeparatorIndex = timeLatPart.IndexOf('h'); if (timeSeparatorIndex > 0) { var rawTime = timeLatPart.Substring(1, timeSeparatorIndex - 1); var rawLat = timeLatPart.Substring(timeSeparatorIndex + 1); // Get raw lat into a decimal string var degrees = int.Parse(rawLat.Substring(0, 2)); var decimalMinutes = decimal.Parse(rawLat.Substring(2, rawLat.Length - 3)); // limit to 5 decimal places var decimalDegrees = degrees + (decimalMinutes / 60); lat = decimalDegrees.ToString("F5", System.Globalization.CultureInfo.InvariantCulture); if (rawTime.Length == 6) { var hour = int.Parse(rawTime.Substring(0, 2)); var minute = int.Parse(rawTime.Substring(2, 2)); var second = int.Parse(rawTime.Substring(4, 2)); DateTime msgTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, hour, minute, second); time = msgTime.ToString("yyyy-MM-ddTHH:mm:ssZ"); } } } // Assuming western hemisphere... index = elements[1].IndexOf('W'); if (index > 0) { var rawLon = elements[1].Substring(0, index); var degrees = int.Parse(rawLon.Substring(0, 3)); var decimalMinutes = decimal.Parse(rawLon.Substring(3)); var decimalDegrees = degrees + (decimalMinutes / 60); // make longitude negative for western hemisphere decimalDegrees = decimalDegrees - (2 * decimalDegrees); //limit to 5 decimal places lng = decimalDegrees.ToString("F5", System.Globalization.CultureInfo.InvariantCulture); } // parse altitude and comment if (!string.IsNullOrEmpty(elements[3])) { hae = elements[3].Substring(elements[3].IndexOf("A=") + 2, 6); comment = elements[3].Substring(8); } // Srccall, Lat, Lng, Hae, Lasttime, Comment); var location = new LocationObject { Srccall = callsign, Lat = lat, Lng = lng, Hae = hae, Lasttime = time, Comment = comment }; EmitCot(location); } } // If there are 8 elements, we hope this is an expected position message... else if (elements.Length == 8) { // sample of IGate data from file... /* [0] "IGate:WinMain:2014-05-10T20:22:03.526 RFtoIS:[Mobilinkd]IGated KG6PPZ-11>KG6PPZ,WIDE2-1,qAR,KG6PPZ:" [1] "202201h3635.49N" [2] "08716.31WO000" [3] "000" [4] "A=000553" [5] "Ti=32" [6] "Te=70" [7] "V=11429 ForceX high altitude balloon test" */ var dateIndex = elements[0].IndexOf(DateTime.Now.Year.ToString()); if (dateIndex >= 0) { var spaceIndex = elements[0].IndexOf(' '); var thisTime = elements[0].Substring(dateIndex, elements[0].IndexOf(' ') - dateIndex); // TODO: if we are going to process the line, log it UpdateOutput("Processing log file line: " + line); //Get callsign - need entire callsign because chase car and balloon use same one // with different extension var callsignIndex = elements[0].IndexOf(textBoxCallsign.Text); if (callsignIndex >= 0) { var endCallsignIndex = elements[0].IndexOf('>'); callsign = elements[0].Substring(callsignIndex, endCallsignIndex - callsignIndex); } var index = elements[1].IndexOf('h'); if (index > 0) { var rawTime = elements[1].Substring(0, index); var rawLat = elements[1].Substring(index + 1); // Get raw lat into a decimal string var degrees = int.Parse(rawLat.Substring(0, 2)); var decimalMinutes = decimal.Parse(rawLat.Substring(2, rawLat.Length - 3)); // limit to 5 decimal places var decimalDegrees = degrees + (decimalMinutes / 60); lat = decimalDegrees.ToString("F5", System.Globalization.CultureInfo.InvariantCulture); if (rawTime.Length == 6) { var hour = int.Parse(rawTime.Substring(0, 2)); var minute = int.Parse(rawTime.Substring(2, 2)); var second = int.Parse(rawTime.Substring(4, 2)); DateTime msgTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, hour, minute, second); time = msgTime.ToString("yyyy-MM-ddTHH:mm:ssZ"); } } // Assuming western hemisphere... index = elements[2].IndexOf('W'); if (index > 0) { var rawLon = elements[2].Substring(0, index); var degrees = int.Parse(rawLon.Substring(0, 3)); var decimalMinutes = decimal.Parse(rawLon.Substring(3)); var decimalDegrees = degrees + (decimalMinutes / 60); // Need negative longitude for W hemisphere decimalDegrees = decimalDegrees - (2 * decimalDegrees); //limit to 5 decimal places lng = decimalDegrees.ToString("F5", System.Globalization.CultureInfo.InvariantCulture); } // parse altitude if (!string.IsNullOrEmpty(elements[4])) { hae = elements[4].Substring(elements[4].IndexOf("A=") + 2); } //TODO: looks like there is another field before the comment... what is it? comment = elements[7]; // Srccall, Lat, Lng, Hae, Lasttime, Comment); var location = new LocationObject { Srccall = callsign, Lat = lat, Lng = lng, Hae = hae, Lasttime = time, Comment = comment }; EmitCot(location); } } } catch (Exception ex) { } finally { } }
private void EmitCot(LocationObject location) { if(_udpClient == null) { InitializeUdp(); } if (_udpClient != null) { string cotString = CotGenerator.GetCot(location); _udpClient.SendUdp(cotString); } }