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;
}
}