mirror of
https://github.com/dotMorten/NmeaParser.git
synced 2026-01-29 11:54:40 +01:00
⏪️ evert PTNL,GGK and AML,SVM
This commit is contained in:
parent
531fe94d0d
commit
eb9904e1df
|
|
@ -1,165 +0,0 @@
|
|||
// *******************************************************************************
|
||||
// * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// * you may not use this file except in compliance with the License.
|
||||
// * You may obtain a copy of the License at
|
||||
// *
|
||||
// * http://www.apache.org/licenses/LICENSE-2.0
|
||||
// *
|
||||
// * Unless required by applicable law or agreed to in writing, software
|
||||
// * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// * See the License for the specific language governing permissions and
|
||||
// * limitations under the License.
|
||||
// ******************************************************************************
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace NmeaParser.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// PTNL,GGK: Time, position, position type, DOP
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// 1.: UTC time of position fix, in hhmmss.ss format. Hours must be two numbers, so may be padded. For example, 7 is shown as 07.
|
||||
/// 2.: UTC date of position fix, in mmddyy format. Day must be two numbers, so may be padded. For example, 8 is shown as 08.
|
||||
/// 3.: Latitude, in degrees and decimal minutes (dddmm.mmmmmmm)
|
||||
/// 4.: Direction of latitude: N: North S: South
|
||||
/// 5.: Longitude, in degrees and decimal minutes (dddmm.mmmmmmm). Should contain three digits of ddd.
|
||||
/// 6.: Direction of longitude: E: East W: West
|
||||
/// 7.: GPS Quality indicator:
|
||||
/// 0: Fix not available or invalid
|
||||
/// 1: Autonomous GPS fix
|
||||
/// 2: RTK float solution
|
||||
/// 3: RTK fix solution
|
||||
/// 4: Differential, code phase only solution(DGPS)
|
||||
/// 5: SBAS solution – WAAS/EGNOS/MSAS
|
||||
/// 6: RTK float or RTK location 3D Network solution
|
||||
/// 7: RTK fixed 3D Network solution
|
||||
/// 8: RTK float or RTK location 2D in a Network solution
|
||||
/// 9: RTK fixed 2D Network solution
|
||||
/// 10: OmniSTAR HP / XP solution
|
||||
/// 11: OmniSTAR VBS solution
|
||||
/// 12: Location RTK solution
|
||||
/// 13: Beacon DGPS
|
||||
/// 14: CenterPoint RTX
|
||||
/// 15: xFill
|
||||
/// 8.: Number of satellites in fix
|
||||
/// 9.: Dilution of Precision of fix (DOP)
|
||||
/// 10.: Ellipsoidal height of fix (antenna height above ellipsoid).
|
||||
/// 11.: M: ellipsoidal height is measured in meters
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// NOTE – The PTNL,GGK message is longer than the NMEA-0183 standard of 80 characters
|
||||
/// NOTE – Even if a user-defined geoid model, or an inclined plane is loaded into the receiver, then the height output in the NMEA GGK string is always an ellipsoid height, for example, EHT24.123.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Ggk")]
|
||||
[NmeaMessageType("GGK")]
|
||||
public class Ggk : NmeaMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Ggk"/> class.
|
||||
/// </summary>
|
||||
/// <param name="type">The message type</param>
|
||||
/// <param name="message">The NMEA message values.</param>
|
||||
public Ggk(string type, string[] message) : base(type, message)
|
||||
{
|
||||
if (message == null || message.Length < 11)
|
||||
throw new ArgumentException("Invalid Ggk", "message");
|
||||
|
||||
UtcTime = StringsToUtcDateTime(message[1], message[0]);
|
||||
Latitude = StringToLatitude(message[2], message[3]);
|
||||
Longitude = StringToLongitude(message[4], message[5]);
|
||||
if (!string.IsNullOrEmpty(message[6]))
|
||||
Quality = (Ggk.QualityIndicator)int.Parse(message[6], CultureInfo.InvariantCulture);
|
||||
if (!string.IsNullOrEmpty(message[7]))
|
||||
NumberOfSatellites = int.Parse(message[7], CultureInfo.InvariantCulture);
|
||||
DilutionOfPrecision = StringToDouble(message[8]);
|
||||
EllipsoidalHeightOfFix = StringToDouble(message[9]);
|
||||
EllipsoidalHeightIsMeasuredInMeters = message[10] == "M";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UTC time of position fix, in DateTime format.
|
||||
/// DateTime(fullYear, month, day, hours, minutes, seconds, milliseconds, DateTimeKind.Utc)
|
||||
/// </summary>
|
||||
public DateTime? UtcTime { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Latitude, in degrees and decimal minutes (dddmm.mmmmmmm)
|
||||
/// </summary>
|
||||
public double Latitude { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Longitude, in degrees and decimal minutes (dddmm.mmmmmmm)
|
||||
/// </summary>
|
||||
public double Longitude { get; }
|
||||
|
||||
/// <summary>
|
||||
/// GPS Quality indicator
|
||||
/// </summary>
|
||||
public Ggk.QualityIndicator Quality { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of satellites in fix
|
||||
/// </summary>
|
||||
public int NumberOfSatellites { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Dilution of Precision of fix (DOP)
|
||||
/// </summary>
|
||||
public double DilutionOfPrecision { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Ellipsoidal height of fix (antenna height above ellipsoid). Must start with EHT.
|
||||
/// </summary>
|
||||
public double EllipsoidalHeightOfFix { get; }
|
||||
|
||||
/// <summary>
|
||||
/// M: ellipsoidal height is measured in meters
|
||||
/// </summary>
|
||||
public bool EllipsoidalHeightIsMeasuredInMeters { get; }
|
||||
|
||||
/// <summary>
|
||||
/// GPS Quality indicator
|
||||
/// </summary>
|
||||
public enum QualityIndicator : int
|
||||
{
|
||||
/// <summary>Fix not available or invalid</summary>
|
||||
Invalid = 0,
|
||||
/// <summary>Autonomous GPS fix</summary>
|
||||
GpsFix = 1,
|
||||
/// <summary>RTK float solution</summary>
|
||||
RtkFloat = 2,
|
||||
/// <summary>RTK fix solution</summary>
|
||||
RtkFix = 3,
|
||||
/// <summary>Differential, code phase only solution(DGPS)</summary>
|
||||
Ggps = 4,
|
||||
/// <summary>SBAS solution – WAAS/EGNOS/MSAS</summary>
|
||||
Sbas = 5,
|
||||
/// <summary>RTK float or RTK location 3D Network solution</summary>
|
||||
RtkFloatOrLocation3DNetworkSolution = 6,
|
||||
/// <summary>RTK fixed 3D Network solution</summary>
|
||||
RtkFixed3DNetworkSolution = 7,
|
||||
/// <summary>RTK float or RTK location 2D in a Network solution</summary>
|
||||
RtkFloatOrLocation2DNetworkSolution = 8,
|
||||
/// <summary>RTK fixed 2D Network solution</summary>
|
||||
RtkFixed2DNetworkSolution = 9,
|
||||
/// <summary>OmniSTAR HP / XP solution</summary>
|
||||
OmistarHpXp = 10,
|
||||
/// <summary>OmniSTAR VBS solution</summary>
|
||||
OmniStarVbs = 11,
|
||||
/// <summary>Location RTK solution</summary>
|
||||
LocationRtk = 12,
|
||||
/// <summary>Beacon DGPS</summary>
|
||||
BeaconDgps = 13,
|
||||
/// <summary>CenterPoint RTX</summary>
|
||||
CenterPointRtx = 14,
|
||||
/// <summary>xFill</summary>
|
||||
XFill = 15,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -188,12 +188,6 @@ namespace NmeaParser.Messages
|
|||
|
||||
string[] parts = message.Split(new char[] { ',' });
|
||||
string MessageType = parts[0].Substring(1);
|
||||
if (MessageType is "PTNL" or "AML") {
|
||||
// PTNL is parent to e.g. AVR, GGK etc.
|
||||
// AML is parent to e.g. SVT, SV, SVP etc
|
||||
MessageType = parts[1];
|
||||
parts = parts.Skip(1).ToArray();
|
||||
}
|
||||
if (MessageType == string.Empty)
|
||||
throw new ArgumentException("Missing NMEA Message Type");
|
||||
string[] MessageParts = parts.Skip(1).ToArray();
|
||||
|
|
@ -310,68 +304,6 @@ namespace NmeaParser.Messages
|
|||
return TimeSpan.Zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse from mmddyy + hhmmss.ss to DateTime (used in e.g. Ggk)
|
||||
/// https://receiverhelp.trimble.com/alloy-gnss/en-us/NMEA-0183messages_PTNL_GGK.html
|
||||
/// </summary>
|
||||
internal static DateTime? StringsToUtcDateTime(string dateField, string timeField)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(dateField) || string.IsNullOrWhiteSpace(timeField))
|
||||
return null;
|
||||
|
||||
// 2. Parse date (mmddyy)
|
||||
// - Typically the year is 2 digits; you must decide how to handle the century.
|
||||
// - For example, if year < 80 => 20xx; else => 19xx
|
||||
if (dateField.Length < 6) return null;
|
||||
if (!int.TryParse(dateField.Substring(0, 2), out int month)) return null; // mm
|
||||
if (!int.TryParse(dateField.Substring(2, 2), out int day)) return null; // dd
|
||||
if (!int.TryParse(dateField.Substring(4, 2), out int year)) return null; // yy
|
||||
|
||||
int fullYear = (year < 80) ? (2000 + year) : (1900 + year);
|
||||
|
||||
// 3. Parse time (hhmmss.ss)
|
||||
// - The fractional seconds might be optional or might have variable length.
|
||||
// - Parse hours, minutes, and then the part after the decimal.
|
||||
// - Time can be "hhmmss", or "hhmmss.sss", etc.
|
||||
// Example: "102939.00" => hh=10, mm=29, ss=39, fraction=0
|
||||
if (timeField.Length < 6) return null;
|
||||
if (!int.TryParse(timeField.Substring(0, 2), out int hours)) return null; // hh
|
||||
if (!int.TryParse(timeField.Substring(2, 2), out int minutes)) return null; // mm
|
||||
|
||||
// The seconds part can have a decimal fraction
|
||||
// So we split around the decimal point:
|
||||
double secondsDouble;
|
||||
if (timeField.Length > 4)
|
||||
{
|
||||
string secString = timeField.Substring(4); // "39.00 in example above"
|
||||
if (!double.TryParse(secString,
|
||||
System.Globalization.NumberStyles.Float,
|
||||
System.Globalization.CultureInfo.InvariantCulture,
|
||||
out secondsDouble))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
secondsDouble = 0.0;
|
||||
}
|
||||
int seconds = (int)secondsDouble;
|
||||
int milliseconds = (int)((secondsDouble - seconds) * 1000.0);
|
||||
|
||||
// 4. Construct the UTC DateTime
|
||||
try
|
||||
{
|
||||
var result = new DateTime(fullYear, month, day, hours, minutes, seconds, milliseconds, DateTimeKind.Utc);
|
||||
return result;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If the date/time is invalid (e.g. month=13, day=32), we return null or handle differently
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the current object is equal to another object of the same type.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1,76 +0,0 @@
|
|||
// *******************************************************************************
|
||||
// * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// * you may not use this file except in compliance with the License.
|
||||
// * You may obtain a copy of the License at
|
||||
// *
|
||||
// * http://www.apache.org/licenses/LICENSE-2.0
|
||||
// *
|
||||
// * Unless required by applicable law or agreed to in writing, software
|
||||
// * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// * See the License for the specific language governing permissions and
|
||||
// * limitations under the License.
|
||||
// ******************************************************************************
|
||||
|
||||
using System;
|
||||
|
||||
namespace NmeaParser.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// AML Sound Velocity (m/s), Temperature (C)
|
||||
/// AML Oceanographic Svm sensor
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// 1.: Sound Velocity m/s
|
||||
/// 2.: Unit of temperature, TC = Celcius
|
||||
/// 3.: Temperature
|
||||
/// 4.: Label, SN: Serial number
|
||||
/// 5.: Device serial number
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[NmeaMessageType("SVM")]
|
||||
public class Svm : NmeaMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Svm"/> class.
|
||||
/// </summary>
|
||||
/// <param name="type">The message type</param>
|
||||
/// <param name="message">The NMEA message values.</param>
|
||||
public Svm(string type, string[] message) : base(type, message)
|
||||
{
|
||||
if (message == null || message.Length < 5)
|
||||
throw new ArgumentException("Invalid Svm", "message");
|
||||
SoundVelocity = StringToDouble(message[0]);
|
||||
TemperatureUnit = message[1];
|
||||
Temperature = StringToDouble(message[2]);
|
||||
IsSerialNumber = message[3] == "SN";
|
||||
SerialNumber = StringToDouble(message[4]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sound Velocity m/s
|
||||
/// </summary>
|
||||
public double SoundVelocity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Temperature Unit
|
||||
/// </summary>
|
||||
public string TemperatureUnit { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Temperature
|
||||
/// </summary>
|
||||
public double Temperature { get; }
|
||||
|
||||
/// <summary>
|
||||
/// String indicating Serial number
|
||||
/// </summary>
|
||||
public bool IsSerialNumber { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Serial Number
|
||||
/// </summary>
|
||||
public double SerialNumber { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -85,8 +85,6 @@ namespace NmeaParser.Tests
|
|||
{
|
||||
var msg = NmeaMessage.Parse(line, previousMessage as IMultiSentenceMessage);
|
||||
Assert.IsNotNull(msg);
|
||||
if (line.IndexOf("PTNL,") > 0) continue; // TODO PTNL
|
||||
if (line.IndexOf("AML,") > 0) continue; // TODO AML
|
||||
var idx = line.IndexOf('*');
|
||||
if (idx >= 0)
|
||||
{
|
||||
|
|
@ -126,20 +124,6 @@ namespace NmeaParser.Tests
|
|||
Assert.ThrowsException<ArgumentException>(() => NmeaMessage.Parse(input, ignoreChecksum: false));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestAmlSvm()
|
||||
{
|
||||
string input = "$AML,SVM,1468.951,TC,15.753,SN,168753*0F";
|
||||
var msg = NmeaMessage.Parse(input);
|
||||
Assert.IsInstanceOfType(msg, typeof(Svm));
|
||||
Svm svm = (Svm)msg;
|
||||
Assert.AreEqual(1468.951, svm.SoundVelocity);
|
||||
Assert.AreEqual("TC", svm.TemperatureUnit);
|
||||
Assert.AreEqual(15.753, svm.Temperature);
|
||||
Assert.IsTrue(svm.IsSerialNumber);
|
||||
Assert.AreEqual(168753, svm.SerialNumber);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestDPT()
|
||||
{
|
||||
|
|
@ -366,23 +350,6 @@ namespace NmeaParser.Tests
|
|||
Assert.AreEqual('M', ptlna.SlopeDistanceUnits);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestPtnlGgk()
|
||||
{
|
||||
string input = "$PTNL,GGK,133703.14,012925,6104.64373420,N,01027.91199999,E,2,08,1.0,0.0,M,*0B";
|
||||
var msg = NmeaMessage.Parse(input);
|
||||
Assert.IsInstanceOfType(msg, typeof(Ggk));
|
||||
Ggk ggk = (Ggk)msg;
|
||||
Assert.AreEqual(new DateTime(2025, 1, 29, 13, 37, 3, 140, DateTimeKind.Utc), ggk.UtcTime);
|
||||
Assert.AreEqual(61.07739557, ggk.Latitude);
|
||||
Assert.AreEqual(10.465199999833333, ggk.Longitude);
|
||||
Assert.AreEqual(Ggk.QualityIndicator.RtkFloat, ggk.Quality);
|
||||
Assert.AreEqual(8, ggk.NumberOfSatellites);
|
||||
Assert.AreEqual(1, ggk.DilutionOfPrecision);
|
||||
Assert.AreEqual(0, ggk.EllipsoidalHeightOfFix);
|
||||
Assert.IsTrue(ggk.EllipsoidalHeightIsMeasuredInMeters);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestPgrme()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue