using GeoVLog.Core.Models; using System; using System.Globalization; using System.Text; namespace GeoVLog.Core.Parsers; /// /// Parser for the GEM GSM-90 VLF dual-station measurement line. /// Produces two objects per input line. /// internal static class VlfParser { public static bool TryParse(ReadOnlySpan msg, DateTime arrivalUtc, out VlfReading[] readings) { readings = Array.Empty(); var line = Encoding.ASCII.GetString(msg).Trim(); if (string.IsNullOrWhiteSpace(line)) return false; string[] parts = line.Split(',', StringSplitOptions.None); if (parts.Length != 22) return false; for (int i = 0; i < parts.Length; i++) parts[i] = parts[i].Trim(); if (!TryParseConsoleTime(parts[0], arrivalUtc.Date, out DateTime consoleTime)) return false; if (!double.TryParse(parts[1], NumberStyles.Float, CultureInfo.InvariantCulture, out double freq1)) return false; if (!double.TryParse(parts[2], NumberStyles.Float, CultureInfo.InvariantCulture, out double tf1)) return false; if (!double.TryParse(parts[3], NumberStyles.Float, CultureInfo.InvariantCulture, out double ip1)) return false; if (!double.TryParse(parts[4], NumberStyles.Float, CultureInfo.InvariantCulture, out double op1)) return false; if (!int.TryParse(parts[5], NumberStyles.Integer, CultureInfo.InvariantCulture, out int h11)) return false; if (!int.TryParse(parts[6], NumberStyles.Integer, CultureInfo.InvariantCulture, out int h21)) return false; if (!int.TryParse(parts[7], NumberStyles.Integer, CultureInfo.InvariantCulture, out int gain1)) return false; if (!double.TryParse(parts[8], NumberStyles.Float, CultureInfo.InvariantCulture, out double freq2)) return false; if (!double.TryParse(parts[9], NumberStyles.Float, CultureInfo.InvariantCulture, out double tf2)) return false; if (!double.TryParse(parts[10], NumberStyles.Float, CultureInfo.InvariantCulture, out double ip2)) return false; if (!double.TryParse(parts[11], NumberStyles.Float, CultureInfo.InvariantCulture, out double op2)) return false; if (!int.TryParse(parts[12], NumberStyles.Integer, CultureInfo.InvariantCulture, out int h12)) return false; if (!int.TryParse(parts[13], NumberStyles.Integer, CultureInfo.InvariantCulture, out int h22)) return false; if (!int.TryParse(parts[14], NumberStyles.Integer, CultureInfo.InvariantCulture, out int gain2)) return false; if (!double.TryParse(parts[15], NumberStyles.Float, CultureInfo.InvariantCulture, out double pitch)) return false; if (!double.TryParse(parts[16], NumberStyles.Float, CultureInfo.InvariantCulture, out double roll)) return false; if (!double.TryParse(parts[17], NumberStyles.Float, CultureInfo.InvariantCulture, out double tilt)) return false; if (!int.TryParse(parts[18], NumberStyles.Integer, CultureInfo.InvariantCulture, out int samples)) return false; if (!double.TryParse(parts[19], NumberStyles.Float, CultureInfo.InvariantCulture, out double vmain)) return false; if (!int.TryParse(parts[20], NumberStyles.Integer, CultureInfo.InvariantCulture, out int vdig)) return false; if (!double.TryParse(parts[21], NumberStyles.Float, CultureInfo.InvariantCulture, out double temp)) return false; readings = new[] { new VlfReading { TimestampUtc = arrivalUtc, ConsoleTimeUtc = consoleTime, Channel = 0, RawLine = line, FrequencyKHz = freq1, TotalField_pT = tf1, InPhase_pct = ip1, QuadPhase_pct = op1, H1 = h11, H2 = h21, Gain = gain1, PitchDeg = pitch, RollDeg = roll, TiltDeg = tilt, Samples = samples, VmainVolt = vmain, VdigMilliV = vdig, TempDegC = temp }, new VlfReading { TimestampUtc = arrivalUtc, ConsoleTimeUtc = consoleTime, Channel = 1, RawLine = line, FrequencyKHz = freq2, TotalField_pT = tf2, InPhase_pct = ip2, QuadPhase_pct = op2, H1 = h12, H2 = h22, Gain = gain2, PitchDeg = pitch, RollDeg = roll, TiltDeg = tilt, Samples = samples, VmainVolt = vmain, VdigMilliV = vdig, TempDegC = temp } }; return true; } private static bool TryParseConsoleTime(string value, DateTime date, out DateTime result) { result = default; if (value.Length < 6) return false; if (!int.TryParse(value.Substring(0, 2), out int hh)) return false; if (!int.TryParse(value.Substring(2, 2), out int mm)) return false; if (!double.TryParse(value.Substring(4), NumberStyles.Float, CultureInfo.InvariantCulture, out double ss)) return false; int sec = (int)Math.Floor(ss); int ms = (int)Math.Round((ss - sec) * 1000.0); result = new DateTime(date.Year, date.Month, date.Day, hh, mm, sec, DateTimeKind.Utc).AddMilliseconds(ms); return true; } }