public static ParseResult TryParse(byte[] message, out DecodeMessage decodeMessage) { if (!Enumerable.SequenceEqual(message.Take(4), new byte[] { 0xad, 0xbc, 0xcb, 0xda })) { decodeMessage = null; return(ParseResult.InvalidMagicNumber); } decodeMessage = new DecodeMessage(); int cur = 4; // length of magic number decodeMessage.SchemaVersion = GetInt32(message, ref cur); if (GetInt32(message, ref cur) != DECODE_MESSAGE_TYPE) { return(ParseResult.NotADecodeMessage); } decodeMessage.Id = GetString(message, ref cur); decodeMessage.New = GetBool(message, ref cur); decodeMessage.SinceMidnight = TimeSpan.FromMilliseconds(GetInt32(message, ref cur)); decodeMessage.Snr = GetInt32(message, ref cur); decodeMessage.DeltaTime = GetDouble(message, ref cur); decodeMessage.DeltaFrequency = GetInt32(message, ref cur); decodeMessage.Mode = GetString(message, ref cur); decodeMessage.Message = GetString(message, ref cur); decodeMessage.LowConfidence = GetBool(message, ref cur); decodeMessage.OffAir = GetBool(message, ref cur); return(ParseResult.Success); }
static void Main(string[] args) { bool all = args.Any(a => a == "--all"); bool grids = args.Any(a => a == "--grids"); string bandArg = args.SingleOrDefault(a => a.EndsWith("m")); if (string.IsNullOrWhiteSpace(bandArg) || !int.TryParse(bandArg.Substring(0, bandArg.Length - 1).Replace("--", ""), out int band)) { band = 20; } Console.WriteLine($"Selected {band}m, specify (e.g.) --6m for another band"); if (!all) { Console.WriteLine("Only showing needed spots. Pass --all for all spots."); } if (!grids) { Console.WriteLine("Not looking for unworked grids. Pass --grids to turn this on."); } if (args.Any(a => a == "--help" || a == "-h" || a == "/?")) { Console.WriteLine(@"A work in progress, that listens to udp://localhost:2237 for WSJT-X, works out the DXCC entity of every call heard using Clublog's cty.xml, then queries a Cloudlog instance via its API to see if it's a needed slot. If it is, it highlights the call in red in the console window."); return; } if (!File.Exists(configFile) || File.ReadAllText(configFile).Contains(connectionStringKey)) { Console.WriteLine("You need to provide a Cloudlog URL, e.g. https://mycloudloginstance.net"); Console.WriteLine("in order for ft8spotter to check spots against Cloudlog. Please provide it now..."); string url = Console.ReadLine(); string dir = Path.GetDirectoryName(configFile); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } File.WriteAllText(configFile, $"{urlKey}={url}"); } var config = GetConfig(); cloudLogUri = new Uri(new Uri(config[urlKey]), "index.php/api/"); var fi = new FileInfo("cty.xml"); bool isFresh = false; if (DateTime.Now - fi.LastWriteTime > TimeSpan.FromDays(1)) { var wc = new WebClient(); wc.DownloadFile("https://cdn.clublog.org/cty.php?api=a11c3235cd74b88212ce726857056939d52372bd&zip=1", "cty_new.xml.zip"); if (File.Exists("cty.xml.bak")) { File.Delete("cty.xml.bak"); } File.Move("cty.xml", "cty.xml.bak"); ZipFile.ExtractToDirectory("cty_new.xml.zip", "."); isFresh = true; File.Delete("cty_new.xml.zip"); } try { ctyXml = ClublogCtyXml.Parse(File.ReadAllText("cty.xml")); } catch (Exception ex) { if (isFresh) { File.Delete("cty.xml"); File.Move("cty.xml.bak", "cty.xml"); Console.WriteLine("Failed to update cty.xml"); ctyXml = ClublogCtyXml.Parse(File.ReadAllText("cty.xml")); } } const int port = 2237; using (var client = new UdpClient(port, AddressFamily.InterNetwork)) { Console.WriteLine($"Listening for WSJT-X on UDP port {port}"); var sw = Stopwatch.StartNew(); while (true) { var ipep = new IPEndPoint(IPAddress.Loopback, 0); byte[] msg = client.Receive(ref ipep); if (ParseResult.Success != DecodeMessage.TryParse(msg, out DecodeMessage decodeMessage)) { continue; } //if (msg[11] == 0x02) //{ //string heardCall = GetHeardCall(msg); string heardCall = GetHeardCall(decodeMessage.Message); if (heardCall == null) { continue; } var entity = GetEntity(heardCall); string grid = GetGrid(msg); var needed = entity == null ? Needed.No : GetNeeded(band, entity.Adif, grids ? grid : null, "ft8"); if (all || !Needed.No.Equals(needed)) { if (sw.Elapsed > TimeSpan.FromSeconds(5)) { Console.WriteLine($"--- {DateTime.Now:HH:mm:ss} --------------------------"); sw.Restart(); } var colBefore = Console.ForegroundColor; if (needed.NewCountryOnAnyBand) { Console.ForegroundColor = ConsoleColor.Green; } else if (needed.NewCountryOnBand) { Console.ForegroundColor = ConsoleColor.Yellow; } else if (needed.NewCountryOnBandOnMode) { Console.ForegroundColor = ConsoleColor.DarkYellow; } else if (needed.NewGridOnAnyBand) { Console.ForegroundColor = ConsoleColor.Red; } else if (needed.NewGridOnBand) { Console.ForegroundColor = ConsoleColor.Magenta; } else if (needed.NewGridOnBandOnMode) { Console.ForegroundColor = ConsoleColor.DarkRed; } WriteAtColumn(0, needed, 19); WriteAtColumn(19, heardCall, 10); WriteAtColumn(30, decodeMessage.Snr, 4); WriteAtColumn(34, IsGrid(grid) ? grid : String.Empty, 4); WriteAtColumn(39, entity?.Adif, 3); WriteAtColumn(43, (entity?.Entity) ?? "Unknown", 50); Console.WriteLine(); Console.ForegroundColor = colBefore; } } } }